f328ee119d1089f13982a9e6957147a722f6647e
[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 //#349 float() isdemo (EXT_CSQC)
1288 static void VM_CL_isdemo (void)
1289 {
1290         VM_SAFEPARMCOUNT(0, VM_CL_isdemo);
1291         PRVM_G_FLOAT(OFS_RETURN) = cls.demoplayback;
1292 }
1293
1294 //#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
1295 static void VM_CL_setlistener (void)
1296 {
1297         VM_SAFEPARMCOUNT(4, VM_CL_setlistener);
1298         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));
1299         cl.csqc_usecsqclistener = true; //use csqc listener at this frame
1300 }
1301
1302 //#352 void(string cmdname) registercommand (EXT_CSQC)
1303 static void VM_CL_registercmd (void)
1304 {
1305         char *t;
1306         VM_SAFEPARMCOUNT(1, VM_CL_registercmd);
1307         if(!Cmd_Exists(PRVM_G_STRING(OFS_PARM0)))
1308         {
1309                 size_t alloclen;
1310
1311                 alloclen = strlen(PRVM_G_STRING(OFS_PARM0)) + 1;
1312                 t = (char *)Z_Malloc(alloclen);
1313                 memcpy(t, PRVM_G_STRING(OFS_PARM0), alloclen);
1314                 Cmd_AddCommand(t, NULL, "console command created by QuakeC");
1315         }
1316         else
1317                 Cmd_AddCommand(PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
1318
1319 }
1320
1321 //#360 float() readbyte (EXT_CSQC)
1322 static void VM_CL_ReadByte (void)
1323 {
1324         VM_SAFEPARMCOUNT(0, VM_CL_ReadByte);
1325         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadByte();
1326 }
1327
1328 //#361 float() readchar (EXT_CSQC)
1329 static void VM_CL_ReadChar (void)
1330 {
1331         VM_SAFEPARMCOUNT(0, VM_CL_ReadChar);
1332         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadChar();
1333 }
1334
1335 //#362 float() readshort (EXT_CSQC)
1336 static void VM_CL_ReadShort (void)
1337 {
1338         VM_SAFEPARMCOUNT(0, VM_CL_ReadShort);
1339         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadShort();
1340 }
1341
1342 //#363 float() readlong (EXT_CSQC)
1343 static void VM_CL_ReadLong (void)
1344 {
1345         VM_SAFEPARMCOUNT(0, VM_CL_ReadLong);
1346         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadLong();
1347 }
1348
1349 //#364 float() readcoord (EXT_CSQC)
1350 static void VM_CL_ReadCoord (void)
1351 {
1352         VM_SAFEPARMCOUNT(0, VM_CL_ReadCoord);
1353         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadCoord(cls.protocol);
1354 }
1355
1356 //#365 float() readangle (EXT_CSQC)
1357 static void VM_CL_ReadAngle (void)
1358 {
1359         VM_SAFEPARMCOUNT(0, VM_CL_ReadAngle);
1360         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadAngle(cls.protocol);
1361 }
1362
1363 //#366 string() readstring (EXT_CSQC)
1364 static void VM_CL_ReadString (void)
1365 {
1366         VM_SAFEPARMCOUNT(0, VM_CL_ReadString);
1367         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(MSG_ReadString());
1368 }
1369
1370 //#367 float() readfloat (EXT_CSQC)
1371 static void VM_CL_ReadFloat (void)
1372 {
1373         VM_SAFEPARMCOUNT(0, VM_CL_ReadFloat);
1374         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat();
1375 }
1376
1377 //#501 string() readpicture (DP_CSQC_READWRITEPICTURE)
1378 extern cvar_t cl_readpicture_force;
1379 static void VM_CL_ReadPicture (void)
1380 {
1381         const char *name;
1382         unsigned char *data;
1383         unsigned char *buf;
1384         int size;
1385         int i;
1386         cachepic_t *pic;
1387
1388         VM_SAFEPARMCOUNT(0, VM_CL_ReadPicture);
1389
1390         name = MSG_ReadString();
1391         size = MSG_ReadShort();
1392
1393         // check if a texture of that name exists
1394         // if yes, it is used and the data is discarded
1395         // if not, the (low quality) data is used to build a new texture, whose name will get returned
1396
1397         pic = Draw_CachePic_Flags (name, CACHEPICFLAG_NOTPERSISTENT);
1398
1399         if(size)
1400         {
1401                 if(pic->tex == r_texture_notexture)
1402                         pic->tex = NULL; // don't overwrite the notexture by Draw_NewPic
1403                 if(pic->tex && !cl_readpicture_force.integer)
1404                 {
1405                         // texture found and loaded
1406                         // skip over the jpeg as we don't need it
1407                         for(i = 0; i < size; ++i)
1408                                 MSG_ReadByte();
1409                 }
1410                 else
1411                 {
1412                         // texture not found
1413                         // use the attached jpeg as texture
1414                         buf = (unsigned char *) Mem_Alloc(tempmempool, size);
1415                         MSG_ReadBytes(size, buf);
1416                         data = JPEG_LoadImage_BGRA(buf, size);
1417                         Mem_Free(buf);
1418                         Draw_NewPic(name, image_width, image_height, false, data);
1419                         Mem_Free(data);
1420                 }
1421         }
1422
1423         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(name);
1424 }
1425
1426 //////////////////////////////////////////////////////////
1427
1428 static void VM_CL_makestatic (void)
1429 {
1430         prvm_edict_t *ent;
1431
1432         VM_SAFEPARMCOUNT(1, VM_CL_makestatic);
1433
1434         ent = PRVM_G_EDICT(OFS_PARM0);
1435         if (ent == prog->edicts)
1436         {
1437                 VM_Warning("makestatic: can not modify world entity\n");
1438                 return;
1439         }
1440         if (ent->priv.server->free)
1441         {
1442                 VM_Warning("makestatic: can not modify free entity\n");
1443                 return;
1444         }
1445
1446         if (cl.num_static_entities < cl.max_static_entities)
1447         {
1448                 int renderflags;
1449                 prvm_eval_t *val;
1450                 entity_t *staticent = &cl.static_entities[cl.num_static_entities++];
1451
1452                 // copy it to the current state
1453                 memset(staticent, 0, sizeof(*staticent));
1454                 staticent->render.model = CL_GetModelByIndex((int)ent->fields.client->modelindex);
1455                 staticent->render.framegroupblend[0].frame = (int)ent->fields.client->frame;
1456                 staticent->render.framegroupblend[0].lerp = 1;
1457                 // make torchs play out of sync
1458                 staticent->render.framegroupblend[0].start = lhrandom(-10, -1);
1459                 staticent->render.skinnum = (int)ent->fields.client->skin;
1460                 staticent->render.effects = (int)ent->fields.client->effects;
1461                 staticent->render.alpha = 1;
1462                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)) && val->_float) staticent->render.alpha = val->_float;
1463                 staticent->render.scale = 1;
1464                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)) && val->_float) staticent->render.scale = val->_float;
1465                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod)) && VectorLength2(val->vector)) VectorCopy(val->vector, staticent->render.colormod);
1466                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glowmod)) && VectorLength2(val->vector)) VectorCopy(val->vector, staticent->render.glowmod);
1467                 if (!VectorLength2(staticent->render.colormod))
1468                         VectorSet(staticent->render.colormod, 1, 1, 1);
1469                 if (!VectorLength2(staticent->render.glowmod))
1470                         VectorSet(staticent->render.glowmod, 1, 1, 1);
1471
1472                 renderflags = 0;
1473                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderflags)) && val->_float) renderflags = (int)val->_float;
1474                 if (renderflags & RF_USEAXIS)
1475                 {
1476                         vec3_t left;
1477                         VectorNegate(prog->globals.client->v_right, left);
1478                         Matrix4x4_FromVectors(&staticent->render.matrix, prog->globals.client->v_forward, left, prog->globals.client->v_up, ent->fields.client->origin);
1479                         Matrix4x4_Scale(&staticent->render.matrix, staticent->render.scale, 1);
1480                 }
1481                 else
1482                         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);
1483
1484                 // either fullbright or lit
1485                 if(!r_fullbright.integer)
1486                 {
1487                         if (!(staticent->render.effects & EF_FULLBRIGHT))
1488                                 staticent->render.flags |= RENDER_LIGHT;
1489                         else if(r_equalize_entities_fullbright.integer)
1490                                 staticent->render.flags |= RENDER_LIGHT | RENDER_EQUALIZE;
1491                 }
1492                 // turn off shadows from transparent objects
1493                 if (!(staticent->render.effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST)) && (staticent->render.alpha >= 1))
1494                         staticent->render.flags |= RENDER_SHADOW;
1495                 if (staticent->render.effects & EF_NODEPTHTEST)
1496                         staticent->render.flags |= RENDER_NODEPTHTEST;
1497                 if (staticent->render.effects & EF_ADDITIVE)
1498                         staticent->render.flags |= RENDER_ADDITIVE;
1499                 if (staticent->render.effects & EF_DOUBLESIDED)
1500                         staticent->render.flags |= RENDER_DOUBLESIDED;
1501
1502                 staticent->render.allowdecals = true;
1503                 CL_UpdateRenderEntity(&staticent->render);
1504         }
1505         else
1506                 Con_Printf("Too many static entities");
1507
1508 // throw the entity away now
1509         PRVM_ED_Free (ent);
1510 }
1511
1512 //=================================================================//
1513
1514 /*
1515 =================
1516 VM_CL_copyentity
1517
1518 copies data from one entity to another
1519
1520 copyentity(src, dst)
1521 =================
1522 */
1523 static void VM_CL_copyentity (void)
1524 {
1525         prvm_edict_t *in, *out;
1526         VM_SAFEPARMCOUNT(2, VM_CL_copyentity);
1527         in = PRVM_G_EDICT(OFS_PARM0);
1528         if (in == prog->edicts)
1529         {
1530                 VM_Warning("copyentity: can not read world entity\n");
1531                 return;
1532         }
1533         if (in->priv.server->free)
1534         {
1535                 VM_Warning("copyentity: can not read free entity\n");
1536                 return;
1537         }
1538         out = PRVM_G_EDICT(OFS_PARM1);
1539         if (out == prog->edicts)
1540         {
1541                 VM_Warning("copyentity: can not modify world entity\n");
1542                 return;
1543         }
1544         if (out->priv.server->free)
1545         {
1546                 VM_Warning("copyentity: can not modify free entity\n");
1547                 return;
1548         }
1549         memcpy(out->fields.vp, in->fields.vp, prog->progs->entityfields * 4);
1550         CL_LinkEdict(out);
1551 }
1552
1553 //=================================================================//
1554
1555 // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
1556 static void VM_CL_effect (void)
1557 {
1558         VM_SAFEPARMCOUNT(5, VM_CL_effect);
1559         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));
1560 }
1561
1562 // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
1563 static void VM_CL_te_blood (void)
1564 {
1565         float   *pos;
1566         vec3_t  pos2;
1567         VM_SAFEPARMCOUNT(3, VM_CL_te_blood);
1568         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
1569                 return;
1570         pos = PRVM_G_VECTOR(OFS_PARM0);
1571         CL_FindNonSolidLocation(pos, pos2, 4);
1572         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1573 }
1574
1575 // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
1576 static void VM_CL_te_bloodshower (void)
1577 {
1578         vec_t speed;
1579         vec3_t vel1, vel2;
1580         VM_SAFEPARMCOUNT(4, VM_CL_te_bloodshower);
1581         if (PRVM_G_FLOAT(OFS_PARM3) < 1)
1582                 return;
1583         speed = PRVM_G_FLOAT(OFS_PARM2);
1584         vel1[0] = -speed;
1585         vel1[1] = -speed;
1586         vel1[2] = -speed;
1587         vel2[0] = speed;
1588         vel2[1] = speed;
1589         vel2[2] = speed;
1590         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM3), PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), vel1, vel2, NULL, 0);
1591 }
1592
1593 // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
1594 static void VM_CL_te_explosionrgb (void)
1595 {
1596         float           *pos;
1597         vec3_t          pos2;
1598         matrix4x4_t     tempmatrix;
1599         VM_SAFEPARMCOUNT(2, VM_CL_te_explosionrgb);
1600         pos = PRVM_G_VECTOR(OFS_PARM0);
1601         CL_FindNonSolidLocation(pos, pos2, 10);
1602         CL_ParticleExplosion(pos2);
1603         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1604         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);
1605 }
1606
1607 // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
1608 static void VM_CL_te_particlecube (void)
1609 {
1610         VM_SAFEPARMCOUNT(7, VM_CL_te_particlecube);
1611         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));
1612 }
1613
1614 // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
1615 static void VM_CL_te_particlerain (void)
1616 {
1617         VM_SAFEPARMCOUNT(5, VM_CL_te_particlerain);
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), 0);
1619 }
1620
1621 // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
1622 static void VM_CL_te_particlesnow (void)
1623 {
1624         VM_SAFEPARMCOUNT(5, VM_CL_te_particlesnow);
1625         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);
1626 }
1627
1628 // #411 void(vector org, vector vel, float howmany) te_spark
1629 static void VM_CL_te_spark (void)
1630 {
1631         float           *pos;
1632         vec3_t          pos2;
1633         VM_SAFEPARMCOUNT(3, VM_CL_te_spark);
1634
1635         pos = PRVM_G_VECTOR(OFS_PARM0);
1636         CL_FindNonSolidLocation(pos, pos2, 4);
1637         CL_ParticleEffect(EFFECT_TE_SPARK, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1638 }
1639
1640 extern cvar_t cl_sound_ric_gunshot;
1641 // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
1642 static void VM_CL_te_gunshotquad (void)
1643 {
1644         float           *pos;
1645         vec3_t          pos2;
1646         int                     rnd;
1647         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshotquad);
1648
1649         pos = PRVM_G_VECTOR(OFS_PARM0);
1650         CL_FindNonSolidLocation(pos, pos2, 4);
1651         CL_ParticleEffect(EFFECT_TE_GUNSHOTQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1652         if(cl_sound_ric_gunshot.integer >= 2)
1653         {
1654                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1655                 else
1656                 {
1657                         rnd = rand() & 3;
1658                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1659                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1660                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1661                 }
1662         }
1663 }
1664
1665 // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
1666 static void VM_CL_te_spikequad (void)
1667 {
1668         float           *pos;
1669         vec3_t          pos2;
1670         int                     rnd;
1671         VM_SAFEPARMCOUNT(1, VM_CL_te_spikequad);
1672
1673         pos = PRVM_G_VECTOR(OFS_PARM0);
1674         CL_FindNonSolidLocation(pos, pos2, 4);
1675         CL_ParticleEffect(EFFECT_TE_SPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1676         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1677         else
1678         {
1679                 rnd = rand() & 3;
1680                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1681                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1682                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1683         }
1684 }
1685
1686 // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1)
1687 static void VM_CL_te_superspikequad (void)
1688 {
1689         float           *pos;
1690         vec3_t          pos2;
1691         int                     rnd;
1692         VM_SAFEPARMCOUNT(1, VM_CL_te_superspikequad);
1693
1694         pos = PRVM_G_VECTOR(OFS_PARM0);
1695         CL_FindNonSolidLocation(pos, pos2, 4);
1696         CL_ParticleEffect(EFFECT_TE_SUPERSPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1697         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
1698         else
1699         {
1700                 rnd = rand() & 3;
1701                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1702                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1703                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1704         }
1705 }
1706
1707 // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1)
1708 static void VM_CL_te_explosionquad (void)
1709 {
1710         float           *pos;
1711         vec3_t          pos2;
1712         VM_SAFEPARMCOUNT(1, VM_CL_te_explosionquad);
1713
1714         pos = PRVM_G_VECTOR(OFS_PARM0);
1715         CL_FindNonSolidLocation(pos, pos2, 10);
1716         CL_ParticleEffect(EFFECT_TE_EXPLOSIONQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1717         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1718 }
1719
1720 // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
1721 static void VM_CL_te_smallflash (void)
1722 {
1723         float           *pos;
1724         vec3_t          pos2;
1725         VM_SAFEPARMCOUNT(1, VM_CL_te_smallflash);
1726
1727         pos = PRVM_G_VECTOR(OFS_PARM0);
1728         CL_FindNonSolidLocation(pos, pos2, 10);
1729         CL_ParticleEffect(EFFECT_TE_SMALLFLASH, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1730 }
1731
1732 // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
1733 static void VM_CL_te_customflash (void)
1734 {
1735         float           *pos;
1736         vec3_t          pos2;
1737         matrix4x4_t     tempmatrix;
1738         VM_SAFEPARMCOUNT(4, VM_CL_te_customflash);
1739
1740         pos = PRVM_G_VECTOR(OFS_PARM0);
1741         CL_FindNonSolidLocation(pos, pos2, 4);
1742         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1743         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);
1744 }
1745
1746 // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
1747 static void VM_CL_te_gunshot (void)
1748 {
1749         float           *pos;
1750         vec3_t          pos2;
1751         int                     rnd;
1752         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshot);
1753
1754         pos = PRVM_G_VECTOR(OFS_PARM0);
1755         CL_FindNonSolidLocation(pos, pos2, 4);
1756         CL_ParticleEffect(EFFECT_TE_GUNSHOT, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1757         if(cl_sound_ric_gunshot.integer == 1 || cl_sound_ric_gunshot.integer == 3)
1758         {
1759                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1760                 else
1761                 {
1762                         rnd = rand() & 3;
1763                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1764                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1765                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1766                 }
1767         }
1768 }
1769
1770 // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
1771 static void VM_CL_te_spike (void)
1772 {
1773         float           *pos;
1774         vec3_t          pos2;
1775         int                     rnd;
1776         VM_SAFEPARMCOUNT(1, VM_CL_te_spike);
1777
1778         pos = PRVM_G_VECTOR(OFS_PARM0);
1779         CL_FindNonSolidLocation(pos, pos2, 4);
1780         CL_ParticleEffect(EFFECT_TE_SPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1781         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1782         else
1783         {
1784                 rnd = rand() & 3;
1785                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1786                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1787                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1788         }
1789 }
1790
1791 // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
1792 static void VM_CL_te_superspike (void)
1793 {
1794         float           *pos;
1795         vec3_t          pos2;
1796         int                     rnd;
1797         VM_SAFEPARMCOUNT(1, VM_CL_te_superspike);
1798
1799         pos = PRVM_G_VECTOR(OFS_PARM0);
1800         CL_FindNonSolidLocation(pos, pos2, 4);
1801         CL_ParticleEffect(EFFECT_TE_SUPERSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1802         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1803         else
1804         {
1805                 rnd = rand() & 3;
1806                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1807                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1808                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1809         }
1810 }
1811
1812 // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
1813 static void VM_CL_te_explosion (void)
1814 {
1815         float           *pos;
1816         vec3_t          pos2;
1817         VM_SAFEPARMCOUNT(1, VM_CL_te_explosion);
1818
1819         pos = PRVM_G_VECTOR(OFS_PARM0);
1820         CL_FindNonSolidLocation(pos, pos2, 10);
1821         CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1822         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1823 }
1824
1825 // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
1826 static void VM_CL_te_tarexplosion (void)
1827 {
1828         float           *pos;
1829         vec3_t          pos2;
1830         VM_SAFEPARMCOUNT(1, VM_CL_te_tarexplosion);
1831
1832         pos = PRVM_G_VECTOR(OFS_PARM0);
1833         CL_FindNonSolidLocation(pos, pos2, 10);
1834         CL_ParticleEffect(EFFECT_TE_TAREXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1835         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1836 }
1837
1838 // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
1839 static void VM_CL_te_wizspike (void)
1840 {
1841         float           *pos;
1842         vec3_t          pos2;
1843         VM_SAFEPARMCOUNT(1, VM_CL_te_wizspike);
1844
1845         pos = PRVM_G_VECTOR(OFS_PARM0);
1846         CL_FindNonSolidLocation(pos, pos2, 4);
1847         CL_ParticleEffect(EFFECT_TE_WIZSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1848         S_StartSound(-1, 0, cl.sfx_wizhit, pos2, 1, 1);
1849 }
1850
1851 // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
1852 static void VM_CL_te_knightspike (void)
1853 {
1854         float           *pos;
1855         vec3_t          pos2;
1856         VM_SAFEPARMCOUNT(1, VM_CL_te_knightspike);
1857
1858         pos = PRVM_G_VECTOR(OFS_PARM0);
1859         CL_FindNonSolidLocation(pos, pos2, 4);
1860         CL_ParticleEffect(EFFECT_TE_KNIGHTSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1861         S_StartSound(-1, 0, cl.sfx_knighthit, pos2, 1, 1);
1862 }
1863
1864 // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
1865 static void VM_CL_te_lavasplash (void)
1866 {
1867         VM_SAFEPARMCOUNT(1, VM_CL_te_lavasplash);
1868         CL_ParticleEffect(EFFECT_TE_LAVASPLASH, 1, PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM0), vec3_origin, vec3_origin, NULL, 0);
1869 }
1870
1871 // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
1872 static void VM_CL_te_teleport (void)
1873 {
1874         VM_SAFEPARMCOUNT(1, VM_CL_te_teleport);
1875         CL_ParticleEffect(EFFECT_TE_TELEPORT, 1, PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM0), vec3_origin, vec3_origin, NULL, 0);
1876 }
1877
1878 // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
1879 static void VM_CL_te_explosion2 (void)
1880 {
1881         float           *pos;
1882         vec3_t          pos2, color;
1883         matrix4x4_t     tempmatrix;
1884         int                     colorStart, colorLength;
1885         unsigned char           *tempcolor;
1886         VM_SAFEPARMCOUNT(3, VM_CL_te_explosion2);
1887
1888         pos = PRVM_G_VECTOR(OFS_PARM0);
1889         colorStart = (int)PRVM_G_FLOAT(OFS_PARM1);
1890         colorLength = (int)PRVM_G_FLOAT(OFS_PARM2);
1891         CL_FindNonSolidLocation(pos, pos2, 10);
1892         CL_ParticleExplosion2(pos2, colorStart, colorLength);
1893         tempcolor = palette_rgb[(rand()%colorLength) + colorStart];
1894         color[0] = tempcolor[0] * (2.0f / 255.0f);
1895         color[1] = tempcolor[1] * (2.0f / 255.0f);
1896         color[2] = tempcolor[2] * (2.0f / 255.0f);
1897         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1898         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);
1899         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1900 }
1901
1902
1903 // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
1904 static void VM_CL_te_lightning1 (void)
1905 {
1906         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning1);
1907         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt, true);
1908 }
1909
1910 // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
1911 static void VM_CL_te_lightning2 (void)
1912 {
1913         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning2);
1914         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt2, true);
1915 }
1916
1917 // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
1918 static void VM_CL_te_lightning3 (void)
1919 {
1920         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning3);
1921         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt3, false);
1922 }
1923
1924 // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
1925 static void VM_CL_te_beam (void)
1926 {
1927         VM_SAFEPARMCOUNT(3, VM_CL_te_beam);
1928         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_beam, false);
1929 }
1930
1931 // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
1932 static void VM_CL_te_plasmaburn (void)
1933 {
1934         float           *pos;
1935         vec3_t          pos2;
1936         VM_SAFEPARMCOUNT(1, VM_CL_te_plasmaburn);
1937
1938         pos = PRVM_G_VECTOR(OFS_PARM0);
1939         CL_FindNonSolidLocation(pos, pos2, 4);
1940         CL_ParticleEffect(EFFECT_TE_PLASMABURN, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1941 }
1942
1943 // #457 void(vector org, vector velocity, float howmany) te_flamejet (DP_TE_FLAMEJET)
1944 static void VM_CL_te_flamejet (void)
1945 {
1946         float *pos;
1947         vec3_t pos2;
1948         VM_SAFEPARMCOUNT(3, VM_CL_te_flamejet);
1949         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
1950                 return;
1951         pos = PRVM_G_VECTOR(OFS_PARM0);
1952         CL_FindNonSolidLocation(pos, pos2, 4);
1953         CL_ParticleEffect(EFFECT_TE_FLAMEJET, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1954 }
1955
1956
1957 // #443 void(entity e, entity tagentity, string tagname) setattachment
1958 void VM_CL_setattachment (void)
1959 {
1960         prvm_edict_t *e;
1961         prvm_edict_t *tagentity;
1962         const char *tagname;
1963         prvm_eval_t *v;
1964         int modelindex;
1965         dp_model_t *model;
1966         VM_SAFEPARMCOUNT(3, VM_CL_setattachment);
1967
1968         e = PRVM_G_EDICT(OFS_PARM0);
1969         tagentity = PRVM_G_EDICT(OFS_PARM1);
1970         tagname = PRVM_G_STRING(OFS_PARM2);
1971
1972         if (e == prog->edicts)
1973         {
1974                 VM_Warning("setattachment: can not modify world entity\n");
1975                 return;
1976         }
1977         if (e->priv.server->free)
1978         {
1979                 VM_Warning("setattachment: can not modify free entity\n");
1980                 return;
1981         }
1982
1983         if (tagentity == NULL)
1984                 tagentity = prog->edicts;
1985
1986         v = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.tag_entity);
1987         if (v)
1988                 v->edict = PRVM_EDICT_TO_PROG(tagentity);
1989
1990         v = PRVM_EDICTFIELDVALUE(e, prog->fieldoffsets.tag_index);
1991         if (v)
1992                 v->_float = 0;
1993         if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0])
1994         {
1995                 modelindex = (int)tagentity->fields.client->modelindex;
1996                 model = CL_GetModelByIndex(modelindex);
1997                 if (model)
1998                 {
1999                         v->_float = Mod_Alias_GetTagIndexForName(model, (int)tagentity->fields.client->skin, tagname);
2000                         if (v->_float == 0)
2001                                 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);
2002                 }
2003                 else
2004                         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));
2005         }
2006 }
2007
2008 /////////////////////////////////////////
2009 // DP_MD3_TAGINFO extension coded by VorteX
2010
2011 int CL_GetTagIndex (prvm_edict_t *e, const char *tagname)
2012 {
2013         dp_model_t *model = CL_GetModelFromEdict(e);
2014         if (model)
2015                 return Mod_Alias_GetTagIndexForName(model, (int)e->fields.client->skin, tagname);
2016         else
2017                 return -1;
2018 }
2019
2020 int CL_GetExtendedTagInfo (prvm_edict_t *e, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
2021 {
2022         int r;
2023         dp_model_t *model;
2024
2025         *tagname = NULL;
2026         *parentindex = 0;
2027         Matrix4x4_CreateIdentity(tag_localmatrix);
2028
2029         if (tagindex >= 0
2030          && (model = CL_GetModelFromEdict(e))
2031          && model->animscenes)
2032         {
2033                 r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)e->fields.client->skin, e->priv.server->frameblend, &e->priv.server->skeleton, tagindex - 1, parentindex, tagname, tag_localmatrix);
2034
2035                 if(!r) // success?
2036                         *parentindex += 1;
2037
2038                 return r;
2039         }
2040
2041         return 1;
2042 }
2043
2044 int CL_GetPitchSign(prvm_edict_t *ent)
2045 {
2046         dp_model_t *model;
2047         if ((model = CL_GetModelFromEdict(ent)) && model->type == mod_alias)
2048                 return -1;
2049         return 1;
2050 }
2051
2052 void CL_GetEntityMatrix (prvm_edict_t *ent, matrix4x4_t *out, qboolean viewmatrix)
2053 {
2054         prvm_eval_t *val;
2055         float scale;
2056         float pitchsign = 1;
2057
2058         scale = 1;
2059         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale);
2060         if (val && val->_float != 0)
2061                 scale = val->_float;
2062
2063         // TODO do we need the same weird angle inverting logic here as in the server side case?
2064         if(viewmatrix)
2065                 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);
2066         else
2067         {
2068                 pitchsign = CL_GetPitchSign(ent);
2069                 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);
2070         }
2071 }
2072
2073 int CL_GetEntityLocalTagMatrix(prvm_edict_t *ent, int tagindex, matrix4x4_t *out)
2074 {
2075         dp_model_t *model;
2076         if (tagindex >= 0
2077          && (model = CL_GetModelFromEdict(ent))
2078          && model->animscenes)
2079         {
2080                 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
2081                 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
2082                 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
2083                 return Mod_Alias_GetTagMatrix(model, ent->priv.server->frameblend, &ent->priv.server->skeleton, tagindex, out);
2084         }
2085         *out = identitymatrix;
2086         return 0;
2087 }
2088
2089 // Warnings/errors code:
2090 // 0 - normal (everything all-right)
2091 // 1 - world entity
2092 // 2 - free entity
2093 // 3 - null or non-precached model
2094 // 4 - no tags with requested index
2095 // 5 - runaway loop at attachment chain
2096 extern cvar_t cl_bob;
2097 extern cvar_t cl_bobcycle;
2098 extern cvar_t cl_bobup;
2099 int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex)
2100 {
2101         int ret;
2102         prvm_eval_t *val;
2103         int attachloop;
2104         matrix4x4_t entitymatrix, tagmatrix, attachmatrix;
2105         dp_model_t *model;
2106
2107         *out = identitymatrix; // warnings and errors return identical matrix
2108
2109         if (ent == prog->edicts)
2110                 return 1;
2111         if (ent->priv.server->free)
2112                 return 2;
2113
2114         model = CL_GetModelFromEdict(ent);
2115         if(!model)
2116                 return 3;
2117
2118         tagmatrix = identitymatrix;
2119         attachloop = 0;
2120         for(;;)
2121         {
2122                 if(attachloop >= 256)
2123                         return 5;
2124                 // apply transformation by child's tagindex on parent entity and then
2125                 // by parent entity itself
2126                 ret = CL_GetEntityLocalTagMatrix(ent, tagindex - 1, &attachmatrix);
2127                 if(ret && attachloop == 0)
2128                         return ret;
2129                 CL_GetEntityMatrix(ent, &entitymatrix, false);
2130                 Matrix4x4_Concat(&tagmatrix, &attachmatrix, out);
2131                 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2132                 // next iteration we process the parent entity
2133                 if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict)
2134                 {
2135                         tagindex = (int)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float;
2136                         ent = PRVM_EDICT_NUM(val->edict);
2137                 }
2138                 else
2139                         break;
2140                 attachloop++;
2141         }
2142
2143         // RENDER_VIEWMODEL magic
2144         if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderflags)) && (RF_VIEWMODEL & (int)val->_float))
2145         {
2146                 Matrix4x4_Copy(&tagmatrix, out);
2147
2148                 CL_GetEntityMatrix(prog->edicts, &entitymatrix, true);
2149                 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2150
2151                 /*
2152                 // Cl_bob, ported from rendering code
2153                 if (ent->fields.client->health > 0 && cl_bob.value && cl_bobcycle.value)
2154                 {
2155                         double bob, cycle;
2156                         // LordHavoc: this code is *weird*, but not replacable (I think it
2157                         // should be done in QC on the server, but oh well, quake is quake)
2158                         // LordHavoc: figured out bobup: the time at which the sin is at 180
2159                         // degrees (which allows lengthening or squishing the peak or valley)
2160                         cycle = cl.time/cl_bobcycle.value;
2161                         cycle -= (int)cycle;
2162                         if (cycle < cl_bobup.value)
2163                                 cycle = sin(M_PI * cycle / cl_bobup.value);
2164                         else
2165                                 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
2166                         // bob is proportional to velocity in the xy plane
2167                         // (don't count Z, or jumping messes it up)
2168                         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;
2169                         bob = bob*0.3 + bob*0.7*cycle;
2170                         Matrix4x4_AdjustOrigin(out, 0, 0, bound(-7, bob, 4));
2171                 }
2172                 */
2173         }
2174         return 0;
2175 }
2176
2177 // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
2178 void VM_CL_gettagindex (void)
2179 {
2180         prvm_edict_t *ent;
2181         const char *tag_name;
2182         int tag_index;
2183
2184         VM_SAFEPARMCOUNT(2, VM_CL_gettagindex);
2185
2186         ent = PRVM_G_EDICT(OFS_PARM0);
2187         tag_name = PRVM_G_STRING(OFS_PARM1);
2188         if (ent == prog->edicts)
2189         {
2190                 VM_Warning("VM_CL_gettagindex(entity #%i): can't affect world entity\n", PRVM_NUM_FOR_EDICT(ent));
2191                 return;
2192         }
2193         if (ent->priv.server->free)
2194         {
2195                 VM_Warning("VM_CL_gettagindex(entity #%i): can't affect free entity\n", PRVM_NUM_FOR_EDICT(ent));
2196                 return;
2197         }
2198
2199         tag_index = 0;
2200         if (!CL_GetModelFromEdict(ent))
2201                 Con_DPrintf("VM_CL_gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
2202         else
2203         {
2204                 tag_index = CL_GetTagIndex(ent, tag_name);
2205                 if (tag_index == 0)
2206                         Con_DPrintf("VM_CL_gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
2207         }
2208         PRVM_G_FLOAT(OFS_RETURN) = tag_index;
2209 }
2210
2211 // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO)
2212 void VM_CL_gettaginfo (void)
2213 {
2214         prvm_edict_t *e;
2215         int tagindex;
2216         matrix4x4_t tag_matrix;
2217         matrix4x4_t tag_localmatrix;
2218         int parentindex;
2219         const char *tagname;
2220         int returncode;
2221         prvm_eval_t *val;
2222         vec3_t fo, le, up, trans;
2223         const dp_model_t *model;
2224
2225         VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo);
2226
2227         e = PRVM_G_EDICT(OFS_PARM0);
2228         tagindex = (int)PRVM_G_FLOAT(OFS_PARM1);
2229         returncode = CL_GetTagMatrix(&tag_matrix, e, tagindex);
2230         Matrix4x4_ToVectors(&tag_matrix, prog->globals.client->v_forward, le, prog->globals.client->v_up, PRVM_G_VECTOR(OFS_RETURN));
2231         VectorScale(le, -1, prog->globals.client->v_right);
2232         model = CL_GetModelFromEdict(e);
2233         VM_GenerateFrameGroupBlend(e->priv.server->framegroupblend, e);
2234         VM_FrameBlendFromFrameGroupBlend(e->priv.server->frameblend, e->priv.server->framegroupblend, model);
2235         VM_UpdateEdictSkeleton(e, model, e->priv.server->frameblend);
2236         CL_GetExtendedTagInfo(e, tagindex, &parentindex, &tagname, &tag_localmatrix);
2237         Matrix4x4_ToVectors(&tag_localmatrix, fo, le, up, trans);
2238
2239         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_parent)))
2240                 val->_float = parentindex;
2241         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_name)))
2242                 val->string = tagname ? PRVM_SetTempString(tagname) : 0;
2243         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_offset)))
2244                 VectorCopy(trans, val->vector);
2245         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_forward)))
2246                 VectorCopy(fo, val->vector);
2247         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_right)))
2248                 VectorScale(le, -1, val->vector);
2249         if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_up)))
2250                 VectorCopy(up, val->vector);
2251
2252         switch(returncode)
2253         {
2254                 case 1:
2255                         VM_Warning("gettagindex: can't affect world entity\n");
2256                         break;
2257                 case 2:
2258                         VM_Warning("gettagindex: can't affect free entity\n");
2259                         break;
2260                 case 3:
2261                         Con_DPrintf("CL_GetTagMatrix(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(e));
2262                         break;
2263                 case 4:
2264                         Con_DPrintf("CL_GetTagMatrix(entity #%i): model has no tag with requested index %i\n", PRVM_NUM_FOR_EDICT(e), tagindex);
2265                         break;
2266                 case 5:
2267                         Con_DPrintf("CL_GetTagMatrix(entity #%i): runaway loop at attachment chain\n", PRVM_NUM_FOR_EDICT(e));
2268                         break;
2269         }
2270 }
2271
2272 //============================================================================
2273
2274 //====================
2275 // DP_CSQC_SPAWNPARTICLE
2276 // a QC hook to engine's CL_NewParticle
2277 //====================
2278
2279 // particle theme struct
2280 typedef struct vmparticletheme_s
2281 {
2282         unsigned short typeindex;
2283         qboolean initialized;
2284         pblend_t blendmode;
2285         porientation_t orientation;
2286         int color1;
2287         int color2;
2288         int tex;
2289         float size;
2290         float sizeincrease;
2291         float alpha;
2292         float alphafade;
2293         float gravity;
2294         float bounce;
2295         float airfriction;
2296         float liquidfriction;
2297         float originjitter;
2298         float velocityjitter;
2299         qboolean qualityreduction;
2300         float lifetime;
2301         float stretch;
2302         int staincolor1;
2303         int staincolor2;
2304         int staintex;
2305         float stainalpha;
2306         float stainsize;
2307         float delayspawn;
2308         float delaycollision;
2309         float angle;
2310         float spin;
2311 }vmparticletheme_t;
2312
2313 // particle spawner
2314 typedef struct vmparticlespawner_s
2315 {
2316         mempool_t                       *pool;
2317         qboolean                        initialized;
2318         qboolean                        verified;
2319         vmparticletheme_t       *themes;
2320         int                                     max_themes;
2321         // global addresses
2322         float *particle_type;
2323         float *particle_blendmode; 
2324         float *particle_orientation;
2325         float *particle_color1;
2326         float *particle_color2;
2327         float *particle_tex;
2328         float *particle_size;
2329         float *particle_sizeincrease;
2330         float *particle_alpha;
2331         float *particle_alphafade;
2332         float *particle_time;
2333         float *particle_gravity;
2334         float *particle_bounce;
2335         float *particle_airfriction;
2336         float *particle_liquidfriction;
2337         float *particle_originjitter;
2338         float *particle_velocityjitter;
2339         float *particle_qualityreduction;
2340         float *particle_stretch;
2341         float *particle_staincolor1;
2342         float *particle_staincolor2;
2343         float *particle_stainalpha;
2344         float *particle_stainsize;
2345         float *particle_staintex;
2346         float *particle_delayspawn;
2347         float *particle_delaycollision;
2348         float *particle_angle;
2349         float *particle_spin;
2350 }vmparticlespawner_t;
2351
2352 vmparticlespawner_t vmpartspawner;
2353
2354 // TODO: automatic max_themes grow
2355 static void VM_InitParticleSpawner (int maxthemes)
2356 {
2357         prvm_eval_t *val;
2358
2359         // bound max themes to not be an insane value
2360         if (maxthemes < 4)
2361                 maxthemes = 4;
2362         if (maxthemes > 2048)
2363                 maxthemes = 2048;
2364         // allocate and set up structure
2365         if (vmpartspawner.initialized) // reallocate
2366         {
2367                 Mem_FreePool(&vmpartspawner.pool);
2368                 memset(&vmpartspawner, 0, sizeof(vmparticlespawner_t));
2369         }
2370         vmpartspawner.pool = Mem_AllocPool("VMPARTICLESPAWNER", 0, NULL);
2371         vmpartspawner.themes = (vmparticletheme_t *)Mem_Alloc(vmpartspawner.pool, sizeof(vmparticletheme_t)*maxthemes);
2372         vmpartspawner.max_themes = maxthemes;
2373         vmpartspawner.initialized = true;
2374         vmpartspawner.verified = true;
2375         // get field addresses for fast querying (we can do 1000 calls of spawnparticle in a frame)
2376         #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; }
2377         #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; }
2378         getglobal(particle_type, "particle_type");
2379         getglobal(particle_blendmode, "particle_blendmode");
2380         getglobal(particle_orientation, "particle_orientation");
2381         getglobalvector(particle_color1, "particle_color1");
2382         getglobalvector(particle_color2, "particle_color2");
2383         getglobal(particle_tex, "particle_tex");
2384         getglobal(particle_size, "particle_size");
2385         getglobal(particle_sizeincrease, "particle_sizeincrease");
2386         getglobal(particle_alpha, "particle_alpha");
2387         getglobal(particle_alphafade, "particle_alphafade");
2388         getglobal(particle_time, "particle_time");
2389         getglobal(particle_gravity, "particle_gravity");
2390         getglobal(particle_bounce, "particle_bounce");
2391         getglobal(particle_airfriction, "particle_airfriction");
2392         getglobal(particle_liquidfriction, "particle_liquidfriction");
2393         getglobal(particle_originjitter, "particle_originjitter");
2394         getglobal(particle_velocityjitter, "particle_velocityjitter");
2395         getglobal(particle_qualityreduction, "particle_qualityreduction");
2396         getglobal(particle_stretch, "particle_stretch");
2397         getglobalvector(particle_staincolor1, "particle_staincolor1");
2398         getglobalvector(particle_staincolor2, "particle_staincolor2");
2399         getglobal(particle_stainalpha, "particle_stainalpha");
2400         getglobal(particle_stainsize, "particle_stainsize");
2401         getglobal(particle_staintex, "particle_staintex");
2402         getglobal(particle_staintex, "particle_staintex");
2403         getglobal(particle_delayspawn, "particle_delayspawn");
2404         getglobal(particle_delaycollision, "particle_delaycollision");
2405         getglobal(particle_angle, "particle_angle");
2406         getglobal(particle_spin, "particle_spin");
2407         #undef getglobal
2408         #undef getglobalvector
2409 }
2410
2411 // reset particle theme to default values
2412 static void VM_ResetParticleTheme (vmparticletheme_t *theme)
2413 {
2414         theme->initialized = true;
2415         theme->typeindex = pt_static;
2416         theme->blendmode = PBLEND_ADD;
2417         theme->orientation = PARTICLE_BILLBOARD;
2418         theme->color1 = 0x808080;
2419         theme->color2 = 0xFFFFFF;
2420         theme->tex = 63;
2421         theme->size = 2;
2422         theme->sizeincrease = 0;
2423         theme->alpha = 256;
2424         theme->alphafade = 512;
2425         theme->gravity = 0.0f;
2426         theme->bounce = 0.0f;
2427         theme->airfriction = 1.0f;
2428         theme->liquidfriction = 4.0f;
2429         theme->originjitter = 0.0f;
2430         theme->velocityjitter = 0.0f;
2431         theme->qualityreduction = false;
2432         theme->lifetime = 4;
2433         theme->stretch = 1;
2434         theme->staincolor1 = -1;
2435         theme->staincolor2 = -1;
2436         theme->staintex = -1;
2437         theme->delayspawn = 0.0f;
2438         theme->delaycollision = 0.0f;
2439         theme->angle = 0.0f;
2440         theme->spin = 0.0f;
2441 }
2442
2443 // particle theme -> QC globals
2444 void VM_CL_ParticleThemeToGlobals(vmparticletheme_t *theme)
2445 {
2446         *vmpartspawner.particle_type = theme->typeindex;
2447         *vmpartspawner.particle_blendmode = theme->blendmode;
2448         *vmpartspawner.particle_orientation = theme->orientation;
2449         vmpartspawner.particle_color1[0] = (theme->color1 >> 16) & 0xFF; // VorteX: int only can store 0-255, not 0-256 which means 0 - 0,99609375...
2450         vmpartspawner.particle_color1[1] = (theme->color1 >> 8) & 0xFF;
2451         vmpartspawner.particle_color1[2] = (theme->color1 >> 0) & 0xFF;
2452         vmpartspawner.particle_color2[0] = (theme->color2 >> 16) & 0xFF;
2453         vmpartspawner.particle_color2[1] = (theme->color2 >> 8) & 0xFF;
2454         vmpartspawner.particle_color2[2] = (theme->color2 >> 0) & 0xFF;
2455         *vmpartspawner.particle_tex = (float)theme->tex;
2456         *vmpartspawner.particle_size = theme->size;
2457         *vmpartspawner.particle_sizeincrease = theme->sizeincrease;
2458         *vmpartspawner.particle_alpha = theme->alpha/256;
2459         *vmpartspawner.particle_alphafade = theme->alphafade/256;
2460         *vmpartspawner.particle_time = theme->lifetime;
2461         *vmpartspawner.particle_gravity = theme->gravity;
2462         *vmpartspawner.particle_bounce = theme->bounce;
2463         *vmpartspawner.particle_airfriction = theme->airfriction;
2464         *vmpartspawner.particle_liquidfriction = theme->liquidfriction;
2465         *vmpartspawner.particle_originjitter = theme->originjitter;
2466         *vmpartspawner.particle_velocityjitter = theme->velocityjitter;
2467         *vmpartspawner.particle_qualityreduction = theme->qualityreduction;
2468         *vmpartspawner.particle_stretch = theme->stretch;
2469         vmpartspawner.particle_staincolor1[0] = ((int)theme->staincolor1 >> 16) & 0xFF;
2470         vmpartspawner.particle_staincolor1[1] = ((int)theme->staincolor1 >> 8) & 0xFF;
2471         vmpartspawner.particle_staincolor1[2] = ((int)theme->staincolor1 >> 0) & 0xFF;
2472         vmpartspawner.particle_staincolor2[0] = ((int)theme->staincolor2 >> 16) & 0xFF;
2473         vmpartspawner.particle_staincolor2[1] = ((int)theme->staincolor2 >> 8) & 0xFF;
2474         vmpartspawner.particle_staincolor2[2] = ((int)theme->staincolor2 >> 0) & 0xFF;
2475         *vmpartspawner.particle_staintex = (float)theme->staintex;
2476         *vmpartspawner.particle_stainalpha = (float)theme->stainalpha/256;
2477         *vmpartspawner.particle_stainsize = (float)theme->stainsize;
2478         *vmpartspawner.particle_delayspawn = theme->delayspawn;
2479         *vmpartspawner.particle_delaycollision = theme->delaycollision;
2480         *vmpartspawner.particle_angle = theme->angle;
2481         *vmpartspawner.particle_spin = theme->spin;
2482 }
2483
2484 // QC globals ->  particle theme
2485 void VM_CL_ParticleThemeFromGlobals(vmparticletheme_t *theme)
2486 {
2487         theme->typeindex = (unsigned short)*vmpartspawner.particle_type;
2488         theme->blendmode = (pblend_t)*vmpartspawner.particle_blendmode;
2489         theme->orientation = (porientation_t)*vmpartspawner.particle_orientation;
2490         theme->color1 = ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]);
2491         theme->color2 = ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]);
2492         theme->tex = (int)*vmpartspawner.particle_tex;
2493         theme->size = *vmpartspawner.particle_size;
2494         theme->sizeincrease = *vmpartspawner.particle_sizeincrease;
2495         theme->alpha = *vmpartspawner.particle_alpha*256;
2496         theme->alphafade = *vmpartspawner.particle_alphafade*256;
2497         theme->lifetime = *vmpartspawner.particle_time;
2498         theme->gravity = *vmpartspawner.particle_gravity;
2499         theme->bounce = *vmpartspawner.particle_bounce;
2500         theme->airfriction = *vmpartspawner.particle_airfriction;
2501         theme->liquidfriction = *vmpartspawner.particle_liquidfriction;
2502         theme->originjitter = *vmpartspawner.particle_originjitter;
2503         theme->velocityjitter = *vmpartspawner.particle_velocityjitter;
2504         theme->qualityreduction = (*vmpartspawner.particle_qualityreduction) ? true : false;
2505         theme->stretch = *vmpartspawner.particle_stretch;
2506         theme->staincolor1 = ((int)vmpartspawner.particle_staincolor1[0])*65536 + (int)(vmpartspawner.particle_staincolor1[1])*256 + (int)(vmpartspawner.particle_staincolor1[2]);
2507         theme->staincolor2 = (int)(vmpartspawner.particle_staincolor2[0])*65536 + (int)(vmpartspawner.particle_staincolor2[1])*256 + (int)(vmpartspawner.particle_staincolor2[2]);
2508         theme->staintex =(int)*vmpartspawner.particle_staintex;
2509         theme->stainalpha = *vmpartspawner.particle_stainalpha*256;
2510         theme->stainsize = *vmpartspawner.particle_stainsize;
2511         theme->delayspawn = *vmpartspawner.particle_delayspawn;
2512         theme->delaycollision = *vmpartspawner.particle_delaycollision;
2513         theme->angle = *vmpartspawner.particle_angle;
2514         theme->spin = *vmpartspawner.particle_spin;
2515 }
2516
2517 // init particle spawner interface
2518 // # float(float max_themes) initparticlespawner
2519 void VM_CL_InitParticleSpawner (void)
2520 {
2521         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_InitParticleSpawner);
2522         VM_InitParticleSpawner((int)PRVM_G_FLOAT(OFS_PARM0));
2523         vmpartspawner.themes[0].initialized = true;
2524         VM_ResetParticleTheme(&vmpartspawner.themes[0]);
2525         PRVM_G_FLOAT(OFS_RETURN) = (vmpartspawner.verified == true) ? 1 : 0;
2526 }
2527
2528 // void() resetparticle
2529 void VM_CL_ResetParticle (void)
2530 {
2531         VM_SAFEPARMCOUNT(0, VM_CL_ResetParticle);
2532         if (vmpartspawner.verified == false)
2533         {
2534                 VM_Warning("VM_CL_ResetParticle: particle spawner not initialized\n");
2535                 return;
2536         }
2537         VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2538 }
2539
2540 // void(float themenum) particletheme
2541 void VM_CL_ParticleTheme (void)
2542 {
2543         int themenum;
2544
2545         VM_SAFEPARMCOUNT(1, VM_CL_ParticleTheme);
2546         if (vmpartspawner.verified == false)
2547         {
2548                 VM_Warning("VM_CL_ParticleTheme: particle spawner not initialized\n");
2549                 return;
2550         }
2551         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2552         if (themenum < 0 || themenum >= vmpartspawner.max_themes)
2553         {
2554                 VM_Warning("VM_CL_ParticleTheme: bad theme number %i\n", themenum);
2555                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2556                 return;
2557         }
2558         if (vmpartspawner.themes[themenum].initialized == false)
2559         {
2560                 VM_Warning("VM_CL_ParticleTheme: theme #%i not exists\n", themenum);
2561                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2562                 return;
2563         }
2564         // load particle theme into globals
2565         VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[themenum]);
2566 }
2567
2568 // float() saveparticletheme
2569 // void(float themenum) updateparticletheme
2570 void VM_CL_ParticleThemeSave (void)
2571 {
2572         int themenum;
2573
2574         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_ParticleThemeSave);
2575         if (vmpartspawner.verified == false)
2576         {
2577                 VM_Warning("VM_CL_ParticleThemeSave: particle spawner not initialized\n");
2578                 return;
2579         }
2580         // allocate new theme, save it and return
2581         if (prog->argc < 1)
2582         {
2583                 for (themenum = 0; themenum < vmpartspawner.max_themes; themenum++)
2584                         if (vmpartspawner.themes[themenum].initialized == false)
2585                                 break;
2586                 if (themenum >= vmpartspawner.max_themes)
2587                 {
2588                         if (vmpartspawner.max_themes == 2048)
2589                                 VM_Warning("VM_CL_ParticleThemeSave: no free theme slots\n");
2590                         else
2591                                 VM_Warning("VM_CL_ParticleThemeSave: no free theme slots, try initparticlespawner() with highter max_themes\n");
2592                         PRVM_G_FLOAT(OFS_RETURN) = -1;
2593                         return;
2594                 }
2595                 vmpartspawner.themes[themenum].initialized = true;
2596                 VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]);
2597                 PRVM_G_FLOAT(OFS_RETURN) = themenum;
2598                 return;
2599         }
2600         // update existing theme
2601         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2602         if (themenum < 0 || themenum >= vmpartspawner.max_themes)
2603         {
2604                 VM_Warning("VM_CL_ParticleThemeSave: bad theme number %i\n", themenum);
2605                 return;
2606         }
2607         vmpartspawner.themes[themenum].initialized = true;
2608         VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]);
2609 }
2610
2611 // void(float themenum) freeparticletheme
2612 void VM_CL_ParticleThemeFree (void)
2613 {
2614         int themenum;
2615
2616         VM_SAFEPARMCOUNT(1, VM_CL_ParticleThemeFree);
2617         if (vmpartspawner.verified == false)
2618         {
2619                 VM_Warning("VM_CL_ParticleThemeFree: particle spawner not initialized\n");
2620                 return;
2621         }
2622         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2623         // check parms
2624         if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2625         {
2626                 VM_Warning("VM_CL_ParticleThemeFree: bad theme number %i\n", themenum);
2627                 return;
2628         }
2629         if (vmpartspawner.themes[themenum].initialized == false)
2630         {
2631                 VM_Warning("VM_CL_ParticleThemeFree: theme #%i already freed\n", themenum);
2632                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2633                 return;
2634         }
2635         // free theme
2636         VM_ResetParticleTheme(&vmpartspawner.themes[themenum]);
2637         vmpartspawner.themes[themenum].initialized = false;
2638 }
2639
2640 // float(vector org, vector dir, [float theme]) particle
2641 // returns 0 if failed, 1 if succesful
2642 void VM_CL_SpawnParticle (void)
2643 {
2644         float *org, *dir;
2645         vmparticletheme_t *theme;
2646         particle_t *part;
2647         int themenum;
2648
2649         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_SpawnParticle2);
2650         if (vmpartspawner.verified == false)
2651         {
2652                 VM_Warning("VM_CL_SpawnParticle: particle spawner not initialized\n");
2653                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
2654                 return;
2655         }
2656         org = PRVM_G_VECTOR(OFS_PARM0);
2657         dir = PRVM_G_VECTOR(OFS_PARM1);
2658         
2659         if (prog->argc < 3) // global-set particle
2660         {
2661                 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);
2662                 if (!part)
2663                 {
2664                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
2665                         return;
2666                 }
2667                 if (*vmpartspawner.particle_delayspawn)
2668                         part->delayedspawn = cl.time + *vmpartspawner.particle_delayspawn;
2669                 //if (*vmpartspawner.particle_delaycollision)
2670                 //      part->delayedcollisions = cl.time + *vmpartspawner.particle_delaycollision;
2671         }
2672         else // quick themed particle
2673         {
2674                 themenum = (int)PRVM_G_FLOAT(OFS_PARM2);
2675                 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2676                 {
2677                         VM_Warning("VM_CL_SpawnParticle: bad theme number %i\n", themenum);
2678                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
2679                         return;
2680                 }
2681                 theme = &vmpartspawner.themes[themenum];
2682                 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);
2683                 if (!part)
2684                 {
2685                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
2686                         return;
2687                 }
2688                 if (theme->delayspawn)
2689                         part->delayedspawn = cl.time + theme->delayspawn;
2690                 //if (theme->delaycollision)
2691                 //      part->delayedcollisions = cl.time + theme->delaycollision;
2692         }
2693         PRVM_G_FLOAT(OFS_RETURN) = 1; 
2694 }
2695
2696 // float(vector org, vector dir, float spawndelay, float collisiondelay, [float theme]) delayedparticle
2697 // returns 0 if failed, 1 if success
2698 void VM_CL_SpawnParticleDelayed (void)
2699 {
2700         float *org, *dir;
2701         vmparticletheme_t *theme;
2702         particle_t *part;
2703         int themenum;
2704
2705         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_SpawnParticle2);
2706         if (vmpartspawner.verified == false)
2707         {
2708                 VM_Warning("VM_CL_SpawnParticle: particle spawner not initialized\n");
2709                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
2710                 return;
2711         }
2712         org = PRVM_G_VECTOR(OFS_PARM0);
2713         dir = PRVM_G_VECTOR(OFS_PARM1);
2714         if (prog->argc < 5) // global-set particle
2715                 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);
2716         else // themed particle
2717         {
2718                 themenum = (int)PRVM_G_FLOAT(OFS_PARM4);
2719                 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2720                 {
2721                         VM_Warning("VM_CL_SpawnParticle: bad theme number %i\n", themenum);
2722                         PRVM_G_FLOAT(OFS_RETURN) = 0;  
2723                         return;
2724                 }
2725                 theme = &vmpartspawner.themes[themenum];
2726                 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);
2727         }
2728         if (!part) 
2729         { 
2730                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
2731                 return; 
2732         }
2733         part->delayedspawn = cl.time + PRVM_G_FLOAT(OFS_PARM2);
2734         //part->delayedcollisions = cl.time + PRVM_G_FLOAT(OFS_PARM3);
2735         PRVM_G_FLOAT(OFS_RETURN) = 0;
2736 }
2737
2738 //====================
2739 //CSQC engine entities query
2740 //====================
2741
2742 // float(float entitynum, float whatfld) getentity;
2743 // vector(float entitynum, float whatfld) getentityvec;
2744 // querying engine-drawn entity
2745 // VorteX: currently it's only tested with whatfld = 1..7
2746 void VM_CL_GetEntity (void)
2747 {
2748         int entnum, fieldnum;
2749         float org[3], v1[3], v2[3];
2750         VM_SAFEPARMCOUNT(2, VM_CL_GetEntityVec);
2751
2752         entnum = PRVM_G_FLOAT(OFS_PARM0);
2753         if (entnum < 0 || entnum >= cl.num_entities)
2754         {
2755                 PRVM_G_FLOAT(OFS_RETURN) = 0;
2756                 return;
2757         }
2758         fieldnum = PRVM_G_FLOAT(OFS_PARM1);
2759         switch(fieldnum)
2760         {
2761                 case 0: // active state
2762                         PRVM_G_FLOAT(OFS_RETURN) = cl.entities_active[entnum];
2763                         break;
2764                 case 1: // origin
2765                         Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, PRVM_G_VECTOR(OFS_RETURN));
2766                         break; 
2767                 case 2: // forward
2768                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, PRVM_G_VECTOR(OFS_RETURN), v1, v2, org);        
2769                         break;
2770                 case 3: // right
2771                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, v1, PRVM_G_VECTOR(OFS_RETURN), v2, org);        
2772                         break;
2773                 case 4: // up
2774                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, v1, v2, PRVM_G_VECTOR(OFS_RETURN), org);        
2775                         break;
2776                 case 5: // scale
2777                         PRVM_G_FLOAT(OFS_RETURN) = Matrix4x4_ScaleFromMatrix(&cl.entities[entnum].render.matrix);
2778                         break;  
2779                 case 6: // origin + v_forward, v_right, v_up
2780                         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)); 
2781                         break;  
2782                 case 7: // alpha
2783                         PRVM_G_FLOAT(OFS_RETURN) = cl.entities[entnum].render.alpha;
2784                         break;  
2785                 case 8: // colormor
2786                         VectorCopy(cl.entities[entnum].render.colormod, PRVM_G_VECTOR(OFS_RETURN));
2787                         break;
2788                 case 9: // pants colormod
2789                         VectorCopy(cl.entities[entnum].render.colormap_pantscolor, PRVM_G_VECTOR(OFS_RETURN));
2790                         break;
2791                 case 10: // shirt colormod
2792                         VectorCopy(cl.entities[entnum].render.colormap_shirtcolor, PRVM_G_VECTOR(OFS_RETURN));
2793                         break;
2794                 case 11: // skinnum
2795                         PRVM_G_FLOAT(OFS_RETURN) = cl.entities[entnum].render.skinnum;
2796                         break;  
2797                 case 12: // mins
2798                         VectorCopy(cl.entities[entnum].render.mins, PRVM_G_VECTOR(OFS_RETURN));         
2799                         break;  
2800                 case 13: // maxs
2801                         VectorCopy(cl.entities[entnum].render.maxs, PRVM_G_VECTOR(OFS_RETURN));         
2802                         break;  
2803                 case 14: // absmin
2804                         Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, org);
2805                         VectorAdd(cl.entities[entnum].render.mins, org, PRVM_G_VECTOR(OFS_RETURN));             
2806                         break;  
2807                 case 15: // absmax
2808                         Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, org);
2809                         VectorAdd(cl.entities[entnum].render.maxs, org, PRVM_G_VECTOR(OFS_RETURN));             
2810                         break;
2811                 case 16: // light
2812                         VectorMA(cl.entities[entnum].render.modellight_ambient, 0.5, cl.entities[entnum].render.modellight_diffuse, PRVM_G_VECTOR(OFS_RETURN));
2813                         break;  
2814                 default:
2815                         PRVM_G_FLOAT(OFS_RETURN) = 0;
2816                         break;
2817         }
2818 }
2819
2820 //====================
2821 //QC POLYGON functions
2822 //====================
2823
2824 #define VMPOLYGONS_MAXPOINTS 64
2825
2826 typedef struct vmpolygons_triangle_s
2827 {
2828         rtexture_t              *texture;
2829         int                             drawflag;
2830         unsigned short  elements[3];
2831 }vmpolygons_triangle_t;
2832
2833 typedef struct vmpolygons_s
2834 {
2835         mempool_t               *pool;
2836         qboolean                initialized;
2837         double          progstarttime;
2838
2839         int                             max_vertices;
2840         int                             num_vertices;
2841         float                   *data_vertex3f;
2842         float                   *data_color4f;
2843         float                   *data_texcoord2f;
2844
2845         int                             max_triangles;
2846         int                             num_triangles;
2847         vmpolygons_triangle_t *data_triangles;
2848         unsigned short  *data_sortedelement3s;
2849
2850         qboolean                begin_active;
2851         rtexture_t              *begin_texture;
2852         int                             begin_drawflag;
2853         int                             begin_vertices;
2854         float                   begin_vertex[VMPOLYGONS_MAXPOINTS][3];
2855         float                   begin_color[VMPOLYGONS_MAXPOINTS][4];
2856         float                   begin_texcoord[VMPOLYGONS_MAXPOINTS][2];
2857 } vmpolygons_t;
2858
2859 // FIXME: make VM_CL_R_Polygon functions use Debug_Polygon functions?
2860 vmpolygons_t vmpolygons[PRVM_MAXPROGS];
2861
2862 //#304 void() renderscene (EXT_CSQC)
2863 // moved that here to reset the polygons,
2864 // resetting them earlier causes R_Mesh_Draw to be called with numvertices = 0
2865 // --blub
2866 void VM_CL_R_RenderScene (void)
2867 {
2868         double t = Sys_DoubleTime();
2869         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
2870         VM_SAFEPARMCOUNT(0, VM_CL_R_RenderScene);
2871
2872         // we need to update any RENDER_VIEWMODEL entities at this point because
2873         // csqc supplies its own view matrix
2874         CL_UpdateViewEntities();
2875         // now draw stuff!
2876         R_RenderView();
2877
2878         polys->num_vertices = polys->num_triangles = 0;
2879         polys->progstarttime = prog->starttime;
2880
2881         // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView
2882         prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t;
2883 }
2884
2885 static void VM_ResizePolygons(vmpolygons_t *polys)
2886 {
2887         float *oldvertex3f = polys->data_vertex3f;
2888         float *oldcolor4f = polys->data_color4f;
2889         float *oldtexcoord2f = polys->data_texcoord2f;
2890         vmpolygons_triangle_t *oldtriangles = polys->data_triangles;
2891         unsigned short *oldsortedelement3s = polys->data_sortedelement3s;
2892         polys->max_vertices = min(polys->max_triangles*3, 65536);
2893         polys->data_vertex3f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[3]));
2894         polys->data_color4f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[4]));
2895         polys->data_texcoord2f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[2]));
2896         polys->data_triangles = (vmpolygons_triangle_t *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(vmpolygons_triangle_t));
2897         polys->data_sortedelement3s = (unsigned short *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(unsigned short[3]));
2898         if (polys->num_vertices)
2899         {
2900                 memcpy(polys->data_vertex3f, oldvertex3f, polys->num_vertices*sizeof(float[3]));
2901                 memcpy(polys->data_color4f, oldcolor4f, polys->num_vertices*sizeof(float[4]));
2902                 memcpy(polys->data_texcoord2f, oldtexcoord2f, polys->num_vertices*sizeof(float[2]));
2903         }
2904         if (polys->num_triangles)
2905         {
2906                 memcpy(polys->data_triangles, oldtriangles, polys->num_triangles*sizeof(vmpolygons_triangle_t));
2907                 memcpy(polys->data_sortedelement3s, oldsortedelement3s, polys->num_triangles*sizeof(unsigned short[3]));
2908         }
2909         if (oldvertex3f)
2910                 Mem_Free(oldvertex3f);
2911         if (oldcolor4f)
2912                 Mem_Free(oldcolor4f);
2913         if (oldtexcoord2f)
2914                 Mem_Free(oldtexcoord2f);
2915         if (oldtriangles)
2916                 Mem_Free(oldtriangles);
2917         if (oldsortedelement3s)
2918                 Mem_Free(oldsortedelement3s);
2919 }
2920
2921 static void VM_InitPolygons (vmpolygons_t* polys)
2922 {
2923         memset(polys, 0, sizeof(*polys));
2924         polys->pool = Mem_AllocPool("VMPOLY", 0, NULL);
2925         polys->max_triangles = 1024;
2926         VM_ResizePolygons(polys);
2927         polys->initialized = true;
2928 }
2929
2930 static void VM_DrawPolygonCallback (const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2931 {
2932         int surfacelistindex;
2933         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
2934         if(polys->progstarttime != prog->starttime) // from other progs? won't draw these (this can cause crashes!)
2935                 return;
2936         R_Mesh_ResetTextureState();
2937         R_EntityMatrix(&identitymatrix);
2938         GL_CullFace(GL_NONE);
2939         R_Mesh_VertexPointer(polys->data_vertex3f, 0, 0);
2940         R_Mesh_ColorPointer(polys->data_color4f, 0, 0);
2941         R_Mesh_TexCoordPointer(0, 2, polys->data_texcoord2f, 0, 0);
2942
2943         for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
2944         {
2945                 int numtriangles = 0;
2946                 rtexture_t *tex = polys->data_triangles[surfacelist[surfacelistindex]].texture;
2947                 int drawflag = polys->data_triangles[surfacelist[surfacelistindex]].drawflag;
2948                 // this can't call _DrawQ_ProcessDrawFlag, but should be in sync with it
2949                 // FIXME factor this out
2950                 if(drawflag == DRAWFLAG_ADDITIVE)
2951                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2952                 else if(drawflag == DRAWFLAG_MODULATE)
2953                         GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2954                 else if(drawflag == DRAWFLAG_2XMODULATE)
2955                         GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
2956                 else if(drawflag == DRAWFLAG_SCREEN)
2957                         GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
2958                 else
2959                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2960                 R_SetupShader_Generic(tex, NULL, GL_MODULATE, 1);
2961                 numtriangles = 0;
2962                 for (;surfacelistindex < numsurfaces;surfacelistindex++)
2963                 {
2964                         if (polys->data_triangles[surfacelist[surfacelistindex]].texture != tex || polys->data_triangles[surfacelist[surfacelistindex]].drawflag != drawflag)
2965                                 break;
2966                         VectorCopy(polys->data_triangles[surfacelist[surfacelistindex]].elements, polys->data_sortedelement3s + 3*numtriangles);
2967                         numtriangles++;
2968                 }
2969                 R_Mesh_Draw(0, polys->num_vertices, 0, numtriangles, NULL, polys->data_sortedelement3s, 0, 0);
2970         }
2971 }
2972
2973 void VMPolygons_Store(vmpolygons_t *polys)
2974 {
2975         if (r_refdef.draw2dstage)
2976         {
2977                 // draw the polygon as 2D immediately
2978                 drawqueuemesh_t mesh;
2979                 mesh.texture = polys->begin_texture;
2980                 mesh.num_vertices = polys->begin_vertices;
2981                 mesh.num_triangles = polys->begin_vertices-2;
2982                 mesh.data_element3i = polygonelement3i;
2983                 mesh.data_element3s = polygonelement3s;
2984                 mesh.data_vertex3f = polys->begin_vertex[0];
2985                 mesh.data_color4f = polys->begin_color[0];
2986                 mesh.data_texcoord2f = polys->begin_texcoord[0];
2987                 DrawQ_Mesh(&mesh, polys->begin_drawflag);
2988         }
2989         else
2990         {
2991                 // queue the polygon as 3D for sorted transparent rendering later
2992                 int i;
2993                 if (polys->max_triangles < polys->num_triangles + polys->begin_vertices-2)
2994                 {
2995                         polys->max_triangles *= 2;
2996                         VM_ResizePolygons(polys);
2997                 }
2998                 if (polys->num_vertices + polys->begin_vertices <= polys->max_vertices)
2999                 {
3000                         // needle in a haystack!
3001                         // polys->num_vertices was used for copying where we actually want to copy begin_vertices
3002                         // that also caused it to not render the first polygon that is added
3003                         // --blub
3004                         memcpy(polys->data_vertex3f + polys->num_vertices * 3, polys->begin_vertex[0], polys->begin_vertices * sizeof(float[3]));
3005                         memcpy(polys->data_color4f + polys->num_vertices * 4, polys->begin_color[0], polys->begin_vertices * sizeof(float[4]));
3006                         memcpy(polys->data_texcoord2f + polys->num_vertices * 2, polys->begin_texcoord[0], polys->begin_vertices * sizeof(float[2]));
3007                         for (i = 0;i < polys->begin_vertices-2;i++)
3008                         {
3009                                 polys->data_triangles[polys->num_triangles].texture = polys->begin_texture;
3010                                 polys->data_triangles[polys->num_triangles].drawflag = polys->begin_drawflag;
3011                                 polys->data_triangles[polys->num_triangles].elements[0] = polys->num_vertices;
3012                                 polys->data_triangles[polys->num_triangles].elements[1] = polys->num_vertices + i+1;
3013                                 polys->data_triangles[polys->num_triangles].elements[2] = polys->num_vertices + i+2;
3014                                 polys->num_triangles++;
3015                         }
3016                         polys->num_vertices += polys->begin_vertices;
3017                 }
3018         }
3019         polys->begin_active = false;
3020 }
3021
3022 // TODO: move this into the client code and clean-up everything else, too! [1/6/2008 Black]
3023 // LordHavoc: agreed, this is a mess
3024 void VM_CL_AddPolygonsToMeshQueue (void)
3025 {
3026         int i;
3027         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3028         vec3_t center;
3029
3030         // only add polygons of the currently active prog to the queue - if there is none, we're done
3031         if( !prog )
3032                 return;
3033
3034         if (!polys->num_triangles)
3035                 return;
3036
3037         for (i = 0;i < polys->num_triangles;i++)
3038         {
3039                 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);
3040                 R_MeshQueue_AddTransparent(center, VM_DrawPolygonCallback, NULL, i, NULL);
3041         }
3042
3043         /*polys->num_triangles = 0; // now done after rendering the scene,
3044           polys->num_vertices = 0;  // otherwise it's not rendered at all and prints an error message --blub */
3045 }
3046
3047 //void(string texturename, float flag) R_BeginPolygon
3048 void VM_CL_R_PolygonBegin (void)
3049 {
3050         const char              *picname;
3051         skinframe_t     *sf;
3052         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3053         int tf;
3054
3055         // TODO instead of using skinframes here (which provides the benefit of
3056         // better management of flags, and is more suited for 3D rendering), what
3057         // about supporting Q3 shaders?
3058
3059         VM_SAFEPARMCOUNT(2, VM_CL_R_PolygonBegin);
3060
3061         if (!polys->initialized)
3062                 VM_InitPolygons(polys);
3063         if(polys->progstarttime != prog->starttime)
3064         {
3065                 // from another progs? then reset the polys first (fixes crashes on map change, because that can make skinframe textures invalid)
3066                 polys->num_vertices = polys->num_triangles = 0;
3067                 polys->progstarttime = prog->starttime;
3068         }
3069         if (polys->begin_active)
3070         {
3071                 VM_Warning("VM_CL_R_PolygonBegin: called twice without VM_CL_R_PolygonBegin after first\n");
3072                 return;
3073         }
3074         picname = PRVM_G_STRING(OFS_PARM0);
3075
3076         sf = NULL;
3077         if(*picname)
3078         {
3079                 tf = TEXF_ALPHA;
3080                 if((int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MIPMAP)
3081                         tf |= TEXF_MIPMAP;
3082
3083                 do
3084                 {
3085                         sf = R_SkinFrame_FindNextByName(sf, picname);
3086                 }
3087                 while(sf && sf->textureflags != tf);
3088
3089                 if(!sf || !sf->base)
3090                         sf = R_SkinFrame_LoadExternal(picname, tf, true);
3091
3092                 if(sf)
3093                         R_SkinFrame_MarkUsed(sf);
3094         }
3095
3096         polys->begin_texture = (sf && sf->base) ? sf->base : r_texture_white;
3097         polys->begin_drawflag = (int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MASK;
3098         polys->begin_vertices = 0;
3099         polys->begin_active = true;
3100 }
3101
3102 //void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
3103 void VM_CL_R_PolygonVertex (void)
3104 {
3105         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3106
3107         VM_SAFEPARMCOUNT(4, VM_CL_R_PolygonVertex);
3108
3109         if (!polys->begin_active)
3110         {
3111                 VM_Warning("VM_CL_R_PolygonVertex: VM_CL_R_PolygonBegin wasn't called\n");
3112                 return;
3113         }
3114
3115         if (polys->begin_vertices >= VMPOLYGONS_MAXPOINTS)
3116         {
3117                 VM_Warning("VM_CL_R_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
3118                 return;
3119         }
3120
3121         polys->begin_vertex[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM0)[0];
3122         polys->begin_vertex[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM0)[1];
3123         polys->begin_vertex[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM0)[2];
3124         polys->begin_texcoord[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM1)[0];
3125         polys->begin_texcoord[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM1)[1];
3126         polys->begin_color[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM2)[0];
3127         polys->begin_color[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM2)[1];
3128         polys->begin_color[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM2)[2];
3129         polys->begin_color[polys->begin_vertices][3] = PRVM_G_FLOAT(OFS_PARM3);
3130         polys->begin_vertices++;
3131 }
3132
3133 //void() R_EndPolygon
3134 void VM_CL_R_PolygonEnd (void)
3135 {
3136         vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3137
3138         VM_SAFEPARMCOUNT(0, VM_CL_R_PolygonEnd);
3139         if (!polys->begin_active)
3140         {
3141                 VM_Warning("VM_CL_R_PolygonEnd: VM_CL_R_PolygonBegin wasn't called\n");
3142                 return;
3143         }
3144         polys->begin_active = false;
3145         if (polys->begin_vertices >= 3)
3146                 VMPolygons_Store(polys);
3147         else
3148                 VM_Warning("VM_CL_R_PolygonEnd: %i vertices isn't a good choice\n", polys->begin_vertices);
3149 }
3150
3151 static vmpolygons_t debugPolys;
3152
3153 void Debug_PolygonBegin(const char *picname, int drawflag)
3154 {
3155         if(!debugPolys.initialized)
3156                 VM_InitPolygons(&debugPolys);
3157         if(debugPolys.begin_active)
3158         {
3159                 Con_Printf("Debug_PolygonBegin: called twice without Debug_PolygonEnd after first\n");
3160                 return;
3161         }
3162         debugPolys.begin_texture = picname[0] ? Draw_CachePic (picname)->tex : r_texture_white;
3163         debugPolys.begin_drawflag = drawflag;
3164         debugPolys.begin_vertices = 0;
3165         debugPolys.begin_active = true;
3166 }
3167
3168 void Debug_PolygonVertex(float x, float y, float z, float s, float t, float r, float g, float b, float a)
3169 {
3170         if(!debugPolys.begin_active)
3171         {
3172                 Con_Printf("Debug_PolygonVertex: Debug_PolygonBegin wasn't called\n");
3173                 return;
3174         }
3175
3176         if(debugPolys.begin_vertices > VMPOLYGONS_MAXPOINTS)
3177         {
3178                 Con_Printf("Debug_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
3179                 return;
3180         }
3181
3182         debugPolys.begin_vertex[debugPolys.begin_vertices][0] = x;
3183         debugPolys.begin_vertex[debugPolys.begin_vertices][1] = y;
3184         debugPolys.begin_vertex[debugPolys.begin_vertices][2] = z;
3185         debugPolys.begin_texcoord[debugPolys.begin_vertices][0] = s;
3186         debugPolys.begin_texcoord[debugPolys.begin_vertices][1] = t;
3187         debugPolys.begin_color[debugPolys.begin_vertices][0] = r;
3188         debugPolys.begin_color[debugPolys.begin_vertices][1] = g;
3189         debugPolys.begin_color[debugPolys.begin_vertices][2] = b;
3190         debugPolys.begin_color[debugPolys.begin_vertices][3] = a;
3191         debugPolys.begin_vertices++;
3192 }
3193
3194 void Debug_PolygonEnd(void)
3195 {
3196         if (!debugPolys.begin_active)
3197         {
3198                 Con_Printf("Debug_PolygonEnd: Debug_PolygonBegin wasn't called\n");
3199                 return;
3200         }
3201         debugPolys.begin_active = false;
3202         if (debugPolys.begin_vertices >= 3)
3203                 VMPolygons_Store(&debugPolys);
3204         else
3205                 Con_Printf("Debug_PolygonEnd: %i vertices isn't a good choice\n", debugPolys.begin_vertices);
3206 }
3207
3208 /*
3209 =============
3210 CL_CheckBottom
3211
3212 Returns false if any part of the bottom of the entity is off an edge that
3213 is not a staircase.
3214
3215 =============
3216 */
3217 qboolean CL_CheckBottom (prvm_edict_t *ent)
3218 {
3219         vec3_t  mins, maxs, start, stop;
3220         trace_t trace;
3221         int             x, y;
3222         float   mid, bottom;
3223
3224         VectorAdd (ent->fields.client->origin, ent->fields.client->mins, mins);
3225         VectorAdd (ent->fields.client->origin, ent->fields.client->maxs, maxs);
3226
3227 // if all of the points under the corners are solid world, don't bother
3228 // with the tougher checks
3229 // the corners must be within 16 of the midpoint
3230         start[2] = mins[2] - 1;
3231         for     (x=0 ; x<=1 ; x++)
3232                 for     (y=0 ; y<=1 ; y++)
3233                 {
3234                         start[0] = x ? maxs[0] : mins[0];
3235                         start[1] = y ? maxs[1] : mins[1];
3236                         if (!(CL_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
3237                                 goto realcheck;
3238                 }
3239
3240         return true;            // we got out easy
3241
3242 realcheck:
3243 //
3244 // check it for real...
3245 //
3246         start[2] = mins[2];
3247
3248 // the midpoint must be within 16 of the bottom
3249         start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
3250         start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
3251         stop[2] = start[2] - 2*sv_stepheight.value;
3252         trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true);
3253
3254         if (trace.fraction == 1.0)
3255                 return false;
3256         mid = bottom = trace.endpos[2];
3257
3258 // the corners must be within 16 of the midpoint
3259         for     (x=0 ; x<=1 ; x++)
3260                 for     (y=0 ; y<=1 ; y++)
3261                 {
3262                         start[0] = stop[0] = x ? maxs[0] : mins[0];
3263                         start[1] = stop[1] = y ? maxs[1] : mins[1];
3264
3265                         trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true);
3266
3267                         if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
3268                                 bottom = trace.endpos[2];
3269                         if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
3270                                 return false;
3271                 }
3272
3273         return true;
3274 }
3275
3276 /*
3277 =============
3278 CL_movestep
3279
3280 Called by monster program code.
3281 The move will be adjusted for slopes and stairs, but if the move isn't
3282 possible, no move is done and false is returned
3283 =============
3284 */
3285 qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean settrace)
3286 {
3287         float           dz;
3288         vec3_t          oldorg, neworg, end, traceendpos;
3289         trace_t         trace;
3290         int                     i, svent;
3291         prvm_edict_t            *enemy;
3292         prvm_eval_t     *val;
3293
3294 // try the move
3295         VectorCopy (ent->fields.client->origin, oldorg);
3296         VectorAdd (ent->fields.client->origin, move, neworg);
3297
3298 // flying monsters don't step up
3299         if ( (int)ent->fields.client->flags & (FL_SWIM | FL_FLY) )
3300         {
3301         // try one move with vertical motion, then one without
3302                 for (i=0 ; i<2 ; i++)
3303                 {
3304                         VectorAdd (ent->fields.client->origin, move, neworg);
3305                         enemy = PRVM_PROG_TO_EDICT(ent->fields.client->enemy);
3306                         if (i == 0 && enemy != prog->edicts)
3307                         {
3308                                 dz = ent->fields.client->origin[2] - PRVM_PROG_TO_EDICT(ent->fields.client->enemy)->fields.client->origin[2];
3309                                 if (dz > 40)
3310                                         neworg[2] -= 8;
3311                                 if (dz < 30)
3312                                         neworg[2] += 8;
3313                         }
3314                         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);
3315                         if (settrace)
3316                                 CL_VM_SetTraceGlobals(&trace, svent);
3317
3318                         if (trace.fraction == 1)
3319                         {
3320                                 VectorCopy(trace.endpos, traceendpos);
3321                                 if (((int)ent->fields.client->flags & FL_SWIM) && !(CL_PointSuperContents(traceendpos) & SUPERCONTENTS_LIQUIDSMASK))
3322                                         return false;   // swim monster left water
3323
3324                                 VectorCopy (traceendpos, ent->fields.client->origin);
3325                                 if (relink)
3326                                         CL_LinkEdict(ent);
3327                                 return true;
3328                         }
3329
3330                         if (enemy == prog->edicts)
3331                                 break;
3332                 }
3333
3334                 return false;
3335         }
3336
3337 // push down from a step height above the wished position
3338         neworg[2] += sv_stepheight.value;
3339         VectorCopy (neworg, end);
3340         end[2] -= sv_stepheight.value*2;
3341
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
3346         if (trace.startsolid)
3347         {
3348                 neworg[2] -= sv_stepheight.value;
3349                 trace = CL_TraceBox(neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
3350                 if (settrace)
3351                         CL_VM_SetTraceGlobals(&trace, svent);
3352                 if (trace.startsolid)
3353                         return false;
3354         }
3355         if (trace.fraction == 1)
3356         {
3357         // if monster had the ground pulled out, go ahead and fall
3358                 if ( (int)ent->fields.client->flags & FL_PARTIALGROUND )
3359                 {
3360                         VectorAdd (ent->fields.client->origin, move, ent->fields.client->origin);
3361                         if (relink)
3362                                 CL_LinkEdict(ent);
3363                         ent->fields.client->flags = (int)ent->fields.client->flags & ~FL_ONGROUND;
3364                         return true;
3365                 }
3366
3367                 return false;           // walked off an edge
3368         }
3369
3370 // check point traces down for dangling corners
3371         VectorCopy (trace.endpos, ent->fields.client->origin);
3372
3373         if (!CL_CheckBottom (ent))
3374         {
3375                 if ( (int)ent->fields.client->flags & FL_PARTIALGROUND )
3376                 {       // entity had floor mostly pulled out from underneath it
3377                         // and is trying to correct
3378                         if (relink)
3379                                 CL_LinkEdict(ent);
3380                         return true;
3381                 }
3382                 VectorCopy (oldorg, ent->fields.client->origin);
3383                 return false;
3384         }
3385
3386         if ( (int)ent->fields.client->flags & FL_PARTIALGROUND )
3387                 ent->fields.client->flags = (int)ent->fields.client->flags & ~FL_PARTIALGROUND;
3388
3389         if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.groundentity)))
3390                 val->edict = PRVM_EDICT_TO_PROG(trace.ent);
3391
3392 // the move is ok
3393         if (relink)
3394                 CL_LinkEdict(ent);
3395         return true;
3396 }
3397
3398 /*
3399 ===============
3400 VM_CL_walkmove
3401
3402 float(float yaw, float dist[, settrace]) walkmove
3403 ===============
3404 */
3405 static void VM_CL_walkmove (void)
3406 {
3407         prvm_edict_t    *ent;
3408         float   yaw, dist;
3409         vec3_t  move;
3410         mfunction_t     *oldf;
3411         int     oldself;
3412         qboolean        settrace;
3413
3414         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_walkmove);
3415
3416         // assume failure if it returns early
3417         PRVM_G_FLOAT(OFS_RETURN) = 0;
3418
3419         ent = PRVM_PROG_TO_EDICT(prog->globals.client->self);
3420         if (ent == prog->edicts)
3421         {
3422                 VM_Warning("walkmove: can not modify world entity\n");
3423                 return;
3424         }
3425         if (ent->priv.server->free)
3426         {
3427                 VM_Warning("walkmove: can not modify free entity\n");
3428                 return;
3429         }
3430         yaw = PRVM_G_FLOAT(OFS_PARM0);
3431         dist = PRVM_G_FLOAT(OFS_PARM1);
3432         settrace = prog->argc >= 3 && PRVM_G_FLOAT(OFS_PARM2);
3433
3434         if ( !( (int)ent->fields.client->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
3435                 return;
3436
3437         yaw = yaw*M_PI*2 / 360;
3438
3439         move[0] = cos(yaw)*dist;
3440         move[1] = sin(yaw)*dist;
3441         move[2] = 0;
3442
3443 // save program state, because CL_movestep may call other progs
3444         oldf = prog->xfunction;
3445         oldself = prog->globals.client->self;
3446
3447         PRVM_G_FLOAT(OFS_RETURN) = CL_movestep(ent, move, true, false, settrace);
3448
3449
3450 // restore program state
3451         prog->xfunction = oldf;
3452         prog->globals.client->self = oldself;
3453 }
3454
3455 /*
3456 ===============
3457 VM_CL_serverkey
3458
3459 string(string key) serverkey
3460 ===============
3461 */
3462 void VM_CL_serverkey(void)
3463 {
3464         char string[VM_STRINGTEMP_LENGTH];
3465         VM_SAFEPARMCOUNT(1, VM_CL_serverkey);
3466         InfoString_GetValue(cl.qw_serverinfo, PRVM_G_STRING(OFS_PARM0), string, sizeof(string));
3467         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
3468 }
3469
3470 /*
3471 =================
3472 VM_CL_checkpvs
3473
3474 Checks if an entity is in a point's PVS.
3475 Should be fast but can be inexact.
3476
3477 float checkpvs(vector viewpos, entity viewee) = #240;
3478 =================
3479 */
3480 static void VM_CL_checkpvs (void)
3481 {
3482         vec3_t viewpos;
3483         prvm_edict_t *viewee;
3484         vec3_t mi, ma;
3485 #if 1
3486         unsigned char *pvs;
3487 #else
3488         int fatpvsbytes;
3489         unsigned char fatpvs[MAX_MAP_LEAFS/8];
3490 #endif
3491
3492         VM_SAFEPARMCOUNT(2, VM_SV_checkpvs);
3493         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), viewpos);
3494         viewee = PRVM_G_EDICT(OFS_PARM1);
3495
3496         if(viewee->priv.required->free)
3497         {
3498                 VM_Warning("checkpvs: can not check free entity\n");
3499                 PRVM_G_FLOAT(OFS_RETURN) = 4;
3500                 return;
3501         }
3502
3503         VectorAdd(viewee->fields.server->origin, viewee->fields.server->mins, mi);
3504         VectorAdd(viewee->fields.server->origin, viewee->fields.server->maxs, ma);
3505
3506 #if 1
3507         if(!sv.worldmodel->brush.GetPVS || !sv.worldmodel->brush.BoxTouchingPVS)
3508         {
3509                 // no PVS support on this worldmodel... darn
3510                 PRVM_G_FLOAT(OFS_RETURN) = 3;
3511                 return;
3512         }
3513         pvs = sv.worldmodel->brush.GetPVS(sv.worldmodel, viewpos);
3514         if(!pvs)
3515         {
3516                 // viewpos isn't in any PVS... darn
3517                 PRVM_G_FLOAT(OFS_RETURN) = 2;
3518                 return;
3519         }
3520         PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, pvs, mi, ma);
3521 #else
3522         // using fat PVS like FTEQW does (slow)
3523         if(!sv.worldmodel->brush.FatPVS || !sv.worldmodel->brush.BoxTouchingPVS)
3524         {
3525                 // no PVS support on this worldmodel... darn
3526                 PRVM_G_FLOAT(OFS_RETURN) = 3;
3527                 return;
3528         }
3529         fatpvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, viewpos, 8, fatpvs, sizeof(fatpvs), false);
3530         if(!fatpvsbytes)
3531         {
3532                 // viewpos isn't in any PVS... darn
3533                 PRVM_G_FLOAT(OFS_RETURN) = 2;
3534                 return;
3535         }
3536         PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, fatpvs, mi, ma);
3537 #endif
3538 }
3539
3540 // #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.
3541 static void VM_CL_skel_create(void)
3542 {
3543         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
3544         dp_model_t *model = CL_GetModelByIndex(modelindex);
3545         skeleton_t *skeleton;
3546         int i;
3547         PRVM_G_FLOAT(OFS_RETURN) = 0;
3548         if (!model || !model->num_bones)
3549                 return;
3550         for (i = 0;i < MAX_EDICTS;i++)
3551                 if (!prog->skeletons[i])
3552                         break;
3553         if (i == MAX_EDICTS)
3554                 return;
3555         prog->skeletons[i] = skeleton = Mem_Alloc(cls.levelmempool, sizeof(skeleton_t) + model->num_bones * sizeof(matrix4x4_t));
3556         PRVM_G_FLOAT(OFS_RETURN) = i + 1;
3557         skeleton->model = model;
3558         skeleton->relativetransforms = (matrix4x4_t *)(skeleton+1);
3559         // initialize to identity matrices
3560         for (i = 0;i < skeleton->model->num_bones;i++)
3561                 skeleton->relativetransforms[i] = identitymatrix;
3562 }
3563
3564 // #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
3565 static void VM_CL_skel_build(void)
3566 {
3567         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3568         skeleton_t *skeleton;
3569         prvm_edict_t *ed = PRVM_G_EDICT(OFS_PARM1);
3570         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM2);
3571         float retainfrac = PRVM_G_FLOAT(OFS_PARM3);
3572         int firstbone = PRVM_G_FLOAT(OFS_PARM4) - 1;
3573         int lastbone = PRVM_G_FLOAT(OFS_PARM5) - 1;
3574         dp_model_t *model = CL_GetModelByIndex(modelindex);
3575         float blendfrac;
3576         int numblends;
3577         int bonenum;
3578         int blendindex;
3579         framegroupblend_t framegroupblend[MAX_FRAMEGROUPBLENDS];
3580         frameblend_t frameblend[MAX_FRAMEBLENDS];
3581         matrix4x4_t blendedmatrix;
3582         matrix4x4_t matrix;
3583         PRVM_G_FLOAT(OFS_RETURN) = 0;
3584         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3585                 return;
3586         firstbone = max(0, firstbone);
3587         lastbone = min(lastbone, model->num_bones - 1);
3588         lastbone = min(lastbone, skeleton->model->num_bones - 1);
3589         VM_GenerateFrameGroupBlend(framegroupblend, ed);
3590         VM_FrameBlendFromFrameGroupBlend(frameblend, framegroupblend, model);
3591         blendfrac = 1.0f - retainfrac;
3592         for (numblends = 0;numblends < MAX_FRAMEBLENDS && frameblend[numblends].lerp;numblends++)
3593                 frameblend[numblends].lerp *= blendfrac;
3594         for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
3595         {
3596                 memset(&blendedmatrix, 0, sizeof(blendedmatrix));
3597                 Matrix4x4_Accumulate(&blendedmatrix, &skeleton->relativetransforms[bonenum], retainfrac);
3598                 for (blendindex = 0;blendindex < numblends;blendindex++)
3599                 {
3600                         Matrix4x4_FromBonePose6s(&matrix, model->num_posescale, model->data_poses6s + 6 * (frameblend[blendindex].subframe * model->num_bones + bonenum));
3601                         Matrix4x4_Accumulate(&blendedmatrix, &matrix, frameblend[blendindex].lerp);
3602                 }
3603                 skeleton->relativetransforms[bonenum] = blendedmatrix;
3604         }
3605         PRVM_G_FLOAT(OFS_RETURN) = skeletonindex + 1;
3606 }
3607
3608 // #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
3609 static void VM_CL_skel_get_numbones(void)
3610 {
3611         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3612         skeleton_t *skeleton;
3613         PRVM_G_FLOAT(OFS_RETURN) = 0;
3614         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3615                 return;
3616         PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->num_bones;
3617 }
3618
3619 // #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
3620 static void VM_CL_skel_get_bonename(void)
3621 {
3622         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3623         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3624         skeleton_t *skeleton;
3625         PRVM_G_INT(OFS_RETURN) = 0;
3626         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3627                 return;
3628         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3629                 return;
3630         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(skeleton->model->data_bones[bonenum].name);
3631 }
3632
3633 // #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)
3634 static void VM_CL_skel_get_boneparent(void)
3635 {
3636         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3637         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3638         skeleton_t *skeleton;
3639         PRVM_G_FLOAT(OFS_RETURN) = 0;
3640         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3641                 return;
3642         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3643                 return;
3644         PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->data_bones[bonenum].parent + 1;
3645 }
3646
3647 // #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
3648 static void VM_CL_skel_find_bone(void)
3649 {
3650         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3651         const char *tagname = PRVM_G_STRING(OFS_PARM1);
3652         skeleton_t *skeleton;
3653         PRVM_G_FLOAT(OFS_RETURN) = 0;
3654         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3655                 return;
3656         PRVM_G_FLOAT(OFS_RETURN) = Mod_Alias_GetTagIndexForName(skeleton->model, 0, tagname) + 1;
3657 }
3658
3659 // #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)
3660 static void VM_CL_skel_get_bonerel(void)
3661 {
3662         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3663         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3664         skeleton_t *skeleton;
3665         matrix4x4_t matrix;
3666         vec3_t forward, left, up, origin;
3667         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
3668         VectorClear(prog->globals.client->v_forward);
3669         VectorClear(prog->globals.client->v_right);
3670         VectorClear(prog->globals.client->v_up);
3671         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3672                 return;
3673         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3674                 return;
3675         matrix = skeleton->relativetransforms[bonenum];
3676         Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
3677         VectorCopy(forward, prog->globals.client->v_forward);
3678         VectorNegate(left, prog->globals.client->v_right);
3679         VectorCopy(up, prog->globals.client->v_up);
3680         VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
3681 }
3682
3683 // #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)
3684 static void VM_CL_skel_get_boneabs(void)
3685 {
3686         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3687         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3688         skeleton_t *skeleton;
3689         matrix4x4_t matrix;
3690         matrix4x4_t temp;
3691         vec3_t forward, left, up, origin;
3692         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
3693         VectorClear(prog->globals.client->v_forward);
3694         VectorClear(prog->globals.client->v_right);
3695         VectorClear(prog->globals.client->v_up);
3696         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3697                 return;
3698         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3699                 return;
3700         matrix = skeleton->relativetransforms[bonenum];
3701         // convert to absolute
3702         while ((bonenum = skeleton->model->data_bones[bonenum].parent) >= 0)
3703         {
3704                 temp = matrix;
3705                 Matrix4x4_Concat(&matrix, &skeleton->relativetransforms[bonenum], &temp);
3706         }
3707         Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
3708         VectorCopy(forward, prog->globals.client->v_forward);
3709         VectorNegate(left, prog->globals.client->v_right);
3710         VectorCopy(up, prog->globals.client->v_up);
3711         VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
3712 }
3713
3714 // #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)
3715 static void VM_CL_skel_set_bone(void)
3716 {
3717         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3718         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3719         vec3_t forward, left, up, origin;
3720         skeleton_t *skeleton;
3721         matrix4x4_t matrix;
3722         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3723                 return;
3724         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3725                 return;
3726         VectorCopy(prog->globals.client->v_forward, forward);
3727         VectorNegate(prog->globals.client->v_right, left);
3728         VectorCopy(prog->globals.client->v_up, up);
3729         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
3730         Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
3731         skeleton->relativetransforms[bonenum] = matrix;
3732 }
3733
3734 // #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)
3735 static void VM_CL_skel_mul_bone(void)
3736 {
3737         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3738         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3739         vec3_t forward, left, up, origin;
3740         skeleton_t *skeleton;
3741         matrix4x4_t matrix;
3742         matrix4x4_t temp;
3743         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3744                 return;
3745         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3746                 return;
3747         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
3748         VectorCopy(prog->globals.client->v_forward, forward);
3749         VectorNegate(prog->globals.client->v_right, left);
3750         VectorCopy(prog->globals.client->v_up, up);
3751         Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
3752         temp = skeleton->relativetransforms[bonenum];
3753         Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
3754 }
3755
3756 // #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)
3757 static void VM_CL_skel_mul_bones(void)
3758 {
3759         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3760         int firstbone = PRVM_G_FLOAT(OFS_PARM1) - 1;
3761         int lastbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
3762         int bonenum;
3763         vec3_t forward, left, up, origin;
3764         skeleton_t *skeleton;
3765         matrix4x4_t matrix;
3766         matrix4x4_t temp;
3767         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3768                 return;
3769         VectorCopy(PRVM_G_VECTOR(OFS_PARM3), origin);
3770         VectorCopy(prog->globals.client->v_forward, forward);
3771         VectorNegate(prog->globals.client->v_right, left);
3772         VectorCopy(prog->globals.client->v_up, up);
3773         Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
3774         firstbone = max(0, firstbone);
3775         lastbone = min(lastbone, skeleton->model->num_bones - 1);
3776         for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
3777         {
3778                 temp = skeleton->relativetransforms[bonenum];
3779                 Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
3780         }
3781 }
3782
3783 // #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
3784 static void VM_CL_skel_copybones(void)
3785 {
3786         int skeletonindexdst = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3787         int skeletonindexsrc = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3788         int firstbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
3789         int lastbone = PRVM_G_FLOAT(OFS_PARM3) - 1;
3790         int bonenum;
3791         skeleton_t *skeletondst;
3792         skeleton_t *skeletonsrc;
3793         if (skeletonindexdst < 0 || skeletonindexdst >= MAX_EDICTS || !(skeletondst = prog->skeletons[skeletonindexdst]))
3794                 return;
3795         if (skeletonindexsrc < 0 || skeletonindexsrc >= MAX_EDICTS || !(skeletonsrc = prog->skeletons[skeletonindexsrc]))
3796                 return;
3797         firstbone = max(0, firstbone);
3798         lastbone = min(lastbone, skeletondst->model->num_bones - 1);
3799         lastbone = min(lastbone, skeletonsrc->model->num_bones - 1);
3800         for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
3801                 skeletondst->relativetransforms[bonenum] = skeletonsrc->relativetransforms[bonenum];
3802 }
3803
3804 // #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)
3805 static void VM_CL_skel_delete(void)
3806 {
3807         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3808         skeleton_t *skeleton;
3809         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3810                 return;
3811         Mem_Free(skeleton);
3812         prog->skeletons[skeletonindex] = NULL;
3813 }
3814
3815 // #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
3816 static void VM_CL_frameforname(void)
3817 {
3818         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
3819         dp_model_t *model = CL_GetModelByIndex(modelindex);
3820         const char *name = PRVM_G_STRING(OFS_PARM1);
3821         int i;
3822         PRVM_G_FLOAT(OFS_RETURN) = -1;
3823         if (!model || !model->animscenes)
3824                 return;
3825         for (i = 0;i < model->numframes;i++)
3826         {
3827                 if (!strcasecmp(model->animscenes[i].name, name))
3828                 {
3829                         PRVM_G_FLOAT(OFS_RETURN) = i;
3830                         break;
3831                 }
3832         }
3833 }
3834
3835 // #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.
3836 static void VM_CL_frameduration(void)
3837 {
3838         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
3839         dp_model_t *model = CL_GetModelByIndex(modelindex);
3840         int framenum = (int)PRVM_G_FLOAT(OFS_PARM1);
3841         PRVM_G_FLOAT(OFS_RETURN) = 0;
3842         if (!model || !model->animscenes || framenum < 0 || framenum >= model->numframes)
3843                 return;
3844         if (model->animscenes[framenum].framerate)
3845                 PRVM_G_FLOAT(OFS_RETURN) = model->animscenes[framenum].framecount / model->animscenes[framenum].framerate;
3846 }
3847
3848 //============================================================================
3849
3850 // To create a almost working builtin file from this replace:
3851 // "^NULL.*" with ""
3852 // "^{.*//.*}:Wh\(.*\)" with "\1"
3853 // "\:" with "//"
3854 // "^.*//:Wh{\#:d*}:Wh{.*}" with "\2 = \1;"
3855 // "\n\n+" with "\n\n"
3856
3857 prvm_builtin_t vm_cl_builtins[] = {
3858 NULL,                                                   // #0 NULL function (not callable) (QUAKE)
3859 VM_CL_makevectors,                              // #1 void(vector ang) makevectors (QUAKE)
3860 VM_CL_setorigin,                                // #2 void(entity e, vector o) setorigin (QUAKE)
3861 VM_CL_setmodel,                                 // #3 void(entity e, string m) setmodel (QUAKE)
3862 VM_CL_setsize,                                  // #4 void(entity e, vector min, vector max) setsize (QUAKE)
3863 NULL,                                                   // #5 void(entity e, vector min, vector max) setabssize (QUAKE)
3864 VM_break,                                               // #6 void() break (QUAKE)
3865 VM_random,                                              // #7 float() random (QUAKE)
3866 VM_CL_sound,                                    // #8 void(entity e, float chan, string samp) sound (QUAKE)
3867 VM_normalize,                                   // #9 vector(vector v) normalize (QUAKE)
3868 VM_error,                                               // #10 void(string e) error (QUAKE)
3869 VM_objerror,                                    // #11 void(string e) objerror (QUAKE)
3870 VM_vlen,                                                // #12 float(vector v) vlen (QUAKE)
3871 VM_vectoyaw,                                    // #13 float(vector v) vectoyaw (QUAKE)
3872 VM_CL_spawn,                                    // #14 entity() spawn (QUAKE)
3873 VM_remove,                                              // #15 void(entity e) remove (QUAKE)
3874 VM_CL_traceline,                                // #16 void(vector v1, vector v2, float tryents, entity ignoreentity) traceline (QUAKE)
3875 NULL,                                                   // #17 entity() checkclient (QUAKE)
3876 VM_find,                                                // #18 entity(entity start, .string fld, string match) find (QUAKE)
3877 VM_precache_sound,                              // #19 void(string s) precache_sound (QUAKE)
3878 VM_CL_precache_model,                   // #20 void(string s) precache_model (QUAKE)
3879 NULL,                                                   // #21 void(entity client, string s, ...) stuffcmd (QUAKE)
3880 VM_CL_findradius,                               // #22 entity(vector org, float rad) findradius (QUAKE)
3881 NULL,                                                   // #23 void(string s, ...) bprint (QUAKE)
3882 NULL,                                                   // #24 void(entity client, string s, ...) sprint (QUAKE)
3883 VM_dprint,                                              // #25 void(string s, ...) dprint (QUAKE)
3884 VM_ftos,                                                // #26 string(float f) ftos (QUAKE)
3885 VM_vtos,                                                // #27 string(vector v) vtos (QUAKE)
3886 VM_coredump,                                    // #28 void() coredump (QUAKE)
3887 VM_traceon,                                             // #29 void() traceon (QUAKE)
3888 VM_traceoff,                                    // #30 void() traceoff (QUAKE)
3889 VM_eprint,                                              // #31 void(entity e) eprint (QUAKE)
3890 VM_CL_walkmove,                                 // #32 float(float yaw, float dist[, float settrace]) walkmove (QUAKE)
3891 NULL,                                                   // #33 (QUAKE)
3892 VM_CL_droptofloor,                              // #34 float() droptofloor (QUAKE)
3893 VM_CL_lightstyle,                               // #35 void(float style, string value) lightstyle (QUAKE)
3894 VM_rint,                                                // #36 float(float v) rint (QUAKE)
3895 VM_floor,                                               // #37 float(float v) floor (QUAKE)
3896 VM_ceil,                                                // #38 float(float v) ceil (QUAKE)
3897 NULL,                                                   // #39 (QUAKE)
3898 VM_CL_checkbottom,                              // #40 float(entity e) checkbottom (QUAKE)
3899 VM_CL_pointcontents,                    // #41 float(vector v) pointcontents (QUAKE)
3900 NULL,                                                   // #42 (QUAKE)
3901 VM_fabs,                                                // #43 float(float f) fabs (QUAKE)
3902 NULL,                                                   // #44 vector(entity e, float speed) aim (QUAKE)
3903 VM_cvar,                                                // #45 float(string s) cvar (QUAKE)
3904 VM_localcmd,                                    // #46 void(string s) localcmd (QUAKE)
3905 VM_nextent,                                             // #47 entity(entity e) nextent (QUAKE)
3906 VM_CL_particle,                                 // #48 void(vector o, vector d, float color, float count) particle (QUAKE)
3907 VM_changeyaw,                                   // #49 void() ChangeYaw (QUAKE)
3908 NULL,                                                   // #50 (QUAKE)
3909 VM_vectoangles,                                 // #51 vector(vector v) vectoangles (QUAKE)
3910 NULL,                                                   // #52 void(float to, float f) WriteByte (QUAKE)
3911 NULL,                                                   // #53 void(float to, float f) WriteChar (QUAKE)
3912 NULL,                                                   // #54 void(float to, float f) WriteShort (QUAKE)
3913 NULL,                                                   // #55 void(float to, float f) WriteLong (QUAKE)
3914 NULL,                                                   // #56 void(float to, float f) WriteCoord (QUAKE)
3915 NUL