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