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