implemented occlusion query support on corona rendering, this enables
[divverent/darkplaces.git] / clvm_cmds.c
1 #include "quakedef.h"
2
3 #include "prvm_cmds.h"
4 #include "csprogs.h"
5 #include "cl_collision.h"
6 #include "r_shadow.h"
7 #include "jpeg.h"
8 #include "image.h"
9
10 //============================================================================
11 // Client
12 //[515]: unsolved PROBLEMS
13 //- finish player physics code (cs_runplayerphysics)
14 //- EntWasFreed ?
15 //- RF_DEPTHHACK is not like it should be
16 //- add builtin that sets cl.viewangles instead of reading "input_angles" global
17 //- finish lines support for R_Polygon***
18 //- insert selecttraceline into traceline somehow
19
20 //4 feature darkplaces csqc: add builtin to clientside qc for reading triangles of model meshes (useful to orient a ui along a triangle of a model mesh)
21 //4 feature darkplaces csqc: add builtins to clientside qc for gl calls
22
23 extern cvar_t v_flipped;
24
25 sfx_t *S_FindName(const char *name);
26 int Sbar_GetSortedPlayerIndex (int index);
27 void Sbar_SortFrags (void);
28 void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius);
29 void CSQC_RelinkAllEntities (int drawmask);
30 void CSQC_RelinkCSQCEntities (void);
31 const char *Key_GetBind (int key);
32
33 // #1 void(vector ang) makevectors
34 static void VM_CL_makevectors (void)
35 {
36         VM_SAFEPARMCOUNT(1, VM_CL_makevectors);
37         AngleVectors (PRVM_G_VECTOR(OFS_PARM0), prog->globals.client->v_forward, prog->globals.client->v_right, prog->globals.client->v_up);
38 }
39
40 // #2 void(entity e, vector o) setorigin
41 void VM_CL_setorigin (void)
42 {
43         prvm_edict_t    *e;
44         float   *org;
45         VM_SAFEPARMCOUNT(2, VM_CL_setorigin);
46
47         e = PRVM_G_EDICT(OFS_PARM0);
48         if (e == prog->edicts)
49         {
50                 VM_Warning("setorigin: can not modify world entity\n");
51                 return;
52         }
53         if (e->priv.required->free)
54         {
55                 VM_Warning("setorigin: can not modify free entity\n");
56                 return;
57         }
58         org = PRVM_G_VECTOR(OFS_PARM1);
59         VectorCopy (org, e->fields.client->origin);
60         CL_LinkEdict(e);
61 }
62
63 static void SetMinMaxSize (prvm_edict_t *e, float *min, float *max)
64 {
65         int             i;
66
67         for (i=0 ; i<3 ; i++)
68                 if (min[i] > max[i])
69                         PRVM_ERROR("SetMinMaxSize: backwards mins/maxs");
70
71         // set derived values
72         VectorCopy (min, e->fields.client->mins);
73         VectorCopy (max, e->fields.client->maxs);
74         VectorSubtract (max, min, e->fields.client->size);
75
76         CL_LinkEdict (e);
77 }
78
79 // #3 void(entity e, string m) setmodel
80 void VM_CL_setmodel (void)
81 {
82         prvm_edict_t    *e;
83         const char              *m;
84         dp_model_t *mod;
85         int                             i;
86
87         VM_SAFEPARMCOUNT(2, VM_CL_setmodel);
88
89         e = PRVM_G_EDICT(OFS_PARM0);
90         e->fields.client->modelindex = 0;
91         e->fields.client->model = 0;
92
93         m = PRVM_G_STRING(OFS_PARM1);
94         mod = NULL;
95         for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
96         {
97                 if (!strcmp(cl.csqc_model_precache[i]->name, m))
98                 {
99                         mod = cl.csqc_model_precache[i];
100                         e->fields.client->model = PRVM_SetEngineString(mod->name);
101                         e->fields.client->modelindex = -(i+1);
102                         break;
103                 }
104         }
105
106         if( !mod ) {
107                 for (i = 0;i < MAX_MODELS;i++)
108                 {
109                         mod = cl.model_precache[i];
110                         if (mod && !strcmp(mod->name, m))
111                         {
112                                 e->fields.client->model = PRVM_SetEngineString(mod->name);
113                                 e->fields.client->modelindex = i;
114                                 break;
115                         }
116                 }
117         }
118
119         if( mod ) {
120                 // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
121                 //SetMinMaxSize (e, mod->normalmins, mod->normalmaxs);
122         }
123         else
124         {
125                 SetMinMaxSize (e, vec3_origin, vec3_origin);
126                 VM_Warning ("setmodel: model '%s' not precached\n", m);
127         }
128 }
129
130 // #4 void(entity e, vector min, vector max) setsize
131 static void VM_CL_setsize (void)
132 {
133         prvm_edict_t    *e;
134         float                   *min, *max;
135         VM_SAFEPARMCOUNT(3, VM_CL_setsize);
136
137         e = PRVM_G_EDICT(OFS_PARM0);
138         if (e == prog->edicts)
139         {
140                 VM_Warning("setsize: can not modify world entity\n");
141                 return;
142         }
143         if (e->priv.server->free)
144         {
145                 VM_Warning("setsize: can not modify free entity\n");
146                 return;
147         }
148         min = PRVM_G_VECTOR(OFS_PARM1);
149         max = PRVM_G_VECTOR(OFS_PARM2);
150
151         SetMinMaxSize( e, min, max );
152
153         CL_LinkEdict(e);
154 }
155
156 // #8 void(entity e, float chan, string samp, float volume, float atten) sound
157 static void VM_CL_sound (void)
158 {
159         const char                      *sample;
160         int                                     channel;
161         prvm_edict_t            *entity;
162         float                           volume;
163         float                           attenuation;
164
165         VM_SAFEPARMCOUNT(5, VM_CL_sound);
166
167         entity = PRVM_G_EDICT(OFS_PARM0);
168         channel = (int)PRVM_G_FLOAT(OFS_PARM1);
169         sample = PRVM_G_STRING(OFS_PARM2);
170         volume = PRVM_G_FLOAT(OFS_PARM3);
171         attenuation = PRVM_G_FLOAT(OFS_PARM4);
172
173         if (volume < 0 || volume > 1)
174         {
175                 VM_Warning("VM_CL_sound: volume must be in range 0-1\n");
176                 return;
177         }
178
179         if (attenuation < 0 || attenuation > 4)
180         {
181                 VM_Warning("VM_CL_sound: attenuation must be in range 0-4\n");
182                 return;
183         }
184
185         if (channel < 0 || channel > 7)
186         {
187                 VM_Warning("VM_CL_sound: channel must be in range 0-7\n");
188                 return;
189         }
190
191         S_StartSound(32768 + PRVM_NUM_FOR_EDICT(entity), channel, S_FindName(sample), entity->fields.client->origin, volume, attenuation);
192 }
193
194 // #483 void(vector origin, string sample, float volume, float attenuation) pointsound
195 static void VM_CL_pointsound(void)
196 {
197         const char                      *sample;
198         float                           volume;
199         float                           attenuation;
200         vec3_t                          org;
201
202         VM_SAFEPARMCOUNT(4, VM_CL_pointsound);
203
204         VectorCopy( PRVM_G_VECTOR(OFS_PARM0), org);
205         sample = PRVM_G_STRING(OFS_PARM1);
206         volume = PRVM_G_FLOAT(OFS_PARM2);
207         attenuation = PRVM_G_FLOAT(OFS_PARM3);
208
209         if (volume < 0 || volume > 1)
210         {
211                 VM_Warning("VM_CL_pointsound: volume must be in range 0-1\n");
212                 return;
213         }
214
215         if (attenuation < 0 || attenuation > 4)
216         {
217                 VM_Warning("VM_CL_pointsound: attenuation must be in range 0-4\n");
218                 return;
219         }
220
221         // Send World Entity as Entity to Play Sound (for CSQC, that is 32768)
222         S_StartSound(32768, 0, S_FindName(sample), org, volume, attenuation);
223 }
224
225 // #14 entity() spawn
226 static void VM_CL_spawn (void)
227 {
228         prvm_edict_t *ed;
229         ed = PRVM_ED_Alloc();
230         VM_RETURN_EDICT(ed);
231 }
232
233 void CL_VM_SetTraceGlobals(const trace_t *trace, int svent)
234 {
235         prvm_eval_t *val;
236         VM_SetTraceGlobals(trace);
237         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_networkentity)))
238                 val->_float = svent;
239 }
240
241 #define CL_HitNetworkBrushModels(move) !((move) == MOVE_WORLDONLY)
242 #define CL_HitNetworkPlayers(move)     !((move) == MOVE_WORLDONLY || (move) == MOVE_NOMONSTERS)
243
244 // #16 float(vector v1, vector v2, float movetype, entity ignore) traceline
245 static void VM_CL_traceline (void)
246 {
247         float   *v1, *v2;
248         trace_t trace;
249         int             move, svent;
250         prvm_edict_t    *ent;
251
252         VM_SAFEPARMCOUNTRANGE(4, 4, VM_CL_traceline);
253
254         prog->xfunction->builtinsprofile += 30;
255
256         v1 = PRVM_G_VECTOR(OFS_PARM0);
257         v2 = PRVM_G_VECTOR(OFS_PARM1);
258         move = (int)PRVM_G_FLOAT(OFS_PARM2);
259         ent = PRVM_G_EDICT(OFS_PARM3);
260
261         if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v1[2]) || IS_NAN(v2[2]))
262                 PRVM_ERROR("%s: NAN errors detected in traceline('%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent));
263
264         trace = CL_Move(v1, vec3_origin, vec3_origin, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true);
265
266         CL_VM_SetTraceGlobals(&trace, svent);
267 }
268
269 /*
270 =================
271 VM_CL_tracebox
272
273 Used for use tracing and shot targeting
274 Traces are blocked by bbox and exact bsp entityes, and also slide box entities
275 if the tryents flag is set.
276
277 tracebox (vector1, vector mins, vector maxs, vector2, tryents)
278 =================
279 */
280 // LordHavoc: added this for my own use, VERY useful, similar to traceline
281 static void VM_CL_tracebox (void)
282 {
283         float   *v1, *v2, *m1, *m2;
284         trace_t trace;
285         int             move, svent;
286         prvm_edict_t    *ent;
287
288         VM_SAFEPARMCOUNTRANGE(6, 8, VM_CL_tracebox); // allow more parameters for future expansion
289
290         prog->xfunction->builtinsprofile += 30;
291
292         v1 = PRVM_G_VECTOR(OFS_PARM0);
293         m1 = PRVM_G_VECTOR(OFS_PARM1);
294         m2 = PRVM_G_VECTOR(OFS_PARM2);
295         v2 = PRVM_G_VECTOR(OFS_PARM3);
296         move = (int)PRVM_G_FLOAT(OFS_PARM4);
297         ent = PRVM_G_EDICT(OFS_PARM5);
298
299         if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v1[2]) || IS_NAN(v2[2]))
300                 PRVM_ERROR("%s: NAN errors detected in tracebox('%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], m1[0], m1[1], m1[2], m2[0], m2[1], m2[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent));
301
302         trace = CL_Move(v1, m1, m2, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true);
303
304         CL_VM_SetTraceGlobals(&trace, svent);
305 }
306
307 trace_t CL_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore, int *svent)
308 {
309         int i;
310         float gravity;
311         vec3_t move, end;
312         vec3_t original_origin;
313         vec3_t original_velocity;
314         vec3_t original_angles;
315         vec3_t original_avelocity;
316         prvm_eval_t *val;
317         trace_t trace;
318
319         VectorCopy(tossent->fields.client->origin   , original_origin   );
320         VectorCopy(tossent->fields.client->velocity , original_velocity );
321         VectorCopy(tossent->fields.client->angles   , original_angles   );
322         VectorCopy(tossent->fields.client->avelocity, original_avelocity);
323
324         val = PRVM_EDICTFIELDVALUE(tossent, prog->fieldoffsets.gravity);
325         if (val != NULL && val->_float != 0)
326                 gravity = val->_float;
327         else
328                 gravity = 1.0;
329         gravity *= cl.movevars_gravity * 0.05;
330
331         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
332         {
333                 tossent->fields.client->velocity[2] -= gravity;
334                 VectorMA (tossent->fields.client->angles, 0.05, tossent->fields.client->avelocity, tossent->fields.client->angles);
335                 VectorScale (tossent->fields.client->velocity, 0.05, move);
336                 VectorAdd (tossent->fields.client->origin, move, end);
337                 trace = CL_Move (tossent->fields.client->origin, tossent->fields.client->mins, tossent->fields.client->maxs, end, MOVE_NORMAL, tossent, CL_GenericHitSuperContentsMask(tossent), true, true, NULL, true);
338                 VectorCopy (trace.endpos, tossent->fields.client->origin);
339
340                 if (trace.fraction < 1)
341                         break;
342         }
343
344         VectorCopy(original_origin   , tossent->fields.client->origin   );
345         VectorCopy(original_velocity , tossent->fields.client->velocity );
346         VectorCopy(original_angles   , tossent->fields.client->angles   );
347         VectorCopy(original_avelocity, tossent->fields.client->avelocity);
348
349         return trace;
350 }
351
352 static void VM_CL_tracetoss (void)
353 {
354         trace_t trace;
355         prvm_edict_t    *ent;
356         prvm_edict_t    *ignore;
357         int svent;
358
359         prog->xfunction->builtinsprofile += 600;
360
361         VM_SAFEPARMCOUNT(2, VM_CL_tracetoss);
362
363         ent = PRVM_G_EDICT(OFS_PARM0);
364         if (ent == prog->edicts)
365         {
366                 VM_Warning("tracetoss: can not use world entity\n");
367                 return;
368         }
369         ignore = PRVM_G_EDICT(OFS_PARM1);
370
371         trace = CL_Trace_Toss (ent, ignore, &svent);
372
373         CL_VM_SetTraceGlobals(&trace, svent);
374 }
375
376
377 // #20 void(string s) precache_model
378 void VM_CL_precache_model (void)
379 {
380         const char      *name;
381         int                     i;
382         dp_model_t              *m;
383
384         VM_SAFEPARMCOUNT(1, VM_CL_precache_model);
385
386         name = PRVM_G_STRING(OFS_PARM0);
387         for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
388         {
389                 if(!strcmp(cl.csqc_model_precache[i]->name, name))
390                 {
391                         PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
392                         return;
393                 }
394         }
395         PRVM_G_FLOAT(OFS_RETURN) = 0;
396         m = Mod_ForName(name, false, false, false);
397         if(m && m->loaded)
398         {
399                 for (i = 0;i < MAX_MODELS;i++)
400                 {
401                         if (!cl.csqc_model_precache[i])
402                         {
403                                 cl.csqc_model_precache[i] = (dp_model_t*)m;
404                                 PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
405                                 return;
406                         }
407                 }
408                 VM_Warning("VM_CL_precache_model: no free models\n");
409                 return;
410         }
411         VM_Warning("VM_CL_precache_model: model \"%s\" not found\n", name);
412 }
413
414 int CSQC_EntitiesInBox (vec3_t mins, vec3_t maxs, int maxlist, prvm_edict_t **list)
415 {
416         prvm_edict_t    *ent;
417         int                             i, k;
418
419         ent = PRVM_NEXT_EDICT(prog->edicts);
420         for(k=0,i=1; i<prog->num_edicts ;i++, ent = PRVM_NEXT_EDICT(ent))
421         {
422                 if (ent->priv.required->free)
423                         continue;
424                 if(BoxesOverlap(mins, maxs, ent->fields.client->absmin, ent->fields.client->absmax))
425                         list[k++] = ent;
426         }
427         return k;
428 }
429
430 // #22 entity(vector org, float rad) findradius
431 static void VM_CL_findradius (void)
432 {
433         prvm_edict_t    *ent, *chain;
434         vec_t                   radius, radius2;
435         vec3_t                  org, eorg, mins, maxs;
436         int                             i, numtouchedicts;
437         prvm_edict_t    *touchedicts[MAX_EDICTS];
438
439         VM_SAFEPARMCOUNT(2, VM_CL_findradius);
440
441         chain = (prvm_edict_t *)prog->edicts;
442
443         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
444         radius = PRVM_G_FLOAT(OFS_PARM1);
445         radius2 = radius * radius;
446
447         mins[0] = org[0] - (radius + 1);
448         mins[1] = org[1] - (radius + 1);
449         mins[2] = org[2] - (radius + 1);
450         maxs[0] = org[0] + (radius + 1);
451         maxs[1] = org[1] + (radius + 1);
452         maxs[2] = org[2] + (radius + 1);
453         numtouchedicts = CSQC_EntitiesInBox(mins, maxs, MAX_EDICTS, touchedicts);
454         if (numtouchedicts > MAX_EDICTS)
455         {
456                 // this never happens   //[515]: for what then ?
457                 Con_Printf("CSQC_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
458                 numtouchedicts = MAX_EDICTS;
459         }
460         for (i = 0;i < numtouchedicts;i++)
461         {
462                 ent = touchedicts[i];
463                 // Quake did not return non-solid entities but darkplaces does
464                 // (note: this is the reason you can't blow up fallen zombies)
465                 if (ent->fields.client->solid == SOLID_NOT && !sv_gameplayfix_blowupfallenzombies.integer)
466                         continue;
467                 // LordHavoc: compare against bounding box rather than center so it
468                 // doesn't miss large objects, and use DotProduct instead of Length
469                 // for a major speedup
470                 VectorSubtract(org, ent->fields.client->origin, eorg);
471                 if (sv_gameplayfix_findradiusdistancetobox.integer)
472                 {
473                         eorg[0] -= bound(ent->fields.client->mins[0], eorg[0], ent->fields.client->maxs[0]);
474                         eorg[1] -= bound(ent->fields.client->mins[1], eorg[1], ent->fields.client->maxs[1]);
475                         eorg[2] -= bound(ent->fields.client->mins[2], eorg[2], ent->fields.client->maxs[2]);
476                 }
477                 else
478                         VectorMAMAM(1, eorg, -0.5f, ent->fields.client->mins, -0.5f, ent->fields.client->maxs, eorg);
479                 if (DotProduct(eorg, eorg) < radius2)
480                 {
481                         ent->fields.client->chain = PRVM_EDICT_TO_PROG(chain);
482                         chain = ent;
483                 }
484         }
485
486         VM_RETURN_EDICT(chain);
487 }
488
489 // #34 float() droptofloor
490 static void VM_CL_droptofloor (void)
491 {
492         prvm_edict_t            *ent;
493         prvm_eval_t                     *val;
494         vec3_t                          end;
495         trace_t                         trace;
496
497         VM_SAFEPARMCOUNTRANGE(0, 2, VM_CL_droptofloor); // allow 2 parameters because the id1 defs.qc had an incorrect prototype
498
499         // assume failure if it returns early
500         PRVM_G_FLOAT(OFS_RETURN) = 0;
501
502         ent = PRVM_PROG_TO_EDICT(prog->globals.client->self);
503         if (ent == prog->edicts)
504         {
505                 VM_Warning("droptofloor: can not modify world entity\n");
506                 return;
507         }
508         if (ent->priv.server->free)
509         {
510                 VM_Warning("droptofloor: can not modify free entity\n");
511                 return;
512         }
513
514         VectorCopy (ent->fields.client->origin, end);
515         end[2] -= 256;
516
517         trace = CL_Move(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
518
519         if (trace.fraction != 1)
520         {
521                 VectorCopy (trace.endpos, ent->fields.client->origin);
522                 ent->fields.client->flags = (int)ent->fields.client->flags | FL_ONGROUND;
523                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.groundentity)))
524                         val->edict = PRVM_EDICT_TO_PROG(trace.ent);
525                 PRVM_G_FLOAT(OFS_RETURN) = 1;
526                 // if support is destroyed, keep suspended (gross hack for floating items in various maps)
527 //              ent->priv.server->suspendedinairflag = true;
528         }
529 }
530
531 // #35 void(float style, string value) lightstyle
532 static void VM_CL_lightstyle (void)
533 {
534         int                     i;
535         const char      *c;
536
537         VM_SAFEPARMCOUNT(2, VM_CL_lightstyle);
538
539         i = (int)PRVM_G_FLOAT(OFS_PARM0);
540         c = PRVM_G_STRING(OFS_PARM1);
541         if (i >= cl.max_lightstyle)
542         {
543                 VM_Warning("VM_CL_lightstyle >= MAX_LIGHTSTYLES\n");
544                 return;
545         }
546         strlcpy (cl.lightstyle[i].map,  MSG_ReadString(), sizeof (cl.lightstyle[i].map));
547         cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
548         cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map);
549 }
550
551 // #40 float(entity e) checkbottom
552 static void VM_CL_checkbottom (void)
553 {
554         static int              cs_yes, cs_no;
555         prvm_edict_t    *ent;
556         vec3_t                  mins, maxs, start, stop;
557         trace_t                 trace;
558         int                             x, y;
559         float                   mid, bottom;
560
561         VM_SAFEPARMCOUNT(1, VM_CL_checkbottom);
562         ent = PRVM_G_EDICT(OFS_PARM0);
563         PRVM_G_FLOAT(OFS_RETURN) = 0;
564
565         VectorAdd (ent->fields.client->origin, ent->fields.client->mins, mins);
566         VectorAdd (ent->fields.client->origin, ent->fields.client->maxs, maxs);
567
568 // if all of the points under the corners are solid world, don't bother
569 // with the tougher checks
570 // the corners must be within 16 of the midpoint
571         start[2] = mins[2] - 1;
572         for     (x=0 ; x<=1 ; x++)
573                 for     (y=0 ; y<=1 ; y++)
574                 {
575                         start[0] = x ? maxs[0] : mins[0];
576                         start[1] = y ? maxs[1] : mins[1];
577                         if (!(CL_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
578                                 goto realcheck;
579                 }
580
581         cs_yes++;
582         PRVM_G_FLOAT(OFS_RETURN) = true;
583         return;         // we got out easy
584
585 realcheck:
586         cs_no++;
587 //
588 // check it for real...
589 //
590         start[2] = mins[2];
591
592 // the midpoint must be within 16 of the bottom
593         start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
594         start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
595         stop[2] = start[2] - 2*sv_stepheight.value;
596         trace = CL_Move (start, vec3_origin, vec3_origin, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
597
598         if (trace.fraction == 1.0)
599                 return;
600
601         mid = bottom = trace.endpos[2];
602
603 // the corners must be within 16 of the midpoint
604         for     (x=0 ; x<=1 ; x++)
605                 for     (y=0 ; y<=1 ; y++)
606                 {
607                         start[0] = stop[0] = x ? maxs[0] : mins[0];
608                         start[1] = stop[1] = y ? maxs[1] : mins[1];
609
610                         trace = CL_Move (start, vec3_origin, vec3_origin, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
611
612                         if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
613                                 bottom = trace.endpos[2];
614                         if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
615                                 return;
616                 }
617
618         cs_yes++;
619         PRVM_G_FLOAT(OFS_RETURN) = true;
620 }
621
622 // #41 float(vector v) pointcontents
623 static void VM_CL_pointcontents (void)
624 {
625         VM_SAFEPARMCOUNT(1, VM_CL_pointcontents);
626         PRVM_G_FLOAT(OFS_RETURN) = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, CL_PointSuperContents(PRVM_G_VECTOR(OFS_PARM0)));
627 }
628
629 // #48 void(vector o, vector d, float color, float count) particle
630 static void VM_CL_particle (void)
631 {
632         float   *org, *dir;
633         int             count;
634         unsigned char   color;
635         VM_SAFEPARMCOUNT(4, VM_CL_particle);
636
637         org = PRVM_G_VECTOR(OFS_PARM0);
638         dir = PRVM_G_VECTOR(OFS_PARM1);
639         color = (int)PRVM_G_FLOAT(OFS_PARM2);
640         count = (int)PRVM_G_FLOAT(OFS_PARM3);
641         CL_ParticleEffect(EFFECT_SVC_PARTICLE, count, org, org, dir, dir, NULL, color);
642 }
643
644 // #74 void(vector pos, string samp, float vol, float atten) ambientsound
645 static void VM_CL_ambientsound (void)
646 {
647         float   *f;
648         sfx_t   *s;
649         VM_SAFEPARMCOUNT(4, VM_CL_ambientsound);
650         s = S_FindName(PRVM_G_STRING(OFS_PARM0));
651         f = PRVM_G_VECTOR(OFS_PARM1);
652         S_StaticSound (s, f, PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3)*64);
653 }
654
655 // #92 vector(vector org) getlight (DP_QC_GETLIGHT)
656 static void VM_CL_getlight (void)
657 {
658         vec3_t ambientcolor, diffusecolor, diffusenormal;
659         vec_t *p;
660
661         VM_SAFEPARMCOUNT(1, VM_CL_getlight);
662
663         p = PRVM_G_VECTOR(OFS_PARM0);
664         VectorClear(ambientcolor);
665         VectorClear(diffusecolor);
666         VectorClear(diffusenormal);
667         if (cl.worldmodel && cl.worldmodel->brush.LightPoint)
668                 cl.worldmodel->brush.LightPoint(cl.worldmodel, p, ambientcolor, diffusecolor, diffusenormal);
669         VectorMA(ambientcolor, 0.5, diffusecolor, PRVM_G_VECTOR(OFS_RETURN));
670 }
671
672
673 //============================================================================
674 //[515]: SCENE MANAGER builtins
675 extern qboolean CSQC_AddRenderEdict (prvm_edict_t *ed);//csprogs.c
676
677 static void CSQC_R_RecalcView (void)
678 {
679         extern matrix4x4_t viewmodelmatrix;
680         Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, cl.csqc_origin[0], cl.csqc_origin[1], cl.csqc_origin[2], cl.csqc_angles[0], cl.csqc_angles[1], cl.csqc_angles[2], 1);
681         Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, cl.csqc_origin[0], cl.csqc_origin[1], cl.csqc_origin[2], cl.csqc_angles[0], cl.csqc_angles[1], cl.csqc_angles[2], cl_viewmodel_scale.value);
682 }
683
684 void CL_RelinkLightFlashes(void);
685 //#300 void() clearscene (EXT_CSQC)
686 void VM_CL_R_ClearScene (void)
687 {
688         VM_SAFEPARMCOUNT(0, VM_CL_R_ClearScene);
689         // clear renderable entity and light lists
690         r_refdef.scene.numentities = 0;
691         r_refdef.scene.numlights = 0;
692         // FIXME: restore these to the values from VM_CL_UpdateView
693         r_refdef.view.x = 0;
694         r_refdef.view.y = 0;
695         r_refdef.view.z = 0;
696         r_refdef.view.width = vid.width;
697         r_refdef.view.height = vid.height;
698         r_refdef.view.depth = 1;
699         // FIXME: restore frustum_x/frustum_y
700         r_refdef.view.useperspective = true;
701         r_refdef.view.frustum_y = tan(scr_fov.value * M_PI / 360.0) * (3.0/4.0) * cl.viewzoom;
702         r_refdef.view.frustum_x = r_refdef.view.frustum_y * (float)r_refdef.view.width / (float)r_refdef.view.height / vid_pixelheight.value;
703         r_refdef.view.frustum_x *= r_refdef.frustumscale_x;
704         r_refdef.view.frustum_y *= r_refdef.frustumscale_y;
705         r_refdef.view.ortho_x = scr_fov.value * (3.0 / 4.0) * (float)r_refdef.view.width / (float)r_refdef.view.height / vid_pixelheight.value;
706         r_refdef.view.ortho_y = scr_fov.value * (3.0 / 4.0);
707         r_refdef.view.clear = true;
708         r_refdef.view.isoverlay = false;
709         // FIXME: restore cl.csqc_origin
710         // FIXME: restore cl.csqc_angles
711         cl.csqc_vidvars.drawworld = true;
712         cl.csqc_vidvars.drawenginesbar = false;
713         cl.csqc_vidvars.drawcrosshair = false;
714 }
715
716 //#301 void(float mask) addentities (EXT_CSQC)
717 extern void CSQC_Predraw (prvm_edict_t *ed);//csprogs.c
718 extern void CSQC_Think (prvm_edict_t *ed);//csprogs.c
719 void VM_CL_R_AddEntities (void)
720 {
721         int                     i, drawmask;
722         prvm_edict_t *ed;
723         VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntities);
724         drawmask = (int)PRVM_G_FLOAT(OFS_PARM0);
725         CSQC_RelinkAllEntities(drawmask);
726         CL_RelinkLightFlashes();
727
728         prog->globals.client->time = cl.time;
729         for(i=1;i<prog->num_edicts;i++)
730         {
731                 ed = &prog->edicts[i];
732                 if(ed->priv.required->free)
733                         continue;
734                 CSQC_Think(ed);
735                 if(ed->priv.required->free)
736                         continue;
737                 // note that for RF_USEAXIS entities, Predraw sets v_forward/v_right/v_up globals that are read by CSQC_AddRenderEdict
738                 CSQC_Predraw(ed);
739                 if(ed->priv.required->free)
740                         continue;
741                 if(!((int)ed->fields.client->drawmask & drawmask))
742                         continue;
743                 CSQC_AddRenderEdict(ed);
744         }
745 }
746
747 //#302 void(entity ent) addentity (EXT_CSQC)
748 void VM_CL_R_AddEntity (void)
749 {
750         VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntity);
751         CSQC_AddRenderEdict(PRVM_G_EDICT(OFS_PARM0));
752 }
753
754 //#303 float(float property, ...) setproperty (EXT_CSQC)
755 void VM_CL_R_SetView (void)
756 {
757         int             c;
758         float   *f;
759         float   k;
760
761         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_R_SetView);
762
763         c = (int)PRVM_G_FLOAT(OFS_PARM0);
764         f = PRVM_G_VECTOR(OFS_PARM1);
765         k = PRVM_G_FLOAT(OFS_PARM1);
766
767         switch(c)
768         {
769         case VF_MIN:
770                 r_refdef.view.x = (int)(f[0]);
771                 r_refdef.view.y = (int)(f[1]);
772                 break;
773         case VF_MIN_X:
774                 r_refdef.view.x = (int)(k);
775                 break;
776         case VF_MIN_Y:
777                 r_refdef.view.y = (int)(k);
778                 break;
779         case VF_SIZE:
780                 r_refdef.view.width = (int)(f[0]);
781                 r_refdef.view.height = (int)(f[1]);
782                 break;
783         case VF_SIZE_X:
784                 r_refdef.view.width = (int)(k);
785                 break;
786         case VF_SIZE_Y:
787                 r_refdef.view.height = (int)(k);
788                 break;
789         case VF_VIEWPORT:
790                 r_refdef.view.x = (int)(f[0]);
791                 r_refdef.view.y = (int)(f[1]);
792                 f = PRVM_G_VECTOR(OFS_PARM2);
793                 r_refdef.view.width = (int)(f[0]);
794                 r_refdef.view.height = (int)(f[1]);
795                 break;
796         case VF_FOV:
797                 r_refdef.view.frustum_x = tan(f[0] * M_PI / 360.0);r_refdef.view.ortho_x = f[0];
798                 r_refdef.view.frustum_y = tan(f[1] * M_PI / 360.0);r_refdef.view.ortho_y = f[1];
799                 break;
800         case VF_FOVX:
801                 r_refdef.view.frustum_x = tan(k * M_PI / 360.0);r_refdef.view.ortho_x = k;
802                 break;
803         case VF_FOVY:
804                 r_refdef.view.frustum_y = tan(k * M_PI / 360.0);r_refdef.view.ortho_y = k;
805                 break;
806         case VF_ORIGIN:
807                 VectorCopy(f, cl.csqc_origin);
808                 CSQC_R_RecalcView();
809                 break;
810         case VF_ORIGIN_X:
811                 cl.csqc_origin[0] = k;
812                 CSQC_R_RecalcView();
813                 break;
814         case VF_ORIGIN_Y:
815                 cl.csqc_origin[1] = k;
816                 CSQC_R_RecalcView();
817                 break;
818         case VF_ORIGIN_Z:
819                 cl.csqc_origin[2] = k;
820                 CSQC_R_RecalcView();
821                 break;
822         case VF_ANGLES:
823                 VectorCopy(f, cl.csqc_angles);
824                 CSQC_R_RecalcView();
825                 break;
826         case VF_ANGLES_X:
827                 cl.csqc_angles[0] = k;
828                 CSQC_R_RecalcView();
829                 break;
830         case VF_ANGLES_Y:
831                 cl.csqc_angles[1] = k;
832                 CSQC_R_RecalcView();
833                 break;
834         case VF_ANGLES_Z:
835                 cl.csqc_angles[2] = k;
836                 CSQC_R_RecalcView();
837                 break;
838         case VF_DRAWWORLD:
839                 cl.csqc_vidvars.drawworld = k;
840                 break;
841         case VF_DRAWENGINESBAR:
842                 cl.csqc_vidvars.drawenginesbar = k;
843                 break;
844         case VF_DRAWCROSSHAIR:
845                 cl.csqc_vidvars.drawcrosshair = k;
846                 break;
847         case VF_CL_VIEWANGLES:
848                 VectorCopy(f, cl.viewangles);
849                 break;
850         case VF_CL_VIEWANGLES_X:
851                 cl.viewangles[0] = k;
852                 break;
853         case VF_CL_VIEWANGLES_Y:
854                 cl.viewangles[1] = k;
855                 break;
856         case VF_CL_VIEWANGLES_Z:
857                 cl.viewangles[2] = k;
858                 break;
859         case VF_PERSPECTIVE:
860                 r_refdef.view.useperspective = k != 0;
861                 break;
862         case VF_CLEARSCREEN:
863                 r_refdef.view.isoverlay = !k;
864                 break;
865         default:
866                 PRVM_G_FLOAT(OFS_RETURN) = 0;
867                 VM_Warning("VM_CL_R_SetView : unknown parm %i\n", c);
868                 return;
869         }
870         PRVM_G_FLOAT(OFS_RETURN) = 1;
871 }
872
873 //#305 void(vector org, float radius, vector lightcolours[, float style, string cubemapname, float pflags]) adddynamiclight (EXT_CSQC)
874 void VM_CL_R_AddDynamicLight (void)
875 {
876         vec_t *org;
877         float radius = 300;
878         vec_t *col;
879         int style = -1;
880         const char *cubemapname = NULL;
881         int pflags = PFLAGS_CORONA | PFLAGS_FULLDYNAMIC;
882         float coronaintensity = 1;
883         float coronasizescale = 0.25;
884         qboolean castshadow = true;
885         float ambientscale = 0;
886         float diffusescale = 1;
887         float specularscale = 1;
888         matrix4x4_t matrix;
889         vec3_t forward, left, up;
890         VM_SAFEPARMCOUNTRANGE(3, 8, VM_CL_R_AddDynamicLight);
891
892         // if we've run out of dlights, just return
893         if (r_refdef.scene.numlights >= MAX_DLIGHTS)
894                 return;
895
896         org = PRVM_G_VECTOR(OFS_PARM0);
897         radius = PRVM_G_FLOAT(OFS_PARM1);
898         col = PRVM_G_VECTOR(OFS_PARM2);
899         if (prog->argc >= 4)
900         {
901                 style = (int)PRVM_G_FLOAT(OFS_PARM3);
902                 if (style >= MAX_LIGHTSTYLES)
903                 {
904                         Con_DPrintf("VM_CL_R_AddDynamicLight: out of bounds lightstyle index %i\n", style);
905                         style = -1;
906                 }
907         }
908         if (prog->argc >= 5)
909                 cubemapname = PRVM_G_STRING(OFS_PARM4);
910         if (prog->argc >= 6)
911                 pflags = (int)PRVM_G_FLOAT(OFS_PARM5);
912         coronaintensity = (pflags & PFLAGS_CORONA) != 0;
913         castshadow = (pflags & PFLAGS_NOSHADOW) == 0;
914
915         VectorScale(prog->globals.client->v_forward, radius, forward);
916         VectorScale(prog->globals.client->v_right, -radius, left);
917         VectorScale(prog->globals.client->v_up, radius, up);
918         Matrix4x4_FromVectors(&matrix, forward, left, up, org);
919
920         R_RTLight_Update(&r_refdef.scene.lights[r_refdef.scene.numlights++], false, &matrix, col, style, cubemapname, castshadow, coronaintensity, coronasizescale, ambientscale, diffusescale, specularscale, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
921 }
922
923 //============================================================================
924
925 //#310 vector (vector v) cs_unproject (EXT_CSQC)
926 static void VM_CL_unproject (void)
927 {
928         float   *f;
929         vec3_t  temp;
930
931         VM_SAFEPARMCOUNT(1, VM_CL_unproject);
932         f = PRVM_G_VECTOR(OFS_PARM0);
933         if(v_flipped.integer)
934                 f[0] = r_refdef.view.x + r_refdef.view.width - f[0];
935         VectorSet(temp, f[2], (-1.0 + 2.0 * (f[0] - r_refdef.view.x)) / r_refdef.view.width * f[2] * -r_refdef.view.frustum_x, (-1.0 + 2.0 * (f[1] - r_refdef.view.y))  / r_refdef.view.height * f[2] * -r_refdef.view.frustum_y);
936         Matrix4x4_Transform(&r_refdef.view.matrix, temp, PRVM_G_VECTOR(OFS_RETURN));
937 }
938
939 //#311 vector (vector v) cs_project (EXT_CSQC)
940 static void VM_CL_project (void)
941 {
942         float   *f;
943         vec3_t  v;
944         matrix4x4_t m;
945
946         VM_SAFEPARMCOUNT(1, VM_CL_project);
947         f = PRVM_G_VECTOR(OFS_PARM0);
948         Matrix4x4_Invert_Simple(&m, &r_refdef.view.matrix);
949         Matrix4x4_Transform(&m, f, v);
950         if(v_flipped.integer)
951                 v[1] = -v[1];
952         VectorSet(PRVM_G_VECTOR(OFS_RETURN), r_refdef.view.x + r_refdef.view.width*0.5*(1.0+v[1]/v[0]/-r_refdef.view.frustum_x), r_refdef.view.y + r_refdef.view.height*0.5*(1.0+v[2]/v[0]/-r_refdef.view.frustum_y), v[0]);
953 }
954
955 //#330 float(float stnum) getstatf (EXT_CSQC)
956 static void VM_CL_getstatf (void)
957 {
958         int i;
959         union
960         {
961                 float f;
962                 int l;
963         }dat;
964         VM_SAFEPARMCOUNT(1, VM_CL_getstatf);
965         i = (int)PRVM_G_FLOAT(OFS_PARM0);
966         if(i < 0 || i >= MAX_CL_STATS)
967         {
968                 VM_Warning("VM_CL_getstatf: index>=MAX_CL_STATS or index<0\n");
969                 return;
970         }
971         dat.l = cl.stats[i];
972         PRVM_G_FLOAT(OFS_RETURN) =  dat.f;
973 }
974
975 //#331 float(float stnum) getstati (EXT_CSQC)
976 static void VM_CL_getstati (void)
977 {
978         int i, index;
979         int firstbit, bitcount;
980
981         VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_getstati);
982
983         index = (int)PRVM_G_FLOAT(OFS_PARM0);
984         if (prog->argc > 1)
985         {
986                 firstbit = (int)PRVM_G_FLOAT(OFS_PARM1);
987                 if (prog->argc > 2)
988                         bitcount = (int)PRVM_G_FLOAT(OFS_PARM2);
989                 else
990                         bitcount = 1;
991         }
992         else
993         {
994                 firstbit = 0;
995                 bitcount = 32;
996         }
997
998         if(index < 0 || index >= MAX_CL_STATS)
999         {
1000                 VM_Warning("VM_CL_getstati: index>=MAX_CL_STATS or index<0\n");
1001                 return;
1002         }
1003         i = cl.stats[index];
1004         if (bitcount != 32)     //32 causes the mask to overflow, so there's nothing to subtract from.
1005                 i = (((unsigned int)i)&(((1<<bitcount)-1)<<firstbit))>>firstbit;
1006         PRVM_G_FLOAT(OFS_RETURN) = i;
1007 }
1008
1009 //#332 string(float firststnum) getstats (EXT_CSQC)
1010 static void VM_CL_getstats (void)
1011 {
1012         int i;
1013         char t[17];
1014         VM_SAFEPARMCOUNT(1, VM_CL_getstats);
1015         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1016         if(i < 0 || i > MAX_CL_STATS-4)
1017         {
1018                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1019                 VM_Warning("VM_CL_getstats: index>MAX_CL_STATS-4 or index<0\n");
1020                 return;
1021         }
1022         strlcpy(t, (char*)&cl.stats[i], sizeof(t));
1023         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
1024 }
1025
1026 //#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
1027 static void VM_CL_setmodelindex (void)
1028 {
1029         int                             i;
1030         prvm_edict_t    *t;
1031         struct model_s  *model;
1032
1033         VM_SAFEPARMCOUNT(2, VM_CL_setmodelindex);
1034
1035         t = PRVM_G_EDICT(OFS_PARM0);
1036
1037         i = (int)PRVM_G_FLOAT(OFS_PARM1);
1038
1039         t->fields.client->model = 0;
1040         t->fields.client->modelindex = 0;
1041
1042         if (!i)
1043                 return;
1044
1045         model = CL_GetModelByIndex(i);
1046         if (!model)
1047         {
1048                 VM_Warning("VM_CL_setmodelindex: null model\n");
1049                 return;
1050         }
1051         t->fields.client->model = PRVM_SetEngineString(model->name);
1052         t->fields.client->modelindex = i;
1053
1054         // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
1055         if (model)
1056         {
1057                 SetMinMaxSize (t, model->normalmins, model->normalmaxs);
1058         }
1059         else
1060                 SetMinMaxSize (t, vec3_origin, vec3_origin);
1061 }
1062
1063 //#334 string(float mdlindex) modelnameforindex (EXT_CSQC)
1064 static void VM_CL_modelnameforindex (void)
1065 {
1066         dp_model_t *model;
1067
1068         VM_SAFEPARMCOUNT(1, VM_CL_modelnameforindex);
1069
1070         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1071         model = CL_GetModelByIndex((int)PRVM_G_FLOAT(OFS_PARM0));
1072         PRVM_G_INT(OFS_RETURN) = model ? PRVM_SetEngineString(model->name) : 0;
1073 }
1074
1075 //#335 float(string effectname) particleeffectnum (EXT_CSQC)
1076 static void VM_CL_particleeffectnum (void)
1077 {
1078         int                     i;
1079         VM_SAFEPARMCOUNT(1, VM_CL_particleeffectnum);
1080         i = CL_ParticleEffectIndexForName(PRVM_G_STRING(OFS_PARM0));
1081         if (i == 0)
1082                 i = -1;
1083         PRVM_G_FLOAT(OFS_RETURN) = i;
1084 }
1085
1086 // #336 void(entity ent, float effectnum, vector start, vector end[, float color]) trailparticles (EXT_CSQC)
1087 static void VM_CL_trailparticles (void)
1088 {
1089         int                             i;
1090         float                   *start, *end;
1091         prvm_edict_t    *t;
1092         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_trailparticles);
1093
1094         t = PRVM_G_EDICT(OFS_PARM0);
1095         i               = (int)PRVM_G_FLOAT(OFS_PARM1);
1096         start   = PRVM_G_VECTOR(OFS_PARM2);
1097         end             = PRVM_G_VECTOR(OFS_PARM3);
1098
1099         if (i < 0)
1100                 return;
1101         CL_ParticleEffect(i, VectorDistance(start, end), start, end, t->fields.client->velocity, t->fields.client->velocity, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0);
1102 }
1103
1104 //#337 void(float effectnum, vector origin, vector dir, float count[, float color]) pointparticles (EXT_CSQC)
1105 static void VM_CL_pointparticles (void)
1106 {
1107         int                     i, n;
1108         float           *f, *v;
1109         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_pointparticles);
1110         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1111         f = PRVM_G_VECTOR(OFS_PARM1);
1112         v = PRVM_G_VECTOR(OFS_PARM2);
1113         n = (int)PRVM_G_FLOAT(OFS_PARM3);
1114         if (i < 0)
1115                 return;
1116         CL_ParticleEffect(i, n, f, f, v, v, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0);
1117 }
1118
1119 //#342 string(float keynum) getkeybind (EXT_CSQC)
1120 static void VM_CL_getkeybind (void)
1121 {
1122         VM_SAFEPARMCOUNT(1, VM_CL_getkeybind);
1123         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_GetBind((int)PRVM_G_FLOAT(OFS_PARM0)));
1124 }
1125
1126 //#343 void(float usecursor) setcursormode (EXT_CSQC)
1127 static void VM_CL_setcursormode (void)
1128 {
1129         VM_SAFEPARMCOUNT(1, VM_CL_setcursormode);
1130         cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0);
1131         cl_ignoremousemoves = 2;
1132 }
1133
1134 //#344 vector() getmousepos (EXT_CSQC)
1135 static void VM_CL_getmousepos(void)
1136 {
1137         VM_SAFEPARMCOUNT(0,VM_CL_getmousepos);
1138
1139         if (key_consoleactive || key_dest != key_game)
1140                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), 0, 0, 0);
1141         else if (cl.csqc_wantsmousemove)
1142                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_windowmouse_x * vid_conwidth.integer / vid.width, in_windowmouse_y * vid_conheight.integer / vid.height, 0);
1143         else
1144                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_mouse_x * vid_conwidth.integer / vid.width, in_mouse_y * vid_conheight.integer / vid.height, 0);
1145 }
1146
1147 //#345 float(float framenum) getinputstate (EXT_CSQC)
1148 static void VM_CL_getinputstate (void)
1149 {
1150         int i, frame;
1151         VM_SAFEPARMCOUNT(1, VM_CL_getinputstate);
1152         frame = (int)PRVM_G_FLOAT(OFS_PARM0);
1153         for (i = 0;i < CL_MAX_USERCMDS;i++)
1154         {
1155                 if (cl.movecmd[i].sequence == frame)
1156                 {
1157                         VectorCopy(cl.movecmd[i].viewangles, prog->globals.client->input_angles);
1158                         prog->globals.client->input_buttons = cl.movecmd[i].buttons; // FIXME: this should not be directly exposed to csqc (translation layer needed?)
1159                         prog->globals.client->input_movevalues[0] = cl.movecmd[i].forwardmove;
1160                         prog->globals.client->input_movevalues[1] = cl.movecmd[i].sidemove;
1161                         prog->globals.client->input_movevalues[2] = cl.movecmd[i].upmove;
1162                         prog->globals.client->input_timelength = cl.movecmd[i].frametime;
1163                         if(cl.movecmd[i].crouch)
1164                         {
1165                                 VectorCopy(cl.playercrouchmins, prog->globals.client->pmove_mins);
1166                                 VectorCopy(cl.playercrouchmaxs, prog->globals.client->pmove_maxs);
1167                         }
1168                         else
1169                         {
1170                                 VectorCopy(cl.playerstandmins, prog->globals.client->pmove_mins);
1171                                 VectorCopy(cl.playerstandmaxs, prog->globals.client->pmove_maxs);
1172                         }
1173                 }
1174         }
1175 }
1176
1177 //#346 void(float sens) setsensitivityscaler (EXT_CSQC)
1178 static void VM_CL_setsensitivityscale (void)
1179 {
1180         VM_SAFEPARMCOUNT(1, VM_CL_setsensitivityscale);
1181         cl.sensitivityscale = PRVM_G_FLOAT(OFS_PARM0);
1182 }
1183
1184 //#347 void() runstandardplayerphysics (EXT_CSQC)
1185 static void VM_CL_runplayerphysics (void)
1186 {
1187 }
1188
1189 //#348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
1190 static void VM_CL_getplayerkey (void)
1191 {
1192         int                     i;
1193         char            t[128];
1194         const char      *c;
1195
1196         VM_SAFEPARMCOUNT(2, VM_CL_getplayerkey);
1197
1198         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1199         c = PRVM_G_STRING(OFS_PARM1);
1200         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1201         Sbar_SortFrags();
1202
1203         if (i < 0)
1204                 i = Sbar_GetSortedPlayerIndex(-1-i);
1205         if(i < 0 || i >= cl.maxclients)
1206                 return;
1207
1208         t[0] = 0;
1209
1210         if(!strcasecmp(c, "name"))
1211                 strlcpy(t, cl.scores[i].name, sizeof(t));
1212         else
1213                 if(!strcasecmp(c, "frags"))
1214                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].frags);
1215         else
1216                 if(!strcasecmp(c, "ping"))
1217                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_ping);
1218         else
1219                 if(!strcasecmp(c, "pl"))
1220                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_packetloss);
1221         else
1222                 if(!strcasecmp(c, "entertime"))
1223                         dpsnprintf(t, sizeof(t), "%f", cl.scores[i].qw_entertime);
1224         else
1225                 if(!strcasecmp(c, "colors"))
1226                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors);
1227         else
1228                 if(!strcasecmp(c, "topcolor"))
1229                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors & 0xf0);
1230         else
1231                 if(!strcasecmp(c, "bottomcolor"))
1232                         dpsnprintf(t, sizeof(t), "%i", (cl.scores[i].colors &15)<<4);
1233         else
1234                 if(!strcasecmp(c, "viewentity"))
1235                         dpsnprintf(t, sizeof(t), "%i", i+1);
1236         if(!t[0])
1237                 return;
1238         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
1239 }
1240
1241 //#349 float() isdemo (EXT_CSQC)
1242 static void VM_CL_isdemo (void)
1243 {
1244         VM_SAFEPARMCOUNT(0, VM_CL_isdemo);
1245         PRVM_G_FLOAT(OFS_RETURN) = cls.demoplayback;
1246 }
1247
1248 //#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
1249 static void VM_CL_setlistener (void)
1250 {
1251         VM_SAFEPARMCOUNT(4, VM_CL_setlistener);
1252         Matrix4x4_FromVectors(&cl.csqc_listenermatrix, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), PRVM_G_VECTOR(OFS_PARM3), PRVM_G_VECTOR(OFS_PARM0));
1253         cl.csqc_usecsqclistener = true; //use csqc listener at this frame
1254 }
1255
1256 //#352 void(string cmdname) registercommand (EXT_CSQC)
1257 static void VM_CL_registercmd (void)
1258 {
1259         char *t;
1260         VM_SAFEPARMCOUNT(1, VM_CL_registercmd);
1261         if(!Cmd_Exists(PRVM_G_STRING(OFS_PARM0)))
1262         {
1263                 size_t alloclen;
1264
1265                 alloclen = strlen(PRVM_G_STRING(OFS_PARM0)) + 1;
1266                 t = (char *)Z_Malloc(alloclen);
1267                 memcpy(t, PRVM_G_STRING(OFS_PARM0), alloclen);
1268                 Cmd_AddCommand(t, NULL, "console command created by QuakeC");
1269         }
1270         else
1271                 Cmd_AddCommand(PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
1272
1273 }
1274
1275 //#360 float() readbyte (EXT_CSQC)
1276 static void VM_CL_ReadByte (void)
1277 {
1278         VM_SAFEPARMCOUNT(0, VM_CL_ReadByte);
1279         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadByte();
1280 }
1281
1282 //#361 float() readchar (EXT_CSQC)
1283 static void VM_CL_ReadChar (void)
1284 {
1285         VM_SAFEPARMCOUNT(0, VM_CL_ReadChar);
1286         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadChar();
1287 }
1288
1289 //#362 float() readshort (EXT_CSQC)
1290 static void VM_CL_ReadShort (void)
1291 {
1292         VM_SAFEPARMCOUNT(0, VM_CL_ReadShort);
1293         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadShort();
1294 }
1295
1296 //#363 float() readlong (EXT_CSQC)
1297 static void VM_CL_ReadLong (void)
1298 {
1299         VM_SAFEPARMCOUNT(0, VM_CL_ReadLong);
1300         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadLong();
1301 }
1302
1303 //#364 float() readcoord (EXT_CSQC)
1304 static void VM_CL_ReadCoord (void)
1305 {
1306         VM_SAFEPARMCOUNT(0, VM_CL_ReadCoord);
1307         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadCoord(cls.protocol);
1308 }
1309
1310 //#365 float() readangle (EXT_CSQC)
1311 static void VM_CL_ReadAngle (void)
1312 {
1313         VM_SAFEPARMCOUNT(0, VM_CL_ReadAngle);
1314         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadAngle(cls.protocol);
1315 }
1316
1317 //#366 string() readstring (EXT_CSQC)
1318 static void VM_CL_ReadString (void)
1319 {
1320         VM_SAFEPARMCOUNT(0, VM_CL_ReadString);
1321         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(MSG_ReadString());
1322 }
1323
1324 //#367 float() readfloat (EXT_CSQC)
1325 static void VM_CL_ReadFloat (void)
1326 {
1327         VM_SAFEPARMCOUNT(0, VM_CL_ReadFloat);
1328         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat();
1329 }
1330
1331 //#501 string() readpicture (DP_CSQC_READWRITEPICTURE)
1332 extern cvar_t cl_readpicture_force;
1333 static void VM_CL_ReadPicture (void)
1334 {
1335         const char *name;
1336         unsigned char *data;
1337         unsigned char *buf;
1338         int size;
1339         int i;
1340         cachepic_t *pic;
1341
1342         VM_SAFEPARMCOUNT(0, VM_CL_ReadPicture);
1343
1344         name = MSG_ReadString();
1345         size = MSG_ReadShort();
1346
1347         // check if a texture of that name exists
1348         // if yes, it is used and the data is discarded
1349         // if not, the (low quality) data is used to build a new texture, whose name will get returned
1350
1351         pic = Draw_CachePic_Flags (name, CACHEPICFLAG_NOTPERSISTENT);
1352
1353         if(size)
1354         {
1355                 if(pic->tex == r_texture_notexture)
1356                         pic->tex = NULL; // don't overwrite the notexture by Draw_NewPic
1357                 if(pic->tex && !cl_readpicture_force.integer)
1358                 {
1359                         // texture found and loaded
1360                         // skip over the jpeg as we don't need it
1361                         for(i = 0; i < size; ++i)
1362                                 MSG_ReadByte();
1363                 }
1364                 else
1365                 {
1366                         // texture not found
1367                         // use the attached jpeg as texture
1368                         buf = (unsigned char *) Mem_Alloc(tempmempool, size);
1369                         MSG_ReadBytes(size, buf);
1370                         data = JPEG_LoadImage_BGRA(buf, size);
1371                         Mem_Free(buf);
1372                         Draw_NewPic(name, image_width, image_height, false, data);
1373                         Mem_Free(data);
1374                 }
1375         }
1376
1377         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(name);
1378 }
1379
1380 //////////////////////////////////////////////////////////
1381
1382 static void VM_CL_makestatic (void)
1383 {
1384         prvm_edict_t *ent;
1385
1386         VM_SAFEPARMCOUNT(1, VM_CL_makestatic);
1387
1388         ent = PRVM_G_EDICT(OFS_PARM0);
1389         if (ent == prog->edicts)
1390         {
1391                 VM_Warning("makestatic: can not modify world entity\n");
1392                 return;
1393         }
1394         if (ent->priv.server->free)
1395         {
1396                 VM_Warning("makestatic: can not modify free entity\n");
1397                 return;
1398         }
1399
1400         if (cl.num_static_entities < cl.max_static_entities)
1401         {
1402                 int renderflags;
1403                 prvm_eval_t *val;
1404                 entity_t *staticent = &cl.static_entities[cl.num_static_entities++];
1405
1406                 // copy it to the current state
1407                 memset(staticent, 0, sizeof(*staticent));
1408                 staticent->render.model = CL_GetModelByIndex((int)ent->fields.client->modelindex);
1409                 staticent->render.frame1 = staticent->render.frame2 = (int)ent->fields.client->frame;
1410                 staticent->render.framelerp = 0;
1411                 // make torchs play out of sync
1412                 staticent->render.frame1time = staticent->render.frame2time = lhrandom(-10, -1);
1413                 staticent->render.skinnum = (int)ent->fields.client->skin;
1414                 staticent->render.effects = (int)ent->fields.client->effects;
1415                 staticent->render.alpha = 1;
1416                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)) && val->_float) staticent->render.alpha = val->_float;
1417                 staticent->render.scale = 1;
1418                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)) && val->_float) staticent->render.scale = val->_float;
1419                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod)) && VectorLength2(val->vector)) VectorCopy(val->vector, staticent->render.colormod);
1420
1421                 renderflags = 0;
1422                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderflags)) && val->_float) renderflags = (int)val->_float;
1423                 if (renderflags & RF_USEAXIS)
1424                 {
1425                         vec3_t left;
1426                         VectorNegate(prog->globals.client->v_right, left);
1427                         Matrix4x4_FromVectors(&staticent->render.matrix, prog->globals.client->v_forward, left, prog->globals.client->v_up, ent->fields.client->origin);
1428                         Matrix4x4_Scale(&staticent->render.matrix, staticent->render.scale, 1);
1429                 }
1430                 else
1431                         Matrix4x4_CreateFromQuakeEntity(&staticent->render.matrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], staticent->render.scale);
1432
1433                 // either fullbright or lit
1434                 if (!(staticent->render.effects & EF_FULLBRIGHT) && !r_fullbright.integer)
1435                         staticent->render.flags |= RENDER_LIGHT;
1436                 // turn off shadows from transparent objects
1437                 if (!(staticent->render.effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST)) && (staticent->render.alpha >= 1))
1438                         staticent->render.flags |= RENDER_SHADOW;
1439
1440                 CL_UpdateRenderEntity(&staticent->render);
1441         }
1442         else
1443                 Con_Printf("Too many static entities");
1444
1445 // throw the entity away now
1446         PRVM_ED_Free (ent);
1447 }
1448
1449 //=================================================================//
1450
1451 /*
1452 =================
1453 VM_CL_copyentity
1454
1455 copies data from one entity to another
1456
1457 copyentity(src, dst)
1458 =================
1459 */
1460 static void VM_CL_copyentity (void)
1461 {
1462         prvm_edict_t *in, *out;
1463         VM_SAFEPARMCOUNT(2, VM_CL_copyentity);
1464         in = PRVM_G_EDICT(OFS_PARM0);
1465         if (in == prog->edicts)
1466         {
1467                 VM_Warning("copyentity: can not read world entity\n");
1468                 return;
1469         }
1470         if (in->priv.server->free)
1471         {
1472                 VM_Warning("copyentity: can not read free entity\n");
1473                 return;
1474         }
1475         out = PRVM_G_EDICT(OFS_PARM1);
1476         if (out == prog->edicts)
1477         {
1478                 VM_Warning("copyentity: can not modify world entity\n");
1479                 return;
1480         }
1481         if (out->priv.server->free)
1482         {
1483                 VM_Warning("copyentity: can not modify free entity\n");
1484                 return;
1485         }
1486         memcpy(out->fields.vp, in->fields.vp, prog->progs->entityfields * 4);
1487         CL_LinkEdict(out);
1488 }
1489
1490 //=================================================================//
1491
1492 // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
1493 static void VM_CL_effect (void)
1494 {
1495         VM_SAFEPARMCOUNT(5, VM_CL_effect);
1496         CL_Effect(PRVM_G_VECTOR(OFS_PARM0), (int)PRVM_G_FLOAT(OFS_PARM1), (int)PRVM_G_FLOAT(OFS_PARM2), (int)PRVM_G_FLOAT(OFS_PARM3), PRVM_G_FLOAT(OFS_PARM4));
1497 }
1498
1499 // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
1500 static void VM_CL_te_blood (void)
1501 {
1502         float   *pos;
1503         vec3_t  pos2;
1504         VM_SAFEPARMCOUNT(3, VM_CL_te_blood);
1505         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
1506                 return;
1507         pos = PRVM_G_VECTOR(OFS_PARM0);
1508         CL_FindNonSolidLocation(pos, pos2, 4);
1509         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1510 }
1511
1512 // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
1513 static void VM_CL_te_bloodshower (void)
1514 {
1515         vec_t speed;
1516         vec3_t vel1, vel2;
1517         VM_SAFEPARMCOUNT(4, VM_CL_te_bloodshower);
1518         if (PRVM_G_FLOAT(OFS_PARM3) < 1)
1519                 return;
1520         speed = PRVM_G_FLOAT(OFS_PARM2);
1521         vel1[0] = -speed;
1522         vel1[1] = -speed;
1523         vel1[2] = -speed;
1524         vel2[0] = speed;
1525         vel2[1] = speed;
1526         vel2[2] = speed;
1527         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM3), PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), vel1, vel2, NULL, 0);
1528 }
1529
1530 // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
1531 static void VM_CL_te_explosionrgb (void)
1532 {
1533         float           *pos;
1534         vec3_t          pos2;
1535         matrix4x4_t     tempmatrix;
1536         VM_SAFEPARMCOUNT(2, VM_CL_te_explosionrgb);
1537         pos = PRVM_G_VECTOR(OFS_PARM0);
1538         CL_FindNonSolidLocation(pos, pos2, 10);
1539         CL_ParticleExplosion(pos2);
1540         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1541         CL_AllocLightFlash(NULL, &tempmatrix, 350, PRVM_G_VECTOR(OFS_PARM1)[0], PRVM_G_VECTOR(OFS_PARM1)[1], PRVM_G_VECTOR(OFS_PARM1)[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1542 }
1543
1544 // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
1545 static void VM_CL_te_particlecube (void)
1546 {
1547         VM_SAFEPARMCOUNT(7, VM_CL_te_particlecube);
1548         CL_ParticleCube(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4), PRVM_G_FLOAT(OFS_PARM5), PRVM_G_FLOAT(OFS_PARM6));
1549 }
1550
1551 // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
1552 static void VM_CL_te_particlerain (void)
1553 {
1554         VM_SAFEPARMCOUNT(5, VM_CL_te_particlerain);
1555         CL_ParticleRain(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4), 0);
1556 }
1557
1558 // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
1559 static void VM_CL_te_particlesnow (void)
1560 {
1561         VM_SAFEPARMCOUNT(5, VM_CL_te_particlesnow);
1562         CL_ParticleRain(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4), 1);
1563 }
1564
1565 // #411 void(vector org, vector vel, float howmany) te_spark
1566 static void VM_CL_te_spark (void)
1567 {
1568         float           *pos;
1569         vec3_t          pos2;
1570         VM_SAFEPARMCOUNT(3, VM_CL_te_spark);
1571
1572         pos = PRVM_G_VECTOR(OFS_PARM0);
1573         CL_FindNonSolidLocation(pos, pos2, 4);
1574         CL_ParticleEffect(EFFECT_TE_SPARK, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1575 }
1576
1577 extern cvar_t cl_sound_ric_gunshot;
1578 // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
1579 static void VM_CL_te_gunshotquad (void)
1580 {
1581         float           *pos;
1582         vec3_t          pos2;
1583         int                     rnd;
1584         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshotquad);
1585
1586         pos = PRVM_G_VECTOR(OFS_PARM0);
1587         CL_FindNonSolidLocation(pos, pos2, 4);
1588         CL_ParticleEffect(EFFECT_TE_GUNSHOTQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1589         if(cl_sound_ric_gunshot.integer >= 2)
1590         {
1591                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1592                 else
1593                 {
1594                         rnd = rand() & 3;
1595                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1596                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1597                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1598                 }
1599         }
1600 }
1601
1602 // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
1603 static void VM_CL_te_spikequad (void)
1604 {
1605         float           *pos;
1606         vec3_t          pos2;
1607         int                     rnd;
1608         VM_SAFEPARMCOUNT(1, VM_CL_te_spikequad);
1609
1610         pos = PRVM_G_VECTOR(OFS_PARM0);
1611         CL_FindNonSolidLocation(pos, pos2, 4);
1612         CL_ParticleEffect(EFFECT_TE_SPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1613         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1614         else
1615         {
1616                 rnd = rand() & 3;
1617                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1618                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1619                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1620         }
1621 }
1622
1623 // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1)
1624 static void VM_CL_te_superspikequad (void)
1625 {
1626         float           *pos;
1627         vec3_t          pos2;
1628         int                     rnd;
1629         VM_SAFEPARMCOUNT(1, VM_CL_te_superspikequad);
1630
1631         pos = PRVM_G_VECTOR(OFS_PARM0);
1632         CL_FindNonSolidLocation(pos, pos2, 4);
1633         CL_ParticleEffect(EFFECT_TE_SUPERSPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1634         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
1635         else
1636         {
1637                 rnd = rand() & 3;
1638                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1639                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1640                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1641         }
1642 }
1643
1644 // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1)
1645 static void VM_CL_te_explosionquad (void)
1646 {
1647         float           *pos;
1648         vec3_t          pos2;
1649         VM_SAFEPARMCOUNT(1, VM_CL_te_explosionquad);
1650
1651         pos = PRVM_G_VECTOR(OFS_PARM0);
1652         CL_FindNonSolidLocation(pos, pos2, 10);
1653         CL_ParticleEffect(EFFECT_TE_EXPLOSIONQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1654         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1655 }
1656
1657 // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
1658 static void VM_CL_te_smallflash (void)
1659 {
1660         float           *pos;
1661         vec3_t          pos2;
1662         VM_SAFEPARMCOUNT(1, VM_CL_te_smallflash);
1663
1664         pos = PRVM_G_VECTOR(OFS_PARM0);
1665         CL_FindNonSolidLocation(pos, pos2, 10);
1666         CL_ParticleEffect(EFFECT_TE_SMALLFLASH, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1667 }
1668
1669 // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
1670 static void VM_CL_te_customflash (void)
1671 {
1672         float           *pos;
1673         vec3_t          pos2;
1674         matrix4x4_t     tempmatrix;
1675         VM_SAFEPARMCOUNT(4, VM_CL_te_customflash);
1676
1677         pos = PRVM_G_VECTOR(OFS_PARM0);
1678         CL_FindNonSolidLocation(pos, pos2, 4);
1679         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1680         CL_AllocLightFlash(NULL, &tempmatrix, PRVM_G_FLOAT(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM3)[0], PRVM_G_VECTOR(OFS_PARM3)[1], PRVM_G_VECTOR(OFS_PARM3)[2], PRVM_G_FLOAT(OFS_PARM1) / PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM2), 0, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1681 }
1682
1683 // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
1684 static void VM_CL_te_gunshot (void)
1685 {
1686         float           *pos;
1687         vec3_t          pos2;
1688         int                     rnd;
1689         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshot);
1690
1691         pos = PRVM_G_VECTOR(OFS_PARM0);
1692         CL_FindNonSolidLocation(pos, pos2, 4);
1693         CL_ParticleEffect(EFFECT_TE_GUNSHOT, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1694         if(cl_sound_ric_gunshot.integer == 1 || cl_sound_ric_gunshot.integer == 3)
1695         {
1696                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1697                 else
1698                 {
1699                         rnd = rand() & 3;
1700                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1701                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1702                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1703                 }
1704         }
1705 }
1706
1707 // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
1708 static void VM_CL_te_spike (void)
1709 {
1710         float           *pos;
1711         vec3_t          pos2;
1712         int                     rnd;
1713         VM_SAFEPARMCOUNT(1, VM_CL_te_spike);
1714
1715         pos = PRVM_G_VECTOR(OFS_PARM0);
1716         CL_FindNonSolidLocation(pos, pos2, 4);
1717         CL_ParticleEffect(EFFECT_TE_SPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1718         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1719         else
1720         {
1721                 rnd = rand() & 3;
1722                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1723                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1724                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1725         }
1726 }
1727
1728 // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
1729 static void VM_CL_te_superspike (void)
1730 {
1731         float           *pos;
1732         vec3_t          pos2;
1733         int                     rnd;
1734         VM_SAFEPARMCOUNT(1, VM_CL_te_superspike);
1735
1736         pos = PRVM_G_VECTOR(OFS_PARM0);
1737         CL_FindNonSolidLocation(pos, pos2, 4);
1738         CL_ParticleEffect(EFFECT_TE_SUPERSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1739         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1740         else
1741         {
1742                 rnd = rand() & 3;
1743                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1744                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1745                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1746         }
1747 }
1748
1749 // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
1750 static void VM_CL_te_explosion (void)
1751 {
1752         float           *pos;
1753         vec3_t          pos2;
1754         VM_SAFEPARMCOUNT(1, VM_CL_te_explosion);
1755
1756         pos = PRVM_G_VECTOR(OFS_PARM0);
1757         CL_FindNonSolidLocation(pos, pos2, 10);
1758         CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1759         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1760 }
1761
1762 // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
1763 static void VM_CL_te_tarexplosion (void)
1764 {
1765         float           *pos;
1766         vec3_t          pos2;
1767         VM_SAFEPARMCOUNT(1, VM_CL_te_tarexplosion);
1768
1769         pos = PRVM_G_VECTOR(OFS_PARM0);
1770         CL_FindNonSolidLocation(pos, pos2, 10);
1771         CL_ParticleEffect(EFFECT_TE_TAREXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1772         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1773 }
1774
1775 // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
1776 static void VM_CL_te_wizspike (void)
1777 {
1778         float           *pos;
1779         vec3_t          pos2;
1780         VM_SAFEPARMCOUNT(1, VM_CL_te_wizspike);
1781
1782         pos = PRVM_G_VECTOR(OFS_PARM0);
1783         CL_FindNonSolidLocation(pos, pos2, 4);
1784         CL_ParticleEffect(EFFECT_TE_WIZSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1785         S_StartSound(-1, 0, cl.sfx_wizhit, pos2, 1, 1);
1786 }
1787
1788 // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
1789 static void VM_CL_te_knightspike (void)
1790 {
1791         float           *pos;
1792         vec3_t          pos2;
1793         VM_SAFEPARMCOUNT(1, VM_CL_te_knightspike);
1794
1795         pos = PRVM_G_VECTOR(OFS_PARM0);
1796         CL_FindNonSolidLocation(pos, pos2, 4);
1797         CL_ParticleEffect(EFFECT_TE_KNIGHTSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1798         S_StartSound(-1, 0, cl.sfx_knighthit, pos2, 1, 1);
1799 }
1800
1801 // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
1802 static void VM_CL_te_lavasplash (void)
1803 {
1804         VM_SAFEPARMCOUNT(1, VM_CL_te_lavasplash);
1805         CL_ParticleEffect(EFFECT_TE_LAVASPLASH, 1, PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM0), vec3_origin, vec3_origin, NULL, 0);
1806 }
1807
1808 // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
1809 static void VM_CL_te_teleport (void)
1810 {
1811         VM_SAFEPARMCOUNT(1, VM_CL_te_teleport);
1812         CL_ParticleEffect(EFFECT_TE_TELEPORT, 1, PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM0), vec3_origin, vec3_origin, NULL, 0);
1813 }
1814
1815 // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
1816 static void VM_CL_te_explosion2 (void)
1817 {
1818         float           *pos;
1819         vec3_t          pos2, color;
1820         matrix4x4_t     tempmatrix;
1821         int                     colorStart, colorLength;
1822         unsigned char           *tempcolor;
1823         VM_SAFEPARMCOUNT(3, VM_CL_te_explosion2);
1824
1825         pos = PRVM_G_VECTOR(OFS_PARM0);
1826         colorStart = (int)PRVM_G_FLOAT(OFS_PARM1);
1827         colorLength = (int)PRVM_G_FLOAT(OFS_PARM2);
1828         CL_FindNonSolidLocation(pos, pos2, 10);
1829         CL_ParticleExplosion2(pos2, colorStart, colorLength);
1830         tempcolor = palette_rgb[(rand()%colorLength) + colorStart];
1831         color[0] = tempcolor[0] * (2.0f / 255.0f);
1832         color[1] = tempcolor[1] * (2.0f / 255.0f);
1833         color[2] = tempcolor[2] * (2.0f / 255.0f);
1834         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1835         CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1836         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1837 }
1838
1839
1840 // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
1841 static void VM_CL_te_lightning1 (void)
1842 {
1843         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning1);
1844         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt, true);
1845 }
1846
1847 // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
1848 static void VM_CL_te_lightning2 (void)
1849 {
1850         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning2);
1851         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt2, true);
1852 }
1853
1854 // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
1855 static void VM_CL_te_lightning3 (void)
1856 {
1857         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning3);
1858         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt3, false);
1859 }
1860
1861 // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
1862 static void VM_CL_te_beam (void)
1863 {
1864         VM_SAFEPARMCOUNT(3, VM_CL_te_beam);
1865         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_beam, false);
1866 }
1867
1868 // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
1869 static void VM_CL_te_plasmaburn (void)
1870 {
1871         float           *pos;
1872         vec3_t          pos2;
1873         VM_SAFEPARMCOUNT(1, VM_CL_te_plasmaburn);
1874
1875         pos = PRVM_G_VECTOR(OFS_PARM0);
1876         CL_FindNonSolidLocation(pos, pos2, 4);
1877         CL_ParticleEffect(EFFECT_TE_PLASMABURN, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1878 }
1879
1880 // #457 void(vector org, vector velocity, float howmany) te_flamejet (DP_TE_FLAMEJET)
1881 static void VM_CL_te_flamejet (void)
1882 {
1883         float *pos;
1884         vec3_t pos2;
1885         VM_SAFEPARMCOUNT(3, VM_CL_te_flamejet);
1886         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
1887                 return;
1888         pos = PRVM_G_VECTOR(OFS_PARM0);
1889         CL_FindNonSolidLocation(pos, pos2, 4);
1890         CL_ParticleEffect(EFFECT_TE_FLAMEJET, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1891 }
1892
1893
1894 //====================================================================
1895 //DP_QC_GETSURFACE
1896
1897 extern void clippointtosurface(dp_model_t *model, msurface_t *surface, vec3_t p, vec3_t out);
1898
1899 static msurface_t *cl_getsurface(dp_model_t *model, int surfacenum)
1900 {
1901         if (surfacenum < 0 || surfacenum >= model->nummodelsurfaces)
1902                 return NULL;
1903         return model->data_surfaces + surfacenum + model->firstmodelsurface;
1904 }
1905
1906 // #434 float(entity e, float s) getsurfacenumpoints
1907 static void VM_CL_getsurfacenumpoints(void)
1908 {
1909         dp_model_t *model;
1910         msurface_t *surface;
1911         VM_SAFEPARMCOUNT(2, VM_CL_getsurfacenumpoints);
1912         // return 0 if no such surface
1913         if (!(model = CL_GetModelFromEdict(PRVM_G_EDICT(OFS_PARM0))) || !(surface = cl_getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
1914         {
1915                 PRVM_G_FLOAT(OFS_RETURN) = 0;
1916                 return;
1917         }
1918
1919         // note: this (incorrectly) assumes it is a simple polygon
1920         PRVM_G_FLOAT(OFS_RETURN) = surface->num_vertices;
1921 }
1922
1923 // #435 vector(entity e, float s, float n) getsurfacepoint
1924 static void VM_CL_getsurfacepoint(void)
1925 {
1926         prvm_edict_t *ed;
1927         dp_model_t *model;
1928         msurface_t *surface;
1929         int pointnum;
1930         VM_SAFEPARMCOUNT(3, VM_CL_getsurfacenumpoints);
1931         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
1932         ed = PRVM_G_EDICT(OFS_PARM0);
1933         if (!(model = CL_GetModelFromEdict(ed)) || !(surface = cl_getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
1934                 return;
1935         // note: this (incorrectly) assumes it is a simple polygon
1936         pointnum = (int)PRVM_G_FLOAT(OFS_PARM2);
1937         if (pointnum < 0 || pointnum >= surface->num_vertices)
1938                 return;
1939         // FIXME: implement rotation/scaling
1940         VectorAdd(&(model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed->fields.client->origin, PRVM_G_VECTOR(OFS_RETURN));
1941 }
1942 //PF_getsurfacepointattribute,     // #486 vector(entity e, float s, float n, float a) getsurfacepointattribute = #486;
1943 // float SPA_POSITION = 0;
1944 // float SPA_S_AXIS = 1;
1945 // float SPA_T_AXIS = 2;
1946 // float SPA_R_AXIS = 3; // same as SPA_NORMAL
1947 // float SPA_TEXCOORDS0 = 4;
1948 // float SPA_LIGHTMAP0_TEXCOORDS = 5;
1949 // float SPA_LIGHTMAP0_COLOR = 6;
1950 // TODO: add some wrapper code and merge VM_CL/SV_getsurface* [12/16/2007 Black]
1951 static void VM_CL_getsurfacepointattribute(void)
1952 {
1953         prvm_edict_t *ed;
1954         dp_model_t *model;
1955         msurface_t *surface;
1956         int pointnum;
1957         int attributetype;
1958
1959         VM_SAFEPARMCOUNT(4, VM_CL_getsurfacenumpoints);
1960         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
1961         ed = PRVM_G_EDICT(OFS_PARM0);
1962         if (!(model = CL_GetModelFromEdict(ed)) || !(surface = cl_getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
1963                 return;
1964         // note: this (incorrectly) assumes it is a simple polygon
1965         pointnum = (int)PRVM_G_FLOAT(OFS_PARM2);
1966         if (pointnum < 0 || pointnum >= surface->num_vertices)
1967                 return;
1968
1969         // FIXME: implement rotation/scaling
1970         attributetype = (int) PRVM_G_FLOAT(OFS_PARM3);
1971
1972         switch( attributetype ) {
1973                 // float SPA_POSITION = 0;
1974                 case 0:
1975                         VectorAdd(&(model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed->fields.client->origin, PRVM_G_VECTOR(OFS_RETURN));
1976                         break;
1977                 // float SPA_S_AXIS = 1;
1978                 case 1:
1979                         VectorCopy(&(model->surfmesh.data_svector3f + 3 * surface->num_firstvertex)[pointnum * 3], PRVM_G_VECTOR(OFS_RETURN));
1980                         break;
1981                 // float SPA_T_AXIS = 2;
1982                 case 2:
1983                         VectorCopy(&(model->surfmesh.data_tvector3f + 3 * surface->num_firstvertex)[pointnum * 3], PRVM_G_VECTOR(OFS_RETURN));
1984                         break;
1985                 // float SPA_R_AXIS = 3; // same as SPA_NORMAL
1986                 case 3:
1987                         VectorCopy(&(model->surfmesh.data_normal3f + 3 * surface->num_firstvertex)[pointnum * 3], PRVM_G_VECTOR(OFS_RETURN));
1988                         break;
1989                 // float SPA_TEXCOORDS0 = 4;
1990                 case 4: {
1991                         float *ret = PRVM_G_VECTOR(OFS_RETURN);
1992                         float *texcoord = &(model->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[pointnum * 2];
1993                         ret[0] = texcoord[0];
1994                         ret[1] = texcoord[1];
1995                         ret[2] = 0.0f;
1996                         break;
1997                 }
1998                 // float SPA_LIGHTMAP0_TEXCOORDS = 5;
1999                 case 5: {
2000                         float *ret = PRVM_G_VECTOR(OFS_RETURN);
2001                         float *texcoord = &(model->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[pointnum * 2];
2002                         ret[0] = texcoord[0];
2003                         ret[1] = texcoord[1];
2004                         ret[2] = 0.0f;
2005                         break;
2006                 }
2007                 // float SPA_LIGHTMAP0_COLOR = 6;
2008                 case 6:
2009                         // ignore alpha for now..
2010                         VectorCopy( &(model->surfmesh.data_lightmapcolor4f + 4 * surface->num_firstvertex)[pointnum * 4], PRVM_G_VECTOR(OFS_RETURN));
2011                         break;
2012                 default:
2013                         VectorSet( PRVM_G_VECTOR(OFS_RETURN), 0.0f, 0.0f, 0.0f );
2014                         break;
2015         }
2016 }
2017 // #436 vector(entity e, float s) getsurfacenormal
2018 static void VM_CL_getsurfacenormal(void)
2019 {
2020         dp_model_t *model;
2021         msurface_t *surface;
2022         vec3_t normal;
2023         VM_SAFEPARMCOUNT(2, VM_CL_getsurfacenormal);
2024         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
2025         if (!(model = CL_GetModelFromEdict(PRVM_G_EDICT(OFS_PARM0))) || !(surface = cl_getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
2026                 return;
2027         // FIXME: implement rotation/scaling
2028         // note: this (incorrectly) assumes it is a simple polygon
2029         // note: this only returns the first triangle, so it doesn't work very
2030         // well for curved surfaces or arbitrary meshes
2031         TriangleNormal((model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex), (model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + 3, (model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + 6, normal);
2032         VectorNormalize(normal);
2033         VectorCopy(normal, PRVM_G_VECTOR(OFS_RETURN));
2034 }
2035
2036 // #437 string(entity e, float s) getsurfacetexture
2037 static void VM_CL_getsurfacetexture(void)
2038 {
2039         dp_model_t *model;
2040         msurface_t *surface;
2041         VM_SAFEPARMCOUNT(2, VM_CL_getsurfacetexture);
2042         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2043         if (!(model = CL_GetModelFromEdict(PRVM_G_EDICT(OFS_PARM0))) || !(surface = cl_getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
2044                 return;
2045         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(surface->texture->name);
2046 }
2047
2048 // #438 float(entity e, vector p) getsurfacenearpoint
2049 static void VM_CL_getsurfacenearpoint(void)
2050 {
2051         int surfacenum, best;
2052         vec3_t clipped, p;
2053         vec_t dist, bestdist;
2054         prvm_edict_t *ed;
2055         dp_model_t *model = NULL;
2056         msurface_t *surface;
2057         vec_t *point;
2058         VM_SAFEPARMCOUNT(2, VM_CL_getsurfacenearpoint);
2059         PRVM_G_FLOAT(OFS_RETURN) = -1;
2060         ed = PRVM_G_EDICT(OFS_PARM0);
2061         if(!(model = CL_GetModelFromEdict(ed)) || !model->num_surfaces)
2062                 return;
2063
2064         // FIXME: implement rotation/scaling
2065         point = PRVM_G_VECTOR(OFS_PARM1);
2066         VectorSubtract(point, ed->fields.client->origin, p);
2067         best = -1;
2068         bestdist = 1000000000;
2069         for (surfacenum = 0;surfacenum < model->nummodelsurfaces;surfacenum++)
2070         {
2071                 surface = model->data_surfaces + surfacenum + model->firstmodelsurface;
2072                 // first see if the nearest point on the surface's box is closer than the previous match
2073                 clipped[0] = bound(surface->mins[0], p[0], surface->maxs[0]) - p[0];
2074                 clipped[1] = bound(surface->mins[1], p[1], surface->maxs[1]) - p[1];
2075                 clipped[2] = bound(surface->mins[2], p[2], surface->maxs[2]) - p[2];
2076                 dist = VectorLength2(clipped);
2077                 if (dist < bestdist)
2078                 {
2079                         // it is, check the nearest point on the actual geometry
2080                         clippointtosurface(model, surface, p, clipped);
2081                         VectorSubtract(clipped, p, clipped);
2082                         dist += VectorLength2(clipped);
2083                         if (dist < bestdist)
2084                         {
2085                                 // that's closer too, store it as the best match
2086                                 best = surfacenum;
2087                                 bestdist = dist;
2088                         }
2089                 }
2090         }
2091         PRVM_G_FLOAT(OFS_RETURN) = best;
2092 }
2093
2094 // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint
2095 static void VM_CL_getsurfaceclippedpoint(void)
2096 {
2097         prvm_edict_t *ed;
2098         dp_model_t *model;
2099         msurface_t *surface;
2100         vec3_t p, out;
2101         VM_SAFEPARMCOUNT(3, VM_CL_getsurfaceclippedpoint);
2102         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
2103         ed = PRVM_G_EDICT(OFS_PARM0);
2104         if (!(model = CL_GetModelFromEdict(ed)) || !(surface = cl_getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
2105                 return;
2106         // FIXME: implement rotation/scaling
2107         VectorSubtract(PRVM_G_VECTOR(OFS_PARM2), ed->fields.client->origin, p);
2108         clippointtosurface(model, surface, p, out);
2109         // FIXME: implement rotation/scaling
2110         VectorAdd(out, ed->fields.client->origin, PRVM_G_VECTOR(OFS_RETURN));
2111 }
2112
2113 // #443 void(entity e, entity tagentity, string tagname) setattachment
2114 void VM_CL_setattachment (void)
2115 {
2116         prvm_edict_t *e;
2117         prvm_edict_t *tagentity;
2118         const char *tagname;
2119         prvm_eval_t *v;
2120         int modelindex;
2121         dp_model_t *model;
2122         VM_SAFEPARMCOUNT(3, VM_CL_setattachment);
2123
2124         e = PRVM_G_EDICT(OFS_PARM0);
2125         tagentity = PRVM_G_EDICT(OFS_PARM1);
2126         tagname = PRVM_G_STRING(OFS_PARM2);
2127
2128         if (e == prog->edicts)
2129         {
2130                 VM_Warning("setattachment: can not modify world entity\n");
2131                 return;
2132         }
2133         if (e->priv.server->free)
2134         {
2135                 VM_Warning("setattachment: can not modify free entity\n");
2136                 return;
2137         }
2138
2139         if (tagentity == NULL)
2140                 tagentity = prog->edicts;
2141
2142         v = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.tag_entity);
2143         if (v)
2144                 v->edict = PRVM_EDICT_TO_PROG(tagentity);
2145
2146         v = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.tag_index);
2147         if (v)
2148                 v->_float = 0;
2149         if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0])
2150         {
2151                 modelindex = (int)tagentity->fields.client->modelindex;
2152                 model = CL_GetModelByIndex(modelindex);
2153                 if (model)
2154                 {
2155                         v->_float = Mod_Alias_GetTagIndexForName(model, (int)tagentity->fields.client->skin, tagname);
2156                         if (v->_float == 0)
2157                                 Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i (model \"%s\") but could not find it\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity), model->name);
2158                 }
2159                 else
2160                         Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i but it has no model\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity));
2161         }
2162 }
2163
2164 /////////////////////////////////////////
2165 // DP_MD3_TAGINFO extension coded by VorteX
2166
2167 int CL_GetTagIndex (prvm_edict_t *e, const char *tagname)
2168 {
2169         dp_model_t *model = CL_GetModelFromEdict(e);
2170         if (model)
2171                 return Mod_Alias_GetTagIndexForName(model, (int)e->fields.client->skin, tagname);
2172         else
2173                 return -1;
2174 };
2175
2176 // Warnings/errors code:
2177 // 0 - normal (everything all-right)
2178 // 1 - world entity
2179 // 2 - free entity
2180 // 3 - null or non-precached model
2181 // 4 - no tags with requested index
2182 // 5 - runaway loop at attachment chain
2183 extern cvar_t cl_bob;
2184 extern cvar_t cl_bobcycle;
2185 extern cvar_t cl_bobup;
2186 int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex)
2187 {
2188         prvm_eval_t *val;
2189         int reqframe, attachloop;
2190         matrix4x4_t entitymatrix, tagmatrix, attachmatrix;
2191         prvm_edict_t *attachent;
2192         dp_model_t *model;
2193         float scale;
2194
2195         *out = identitymatrix; // warnings and errors return identical matrix
2196
2197         if (ent == prog->edicts)
2198                 return 1;
2199         if (ent->priv.server->free)
2200                 return 2;
2201
2202         model = CL_GetModelFromEdict(ent);
2203
2204         if(!model)
2205                 return 3;
2206
2207         if (ent->fields.client->frame >= 0 && ent->fields.client->frame < model->numframes && model->animscenes)
2208                 reqframe = model->animscenes[(int)ent->fields.client->frame].firstframe;
2209         else
2210                 reqframe = 0; // if model has wrong frame, engine automatically switches to model first frame
2211
2212         // get initial tag matrix
2213         if (tagindex)
2214         {
2215                 int ret = Mod_Alias_GetTagMatrix(model, reqframe, tagindex - 1, &tagmatrix);
2216                 if (ret)
2217                         return ret;
2218         }
2219         else
2220                 tagmatrix = identitymatrix;
2221
2222         if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict)
2223         { // DP_GFX_QUAKE3MODELTAGS, scan all chain and stop on unattached entity
2224                 attachloop = 0;
2225                 do
2226                 {
2227                         attachent = PRVM_EDICT_NUM(val->edict); // to this it entity our entity is attached
2228                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index);
2229
2230                         model = CL_GetModelFromEdict(attachent);
2231
2232                         if (model && val->_float >= 1 && model->animscenes && attachent->fields.client->frame >= 0 && attachent->fields.client->frame < model->numframes)
2233                                 Mod_Alias_GetTagMatrix(model, model->animscenes[(int)attachent->fields.client->frame].firstframe, (int)val->_float - 1, &attachmatrix);
2234                         else
2235                                 attachmatrix = identitymatrix;
2236
2237                         // apply transformation by child entity matrix
2238                         scale = 1;
2239                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale);
2240                         if (val && val->_float != 0)
2241                                 scale = val->_float;
2242                         Matrix4x4_CreateFromQuakeEntity(&entitymatrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], -ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], scale);
2243                         Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2244                         Matrix4x4_Copy(&tagmatrix, out);
2245
2246                         // finally transformate by matrix of tag on parent entity
2247                         Matrix4x4_Concat(out, &attachmatrix, &tagmatrix);
2248                         Matrix4x4_Copy(&tagmatrix, out);
2249
2250                         ent = attachent;
2251                         attachloop += 1;
2252                         if (attachloop > 255) // prevent runaway looping
2253                                 return 5;
2254                 }
2255                 while ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict);
2256         }
2257
2258         // normal or RENDER_VIEWMODEL entity (or main parent entity on attach chain)
2259         scale = 1;
2260         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale);
2261         if (val && val->_float != 0)
2262                 scale = val->_float;
2263         // Alias models have inverse pitch, bmodels can't have tags, so don't check for modeltype...
2264         Matrix4x4_CreateFromQuakeEntity(&entitymatrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], -ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], scale);
2265         Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2266
2267         if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderflags)) && (RF_VIEWMODEL & (int)val->_float))
2268         {// RENDER_VIEWMODEL magic
2269                 Matrix4x4_Copy(&tagmatrix, out);
2270
2271                 scale = 1;
2272                 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale);
2273                 if (val && val->_float != 0)
2274                         scale = val->_float;
2275
2276                 Matrix4x4_CreateFromQuakeEntity(&entitymatrix, cl.csqc_origin[0], cl.csqc_origin[1], cl.csqc_origin[2], cl.csqc_angles[0], cl.csqc_angles[1], cl.csqc_angles[2], scale);
2277                 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2278
2279                 /*
2280                 // Cl_bob, ported from rendering code
2281                 if (ent->fields.client->health > 0 && cl_bob.value && cl_bobcycle.value)
2282                 {
2283                         double bob, cycle;
2284                         // LordHavoc: this code is *weird*, but not replacable (I think it
2285                         // should be done in QC on the server, but oh well, quake is quake)
2286                         // LordHavoc: figured out bobup: the time at which the sin is at 180
2287                         // degrees (which allows lengthening or squishing the peak or valley)
2288                         cycle = cl.time/cl_bobcycle.value;
2289                         cycle -= (int)cycle;
2290                         if (cycle < cl_bobup.value)
2291                                 cycle = sin(M_PI * cycle / cl_bobup.value);
2292                         else
2293                                 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
2294                         // bob is proportional to velocity in the xy plane
2295                         // (don't count Z, or jumping messes it up)
2296                         bob = sqrt(ent->fields.client->velocity[0]*ent->fields.client->velocity[0] + ent->fields.client->velocity[1]*ent->fields.client->velocity[1])*cl_bob.value;
2297                         bob = bob*0.3 + bob*0.7*cycle;
2298                         Matrix4x4_AdjustOrigin(out, 0, 0, bound(-7, bob, 4));
2299                 }
2300                 */
2301         }
2302         return 0;
2303 }
2304
2305 // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
2306 void VM_CL_gettagindex (void)
2307 {
2308         prvm_edict_t *ent;
2309         const char *tag_name;
2310         int modelindex, tag_index;
2311
2312         VM_SAFEPARMCOUNT(2, VM_CL_gettagindex);
2313
2314         ent = PRVM_G_EDICT(OFS_PARM0);
2315         tag_name = PRVM_G_STRING(OFS_PARM1);
2316         if (ent == prog->edicts)
2317         {
2318                 VM_Warning("gettagindex: can't affect world entity\n");
2319                 return;
2320         }
2321         if (ent->priv.server->free)
2322         {
2323                 VM_Warning("gettagindex: can't affect free entity\n");
2324                 return;
2325         }
2326
2327         modelindex = (int)ent->fields.client->modelindex;
2328         tag_index = 0;
2329         if (modelindex >= MAX_MODELS || (modelindex <= -MAX_MODELS /* client models */))
2330                 Con_DPrintf("gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
2331         else
2332         {
2333                 tag_index = CL_GetTagIndex(ent, tag_name);
2334                 if (tag_index == 0)
2335                         Con_DPrintf("gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
2336         }
2337         PRVM_G_FLOAT(OFS_RETURN) = tag_index;
2338 }
2339
2340 // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO)
2341 void VM_CL_gettaginfo (void)
2342 {
2343         prvm_edict_t *e;
2344         int tagindex;
2345         matrix4x4_t tag_matrix;
2346         int returncode;
2347
2348         VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo);
2349
2350         e = PRVM_G_EDICT(OFS_PARM0);
2351         tagindex = (int)PRVM_G_FLOAT(OFS_PARM1);
2352         returncode = CL_GetTagMatrix(&tag_matrix, e, tagindex);
2353         Matrix4x4_ToVectors(&tag_matrix, prog->globals.client->v_forward, prog->globals.client->v_right, prog->globals.client->v_up, PRVM_G_VECTOR(OFS_RETURN));
2354
2355         switch(returncode)
2356         {
2357                 case 1:
2358                         VM_Warning("gettagindex: can't affect world entity\n");
2359                         break;
2360                 case 2:
2361                         VM_Warning("gettagindex: can't affect free entity\n");
2362                         break;
2363                 case 3:
2364                         Con_DPrintf("CL_GetTagMatrix(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(e));
2365                         break;
2366                 case 4:
2367                         Con_DPrintf("CL_GetTagMatrix(entity #%i): model has no tag with requested index %i\n", PRVM_NUM_FOR_EDICT(e), tagindex);
2368                         break;
2369                 case 5:
2370                         Con_DPrintf("CL_GetTagMatrix(entity #%i): runaway loop at attachment chain\n", PRVM_NUM_FOR_EDICT(e));
2371                         break;
2372         }
2373 }
2374
2375 //============================================================================
2376
2377 //====================
2378 //QC POLYGON functions
2379 //====================
2380
2381 #define VMPOLYGONS_MAXPOINTS 64
2382
2383 typedef struct vmpolygons_triangle_s
2384 {
2385         rtexture_t              *texture;
2386         int                             drawflag;
2387         unsigned short  elements[3];
2388 }vmpolygons_triangle_t;
2389
2390 typedef struct vmpolygons_s
2391 {
2392         mempool_t               *pool;
2393         qboolean                initialized;
2394         double          progstarttime;
2395
2396         int                             max_vertices;
2397         int                             num_vertices;
2398         float                   *data_vertex3f;
2399         float                   *data_color4f;
2400         float                   *data_texcoord2f;
2401
2402         int                             max_triangles;
2403         int                             num_triangles;
2404         vmpolygons_triangle_t *data_triangles;
2405         unsigned short  *data_sortedelement3s;
2406
2407         qboolean                begin_active;
2408         rtexture_t              *begin_texture;
2409         int                             begin_drawflag;
2410         int                             begin_vertices;
2411         float                   begin_vertex[VMPOLYGONS_MAXPOINTS][3];
2412         float                   begin_color[VMPOLYGONS_MAXPOINTS][4];
2413         float                   begin_texcoord[VMPOLYGONS_MAXPOINTS][2];
2414 } vmpolygons_t;
2415
2416 // FIXME: make VM_CL_R_Polygon functions use Debug_Polygon functions?
2417 vmpolygons_t vmpolygons[PRVM_MAXPROGS];
2418
2419 //#304 void() renderscene (EXT_CSQC)
2420 // moved that here to reset the polygons,
2421 // resetting them earlier causes R_Mesh_Draw to be called with numvertices = 0
2422 // --blub
2423 void VM_CL_R_RenderScene (void)
2424 {
2425         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
2426         VM_SAFEPARMCOUNT(0, VM_CL_R_RenderScene);
2427         // we need to update any RENDER_VIEWMODEL entities at this point because
2428         // csqc supplies its own view matrix
2429         CL_UpdateViewEntities();
2430         // now draw stuff!
2431         R_RenderView();
2432
2433         polys->num_vertices = polys->num_triangles = 0;
2434         polys->progstarttime = prog->starttime;
2435 }
2436
2437 static void VM_ResizePolygons(vmpolygons_t *polys)
2438 {
2439         float *oldvertex3f = polys->data_vertex3f;
2440         float *oldcolor4f = polys->data_color4f;
2441         float *oldtexcoord2f = polys->data_texcoord2f;
2442         vmpolygons_triangle_t *oldtriangles = polys->data_triangles;
2443         unsigned short *oldsortedelement3s = polys->data_sortedelement3s;
2444         polys->max_vertices = min(polys->max_triangles*3, 65536);
2445         polys->data_vertex3f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[3]));
2446         polys->data_color4f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[4]));
2447         polys->data_texcoord2f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[2]));
2448         polys->data_triangles = (vmpolygons_triangle_t *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(vmpolygons_triangle_t));
2449         polys->data_sortedelement3s = (unsigned short *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(unsigned short[3]));
2450         if (polys->num_vertices)
2451         {
2452                 memcpy(polys->data_vertex3f, oldvertex3f, polys->num_vertices*sizeof(float[3]));
2453                 memcpy(polys->data_color4f, oldcolor4f, polys->num_vertices*sizeof(float[4]));
2454                 memcpy(polys->data_texcoord2f, oldtexcoord2f, polys->num_vertices*sizeof(float[2]));
2455         }
2456         if (polys->num_triangles)
2457         {
2458                 memcpy(polys->data_triangles, oldtriangles, polys->num_triangles*sizeof(vmpolygons_triangle_t));
2459                 memcpy(polys->data_sortedelement3s, oldsortedelement3s, polys->num_triangles*sizeof(unsigned short[3]));
2460         }
2461         if (oldvertex3f)
2462                 Mem_Free(oldvertex3f);
2463         if (oldcolor4f)
2464                 Mem_Free(oldcolor4f);
2465         if (oldtexcoord2f)
2466                 Mem_Free(oldtexcoord2f);
2467         if (oldtriangles)
2468                 Mem_Free(oldtriangles);
2469         if (oldsortedelement3s)
2470                 Mem_Free(oldsortedelement3s);
2471 }
2472
2473 static void VM_InitPolygons (vmpolygons_t* polys)
2474 {
2475         memset(polys, 0, sizeof(*polys));
2476         polys->pool = Mem_AllocPool("VMPOLY", 0, NULL);
2477         polys->max_triangles = 1024;
2478         VM_ResizePolygons(polys);
2479         polys->initialized = true;
2480 }
2481
2482 static void VM_DrawPolygonCallback (const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2483 {
2484         int surfacelistindex;
2485         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
2486         if(polys->progstarttime != prog->starttime) // from other progs? won't draw these (this can cause crashes!)
2487                 return;
2488         R_Mesh_ResetTextureState();
2489         R_Mesh_Matrix(&identitymatrix);
2490         GL_CullFace(GL_NONE);
2491         R_Mesh_VertexPointer(polys->data_vertex3f, 0, 0);
2492         R_Mesh_ColorPointer(polys->data_color4f, 0, 0);
2493         R_Mesh_TexCoordPointer(0, 2, polys->data_texcoord2f, 0, 0);
2494         R_SetupGenericShader(true);
2495
2496         for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
2497         {
2498                 int numtriangles = 0;
2499                 rtexture_t *tex = polys->data_triangles[surfacelist[surfacelistindex]].texture;
2500                 int drawflag = polys->data_triangles[surfacelist[surfacelistindex]].drawflag;
2501                 // this can't call _DrawQ_ProcessDrawFlag, but should be in sync with it
2502                 // FIXME factor this out
2503                 if(drawflag == DRAWFLAG_ADDITIVE)
2504                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2505                 else if(drawflag == DRAWFLAG_MODULATE)
2506                         GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2507                 else if(drawflag == DRAWFLAG_2XMODULATE)
2508                         GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
2509                 else if(drawflag == DRAWFLAG_SCREEN)
2510                         GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
2511                 else
2512                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2513                 R_Mesh_TexBind(0, R_GetTexture(tex));
2514                 numtriangles = 0;
2515                 for (;surfacelistindex < numsurfaces;surfacelistindex++)
2516                 {
2517                         if (polys->data_triangles[surfacelist[surfacelistindex]].texture != tex || polys->data_triangles[surfacelist[surfacelistindex]].drawflag != drawflag)
2518                                 break;
2519                         VectorCopy(polys->data_triangles[surfacelist[surfacelistindex]].elements, polys->data_sortedelement3s + 3*numtriangles);
2520                         numtriangles++;
2521                 }
2522                 R_Mesh_Draw(0, polys->num_vertices, 0, numtriangles, NULL, polys->data_sortedelement3s, 0, 0);
2523         }
2524 }
2525
2526 void VMPolygons_Store(vmpolygons_t *polys)
2527 {
2528         if (r_refdef.draw2dstage)
2529         {
2530                 // draw the polygon as 2D immediately
2531                 drawqueuemesh_t mesh;
2532                 mesh.texture = polys->begin_texture;
2533                 mesh.num_vertices = polys->begin_vertices;
2534                 mesh.num_triangles = polys->begin_vertices-2;
2535                 mesh.data_element3s = polygonelements;
2536                 mesh.data_vertex3f = polys->begin_vertex[0];
2537                 mesh.data_color4f = polys->begin_color[0];
2538                 mesh.data_texcoord2f = polys->begin_texcoord[0];
2539                 DrawQ_Mesh(&mesh, polys->begin_drawflag);
2540         }
2541         else
2542         {
2543                 // queue the polygon as 3D for sorted transparent rendering later
2544                 int i;
2545                 if (polys->max_triangles < polys->num_triangles + polys->begin_vertices-2)
2546                 {
2547                         polys->max_triangles *= 2;
2548                         VM_ResizePolygons(polys);
2549                 }
2550                 if (polys->num_vertices + polys->begin_vertices <= polys->max_vertices)
2551                 {
2552                         // needle in a haystack!
2553                         // polys->num_vertices was used for copying where we actually want to copy begin_vertices
2554                         // that also caused it to not render the first polygon that is added
2555                         // --blub
2556                         memcpy(polys->data_vertex3f + polys->num_vertices * 3, polys->begin_vertex[0], polys->begin_vertices * sizeof(float[3]));
2557                         memcpy(polys->data_color4f + polys->num_vertices * 4, polys->begin_color[0], polys->begin_vertices * sizeof(float[4]));
2558                         memcpy(polys->data_texcoord2f + polys->num_vertices * 2, polys->begin_texcoord[0], polys->begin_vertices * sizeof(float[2]));
2559                         for (i = 0;i < polys->begin_vertices-2;i++)
2560                         {
2561                                 polys->data_triangles[polys->num_triangles].texture = polys->begin_texture;
2562                                 polys->data_triangles[polys->num_triangles].drawflag = polys->begin_drawflag;
2563                                 polys->data_triangles[polys->num_triangles].elements[0] = polys->num_vertices;
2564                                 polys->data_triangles[polys->num_triangles].elements[1] = polys->num_vertices + i+1;
2565                                 polys->data_triangles[polys->num_triangles].elements[2] = polys->num_vertices + i+2;
2566                                 polys->num_triangles++;
2567                         }
2568                         polys->num_vertices += polys->begin_vertices;
2569                 }
2570         }
2571         polys->begin_active = false;
2572 }
2573
2574 // TODO: move this into the client code and clean-up everything else, too! [1/6/2008 Black]
2575 // LordHavoc: agreed, this is a mess
2576 void VM_CL_AddPolygonsToMeshQueue (void)
2577 {
2578         int i;
2579         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
2580         vec3_t center;
2581
2582         // only add polygons of the currently active prog to the queue - if there is none, we're done
2583         if( !prog )
2584                 return;
2585
2586         if (!polys->num_triangles)
2587                 return;
2588
2589         for (i = 0;i < polys->num_triangles;i++)
2590         {
2591                 VectorMAMAM(1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[0], 1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[1], 1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[2], center);
2592                 R_MeshQueue_AddTransparent(center, VM_DrawPolygonCallback, NULL, i, NULL);
2593         }
2594
2595         /*polys->num_triangles = 0; // now done after rendering the scene,
2596           polys->num_vertices = 0;  // otherwise it's not rendered at all and prints an error message --blub */
2597 }
2598
2599 //void(string texturename, float flag) R_BeginPolygon
2600 void VM_CL_R_PolygonBegin (void)
2601 {
2602         const char              *picname;
2603         skinframe_t     *sf;
2604         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
2605         int tf;
2606
2607         // TODO instead of using skinframes here (which provides the benefit of
2608         // better management of flags, and is more suited for 3D rendering), what
2609         // about supporting Q3 shaders?
2610
2611         VM_SAFEPARMCOUNT(2, VM_CL_R_PolygonBegin);
2612
2613         if (!polys->initialized)
2614                 VM_InitPolygons(polys);
2615         if(polys->progstarttime != prog->starttime)
2616         {
2617                 // from another progs? then reset the polys first (fixes crashes on map change, because that can make skinframe textures invalid)
2618                 polys->num_vertices = polys->num_triangles = 0;
2619                 polys->progstarttime = prog->starttime;
2620         }
2621         if (polys->begin_active)
2622         {
2623                 VM_Warning("VM_CL_R_PolygonBegin: called twice without VM_CL_R_PolygonBegin after first\n");
2624                 return;
2625         }
2626         picname = PRVM_G_STRING(OFS_PARM0);
2627
2628         sf = NULL;
2629         if(*picname)
2630         {
2631                 tf = TEXF_ALPHA;
2632                 if((int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MIPMAP)
2633                         tf |= TEXF_MIPMAP;
2634
2635                 do
2636                 {
2637                         sf = R_SkinFrame_FindNextByName(sf, picname);
2638                 }
2639                 while(sf && sf->textureflags != tf);
2640
2641                 if(!sf || !sf->base)
2642                         sf = R_SkinFrame_LoadExternal(picname, tf, true);
2643
2644                 if(sf)
2645                         R_SkinFrame_MarkUsed(sf);
2646         }
2647
2648         polys->begin_texture = (sf && sf->base) ? sf->base : r_texture_white;
2649         polys->begin_drawflag = (int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MASK;
2650         polys->begin_vertices = 0;
2651         polys->begin_active = true;
2652 }
2653
2654 //void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
2655 void VM_CL_R_PolygonVertex (void)
2656 {
2657         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
2658
2659         VM_SAFEPARMCOUNT(4, VM_CL_R_PolygonVertex);
2660
2661         if (!polys->begin_active)
2662         {
2663                 VM_Warning("VM_CL_R_PolygonVertex: VM_CL_R_PolygonBegin wasn't called\n");
2664                 return;
2665         }
2666
2667         if (polys->begin_vertices >= VMPOLYGONS_MAXPOINTS)
2668         {
2669                 VM_Warning("VM_CL_R_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
2670                 return;
2671         }
2672
2673         polys->begin_vertex[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM0)[0];
2674         polys->begin_vertex[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM0)[1];
2675         polys->begin_vertex[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM0)[2];
2676         polys->begin_texcoord[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM1)[0];
2677         polys->begin_texcoord[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM1)[1];
2678         polys->begin_color[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM2)[0];
2679         polys->begin_color[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM2)[1];
2680         polys->begin_color[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM2)[2];
2681         polys->begin_color[polys->begin_vertices][3] = PRVM_G_FLOAT(OFS_PARM3);
2682         polys->begin_vertices++;
2683 }
2684
2685 //void() R_EndPolygon
2686 void VM_CL_R_PolygonEnd (void)
2687 {
2688         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
2689
2690         VM_SAFEPARMCOUNT(0, VM_CL_R_PolygonEnd);
2691         if (!polys->begin_active)
2692         {
2693                 VM_Warning("VM_CL_R_PolygonEnd: VM_CL_R_PolygonBegin wasn't called\n");
2694                 return;
2695         }
2696         polys->begin_active = false;
2697         if (polys->begin_vertices >= 3)
2698                 VMPolygons_Store(polys);
2699         else
2700                 VM_Warning("VM_CL_R_PolygonEnd: %i vertices isn't a good choice\n", polys->begin_vertices);
2701 }
2702
2703 static vmpolygons_t debugPolys;
2704
2705 void Debug_PolygonBegin(const char *picname, int drawflag)
2706 {
2707         if(!debugPolys.initialized)
2708                 VM_InitPolygons(&debugPolys);
2709         if(debugPolys.begin_active)
2710         {
2711                 Con_Printf("Debug_PolygonBegin: called twice without Debug_PolygonEnd after first\n");
2712                 return;
2713         }
2714         debugPolys.begin_texture = picname[0] ? Draw_CachePic (picname)->tex : r_texture_white;
2715         debugPolys.begin_drawflag = drawflag;
2716         debugPolys.begin_vertices = 0;
2717         debugPolys.begin_active = true;
2718 }
2719
2720 void Debug_PolygonVertex(float x, float y, float z, float s, float t, float r, float g, float b, float a)
2721 {
2722         if(!debugPolys.begin_active)
2723         {
2724                 Con_Printf("Debug_PolygonVertex: Debug_PolygonBegin wasn't called\n");
2725                 return;
2726         }
2727
2728         if(debugPolys.begin_vertices > VMPOLYGONS_MAXPOINTS)
2729         {
2730                 Con_Printf("Debug_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
2731                 return;
2732         }
2733
2734         debugPolys.begin_vertex[debugPolys.begin_vertices][0] = x;
2735         debugPolys.begin_vertex[debugPolys.begin_vertices][1] = y;
2736         debugPolys.begin_vertex[debugPolys.begin_vertices][2] = z;
2737         debugPolys.begin_texcoord[debugPolys.begin_vertices][0] = s;
2738         debugPolys.begin_texcoord[debugPolys.begin_vertices][1] = t;
2739         debugPolys.begin_color[debugPolys.begin_vertices][0] = r;
2740         debugPolys.begin_color[debugPolys.begin_vertices][1] = g;
2741         debugPolys.begin_color[debugPolys.begin_vertices][2] = b;
2742         debugPolys.begin_color[debugPolys.begin_vertices][3] = a;
2743         debugPolys.begin_vertices++;
2744 }
2745
2746 void Debug_PolygonEnd(void)
2747 {
2748         if (!debugPolys.begin_active)
2749         {
2750                 Con_Printf("Debug_PolygonEnd: Debug_PolygonBegin wasn't called\n");
2751                 return;
2752         }
2753         debugPolys.begin_active = false;
2754         if (debugPolys.begin_vertices >= 3)
2755                 VMPolygons_Store(&debugPolys);
2756         else
2757                 Con_Printf("Debug_PolygonEnd: %i vertices isn't a good choice\n", debugPolys.begin_vertices);
2758 }
2759
2760 /*
2761 =============
2762 CL_CheckBottom
2763
2764 Returns false if any part of the bottom of the entity is off an edge that
2765 is not a staircase.
2766
2767 =============
2768 */
2769 qboolean CL_CheckBottom (prvm_edict_t *ent)
2770 {
2771         vec3_t  mins, maxs, start, stop;
2772         trace_t trace;
2773         int             x, y;
2774         float   mid, bottom;
2775
2776         VectorAdd (ent->fields.client->origin, ent->fields.client->mins, mins);
2777         VectorAdd (ent->fields.client->origin, ent->fields.client->maxs, maxs);
2778
2779 // if all of the points under the corners are solid world, don't bother
2780 // with the tougher checks
2781 // the corners must be within 16 of the midpoint
2782         start[2] = mins[2] - 1;
2783         for     (x=0 ; x<=1 ; x++)
2784                 for     (y=0 ; y<=1 ; y++)
2785                 {
2786                         start[0] = x ? maxs[0] : mins[0];
2787                         start[1] = y ? maxs[1] : mins[1];
2788                         if (!(CL_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
2789                                 goto realcheck;
2790                 }
2791
2792         return true;            // we got out easy
2793
2794 realcheck:
2795 //
2796 // check it for real...
2797 //
2798         start[2] = mins[2];
2799
2800 // the midpoint must be within 16 of the bottom
2801         start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
2802         start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
2803         stop[2] = start[2] - 2*sv_stepheight.value;
2804         trace = CL_Move (start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true);
2805
2806         if (trace.fraction == 1.0)
2807                 return false;
2808         mid = bottom = trace.endpos[2];
2809
2810 // the corners must be within 16 of the midpoint
2811         for     (x=0 ; x<=1 ; x++)
2812                 for     (y=0 ; y<=1 ; y++)
2813                 {
2814                         start[0] = stop[0] = x ? maxs[0] : mins[0];
2815                         start[1] = stop[1] = y ? maxs[1] : mins[1];
2816
2817                         trace = CL_Move (start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true);
2818
2819                         if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
2820                                 bottom = trace.endpos[2];
2821                         if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
2822                                 return false;
2823                 }
2824
2825         return true;
2826 }
2827
2828 /*
2829 =============
2830 CL_movestep
2831
2832 Called by monster program code.
2833 The move will be adjusted for slopes and stairs, but if the move isn't
2834 possible, no move is done and false is returned
2835 =============
2836 */
2837 qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean settrace)
2838 {
2839         float           dz;
2840         vec3_t          oldorg, neworg, end, traceendpos;
2841         trace_t         trace;
2842         int                     i, svent;
2843         prvm_edict_t            *enemy;
2844         prvm_eval_t     *val;
2845
2846 // try the move
2847         VectorCopy (ent->fields.client->origin, oldorg);
2848         VectorAdd (ent->fields.client->origin, move, neworg);
2849
2850 // flying monsters don't step up
2851         if ( (int)ent->fields.client->flags & (FL_SWIM | FL_FLY) )
2852         {
2853         // try one move with vertical motion, then one without
2854                 for (i=0 ; i<2 ; i++)
2855                 {
2856                         VectorAdd (ent->fields.client->origin, move, neworg);
2857                         enemy = PRVM_PROG_TO_EDICT(ent->fields.client->enemy);
2858                         if (i == 0 && enemy != prog->edicts)
2859                         {
2860                                 dz = ent->fields.client->origin[2] - PRVM_PROG_TO_EDICT(ent->fields.client->enemy)->fields.client->origin[2];
2861                                 if (dz > 40)
2862                                         neworg[2] -= 8;
2863                                 if (dz < 30)
2864                                         neworg[2] += 8;
2865                         }
2866                         trace = CL_Move (ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, neworg, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
2867                         if (settrace)
2868                                 CL_VM_SetTraceGlobals(&trace, svent);
2869
2870                         if (trace.fraction == 1)
2871                         {
2872                                 VectorCopy(trace.endpos, traceendpos);
2873                                 if (((int)ent->fields.client->flags & FL_SWIM) && !(CL_PointSuperContents(traceendpos) & SUPERCONTENTS_LIQUIDSMASK))
2874                                         return false;   // swim monster left water
2875
2876                                 VectorCopy (traceendpos, ent->fields.client->origin);
2877                                 if (relink)
2878                                         CL_LinkEdict(ent);
2879                                 return true;
2880                         }
2881
2882                         if (enemy == prog->edicts)
2883                                 break;
2884                 }
2885
2886                 return false;
2887         }
2888
2889 // push down from a step height above the wished position
2890         neworg[2] += sv_stepheight.value;
2891         VectorCopy (neworg, end);
2892         end[2] -= sv_stepheight.value*2;
2893
2894         trace = CL_Move (neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
2895         if (settrace)
2896                 CL_VM_SetTraceGlobals(&trace, svent);
2897
2898         if (trace.startsolid)
2899         {
2900                 neworg[2] -= sv_stepheight.value;
2901                 trace = CL_Move (neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
2902                 if (settrace)
2903                         CL_VM_SetTraceGlobals(&trace, svent);
2904                 if (trace.startsolid)
2905                         return false;
2906         }
2907         if (trace.fraction == 1)
2908         {
2909         // if monster had the ground pulled out, go ahead and fall
2910                 if ( (int)ent->fields.client->flags & FL_PARTIALGROUND )
2911                 {
2912                         VectorAdd (ent->fields.client->origin, move, ent->fields.client->origin);
2913                         if (relink)
2914                                 CL_LinkEdict(ent);
2915                         ent->fields.client->flags = (int)ent->fields.client->flags & ~FL_ONGROUND;
2916                         return true;
2917                 }
2918
2919                 return false;           // walked off an edge
2920         }
2921
2922 // check point traces down for dangling corners
2923         VectorCopy (trace.endpos, ent->fields.client->origin);
2924
2925         if (!CL_CheckBottom (ent))
2926         {
2927                 if ( (int)ent->fields.client->flags & FL_PARTIALGROUND )
2928                 {       // entity had floor mostly pulled out from underneath it
2929                         // and is trying to correct
2930                         if (relink)
2931                                 CL_LinkEdict(ent);
2932                         return true;
2933                 }
2934                 VectorCopy (oldorg, ent->fields.client->origin);
2935                 return false;
2936         }
2937
2938         if ( (int)ent->fields.client->flags & FL_PARTIALGROUND )
2939                 ent->fields.client->flags = (int)ent->fields.client->flags & ~FL_PARTIALGROUND;
2940
2941         if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.groundentity)))
2942                 val->edict = PRVM_EDICT_TO_PROG(trace.ent);
2943
2944 // the move is ok
2945         if (relink)
2946                 CL_LinkEdict(ent);
2947         return true;
2948 }
2949
2950 /*
2951 ===============
2952 VM_CL_walkmove
2953
2954 float(float yaw, float dist[, settrace]) walkmove
2955 ===============
2956 */
2957 static void VM_CL_walkmove (void)
2958 {
2959         prvm_edict_t    *ent;
2960         float   yaw, dist;
2961         vec3_t  move;
2962         mfunction_t     *oldf;
2963         int     oldself;
2964         qboolean        settrace;
2965
2966         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_walkmove);
2967
2968         // assume failure if it returns early
2969         PRVM_G_FLOAT(OFS_RETURN) = 0;
2970
2971         ent = PRVM_PROG_TO_EDICT(prog->globals.client->self);
2972         if (ent == prog->edicts)
2973         {
2974                 VM_Warning("walkmove: can not modify world entity\n");
2975                 return;
2976         }
2977         if (ent->priv.server->free)
2978         {
2979                 VM_Warning("walkmove: can not modify free entity\n");
2980                 return;
2981         }
2982         yaw = PRVM_G_FLOAT(OFS_PARM0);
2983         dist = PRVM_G_FLOAT(OFS_PARM1);
2984         settrace = prog->argc >= 3 && PRVM_G_FLOAT(OFS_PARM2);
2985
2986         if ( !( (int)ent->fields.client->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
2987                 return;
2988
2989         yaw = yaw*M_PI*2 / 360;
2990
2991         move[0] = cos(yaw)*dist;
2992         move[1] = sin(yaw)*dist;
2993         move[2] = 0;
2994
2995 // save program state, because CL_movestep may call other progs
2996         oldf = prog->xfunction;
2997         oldself = prog->globals.client->self;
2998
2999         PRVM_G_FLOAT(OFS_RETURN) = CL_movestep(ent, move, true, false, settrace);
3000
3001
3002 // restore program state
3003         prog->xfunction = oldf;
3004         prog->globals.client->self = oldself;
3005 }
3006
3007 /*
3008 ===============
3009 VM_CL_serverkey
3010
3011 string(string key) serverkey
3012 ===============
3013 */
3014 void VM_CL_serverkey(void)
3015 {
3016         char string[VM_STRINGTEMP_LENGTH];
3017         VM_SAFEPARMCOUNT(1, VM_CL_serverkey);
3018         InfoString_GetValue(cl.qw_serverinfo, PRVM_G_STRING(OFS_PARM0), string, sizeof(string));
3019         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
3020 }
3021
3022 //============================================================================
3023
3024 // To create a almost working builtin file from this replace:
3025 // "^NULL.*" with ""
3026 // "^{.*//.*}:Wh\(.*\)" with "\1"
3027 // "\:" with "//"
3028 // "^.*//:Wh{\#:d*}:Wh{.*}" with "\2 = \1;"
3029 // "\n\n+" with "\n\n"
3030
3031 prvm_builtin_t vm_cl_builtins[] = {
3032 NULL,                                                   // #0 NULL function (not callable) (QUAKE)
3033 VM_CL_makevectors,                              // #1 void(vector ang) makevectors (QUAKE)
3034 VM_CL_setorigin,                                // #2 void(entity e, vector o) setorigin (QUAKE)
3035 VM_CL_setmodel,                                 // #3 void(entity e, string m) setmodel (QUAKE)
3036 VM_CL_setsize,                                  // #4 void(entity e, vector min, vector max) setsize (QUAKE)
3037 NULL,                                                   // #5 void(entity e, vector min, vector max) setabssize (QUAKE)
3038 VM_break,                                               // #6 void() break (QUAKE)
3039 VM_random,                                              // #7 float() random (QUAKE)
3040 VM_CL_sound,                                    // #8 void(entity e, float chan, string samp) sound (QUAKE)
3041 VM_normalize,                                   // #9 vector(vector v) normalize (QUAKE)
3042 VM_error,                                               // #10 void(string e) error (QUAKE)
3043 VM_objerror,                                    // #11 void(string e) objerror (QUAKE)
3044 VM_vlen,                                                // #12 float(vector v) vlen (QUAKE)
3045 VM_vectoyaw,                                    // #13 float(vector v) vectoyaw (QUAKE)
3046 VM_CL_spawn,                                    // #14 entity() spawn (QUAKE)
3047 VM_remove,                                              // #15 void(entity e) remove (QUAKE)
3048 VM_CL_traceline,                                // #16 float(vector v1, vector v2, float tryents, entity ignoreentity) traceline (QUAKE)
3049 NULL,                                                   // #17 entity() checkclient (QUAKE)
3050 VM_find,                                                // #18 entity(entity start, .string fld, string match) find (QUAKE)
3051 VM_precache_sound,                              // #19 void(string s) precache_sound (QUAKE)
3052 VM_CL_precache_model,                   // #20 void(string s) precache_model (QUAKE)
3053 NULL,                                                   // #21 void(entity client, string s, ...) stuffcmd (QUAKE)
3054 VM_CL_findradius,                               // #22 entity(vector org, float rad) findradius (QUAKE)
3055 NULL,                                                   // #23 void(string s, ...) bprint (QUAKE)
3056 NULL,                                                   // #24 void(entity client, string s, ...) sprint (QUAKE)
3057 VM_dprint,                                              // #25 void(string s, ...) dprint (QUAKE)
3058 VM_ftos,                                                // #26 string(float f) ftos (QUAKE)
3059 VM_vtos,                                                // #27 string(vector v) vtos (QUAKE)
3060 VM_coredump,                                    // #28 void() coredump (QUAKE)
3061 VM_traceon,                                             // #29 void() traceon (QUAKE)
3062 VM_traceoff,                                    // #30 void() traceoff (QUAKE)
3063 VM_eprint,                                              // #31 void(entity e) eprint (QUAKE)
3064 VM_CL_walkmove,                                 // #32 float(float yaw, float dist[, float settrace]) walkmove (QUAKE)
3065 NULL,                                                   // #33 (QUAKE)
3066 VM_CL_droptofloor,                              // #34 float() droptofloor (QUAKE)
3067 VM_CL_lightstyle,                               // #35 void(float style, string value) lightstyle (QUAKE)
3068 VM_rint,                                                // #36 float(float v) rint (QUAKE)
3069 VM_floor,                                               // #37 float(float v) floor (QUAKE)
3070 VM_ceil,                                                // #38 float(float v) ceil (QUAKE)
3071 NULL,                                                   // #39 (QUAKE)
3072 VM_CL_checkbottom,                              // #40 float(entity e) checkbottom (QUAKE)
3073 VM_CL_pointcontents,                    // #41 float(vector v) pointcontents (QUAKE)
3074 NULL,                                                   // #42 (QUAKE)
3075 VM_fabs,                                                // #43 float(float f) fabs (QUAKE)
3076 NULL,                                                   // #44 vector(entity e, float speed) aim (QUAKE)
3077 VM_cvar,                                                // #45 float(string s) cvar (QUAKE)
3078 VM_localcmd,                                    // #46 void(string s) localcmd (QUAKE)
3079 VM_nextent,                                             // #47 entity(entity e) nextent (QUAKE)
3080 VM_CL_particle,                                 // #48 void(vector o, vector d, float color, float count) particle (QUAKE)
3081 VM_changeyaw,                                   // #49 void() ChangeYaw (QUAKE)
3082 NULL,                                                   // #50 (QUAKE)
3083 VM_vectoangles,                                 // #51 vector(vector v) vectoangles (QUAKE)
3084 NULL,                                                   // #52 void(float to, float f) WriteByte (QUAKE)
3085 NULL,                                                   // #53 void(float to, float f) WriteChar (QUAKE)
3086 NULL,                                                   // #54 void(float to, float f) WriteShort (QUAKE)
3087 NULL,                                                   // #55 void(float to, float f) WriteLong (QUAKE)
3088 NULL,                                                   // #56 void(float to, float f) WriteCoord (QUAKE)
3089 NULL,                                                   // #57 void(float to, float f) WriteAngle (QUAKE)
3090 NULL,                                                   // #58 void(float to, string s) WriteString (QUAKE)
3091 NULL,                                                   // #59 (QUAKE)
3092 VM_sin,                                                 // #60 float(float f) sin (DP_QC_SINCOSSQRTPOW)
3093 VM_cos,                                                 // #61 float(float f) cos (DP_QC_SINCOSSQRTPOW)
3094 VM_sqrt,                                                // #62 float(float f) sqrt (DP_QC_SINCOSSQRTPOW)
3095 VM_changepitch,                                 // #63 void(entity ent) changepitch (DP_QC_CHANGEPITCH)
3096 VM_CL_tracetoss,                                // #64 void(entity e, entity ignore) tracetoss (DP_QC_TRACETOSS)
3097 VM_etos,                                                // #65 string(entity ent) etos (DP_QC_ETOS)
3098 NULL,                                                   // #66 (QUAKE)
3099 NULL,                                                   // #67 void(float step) movetogoal (QUAKE)
3100 VM_precache_file,                               // #68 string(string s) precache_file (QUAKE)
3101 VM_CL_makestatic,                               // #69 void(entity e) makestatic (QUAKE)
3102 NULL,                                                   // #70 void(string s) changelevel (QUAKE)
3103 NULL,                                                   // #71 (QUAKE)
3104 VM_cvar_set,                                    // #72 void(string var, string val) cvar_set (QUAKE)
3105 NULL,                                                   // #73 void(entity client, strings) centerprint (QUAKE)
3106 VM_CL_ambientsound,                             // #74 void(vector pos, string samp, float vol, float atten) ambientsound (QUAKE)
3107 VM_CL_precache_model,                   // #75 string(string s) precache_model2 (QUAKE)
3108 VM_precache_sound,                              // #76 string(string s) precache_sound2 (QUAKE)
3109 VM_precache_file,                               // #77 string(string s) precache_file2 (QUAKE)
3110 NULL,                                                   // #78 void(entity e) setspawnparms (QUAKE)
3111 NULL,                                                   // #79 void(entity killer, entity killee) logfrag (QUAKEWORLD)
3112 NULL,                                                   // #80 string(entity e, string keyname) infokey (QUAKEWORLD)
3113 VM_stof,                                                // #81 float(string s) stof (FRIK_FILE)
3114 NULL,                                                   // #82 void(vector where, float set) multicast (QUAKEWORLD)
3115 NULL,                                                   // #83 (QUAKE)
3116 NULL,                                                   // #84 (QUAKE)
3117 NULL,                                                   // #85 (QUAKE)
3118 NULL,                                                   // #86 (QUAKE)
3119 NULL,                                                   // #87 (QUAKE)
3120 NULL,                                                   // #88 (QUAKE)
3121 NULL,                                                   // #89 (QUAKE)
3122 VM_CL_tracebox,                                 // #90 void(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox (DP_QC_TRACEBOX)
3123 VM_randomvec,                                   // #91 vector() randomvec (DP_QC_RANDOMVEC)
3124 VM_CL_getlight,                                 // #92 vector(vector org) getlight (DP_QC_GETLIGHT)
3125 VM_registercvar,                                // #93 float(string name, string value) registercvar (DP_REGISTERCVAR)
3126 VM_min,                                                 // #94 float(float a, floats) min (DP_QC_MINMAXBOUND)
3127 VM_max,                                                 // #95 float(float a, floats) max (DP_QC_MINMAXBOUND)
3128 VM_bound,                                               // #96 float(float minimum, float val, float maximum) bound (DP_QC_MINMAXBOUND)
3129 VM_pow,                                                 // #97 float(float f, float f) pow (DP_QC_SINCOSSQRTPOW)
3130 VM_findfloat,                                   // #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT)
3131 VM_checkextension,                              // #99 float(string s) checkextension (the basis of the extension system)
3132 // FrikaC and Telejano range #100-#199
3133 NULL,                                                   // #100
3134 NULL,                                                   // #101
3135 NULL,                                                   // #102
3136 NULL,                                                   // #103
3137 NULL,                                                   // #104
3138 NULL,                                                   // #105
3139 NULL,                                                   // #106
3140 NULL,                                                   // #107
3141 NULL,                                                   // #108
3142 NULL,                                                   // #109
3143 VM_fopen,                                               // #110 float(string filename, float mode) fopen (FRIK_FILE)
3144 VM_fclose,                                              // #111 void(float fhandle) fclose (FRIK_FILE)
3145 VM_fgets,                                               // #112 string(float fhandle) fgets (FRIK_FILE)
3146 VM_fputs,                                               // #113 void(float fhandle, string s) fputs (FRIK_FILE)
3147 VM_strlen,                                              // #114 float(string s) strlen (FRIK_FILE)
3148 VM_strcat,                                              // #115 string(string s1, string s2, ...) strcat (FRIK_FILE)
3149 VM_substring,                                   // #116 string(string s, float start, float length) substring (FRIK_FILE)
3150 VM_stov,                                                // #117 vector(string) stov (FRIK_FILE)
3151 VM_strzone,                                             // #118 string(string s) strzone (FRIK_FILE)
3152 VM_strunzone,                                   // #119 void(string s) strunzone (FRIK_FILE)
3153 NULL,                                                   // #120
3154 NULL,                                                   // #121
3155 NULL,                                                   // #122
3156 NULL,                                                   // #123
3157 NULL,                                                   // #124
3158 NULL,                                                   // #125
3159 NULL,                                                   // #126
3160 NULL,                                                   // #127
3161 NULL,                                                   // #128
3162 NULL,                                                   // #129
3163 NULL,                                                   // #130
3164 NULL,                                                   // #131
3165 NULL,                                                   // #132
3166 NULL,                                                   // #133
3167 NULL,                                                   // #134
3168 NULL,                                                   // #135
3169 NULL,                                                   // #136
3170 NULL,                                                   // #137
3171 NULL,                                                   // #138
3172 NULL,                                                   // #139
3173 NULL,                                                   // #140
3174 NULL,                                                   // #141
3175 NULL,                                                   // #142
3176 NULL,                                                   // #143
3177 NULL,                                                   // #144
3178 NULL,                                                   // #145
3179 NULL,                                                   // #146
3180 NULL,                                                   // #147
3181 NULL,                                                   // #148
3182 NULL,                                                   // #149
3183 NULL,                                                   // #150
3184 NULL,                                                   // #151
3185 NULL,                                                   // #152
3186 NULL,                                                   // #153
3187 NULL,                                                   // #154
3188 NULL,                                                   // #155
3189 NULL,                                                   // #156
3190 NULL,                                                   // #157
3191 NULL,                                                   // #158
3192 NULL,                                                   // #159
3193 NULL,                                                   // #160
3194 NULL,                                                   // #161
3195 NULL,                                                   // #162
3196 NULL,                                                   // #163
3197 NULL,                                                   // #164
3198 NULL,                                                   // #165
3199 NULL,                                                   // #166
3200 NULL,                                                   // #167
3201 NULL,                                                   // #168
3202 NULL,                                                   // #169
3203 NULL,                                                   // #170
3204 NULL,                                                   // #171
3205 NULL,                                                   // #172
3206 NULL,                                                   // #173
3207 NULL,                                                   // #174
3208 NULL,                                                   // #175
3209 NULL,                                                   // #176
3210 NULL,                                                   // #177
3211 NULL,                                                   // #178
3212 NULL,                                                   // #179
3213 NULL,                                                   // #180
3214 NULL,                                                   // #181
3215 NULL,                                                   // #182
3216 NULL,                                                   // #183
3217 NULL,                                                   // #184
3218 NULL,                                                   // #185
3219 NULL,                                                   // #186
3220 NULL,                                                   // #187
3221 NULL,                                                   // #188
3222 NULL,                                                   // #189
3223 NULL,                                                   // #190
3224 NULL,                                                   // #191
3225 NULL,                                                   // #192
3226 NULL,                                                   // #193
3227 NULL,                                                   // #194
3228 NULL,                                                   // #195
3229 NULL,                                                   // #196
3230 NULL,                                                   // #197
3231 NULL,                                                   // #198
3232 NULL,                                                   // #199
3233 // FTEQW range #200-#299
3234 NULL,                                                   // #200
3235 NULL,                                                   // #201
3236 NULL,                                                   // #202
3237 NULL,                                                   // #203
3238 NULL,                                                   // #204
3239 NULL,                                                   // #205
3240 NULL,                                                &nb