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