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