]> icculus.org git repositories - divverent/darkplaces.git/blob - clvm_cmds.c
Merge commit 'origin/master' into div0/traceboxbox
[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 extern cvar_t r_equalize_entities_fullbright;
25
26 sfx_t *S_FindName(const char *name);
27 int Sbar_GetSortedPlayerIndex (int index);
28 void Sbar_SortFrags (void);
29 void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius);
30 void CSQC_RelinkAllEntities (int drawmask);
31 void CSQC_RelinkCSQCEntities (void);
32 const char *Key_GetBind (int key);
33
34 // #1 void(vector ang) makevectors
35 static void VM_CL_makevectors (void)
36 {
37         VM_SAFEPARMCOUNT(1, VM_CL_makevectors);
38         AngleVectors (PRVM_G_VECTOR(OFS_PARM0), prog->globals.client->v_forward, prog->globals.client->v_right, prog->globals.client->v_up);
39 }
40
41 // #2 void(entity e, vector o) setorigin
42 void VM_CL_setorigin (void)
43 {
44         prvm_edict_t    *e;
45         float   *org;
46         VM_SAFEPARMCOUNT(2, VM_CL_setorigin);
47
48         e = PRVM_G_EDICT(OFS_PARM0);
49         if (e == prog->edicts)
50         {
51                 VM_Warning("setorigin: can not modify world entity\n");
52                 return;
53         }
54         if (e->priv.required->free)
55         {
56                 VM_Warning("setorigin: can not modify free entity\n");
57                 return;
58         }
59         org = PRVM_G_VECTOR(OFS_PARM1);
60         VectorCopy (org, e->fields.client->origin);
61         CL_LinkEdict(e);
62 }
63
64 static void SetMinMaxSize (prvm_edict_t *e, float *min, float *max)
65 {
66         int             i;
67
68         for (i=0 ; i<3 ; i++)
69                 if (min[i] > max[i])
70                         PRVM_ERROR("SetMinMaxSize: backwards mins/maxs");
71
72         // set derived values
73         VectorCopy (min, e->fields.client->mins);
74         VectorCopy (max, e->fields.client->maxs);
75         VectorSubtract (max, min, e->fields.client->size);
76
77         CL_LinkEdict (e);
78 }
79
80 // #3 void(entity e, string m) setmodel
81 void VM_CL_setmodel (void)
82 {
83         prvm_edict_t    *e;
84         const char              *m;
85         dp_model_t *mod;
86         int                             i;
87
88         VM_SAFEPARMCOUNT(2, VM_CL_setmodel);
89
90         e = PRVM_G_EDICT(OFS_PARM0);
91         e->fields.client->modelindex = 0;
92         e->fields.client->model = 0;
93
94         m = PRVM_G_STRING(OFS_PARM1);
95         mod = NULL;
96         for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
97         {
98                 if (!strcmp(cl.csqc_model_precache[i]->name, m))
99                 {
100                         mod = cl.csqc_model_precache[i];
101                         e->fields.client->model = PRVM_SetEngineString(mod->name);
102                         e->fields.client->modelindex = -(i+1);
103                         break;
104                 }
105         }
106
107         if( !mod ) {
108                 for (i = 0;i < MAX_MODELS;i++)
109                 {
110                         mod = cl.model_precache[i];
111                         if (mod && !strcmp(mod->name, m))
112                         {
113                                 e->fields.client->model = PRVM_SetEngineString(mod->name);
114                                 e->fields.client->modelindex = i;
115                                 break;
116                         }
117                 }
118         }
119
120         if( mod ) {
121                 // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
122                 //SetMinMaxSize (e, mod->normalmins, mod->normalmaxs);
123         }
124         else
125         {
126                 SetMinMaxSize (e, vec3_origin, vec3_origin);
127                 VM_Warning ("setmodel: model '%s' not precached\n", m);
128         }
129 }
130
131 // #4 void(entity e, vector min, vector max) setsize
132 static void VM_CL_setsize (void)
133 {
134         prvm_edict_t    *e;
135         float                   *min, *max;
136         VM_SAFEPARMCOUNT(3, VM_CL_setsize);
137
138         e = PRVM_G_EDICT(OFS_PARM0);
139         if (e == prog->edicts)
140         {
141                 VM_Warning("setsize: can not modify world entity\n");
142                 return;
143         }
144         if (e->priv.server->free)
145         {
146                 VM_Warning("setsize: can not modify free entity\n");
147                 return;
148         }
149         min = PRVM_G_VECTOR(OFS_PARM1);
150         max = PRVM_G_VECTOR(OFS_PARM2);
151
152         SetMinMaxSize( e, min, max );
153
154         CL_LinkEdict(e);
155 }
156
157 // #8 void(entity e, float chan, string samp, float volume, float atten) sound
158 static void VM_CL_sound (void)
159 {
160         const char                      *sample;
161         int                                     channel;
162         prvm_edict_t            *entity;
163         float                           volume;
164         float                           attenuation;
165         vec3_t                          org;
166
167         VM_SAFEPARMCOUNT(5, VM_CL_sound);
168
169         entity = PRVM_G_EDICT(OFS_PARM0);
170         channel = (int)PRVM_G_FLOAT(OFS_PARM1);
171         sample = PRVM_G_STRING(OFS_PARM2);
172         volume = PRVM_G_FLOAT(OFS_PARM3);
173         attenuation = PRVM_G_FLOAT(OFS_PARM4);
174
175         if (volume < 0 || volume > 1)
176         {
177                 VM_Warning("VM_CL_sound: volume must be in range 0-1\n");
178                 return;
179         }
180
181         if (attenuation < 0 || attenuation > 4)
182         {
183                 VM_Warning("VM_CL_sound: attenuation must be in range 0-4\n");
184                 return;
185         }
186
187         if (channel < 0 || channel > 7)
188         {
189                 VM_Warning("VM_CL_sound: channel must be in range 0-7\n");
190                 return;
191         }
192
193         CL_VM_GetEntitySoundOrigin(MAX_EDICTS + PRVM_NUM_FOR_EDICT(entity), org);
194         S_StartSound(MAX_EDICTS + PRVM_NUM_FOR_EDICT(entity), channel, S_FindName(sample), org, volume, attenuation);
195 }
196
197 // #483 void(vector origin, string sample, float volume, float attenuation) pointsound
198 static void VM_CL_pointsound(void)
199 {
200         const char                      *sample;
201         float                           volume;
202         float                           attenuation;
203         vec3_t                          org;
204
205         VM_SAFEPARMCOUNT(4, VM_CL_pointsound);
206
207         VectorCopy( PRVM_G_VECTOR(OFS_PARM0), org);
208         sample = PRVM_G_STRING(OFS_PARM1);
209         volume = PRVM_G_FLOAT(OFS_PARM2);
210         attenuation = PRVM_G_FLOAT(OFS_PARM3);
211
212         if (volume < 0 || volume > 1)
213         {
214                 VM_Warning("VM_CL_pointsound: volume must be in range 0-1\n");
215                 return;
216         }
217
218         if (attenuation < 0 || attenuation > 4)
219         {
220                 VM_Warning("VM_CL_pointsound: attenuation must be in range 0-4\n");
221                 return;
222         }
223
224         // Send World Entity as Entity to Play Sound (for CSQC, that is MAX_EDICTS)
225         S_StartSound(MAX_EDICTS, 0, S_FindName(sample), org, volume, attenuation);
226 }
227
228 // #14 entity() spawn
229 static void VM_CL_spawn (void)
230 {
231         prvm_edict_t *ed;
232         ed = PRVM_ED_Alloc();
233         VM_RETURN_EDICT(ed);
234 }
235
236 void CL_VM_SetTraceGlobals(const trace_t *trace, int svent)
237 {
238         prvm_eval_t *val;
239         VM_SetTraceGlobals(trace);
240         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_networkentity)))
241                 val->_float = svent;
242 }
243
244 #define CL_HitNetworkBrushModels(move) !((move) == MOVE_WORLDONLY)
245 #define CL_HitNetworkPlayers(move)     !((move) == MOVE_WORLDONLY || (move) == MOVE_NOMONSTERS)
246
247 // #16 void(vector v1, vector v2, float movetype, entity ignore) traceline
248 static void VM_CL_traceline (void)
249 {
250         float   *v1, *v2;
251         trace_t trace;
252         int             move, svent;
253         prvm_edict_t    *ent;
254
255 //      R_TimeReport("pretraceline");
256
257         VM_SAFEPARMCOUNTRANGE(4, 4, VM_CL_traceline);
258
259         prog->xfunction->builtinsprofile += 30;
260
261         v1 = PRVM_G_VECTOR(OFS_PARM0);
262         v2 = PRVM_G_VECTOR(OFS_PARM1);
263         move = (int)PRVM_G_FLOAT(OFS_PARM2);
264         ent = PRVM_G_EDICT(OFS_PARM3);
265
266         if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2]))
267                 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));
268
269         trace = CL_TraceLine(v1, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true);
270
271         CL_VM_SetTraceGlobals(&trace, svent);
272 //      R_TimeReport("traceline");
273 }
274
275 /*
276 =================
277 VM_CL_tracebox
278
279 Used for use tracing and shot targeting
280 Traces are blocked by bbox and exact bsp entityes, and also slide box entities
281 if the tryents flag is set.
282
283 tracebox (vector1, vector mins, vector maxs, vector2, tryents)
284 =================
285 */
286 // LordHavoc: added this for my own use, VERY useful, similar to traceline
287 static void VM_CL_tracebox (void)
288 {
289         float   *v1, *v2, *m1, *m2;
290         trace_t trace;
291         int             move, svent;
292         prvm_edict_t    *ent;
293
294 //      R_TimeReport("pretracebox");
295         VM_SAFEPARMCOUNTRANGE(6, 8, VM_CL_tracebox); // allow more parameters for future expansion
296
297         prog->xfunction->builtinsprofile += 30;
298
299         v1 = PRVM_G_VECTOR(OFS_PARM0);
300         m1 = PRVM_G_VECTOR(OFS_PARM1);
301         m2 = PRVM_G_VECTOR(OFS_PARM2);
302         v2 = PRVM_G_VECTOR(OFS_PARM3);
303         move = (int)PRVM_G_FLOAT(OFS_PARM4);
304         ent = PRVM_G_EDICT(OFS_PARM5);
305
306         if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2]))
307                 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));
308
309         trace = CL_TraceBox(v1, m1, m2, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true);
310
311         CL_VM_SetTraceGlobals(&trace, svent);
312 //      R_TimeReport("tracebox");
313 }
314
315 /*
316 =================
317 VM_CL_traceboxbox
318
319 Used for use tracing and shot targeting
320 Traces are blocked by bbox and exact bsp entityes, and also slide box entities
321 if the tryents flag is set.
322
323 traceboxbox (vector1, vector mins, vector maxs, vector2, vector mins2, vector maxs2, tryents)
324 =================
325 */
326 // LordHavoc: added this for my own use, VERY useful, similar to traceline
327 static void VM_CL_traceboxbox (void)
328 {
329         float   *v1, *v2, *mi1, *mi2, *ma1, *ma2;
330         trace_t trace;
331         int             move, svent;
332         prvm_edict_t    *ent;
333
334         VM_SAFEPARMCOUNT(8, VM_CL_traceboxbox);
335
336         prog->xfunction->builtinsprofile += 30;
337
338         v1 = PRVM_G_VECTOR(OFS_PARM0);
339         mi1 = PRVM_G_VECTOR(OFS_PARM1);
340         ma1 = PRVM_G_VECTOR(OFS_PARM2);
341         v2 = PRVM_G_VECTOR(OFS_PARM3);
342         mi2 = PRVM_G_VECTOR(OFS_PARM4);
343         ma2 = PRVM_G_VECTOR(OFS_PARM5);
344         move = (int)PRVM_G_FLOAT(OFS_PARM6);
345         ent = PRVM_G_EDICT(OFS_PARM7);
346
347         if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2]))
348                 PRVM_ERROR("%s: NAN errors detected in traceboxbox('%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], mi1[0], mi1[1], mi1[2], ma1[0], ma1[1], ma1[2], v2[0], v2[1], v2[2], mi2[0], mi2[1], mi2[2], ma2[0], ma2[1], ma2[2], move, PRVM_EDICT_TO_PROG(ent));
349
350         trace = CL_TraceBoxBox(v1, mi1, ma1, v2, mi2, ma2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true);
351
352         CL_VM_SetTraceGlobals(&trace, svent);
353 }
354
355 trace_t CL_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore, int *svent)
356 {
357         int i;
358         float gravity;
359         vec3_t move, end;
360         vec3_t original_origin;
361         vec3_t original_velocity;
362         vec3_t original_angles;
363         vec3_t original_avelocity;
364         prvm_eval_t *val;
365         trace_t trace;
366
367         VectorCopy(tossent->fields.client->origin   , original_origin   );
368         VectorCopy(tossent->fields.client->velocity , original_velocity );
369         VectorCopy(tossent->fields.client->angles   , original_angles   );
370         VectorCopy(tossent->fields.client->avelocity, original_avelocity);
371
372         val = PRVM_EDICTFIELDVALUE(tossent, prog->fieldoffsets.gravity);
373         if (val != NULL && val->_float != 0)
374                 gravity = val->_float;
375         else
376                 gravity = 1.0;
377         gravity *= cl.movevars_gravity * 0.05;
378
379         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
380         {
381                 tossent->fields.client->velocity[2] -= gravity;
382                 VectorMA (tossent->fields.client->angles, 0.05, tossent->fields.client->avelocity, tossent->fields.client->angles);
383                 VectorScale (tossent->fields.client->velocity, 0.05, move);
384                 VectorAdd (tossent->fields.client->origin, move, end);
385                 trace = CL_TraceBox(tossent->fields.client->origin, tossent->fields.client->mins, tossent->fields.client->maxs, end, MOVE_NORMAL, tossent, CL_GenericHitSuperContentsMask(tossent), true, true, NULL, true);
386                 VectorCopy (trace.endpos, tossent->fields.client->origin);
387
388                 if (trace.fraction < 1)
389                         break;
390         }
391
392         VectorCopy(original_origin   , tossent->fields.client->origin   );
393         VectorCopy(original_velocity , tossent->fields.client->velocity );
394         VectorCopy(original_angles   , tossent->fields.client->angles   );
395         VectorCopy(original_avelocity, tossent->fields.client->avelocity);
396
397         return trace;
398 }
399
400 static void VM_CL_tracetoss (void)
401 {
402         trace_t trace;
403         prvm_edict_t    *ent;
404         prvm_edict_t    *ignore;
405         int svent = 0;
406
407         prog->xfunction->builtinsprofile += 600;
408
409         VM_SAFEPARMCOUNT(2, VM_CL_tracetoss);
410
411         ent = PRVM_G_EDICT(OFS_PARM0);
412         if (ent == prog->edicts)
413         {
414                 VM_Warning("tracetoss: can not use world entity\n");
415                 return;
416         }
417         ignore = PRVM_G_EDICT(OFS_PARM1);
418
419         trace = CL_Trace_Toss (ent, ignore, &svent);
420
421         CL_VM_SetTraceGlobals(&trace, svent);
422 }
423
424
425 // #20 void(string s) precache_model
426 void VM_CL_precache_model (void)
427 {
428         const char      *name;
429         int                     i;
430         dp_model_t              *m;
431
432         VM_SAFEPARMCOUNT(1, VM_CL_precache_model);
433
434         name = PRVM_G_STRING(OFS_PARM0);
435         for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
436         {
437                 if(!strcmp(cl.csqc_model_precache[i]->name, name))
438                 {
439                         PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
440                         return;
441                 }
442         }
443         PRVM_G_FLOAT(OFS_RETURN) = 0;
444         m = Mod_ForName(name, false, false, name[0] == '*' ? cl.model_name[1] : NULL);
445         if(m && m->loaded)
446         {
447                 for (i = 0;i < MAX_MODELS;i++)
448                 {
449                         if (!cl.csqc_model_precache[i])
450                         {
451                                 cl.csqc_model_precache[i] = (dp_model_t*)m;
452                                 PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
453                                 return;
454                         }
455                 }
456                 VM_Warning("VM_CL_precache_model: no free models\n");
457                 return;
458         }
459         VM_Warning("VM_CL_precache_model: model \"%s\" not found\n", name);
460 }
461
462 int CSQC_EntitiesInBox (vec3_t mins, vec3_t maxs, int maxlist, prvm_edict_t **list)
463 {
464         prvm_edict_t    *ent;
465         int                             i, k;
466
467         ent = PRVM_NEXT_EDICT(prog->edicts);
468         for(k=0,i=1; i<prog->num_edicts ;i++, ent = PRVM_NEXT_EDICT(ent))
469         {
470                 if (ent->priv.required->free)
471                         continue;
472                 if(BoxesOverlap(mins, maxs, ent->fields.client->absmin, ent->fields.client->absmax))
473                         list[k++] = ent;
474         }
475         return k;
476 }
477
478 // #22 entity(vector org, float rad) findradius
479 static void VM_CL_findradius (void)
480 {
481         prvm_edict_t    *ent, *chain;
482         vec_t                   radius, radius2;
483         vec3_t                  org, eorg, mins, maxs;
484         int                             i, numtouchedicts;
485         static prvm_edict_t     *touchedicts[MAX_EDICTS];
486         int             chainfield;
487
488         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_findradius);
489
490         if(prog->argc == 3)
491                 chainfield = PRVM_G_INT(OFS_PARM2);
492         else
493                 chainfield = prog->fieldoffsets.chain;
494         if(chainfield < 0)
495                 PRVM_ERROR("VM_findchain: %s doesnt have the specified chain field !", PRVM_NAME);
496
497         chain = (prvm_edict_t *)prog->edicts;
498
499         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
500         radius = PRVM_G_FLOAT(OFS_PARM1);
501         radius2 = radius * radius;
502
503         mins[0] = org[0] - (radius + 1);
504         mins[1] = org[1] - (radius + 1);
505         mins[2] = org[2] - (radius + 1);
506         maxs[0] = org[0] + (radius + 1);
507         maxs[1] = org[1] + (radius + 1);
508         maxs[2] = org[2] + (radius + 1);
509         numtouchedicts = CSQC_EntitiesInBox(mins, maxs, MAX_EDICTS, touchedicts);
510         if (numtouchedicts > MAX_EDICTS)
511         {
512                 // this never happens   //[515]: for what then ?
513                 Con_Printf("CSQC_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
514                 numtouchedicts = MAX_EDICTS;
515         }
516         for (i = 0;i < numtouchedicts;i++)
517         {
518                 ent = touchedicts[i];
519                 // Quake did not return non-solid entities but darkplaces does
520                 // (note: this is the reason you can't blow up fallen zombies)
521                 if (ent->fields.client->solid == SOLID_NOT && !sv_gameplayfix_blowupfallenzombies.integer)
522                         continue;
523                 // LordHavoc: compare against bounding box rather than center so it
524                 // doesn't miss large objects, and use DotProduct instead of Length
525                 // for a major speedup
526                 VectorSubtract(org, ent->fields.client->origin, eorg);
527                 if (sv_gameplayfix_findradiusdistancetobox.integer)
528                 {
529                         eorg[0] -= bound(ent->fields.client->mins[0], eorg[0], ent->fields.client->maxs[0]);
530                         eorg[1] -= bound(ent->fields.client->mins[1], eorg[1], ent->fields.client->maxs[1]);
531                         eorg[2] -= bound(ent->fields.client->mins[2], eorg[2], ent->fields.client->maxs[2]);
532                 }
533                 else
534                         VectorMAMAM(1, eorg, -0.5f, ent->fields.client->mins, -0.5f, ent->fields.client->maxs, eorg);
535                 if (DotProduct(eorg, eorg) < radius2)
536                 {
537                         PRVM_EDICTFIELDVALUE(ent, chainfield)->edict = PRVM_EDICT_TO_PROG(chain);
538                         chain = ent;
539                 }
540         }
541
542         VM_RETURN_EDICT(chain);
543 }
544
545 // #34 float() droptofloor
546 static void VM_CL_droptofloor (void)
547 {
548         prvm_edict_t            *ent;
549         prvm_eval_t                     *val;
550         vec3_t                          end;
551         trace_t                         trace;
552
553         VM_SAFEPARMCOUNTRANGE(0, 2, VM_CL_droptofloor); // allow 2 parameters because the id1 defs.qc had an incorrect prototype
554
555         // assume failure if it returns early
556         PRVM_G_FLOAT(OFS_RETURN) = 0;
557
558         ent = PRVM_PROG_TO_EDICT(prog->globals.client->self);
559         if (ent == prog->edicts)
560         {
561                 VM_Warning("droptofloor: can not modify world entity\n");
562                 return;
563         }
564         if (ent->priv.server->free)
565         {
566                 VM_Warning("droptofloor: can not modify free entity\n");
567                 return;
568         }
569
570         VectorCopy (ent->fields.client->origin, end);
571         end[2] -= 256;
572
573         trace = CL_TraceBox(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
574
575         if (trace.fraction != 1)
576         {
577                 VectorCopy (trace.endpos, ent->fields.client->origin);
578                 ent->fields.client->flags = (int)ent->fields.client->flags | FL_ONGROUND;
579                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.groundentity)))
580                         val->edict = PRVM_EDICT_TO_PROG(trace.ent);
581                 PRVM_G_FLOAT(OFS_RETURN) = 1;
582                 // if support is destroyed, keep suspended (gross hack for floating items in various maps)
583 //              ent->priv.server->suspendedinairflag = true;
584         }
585 }
586
587 // #35 void(float style, string value) lightstyle
588 static void VM_CL_lightstyle (void)
589 {
590         int                     i;
591         const char      *c;
592
593         VM_SAFEPARMCOUNT(2, VM_CL_lightstyle);
594
595         i = (int)PRVM_G_FLOAT(OFS_PARM0);
596         c = PRVM_G_STRING(OFS_PARM1);
597         if (i >= cl.max_lightstyle)
598         {
599                 VM_Warning("VM_CL_lightstyle >= MAX_LIGHTSTYLES\n");
600                 return;
601         }
602         strlcpy (cl.lightstyle[i].map, c, sizeof (cl.lightstyle[i].map));
603         cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
604         cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map);
605 }
606
607 // #40 float(entity e) checkbottom
608 static void VM_CL_checkbottom (void)
609 {
610         static int              cs_yes, cs_no;
611         prvm_edict_t    *ent;
612         vec3_t                  mins, maxs, start, stop;
613         trace_t                 trace;
614         int                             x, y;
615         float                   mid, bottom;
616
617         VM_SAFEPARMCOUNT(1, VM_CL_checkbottom);
618         ent = PRVM_G_EDICT(OFS_PARM0);
619         PRVM_G_FLOAT(OFS_RETURN) = 0;
620
621         VectorAdd (ent->fields.client->origin, ent->fields.client->mins, mins);
622         VectorAdd (ent->fields.client->origin, ent->fields.client->maxs, maxs);
623
624 // if all of the points under the corners are solid world, don't bother
625 // with the tougher checks
626 // the corners must be within 16 of the midpoint
627         start[2] = mins[2] - 1;
628         for     (x=0 ; x<=1 ; x++)
629                 for     (y=0 ; y<=1 ; y++)
630                 {
631                         start[0] = x ? maxs[0] : mins[0];
632                         start[1] = y ? maxs[1] : mins[1];
633                         if (!(CL_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
634                                 goto realcheck;
635                 }
636
637         cs_yes++;
638         PRVM_G_FLOAT(OFS_RETURN) = true;
639         return;         // we got out easy
640
641 realcheck:
642         cs_no++;
643 //
644 // check it for real...
645 //
646         start[2] = mins[2];
647
648 // the midpoint must be within 16 of the bottom
649         start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
650         start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
651         stop[2] = start[2] - 2*sv_stepheight.value;
652         trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
653
654         if (trace.fraction == 1.0)
655                 return;
656
657         mid = bottom = trace.endpos[2];
658
659 // the corners must be within 16 of the midpoint
660         for     (x=0 ; x<=1 ; x++)
661                 for     (y=0 ; y<=1 ; y++)
662                 {
663                         start[0] = stop[0] = x ? maxs[0] : mins[0];
664                         start[1] = stop[1] = y ? maxs[1] : mins[1];
665
666                         trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
667
668                         if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
669                                 bottom = trace.endpos[2];
670                         if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
671                                 return;
672                 }
673
674         cs_yes++;
675         PRVM_G_FLOAT(OFS_RETURN) = true;
676 }
677
678 // #41 float(vector v) pointcontents
679 static void VM_CL_pointcontents (void)
680 {
681         VM_SAFEPARMCOUNT(1, VM_CL_pointcontents);
682         PRVM_G_FLOAT(OFS_RETURN) = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, CL_PointSuperContents(PRVM_G_VECTOR(OFS_PARM0)));
683 }
684
685 // #48 void(vector o, vector d, float color, float count) particle
686 static void VM_CL_particle (void)
687 {
688         float   *org, *dir;
689         int             count;
690         unsigned char   color;
691         VM_SAFEPARMCOUNT(4, VM_CL_particle);
692
693         org = PRVM_G_VECTOR(OFS_PARM0);
694         dir = PRVM_G_VECTOR(OFS_PARM1);
695         color = (int)PRVM_G_FLOAT(OFS_PARM2);
696         count = (int)PRVM_G_FLOAT(OFS_PARM3);
697         CL_ParticleEffect(EFFECT_SVC_PARTICLE, count, org, org, dir, dir, NULL, color);
698 }
699
700 // #74 void(vector pos, string samp, float vol, float atten) ambientsound
701 static void VM_CL_ambientsound (void)
702 {
703         float   *f;
704         sfx_t   *s;
705         VM_SAFEPARMCOUNT(4, VM_CL_ambientsound);
706         s = S_FindName(PRVM_G_STRING(OFS_PARM0));
707         f = PRVM_G_VECTOR(OFS_PARM1);
708         S_StaticSound (s, f, PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3)*64);
709 }
710
711 // #92 vector(vector org) getlight (DP_QC_GETLIGHT)
712 static void VM_CL_getlight (void)
713 {
714         vec3_t ambientcolor, diffusecolor, diffusenormal;
715         vec_t *p;
716
717         VM_SAFEPARMCOUNT(1, VM_CL_getlight);
718
719         p = PRVM_G_VECTOR(OFS_PARM0);
720         VectorClear(ambientcolor);
721         VectorClear(diffusecolor);
722         VectorClear(diffusenormal);
723         if (cl.worldmodel && cl.worldmodel->brush.LightPoint)
724                 cl.worldmodel->brush.LightPoint(cl.worldmodel, p, ambientcolor, diffusecolor, diffusenormal);
725         VectorMA(ambientcolor, 0.5, diffusecolor, PRVM_G_VECTOR(OFS_RETURN));
726 }
727
728
729 //============================================================================
730 //[515]: SCENE MANAGER builtins
731 extern qboolean CSQC_AddRenderEdict (prvm_edict_t *ed, int edictnum);//csprogs.c
732
733 static void CSQC_R_RecalcView (void)
734 {
735         extern matrix4x4_t viewmodelmatrix;
736         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);
737         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);
738 }
739
740 void CL_RelinkLightFlashes(void);
741 //#300 void() clearscene (EXT_CSQC)
742 void VM_CL_R_ClearScene (void)
743 {
744         VM_SAFEPARMCOUNT(0, VM_CL_R_ClearScene);
745         // clear renderable entity and light lists
746         r_refdef.scene.numentities = 0;
747         r_refdef.scene.numlights = 0;
748         // FIXME: restore these to the values from VM_CL_UpdateView
749         r_refdef.view.x = 0;
750         r_refdef.view.y = 0;
751         r_refdef.view.z = 0;
752         r_refdef.view.width = vid.width;
753         r_refdef.view.height = vid.height;
754         r_refdef.view.depth = 1;
755         // FIXME: restore frustum_x/frustum_y
756         r_refdef.view.useperspective = true;
757         r_refdef.view.frustum_y = tan(scr_fov.value * M_PI / 360.0) * (3.0/4.0) * cl.viewzoom;
758         r_refdef.view.frustum_x = r_refdef.view.frustum_y * (float)r_refdef.view.width / (float)r_refdef.view.height / vid_pixelheight.value;
759         r_refdef.view.frustum_x *= r_refdef.frustumscale_x;
760         r_refdef.view.frustum_y *= r_refdef.frustumscale_y;
761         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;
762         r_refdef.view.ortho_y = scr_fov.value * (3.0 / 4.0);
763         r_refdef.view.clear = true;
764         r_refdef.view.isoverlay = false;
765         // FIXME: restore cl.csqc_origin
766         // FIXME: restore cl.csqc_angles
767         cl.csqc_vidvars.drawworld = true;
768         cl.csqc_vidvars.drawenginesbar = false;
769         cl.csqc_vidvars.drawcrosshair = false;
770 }
771
772 //#301 void(float mask) addentities (EXT_CSQC)
773 extern void CSQC_Predraw (prvm_edict_t *ed);//csprogs.c
774 extern void CSQC_Think (prvm_edict_t *ed);//csprogs.c
775 void VM_CL_R_AddEntities (void)
776 {
777         double t = Sys_DoubleTime();
778         int                     i, drawmask;
779         prvm_edict_t *ed;
780         VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntities);
781         drawmask = (int)PRVM_G_FLOAT(OFS_PARM0);
782         CSQC_RelinkAllEntities(drawmask);
783         CL_RelinkLightFlashes();
784
785         prog->globals.client->time = cl.time;
786         for(i=1;i<prog->num_edicts;i++)
787         {
788                 ed = &prog->edicts[i];
789                 if(ed->priv.required->free)
790                         continue;
791                 CSQC_Think(ed);
792                 if(ed->priv.required->free)
793                         continue;
794                 // note that for RF_USEAXIS entities, Predraw sets v_forward/v_right/v_up globals that are read by CSQC_AddRenderEdict
795                 CSQC_Predraw(ed);
796                 if(ed->priv.required->free)
797                         continue;
798                 if(!((int)ed->fields.client->drawmask & drawmask))
799                         continue;
800                 CSQC_AddRenderEdict(ed, i);
801         }
802
803         // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView
804         prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t;
805 }
806
807 //#302 void(entity ent) addentity (EXT_CSQC)
808 void VM_CL_R_AddEntity (void)
809 {
810         double t = Sys_DoubleTime();
811         VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntity);
812         CSQC_AddRenderEdict(PRVM_G_EDICT(OFS_PARM0), 0);
813         prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t;
814 }
815
816 //#303 float(float property, ...) setproperty (EXT_CSQC)
817 void VM_CL_R_SetView (void)
818 {
819         int             c;
820         float   *f;
821         float   k;
822
823         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_R_SetView);
824
825         c = (int)PRVM_G_FLOAT(OFS_PARM0);
826         f = PRVM_G_VECTOR(OFS_PARM1);
827         k = PRVM_G_FLOAT(OFS_PARM1);
828
829         switch(c)
830         {
831         case VF_MIN:
832                 r_refdef.view.x = (int)(f[0]);
833                 r_refdef.view.y = (int)(f[1]);
834                 break;
835         case VF_MIN_X:
836                 r_refdef.view.x = (int)(k);
837                 break;
838         case VF_MIN_Y:
839                 r_refdef.view.y = (int)(k);
840                 break;
841         case VF_SIZE:
842                 r_refdef.view.width = (int)(f[0]);
843                 r_refdef.view.height = (int)(f[1]);
844                 break;
845         case VF_SIZE_X:
846                 r_refdef.view.width = (int)(k);
847                 break;
848         case VF_SIZE_Y:
849                 r_refdef.view.height = (int)(k);
850                 break;
851         case VF_VIEWPORT:
852                 r_refdef.view.x = (int)(f[0]);
853                 r_refdef.view.y = (int)(f[1]);
854                 f = PRVM_G_VECTOR(OFS_PARM2);
855                 r_refdef.view.width = (int)(f[0]);
856                 r_refdef.view.height = (int)(f[1]);
857                 break;
858         case VF_FOV:
859                 r_refdef.view.frustum_x = tan(f[0] * M_PI / 360.0);r_refdef.view.ortho_x = f[0];
860                 r_refdef.view.frustum_y = tan(f[1] * M_PI / 360.0);r_refdef.view.ortho_y = f[1];
861                 break;
862         case VF_FOVX:
863                 r_refdef.view.frustum_x = tan(k * M_PI / 360.0);r_refdef.view.ortho_x = k;
864                 break;
865         case VF_FOVY:
866                 r_refdef.view.frustum_y = tan(k * M_PI / 360.0);r_refdef.view.ortho_y = k;
867                 break;
868         case VF_ORIGIN:
869                 VectorCopy(f, cl.csqc_origin);
870                 CSQC_R_RecalcView();
871                 break;
872         case VF_ORIGIN_X:
873                 cl.csqc_origin[0] = k;
874                 CSQC_R_RecalcView();
875                 break;
876         case VF_ORIGIN_Y:
877                 cl.csqc_origin[1] = k;
878                 CSQC_R_RecalcView();
879                 break;
880         case VF_ORIGIN_Z:
881                 cl.csqc_origin[2] = k;
882                 CSQC_R_RecalcView();
883                 break;
884         case VF_ANGLES:
885                 VectorCopy(f, cl.csqc_angles);
886                 CSQC_R_RecalcView();
887                 break;
888         case VF_ANGLES_X:
889                 cl.csqc_angles[0] = k;
890                 CSQC_R_RecalcView();
891                 break;
892         case VF_ANGLES_Y:
893                 cl.csqc_angles[1] = k;
894                 CSQC_R_RecalcView();
895                 break;
896         case VF_ANGLES_Z:
897                 cl.csqc_angles[2] = k;
898                 CSQC_R_RecalcView();
899                 break;
900         case VF_DRAWWORLD:
901                 cl.csqc_vidvars.drawworld = k != 0;
902                 break;
903         case VF_DRAWENGINESBAR:
904                 cl.csqc_vidvars.drawenginesbar = k != 0;
905                 break;
906         case VF_DRAWCROSSHAIR:
907                 cl.csqc_vidvars.drawcrosshair = k != 0;
908                 break;
909         case VF_CL_VIEWANGLES:
910                 VectorCopy(f, cl.viewangles);
911                 break;
912         case VF_CL_VIEWANGLES_X:
913                 cl.viewangles[0] = k;
914                 break;
915         case VF_CL_VIEWANGLES_Y:
916                 cl.viewangles[1] = k;
917                 break;
918         case VF_CL_VIEWANGLES_Z:
919                 cl.viewangles[2] = k;
920                 break;
921         case VF_PERSPECTIVE:
922                 r_refdef.view.useperspective = k != 0;
923                 break;
924         case VF_CLEARSCREEN:
925                 r_refdef.view.isoverlay = !k;
926                 break;
927         default:
928                 PRVM_G_FLOAT(OFS_RETURN) = 0;
929                 VM_Warning("VM_CL_R_SetView : unknown parm %i\n", c);
930                 return;
931         }
932         PRVM_G_FLOAT(OFS_RETURN) = 1;
933 }
934
935 //#305 void(vector org, float radius, vector lightcolours[, float style, string cubemapname, float pflags]) adddynamiclight (EXT_CSQC)
936 void VM_CL_R_AddDynamicLight (void)
937 {
938         double t = Sys_DoubleTime();
939         vec_t *org;
940         float radius = 300;
941         vec_t *col;
942         int style = -1;
943         const char *cubemapname = NULL;
944         int pflags = PFLAGS_CORONA | PFLAGS_FULLDYNAMIC;
945         float coronaintensity = 1;
946         float coronasizescale = 0.25;
947         qboolean castshadow = true;
948         float ambientscale = 0;
949         float diffusescale = 1;
950         float specularscale = 1;
951         matrix4x4_t matrix;
952         vec3_t forward, left, up;
953         VM_SAFEPARMCOUNTRANGE(3, 8, VM_CL_R_AddDynamicLight);
954
955         // if we've run out of dlights, just return
956         if (r_refdef.scene.numlights >= MAX_DLIGHTS)
957                 return;
958
959         org = PRVM_G_VECTOR(OFS_PARM0);
960         radius = PRVM_G_FLOAT(OFS_PARM1);
961         col = PRVM_G_VECTOR(OFS_PARM2);
962         if (prog->argc >= 4)
963         {
964                 style = (int)PRVM_G_FLOAT(OFS_PARM3);
965                 if (style >= MAX_LIGHTSTYLES)
966                 {
967                         Con_DPrintf("VM_CL_R_AddDynamicLight: out of bounds lightstyle index %i\n", style);
968                         style = -1;
969                 }
970         }
971         if (prog->argc >= 5)
972                 cubemapname = PRVM_G_STRING(OFS_PARM4);
973         if (prog->argc >= 6)
974                 pflags = (int)PRVM_G_FLOAT(OFS_PARM5);
975         coronaintensity = (pflags & PFLAGS_CORONA) != 0;
976         castshadow = (pflags & PFLAGS_NOSHADOW) == 0;
977
978         VectorScale(prog->globals.client->v_forward, radius, forward);
979         VectorScale(prog->globals.client->v_right, -radius, left);
980         VectorScale(prog->globals.client->v_up, radius, up);
981         Matrix4x4_FromVectors(&matrix, forward, left, up, org);
982
983         R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &matrix, col, style, cubemapname, castshadow, coronaintensity, coronasizescale, ambientscale, diffusescale, specularscale, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
984         r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
985         prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t;
986 }
987
988 //============================================================================
989
990 //#310 vector (vector v) cs_unproject (EXT_CSQC)
991 static void VM_CL_unproject (void)
992 {
993         float   *f;
994         vec3_t  temp;
995
996         VM_SAFEPARMCOUNT(1, VM_CL_unproject);
997         f = PRVM_G_VECTOR(OFS_PARM0);
998         if(v_flipped.integer)
999                 f[0] = (2 * r_refdef.view.x + r_refdef.view.width) * (vid_conwidth.integer / (float) vid.width) - f[0];
1000         VectorSet(temp,
1001                 f[2],
1002                 (-1.0 + 2.0 * (f[0] / (vid_conwidth.integer / (float) vid.width) - r_refdef.view.x) / r_refdef.view.width) * f[2] * -r_refdef.view.frustum_x,
1003                 (-1.0 + 2.0 * (f[1] / (vid_conheight.integer / (float) vid.height) - r_refdef.view.y) / r_refdef.view.height) * f[2] * -r_refdef.view.frustum_y);
1004         Matrix4x4_Transform(&r_refdef.view.matrix, temp, PRVM_G_VECTOR(OFS_RETURN));
1005 }
1006
1007 //#311 vector (vector v) cs_project (EXT_CSQC)
1008 static void VM_CL_project (void)
1009 {
1010         float   *f;
1011         vec3_t  v;
1012         matrix4x4_t m;
1013
1014         VM_SAFEPARMCOUNT(1, VM_CL_project);
1015         f = PRVM_G_VECTOR(OFS_PARM0);
1016         Matrix4x4_Invert_Simple(&m, &r_refdef.view.matrix);
1017         Matrix4x4_Transform(&m, f, v);
1018         if(v_flipped.integer)
1019                 v[1] = -v[1];
1020         VectorSet(PRVM_G_VECTOR(OFS_RETURN),
1021                 (vid_conwidth.integer / (float) vid.width) * (r_refdef.view.x + r_refdef.view.width*0.5*(1.0+v[1]/v[0]/-r_refdef.view.frustum_x)),
1022                 (vid_conheight.integer / (float) vid.height) * (r_refdef.view.y + r_refdef.view.height*0.5*(1.0+v[2]/v[0]/-r_refdef.view.frustum_y)),
1023                 v[0]);
1024 }
1025
1026 //#330 float(float stnum) getstatf (EXT_CSQC)
1027 static void VM_CL_getstatf (void)
1028 {
1029         int i;
1030         union
1031         {
1032                 float f;
1033                 int l;
1034         }dat;
1035         VM_SAFEPARMCOUNT(1, VM_CL_getstatf);
1036         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1037         if(i < 0 || i >= MAX_CL_STATS)
1038         {
1039                 VM_Warning("VM_CL_getstatf: index>=MAX_CL_STATS or index<0\n");
1040                 return;
1041         }
1042         dat.l = cl.stats[i];
1043         PRVM_G_FLOAT(OFS_RETURN) =  dat.f;
1044 }
1045
1046 //#331 float(float stnum) getstati (EXT_CSQC)
1047 static void VM_CL_getstati (void)
1048 {
1049         int i, index;
1050         int firstbit, bitcount;
1051
1052         VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_getstati);
1053
1054         index = (int)PRVM_G_FLOAT(OFS_PARM0);
1055         if (prog->argc > 1)
1056         {
1057                 firstbit = (int)PRVM_G_FLOAT(OFS_PARM1);
1058                 if (prog->argc > 2)
1059                         bitcount = (int)PRVM_G_FLOAT(OFS_PARM2);
1060                 else
1061                         bitcount = 1;
1062         }
1063         else
1064         {
1065                 firstbit = 0;
1066                 bitcount = 32;
1067         }
1068
1069         if(index < 0 || index >= MAX_CL_STATS)
1070         {
1071                 VM_Warning("VM_CL_getstati: index>=MAX_CL_STATS or index<0\n");
1072                 return;
1073         }
1074         i = cl.stats[index];
1075         if (bitcount != 32)     //32 causes the mask to overflow, so there's nothing to subtract from.
1076                 i = (((unsigned int)i)&(((1<<bitcount)-1)<<firstbit))>>firstbit;
1077         PRVM_G_FLOAT(OFS_RETURN) = i;
1078 }
1079
1080 //#332 string(float firststnum) getstats (EXT_CSQC)
1081 static void VM_CL_getstats (void)
1082 {
1083         int i;
1084         char t[17];
1085         VM_SAFEPARMCOUNT(1, VM_CL_getstats);
1086         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1087         if(i < 0 || i > MAX_CL_STATS-4)
1088         {
1089                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1090                 VM_Warning("VM_CL_getstats: index>MAX_CL_STATS-4 or index<0\n");
1091                 return;
1092         }
1093         strlcpy(t, (char*)&cl.stats[i], sizeof(t));
1094         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
1095 }
1096
1097 //#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
1098 static void VM_CL_setmodelindex (void)
1099 {
1100         int                             i;
1101         prvm_edict_t    *t;
1102         struct model_s  *model;
1103
1104         VM_SAFEPARMCOUNT(2, VM_CL_setmodelindex);
1105
1106         t = PRVM_G_EDICT(OFS_PARM0);
1107
1108         i = (int)PRVM_G_FLOAT(OFS_PARM1);
1109
1110         t->fields.client->model = 0;
1111         t->fields.client->modelindex = 0;
1112
1113         if (!i)
1114                 return;
1115
1116         model = CL_GetModelByIndex(i);
1117         if (!model)
1118         {
1119                 VM_Warning("VM_CL_setmodelindex: null model\n");
1120                 return;
1121         }
1122         t->fields.client->model = PRVM_SetEngineString(model->name);
1123         t->fields.client->modelindex = i;
1124
1125         // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
1126         if (model)
1127         {
1128                 SetMinMaxSize (t, model->normalmins, model->normalmaxs);
1129         }
1130         else
1131                 SetMinMaxSize (t, vec3_origin, vec3_origin);
1132 }
1133
1134 //#334 string(float mdlindex) modelnameforindex (EXT_CSQC)
1135 static void VM_CL_modelnameforindex (void)
1136 {
1137         dp_model_t *model;
1138
1139         VM_SAFEPARMCOUNT(1, VM_CL_modelnameforindex);
1140
1141         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1142         model = CL_GetModelByIndex((int)PRVM_G_FLOAT(OFS_PARM0));
1143         PRVM_G_INT(OFS_RETURN) = model ? PRVM_SetEngineString(model->name) : 0;
1144 }
1145
1146 //#335 float(string effectname) particleeffectnum (EXT_CSQC)
1147 static void VM_CL_particleeffectnum (void)
1148 {
1149         int                     i;
1150         VM_SAFEPARMCOUNT(1, VM_CL_particleeffectnum);
1151         i = CL_ParticleEffectIndexForName(PRVM_G_STRING(OFS_PARM0));
1152         if (i == 0)
1153                 i = -1;
1154         PRVM_G_FLOAT(OFS_RETURN) = i;
1155 }
1156
1157 // #336 void(entity ent, float effectnum, vector start, vector end[, float color]) trailparticles (EXT_CSQC)
1158 static void VM_CL_trailparticles (void)
1159 {
1160         int                             i;
1161         float                   *start, *end;
1162         prvm_edict_t    *t;
1163         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_trailparticles);
1164
1165         t = PRVM_G_EDICT(OFS_PARM0);
1166         i               = (int)PRVM_G_FLOAT(OFS_PARM1);
1167         start   = PRVM_G_VECTOR(OFS_PARM2);
1168         end             = PRVM_G_VECTOR(OFS_PARM3);
1169
1170         if (i < 0)
1171                 return;
1172         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);
1173 }
1174
1175 //#337 void(float effectnum, vector origin, vector dir, float count[, float color]) pointparticles (EXT_CSQC)
1176 static void VM_CL_pointparticles (void)
1177 {
1178         int                     i, n;
1179         float           *f, *v;
1180         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_pointparticles);
1181         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1182         f = PRVM_G_VECTOR(OFS_PARM1);
1183         v = PRVM_G_VECTOR(OFS_PARM2);
1184         n = (int)PRVM_G_FLOAT(OFS_PARM3);
1185         if (i < 0)
1186                 return;
1187         CL_ParticleEffect(i, n, f, f, v, v, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0);
1188 }
1189
1190 //#342 string(float keynum) getkeybind (EXT_CSQC)
1191 static void VM_CL_getkeybind (void)
1192 {
1193         VM_SAFEPARMCOUNT(1, VM_CL_getkeybind);
1194         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_GetBind((int)PRVM_G_FLOAT(OFS_PARM0)));
1195 }
1196
1197 //#343 void(float usecursor) setcursormode (EXT_CSQC)
1198 static void VM_CL_setcursormode (void)
1199 {
1200         VM_SAFEPARMCOUNT(1, VM_CL_setcursormode);
1201         cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0) != 0;
1202         cl_ignoremousemoves = 2;
1203 }
1204
1205 //#344 vector() getmousepos (EXT_CSQC)
1206 static void VM_CL_getmousepos(void)
1207 {
1208         VM_SAFEPARMCOUNT(0,VM_CL_getmousepos);
1209
1210         if (key_consoleactive || key_dest != key_game)
1211                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), 0, 0, 0);
1212         else if (cl.csqc_wantsmousemove)
1213                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_windowmouse_x * vid_conwidth.integer / vid.width, in_windowmouse_y * vid_conheight.integer / vid.height, 0);
1214         else
1215                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_mouse_x * vid_conwidth.integer / vid.width, in_mouse_y * vid_conheight.integer / vid.height, 0);
1216 }
1217
1218 //#345 float(float framenum) getinputstate (EXT_CSQC)
1219 static void VM_CL_getinputstate (void)
1220 {
1221         int i, frame;
1222         VM_SAFEPARMCOUNT(1, VM_CL_getinputstate);
1223         frame = (int)PRVM_G_FLOAT(OFS_PARM0);
1224         PRVM_G_FLOAT(OFS_RETURN) = false;
1225         for (i = 0;i < CL_MAX_USERCMDS;i++)
1226         {
1227                 if (cl.movecmd[i].sequence == frame)
1228                 {
1229                         VectorCopy(cl.movecmd[i].viewangles, prog->globals.client->input_angles);
1230                         prog->globals.client->input_buttons = cl.movecmd[i].buttons; // FIXME: this should not be directly exposed to csqc (translation layer needed?)
1231                         prog->globals.client->input_movevalues[0] = cl.movecmd[i].forwardmove;
1232                         prog->globals.client->input_movevalues[1] = cl.movecmd[i].sidemove;
1233                         prog->globals.client->input_movevalues[2] = cl.movecmd[i].upmove;
1234                         prog->globals.client->input_timelength = cl.movecmd[i].frametime;
1235                         if(cl.movecmd[i].crouch)
1236                         {
1237                                 VectorCopy(cl.playercrouchmins, prog->globals.client->pmove_mins);
1238                                 VectorCopy(cl.playercrouchmaxs, prog->globals.client->pmove_maxs);
1239                         }
1240                         else
1241                         {
1242                                 VectorCopy(cl.playerstandmins, prog->globals.client->pmove_mins);
1243                                 VectorCopy(cl.playerstandmaxs, prog->globals.client->pmove_maxs);
1244                         }
1245                         PRVM_G_FLOAT(OFS_RETURN) = true;
1246                 }
1247         }
1248 }
1249
1250 //#346 void(float sens) setsensitivityscaler (EXT_CSQC)
1251 static void VM_CL_setsensitivityscale (void)
1252 {
1253         VM_SAFEPARMCOUNT(1, VM_CL_setsensitivityscale);
1254         cl.sensitivityscale = PRVM_G_FLOAT(OFS_PARM0);
1255 }
1256
1257 //#347 void() runstandardplayerphysics (EXT_CSQC)
1258 static void VM_CL_runplayerphysics (void)
1259 {
1260 }
1261
1262 //#348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
1263 static void VM_CL_getplayerkey (void)
1264 {
1265         int                     i;
1266         char            t[128];
1267         const char      *c;
1268
1269         VM_SAFEPARMCOUNT(2, VM_CL_getplayerkey);
1270
1271         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1272         c = PRVM_G_STRING(OFS_PARM1);
1273         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1274         Sbar_SortFrags();
1275
1276         if (i < 0)
1277                 i = Sbar_GetSortedPlayerIndex(-1-i);
1278         if(i < 0 || i >= cl.maxclients)
1279                 return;
1280
1281         t[0] = 0;
1282
1283         if(!strcasecmp(c, "name"))
1284                 strlcpy(t, cl.scores[i].name, sizeof(t));
1285         else
1286                 if(!strcasecmp(c, "frags"))
1287                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].frags);
1288         else
1289                 if(!strcasecmp(c, "ping"))
1290                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_ping);
1291         else
1292                 if(!strcasecmp(c, "pl"))
1293                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_packetloss);
1294         else
1295                 if(!strcasecmp(c, "movementloss"))
1296                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_movementloss);
1297         else
1298                 if(!strcasecmp(c, "entertime"))
1299                         dpsnprintf(t, sizeof(t), "%f", cl.scores[i].qw_entertime);
1300         else
1301                 if(!strcasecmp(c, "colors"))
1302                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors);
1303         else
1304                 if(!strcasecmp(c, "topcolor"))
1305                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors & 0xf0);
1306         else
1307                 if(!strcasecmp(c, "bottomcolor"))
1308                         dpsnprintf(t, sizeof(t), "%i", (cl.scores[i].colors &15)<<4);
1309         else
1310                 if(!strcasecmp(c, "viewentity"))
1311                         dpsnprintf(t, sizeof(t), "%i", i+1);
1312         if(!t[0])
1313                 return;
1314         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
1315 }
1316
1317 //#349 float() isdemo (EXT_CSQC)
1318 static void VM_CL_isdemo (void)
1319 {
1320         VM_SAFEPARMCOUNT(0, VM_CL_isdemo);
1321         PRVM_G_FLOAT(OFS_RETURN) = cls.demoplayback;
1322 }
1323
1324 //#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
1325 static void VM_CL_setlistener (void)
1326 {
1327         VM_SAFEPARMCOUNT(4, VM_CL_setlistener);
1328         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));
1329         cl.csqc_usecsqclistener = true; //use csqc listener at this frame
1330 }
1331
1332 //#352 void(string cmdname) registercommand (EXT_CSQC)
1333 static void VM_CL_registercmd (void)
1334 {
1335         char *t;
1336         VM_SAFEPARMCOUNT(1, VM_CL_registercmd);
1337         if(!Cmd_Exists(PRVM_G_STRING(OFS_PARM0)))
1338         {
1339                 size_t alloclen;
1340
1341                 alloclen = strlen(PRVM_G_STRING(OFS_PARM0)) + 1;
1342                 t = (char *)Z_Malloc(alloclen);
1343                 memcpy(t, PRVM_G_STRING(OFS_PARM0), alloclen);
1344                 Cmd_AddCommand(t, NULL, "console command created by QuakeC");
1345         }
1346         else
1347                 Cmd_AddCommand(PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
1348
1349 }
1350
1351 //#360 float() readbyte (EXT_CSQC)
1352 static void VM_CL_ReadByte (void)
1353 {
1354         VM_SAFEPARMCOUNT(0, VM_CL_ReadByte);
1355         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadByte();
1356 }
1357
1358 //#361 float() readchar (EXT_CSQC)
1359 static void VM_CL_ReadChar (void)
1360 {
1361         VM_SAFEPARMCOUNT(0, VM_CL_ReadChar);
1362         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadChar();
1363 }
1364
1365 //#362 float() readshort (EXT_CSQC)
1366 static void VM_CL_ReadShort (void)
1367 {
1368         VM_SAFEPARMCOUNT(0, VM_CL_ReadShort);
1369         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadShort();
1370 }
1371
1372 //#363 float() readlong (EXT_CSQC)
1373 static void VM_CL_ReadLong (void)
1374 {
1375         VM_SAFEPARMCOUNT(0, VM_CL_ReadLong);
1376         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadLong();
1377 }
1378
1379 //#364 float() readcoord (EXT_CSQC)
1380 static void VM_CL_ReadCoord (void)
1381 {
1382         VM_SAFEPARMCOUNT(0, VM_CL_ReadCoord);
1383         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadCoord(cls.protocol);
1384 }
1385
1386 //#365 float() readangle (EXT_CSQC)
1387 static void VM_CL_ReadAngle (void)
1388 {
1389         VM_SAFEPARMCOUNT(0, VM_CL_ReadAngle);
1390         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadAngle(cls.protocol);
1391 }
1392
1393 //#366 string() readstring (EXT_CSQC)
1394 static void VM_CL_ReadString (void)
1395 {
1396         VM_SAFEPARMCOUNT(0, VM_CL_ReadString);
1397         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(MSG_ReadString());
1398 }
1399
1400 //#367 float() readfloat (EXT_CSQC)
1401 static void VM_CL_ReadFloat (void)
1402 {
1403         VM_SAFEPARMCOUNT(0, VM_CL_ReadFloat);
1404         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat();
1405 }
1406
1407 //#501 string() readpicture (DP_CSQC_READWRITEPICTURE)
1408 extern cvar_t cl_readpicture_force;
1409 static void VM_CL_ReadPicture (void)
1410 {
1411         const char *name;
1412         unsigned char *data;
1413         unsigned char *buf;
1414         int size;
1415         int i;
1416         cachepic_t *pic;
1417
1418         VM_SAFEPARMCOUNT(0, VM_CL_ReadPicture);
1419
1420         name = MSG_ReadString();
1421         size = MSG_ReadShort();
1422
1423         // check if a texture of that name exists
1424         // if yes, it is used and the data is discarded
1425         // if not, the (low quality) data is used to build a new texture, whose name will get returned
1426
1427         pic = Draw_CachePic_Flags (name, CACHEPICFLAG_NOTPERSISTENT);
1428
1429         if(size)
1430         {
1431                 if(pic->tex == r_texture_notexture)
1432                         pic->tex = NULL; // don't overwrite the notexture by Draw_NewPic
1433                 if(pic->tex && !cl_readpicture_force.integer)
1434                 {
1435                         // texture found and loaded
1436                         // skip over the jpeg as we don't need it
1437                         for(i = 0; i < size; ++i)
1438                                 MSG_ReadByte();
1439                 }
1440                 else
1441                 {
1442                         // texture not found
1443                         // use the attached jpeg as texture
1444                         buf = (unsigned char *) Mem_Alloc(tempmempool, size);
1445                         MSG_ReadBytes(size, buf);
1446                         data = JPEG_LoadImage_BGRA(buf, size);
1447                         Mem_Free(buf);
1448                         Draw_NewPic(name, image_width, image_height, false, data);
1449                         Mem_Free(data);
1450                 }
1451         }
1452
1453         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(name);
1454 }
1455
1456 //////////////////////////////////////////////////////////
1457
1458 static void VM_CL_makestatic (void)
1459 {
1460         prvm_edict_t *ent;
1461
1462         VM_SAFEPARMCOUNT(1, VM_CL_makestatic);
1463
1464         ent = PRVM_G_EDICT(OFS_PARM0);
1465         if (ent == prog->edicts)
1466         {
1467                 VM_Warning("makestatic: can not modify world entity\n");
1468                 return;
1469         }
1470         if (ent->priv.server->free)
1471         {
1472                 VM_Warning("makestatic: can not modify free entity\n");
1473                 return;
1474         }
1475
1476         if (cl.num_static_entities < cl.max_static_entities)
1477         {
1478                 int renderflags;
1479                 prvm_eval_t *val;
1480                 entity_t *staticent = &cl.static_entities[cl.num_static_entities++];
1481
1482                 // copy it to the current state
1483                 memset(staticent, 0, sizeof(*staticent));
1484                 staticent->render.model = CL_GetModelByIndex((int)ent->fields.client->modelindex);
1485                 staticent->render.framegroupblend[0].frame = (int)ent->fields.client->frame;
1486                 staticent->render.framegroupblend[0].lerp = 1;
1487                 // make torchs play out of sync
1488                 staticent->render.framegroupblend[0].start = lhrandom(-10, -1);
1489                 staticent->render.skinnum = (int)ent->fields.client->skin;
1490                 staticent->render.effects = (int)ent->fields.client->effects;
1491                 staticent->render.alpha = 1;
1492                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)) && val->_float) staticent->render.alpha = val->_float;
1493                 staticent->render.scale = 1;
1494                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)) && val->_float) staticent->render.scale = val->_float;
1495                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod)) && VectorLength2(val->vector)) VectorCopy(val->vector, staticent->render.colormod);
1496                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glowmod)) && VectorLength2(val->vector)) VectorCopy(val->vector, staticent->render.glowmod);
1497                 if (!VectorLength2(staticent->render.colormod))
1498                         VectorSet(staticent->render.colormod, 1, 1, 1);
1499                 if (!VectorLength2(staticent->render.glowmod))
1500                         VectorSet(staticent->render.glowmod, 1, 1, 1);
1501
1502                 renderflags = 0;
1503                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderflags)) && val->_float) renderflags = (int)val->_float;
1504                 if (renderflags & RF_USEAXIS)
1505                 {
1506                         vec3_t left;
1507                         VectorNegate(prog->globals.client->v_right, left);
1508                         Matrix4x4_FromVectors(&staticent->render.matrix, prog->globals.client->v_forward, left, prog->globals.client->v_up, ent->fields.client->origin);
1509                         Matrix4x4_Scale(&staticent->render.matrix, staticent->render.scale, 1);
1510                 }
1511                 else
1512                         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);
1513
1514                 // either fullbright or lit
1515                 if(!r_fullbright.integer)
1516                 {
1517                         if (!(staticent->render.effects & EF_FULLBRIGHT))
1518                                 staticent->render.flags |= RENDER_LIGHT;
1519                         else if(r_equalize_entities_fullbright.integer)
1520                                 staticent->render.flags |= RENDER_LIGHT | RENDER_EQUALIZE;
1521                 }
1522                 // turn off shadows from transparent objects
1523                 if (!(staticent->render.effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST)) && (staticent->render.alpha >= 1))
1524                         staticent->render.flags |= RENDER_SHADOW;
1525                 if (staticent->render.effects & EF_NODEPTHTEST)
1526                         staticent->render.flags |= RENDER_NODEPTHTEST;
1527                 if (staticent->render.effects & EF_ADDITIVE)
1528                         staticent->render.flags |= RENDER_ADDITIVE;
1529                 if (staticent->render.effects & EF_DOUBLESIDED)
1530                         staticent->render.flags |= RENDER_DOUBLESIDED;
1531
1532                 staticent->render.allowdecals = true;
1533                 CL_UpdateRenderEntity(&staticent->render);
1534         }
1535         else
1536                 Con_Printf("Too many static entities");
1537
1538 // throw the entity away now
1539         PRVM_ED_Free (ent);
1540 }
1541
1542 //=================================================================//
1543
1544 /*
1545 =================
1546 VM_CL_copyentity
1547
1548 copies data from one entity to another
1549
1550 copyentity(src, dst)
1551 =================
1552 */
1553 static void VM_CL_copyentity (void)
1554 {
1555         prvm_edict_t *in, *out;
1556         VM_SAFEPARMCOUNT(2, VM_CL_copyentity);
1557         in = PRVM_G_EDICT(OFS_PARM0);
1558         if (in == prog->edicts)
1559         {
1560                 VM_Warning("copyentity: can not read world entity\n");
1561                 return;
1562         }
1563         if (in->priv.server->free)
1564         {
1565                 VM_Warning("copyentity: can not read free entity\n");
1566                 return;
1567         }
1568         out = PRVM_G_EDICT(OFS_PARM1);
1569         if (out == prog->edicts)
1570         {
1571                 VM_Warning("copyentity: can not modify world entity\n");
1572                 return;
1573         }
1574         if (out->priv.server->free)
1575         {
1576                 VM_Warning("copyentity: can not modify free entity\n");
1577                 return;
1578         }
1579         memcpy(out->fields.vp, in->fields.vp, prog->progs->entityfields * 4);
1580         CL_LinkEdict(out);
1581 }
1582
1583 //=================================================================//
1584
1585 // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
1586 static void VM_CL_effect (void)
1587 {
1588         VM_SAFEPARMCOUNT(5, VM_CL_effect);
1589         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));
1590 }
1591
1592 // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
1593 static void VM_CL_te_blood (void)
1594 {
1595         float   *pos;
1596         vec3_t  pos2;
1597         VM_SAFEPARMCOUNT(3, VM_CL_te_blood);
1598         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
1599                 return;
1600         pos = PRVM_G_VECTOR(OFS_PARM0);
1601         CL_FindNonSolidLocation(pos, pos2, 4);
1602         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1603 }
1604
1605 // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
1606 static void VM_CL_te_bloodshower (void)
1607 {
1608         vec_t speed;
1609         vec3_t vel1, vel2;
1610         VM_SAFEPARMCOUNT(4, VM_CL_te_bloodshower);
1611         if (PRVM_G_FLOAT(OFS_PARM3) < 1)
1612                 return;
1613         speed = PRVM_G_FLOAT(OFS_PARM2);
1614         vel1[0] = -speed;
1615         vel1[1] = -speed;
1616         vel1[2] = -speed;
1617         vel2[0] = speed;
1618         vel2[1] = speed;
1619         vel2[2] = speed;
1620         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM3), PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), vel1, vel2, NULL, 0);
1621 }
1622
1623 // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
1624 static void VM_CL_te_explosionrgb (void)
1625 {
1626         float           *pos;
1627         vec3_t          pos2;
1628         matrix4x4_t     tempmatrix;
1629         VM_SAFEPARMCOUNT(2, VM_CL_te_explosionrgb);
1630         pos = PRVM_G_VECTOR(OFS_PARM0);
1631         CL_FindNonSolidLocation(pos, pos2, 10);
1632         CL_ParticleExplosion(pos2);
1633         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1634         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);
1635 }
1636
1637 // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
1638 static void VM_CL_te_particlecube (void)
1639 {
1640         VM_SAFEPARMCOUNT(7, VM_CL_te_particlecube);
1641         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));
1642 }
1643
1644 // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
1645 static void VM_CL_te_particlerain (void)
1646 {
1647         VM_SAFEPARMCOUNT(5, VM_CL_te_particlerain);
1648         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);
1649 }
1650
1651 // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
1652 static void VM_CL_te_particlesnow (void)
1653 {
1654         VM_SAFEPARMCOUNT(5, VM_CL_te_particlesnow);
1655         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);
1656 }
1657
1658 // #411 void(vector org, vector vel, float howmany) te_spark
1659 static void VM_CL_te_spark (void)
1660 {
1661         float           *pos;
1662         vec3_t          pos2;
1663         VM_SAFEPARMCOUNT(3, VM_CL_te_spark);
1664
1665         pos = PRVM_G_VECTOR(OFS_PARM0);
1666         CL_FindNonSolidLocation(pos, pos2, 4);
1667         CL_ParticleEffect(EFFECT_TE_SPARK, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1668 }
1669
1670 extern cvar_t cl_sound_ric_gunshot;
1671 // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
1672 static void VM_CL_te_gunshotquad (void)
1673 {
1674         float           *pos;
1675         vec3_t          pos2;
1676         int                     rnd;
1677         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshotquad);
1678
1679         pos = PRVM_G_VECTOR(OFS_PARM0);
1680         CL_FindNonSolidLocation(pos, pos2, 4);
1681         CL_ParticleEffect(EFFECT_TE_GUNSHOTQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1682         if(cl_sound_ric_gunshot.integer >= 2)
1683         {
1684                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1685                 else
1686                 {
1687                         rnd = rand() & 3;
1688                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1689                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1690                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1691                 }
1692         }
1693 }
1694
1695 // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
1696 static void VM_CL_te_spikequad (void)
1697 {
1698         float           *pos;
1699         vec3_t          pos2;
1700         int                     rnd;
1701         VM_SAFEPARMCOUNT(1, VM_CL_te_spikequad);
1702
1703         pos = PRVM_G_VECTOR(OFS_PARM0);
1704         CL_FindNonSolidLocation(pos, pos2, 4);
1705         CL_ParticleEffect(EFFECT_TE_SPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1706         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1707         else
1708         {
1709                 rnd = rand() & 3;
1710                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1711                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1712                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1713         }
1714 }
1715
1716 // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1)
1717 static void VM_CL_te_superspikequad (void)
1718 {
1719         float           *pos;
1720         vec3_t          pos2;
1721         int                     rnd;
1722         VM_SAFEPARMCOUNT(1, VM_CL_te_superspikequad);
1723
1724         pos = PRVM_G_VECTOR(OFS_PARM0);
1725         CL_FindNonSolidLocation(pos, pos2, 4);
1726         CL_ParticleEffect(EFFECT_TE_SUPERSPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1727         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
1728         else
1729         {
1730                 rnd = rand() & 3;
1731                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1732                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1733                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1734         }
1735 }
1736
1737 // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1)
1738 static void VM_CL_te_explosionquad (void)
1739 {
1740         float           *pos;
1741         vec3_t          pos2;
1742         VM_SAFEPARMCOUNT(1, VM_CL_te_explosionquad);
1743
1744         pos = PRVM_G_VECTOR(OFS_PARM0);
1745         CL_FindNonSolidLocation(pos, pos2, 10);
1746         CL_ParticleEffect(EFFECT_TE_EXPLOSIONQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1747         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1748 }
1749
1750 // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
1751 static void VM_CL_te_smallflash (void)
1752 {
1753         float           *pos;
1754         vec3_t          pos2;
1755         VM_SAFEPARMCOUNT(1, VM_CL_te_smallflash);
1756
1757         pos = PRVM_G_VECTOR(OFS_PARM0);
1758         CL_FindNonSolidLocation(pos, pos2, 10);
1759         CL_ParticleEffect(EFFECT_TE_SMALLFLASH, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1760 }
1761
1762 // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
1763 static void VM_CL_te_customflash (void)
1764 {
1765         float           *pos;
1766         vec3_t          pos2;
1767         matrix4x4_t     tempmatrix;
1768         VM_SAFEPARMCOUNT(4, VM_CL_te_customflash);
1769
1770         pos = PRVM_G_VECTOR(OFS_PARM0);
1771         CL_FindNonSolidLocation(pos, pos2, 4);
1772         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1773         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);
1774 }
1775
1776 // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
1777 static void VM_CL_te_gunshot (void)
1778 {
1779         float           *pos;
1780         vec3_t          pos2;
1781         int                     rnd;
1782         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshot);
1783
1784         pos = PRVM_G_VECTOR(OFS_PARM0);
1785         CL_FindNonSolidLocation(pos, pos2, 4);
1786         CL_ParticleEffect(EFFECT_TE_GUNSHOT, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1787         if(cl_sound_ric_gunshot.integer == 1 || cl_sound_ric_gunshot.integer == 3)
1788         {
1789                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1790                 else
1791                 {
1792                         rnd = rand() & 3;
1793                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1794                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1795                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1796                 }
1797         }
1798 }
1799
1800 // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
1801 static void VM_CL_te_spike (void)
1802 {
1803         float           *pos;
1804         vec3_t          pos2;
1805         int                     rnd;
1806         VM_SAFEPARMCOUNT(1, VM_CL_te_spike);
1807
1808         pos = PRVM_G_VECTOR(OFS_PARM0);
1809         CL_FindNonSolidLocation(pos, pos2, 4);
1810         CL_ParticleEffect(EFFECT_TE_SPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1811         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1812         else
1813         {
1814                 rnd = rand() & 3;
1815                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1816                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1817                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1818         }
1819 }
1820
1821 // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
1822 static void VM_CL_te_superspike (void)
1823 {
1824         float           *pos;
1825         vec3_t          pos2;
1826         int                     rnd;
1827         VM_SAFEPARMCOUNT(1, VM_CL_te_superspike);
1828
1829         pos = PRVM_G_VECTOR(OFS_PARM0);
1830         CL_FindNonSolidLocation(pos, pos2, 4);
1831         CL_ParticleEffect(EFFECT_TE_SUPERSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1832         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1833         else
1834         {
1835                 rnd = rand() & 3;
1836                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1837                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1838                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1839         }
1840 }
1841
1842 // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
1843 static void VM_CL_te_explosion (void)
1844 {
1845         float           *pos;
1846         vec3_t          pos2;
1847         VM_SAFEPARMCOUNT(1, VM_CL_te_explosion);
1848
1849         pos = PRVM_G_VECTOR(OFS_PARM0);
1850         CL_FindNonSolidLocation(pos, pos2, 10);
1851         CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1852         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1853 }
1854
1855 // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
1856 static void VM_CL_te_tarexplosion (void)
1857 {
1858         float           *pos;
1859         vec3_t          pos2;
1860         VM_SAFEPARMCOUNT(1, VM_CL_te_tarexplosion);
1861
1862         pos = PRVM_G_VECTOR(OFS_PARM0);
1863         CL_FindNonSolidLocation(pos, pos2, 10);
1864         CL_ParticleEffect(EFFECT_TE_TAREXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1865         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1866 }
1867
1868 // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
1869 static void VM_CL_te_wizspike (void)
1870 {
1871         float           *pos;
1872         vec3_t          pos2;
1873         VM_SAFEPARMCOUNT(1, VM_CL_te_wizspike);
1874
1875         pos = PRVM_G_VECTOR(OFS_PARM0);
1876         CL_FindNonSolidLocation(pos, pos2, 4);
1877         CL_ParticleEffect(EFFECT_TE_WIZSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1878         S_StartSound(-1, 0, cl.sfx_wizhit, pos2, 1, 1);
1879 }
1880
1881 // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
1882 static void VM_CL_te_knightspike (void)
1883 {
1884         float           *pos;
1885         vec3_t          pos2;
1886         VM_SAFEPARMCOUNT(1, VM_CL_te_knightspike);
1887
1888         pos = PRVM_G_VECTOR(OFS_PARM0);
1889         CL_FindNonSolidLocation(pos, pos2, 4);
1890         CL_ParticleEffect(EFFECT_TE_KNIGHTSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1891         S_StartSound(-1, 0, cl.sfx_knighthit, pos2, 1, 1);
1892 }
1893
1894 // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
1895 static void VM_CL_te_lavasplash (void)
1896 {
1897         VM_SAFEPARMCOUNT(1, VM_CL_te_lavasplash);
1898         CL_ParticleEffect(EFFECT_TE_LAVASPLASH, 1, PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM0), vec3_origin, vec3_origin, NULL, 0);
1899 }
1900
1901 // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
1902 static void VM_CL_te_teleport (void)
1903 {
1904         VM_SAFEPARMCOUNT(1, VM_CL_te_teleport);
1905         CL_ParticleEffect(EFFECT_TE_TELEPORT, 1, PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM0), vec3_origin, vec3_origin, NULL, 0);
1906 }
1907
1908 // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
1909 static void VM_CL_te_explosion2 (void)
1910 {
1911         float           *pos;
1912         vec3_t          pos2, color;
1913         matrix4x4_t     tempmatrix;
1914         int                     colorStart, colorLength;
1915         unsigned char           *tempcolor;
1916         VM_SAFEPARMCOUNT(3, VM_CL_te_explosion2);
1917
1918         pos = PRVM_G_VECTOR(OFS_PARM0);
1919         colorStart = (int)PRVM_G_FLOAT(OFS_PARM1);
1920         colorLength = (int)PRVM_G_FLOAT(OFS_PARM2);
1921         CL_FindNonSolidLocation(pos, pos2, 10);
1922         CL_ParticleExplosion2(pos2, colorStart, colorLength);
1923         tempcolor = palette_rgb[(rand()%colorLength) + colorStart];
1924         color[0] = tempcolor[0] * (2.0f / 255.0f);
1925         color[1] = tempcolor[1] * (2.0f / 255.0f);
1926         color[2] = tempcolor[2] * (2.0f / 255.0f);
1927         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1928         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);
1929         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1930 }
1931
1932
1933 // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
1934 static void VM_CL_te_lightning1 (void)
1935 {
1936         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning1);
1937         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt, true);
1938 }
1939
1940 // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
1941 static void VM_CL_te_lightning2 (void)
1942 {
1943         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning2);
1944         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt2, true);
1945 }
1946
1947 // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
1948 static void VM_CL_te_lightning3 (void)
1949 {
1950         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning3);
1951         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt3, false);
1952 }
1953
1954 // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
1955 static void VM_CL_te_beam (void)
1956 {
1957         VM_SAFEPARMCOUNT(3, VM_CL_te_beam);
1958         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_beam, false);
1959 }
1960
1961 // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
1962 static void VM_CL_te_plasmaburn (void)
1963 {
1964         float           *pos;
1965         vec3_t          pos2;
1966         VM_SAFEPARMCOUNT(1, VM_CL_te_plasmaburn);
1967
1968         pos = PRVM_G_VECTOR(OFS_PARM0);
1969         CL_FindNonSolidLocation(pos, pos2, 4);
1970         CL_ParticleEffect(EFFECT_TE_PLASMABURN, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1971 }
1972
1973 // #457 void(vector org, vector velocity, float howmany) te_flamejet (DP_TE_FLAMEJET)
1974 static void VM_CL_te_flamejet (void)
1975 {
1976         float *pos;
1977         vec3_t pos2;
1978         VM_SAFEPARMCOUNT(3, VM_CL_te_flamejet);
1979         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
1980                 return;
1981         pos = PRVM_G_VECTOR(OFS_PARM0);
1982         CL_FindNonSolidLocation(pos, pos2, 4);
1983         CL_ParticleEffect(EFFECT_TE_FLAMEJET, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1984 }
1985
1986
1987 // #443 void(entity e, entity tagentity, string tagname) setattachment
1988 void VM_CL_setattachment (void)
1989 {
1990         prvm_edict_t *e;
1991         prvm_edict_t *tagentity;
1992         const char *tagname;
1993         prvm_eval_t *v;
1994         int modelindex;
1995         dp_model_t *model;
1996         VM_SAFEPARMCOUNT(3, VM_CL_setattachment);
1997
1998         e = PRVM_G_EDICT(OFS_PARM0);
1999         tagentity = PRVM_G_EDICT(OFS_PARM1);
2000         tagname = PRVM_G_STRING(OFS_PARM2);
2001
2002         if (e == prog->edicts)
2003         {
2004                 VM_Warning("setattachment: can not modify world entity\n");
2005                 return;
2006         }
2007         if (e->priv.server->free)
2008         {
2009                 VM_Warning("setattachment: can not modify free entity\n");
2010                 return;
2011         }
2012
2013         if (tagentity == NULL)
2014                 tagentity = prog->edicts;
2015
2016         v = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.tag_entity);
2017         if (v)
2018                 v->edict = PRVM_EDICT_TO_PROG(tagentity);
2019
2020         v = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.tag_index);
2021         if (v)
2022                 v->_float = 0;
2023         if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0])
2024         {
2025                 modelindex = (int)tagentity->fields.client->modelindex;
2026                 model = CL_GetModelByIndex(modelindex);
2027                 if (model)
2028                 {
2029                         v->_float = Mod_Alias_GetTagIndexForName(model, (int)tagentity->fields.client->skin, tagname);
2030                         if (v->_float == 0)
2031                                 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);
2032                 }
2033                 else
2034                         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));
2035         }
2036 }
2037
2038 /////////////////////////////////////////
2039 // DP_MD3_TAGINFO extension coded by VorteX
2040
2041 int CL_GetTagIndex (prvm_edict_t *e, const char *tagname)
2042 {
2043         dp_model_t *model = CL_GetModelFromEdict(e);
2044         if (model)
2045                 return Mod_Alias_GetTagIndexForName(model, (int)e->fields.client->skin, tagname);
2046         else
2047                 return -1;
2048 }
2049
2050 int CL_GetExtendedTagInfo (prvm_edict_t *e, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
2051 {
2052         int r;
2053         dp_model_t *model;
2054
2055         *tagname = NULL;
2056         *parentindex = 0;
2057         Matrix4x4_CreateIdentity(tag_localmatrix);
2058
2059         if (tagindex >= 0
2060          && (model = CL_GetModelFromEdict(e))
2061          && model->animscenes)
2062         {
2063                 r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)e->fields.client->skin, e->priv.server->frameblend, &e->priv.server->skeleton, tagindex - 1, parentindex, tagname, tag_localmatrix);
2064
2065                 if(!r) // success?
2066                         *parentindex += 1;
2067
2068                 return r;
2069         }
2070
2071         return 1;
2072 }
2073
2074 int CL_GetPitchSign(prvm_edict_t *ent)
2075 {
2076         dp_model_t *model;
2077         if ((model = CL_GetModelFromEdict(ent)) && model->type == mod_alias)
2078                 return -1;
2079         return 1;
2080 }
2081
2082 void CL_GetEntityMatrix (prvm_edict_t *ent, matrix4x4_t *out, qboolean viewmatrix)
2083 {
2084         prvm_eval_t *val;
2085         float scale;
2086         float pitchsign = 1;
2087
2088         scale = 1;
2089         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale);
2090         if (val && val->_float != 0)
2091                 scale = val->_float;
2092
2093         // TODO do we need the same weird angle inverting logic here as in the server side case?
2094         if(viewmatrix)
2095                 Matrix4x4_CreateFromQuakeEntity(out, cl.csqc_origin[0], cl.csqc_origin[1], cl.csqc_origin[2], cl.csqc_angles[0], cl.csqc_angles[1], cl.csqc_angles[2], scale * cl_viewmodel_scale.value);
2096         else
2097         {
2098                 pitchsign = CL_GetPitchSign(ent);
2099                 Matrix4x4_CreateFromQuakeEntity(out, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], pitchsign * ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], scale);
2100         }
2101 }
2102
2103 int CL_GetEntityLocalTagMatrix(prvm_edict_t *ent, int tagindex, matrix4x4_t *out)
2104 {
2105         dp_model_t *model;
2106         if (tagindex >= 0
2107          && (model = CL_GetModelFromEdict(ent))
2108          && model->animscenes)
2109         {
2110                 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
2111                 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
2112                 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
2113                 return Mod_Alias_GetTagMatrix(model, ent->priv.server->frameblend, &ent->priv.server->skeleton, tagindex, out);
2114         }
2115         *out = identitymatrix;
2116         return 0;
2117 }
2118
2119 // Warnings/errors code:
2120 // 0 - normal (everything all-right)
2121 // 1 - world entity
2122 // 2 - free entity
2123 // 3 - null or non-precached model
2124 // 4 - no tags with requested index
2125 // 5 - runaway loop at attachment chain
2126 extern cvar_t cl_bob;
2127 extern cvar_t cl_bobcycle;
2128 extern cvar_t cl_bobup;
2129 int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex)
2130 {
2131         int ret;
2132         prvm_eval_t *val;
2133         int attachloop;
2134         matrix4x4_t entitymatrix, tagmatrix, attachmatrix;
2135         dp_model_t *model;
2136
2137         *out = identitymatrix; // warnings and errors return identical matrix
2138
2139         if (ent == prog->edicts)
2140                 return 1;
2141         if (ent->priv.server->free)
2142                 return 2;
2143
2144         model = CL_GetModelFromEdict(ent);
2145         if(!model)
2146                 return 3;
2147
2148         tagmatrix = identitymatrix;
2149         attachloop = 0;
2150         for(;;)
2151         {
2152                 if(attachloop >= 256)
2153                         return 5;
2154                 // apply transformation by child's tagindex on parent entity and then
2155                 // by parent entity itself
2156                 ret = CL_GetEntityLocalTagMatrix(ent, tagindex - 1, &attachmatrix);
2157                 if(ret && attachloop == 0)
2158                         return ret;
2159                 CL_GetEntityMatrix(ent, &entitymatrix, false);
2160                 Matrix4x4_Concat(&tagmatrix, &attachmatrix, out);
2161                 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2162                 // next iteration we process the parent entity
2163                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict)
2164                 {
2165                         tagindex = (int)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float;
2166                         ent = PRVM_EDICT_NUM(val->edict);
2167                 }
2168                 else
2169                         break;
2170                 attachloop++;
2171         }
2172
2173         // RENDER_VIEWMODEL magic
2174         if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderflags)) && (RF_VIEWMODEL & (int)val->_float))
2175         {
2176                 Matrix4x4_Copy(&tagmatrix, out);
2177
2178                 CL_GetEntityMatrix(prog->edicts, &entitymatrix, true);
2179                 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2180
2181                 /*
2182                 // Cl_bob, ported from rendering code
2183                 if (ent->fields.client->health > 0 && cl_bob.value && cl_bobcycle.value)
2184                 {
2185                         double bob, cycle;
2186                         // LordHavoc: this code is *weird*, but not replacable (I think it
2187                         // should be done in QC on the server, but oh well, quake is quake)
2188                         // LordHavoc: figured out bobup: the time at which the sin is at 180
2189                         // degrees (which allows lengthening or squishing the peak or valley)
2190                         cycle = cl.time/cl_bobcycle.value;
2191                         cycle -= (int)cycle;
2192                         if (cycle < cl_bobup.value)
2193                                 cycle = sin(M_PI * cycle / cl_bobup.value);
2194                         else
2195                                 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
2196                         // bob is proportional to velocity in the xy plane
2197                         // (don't count Z, or jumping messes it up)
2198                         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;
2199                         bob = bob*0.3 + bob*0.7*cycle;
2200                         Matrix4x4_AdjustOrigin(out, 0, 0, bound(-7, bob, 4));
2201                 }
2202                 */
2203         }
2204         return 0;
2205 }
2206
2207 // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
2208 void VM_CL_gettagindex (void)
2209 {
2210         prvm_edict_t *ent;
2211         const char *tag_name;
2212         int tag_index;
2213
2214         VM_SAFEPARMCOUNT(2, VM_CL_gettagindex);
2215
2216         ent = PRVM_G_EDICT(OFS_PARM0);
2217         tag_name = PRVM_G_STRING(OFS_PARM1);
2218         if (ent == prog->edicts)
2219         {
2220                 VM_Warning("VM_CL_gettagindex(entity #%i): can't affect world entity\n", PRVM_NUM_FOR_EDICT(ent));
2221                 return;
2222         }
2223         if (ent->priv.server->free)
2224         {
2225                 VM_Warning("VM_CL_gettagindex(entity #%i): can't affect free entity\n", PRVM_NUM_FOR_EDICT(ent));
2226                 return;
2227         }
2228
2229         tag_index = 0;
2230         if (!CL_GetModelFromEdict(ent))
2231                 Con_DPrintf("VM_CL_gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
2232         else
2233         {
2234                 tag_index = CL_GetTagIndex(ent, tag_name);
2235                 if (tag_index == 0)
2236                         Con_DPrintf("VM_CL_gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
2237         }
2238         PRVM_G_FLOAT(OFS_RETURN) = tag_index;
2239 }
2240
2241 // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO)
2242 void VM_CL_gettaginfo (void)
2243 {
2244         prvm_edict_t *e;
2245         int tagindex;
2246         matrix4x4_t tag_matrix;
2247         matrix4x4_t tag_localmatrix;
2248         int parentindex;
2249         const char *tagname;
2250         int returncode;
2251         prvm_eval_t *val;
2252         vec3_t fo, le, up, trans;
2253         const dp_model_t *model;
2254
2255         VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo);
2256
2257         e = PRVM_G_EDICT(OFS_PARM0);
2258         tagindex = (int)PRVM_G_FLOAT(OFS_PARM1);
2259         returncode = CL_GetTagMatrix(&tag_matrix, e, tagindex);
2260         Matrix4x4_ToVectors(&tag_matrix, prog->globals.client->v_forward, le, prog->globals.client->v_up, PRVM_G_VECTOR(OFS_RETURN));
2261         VectorScale(le, -1, prog->globals.client->v_right);
2262         model = CL_GetModelFromEdict(e);
2263         VM_GenerateFrameGroupBlend(e->priv.server->framegroupblend, e);
2264         VM_FrameBlendFromFrameGroupBlend(e->priv.server->frameblend, e->priv.server->framegroupblend, model);
2265         VM_UpdateEdictSkeleton(e, model, e->priv.server->frameblend);
2266         CL_GetExtendedTagInfo(e, tagindex, &parentindex, &tagname, &tag_localmatrix);
2267         Matrix4x4_ToVectors(&tag_localmatrix, fo, le, up, trans);
2268
2269         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_parent)))
2270                 val->_float = parentindex;
2271         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_name)))
2272                 val->string = tagname ? PRVM_SetTempString(tagname) : 0;
2273         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_offset)))
2274                 VectorCopy(trans, val->vector);
2275         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_forward)))
2276                 VectorCopy(fo, val->vector);
2277         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_right)))
2278                 VectorScale(le, -1, val->vector);
2279         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_up)))
2280                 VectorCopy(up, val->vector);
2281
2282         switch(returncode)
2283         {
2284                 case 1:
2285                         VM_Warning("gettagindex: can't affect world entity\n");
2286                         break;
2287                 case 2:
2288                         VM_Warning("gettagindex: can't affect free entity\n");
2289                         break;
2290                 case 3:
2291                         Con_DPrintf("CL_GetTagMatrix(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(e));
2292                         break;
2293                 case 4:
2294                         Con_DPrintf("CL_GetTagMatrix(entity #%i): model has no tag with requested index %i\n", PRVM_NUM_FOR_EDICT(e), tagindex);
2295                         break;
2296                 case 5:
2297                         Con_DPrintf("CL_GetTagMatrix(entity #%i): runaway loop at attachment chain\n", PRVM_NUM_FOR_EDICT(e));
2298                         break;
2299         }
2300 }
2301
2302 //============================================================================
2303
2304 //====================
2305 // DP_CSQC_SPAWNPARTICLE
2306 // a QC hook to engine's CL_NewParticle
2307 //====================
2308
2309 // particle theme struct
2310 typedef struct vmparticletheme_s
2311 {
2312         unsigned short typeindex;
2313         qboolean initialized;
2314         pblend_t blendmode;
2315         porientation_t orientation;
2316         int color1;
2317         int color2;
2318         int tex;
2319         float size;
2320         float sizeincrease;
2321         float alpha;
2322         float alphafade;
2323         float gravity;
2324         float bounce;
2325         float airfriction;
2326         float liquidfriction;
2327         float originjitter;
2328         float velocityjitter;
2329         qboolean qualityreduction;
2330         float lifetime;
2331         float stretch;
2332         int staincolor1;
2333         int staincolor2;
2334         int staintex;
2335         float stainalpha;
2336         float stainsize;
2337         float delayspawn;
2338         float delaycollision;
2339         float angle;
2340         float spin;
2341 }vmparticletheme_t;
2342
2343 // particle spawner
2344 typedef struct vmparticlespawner_s
2345 {
2346         mempool_t                       *pool;
2347         qboolean                        initialized;
2348         qboolean                        verified;
2349         vmparticletheme_t       *themes;
2350         int                                     max_themes;
2351         // global addresses
2352         float *particle_type;
2353         float *particle_blendmode; 
2354         float *particle_orientation;
2355         float *particle_color1;
2356         float *particle_color2;
2357         float *particle_tex;
2358         float *particle_size;
2359         float *particle_sizeincrease;
2360         float *particle_alpha;
2361         float *particle_alphafade;
2362         float *particle_time;
2363         float *particle_gravity;
2364         float *particle_bounce;
2365         float *particle_airfriction;
2366         float *particle_liquidfriction;
2367         float *particle_originjitter;
2368         float *particle_velocityjitter;
2369         float *particle_qualityreduction;
2370         float *particle_stretch;
2371         float *particle_staincolor1;
2372         float *particle_staincolor2;
2373         float *particle_stainalpha;
2374         float *particle_stainsize;
2375         float *particle_staintex;
2376         float *particle_delayspawn;
2377         float *particle_delaycollision;
2378         float *particle_angle;
2379         float *particle_spin;
2380 }vmparticlespawner_t;
2381
2382 vmparticlespawner_t vmpartspawner;
2383
2384 // TODO: automatic max_themes grow
2385 static void VM_InitParticleSpawner (int maxthemes)
2386 {
2387         prvm_eval_t *val;
2388
2389         // bound max themes to not be an insane value
2390         if (maxthemes < 4)
2391                 maxthemes = 4;
2392         if (maxthemes > 2048)
2393                 maxthemes = 2048;
2394         // allocate and set up structure
2395         if (vmpartspawner.initialized) // reallocate
2396         {
2397                 Mem_FreePool(&vmpartspawner.pool);
2398                 memset(&vmpartspawner, 0, sizeof(vmparticlespawner_t));
2399         }
2400         vmpartspawner.pool = Mem_AllocPool("VMPARTICLESPAWNER", 0, NULL);
2401         vmpartspawner.themes = (vmparticletheme_t *)Mem_Alloc(vmpartspawner.pool, sizeof(vmparticletheme_t)*maxthemes);
2402         vmpartspawner.max_themes = maxthemes;
2403         vmpartspawner.initialized = true;
2404         vmpartspawner.verified = true;
2405         // get field addresses for fast querying (we can do 1000 calls of spawnparticle in a frame)
2406         #define getglobal(v,s) val = PRVM_GLOBALFIELDVALUE(PRVM_ED_FindGlobalOffset(s)); if (val) { vmpartspawner.v = &val->_float; } else { VM_Warning("VM_InitParticleSpawner: missing global '%s', spawner cannot work\n", s); vmpartspawner.verified = false; }
2407         #define getglobalvector(v,s) val = PRVM_GLOBALFIELDVALUE(PRVM_ED_FindGlobalOffset(s)); if (val) { vmpartspawner.v = (float *)val->vector; } else { VM_Warning("VM_InitParticleSpawner: missing global '%s', spawner cannot work\n", s); vmpartspawner.verified = false; }
2408         getglobal(particle_type, "particle_type");
2409         getglobal(particle_blendmode, "particle_blendmode");
2410         getglobal(particle_orientation, "particle_orientation");
2411         getglobalvector(particle_color1, "particle_color1");
2412         getglobalvector(particle_color2, "particle_color2");
2413         getglobal(particle_tex, "particle_tex");
2414         getglobal(particle_size, "particle_size");
2415         getglobal(particle_sizeincrease, "particle_sizeincrease");
2416         getglobal(particle_alpha, "particle_alpha");
2417         getglobal(particle_alphafade, "particle_alphafade");
2418         getglobal(particle_time, "particle_time");
2419         getglobal(particle_gravity, "particle_gravity");
2420         getglobal(particle_bounce, "particle_bounce");
2421         getglobal(particle_airfriction, "particle_airfriction");
2422         getglobal(particle_liquidfriction, "particle_liquidfriction");
2423         getglobal(particle_originjitter, "particle_originjitter");
2424         getglobal(particle_velocityjitter, "particle_velocityjitter");
2425         getglobal(particle_qualityreduction, "particle_qualityreduction");
2426         getglobal(particle_stretch, "particle_stretch");
2427         getglobalvector(particle_staincolor1, "particle_staincolor1");
2428         getglobalvector(particle_staincolor2, "particle_staincolor2");
2429         getglobal(particle_stainalpha, "particle_stainalpha");
2430         getglobal(particle_stainsize, "particle_stainsize");
2431         getglobal(particle_staintex, "particle_staintex");
2432         getglobal(particle_staintex, "particle_staintex");
2433         getglobal(particle_delayspawn, "particle_delayspawn");
2434         getglobal(particle_delaycollision, "particle_delaycollision");
2435         getglobal(particle_angle, "particle_angle");
2436         getglobal(particle_spin, "particle_spin");
2437         #undef getglobal
2438         #undef getglobalvector
2439 }
2440
2441 // reset particle theme to default values
2442 static void VM_ResetParticleTheme (vmparticletheme_t *theme)
2443 {
2444         theme->initialized = true;
2445         theme->typeindex = pt_static;
2446         theme->blendmode = PBLEND_ADD;
2447         theme->orientation = PARTICLE_BILLBOARD;
2448         theme->color1 = 0x808080;
2449         theme->color2 = 0xFFFFFF;
2450         theme->tex = 63;
2451         theme->size = 2;
2452         theme->sizeincrease = 0;
2453         theme->alpha = 256;
2454         theme->alphafade = 512;
2455         theme->gravity = 0.0f;
2456         theme->bounce = 0.0f;
2457         theme->airfriction = 1.0f;
2458         theme->liquidfriction = 4.0f;
2459         theme->originjitter = 0.0f;
2460         theme->velocityjitter = 0.0f;
2461         theme->qualityreduction = false;
2462         theme->lifetime = 4;
2463         theme->stretch = 1;
2464         theme->staincolor1 = -1;
2465         theme->staincolor2 = -1;
2466         theme->staintex = -1;
2467         theme->delayspawn = 0.0f;
2468         theme->delaycollision = 0.0f;
2469         theme->angle = 0.0f;
2470         theme->spin = 0.0f;
2471 }
2472
2473 // particle theme -> QC globals
2474 void VM_CL_ParticleThemeToGlobals(vmparticletheme_t *theme)
2475 {
2476         *vmpartspawner.particle_type = theme->typeindex;
2477         *vmpartspawner.particle_blendmode = theme->blendmode;
2478         *vmpartspawner.particle_orientation = theme->orientation;
2479         vmpartspawner.particle_color1[0] = (theme->color1 >> 16) & 0xFF; // VorteX: int only can store 0-255, not 0-256 which means 0 - 0,99609375...
2480         vmpartspawner.particle_color1[1] = (theme->color1 >> 8) & 0xFF;
2481         vmpartspawner.particle_color1[2] = (theme->color1 >> 0) & 0xFF;
2482         vmpartspawner.particle_color2[0] = (theme->color2 >> 16) & 0xFF;
2483         vmpartspawner.particle_color2[1] = (theme->color2 >> 8) & 0xFF;
2484         vmpartspawner.particle_color2[2] = (theme->color2 >> 0) & 0xFF;
2485         *vmpartspawner.particle_tex = (float)theme->tex;
2486         *vmpartspawner.particle_size = theme->size;
2487         *vmpartspawner.particle_sizeincrease = theme->sizeincrease;
2488         *vmpartspawner.particle_alpha = theme->alpha/256;
2489         *vmpartspawner.particle_alphafade = theme->alphafade/256;
2490         *vmpartspawner.particle_time = theme->lifetime;
2491         *vmpartspawner.particle_gravity = theme->gravity;
2492         *vmpartspawner.particle_bounce = theme->bounce;
2493         *vmpartspawner.particle_airfriction = theme->airfriction;
2494         *vmpartspawner.particle_liquidfriction = theme->liquidfriction;
2495         *vmpartspawner.particle_originjitter = theme->originjitter;
2496         *vmpartspawner.particle_velocityjitter = theme->velocityjitter;
2497         *vmpartspawner.particle_qualityreduction = theme->qualityreduction;
2498         *vmpartspawner.particle_stretch = theme->stretch;
2499         vmpartspawner.particle_staincolor1[0] = ((int)theme->staincolor1 >> 16) & 0xFF;
2500         vmpartspawner.particle_staincolor1[1] = ((int)theme->staincolor1 >> 8) & 0xFF;
2501         vmpartspawner.particle_staincolor1[2] = ((int)theme->staincolor1 >> 0) & 0xFF;
2502         vmpartspawner.particle_staincolor2[0] = ((int)theme->staincolor2 >> 16) & 0xFF;
2503         vmpartspawner.particle_staincolor2[1] = ((int)theme->staincolor2 >> 8) & 0xFF;
2504         vmpartspawner.particle_staincolor2[2] = ((int)theme->staincolor2 >> 0) & 0xFF;
2505         *vmpartspawner.particle_staintex = (float)theme->staintex;
2506         *vmpartspawner.particle_stainalpha = (float)theme->stainalpha/256;
2507         *vmpartspawner.particle_stainsize = (float)theme->stainsize;
2508         *vmpartspawner.particle_delayspawn = theme->delayspawn;
2509         *vmpartspawner.particle_delaycollision = theme->delaycollision;
2510         *vmpartspawner.particle_angle = theme->angle;
2511         *vmpartspawner.particle_spin = theme->spin;
2512 }
2513
2514 // QC globals ->  particle theme
2515 void VM_CL_ParticleThemeFromGlobals(vmparticletheme_t *theme)
2516 {
2517         theme->typeindex = (unsigned short)*vmpartspawner.particle_type;
2518         theme->blendmode = (pblend_t)*vmpartspawner.particle_blendmode;
2519         theme->orientation = (porientation_t)*vmpartspawner.particle_orientation;
2520         theme->color1 = ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]);
2521         theme->color2 = ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]);
2522         theme->tex = (int)*vmpartspawner.particle_tex;
2523         theme->size = *vmpartspawner.particle_size;
2524         theme->sizeincrease = *vmpartspawner.particle_sizeincrease;
2525         theme->alpha = *vmpartspawner.particle_alpha*256;
2526         theme->alphafade = *vmpartspawner.particle_alphafade*256;
2527         theme->lifetime = *vmpartspawner.particle_time;
2528         theme->gravity = *vmpartspawner.particle_gravity;
2529         theme->bounce = *vmpartspawner.particle_bounce;
2530         theme->airfriction = *vmpartspawner.particle_airfriction;
2531         theme->liquidfriction = *vmpartspawner.particle_liquidfriction;
2532         theme->originjitter = *vmpartspawner.particle_originjitter;
2533         theme->velocityjitter = *vmpartspawner.particle_velocityjitter;
2534         theme->qualityreduction = (*vmpartspawner.particle_qualityreduction) ? true : false;
2535         theme->stretch = *vmpartspawner.particle_stretch;
2536         theme->staincolor1 = ((int)vmpartspawner.particle_staincolor1[0])*65536 + (int)(vmpartspawner.particle_staincolor1[1])*256 + (int)(vmpartspawner.particle_staincolor1[2]);
2537         theme->staincolor2 = (int)(vmpartspawner.particle_staincolor2[0])*65536 + (int)(vmpartspawner.particle_staincolor2[1])*256 + (int)(vmpartspawner.particle_staincolor2[2]);
2538         theme->staintex =(int)*vmpartspawner.particle_staintex;
2539         theme->stainalpha = *vmpartspawner.particle_stainalpha*256;
2540         theme->stainsize = *vmpartspawner.particle_stainsize;
2541         theme->delayspawn = *vmpartspawner.particle_delayspawn;
2542         theme->delaycollision = *vmpartspawner.particle_delaycollision;
2543         theme->angle = *vmpartspawner.particle_angle;
2544         theme->spin = *vmpartspawner.particle_spin;
2545 }
2546
2547 // init particle spawner interface
2548 // # float(float max_themes) initparticlespawner
2549 void VM_CL_InitParticleSpawner (void)
2550 {
2551         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_InitParticleSpawner);
2552         VM_InitParticleSpawner((int)PRVM_G_FLOAT(OFS_PARM0));
2553         vmpartspawner.themes[0].initialized = true;
2554         VM_ResetParticleTheme(&vmpartspawner.themes[0]);
2555         PRVM_G_FLOAT(OFS_RETURN) = (vmpartspawner.verified == true) ? 1 : 0;
2556 }
2557
2558 // void() resetparticle
2559 void VM_CL_ResetParticle (void)
2560 {
2561         VM_SAFEPARMCOUNT(0, VM_CL_ResetParticle);
2562         if (vmpartspawner.verified == false)
2563         {
2564                 VM_Warning("VM_CL_ResetParticle: particle spawner not initialized\n");
2565                 return;
2566         }
2567         VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2568 }
2569
2570 // void(float themenum) particletheme
2571 void VM_CL_ParticleTheme (void)
2572 {
2573         int themenum;
2574
2575         VM_SAFEPARMCOUNT(1, VM_CL_ParticleTheme);
2576         if (vmpartspawner.verified == false)
2577         {
2578                 VM_Warning("VM_CL_ParticleTheme: particle spawner not initialized\n");
2579                 return;
2580         }
2581         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2582         if (themenum < 0 || themenum >= vmpartspawner.max_themes)
2583         {
2584                 VM_Warning("VM_CL_ParticleTheme: bad theme number %i\n", themenum);
2585                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2586                 return;
2587         }
2588         if (vmpartspawner.themes[themenum].initialized == false)
2589         {
2590                 VM_Warning("VM_CL_ParticleTheme: theme #%i not exists\n", themenum);
2591                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2592                 return;
2593         }
2594         // load particle theme into globals
2595         VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[themenum]);
2596 }
2597
2598 // float() saveparticletheme
2599 // void(float themenum) updateparticletheme
2600 void VM_CL_ParticleThemeSave (void)
2601 {
2602         int themenum;
2603
2604         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_ParticleThemeSave);
2605         if (vmpartspawner.verified == false)
2606         {
2607                 VM_Warning("VM_CL_ParticleThemeSave: particle spawner not initialized\n");
2608                 return;
2609         }
2610         // allocate new theme, save it and return
2611         if (prog->argc < 1)
2612         {
2613                 for (themenum = 0; themenum < vmpartspawner.max_themes; themenum++)
2614                         if (vmpartspawner.themes[themenum].initialized == false)
2615                                 break;
2616                 if (themenum >= vmpartspawner.max_themes)
2617                 {
2618                         if (vmpartspawner.max_themes == 2048)
2619                                 VM_Warning("VM_CL_ParticleThemeSave: no free theme slots\n");
2620                         else
2621                                 VM_Warning("VM_CL_ParticleThemeSave: no free theme slots, try initparticlespawner() with highter max_themes\n");
2622                         PRVM_G_FLOAT(OFS_RETURN) = -1;
2623                         return;
2624                 }
2625                 vmpartspawner.themes[themenum].initialized = true;
2626                 VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]);
2627                 PRVM_G_FLOAT(OFS_RETURN) = themenum;
2628                 return;
2629         }
2630         // update existing theme
2631         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2632         if (themenum < 0 || themenum >= vmpartspawner.max_themes)
2633         {
2634                 VM_Warning("VM_CL_ParticleThemeSave: bad theme number %i\n", themenum);
2635                 return;
2636         }
2637         vmpartspawner.themes[themenum].initialized = true;
2638         VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]);
2639 }
2640
2641 // void(float themenum) freeparticletheme
2642 void VM_CL_ParticleThemeFree (void)
2643 {
2644         int themenum;
2645
2646         VM_SAFEPARMCOUNT(1, VM_CL_ParticleThemeFree);
2647         if (vmpartspawner.verified == false)
2648         {
2649                 VM_Warning("VM_CL_ParticleThemeFree: particle spawner not initialized\n");
2650                 return;
2651         }
2652         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2653         // check parms
2654         if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2655         {
2656                 VM_Warning("VM_CL_ParticleThemeFree: bad theme number %i\n", themenum);
2657                 return;
2658         }
2659         if (vmpartspawner.themes[themenum].initialized == false)
2660         {
2661                 VM_Warning("VM_CL_ParticleThemeFree: theme #%i already freed\n", themenum);
2662                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2663                 return;
2664         }
2665         // free theme
2666         VM_ResetParticleTheme(&vmpartspawner.themes[themenum]);
2667         vmpartspawner.themes[themenum].initialized = false;
2668 }
2669
2670 // float(vector org, vector dir, [float theme]) particle
2671 // returns 0 if failed, 1 if succesful
2672 void VM_CL_SpawnParticle (void)
2673 {
2674         float *org, *dir;
2675         vmparticletheme_t *theme;
2676         particle_t *part;
2677         int themenum;
2678
2679         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_SpawnParticle2);
2680         if (vmpartspawner.verified == false)
2681         {
2682                 VM_Warning("VM_CL_SpawnParticle: particle spawner not initialized\n");
2683                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
2684                 return;
2685         }
2686         org = PRVM_G_VECTOR(OFS_PARM0);
2687         dir = PRVM_G_VECTOR(OFS_PARM1);
2688         
2689         if (prog->argc < 3) // global-set particle
2690         {
2691                 part = CL_NewParticle(org, (unsigned short)*vmpartspawner.particle_type, ((int)(vmpartspawner.particle_color1[0]) << 16) + ((int)(vmpartspawner.particle_color1[1]) << 8) + ((int)(vmpartspawner.particle_color1[2])), ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]), (int)*vmpartspawner.particle_tex, *vmpartspawner.particle_size, *vmpartspawner.particle_sizeincrease, *vmpartspawner.particle_alpha*256, *vmpartspawner.particle_alphafade*256, *vmpartspawner.particle_gravity, *vmpartspawner.particle_bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], *vmpartspawner.particle_airfriction, *vmpartspawner.particle_liquidfriction, *vmpartspawner.particle_originjitter, *vmpartspawner.particle_velocityjitter, (*vmpartspawner.particle_qualityreduction) ? true : false, *vmpartspawner.particle_time, *vmpartspawner.particle_stretch, (pblend_t)*vmpartspawner.particle_blendmode, (porientation_t)*vmpartspawner.particle_orientation, (int)(vmpartspawner.particle_staincolor1[0])*65536 + (int)(vmpartspawner.particle_staincolor1[1])*256 + (int)(vmpartspawner.particle_staincolor1[2]), (int)(vmpartspawner.particle_staincolor2[0])*65536 + (int)(vmpartspawner.particle_staincolor2[1])*256 + (int)(vmpartspawner.particle_staincolor2[2]), (int)*vmpartspawner.particle_staintex, *vmpartspawner.particle_stainalpha*256, *vmpartspawner.particle_stainsize, *vmpartspawner.particle_angle, *vmpartspawner.particle_spin);
2692                 if (!part)
2693                 {
2694                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
2695                         return;
2696                 }
2697                 if (*vmpartspawner.particle_delayspawn)
2698                         part->delayedspawn = cl.time + *vmpartspawner.particle_delayspawn;
2699                 //if (*vmpartspawner.particle_delaycollision)
2700                 //      part->delayedcollisions = cl.time + *vmpartspawner.particle_delaycollision;
2701         }
2702         else // quick themed particle
2703         {
2704                 themenum = (int)PRVM_G_FLOAT(OFS_PARM2);
2705                 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2706                 {
2707                         VM_Warning("VM_CL_SpawnParticle: bad theme number %i\n", themenum);
2708                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
2709                         return;
2710                 }
2711                 theme = &vmpartspawner.themes[themenum];
2712                 part = CL_NewParticle(org, theme->typeindex, theme->color1, theme->color2, theme->tex, theme->size, theme->sizeincrease, theme->alpha, theme->alphafade, theme->gravity, theme->bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], theme->airfriction, theme->liquidfriction, theme->originjitter, theme->velocityjitter, theme->qualityreduction, theme->lifetime, theme->stretch, theme->blendmode, theme->orientation, theme->staincolor1, theme->staincolor2, theme->staintex, theme->stainalpha, theme->stainsize, theme->angle, theme->spin);
2713                 if (!part)
2714                 {
2715                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
2716                         return;
2717                 }
2718                 if (theme->delayspawn)
2719                         part->delayedspawn = cl.time + theme->delayspawn;
2720                 //if (theme->delaycollision)
2721                 //      part->delayedcollisions = cl.time + theme->delaycollision;
2722         }
2723         PRVM_G_FLOAT(OFS_RETURN) = 1; 
2724 }
2725
2726 // float(vector org, vector dir, float spawndelay, float collisiondelay, [float theme]) delayedparticle
2727 // returns 0 if failed, 1 if success
2728 void VM_CL_SpawnParticleDelayed (void)
2729 {
2730         float *org, *dir;
2731         vmparticletheme_t *theme;
2732         particle_t *part;
2733         int themenum;
2734
2735         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_SpawnParticle2);
2736         if (vmpartspawner.verified == false)
2737         {
2738                 VM_Warning("VM_CL_SpawnParticle: particle spawner not initialized\n");
2739                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
2740                 return;
2741         }
2742         org = PRVM_G_VECTOR(OFS_PARM0);
2743         dir = PRVM_G_VECTOR(OFS_PARM1);
2744         if (prog->argc < 5) // global-set particle
2745                 part = CL_NewParticle(org, (unsigned short)*vmpartspawner.particle_type, ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]), ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]), (int)*vmpartspawner.particle_tex, *vmpartspawner.particle_size, *vmpartspawner.particle_sizeincrease, *vmpartspawner.particle_alpha*256, *vmpartspawner.particle_alphafade*256, *vmpartspawner.particle_gravity, *vmpartspawner.particle_bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], *vmpartspawner.particle_airfriction, *vmpartspawner.particle_liquidfriction, *vmpartspawner.particle_originjitter, *vmpartspawner.particle_velocityjitter, (*vmpartspawner.particle_qualityreduction) ? true : false, *vmpartspawner.particle_time, *vmpartspawner.particle_stretch, (pblend_t)*vmpartspawner.particle_blendmode, (porientation_t)*vmpartspawner.particle_orientation, ((int)vmpartspawner.particle_staincolor1[0] << 16) + ((int)vmpartspawner.particle_staincolor1[1] << 8) + ((int)vmpartspawner.particle_staincolor1[2]), ((int)vmpartspawner.particle_staincolor2[0] << 16) + ((int)vmpartspawner.particle_staincolor2[1] << 8) + ((int)vmpartspawner.particle_staincolor2[2]), (int)*vmpartspawner.particle_staintex, *vmpartspawner.particle_stainalpha*256, *vmpartspawner.particle_stainsize, *vmpartspawner.particle_angle, *vmpartspawner.particle_spin);
2746         else // themed particle
2747         {
2748                 themenum = (int)PRVM_G_FLOAT(OFS_PARM4);
2749                 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2750                 {
2751                         VM_Warning("VM_CL_SpawnParticle: bad theme number %i\n", themenum);
2752                         PRVM_G_FLOAT(OFS_RETURN) = 0;  
2753                         return;
2754                 }
2755                 theme = &vmpartspawner.themes[themenum];
2756                 part = CL_NewParticle(org, theme->typeindex, theme->color1, theme->color2, theme->tex, theme->size, theme->sizeincrease, theme->alpha, theme->alphafade, theme->gravity, theme->bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], theme->airfriction, theme->liquidfriction, theme->originjitter, theme->velocityjitter, theme->qualityreduction, theme->lifetime, theme->stretch, theme->blendmode, theme->orientation, theme->staincolor1, theme->staincolor2, theme->staintex, theme->stainalpha, theme->stainsize, theme->angle, theme->spin);
2757         }
2758         if (!part) 
2759         { 
2760                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
2761                 return; 
2762         }
2763         part->delayedspawn = cl.time + PRVM_G_FLOAT(OFS_PARM2);
2764         //part->delayedcollisions = cl.time + PRVM_G_FLOAT(OFS_PARM3);
2765         PRVM_G_FLOAT(OFS_RETURN) = 0;
2766 }
2767
2768 //
2769 //====================
2770 //QC POLYGON functions
2771 //====================
2772
2773 #define VMPOLYGONS_MAXPOINTS 64
2774
2775 typedef struct vmpolygons_triangle_s
2776 {
2777         rtexture_t              *texture;
2778         int                             drawflag;
2779         unsigned short  elements[3];
2780 }vmpolygons_triangle_t;
2781
2782 typedef struct vmpolygons_s
2783 {
2784         mempool_t               *pool;
2785         qboolean                initialized;
2786         double          progstarttime;
2787
2788         int                             max_vertices;
2789         int                             num_vertices;
2790         float                   *data_vertex3f;
2791         float                   *data_color4f;
2792         float                   *data_texcoord2f;
2793
2794         int                             max_triangles;
2795         int                             num_triangles;
2796         vmpolygons_triangle_t *data_triangles;
2797         unsigned short  *data_sortedelement3s;
2798
2799         qboolean                begin_active;
2800         rtexture_t              *begin_texture;
2801         int                             begin_drawflag;
2802         int                             begin_vertices;
2803         float                   begin_vertex[VMPOLYGONS_MAXPOINTS][3];
2804         float                   begin_color[VMPOLYGONS_MAXPOINTS][4];
2805         float                   begin_texcoord[VMPOLYGONS_MAXPOINTS][2];
2806 } vmpolygons_t;
2807
2808 // FIXME: make VM_CL_R_Polygon functions use Debug_Polygon functions?
2809 vmpolygons_t vmpolygons[PRVM_MAXPROGS];
2810
2811 //#304 void() renderscene (EXT_CSQC)
2812 // moved that here to reset the polygons,
2813 // resetting them earlier causes R_Mesh_Draw to be called with numvertices = 0
2814 // --blub
2815 void VM_CL_R_RenderScene (void)
2816 {
2817         double t = Sys_DoubleTime();
2818         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
2819         VM_SAFEPARMCOUNT(0, VM_CL_R_RenderScene);
2820
2821         // we need to update any RENDER_VIEWMODEL entities at this point because
2822         // csqc supplies its own view matrix
2823         CL_UpdateViewEntities();
2824         // now draw stuff!
2825         R_RenderView();
2826
2827         polys->num_vertices = polys->num_triangles = 0;
2828         polys->progstarttime = prog->starttime;
2829
2830         // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView
2831         prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t;
2832 }
2833
2834 static void VM_ResizePolygons(vmpolygons_t *polys)
2835 {
2836         float *oldvertex3f = polys->data_vertex3f;
2837         float *oldcolor4f = polys->data_color4f;
2838         float *oldtexcoord2f = polys->data_texcoord2f;
2839         vmpolygons_triangle_t *oldtriangles = polys->data_triangles;
2840         unsigned short *oldsortedelement3s = polys->data_sortedelement3s;
2841         polys->max_vertices = min(polys->max_triangles*3, 65536);
2842         polys->data_vertex3f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[3]));
2843         polys->data_color4f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[4]));
2844         polys->data_texcoord2f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[2]));
2845         polys->data_triangles = (vmpolygons_triangle_t *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(vmpolygons_triangle_t));
2846         polys->data_sortedelement3s = (unsigned short *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(unsigned short[3]));
2847         if (polys->num_vertices)
2848         {
2849                 memcpy(polys->data_vertex3f, oldvertex3f, polys->num_vertices*sizeof(float[3]));
2850                 memcpy(polys->data_color4f, oldcolor4f, polys->num_vertices*sizeof(float[4]));
2851                 memcpy(polys->data_texcoord2f, oldtexcoord2f, polys->num_vertices*sizeof(float[2]));
2852         }
2853         if (polys->num_triangles)
2854         {
2855                 memcpy(polys->data_triangles, oldtriangles, polys->num_triangles*sizeof(vmpolygons_triangle_t));
2856                 memcpy(polys->data_sortedelement3s, oldsortedelement3s, polys->num_triangles*sizeof(unsigned short[3]));
2857         }
2858         if (oldvertex3f)
2859                 Mem_Free(oldvertex3f);
2860         if (oldcolor4f)
2861                 Mem_Free(oldcolor4f);
2862         if (oldtexcoord2f)
2863                 Mem_Free(oldtexcoord2f);
2864         if (oldtriangles)
2865                 Mem_Free(oldtriangles);
2866         if (oldsortedelement3s)
2867                 Mem_Free(oldsortedelement3s);
2868 }
2869
2870 static void VM_InitPolygons (vmpolygons_t* polys)
2871 {
2872         memset(polys, 0, sizeof(*polys));
2873         polys->pool = Mem_AllocPool("VMPOLY", 0, NULL);
2874         polys->max_triangles = 1024;
2875         VM_ResizePolygons(polys);
2876         polys->initialized = true;
2877 }
2878
2879 static void VM_DrawPolygonCallback (const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2880 {
2881         int surfacelistindex;
2882         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
2883         if(polys->progstarttime != prog->starttime) // from other progs? won't draw these (this can cause crashes!)
2884                 return;
2885         R_Mesh_ResetTextureState();
2886         R_EntityMatrix(&identitymatrix);
2887         GL_CullFace(GL_NONE);
2888         R_Mesh_VertexPointer(polys->data_vertex3f, 0, 0);
2889         R_Mesh_ColorPointer(polys->data_color4f, 0, 0);
2890         R_Mesh_TexCoordPointer(0, 2, polys->data_texcoord2f, 0, 0);
2891
2892         for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
2893         {
2894                 int numtriangles = 0;
2895                 rtexture_t *tex = polys->data_triangles[surfacelist[surfacelistindex]].texture;
2896                 int drawflag = polys->data_triangles[surfacelist[surfacelistindex]].drawflag;
2897                 // this can't call _DrawQ_ProcessDrawFlag, but should be in sync with it
2898                 // FIXME factor this out
2899                 if(drawflag == DRAWFLAG_ADDITIVE)
2900                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2901                 else if(drawflag == DRAWFLAG_MODULATE)
2902                         GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2903                 else if(drawflag == DRAWFLAG_2XMODULATE)
2904                         GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
2905                 else if(drawflag == DRAWFLAG_SCREEN)
2906                         GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
2907                 else
2908                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2909                 R_SetupShader_Generic(tex, NULL, GL_MODULATE, 1);
2910                 numtriangles = 0;
2911                 for (;surfacelistindex < numsurfaces;surfacelistindex++)
2912                 {
2913                         if (polys->data_triangles[surfacelist[surfacelistindex]].texture != tex || polys->data_triangles[surfacelist[surfacelistindex]].drawflag != drawflag)
2914                                 break;
2915                         VectorCopy(polys->data_triangles[surfacelist[surfacelistindex]].elements, polys->data_sortedelement3s + 3*numtriangles);
2916                         numtriangles++;
2917                 }
2918                 R_Mesh_Draw(0, polys->num_vertices, 0, numtriangles, NULL, polys->data_sortedelement3s, 0, 0);
2919         }
2920 }
2921
2922 void VMPolygons_Store(vmpolygons_t *polys)
2923 {
2924         if (r_refdef.draw2dstage)
2925         {
2926                 // draw the polygon as 2D immediately
2927                 drawqueuemesh_t mesh;
2928                 mesh.texture = polys->begin_texture;
2929                 mesh.num_vertices = polys->begin_vertices;
2930                 mesh.num_triangles = polys->begin_vertices-2;
2931                 mesh.data_element3i = polygonelement3i;
2932                 mesh.data_element3s = polygonelement3s;
2933                 mesh.data_vertex3f = polys->begin_vertex[0];
2934                 mesh.data_color4f = polys->begin_color[0];
2935                 mesh.data_texcoord2f = polys->begin_texcoord[0];
2936                 DrawQ_Mesh(&mesh, polys->begin_drawflag);
2937         }
2938         else
2939         {
2940                 // queue the polygon as 3D for sorted transparent rendering later
2941                 int i;
2942                 if (polys->max_triangles < polys->num_triangles + polys->begin_vertices-2)
2943                 {
2944                         polys->max_triangles *= 2;
2945                         VM_ResizePolygons(polys);
2946                 }
2947                 if (polys->num_vertices + polys->begin_vertices <= polys->max_vertices)
2948                 {
2949                         // needle in a haystack!
2950                         // polys->num_vertices was used for copying where we actually want to copy begin_vertices
2951                         // that also caused it to not render the first polygon that is added
2952                         // --blub
2953                         memcpy(polys->data_vertex3f + polys->num_vertices * 3, polys->begin_vertex[0], polys->begin_vertices * sizeof(float[3]));
2954                         memcpy(polys->data_color4f + polys->num_vertices * 4, polys->begin_color[0], polys->begin_vertices * sizeof(float[4]));
2955                         memcpy(polys->data_texcoord2f + polys->num_vertices * 2, polys->begin_texcoord[0], polys->begin_vertices * sizeof(float[2]));
2956                         for (i = 0;i < polys->begin_vertices-2;i++)
2957                         {
2958                                 polys->data_triangles[polys->num_triangles].texture = polys->begin_texture;
2959                                 polys->data_triangles[polys->num_triangles].drawflag = polys->begin_drawflag;
2960                                 polys->data_triangles[polys->num_triangles].elements[0] = polys->num_vertices;
2961                                 polys->data_triangles[polys->num_triangles].elements[1] = polys->num_vertices + i+1;
2962                                 polys->data_triangles[polys->num_triangles].elements[2] = polys->num_vertices + i+2;
2963                                 polys->num_triangles++;
2964                         }
2965                         polys->num_vertices += polys->begin_vertices;
2966                 }
2967         }
2968         polys->begin_active = false;
2969 }
2970
2971 // TODO: move this into the client code and clean-up everything else, too! [1/6/2008 Black]
2972 // LordHavoc: agreed, this is a mess
2973 void VM_CL_AddPolygonsToMeshQueue (void)
2974 {
2975         int i;
2976         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
2977         vec3_t center;
2978
2979         // only add polygons of the currently active prog to the queue - if there is none, we're done
2980         if( !prog )
2981                 return;
2982
2983         if (!polys->num_triangles)
2984                 return;
2985
2986         for (i = 0;i < polys->num_triangles;i++)
2987         {
2988                 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);
2989                 R_MeshQueue_AddTransparent(center, VM_DrawPolygonCallback, NULL, i, NULL);
2990         }
2991
2992         /*polys->num_triangles = 0; // now done after rendering the scene,
2993           polys->num_vertices = 0;  // otherwise it's not rendered at all and prints an error message --blub */
2994 }
2995
2996 //void(string texturename, float flag) R_BeginPolygon
2997 void VM_CL_R_PolygonBegin (void)
2998 {
2999         const char              *picname;
3000         skinframe_t     *sf;
3001         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3002         int tf;
3003
3004         // TODO instead of using skinframes here (which provides the benefit of
3005         // better management of flags, and is more suited for 3D rendering), what
3006         // about supporting Q3 shaders?
3007
3008         VM_SAFEPARMCOUNT(2, VM_CL_R_PolygonBegin);
3009
3010         if (!polys->initialized)
3011                 VM_InitPolygons(polys);
3012         if(polys->progstarttime != prog->starttime)
3013         {
3014                 // from another progs? then reset the polys first (fixes crashes on map change, because that can make skinframe textures invalid)
3015                 polys->num_vertices = polys->num_triangles = 0;
3016                 polys->progstarttime = prog->starttime;
3017         }
3018         if (polys->begin_active)
3019         {
3020                 VM_Warning("VM_CL_R_PolygonBegin: called twice without VM_CL_R_PolygonBegin after first\n");
3021                 return;
3022         }
3023         picname = PRVM_G_STRING(OFS_PARM0);
3024
3025         sf = NULL;
3026         if(*picname)
3027         {
3028                 tf = TEXF_ALPHA;
3029                 if((int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MIPMAP)
3030                         tf |= TEXF_MIPMAP;
3031
3032                 do
3033                 {
3034                         sf = R_SkinFrame_FindNextByName(sf, picname);
3035                 }
3036                 while(sf && sf->textureflags != tf);
3037
3038                 if(!sf || !sf->base)
3039                         sf = R_SkinFrame_LoadExternal(picname, tf, true);
3040
3041                 if(sf)
3042                         R_SkinFrame_MarkUsed(sf);
3043         }
3044
3045         polys->begin_texture = (sf && sf->base) ? sf->base : r_texture_white;
3046         polys->begin_drawflag = (int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MASK;
3047         polys->begin_vertices = 0;
3048         polys->begin_active = true;
3049 }
3050
3051 //void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
3052 void VM_CL_R_PolygonVertex (void)
3053 {
3054         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3055
3056         VM_SAFEPARMCOUNT(4, VM_CL_R_PolygonVertex);
3057
3058         if (!polys->begin_active)
3059         {
3060                 VM_Warning("VM_CL_R_PolygonVertex: VM_CL_R_PolygonBegin wasn't called\n");
3061                 return;
3062         }
3063
3064         if (polys->begin_vertices >= VMPOLYGONS_MAXPOINTS)
3065         {
3066                 VM_Warning("VM_CL_R_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
3067                 return;
3068         }
3069
3070         polys->begin_vertex[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM0)[0];
3071         polys->begin_vertex[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM0)[1];
3072         polys->begin_vertex[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM0)[2];
3073         polys->begin_texcoord[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM1)[0];
3074         polys->begin_texcoord[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM1)[1];
3075         polys->begin_color[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM2)[0];
3076         polys->begin_color[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM2)[1];
3077         polys->begin_color[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM2)[2];
3078         polys->begin_color[polys->begin_vertices][3] = PRVM_G_FLOAT(OFS_PARM3);
3079         polys->begin_vertices++;
3080 }
3081
3082 //void() R_EndPolygon
3083 void VM_CL_R_PolygonEnd (void)
3084 {
3085         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3086
3087         VM_SAFEPARMCOUNT(0, VM_CL_R_PolygonEnd);
3088         if (!polys->begin_active)
3089         {
3090                 VM_Warning("VM_CL_R_PolygonEnd: VM_CL_R_PolygonBegin wasn't called\n");
3091                 return;
3092         }
3093         polys->begin_active = false;
3094         if (polys->begin_vertices >= 3)
3095                 VMPolygons_Store(polys);
3096         else
3097                 VM_Warning("VM_CL_R_PolygonEnd: %i vertices isn't a good choice\n", polys->begin_vertices);
3098 }
3099
3100 static vmpolygons_t debugPolys;
3101
3102 void Debug_PolygonBegin(const char *picname, int drawflag)
3103 {
3104         if(!debugPolys.initialized)
3105                 VM_InitPolygons(&debugPolys);
3106         if(debugPolys.begin_active)
3107         {
3108                 Con_Printf("Debug_PolygonBegin: called twice without Debug_PolygonEnd after first\n");
3109                 return;
3110         }
3111         debugPolys.begin_texture = picname[0] ? Draw_CachePic (picname)->tex : r_texture_white;
3112         debugPolys.begin_drawflag = drawflag;
3113         debugPolys.begin_vertices = 0;
3114         debugPolys.begin_active = true;
3115 }
3116
3117 void Debug_PolygonVertex(float x, float y, float z, float s, float t, float r, float g, float b, float a)
3118 {
3119         if(!debugPolys.begin_active)
3120         {
3121                 Con_Printf("Debug_PolygonVertex: Debug_PolygonBegin wasn't called\n");
3122                 return;
3123         }
3124
3125         if(debugPolys.begin_vertices > VMPOLYGONS_MAXPOINTS)
3126         {
3127                 Con_Printf("Debug_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
3128                 return;
3129         }
3130
3131         debugPolys.begin_vertex[debugPolys.begin_vertices][0] = x;
3132         debugPolys.begin_vertex[debugPolys.begin_vertices][1] = y;
3133         debugPolys.begin_vertex[debugPolys.begin_vertices][2] = z;
3134         debugPolys.begin_texcoord[debugPolys.begin_vertices][0] = s;
3135         debugPolys.begin_texcoord[debugPolys.begin_vertices][1] = t;
3136         debugPolys.begin_color[debugPolys.begin_vertices][0] = r;
3137         debugPolys.begin_color[debugPolys.begin_vertices][1] = g;
3138         debugPolys.begin_color[debugPolys.begin_vertices][2] = b;
3139         debugPolys.begin_color[debugPolys.begin_vertices][3] = a;
3140         debugPolys.begin_vertices++;
3141 }
3142
3143 void Debug_PolygonEnd(void)
3144 {
3145         if (!debugPolys.begin_active)
3146         {
3147                 Con_Printf("Debug_PolygonEnd: Debug_PolygonBegin wasn't called\n");
3148                 return;
3149         }
3150         debugPolys.begin_active = false;
3151         if (debugPolys.begin_vertices >= 3)
3152                 VMPolygons_Store(&debugPolys);
3153         else
3154                 Con_Printf("Debug_PolygonEnd: %i vertices isn't a good choice\n", debugPolys.begin_vertices);
3155 }
3156
3157 /*
3158 =============
3159 CL_CheckBottom
3160
3161 Returns false if any part of the bottom of the entity is off an edge that
3162 is not a staircase.
3163
3164 =============
3165 */
3166 qboolean CL_CheckBottom (prvm_edict_t *ent)
3167 {
3168         vec3_t  mins, maxs, start, stop;
3169         trace_t trace;
3170         int             x, y;
3171         float   mid, bottom;
3172
3173         VectorAdd (ent->fields.client->origin, ent->fields.client->mins, mins);
3174         VectorAdd (ent->fields.client->origin, ent->fields.client->maxs, maxs);
3175
3176 // if all of the points under the corners are solid world, don't bother
3177 // with the tougher checks
3178 // the corners must be within 16 of the midpoint
3179         start[2] = mins[2] - 1;
3180         for     (x=0 ; x<=1 ; x++)
3181                 for     (y=0 ; y<=1 ; y++)
3182                 {
3183                         start[0] = x ? maxs[0] : mins[0];
3184                         start[1] = y ? maxs[1] : mins[1];
3185                         if (!(CL_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
3186                                 goto realcheck;
3187                 }
3188
3189         return true;            // we got out easy
3190
3191 realcheck:
3192 //
3193 // check it for real...
3194 //
3195         start[2] = mins[2];
3196
3197 // the midpoint must be within 16 of the bottom
3198         start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
3199         start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
3200         stop[2] = start[2] - 2*sv_stepheight.value;
3201         trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true);
3202
3203         if (trace.fraction == 1.0)
3204                 return false;
3205         mid = bottom = trace.endpos[2];
3206
3207 // the corners must be within 16 of the midpoint
3208         for     (x=0 ; x<=1 ; x++)
3209                 for     (y=0 ; y<=1 ; y++)
3210                 {
3211                         start[0] = stop[0] = x ? maxs[0] : mins[0];
3212                         start[1] = stop[1] = y ? maxs[1] : mins[1];
3213
3214                         trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true);
3215
3216                         if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
3217                                 bottom = trace.endpos[2];
3218                         if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
3219                                 return false;
3220                 }
3221
3222         return true;
3223 }
3224
3225 /*
3226 =============
3227 CL_movestep
3228
3229 Called by monster program code.
3230 The move will be adjusted for slopes and stairs, but if the move isn't
3231 possible, no move is done and false is returned
3232 =============
3233 */
3234 qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean settrace)
3235 {
3236         float           dz;
3237         vec3_t          oldorg, neworg, end, traceendpos;
3238         trace_t         trace;
3239         int                     i, svent;
3240         prvm_edict_t            *enemy;
3241         prvm_eval_t     *val;
3242
3243 // try the move
3244         VectorCopy (ent->fields.client->origin, oldorg);
3245         VectorAdd (ent->fields.client->origin, move, neworg);
3246
3247 // flying monsters don't step up
3248         if ( (int)ent->fields.client->flags & (FL_SWIM | FL_FLY) )
3249         {
3250         // try one move with vertical motion, then one without
3251                 for (i=0 ; i<2 ; i++)
3252                 {
3253                         VectorAdd (ent->fields.client->origin, move, neworg);
3254                         enemy = PRVM_PROG_TO_EDICT(ent->fields.client->enemy);
3255                         if (i == 0 && enemy != prog->edicts)
3256                         {
3257                                 dz = ent->fields.client->origin[2] - PRVM_PROG_TO_EDICT(ent->fields.client->enemy)->fields.client->origin[2];
3258                                 if (dz > 40)
3259                                         neworg[2] -= 8;
3260                                 if (dz < 30)
3261                                         neworg[2] += 8;
3262                         }
3263                         trace = CL_TraceBox(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, neworg, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
3264                         if (settrace)
3265                                 CL_VM_SetTraceGlobals(&trace, svent);
3266
3267                         if (trace.fraction == 1)
3268                         {
3269                                 VectorCopy(trace.endpos, traceendpos);
3270                                 if (((int)ent->fields.client->flags & FL_SWIM) && !(CL_PointSuperContents(traceendpos) & SUPERCONTENTS_LIQUIDSMASK))
3271                                         return false;   // swim monster left water
3272
3273                                 VectorCopy (traceendpos, ent->fields.client->origin);
3274                                 if (relink)
3275                                         CL_LinkEdict(ent);
3276                                 return true;
3277                         }
3278
3279                         if (enemy == prog->edicts)
3280                                 break;
3281                 }
3282
3283                 return false;
3284         }
3285
3286 // push down from a step height above the wished position
3287         neworg[2] += sv_stepheight.value;
3288         VectorCopy (neworg, end);
3289         end[2] -= sv_stepheight.value*2;
3290
3291         trace = CL_TraceBox(neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
3292         if (settrace)
3293                 CL_VM_SetTraceGlobals(&trace, svent);
3294
3295         if (trace.startsolid)
3296         {
3297                 neworg[2] -= sv_stepheight.value;
3298                 trace = CL_TraceBox(neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
3299                 if (settrace)
3300                         CL_VM_SetTraceGlobals(&trace, svent);
3301                 if (trace.startsolid)
3302                         return false;
3303         }
3304         if (trace.fraction == 1)
3305         {
3306         // if monster had the ground pulled out, go ahead and fall
3307                 if ( (int)ent->fields.client->flags & FL_PARTIALGROUND )
3308                 {
3309                         VectorAdd (ent->fields.client->origin, move, ent->fields.client->origin);
3310                         if (relink)
3311                                 CL_LinkEdict(ent);
3312                         ent->fields.client->flags = (int)ent->fields.client->flags & ~FL_ONGROUND;
3313                         return true;
3314                 }
3315
3316                 return false;           // walked off an edge
3317         }
3318
3319 // check point traces down for dangling corners
3320         VectorCopy (trace.endpos, ent->fields.client->origin);
3321
3322         if (!CL_CheckBottom (ent))
3323         {
3324                 if ( (int)ent->fields.client->flags & FL_PARTIALGROUND )
3325                 {       // entity had floor mostly pulled out from underneath it
3326                         // and is trying to correct
3327                         if (relink)
3328                                 CL_LinkEdict(ent);
3329                         return true;
3330                 }
3331                 VectorCopy (oldorg, ent->fields.client->origin);
3332                 return false;
3333         }
3334
3335         if ( (int)ent->fields.client->flags & FL_PARTIALGROUND )
3336                 ent->fields.client->flags = (int)ent->fields.client->flags & ~FL_PARTIALGROUND;
3337
3338         if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.groundentity)))
3339                 val->edict = PRVM_EDICT_TO_PROG(trace.ent);
3340
3341 // the move is ok
3342         if (relink)
3343                 CL_LinkEdict(ent);
3344         return true;
3345 }
3346
3347 /*
3348 ===============
3349 VM_CL_walkmove
3350
3351 float(float yaw, float dist[, settrace]) walkmove
3352 ===============
3353 */
3354 static void VM_CL_walkmove (void)
3355 {
3356         prvm_edict_t    *ent;
3357         float   yaw, dist;
3358         vec3_t  move;
3359         mfunction_t     *oldf;
3360         int     oldself;
3361         qboolean        settrace;
3362
3363         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_walkmove);
3364
3365         // assume failure if it returns early
3366         PRVM_G_FLOAT(OFS_RETURN) = 0;
3367
3368         ent = PRVM_PROG_TO_EDICT(prog->globals.client->self);
3369         if (ent == prog->edicts)
3370         {
3371                 VM_Warning("walkmove: can not modify world entity\n");
3372                 return;
3373         }
3374         if (ent->priv.server->free)
3375         {
3376                 VM_Warning("walkmove: can not modify free entity\n");
3377                 return;
3378         }
3379         yaw = PRVM_G_FLOAT(OFS_PARM0);
3380         dist = PRVM_G_FLOAT(OFS_PARM1);
3381         settrace = prog->argc >= 3 && PRVM_G_FLOAT(OFS_PARM2);
3382
3383         if ( !( (int)ent->fields.client->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
3384                 return;
3385
3386         yaw = yaw*M_PI*2 / 360;
3387
3388         move[0] = cos(yaw)*dist;
3389         move[1] = sin(yaw)*dist;
3390         move[2] = 0;
3391
3392 // save program state, because CL_movestep may call other progs
3393         oldf = prog->xfunction;
3394         oldself = prog->globals.client->self;
3395
3396         PRVM_G_FLOAT(OFS_RETURN) = CL_movestep(ent, move, true, false, settrace);
3397
3398
3399 // restore program state
3400         prog->xfunction = oldf;
3401         prog->globals.client->self = oldself;
3402 }
3403
3404 /*
3405 ===============
3406 VM_CL_serverkey
3407
3408 string(string key) serverkey
3409 ===============
3410 */
3411 void VM_CL_serverkey(void)
3412 {
3413         char string[VM_STRINGTEMP_LENGTH];
3414         VM_SAFEPARMCOUNT(1, VM_CL_serverkey);
3415         InfoString_GetValue(cl.qw_serverinfo, PRVM_G_STRING(OFS_PARM0), string, sizeof(string));
3416         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
3417 }
3418
3419 /*
3420 =================
3421 VM_CL_checkpvs
3422
3423 Checks if an entity is in a point's PVS.
3424 Should be fast but can be inexact.
3425
3426 float checkpvs(vector viewpos, entity viewee) = #240;
3427 =================
3428 */
3429 static void VM_CL_checkpvs (void)
3430 {
3431         vec3_t viewpos;
3432         prvm_edict_t *viewee;
3433         vec3_t mi, ma;
3434 #if 1
3435         unsigned char *pvs;
3436 #else
3437         int fatpvsbytes;
3438         unsigned char fatpvs[MAX_MAP_LEAFS/8];
3439 #endif
3440
3441         VM_SAFEPARMCOUNT(2, VM_SV_checkpvs);
3442         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), viewpos);
3443         viewee = PRVM_G_EDICT(OFS_PARM1);
3444
3445         if(viewee->priv.required->free)
3446         {
3447                 VM_Warning("checkpvs: can not check free entity\n");
3448                 PRVM_G_FLOAT(OFS_RETURN) = 4;
3449                 return;
3450         }
3451
3452         VectorAdd(viewee->fields.server->origin, viewee->fields.server->mins, mi);
3453         VectorAdd(viewee->fields.server->origin, viewee->fields.server->maxs, ma);
3454
3455 #if 1
3456         if(!sv.worldmodel->brush.GetPVS || !sv.worldmodel->brush.BoxTouchingPVS)
3457         {
3458                 // no PVS support on this worldmodel... darn
3459                 PRVM_G_FLOAT(OFS_RETURN) = 3;
3460                 return;
3461         }
3462         pvs = sv.worldmodel->brush.GetPVS(sv.worldmodel, viewpos);
3463         if(!pvs)
3464         {
3465                 // viewpos isn't in any PVS... darn
3466                 PRVM_G_FLOAT(OFS_RETURN) = 2;
3467                 return;
3468         }
3469         PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, pvs, mi, ma);
3470 #else
3471         // using fat PVS like FTEQW does (slow)
3472         if(!sv.worldmodel->brush.FatPVS || !sv.worldmodel->brush.BoxTouchingPVS)
3473         {
3474                 // no PVS support on this worldmodel... darn
3475                 PRVM_G_FLOAT(OFS_RETURN) = 3;
3476                 return;
3477         }
3478         fatpvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, viewpos, 8, fatpvs, sizeof(fatpvs), false);
3479         if(!fatpvsbytes)
3480         {
3481                 // viewpos isn't in any PVS... darn
3482                 PRVM_G_FLOAT(OFS_RETURN) = 2;
3483                 return;
3484         }
3485         PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, fatpvs, mi, ma);
3486 #endif
3487 }
3488
3489 // #263 float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) create a skeleton (be sure to assign this value into .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure  (for example if the modelindex is not skeletal), it is recommended that you create a new skeleton if you change modelindex.
3490 static void VM_CL_skel_create(void)
3491 {
3492         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
3493         dp_model_t *model = CL_GetModelByIndex(modelindex);
3494         skeleton_t *skeleton;
3495         int i;
3496         PRVM_G_FLOAT(OFS_RETURN) = 0;
3497         if (!model || !model->num_bones)
3498                 return;
3499         for (i = 0;i < MAX_EDICTS;i++)
3500                 if (!prog->skeletons[i])
3501                         break;
3502         if (i == MAX_EDICTS)
3503                 return;
3504         prog->skeletons[i] = skeleton = Mem_Alloc(cls.levelmempool, sizeof(skeleton_t) + model->num_bones * sizeof(matrix4x4_t));
3505         PRVM_G_FLOAT(OFS_RETURN) = i + 1;
3506         skeleton->model = model;
3507         skeleton->relativetransforms = (matrix4x4_t *)(skeleton+1);
3508         // initialize to identity matrices
3509         for (i = 0;i < skeleton->model->num_bones;i++)
3510                 skeleton->relativetransforms[i] = identitymatrix;
3511 }
3512
3513 // #264 float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .frame3, .frame4, .lerpfrac, .lerpfrac3, .lerpfrac4, .frame1time, .frame2time, .frame3time, .frame4time), returns skel on success, 0 on failure
3514 static void VM_CL_skel_build(void)
3515 {
3516         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3517         skeleton_t *skeleton;
3518         prvm_edict_t *ed = PRVM_G_EDICT(OFS_PARM1);
3519         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM2);
3520         float retainfrac = PRVM_G_FLOAT(OFS_PARM3);
3521         int firstbone = PRVM_G_FLOAT(OFS_PARM4) - 1;
3522         int lastbone = PRVM_G_FLOAT(OFS_PARM5) - 1;
3523         dp_model_t *model = CL_GetModelByIndex(modelindex);
3524         float blendfrac;
3525         int numblends;
3526         int bonenum;
3527         int blendindex;
3528         framegroupblend_t framegroupblend[MAX_FRAMEGROUPBLENDS];
3529         frameblend_t frameblend[MAX_FRAMEBLENDS];
3530         matrix4x4_t blendedmatrix;
3531         matrix4x4_t matrix;
3532         PRVM_G_FLOAT(OFS_RETURN) = 0;
3533         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3534                 return;
3535         firstbone = max(0, firstbone);
3536         lastbone = min(lastbone, model->num_bones - 1);
3537         lastbone = min(lastbone, skeleton->model->num_bones - 1);
3538         VM_GenerateFrameGroupBlend(framegroupblend, ed);
3539         VM_FrameBlendFromFrameGroupBlend(frameblend, framegroupblend, model);
3540         blendfrac = 1.0f - retainfrac;
3541         for (numblends = 0;numblends < MAX_FRAMEBLENDS && frameblend[numblends].lerp;numblends++)
3542                 frameblend[numblends].lerp *= blendfrac;
3543         for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
3544         {
3545                 memset(&blendedmatrix, 0, sizeof(blendedmatrix));
3546                 Matrix4x4_Accumulate(&blendedmatrix, &skeleton->relativetransforms[bonenum], retainfrac);
3547                 for (blendindex = 0;blendindex < numblends;blendindex++)
3548                 {
3549                         Matrix4x4_FromBonePose6s(&matrix, model->num_posescale, model->data_poses6s + 6 * (frameblend[blendindex].subframe * model->num_bones + bonenum));
3550                         Matrix4x4_Accumulate(&blendedmatrix, &matrix, frameblend[blendindex].lerp);
3551                 }
3552                 skeleton->relativetransforms[bonenum] = blendedmatrix;
3553         }
3554         PRVM_G_FLOAT(OFS_RETURN) = skeletonindex + 1;
3555 }
3556
3557 // #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
3558 static void VM_CL_skel_get_numbones(void)
3559 {
3560         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3561         skeleton_t *skeleton;
3562         PRVM_G_FLOAT(OFS_RETURN) = 0;
3563         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3564                 return;
3565         PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->num_bones;
3566 }
3567
3568 // #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
3569 static void VM_CL_skel_get_bonename(void)
3570 {
3571         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3572         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3573         skeleton_t *skeleton;
3574         PRVM_G_INT(OFS_RETURN) = 0;
3575         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3576                 return;
3577         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3578                 return;
3579         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(skeleton->model->data_bones[bonenum].name);
3580 }
3581
3582 // #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) returns parent num for supplied bonenum, 0 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
3583 static void VM_CL_skel_get_boneparent(void)
3584 {
3585         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3586         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3587         skeleton_t *skeleton;
3588         PRVM_G_FLOAT(OFS_RETURN) = 0;
3589         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3590                 return;
3591         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3592                 return;
3593         PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->data_bones[bonenum].parent + 1;
3594 }
3595
3596 // #268 float(float skel, string tagname) skel_find_bone = #268; // (FTE_CSQC_SKELETONOBJECTS) get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex
3597 static void VM_CL_skel_find_bone(void)
3598 {
3599         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3600         const char *tagname = PRVM_G_STRING(OFS_PARM1);
3601         skeleton_t *skeleton;
3602         PRVM_G_FLOAT(OFS_RETURN) = 0;
3603         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3604                 return;
3605         PRVM_G_FLOAT(OFS_RETURN) = Mod_Alias_GetTagIndexForName(skeleton->model, 0, tagname) + 1;
3606 }
3607
3608 // #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
3609 static void VM_CL_skel_get_bonerel(void)
3610 {
3611         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3612         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3613         skeleton_t *skeleton;
3614         matrix4x4_t matrix;
3615         vec3_t forward, left, up, origin;
3616         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
3617         VectorClear(prog->globals.client->v_forward);
3618         VectorClear(prog->globals.client->v_right);
3619         VectorClear(prog->globals.client->v_up);
3620         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3621                 return;
3622         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3623                 return;
3624         matrix = skeleton->relativetransforms[bonenum];
3625         Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
3626         VectorCopy(forward, prog->globals.client->v_forward);
3627         VectorNegate(left, prog->globals.client->v_right);
3628         VectorCopy(up, prog->globals.client->v_up);
3629         VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
3630 }
3631
3632 // #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
3633 static void VM_CL_skel_get_boneabs(void)
3634 {
3635         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3636         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3637         skeleton_t *skeleton;
3638         matrix4x4_t matrix;
3639         matrix4x4_t temp;
3640         vec3_t forward, left, up, origin;
3641         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
3642         VectorClear(prog->globals.client->v_forward);
3643         VectorClear(prog->globals.client->v_right);
3644         VectorClear(prog->globals.client->v_up);
3645         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3646                 return;
3647         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3648                 return;
3649         matrix = skeleton->relativetransforms[bonenum];
3650         // convert to absolute
3651         while ((bonenum = skeleton->model->data_bones[bonenum].parent) >= 0)
3652         {
3653                 temp = matrix;
3654                 Matrix4x4_Concat(&matrix, &skeleton->relativetransforms[bonenum], &temp);
3655         }
3656         Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
3657         VectorCopy(forward, prog->globals.client->v_forward);
3658         VectorNegate(left, prog->globals.client->v_right);
3659         VectorCopy(up, prog->globals.client->v_up);
3660         VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
3661 }
3662
3663 // #271 void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
3664 static void VM_CL_skel_set_bone(void)
3665 {
3666         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3667         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3668         vec3_t forward, left, up, origin;
3669         skeleton_t *skeleton;
3670         matrix4x4_t matrix;
3671         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3672                 return;
3673         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3674                 return;
3675         VectorCopy(prog->globals.client->v_forward, forward);
3676         VectorNegate(prog->globals.client->v_right, left);
3677         VectorCopy(prog->globals.client->v_up, up);
3678         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
3679         Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
3680         skeleton->relativetransforms[bonenum] = matrix;
3681 }
3682
3683 // #272 void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
3684 static void VM_CL_skel_mul_bone(void)
3685 {
3686         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3687         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3688         vec3_t forward, left, up, origin;
3689         skeleton_t *skeleton;
3690         matrix4x4_t matrix;
3691         matrix4x4_t temp;
3692         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3693                 return;
3694         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3695                 return;
3696         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
3697         VectorCopy(prog->globals.client->v_forward, forward);
3698         VectorNegate(prog->globals.client->v_right, left);
3699         VectorCopy(prog->globals.client->v_up, up);
3700         Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
3701         temp = skeleton->relativetransforms[bonenum];
3702         Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
3703 }
3704
3705 // #273 void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrices (relative to their parents) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bones)
3706 static void VM_CL_skel_mul_bones(void)
3707 {
3708         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3709         int firstbone = PRVM_G_FLOAT(OFS_PARM1) - 1;
3710         int lastbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
3711         int bonenum;
3712         vec3_t forward, left, up, origin;
3713         skeleton_t *skeleton;
3714         matrix4x4_t matrix;
3715         matrix4x4_t temp;
3716         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3717                 return;
3718         VectorCopy(PRVM_G_VECTOR(OFS_PARM3), origin);
3719         VectorCopy(prog->globals.client->v_forward, forward);
3720         VectorNegate(prog->globals.client->v_right, left);
3721         VectorCopy(prog->globals.client->v_up, up);
3722         Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
3723         firstbone = max(0, firstbone);
3724         lastbone = min(lastbone, skeleton->model->num_bones - 1);
3725         for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
3726         {
3727                 temp = skeleton->relativetransforms[bonenum];
3728                 Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
3729         }
3730 }
3731
3732 // #274 void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS) copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse
3733 static void VM_CL_skel_copybones(void)
3734 {
3735         int skeletonindexdst = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3736         int skeletonindexsrc = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3737         int firstbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
3738         int lastbone = PRVM_G_FLOAT(OFS_PARM3) - 1;
3739         int bonenum;
3740         skeleton_t *skeletondst;
3741         skeleton_t *skeletonsrc;
3742         if (skeletonindexdst < 0 || skeletonindexdst >= MAX_EDICTS || !(skeletondst = prog->skeletons[skeletonindexdst]))
3743                 return;
3744         if (skeletonindexsrc < 0 || skeletonindexsrc >= MAX_EDICTS || !(skeletonsrc = prog->skeletons[skeletonindexsrc]))
3745                 return;
3746         firstbone = max(0, firstbone);
3747         lastbone = min(lastbone, skeletondst->model->num_bones - 1);
3748         lastbone = min(lastbone, skeletonsrc->model->num_bones - 1);
3749         for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
3750                 skeletondst->relativetransforms[bonenum] = skeletonsrc->relativetransforms[bonenum];
3751 }
3752
3753 // #275 void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS) deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)
3754 static void VM_CL_skel_delete(void)
3755 {
3756         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3757         skeleton_t *skeleton;
3758         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3759                 return;
3760         Mem_Free(skeleton);
3761         prog->skeletons[skeletonindex] = NULL;
3762 }
3763
3764 // #276 float(float modlindex, string framename) frameforname = #276; // (FTE_CSQC_SKELETONOBJECTS) finds number of a specified frame in the animation, returns -1 if no match found
3765 static void VM_CL_frameforname(void)
3766 {
3767         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
3768         dp_model_t *model = CL_GetModelByIndex(modelindex);
3769         const char *name = PRVM_G_STRING(OFS_PARM1);
3770         int i;
3771         PRVM_G_FLOAT(OFS_RETURN) = -1;
3772         if (!model || !model->animscenes)
3773                 return;
3774         for (i = 0;i < model->numframes;i++)
3775         {
3776                 if (!strcasecmp(model->animscenes[i].name, name))
3777                 {
3778                         PRVM_G_FLOAT(OFS_RETURN) = i;
3779                         break;
3780                 }
3781         }
3782 }
3783
3784 // #277 float(float modlindex, float framenum) frameduration = #277; // (FTE_CSQC_SKELETONOBJECTS) returns the intended play time (in seconds) of the specified framegroup, if it does not exist the result is 0, if it is a single frame it may be a small value around 0.1 or 0.
3785 static void VM_CL_frameduration(void)
3786 {
3787         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
3788         dp_model_t *model = CL_GetModelByIndex(modelindex);
3789         int framenum = (int)PRVM_G_FLOAT(OFS_PARM1);
3790         PRVM_G_FLOAT(OFS_RETURN) = 0;
3791         if (!model || !model->animscenes || framenum < 0 || framenum >= model->numframes)
3792                 return;
3793         if (model->animscenes[framenum].framerate)
3794                 PRVM_G_FLOAT(OFS_RETURN) = model->animscenes[framenum].framecount / model->animscenes[framenum].framerate;
3795 }
3796
3797 //============================================================================
3798
3799 // To create a almost working builtin file from this replace:
3800 // "^NULL.*" with ""
3801 // "^{.*//.*}:Wh\(.*\)" with "\1"
3802 // "\:" with "//"
3803 // "^.*//:Wh{\#:d*}:Wh{.*}" with "\2 = \1;"
3804 // "\n\n+" with "\n\n"
3805
3806 prvm_builtin_t vm_cl_builtins[] = {
3807 NULL,                                                   // #0 NULL function (not callable) (QUAKE)
3808 VM_CL_makevectors,                              // #1 void(vector ang) makevectors (QUAKE)
3809 VM_CL_setorigin,                                // #2 void(entity e, vector o) setorigin (QUAKE)
3810 VM_CL_setmodel,                                 // #3 void(entity e, string m) setmodel (QUAKE)
3811 VM_CL_setsize,                                  // #4 void(entity e, vector min, vector max) setsize (QUAKE)
3812 NULL,                                                   // #5 void(entity e, vector min, vector max) setabssize (QUAKE)
3813 VM_break,                                               // #6 void() break (QUAKE)
3814 VM_random,                                              // #7 float() random (QUAKE)
3815 VM_CL_sound,                                    // #8 void(entity e, float chan, string samp) sound (QUAKE)
3816 VM_normalize,                                   // #9 vector(vector v) normalize (QUAKE)
3817 VM_error,                                               // #10 void(string e) error (QUAKE)
3818 VM_objerror,                                    // #11 void(string e) objerror (QUAKE)
3819 VM_vlen,                                                // #12 float(vector v) vlen (QUAKE)
3820 VM_vectoyaw,                                    // #13 float(vector v) vectoyaw (QUAKE)
3821 VM_CL_spawn,                                    // #14 entity() spawn (QUAKE)
3822 VM_remove,                                              // #15 void(entity e) remove (QUAKE)
3823 VM_CL_traceline,                                // #16 void(vector v1, vector v2, float tryents, entity ignoreentity) traceline (QUAKE)
3824 NULL,                                                   // #17 entity() checkclient (QUAKE)
3825 VM_find,                                                // #18 entity(entity start, .string fld, string match) find (QUAKE)
3826 VM_precache_sound,                              // #19 void(string s) precache_sound (QUAKE)
3827 VM_CL_precache_model,                   // #20 void(string s) precache_model (QUAKE)
3828 NULL,                                                   // #21 void(entity client, string s, ...) stuffcmd (QUAKE)
3829 VM_CL_findradius,                               // #22 entity(vector org, float rad) findradius (QUAKE)
3830 NULL,                                                   // #23 void(string s, ...) bprint (QUAKE)
3831 NULL,                                                   // #24 void(entity client, string s, ...) sprint (QUAKE)
3832 VM_dprint,                                              // #25 void(string s, ...) dprint (QUAKE)
3833 VM_ftos,                                                // #26 string(float f) ftos (QUAKE)
3834 VM_vtos,                                                // #27 string(vector v) vtos (QUAKE)
3835 VM_coredump,                                    // #28 void() coredump (QUAKE)
3836 VM_traceon,                                             // #29 void() traceon (QUAKE)
3837 VM_traceoff,                                    // #30 void() traceoff (QUAKE)
3838 VM_eprint,                                              // #31 void(entity e) eprint (QUAKE)
3839 VM_CL_walkmove,                                 // #32 float(float yaw, float dist[, float settrace]) walkmove (QUAKE)
3840 NULL,                                                   // #33 (QUAKE)
3841 VM_CL_droptofloor,                              // #34 float() droptofloor (QUAKE)
3842 VM_CL_lightstyle,                               // #35 void(float style, string value) lightstyle (QUAKE)
3843 VM_rint,                                                // #36 float(float v) rint (QUAKE)
3844 VM_floor,                                               // #37 float(float v) floor (QUAKE)
3845 VM_ceil,                                                // #38 float(float v) ceil (QUAKE)
3846 NULL,                                                   // #39 (QUAKE)
3847 VM_CL_checkbottom,                              // #40 float(entity e) checkbottom (QUAKE)
3848 VM_CL_pointcontents,                    // #41 float(vector v) pointcontents (QUAKE)
3849 NULL,                                                   // #42 (QUAKE)
3850 VM_fabs,                                                // #43 float(float f) fabs (QUAKE)
3851 NULL,                                                   // #44 vector(entity e, float speed) aim (QUAKE)
3852 VM_cvar,                                                // #45 float(string s) cvar (QUAKE)
3853 VM_localcmd,                                    // #46 void(string s) localcmd (QUAKE)
3854 VM_nextent,                                             // #47 entity(entity e) nextent (QUAKE)
3855 VM_CL_particle,                                 // #48 void(vector o, vector d, float color, float count) particle (QUAKE)
3856 VM_changeyaw,                                   // #49 void() ChangeYaw (QUAKE)
3857 NULL,                                                   // #50 (QUAKE)
3858 VM_vectoangles,                                 // #51 vector(vector v) vectoangles (QUAKE)
3859 NULL,                                                   // #52 void(float to, float f) WriteByte (QUAKE)
3860 NULL,                                                   // #53 void(float to, float f) WriteChar (QUAKE)
3861 NULL,                                                   // #54 void(float to, float f) WriteShort (QUAKE)
3862 NULL,                                                   // #55 void(float to, float f) WriteLong (QUAKE)
3863 NULL,                                                   // #56 void(float to, float f) WriteCoord (QUAKE)
3864 NULL,                                                   // #57 void(float to, float f) WriteAngle (QUAKE)
3865 NULL,                                                   // #58 void(float to, string s) WriteString (QUAKE)
3866 NULL,                                                   // #59 (QUAKE)
3867 VM_sin,                                                 // #60 float(float f) sin (DP_QC_SINCOSSQRTPOW)
3868 VM_cos,                                                 // #61 float(float f) cos (DP_QC_SINCOSSQRTPOW)
3869 VM_sqrt,                                                // #62 float(float f) sqrt (DP_QC_SINCOSSQRTPOW)
3870 VM_changepitch,                                 // #63 void(entity ent) changepitch (DP_QC_CHANGEPITCH)
3871 VM_CL_tracetoss,                                // #64 void(entity e, entity ignore) tracetoss (DP_QC_TRACETOSS)
3872 VM_etos,                                                // #65 string(entity ent) etos (DP_QC_ETOS)
3873 NULL,                                                   // #66 (QUAKE)
3874 NULL,                                                   // #67 void(float step) movetogoal (QUAKE)
3875 VM_precache_file,                               // #68 string(string s) precache_file (QUAKE)
3876 VM_CL_makestatic,                               // #69 void(entity e) makestatic (QUAKE)
3877 NULL,                                                   // #70 void(string s) changelevel (QUAKE)
3878 NULL,                                                   // #71 (QUAKE)
3879 VM_cvar_set,                                    // #72 void(string var, string val) cvar_set (QUAKE)
3880 NULL,                                                   // #73 void(entity client, strings) centerprint (QUAKE)
3881 VM_CL_ambientsound,                             // #74 void(vector pos, string samp, float vol, float atten) ambientsound (QUAKE)
3882 VM_CL_precache_model,                   // #75 string(string s) precache_model2 (QUAKE)
3883 VM_precache_sound,                              // #76 string(string s) precache_sound2 (QUAKE)
3884 VM_precache_file,                               // #77 string(string s) precache_file2 (QUAKE)
3885 NULL,                                                   // #78 void(entity e) setspawnparms (QUAKE)
3886 NULL,                                                   // #79 void(entity killer, entity killee) logfrag (QUAKEWORLD)
3887 NULL,                                                   // #80 string(entity e, string keyname) infokey (QUAKEWORLD)
3888 VM_stof,                                                // #81 float(string s) stof (FRIK_FILE)
3889 NULL,                                                   // #82 void(vector where, float set) multicast (QUAKEWORLD)
3890 NULL,                                                   // #83 (QUAKE)
3891 NULL,                                                   // #84 (QUAKE)
3892 NULL,                                                   // #85 (QUAKE)
3893 NULL,                                                   // #86 (QUAKE)
3894 NULL,                                                   // #87 (QUAKE)
3895 NULL,                                                   // #88 (QUAKE)
3896 NULL,                                                   // #89 (QUAKE)
3897 VM_CL_tracebox,                                 // #90 void(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox (DP_QC_TRACEBOX)
3898 VM_randomvec,                                   // #91 vector() randomvec (DP_QC_RANDOMVEC)
3899 VM_CL_getlight,                                 // #92 vector(vector org) getlight (DP_QC_GETLIGHT)
3900 VM_registercvar,                                // #93 float(string name, string value) registercvar (DP_REGISTERCVAR)
3901 VM_min,                                                 // #94 float(float a, floats) min (DP_QC_MINMAXBOUND)
3902 VM_max,                                                 // #95 float(float a, floats) max (DP_QC_MINMAXBOUND)
3903 VM_bound,                                               // #96 float(float minimum, float val, float maximum) bound (DP_QC_MINMAXBOUND)
3904 VM_pow,                                                 // #97 float(float f, float f) pow (DP_QC_SINCOSSQRTPOW)
3905 VM_findfloat,                                   // #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT)
3906 VM_checkextension,                              // #99 float(string s) checkextension (the basis of the extension system)
3907 // FrikaC and Telejano range #100-#199
3908 NULL,                                                   // #100
3909 NULL,                                                   // #101
3910 NULL,                                                   // #102
3911 NULL,                                                   // #103
3912 NULL,                                                   // #104
3913 NULL,                                                   // #105
3914 NULL,                                                   // #106
3915 NULL,                                                   // #107
3916 NULL,                                                   // #108
3917 NULL,                                                   // #109
3918 VM_fopen,                                               // #110 float(string filename, float mode) fopen (FRIK_FILE)
3919 VM_fclose,                                              // #111 void(float fhandle) fclose (FRIK_FILE)
3920 VM_fgets,                                               // #112 string(float fhandle) fgets (FRIK_FILE)
3921 VM_fputs,                                               // #113 void(float fhandle, string s) fputs (FRIK_FILE)
3922 VM_strlen,                                              // #114 float(string s) strlen (FRIK_FILE)
3923 VM_strcat,                                              // #115 string(string s1, string s2, ...) strcat (FRIK_FILE)
3924 VM_substring,                                   // #116 string(string s, float start, float length) substring (FRIK_FILE)
3925 VM_stov,                                                // #117 vector(string) stov (FRIK_FILE)
3926 VM_strzone,                                             // #118 string(string s) strzone (FRIK_FILE)
3927 VM_strunzone,                                   // #119 void(string s) strunzone (FRIK_FILE)
3928 NULL,                                                   // #120
3929 NULL,                                                   // #121
3930 NULL,                                                   // #122
3931 NULL,                                                   // #123
3932 NULL,                                                   // #124
3933 NULL,                                                   // #125
3934 NULL,                                                   // #126
3935 NULL,                                                   // #127
3936 NULL,                                                   // #128
3937 NULL,                                                   // #129
3938 NULL,                                                   // #130
3939 NULL,                                                   // #131
3940 NULL,                                                   // #132
3941 NULL,                                                   // #133
3942 NULL,                                                   // #134
3943 NULL,                                                   // #135
3944 NULL,                                                   // #136
3945 NULL,                                                   // #137
3946 NULL,                                                   // #138
3947 NULL,                                                   // #139
3948 NULL,                                                   // #140
3949 NULL,                                                   // #141
3950 NULL,                                                   // #142
3951 NULL,                                                   // #143
3952 NULL,                                                   // #144
3953 NULL,                                                   // #145
3954 NULL,                                                   // #146
3955 NULL,                                                   // #147
3956 NULL,                                                   // #148
3957 NULL,                                                   // #149
3958 NULL,                                                   // #150
3959 NULL,                                                   // #151
3960 NULL,                                                   // #152
3961 NULL,                                                   // #153
3962 NULL,                                                   // #154
3963 NULL,                                                   // #155
3964 NULL,                                                   // #156
3965 NULL,                                                   // #157
3966 NULL,                                                   // #158
3967 NULL,                                                   // #159
3968 NULL,                                                   // #160
3969 NULL,                                                   // #161
3970 NULL,                                                   // #162
3971 NULL,                                                   // #163
3972 NULL,                                                   // #164
3973 NULL,                                                   // #165
3974 NULL,                                                   // #166
3975 NULL,                                                   // #167
3976 NULL,                                                   // #168
3977 NULL,                                                   // #169
3978 NULL,                                                   // #170
3979 NULL,                                                   // #171
3980 NULL,                                                   // #172
3981 NULL,                                                   // #173
3982 NULL,                                                   // #174
3983 NULL,                                                   // #175
3984 NULL,                                                   // #176
3985 NULL,                                                   // #177
3986 NULL,                                                   // #178
3987 NULL,                                                   // #179
3988 NULL,                                                   // #180
3989 NULL,                                                   // #181
3990 NULL,                                                   // #182
3991 NULL,                                                   // #183
3992 NULL,                                                   // #184
3993 NULL,                                                   // #185
3994 NULL,                                                   // #186
3995 NULL,                                                   // #187
3996 NULL,                                                   // #188
3997 NULL,                                                   // #189
3998 NULL,                                                   // #190
3999 NULL,                                                   // #191
4000 NULL,                                                   // #192
4001 NULL,                                                   // #193
4002 NULL,                                                   // #194
4003 NULL,                                                   // #195
4004 NULL,                                                   // #196
4005 NULL,                                                   // #197
4006 NULL,                                                   // #198
4007 NULL,                                                   // #199
4008 // FTEQW range #200-#299
4009 NULL,                                                   // #200
4010 NULL,                                                   // #201
4011 NULL,                                                   // #202
4012 NULL,                                                   // #203
4013 NULL,                                                   // #204
4014 NULL,                                                   // #205
4015 NULL,                                                   // #206
4016 NULL,                                                   // #207
4017 NULL,                                                   // #208
4018 NULL,                                                   // #209
4019 NULL,                                                   // #210
4020 NULL,                                                   // #211
4021 NULL,                                                   // #212
4022 NULL,                                                   // #213
4023 NULL,                                                   // #214
4024 NULL,                                                   // #215
4025 NULL,                                                   // #216
4026 NULL,                                                   // #217
4027 VM_bitshift,                                    // #218 float(float number, float quantity) bitshift (EXT_BITSHIFT)
4028 NULL,                                                   // #219
4029 NULL,                                                   // #220
4030 VM_strstrofs,                                   // #221 float(string str, string sub[, float startpos]) strstrofs (FTE_STRINGS)
4031 VM_str2chr,                                             // #222 float(string str, float ofs) str2chr (FTE_STRINGS)
4032 VM_chr2str,                                             // #223 string(float c, ...) chr2str (FTE_STRINGS)
4033 VM_strconv,                                             // #224 string(float ccase, float calpha, float cnum, string s, ...) strconv (FTE_STRINGS)
4034 VM_strpad,                                              // #225 string(float chars, string s, ...) strpad (FTE_STRINGS)
4035 VM_infoadd,                                             // #226 string(string info, string key, string value, ...) infoadd (FTE_STRINGS)
4036 VM_infoget,                                             // #227 string(string info, string key) infoget (FTE_STRINGS)
4037 VM_strncmp,                                             // #228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
4038 VM_strncasecmp,                                 // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)
4039 VM_strncasecmp,                                 // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)
4040 NULL,                                                   // #231
4041 NULL,                                                   // #232 void(float index, float type, .void field) SV_AddStat (EXT_CSQC)
4042 NULL,                                                   // #233
4043 NULL,                                                   // #234
4044 NULL,                                                   // #235
4045 NULL,                                                   // #236
4046 NULL,                                                   // #237
4047 NULL,                                                   // #238
4048 NULL,                                                   // #239
4049 VM_CL_checkpvs,                                 // #240
4050 NULL,                                                   // #241
4051 NULL,                                                   // #242
4052 NULL,                                                   // #243
4053 NULL,                                                   // #244
4054 NULL,                                                   // #245
4055 NULL,                                                   // #246
4056 NULL,                                                   // #247
4057 NULL,                                                   // #248
4058 NULL,                                                   // #249
4059 NULL,                                                   // #250
4060 NULL,                                                   // #251
4061 NULL,                                                   // #252
4062 NULL,                                                   // #253
4063 NULL,                                                   // #254
4064 NULL,                                                   // #255
4065 NULL,                                                   // #256
4066 NULL,                                                   // #257
4067 NULL,                                                   // #258
4068 NULL,                                                   // #259
4069 NULL,                                                   // #260
4070 NULL,                                                   // #261
4071 NULL,                                                   // #262
4072 VM_CL_skel_create,                              // #263 float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) create a skeleton (be sure to assign this value into .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure  (for example if the modelindex is not skeletal), it is recommended that you create a new skeleton if you change modelindex.
4073 VM_CL_skel_build,                               // #264 float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .frame3, .frame4, .lerpfrac, .lerpfrac3, .lerpfrac4, .frame1time, .frame2time, .frame3time, .frame4time), returns skel on success, 0 on failure
4074 VM_CL_skel_get_numbones,                // #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
4075 VM_CL_skel_get_bonename,                // #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
4076 VM_CL_skel_get_boneparent,              // #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) returns parent num for supplied bonenum, -1 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
4077 VM_CL_skel_find_bone,                   // #268 float(float skel, string tagname) skel_find_bone = #268; // (FTE_CSQC_SKELETONOBJECTS) get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex
4078 VM_CL_skel_get_bonerel,                 // #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
4079 VM_CL_skel_get_boneabs,                 // #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
4080 VM_CL_skel_set_bone,                    // #271 void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
4081 VM_CL_skel_mul_bone,                    // #272 void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
4082 VM_CL_skel_mul_bones,                   // #273 void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrices (relative to their parents) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bones)
4083 VM_CL_skel_copybones,                   // #274 void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS) copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse
4084 VM_CL_skel_delete,                              // #275 void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS) deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)
4085 VM_CL_frameforname,                             // #276 float(float modlindex, string framename) frameforname = #276; // (FTE_CSQC_SKELETONOBJECTS) finds number of a specified frame in the animation, returns -1 if no match found
4086 VM_CL_frameduration,                    // #277 float(float modlindex, float framenum) frameduration = #277; // (FTE_CSQC_SKELETONOBJECTS) returns the intended play time (in seconds) of the specified framegroup, if it does not exist the result is 0, if it is a single frame it may be a small value around 0.1 or 0.
4087 NULL,                                                   // #278
4088 NULL,                                                   // #279
4089 NULL,                                                   // #280
4090 NULL,                                                   // #281
4091 NULL,                                                   // #282
4092 NULL,                                                   // #283
4093 NULL,                                                   // #284
4094 NULL,                                                   // #285
4095 NULL,                                                   // #286
4096 NULL,                                                   // #287
4097 NULL,                                                   // #288
4098 NULL,                                                   // #289
4099 NULL,                                                   // #290
4100 NULL,                                                   // #291
4101 NULL,                                                   // #292
4102 NULL,                                                   // #293
4103 NULL,                                                   // #294
4104 NULL,                                                   // #295
4105 NULL,                                                   // #296
4106 NULL,                                                   // #297
4107 NULL,                                                   // #298
4108 NULL,                                                   // #299
4109 // CSQC range #300-#399
4110 VM_CL_R_ClearScene,                             // #300 void() clearscene (EXT_CSQC)
4111 VM_CL_R_AddEntities,                    // #301 void(float mask) addentities (EXT_CSQC)
4112 VM_CL_R_AddEntity,                              // #302 void(entity ent) addentity (EXT_CSQC)
4113 VM_CL_R_SetView,                                // #303 float(float property, ...) setproperty (EXT_CSQC)
4114 VM_CL_R_RenderScene,                    // #304 void() renderscene (EXT_CSQC)
4115 VM_CL_R_AddDynamicLight,                // #305 void(vector org, float radius, vector lightcolours) adddynamiclight (EXT_CSQC)
4116 VM_CL_R_PolygonBegin,                   // #306 void(string texturename, float flag[, float is2d, float lines]) R_BeginPolygon
4117 VM_CL_R_PolygonVertex,                  // #307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
4118 VM_CL_R_PolygonEnd,                             // #308 void() R_EndPolygon
4119 NULL /* R_LoadWorldModel in menu VM, should stay unassigned in client*/, // #309
4120 VM_CL_unproject,                                // #310 vector (vector v) cs_unproject (EXT_CSQC)
4121 VM_CL_project,                                  // #311 vector (vector v) cs_project (EXT_CSQC)
4122 NULL,                                                   // #312
4123 NULL,                                                   // #313
4124 NULL,                                                   // #314
4125 VM_drawline,                                    // #315 void(float width, vector pos1, vector pos2, float flag) drawline (EXT_CSQC)
4126 VM_iscachedpic,                                 // #316 float(string name) iscachedpic (EXT_CSQC)
4127 VM_precache_pic,                                // #317 string(string name, float trywad) precache_pic (EXT_CSQC)
4128 VM_getimagesize,                                // #318 vector(string picname) draw_getimagesize (EXT_CSQC)
4129 VM_freepic,                                             // #319 void(string name) freepic (EXT_CSQC)
4130 VM_drawcharacter,                               // #320 float(vector position, float character, vector scale, vector rgb, float alpha, float flag) drawcharacter (EXT_CSQC)
4131 VM_drawstring,                                  // #321 float(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawstring (EXT_CSQC)
4132 VM_drawpic,                                             // #322 float(vector position, string pic, vector size, vector rgb, float alpha, float flag) drawpic (EXT_CSQC)
4133 VM_drawfill,                                    // #323 float(vector position, vector size, vector rgb, float alpha, float flag) drawfill (EXT_CSQC)
4134 VM_drawsetcliparea,                             // #324 void(float x, float y, float width, float height) drawsetcliparea
4135 VM_drawresetcliparea,                   // #325 void(void) drawresetcliparea
4136 VM_drawcolorcodedstring,                // #326 float drawcolorcodedstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) (EXT_CSQC)
4137 VM_stringwidth,                 // #327 // FIXME is this okay?
4138 VM_drawsubpic,                                  // #328 // FIXME is this okay?
4139 VM_drawrotpic,                                  // #329 // FIXME is this okay?
4140 VM_CL_getstatf,                                 // #330 float(float stnum) getstatf (EXT_CSQC)
4141 VM_CL_getstati,                                 // #331 float(float stnum) getstati (EXT_CSQC)
4142 VM_CL_getstats,                                 // #332 string(float firststnum) getstats (EXT_CSQC)
4143 VM_CL_setmodelindex,                    // #333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
4144 VM_CL_modelnameforindex,                // #334 string(float mdlindex) modelnameforindex (EXT_CSQC)
4145 VM_CL_particleeffectnum,                // #335 float(string effectname) particleeffectnum (EXT_CSQC)
4146 VM_CL_trailparticles,                   // #336 void(entity ent, float effectnum, vector start, vector end) trailparticles (EXT_CSQC)
4147 VM_CL_pointparticles,                   // #337 void(float effectnum, vector origin [, vector dir, float count]) pointparticles (EXT_CSQC)
4148 VM_centerprint,                                 // #338 void(string s, ...) centerprint (EXT_CSQC)
4149 VM_print,                                               // #339 void(string s, ...) print (EXT_CSQC, DP_SV_PRINT)
4150 VM_keynumtostring,                              // #340 string(float keynum) keynumtostring (EXT_CSQC)
4151 VM_stringtokeynum,                              // #341 float(string keyname) stringtokeynum (EXT_CSQC)
4152 VM_CL_getkeybind,                               // #342 string(float keynum) getkeybind (EXT_CSQC)
4153 VM_CL_setcursormode,                    // #343 void(float usecursor) setcursormode (EXT_CSQC)
4154 VM_CL_getmousepos,                              // #344 vector() getmousepos (EXT_CSQC)
4155 VM_CL_getinputstate,                    // #345 float(float framenum) getinputstate (EXT_CSQC)
4156 VM_CL_setsensitivityscale,              // #346 void(float sens) setsensitivityscale (EXT_CSQC)
4157 VM_CL_runplayerphysics,                 // #347 void() runstandardplayerphysics (EXT_CSQC)
4158 VM_CL_getplayerkey,                             // #348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
4159 VM_CL_isdemo,                                   // #349 float() isdemo (EXT_CSQC)
4160 VM_isserver,                                    // #350 float() isserver (EXT_CSQC)
4161 VM_CL_setlistener,                              // #351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
4162 VM_CL_registercmd,                              // #352 void(string cmdname) registercommand (EXT_CSQC)
4163 VM_wasfreed,                                    // #353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too)
4164 VM_CL_serverkey,                                // #354 string(string key) serverkey (EXT_CSQC)
4165 NULL,                                                   // #355
4166 NULL,                                                   // #356
4167 NULL,                                                   // #357
4168 NULL,                                                   // #358
4169 NULL,                                                   // #359
4170 VM_CL_ReadByte,                                 // #360 float() readbyte (EXT_CSQC)
4171 VM_CL_ReadChar,                                 // #361 float() readchar (EXT_CSQC)
4172 VM_CL_ReadShort,                                // #362 float() readshort (EXT_CSQC)
4173 VM_CL_ReadLong,                                 // #363 float() readlong (EXT_CSQC)
4174 VM_CL_ReadCoord,                                // #364 float() readcoord (EXT_CSQC)
4175 VM_CL_ReadAngle,                                // #365 float() readangle (EXT_CSQC)
4176 VM_CL_ReadString,                               // #366 string() readstring (EXT_CSQC)
4177 VM_CL_ReadFloat,                                // #367 float() readfloat (EXT_CSQC)
4178 NULL,                                           // #368
4179 NULL,                                                   // #369
4180 NULL,                                                   // #370
4181 NULL,                                                   // #371
4182 NULL,                                                   // #372
4183 NULL,                                                   // #373
4184 NULL,                                                   // #374
4185 NULL,                                                   // #375
4186 NULL,                                                   // #376
4187 NULL,                                                   // #377
4188 NULL,                                                   // #378
4189 NULL,                                                   // #379
4190 NULL,                                                   // #380
4191 NULL,                                                   // #381
4192 NULL,                                                   // #382
4193 NULL,                                                   // #383
4194 NULL,                                                   // #384
4195 NULL,                                                   // #385
4196 NULL,                                                   // #386
4197 NULL,                                                   // #387
4198 NULL,                                                   // #388
4199 NULL,                                                   // #389
4200 NULL,                                                   // #390
4201 NULL,                                                   // #391
4202 NULL,                                                   // #392
4203 NULL,                                                   // #393
4204 NULL,                                                   // #394
4205 NULL,                                                   // #395
4206 NULL,                                                   // #396
4207 NULL,                                                   // #397
4208 NULL,                                                   // #398
4209 NULL,                                                   // #399
4210 // LordHavoc's range #400-#499
4211 VM_CL_copyentity,                               // #400 void(entity from, entity to) copyentity (DP_QC_COPYENTITY)
4212 NULL,                                                   // #401 void(entity ent, float colors) setcolor (DP_QC_SETCOLOR)
4213 VM_findchain,                                   // #402 entity(.string fld, string match) findchain (DP_QC_FINDCHAIN)
4214 VM_findchainfloat,                              // #403 entity(.float fld, float match) findchainfloat (DP_QC_FINDCHAINFLOAT)
4215 VM_CL_effect,                                   // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
4216 VM_CL_te_blood,                                 // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
4217 VM_CL_te_bloodshower,                   // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
4218 VM_CL_te_explosionrgb,                  // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
4219 VM_CL_te_particlecube,                  // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
4220 VM_CL_te_particlerain,                  // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
4221 VM_CL_te_particlesnow,                  // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
4222 VM_CL_te_spark,                                 // #411 void(vector org, vector vel, float howmany) te_spark (DP_TE_SPARK)
4223 VM_CL_te_gunshotquad,                   // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
4224 VM_CL_te_spikequad,                             // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
4225 VM_CL_te_superspikequad,                // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1)
4226 VM_CL_te_explosionquad,                 // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1)
4227 VM_CL_te_smallflash,                    // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
4228 VM_CL_te_customflash,                   // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
4229 VM_CL_te_gunshot,                               // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
4230 VM_CL_te_spike,                                 // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
4231 VM_CL_te_superspike,                    // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
4232 VM_CL_te_explosion,                             // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
4233 VM_CL_te_tarexplosion,                  // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
4234 VM_CL_te_wizspike,                              // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
4235 VM_CL_te_knightspike,                   // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
4236 VM_CL_te_lavasplash,                    // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
4237 VM_CL_te_teleport,                              // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
4238 VM_CL_te_explosion2,                    // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
4239 VM_CL_te_lightning1,                    // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
4240 VM_CL_te_lightning2,                    // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
4241 VM_CL_te_lightning3,                    // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
4242 VM_CL_te_beam,                                  // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
4243 VM_vectorvectors,                               // #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS)
4244 VM_CL_te_plasmaburn,                    // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
4245 VM_getsurfacenumpoints,         // #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE)
4246 VM_getsurfacepoint,                     // #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE)
4247 VM_getsurfacenormal,                    // #436 vector(entity e, float s) getsurfacenormal (DP_QC_GETSURFACE)
4248 VM_getsurfacetexture,           // #437 string(entity e, float s) getsurfacetexture (DP_QC_GETSURFACE)
4249 VM_getsurfacenearpoint,         // #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE)
4250 VM_getsurfaceclippedpoint,      // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE)
4251 NULL,                                                   // #440 void(entity e, string s) clientcommand (KRIMZON_SV_PARSECLIENTCOMMAND)
4252 VM_tokenize,                                    // #441 float(string s) tokenize (KRIMZON_SV_PARSECLIENTCOMMAND)
4253 VM_argv,                                                // #442 string(float n) argv (KRIMZON_SV_PARSECLIENTCOMMAND)
4254 VM_CL_setattachment,                    // #443 void(entity e, entity tagentity, string tagname) setattachment (DP_GFX_QUAKE3MODELTAGS)
4255 VM_search_begin,                                // #444 float(string pattern, float caseinsensitive, float quiet) search_begin (DP_QC_FS_SEARCH)
4256 VM_search_end,                                  // #445 void(float handle) search_end (DP_QC_FS_SEARCH)
4257 VM_search_getsize,                              // #446 float(float handle) search_getsize (DP_QC_FS_SEARCH)
4258 VM_search_getfilename,                  // #447 string(float handle, float num) search_getfilename (DP_QC_FS_SEARCH)
4259 VM_cvar_string,                                 // #448 string(string s) cvar_string (DP_QC_CVAR_STRING)
4260 VM_findflags,                                   // #449 entity(entity start, .float fld, float match) findflags (DP_QC_FINDFLAGS)
4261 VM_findchainflags,                              // #450 entity(.float fld, float match) findchainflags (DP_QC_FINDCHAINFLAGS)
4262 VM_CL_gettagindex,                              // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
4263 VM_CL_gettaginfo,                               // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO)
4264 NULL,                                                   // #453 void(entity clent) dropclient (DP_SV_DROPCLIENT)
4265 NULL,                                                   // #454 entity() spawnclient (DP_SV_BOTCLIENT)
4266 NULL,                                                   // #455 float(entity clent) clienttype (DP_SV_BOTCLIENT)
4267 NULL,                                                   // #456 void(float to, string s) WriteUnterminatedString (DP_SV_WRITEUNTERMINATEDSTRING)
4268 VM_CL_te_flamejet,                              // #457 void(vector org, vector vel, float howmany) te_flamejet (DP_TE_FLAMEJET)
4269 NULL,                                                   // #458
4270 VM_ftoe,                                                // #459 entity(float num) entitybyindex (DP_QC_EDICT_NUM)
4271 VM_buf_create,                                  // #460 float() buf_create (DP_QC_STRINGBUFFERS)
4272 VM_buf_del,                                             // #461 void(float bufhandle) buf_del (DP_QC_STRINGBUFFERS)
4273 VM_buf_getsize,                                 // #462 float(float bufhandle) buf_getsize (DP_QC_STRINGBUFFERS)
4274 VM_buf_copy,                                    // #463 void(float bufhandle_from, float bufhandle_to) buf_copy (DP_QC_STRINGBUFFERS)
4275 VM_buf_sort,                                    // #464 void(float bufhandle, float sortpower, float backward) buf_sort (DP_QC_STRINGBUFFERS)
4276 VM_buf_implode,                                 // #465 string(float bufhandle, string glue) buf_implode (DP_QC_STRINGBUFFERS)
4277 VM_bufstr_get,                                  // #466 string(float bufhandle, float string_index) bufstr_get (DP_QC_STRINGBUFFERS)
4278 VM_bufstr_set,                                  // #467 void(float bufhandle, float string_index, string str) bufstr_set (DP_QC_STRINGBUFFERS)
4279 VM_bufstr_add,                                  // #468 float(float bufhandle, string str, float order) bufstr_add (DP_QC_STRINGBUFFERS)
4280 VM_bufstr_free,                                 // #469 void(float bufhandle, float string_index) bufstr_free (DP_QC_STRINGBUFFERS)
4281 NULL,                                                   // #470 void(float index, float type, .void field) SV_AddStat (EXT_CSQC)
4282 VM_asin,                                                // #471 float(float s) VM_asin (DP_QC_ASINACOSATANATAN2TAN)
4283 VM_acos,                                                // #472 float(float c) VM_acos (DP_QC_ASINACOSATANATAN2TAN)
4284 VM_atan,                                                // #473 float(float t) VM_atan (DP_QC_ASINACOSATANATAN2TAN)
4285 VM_atan2,                                               // #474 float(float c, float s) VM_atan2 (DP_QC_ASINACOSATANATAN2TAN)
4286 VM_tan,                                                 // #475 float(float a) VM_tan (DP_QC_ASINACOSATANATAN2TAN)
4287 VM_strlennocol,                                 // #476 float(string s) : DRESK - String Length (not counting color codes) (DP_QC_STRINGCOLORFUNCTIONS)
4288 VM_strdecolorize,                               // #477 string(string s) : DRESK - Decolorized String (DP_QC_STRINGCOLORFUNCTIONS)
4289 VM_strftime,                                    // #478 string(float uselocaltime, string format, ...) (DP_QC_STRFTIME)
4290 VM_tokenizebyseparator,                 // #479 float(string s) tokenizebyseparator (DP_QC_TOKENIZEBYSEPARATOR)
4291 VM_strtolower,                                  // #480 string(string s) VM_strtolower (DP_QC_STRING_CASE_FUNCTIONS)
4292 VM_strtoupper,                                  // #481 string(string s) VM_strtoupper (DP_QC_STRING_CASE_FUNCTIONS)
4293 VM_cvar_defstring,                              // #482 string(string s) cvar_defstring (DP_QC_CVAR_DEFSTRING)
4294 VM_CL_pointsound,                               // #483 void(vector origin, string sample, float volume, float attenuation) pointsound (DP_SV_POINTSOUND)
4295 VM_strreplace,                                  // #484 string(string search, string replace, string subject) strreplace (DP_QC_STRREPLACE)
4296 VM_strireplace,                                 // #485 string(string search, string replace, string subject) strireplace (DP_QC_STRREPLACE)
4297 VM_getsurfacepointattribute,// #486 vector(entity e, float s, float n, float a) getsurfacepointattribute
4298 VM_gecko_create,                                        // #487 float gecko_create( string name )
4299 VM_gecko_destroy,                                       // #488 void gecko_destroy( string name )
4300 VM_gecko_navigate,                              // #489 void gecko_navigate( string name, string URI )
4301 VM_gecko_keyevent,                              // #490 float gecko_keyevent( string name, float key, float eventtype )
4302 VM_gecko_movemouse,                             // #491 void gecko_mousemove( string name, float x, float y )
4303 VM_gecko_resize,                                        // #492 void gecko_resize( string name, float w, float h )
4304 VM_gecko_get_texture_extent,    // #493 vector gecko_get_texture_extent( string name )
4305 VM_crc16,                                               // #494 float(float caseinsensitive, string s, ...) crc16 = #494 (DP_QC_CRC16)
4306 VM_cvar_type,                                   // #495 float(string name) cvar_type = #495; (DP_QC_CVAR_TYPE)
4307 VM_numentityfields,                             // #496 float() numentityfields = #496; (QP_QC_ENTITYDATA)
4308 VM_entityfieldname,                             // #497 string(float fieldnum) entityfieldname = #497; (DP_QC_ENTITYDATA)
4309 VM_entityfieldtype,                             // #498 float(float fieldnum) entityfieldtype = #498; (DP_QC_ENTITYDATA)
4310 VM_getentityfieldstring,                // #499 string(float fieldnum, entity ent) getentityfieldstring = #499; (DP_QC_ENTITYDATA)
4311 VM_putentityfieldstring,                // #500 float(float fieldnum, entity ent, string s) putentityfieldstring = #500; (DP_QC_ENTITYDATA)
4312 VM_CL_ReadPicture,                              // #501 string() ReadPicture = #501;
4313 NULL,                                                   // #502
4314 VM_whichpack,                                   // #503 string(string) whichpack = #503;
4315 NULL,                                                   // #504
4316 NULL,                                                   // #505
4317 NULL,                                                   // #506
4318 NULL,                                                   // #507
4319 NULL,                                                   // #508
4320 NULL,                                                   // #509
4321 VM_uri_escape,                                  // #510 string(string in) uri_escape = #510;
4322 VM_uri_unescape,                                // #511 string(string in) uri_unescape = #511;
4323 VM_etof,                                        // #512 float(entity ent) num_for_edict = #512 (DP_QC_NUM_FOR_EDICT)
4324 VM_uri_get,                                             // #513 float(string uril, float id) uri_get = #512; (DP_QC_URI_GET)
4325 VM_tokenize_console,                                    // #514 float(string str) tokenize_console = #514; (DP_QC_TOKENIZE_CONSOLE)
4326 VM_argv_start_index,                                    // #515 float(float idx) argv_start_index = #515; (DP_QC_TOKENIZE_CONSOLE)
4327 VM_argv_end_index,                                              // #516 float(float idx) argv_end_index = #516; (DP_QC_TOKENIZE_CONSOLE)
4328 VM_buf_cvarlist,                                                // #517 void(float buf, string prefix, string antiprefix) buf_cvarlist = #517; (DP_QC_STRINGBUFFERS_CVARLIST)
4329 VM_cvar_description,                                    // #518 float(string name) cvar_description = #518; (DP_QC_CVAR_DESCRIPTION)
4330 VM_gettime,                                             // #519 float(float timer) gettime = #519; (DP_QC_GETTIME)
4331 VM_keynumtostring,                              // #520 string keynumtostring(float keynum)
4332 VM_findkeysforcommand,                  // #521 string findkeysforcommand(string command)
4333 VM_CL_InitParticleSpawner,              // #522 void(float max_themes) initparticlespawner (DP_CSQC_SPAWNPARTICLE)
4334 VM_CL_ResetParticle,                    // #523 void() resetparticle (DP_CSQC_SPAWNPARTICLE)
4335 VM_CL_ParticleTheme,                    // #524 void(float theme) particletheme (DP_CSQC_SPAWNPARTICLE)
4336 VM_CL_ParticleThemeSave,                // #525 void() particlethemesave, void(float theme) particlethemeupdate (DP_CSQC_SPAWNPARTICLE)
4337 VM_CL_ParticleThemeFree,                // #526 void() particlethemefree (DP_CSQC_SPAWNPARTICLE)
4338 VM_CL_SpawnParticle,                    // #527 float(vector org, vector vel, [float theme]) particle (DP_CSQC_SPAWNPARTICLE)
4339 VM_CL_SpawnParticleDelayed,             // #528 float(vector org, vector vel, float delay, float collisiondelay, [float theme]) delayedparticle (DP_CSQC_SPAWNPARTICLE)
4340 VM_loadfromdata,                                // #529
4341 VM_loadfromfile,                                // #530
4342 NULL,                                                   // #531
4343 VM_log,                                                 // #532
4344 NULL,                                                   // #533
4345 NULL,                                                   // #534
4346 NULL,                                                   // #535
4347 NULL,                                                   // #536
4348 NULL,                                                   // #537
4349 NULL,                                                   // #538
4350 NULL,                                                   // #539
4351 NULL,                                                   // #540
4352 NULL,                                                   // #541
4353 NULL,                                                   // #542
4354 NULL,                                                   // #543
4355 NULL,                                                   // #544
4356 NULL,                                                   // #545
4357 NULL,                                                   // #546
4358 NULL,                                                   // #547
4359 NULL,                                                   // #548
4360 NULL,                                                   // #549
4361 NULL,                                                   // #550
4362 NULL,                                                   // #551
4363 NULL,                                                   // #552
4364 NULL,                                                   // #553
4365 NULL,                                                   // #554
4366 NULL,                                                   // #555
4367 NULL,                                                   // #556
4368 NULL,                                                   // #557
4369 NULL,                                                   // #558
4370 NULL,                                                   // #559
4371 NULL,                                                   // #560
4372 NULL,                                                   // #561
4373 NULL,                                                   // #562
4374 NULL,                                                   // #563
4375 NULL,                                                   // #564
4376 NULL,                                                   // #565
4377 NULL,                                                   // #566
4378 NULL,                                                   // #567
4379 NULL,                                                   // #568
4380 NULL,                                                   // #569
4381 NULL,                                                   // #570
4382 NULL,                                                   // #571
4383 NULL,                                                   // #572
4384 NULL,                                                   // #573
4385 NULL,                                                   // #574
4386 NULL,                                                   // #575
4387 NULL,                                                   // #576
4388 NULL,                                                   // #577
4389 NULL,                                                   // #578
4390 NULL,                                                   // #579
4391 NULL,                                                   // #580
4392 NULL,                                                   // #581
4393 NULL,                                                   // #582
4394 NULL,                                                   // #583
4395 NULL,                                                   // #584
4396 NULL,                                                   // #585
4397 NULL,                                                   // #586
4398 NULL,                                                   // #587
4399 NULL,                                                   // #588
4400 NULL,                                                   // #589
4401 NULL,                                                   // #590
4402 NULL,                                                   // #591
4403 NULL,                                                   // #592
4404 NULL,                                                   // #593
4405 NULL,                                                   // #594
4406 NULL,                                                   // #595
4407 NULL,                                                   // #596
4408 NULL,                                                   // #597
4409 NULL,                                                   // #598
4410 NULL,                                                   // #599
4411 NULL,                                                   // #600
4412 NULL,                                                   // #601
4413 NULL,                                                   // #602
4414 NULL,                                                   // #603
4415 NULL,                                                   // #604
4416 VM_callfunction,                                // #605
4417 VM_writetofile,                                 // #606
4418 VM_isfunction,                                  // #607
4419 NULL,                                                   // #608
4420 NULL,                                                   // #609
4421 NULL,                                                   // #610
4422 NULL,                                                   // #611
4423 NULL,                                                   // #612
4424 VM_parseentitydata,                             // #613
4425 NULL,                                                   // #614
4426 NULL,                                                   // #615
4427 NULL,                                                   // #616
4428 NULL,                                                   // #617
4429 NULL,                                                   // #618
4430 NULL,                                                   // #619
4431 NULL,                                                   // #620
4432 NULL,                                                   // #621
4433 NULL,                                                   // #622
4434 NULL,                                                   // #623
4435 VM_CL_getextresponse,                   // #624 string getextresponse(void)
4436 NULL,                                                   // #625
4437 NULL,                                                   // #626
4438 VM_sprintf,                     // #627 string sprintf(string format, ...)
4439 VM_getsurfacenumtriangles,              // #628 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACETRIANGLE)
4440 VM_getsurfacetriangle,                  // #629 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACETRIANGLE)
4441 VM_CL_traceboxbox,                              // #630
4442 NULL,                                                   // #631
4443 };
4444
4445 const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t);
4446
4447 void VM_Polygons_Reset(void)
4448 {
4449         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
4450
4451         // TODO: replace vm_polygons stuff with a more general debugging polygon system, and make vm_polygons functions use that system
4452         if(polys->initialized)
4453         {
4454                 Mem_FreePool(&polys->pool);
4455                 polys->initialized = false;
4456         }
4457 }
4458
4459 void VM_CL_Cmd_Init(void)
4460 {
4461         VM_Cmd_Init();
4462         VM_Polygons_Reset();
4463 }
4464
4465 void VM_CL_Cmd_Reset(void)
4466 {
4467         World_End(&cl.world);
4468         VM_Cmd_Reset();
4469         VM_Polygons_Reset();
4470 }
4471
4472