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