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