cbb31b42eccf63961cf031b02573968b71cda183
[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
25 r_refdef_view_t csqc_original_r_refdef_view;
26 r_refdef_view_t csqc_main_r_refdef_view;
27
28 // #1 void(vector ang) makevectors
29 static void VM_CL_makevectors (prvm_prog_t *prog)
30 {
31         vec3_t angles, forward, right, up;
32         VM_SAFEPARMCOUNT(1, VM_CL_makevectors);
33         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), angles);
34         AngleVectors(angles, forward, right, up);
35         VectorCopy(forward, PRVM_clientglobalvector(v_forward));
36         VectorCopy(right, PRVM_clientglobalvector(v_right));
37         VectorCopy(up, PRVM_clientglobalvector(v_up));
38 }
39
40 // #2 void(entity e, vector o) setorigin
41 static void VM_CL_setorigin (prvm_prog_t *prog)
42 {
43         prvm_edict_t    *e;
44         prvm_vec_t      *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(prog, "setorigin: can not modify world entity\n");
51                 return;
52         }
53         if (e->free)
54         {
55                 VM_Warning(prog, "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         if(e->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
61                 e->priv.required->mark = PRVM_EDICT_MARK_SETORIGIN_CAUGHT;
62         CL_LinkEdict(e);
63 }
64
65 static void SetMinMaxSizePRVM (prvm_prog_t *prog, prvm_edict_t *e, prvm_vec_t *min, prvm_vec_t *max)
66 {
67         int             i;
68
69         for (i=0 ; i<3 ; i++)
70                 if (min[i] > max[i])
71                         prog->error_cmd("SetMinMaxSize: backwards mins/maxs");
72
73         // set derived values
74         VectorCopy (min, PRVM_clientedictvector(e, mins));
75         VectorCopy (max, PRVM_clientedictvector(e, maxs));
76         VectorSubtract (max, min, PRVM_clientedictvector(e, size));
77
78         CL_LinkEdict (e);
79 }
80
81 static void SetMinMaxSize (prvm_prog_t *prog, prvm_edict_t *e, const vec_t *min, const vec_t *max)
82 {
83         prvm_vec3_t mins, maxs;
84         VectorCopy(min, mins);
85         VectorCopy(max, maxs);
86         SetMinMaxSizePRVM(prog, e, mins, maxs);
87 }
88
89 // #3 void(entity e, string m) setmodel
90 static void VM_CL_setmodel (prvm_prog_t *prog)
91 {
92         prvm_edict_t    *e;
93         const char              *m;
94         model_t *mod;
95         int                             i;
96
97         VM_SAFEPARMCOUNT(2, VM_CL_setmodel);
98
99         e = PRVM_G_EDICT(OFS_PARM0);
100         PRVM_clientedictfloat(e, modelindex) = 0;
101         PRVM_clientedictstring(e, model) = 0;
102
103         m = PRVM_G_STRING(OFS_PARM1);
104         mod = NULL;
105         for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
106         {
107                 if (!strcmp(cl.csqc_model_precache[i]->name, m))
108                 {
109                         mod = cl.csqc_model_precache[i];
110                         PRVM_clientedictstring(e, model) = PRVM_SetEngineString(prog, mod->name);
111                         PRVM_clientedictfloat(e, modelindex) = -(i+1);
112                         break;
113                 }
114         }
115
116         if( !mod ) {
117                 for (i = 0;i < MAX_MODELS;i++)
118                 {
119                         mod = cl.model_precache[i];
120                         if (mod && !strcmp(mod->name, m))
121                         {
122                                 PRVM_clientedictstring(e, model) = PRVM_SetEngineString(prog, mod->name);
123                                 PRVM_clientedictfloat(e, modelindex) = i;
124                                 break;
125                         }
126                 }
127         }
128
129         if( mod ) {
130                 // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
131                 // LadyHavoc: erm you broke it by commenting this out - setmodel must do setsize or else the qc can't find out the model size, and ssqc does this by necessity, consistency.
132                 SetMinMaxSize (prog, e, mod->normalmins, mod->normalmaxs);
133         }
134         else
135         {
136                 SetMinMaxSize (prog, e, vec3_origin, vec3_origin);
137                 VM_Warning(prog, "setmodel: model '%s' not precached\n", m);
138         }
139 }
140
141 // #4 void(entity e, vector min, vector max) setsize
142 static void VM_CL_setsize (prvm_prog_t *prog)
143 {
144         prvm_edict_t    *e;
145         vec3_t          mins, maxs;
146         VM_SAFEPARMCOUNT(3, VM_CL_setsize);
147
148         e = PRVM_G_EDICT(OFS_PARM0);
149         if (e == prog->edicts)
150         {
151                 VM_Warning(prog, "setsize: can not modify world entity\n");
152                 return;
153         }
154         if (e->free)
155         {
156                 VM_Warning(prog, "setsize: can not modify free entity\n");
157                 return;
158         }
159         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), mins);
160         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), maxs);
161
162         SetMinMaxSize( prog, e, mins, maxs );
163
164         CL_LinkEdict(e);
165 }
166
167 // #8 void(entity e, float chan, string samp, float volume, float atten[, float pitchchange[, float flags]]) sound
168 static void VM_CL_sound (prvm_prog_t *prog)
169 {
170         const char                      *sample;
171         int                                     channel;
172         prvm_edict_t            *entity;
173         float                           fvolume;
174         float                           attenuation;
175         float pitchchange;
176         float                           startposition;
177         int flags;
178         vec3_t                          org;
179
180         VM_SAFEPARMCOUNTRANGE(5, 7, VM_CL_sound);
181
182         entity = PRVM_G_EDICT(OFS_PARM0);
183         channel = (int)PRVM_G_FLOAT(OFS_PARM1);
184         sample = PRVM_G_STRING(OFS_PARM2);
185         fvolume = PRVM_G_FLOAT(OFS_PARM3);
186         attenuation = PRVM_G_FLOAT(OFS_PARM4);
187
188         if (fvolume < 0 || fvolume > 1)
189         {
190                 VM_Warning(prog, "VM_CL_sound: volume must be in range 0-1\n");
191                 return;
192         }
193
194         if (attenuation < 0 || attenuation > 4)
195         {
196                 VM_Warning(prog, "VM_CL_sound: attenuation must be in range 0-4\n");
197                 return;
198         }
199
200         if (prog->argc < 6)
201                 pitchchange = 0;
202         else
203                 pitchchange = PRVM_G_FLOAT(OFS_PARM5);
204
205         if (prog->argc < 7)
206                 flags = 0;
207         else
208         {
209                 // LadyHavoc: we only let the qc set certain flags, others are off-limits
210                 flags = (int)PRVM_G_FLOAT(OFS_PARM6) & (CHANNELFLAG_RELIABLE | CHANNELFLAG_FORCELOOP | CHANNELFLAG_PAUSED | CHANNELFLAG_FULLVOLUME);
211         }
212
213         // sound_starttime exists instead of sound_startposition because in a
214         // networking sense you might not know when something is being received,
215         // so making sounds match up in sync would be impossible if relative
216         // position was sent
217         if (PRVM_clientglobalfloat(sound_starttime))
218                 startposition = cl.time - PRVM_clientglobalfloat(sound_starttime);
219         else
220                 startposition = 0;
221
222         if (!IS_CHAN(channel))
223         {
224                 VM_Warning(prog, "VM_CL_sound: channel must be in range 0-127\n");
225                 return;
226         }
227
228         CL_VM_GetEntitySoundOrigin(MAX_EDICTS + PRVM_NUM_FOR_EDICT(entity), org);
229         S_StartSound_StartPosition_Flags(MAX_EDICTS + PRVM_NUM_FOR_EDICT(entity), channel, S_FindName(sample), org, fvolume, attenuation, startposition, flags, pitchchange > 0.0f ? pitchchange * 0.01f : 1.0f);
230 }
231
232 // #483 void(vector origin, string sample, float volume, float attenuation) pointsound
233 static void VM_CL_pointsound(prvm_prog_t *prog)
234 {
235         const char                      *sample;
236         float                           fvolume;
237         float                           attenuation;
238         vec3_t                          org;
239
240         VM_SAFEPARMCOUNT(4, VM_CL_pointsound);
241
242         VectorCopy( PRVM_G_VECTOR(OFS_PARM0), org);
243         sample = PRVM_G_STRING(OFS_PARM1);
244         fvolume = PRVM_G_FLOAT(OFS_PARM2);
245         attenuation = PRVM_G_FLOAT(OFS_PARM3);
246
247         if (fvolume < 0 || fvolume > 1)
248         {
249                 VM_Warning(prog, "VM_CL_pointsound: volume must be in range 0-1\n");
250                 return;
251         }
252
253         if (attenuation < 0 || attenuation > 4)
254         {
255                 VM_Warning(prog, "VM_CL_pointsound: attenuation must be in range 0-4\n");
256                 return;
257         }
258
259         // Send World Entity as Entity to Play Sound (for CSQC, that is MAX_EDICTS)
260         S_StartSound(MAX_EDICTS, 0, S_FindName(sample), org, fvolume, attenuation);
261 }
262
263 // #14 entity() spawn
264 static void VM_CL_spawn (prvm_prog_t *prog)
265 {
266         prvm_edict_t *ed;
267         ed = PRVM_ED_Alloc(prog);
268         VM_RETURN_EDICT(ed);
269 }
270
271 static void CL_VM_SetTraceGlobals(prvm_prog_t *prog, const trace_t *trace, int svent)
272 {
273         VM_SetTraceGlobals(prog, trace);
274         PRVM_clientglobalfloat(trace_networkentity) = svent;
275 }
276
277 #define CL_HitNetworkBrushModels(move) !((move) == MOVE_WORLDONLY)
278 #define CL_HitNetworkPlayers(move)     !((move) == MOVE_WORLDONLY || (move) == MOVE_NOMONSTERS)
279
280 // #16 void(vector v1, vector v2, float movetype, entity ignore) traceline
281 static void VM_CL_traceline (prvm_prog_t *prog)
282 {
283         vec3_t  v1, v2;
284         trace_t trace;
285         int             move, svent;
286         prvm_edict_t    *ent;
287
288 //      R_TimeReport("pretraceline");
289
290         VM_SAFEPARMCOUNTRANGE(4, 4, VM_CL_traceline);
291
292         prog->xfunction->builtinsprofile += 30;
293
294         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), v1);
295         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), v2);
296         move = (int)PRVM_G_FLOAT(OFS_PARM2);
297         ent = PRVM_G_EDICT(OFS_PARM3);
298
299         if (VEC_IS_NAN(v1[0]) || VEC_IS_NAN(v1[1]) || VEC_IS_NAN(v1[2]) || VEC_IS_NAN(v2[0]) || VEC_IS_NAN(v2[1]) || VEC_IS_NAN(v2[2]))
300                 prog->error_cmd("%s: NAN errors detected in traceline('%f %f %f', '%f %f %f', %i, entity %i)\n", prog->name, v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent));
301
302         trace = CL_TraceLine(v1, v2, move, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendtracelinelength.value, CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true, false);
303
304         CL_VM_SetTraceGlobals(prog, &trace, svent);
305 //      R_TimeReport("traceline");
306 }
307
308 /*
309 =================
310 VM_CL_tracebox
311
312 Used for use tracing and shot targeting
313 Traces are blocked by bbox and exact bsp entityes, and also slide box entities
314 if the tryents flag is set.
315
316 tracebox (vector1, vector mins, vector maxs, vector2, tryents)
317 =================
318 */
319 // LadyHavoc: added this for my own use, VERY useful, similar to traceline
320 static void VM_CL_tracebox (prvm_prog_t *prog)
321 {
322         vec3_t  v1, v2, m1, m2;
323         trace_t trace;
324         int             move, svent;
325         prvm_edict_t    *ent;
326
327 //      R_TimeReport("pretracebox");
328         VM_SAFEPARMCOUNTRANGE(6, 8, VM_CL_tracebox); // allow more parameters for future expansion
329
330         prog->xfunction->builtinsprofile += 30;
331
332         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), v1);
333         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), m1);
334         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), m2);
335         VectorCopy(PRVM_G_VECTOR(OFS_PARM3), v2);
336         move = (int)PRVM_G_FLOAT(OFS_PARM4);
337         ent = PRVM_G_EDICT(OFS_PARM5);
338
339         if (VEC_IS_NAN(v1[0]) || VEC_IS_NAN(v1[1]) || VEC_IS_NAN(v1[2]) || VEC_IS_NAN(v2[0]) || VEC_IS_NAN(v2[1]) || VEC_IS_NAN(v2[2]))
340                 prog->error_cmd("%s: NAN errors detected in tracebox('%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', %i, entity %i)\n", prog->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));
341
342         trace = CL_TraceBox(v1, m1, m2, v2, move, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendtraceboxlength.value, CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true);
343
344         CL_VM_SetTraceGlobals(prog, &trace, svent);
345 //      R_TimeReport("tracebox");
346 }
347
348 static trace_t CL_Trace_Toss (prvm_prog_t *prog, prvm_edict_t *tossent, prvm_edict_t *ignore, int *svent)
349 {
350         int i;
351         float gravity;
352         vec3_t start, end, mins, maxs, move;
353         vec3_t original_origin;
354         vec3_t original_velocity;
355         vec3_t original_angles;
356         vec3_t original_avelocity;
357         trace_t trace;
358
359         VectorCopy(PRVM_clientedictvector(tossent, origin)   , original_origin   );
360         VectorCopy(PRVM_clientedictvector(tossent, velocity) , original_velocity );
361         VectorCopy(PRVM_clientedictvector(tossent, angles)   , original_angles   );
362         VectorCopy(PRVM_clientedictvector(tossent, avelocity), original_avelocity);
363
364         gravity = PRVM_clientedictfloat(tossent, gravity);
365         if (!gravity)
366                 gravity = 1.0f;
367         gravity *= cl.movevars_gravity * 0.05;
368
369         for (i = 0;i < 200;i++) // LadyHavoc: sanity check; never trace more than 10 seconds
370         {
371                 PRVM_clientedictvector(tossent, velocity)[2] -= gravity;
372                 VectorMA (PRVM_clientedictvector(tossent, angles), 0.05, PRVM_clientedictvector(tossent, avelocity), PRVM_clientedictvector(tossent, angles));
373                 VectorScale (PRVM_clientedictvector(tossent, velocity), 0.05, move);
374                 VectorAdd (PRVM_clientedictvector(tossent, origin), move, end);
375                 VectorCopy(PRVM_clientedictvector(tossent, origin), start);
376                 VectorCopy(PRVM_clientedictvector(tossent, mins), mins);
377                 VectorCopy(PRVM_clientedictvector(tossent, maxs), maxs);
378                 trace = CL_TraceBox(start, mins, maxs, end, MOVE_NORMAL, tossent, CL_GenericHitSuperContentsMask(tossent), 0, 0, collision_extendmovelength.value, true, true, NULL, true);
379                 VectorCopy (trace.endpos, PRVM_clientedictvector(tossent, origin));
380
381                 if (trace.fraction < 1)
382                         break;
383         }
384
385         VectorCopy(original_origin   , PRVM_clientedictvector(tossent, origin)   );
386         VectorCopy(original_velocity , PRVM_clientedictvector(tossent, velocity) );
387         VectorCopy(original_angles   , PRVM_clientedictvector(tossent, angles)   );
388         VectorCopy(original_avelocity, PRVM_clientedictvector(tossent, avelocity));
389
390         return trace;
391 }
392
393 static void VM_CL_tracetoss (prvm_prog_t *prog)
394 {
395         trace_t trace;
396         prvm_edict_t    *ent;
397         prvm_edict_t    *ignore;
398         int svent = 0;
399
400         prog->xfunction->builtinsprofile += 600;
401
402         VM_SAFEPARMCOUNT(2, VM_CL_tracetoss);
403
404         ent = PRVM_G_EDICT(OFS_PARM0);
405         if (ent == prog->edicts)
406         {
407                 VM_Warning(prog, "tracetoss: can not use world entity\n");
408                 return;
409         }
410         ignore = PRVM_G_EDICT(OFS_PARM1);
411
412         trace = CL_Trace_Toss (prog, ent, ignore, &svent);
413
414         CL_VM_SetTraceGlobals(prog, &trace, svent);
415 }
416
417
418 // #20 void(string s) precache_model
419 static void VM_CL_precache_model (prvm_prog_t *prog)
420 {
421         const char      *name;
422         int                     i;
423         model_t         *m;
424
425         VM_SAFEPARMCOUNT(1, VM_CL_precache_model);
426
427         name = PRVM_G_STRING(OFS_PARM0);
428         for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
429         {
430                 if(!strcmp(cl.csqc_model_precache[i]->name, name))
431                 {
432                         PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
433                         return;
434                 }
435         }
436         PRVM_G_FLOAT(OFS_RETURN) = 0;
437         m = Mod_ForName(name, false, false, name[0] == '*' ? cl.model_name[1] : NULL);
438         if(m && m->loaded)
439         {
440                 for (i = 0;i < MAX_MODELS;i++)
441                 {
442                         if (!cl.csqc_model_precache[i])
443                         {
444                                 cl.csqc_model_precache[i] = (model_t*)m;
445                                 PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
446                                 return;
447                         }
448                 }
449                 VM_Warning(prog, "VM_CL_precache_model: no free models\n");
450                 return;
451         }
452         VM_Warning(prog, "VM_CL_precache_model: model \"%s\" not found\n", name);
453 }
454
455 // #22 entity(vector org, float rad) findradius
456 static void VM_CL_findradius (prvm_prog_t *prog)
457 {
458         prvm_edict_t    *ent, *chain;
459         vec_t                   radius, radius2;
460         vec3_t                  org, eorg, mins, maxs;
461         int                             i, numtouchedicts;
462         static prvm_edict_t     *touchedicts[MAX_EDICTS];
463         int             chainfield;
464
465         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_findradius);
466
467         if(prog->argc == 3)
468                 chainfield = PRVM_G_INT(OFS_PARM2);
469         else
470                 chainfield = prog->fieldoffsets.chain;
471         if(chainfield < 0)
472                 prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
473
474         chain = (prvm_edict_t *)prog->edicts;
475
476         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
477         radius = PRVM_G_FLOAT(OFS_PARM1);
478         radius2 = radius * radius;
479
480         mins[0] = org[0] - (radius + 1);
481         mins[1] = org[1] - (radius + 1);
482         mins[2] = org[2] - (radius + 1);
483         maxs[0] = org[0] + (radius + 1);
484         maxs[1] = org[1] + (radius + 1);
485         maxs[2] = org[2] + (radius + 1);
486         numtouchedicts = World_EntitiesInBox(&cl.world, mins, maxs, MAX_EDICTS, touchedicts);
487         if (numtouchedicts > MAX_EDICTS)
488         {
489                 // this never happens   //[515]: for what then ?
490                 Con_Printf("CSQC_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
491                 numtouchedicts = MAX_EDICTS;
492         }
493         for (i = 0;i < numtouchedicts;i++)
494         {
495                 ent = touchedicts[i];
496                 // Quake did not return non-solid entities but darkplaces does
497                 // (note: this is the reason you can't blow up fallen zombies)
498                 if (PRVM_clientedictfloat(ent, solid) == SOLID_NOT && !sv_gameplayfix_blowupfallenzombies.integer)
499                         continue;
500                 // LadyHavoc: compare against bounding box rather than center so it
501                 // doesn't miss large objects, and use DotProduct instead of Length
502                 // for a major speedup
503                 VectorSubtract(org, PRVM_clientedictvector(ent, origin), eorg);
504                 if (sv_gameplayfix_findradiusdistancetobox.integer)
505                 {
506                         eorg[0] -= bound(PRVM_clientedictvector(ent, mins)[0], eorg[0], PRVM_clientedictvector(ent, maxs)[0]);
507                         eorg[1] -= bound(PRVM_clientedictvector(ent, mins)[1], eorg[1], PRVM_clientedictvector(ent, maxs)[1]);
508                         eorg[2] -= bound(PRVM_clientedictvector(ent, mins)[2], eorg[2], PRVM_clientedictvector(ent, maxs)[2]);
509                 }
510                 else
511                         VectorMAMAM(1, eorg, -0.5f, PRVM_clientedictvector(ent, mins), -0.5f, PRVM_clientedictvector(ent, maxs), eorg);
512                 if (DotProduct(eorg, eorg) < radius2)
513                 {
514                         PRVM_EDICTFIELDEDICT(ent, chainfield) = PRVM_EDICT_TO_PROG(chain);
515                         chain = ent;
516                 }
517         }
518
519         VM_RETURN_EDICT(chain);
520 }
521
522 // #34 float() droptofloor
523 static void VM_CL_droptofloor (prvm_prog_t *prog)
524 {
525         prvm_edict_t            *ent;
526         vec3_t                          start, end, mins, maxs;
527         trace_t                         trace;
528
529         VM_SAFEPARMCOUNTRANGE(0, 2, VM_CL_droptofloor); // allow 2 parameters because the id1 defs.qc had an incorrect prototype
530
531         // assume failure if it returns early
532         PRVM_G_FLOAT(OFS_RETURN) = 0;
533
534         ent = PRVM_PROG_TO_EDICT(PRVM_clientglobaledict(self));
535         if (ent == prog->edicts)
536         {
537                 VM_Warning(prog, "droptofloor: can not modify world entity\n");
538                 return;
539         }
540         if (ent->free)
541         {
542                 VM_Warning(prog, "droptofloor: can not modify free entity\n");
543                 return;
544         }
545
546         VectorCopy(PRVM_clientedictvector(ent, origin), start);
547         VectorCopy(PRVM_clientedictvector(ent, mins), mins);
548         VectorCopy(PRVM_clientedictvector(ent, maxs), maxs);
549         VectorCopy(PRVM_clientedictvector(ent, origin), end);
550         end[2] -= 256;
551
552         trace = CL_TraceBox(start, mins, maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, NULL, true);
553
554         if (trace.fraction != 1)
555         {
556                 VectorCopy (trace.endpos, PRVM_clientedictvector(ent, origin));
557                 PRVM_clientedictfloat(ent, flags) = (int)PRVM_clientedictfloat(ent, flags) | FL_ONGROUND;
558                 PRVM_clientedictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
559                 PRVM_G_FLOAT(OFS_RETURN) = 1;
560                 // if support is destroyed, keep suspended (gross hack for floating items in various maps)
561 //              ent->priv.server->suspendedinairflag = true;
562         }
563 }
564
565 // #35 void(float style, string value) lightstyle
566 static void VM_CL_lightstyle (prvm_prog_t *prog)
567 {
568         int                     i;
569         const char      *c;
570
571         VM_SAFEPARMCOUNT(2, VM_CL_lightstyle);
572
573         i = (int)PRVM_G_FLOAT(OFS_PARM0);
574         c = PRVM_G_STRING(OFS_PARM1);
575         if (i >= cl.max_lightstyle)
576         {
577                 VM_Warning(prog, "VM_CL_lightstyle >= MAX_LIGHTSTYLES\n");
578                 return;
579         }
580         strlcpy (cl.lightstyle[i].map, c, sizeof (cl.lightstyle[i].map));
581         cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
582         cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map);
583 }
584
585 // #40 float(entity e) checkbottom
586 static void VM_CL_checkbottom (prvm_prog_t *prog)
587 {
588         static int              cs_yes, cs_no;
589         prvm_edict_t    *ent;
590         vec3_t                  mins, maxs, start, stop;
591         trace_t                 trace;
592         int                             x, y;
593         float                   mid, bottom;
594
595         VM_SAFEPARMCOUNT(1, VM_CL_checkbottom);
596         ent = PRVM_G_EDICT(OFS_PARM0);
597         PRVM_G_FLOAT(OFS_RETURN) = 0;
598
599         VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
600         VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
601
602 // if all of the points under the corners are solid world, don't bother
603 // with the tougher checks
604 // the corners must be within 16 of the midpoint
605         start[2] = mins[2] - 1;
606         for     (x=0 ; x<=1 ; x++)
607                 for     (y=0 ; y<=1 ; y++)
608                 {
609                         start[0] = x ? maxs[0] : mins[0];
610                         start[1] = y ? maxs[1] : mins[1];
611                         if (!(CL_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
612                                 goto realcheck;
613                 }
614
615         cs_yes++;
616         PRVM_G_FLOAT(OFS_RETURN) = true;
617         return;         // we got out easy
618
619 realcheck:
620         cs_no++;
621 //
622 // check it for real...
623 //
624         start[2] = mins[2];
625
626 // the midpoint must be within 16 of the bottom
627         start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
628         start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
629         stop[2] = start[2] - 2*sv_stepheight.value;
630         trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, NULL, true, false);
631
632         if (trace.fraction == 1.0)
633                 return;
634
635         mid = bottom = trace.endpos[2];
636
637 // the corners must be within 16 of the midpoint
638         for     (x=0 ; x<=1 ; x++)
639                 for     (y=0 ; y<=1 ; y++)
640                 {
641                         start[0] = stop[0] = x ? maxs[0] : mins[0];
642                         start[1] = stop[1] = y ? maxs[1] : mins[1];
643
644                         trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, NULL, true, false);
645
646                         if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
647                                 bottom = trace.endpos[2];
648                         if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
649                                 return;
650                 }
651
652         cs_yes++;
653         PRVM_G_FLOAT(OFS_RETURN) = true;
654 }
655
656 // #41 float(vector v) pointcontents
657 static void VM_CL_pointcontents (prvm_prog_t *prog)
658 {
659         vec3_t point;
660         VM_SAFEPARMCOUNT(1, VM_CL_pointcontents);
661         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), point);
662         PRVM_G_FLOAT(OFS_RETURN) = Mod_Q1BSP_NativeContentsFromSuperContents(CL_PointSuperContents(point));
663 }
664
665 // #48 void(vector o, vector d, float color, float count) particle
666 static void VM_CL_particle (prvm_prog_t *prog)
667 {
668         vec3_t org, dir;
669         int             count;
670         unsigned char   color;
671         VM_SAFEPARMCOUNT(4, VM_CL_particle);
672
673         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
674         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), dir);
675         color = (int)PRVM_G_FLOAT(OFS_PARM2);
676         count = (int)PRVM_G_FLOAT(OFS_PARM3);
677         CL_ParticleEffect(EFFECT_SVC_PARTICLE, count, org, org, dir, dir, NULL, color);
678 }
679
680 // #74 void(vector pos, string samp, float vol, float atten) ambientsound
681 static void VM_CL_ambientsound (prvm_prog_t *prog)
682 {
683         vec3_t f;
684         sfx_t   *s;
685         VM_SAFEPARMCOUNT(4, VM_CL_ambientsound);
686         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), f);
687         s = S_FindName(PRVM_G_STRING(OFS_PARM1));
688         S_StaticSound (s, f, PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3)*64);
689 }
690
691 // #92 vector(vector org[, float lpflag]) getlight (DP_QC_GETLIGHT)
692 static void VM_CL_getlight (prvm_prog_t *prog)
693 {
694         vec3_t ambientcolor, diffusecolor, diffusenormal;
695         vec3_t p;
696         int flags = prog->argc >= 2 ? PRVM_G_FLOAT(OFS_PARM1) : LP_LIGHTMAP;
697
698         VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_getlight);
699
700         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), p);
701         R_CompleteLightPoint(ambientcolor, diffusecolor, diffusenormal, p, flags, r_refdef.scene.lightmapintensity, r_refdef.scene.ambientintensity);
702         VectorMA(ambientcolor, 0.5, diffusecolor, PRVM_G_VECTOR(OFS_RETURN));
703         if (PRVM_clientglobalvector(getlight_ambient))
704                 VectorCopy(ambientcolor, PRVM_clientglobalvector(getlight_ambient));
705         if (PRVM_clientglobalvector(getlight_diffuse))
706                 VectorCopy(diffusecolor, PRVM_clientglobalvector(getlight_diffuse));
707         if (PRVM_clientglobalvector(getlight_dir))
708                 VectorCopy(diffusenormal, PRVM_clientglobalvector(getlight_dir));
709 }
710
711 //============================================================================
712 //[515]: SCENE MANAGER builtins
713
714 extern cvar_t v_yshearing;
715 void CSQC_R_RecalcView (void)
716 {
717         extern matrix4x4_t viewmodelmatrix_nobob;
718         extern matrix4x4_t viewmodelmatrix_withbob;
719         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);
720         if (v_yshearing.value > 0)
721                 Matrix4x4_QuakeToDuke3D(&r_refdef.view.matrix, &r_refdef.view.matrix, v_yshearing.value);
722         Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
723         Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
724         Matrix4x4_Concat(&viewmodelmatrix_withbob, &r_refdef.view.matrix, &cl.csqc_viewmodelmatrixfromengine);
725 }
726
727 //#300 void() clearscene (EXT_CSQC)
728 static void VM_CL_R_ClearScene (prvm_prog_t *prog)
729 {
730         VM_SAFEPARMCOUNT(0, VM_CL_R_ClearScene);
731         // clear renderable entity and light lists
732         r_refdef.scene.numentities = 0;
733         r_refdef.scene.numlights = 0;
734         // restore the view settings to the values that VM_CL_UpdateView received from the client code
735         r_refdef.view = csqc_original_r_refdef_view;
736         // polygonbegin without draw2d arg has to guess
737         prog->polygonbegin_guess2d = false;
738         VectorCopy(cl.csqc_vieworiginfromengine, cl.csqc_vieworigin);
739         VectorCopy(cl.csqc_viewanglesfromengine, cl.csqc_viewangles);
740         cl.csqc_vidvars.drawworld = r_drawworld.integer != 0;
741         cl.csqc_vidvars.drawenginesbar = false;
742         cl.csqc_vidvars.drawcrosshair = false;
743         CSQC_R_RecalcView();
744         // clear the CL_Mesh_Scene() used for CSQC polygons and engine effects, they will be added by CSQC_RelinkAllEntities and manually created by CSQC
745         CL_MeshEntities_Scene_Clear();
746 }
747
748 //#301 void(float mask) addentities (EXT_CSQC)
749 static void VM_CL_R_AddEntities (prvm_prog_t *prog)
750 {
751         double t = Sys_DirtyTime();
752         int                     i, drawmask;
753         prvm_edict_t *ed;
754         VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntities);
755         drawmask = (int)PRVM_G_FLOAT(OFS_PARM0);
756         CSQC_RelinkAllEntities(drawmask);
757
758         PRVM_clientglobalfloat(time) = cl.time;
759         for(i=1;i<prog->num_edicts;i++)
760         {
761                 // so we can easily check if CSQC entity #edictnum is currently drawn
762                 cl.csqcrenderentities[i].entitynumber = 0;
763                 ed = &prog->edicts[i];
764                 if(ed->free)
765                         continue;
766                 CSQC_Think(ed);
767                 if(ed->free)
768                         continue;
769                 // note that for RF_USEAXIS entities, Predraw sets v_forward/v_right/v_up globals that are read by CSQC_AddRenderEdict
770                 CSQC_Predraw(ed);
771                 if(ed->free)
772                         continue;
773                 if(!((int)PRVM_clientedictfloat(ed, drawmask) & drawmask))
774                         continue;
775                 CSQC_AddRenderEdict(ed, i);
776         }
777
778         // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView
779         t = Sys_DirtyTime() - t;if (t < 0 || t >= 1800) t = 0;
780         prog->functions[PRVM_clientfunction(CSQC_UpdateView)].totaltime -= t;
781 }
782
783 //#302 void(entity ent) addentity (EXT_CSQC)
784 static void VM_CL_R_AddEntity (prvm_prog_t *prog)
785 {
786         double t = Sys_DirtyTime();
787         VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntity);
788         CSQC_AddRenderEdict(PRVM_G_EDICT(OFS_PARM0), 0);
789         t = Sys_DirtyTime() - t;if (t < 0 || t >= 1800) t = 0;
790         prog->functions[PRVM_clientfunction(CSQC_UpdateView)].totaltime -= t;
791 }
792
793 //#303 float(float property, ...) setproperty (EXT_CSQC)
794 //#303 float(float property) getproperty
795 //#303 vector(float property) getpropertyvec
796 //#309 float(float property) getproperty
797 //#309 vector(float property) getpropertyvec
798 // VorteX: make this function be able to return previously set property if new value is not given
799 static void VM_CL_R_SetView (prvm_prog_t *prog)
800 {
801         int             c;
802         prvm_vec_t      *f;
803         float   k;
804
805         VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_R_SetView);
806
807         c = (int)PRVM_G_FLOAT(OFS_PARM0);
808
809         // return value?
810         if (prog->argc < 2)
811         {
812                 switch(c)
813                 {
814                 case VF_MIN:
815                         VectorSet(PRVM_G_VECTOR(OFS_RETURN), r_refdef.view.x, r_refdef.view.y, 0);
816                         break;
817                 case VF_MIN_X:
818                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.x;
819                         break;
820                 case VF_MIN_Y:
821                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.y;
822                         break;
823                 case VF_SIZE:
824                         VectorSet(PRVM_G_VECTOR(OFS_RETURN), r_refdef.view.width, r_refdef.view.height, 0);
825                         break;
826                 case VF_SIZE_X:
827                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.width;
828                         break;
829                 case VF_SIZE_Y:
830                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.height;
831                         break;
832                 case VF_VIEWPORT:
833                         VM_Warning(prog, "VM_CL_R_GetView : VF_VIEWPORT can't be retrieved, use VF_MIN/VF_SIZE instead\n");
834                         break;
835                 case VF_FOV:
836                         VectorSet(PRVM_G_VECTOR(OFS_RETURN), r_refdef.view.ortho_x, r_refdef.view.ortho_y, 0);
837                         break;
838                 case VF_FOVX:
839                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.ortho_x;
840                         break;
841                 case VF_FOVY:
842                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.ortho_y;
843                         break;
844                 case VF_ORIGIN:
845                         VectorCopy(cl.csqc_vieworigin, PRVM_G_VECTOR(OFS_RETURN));
846                         break;
847                 case VF_ORIGIN_X:
848                         PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vieworigin[0];
849                         break;
850                 case VF_ORIGIN_Y:
851                         PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vieworigin[1];
852                         break;
853                 case VF_ORIGIN_Z:
854                         PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vieworigin[2];
855                         break;
856                 case VF_ANGLES:
857                         VectorCopy(cl.csqc_viewangles, PRVM_G_VECTOR(OFS_RETURN));
858                         break;
859                 case VF_ANGLES_X:
860                         PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_viewangles[0];
861                         break;
862                 case VF_ANGLES_Y:
863                         PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_viewangles[1];
864                         break;
865                 case VF_ANGLES_Z:
866                         PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_viewangles[2];
867                         break;
868                 case VF_DRAWWORLD:
869                         PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vidvars.drawworld;
870                         break;
871                 case VF_DRAWENGINESBAR:
872                         PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vidvars.drawenginesbar;
873                         break;
874                 case VF_DRAWCROSSHAIR:
875                         PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vidvars.drawcrosshair;
876                         break;
877                 case VF_CL_VIEWANGLES:
878                         VectorCopy(cl.viewangles, PRVM_G_VECTOR(OFS_RETURN));;
879                         break;
880                 case VF_CL_VIEWANGLES_X:
881                         PRVM_G_FLOAT(OFS_RETURN) = cl.viewangles[0];
882                         break;
883                 case VF_CL_VIEWANGLES_Y:
884                         PRVM_G_FLOAT(OFS_RETURN) = cl.viewangles[1];
885                         break;
886                 case VF_CL_VIEWANGLES_Z:
887                         PRVM_G_FLOAT(OFS_RETURN) = cl.viewangles[2];
888                         break;
889                 case VF_PERSPECTIVE:
890                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.useperspective;
891                         break;
892                 case VF_CLEARSCREEN:
893                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.isoverlay;
894                         break;
895                 case VF_MAINVIEW:
896                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.ismain;
897                         break;
898                 case VF_FOG_DENSITY:
899                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_density;
900                         break;
901                 case VF_FOG_COLOR:
902                         PRVM_G_VECTOR(OFS_RETURN)[0] = r_refdef.fog_red;
903                         PRVM_G_VECTOR(OFS_RETURN)[1] = r_refdef.fog_green;
904                         PRVM_G_VECTOR(OFS_RETURN)[2] = r_refdef.fog_blue;
905                         break;
906                 case VF_FOG_COLOR_R:
907                         PRVM_G_VECTOR(OFS_RETURN)[0] = r_refdef.fog_red;
908                         break;
909                 case VF_FOG_COLOR_G:
910                         PRVM_G_VECTOR(OFS_RETURN)[1] = r_refdef.fog_green;
911                         break;
912                 case VF_FOG_COLOR_B:
913                         PRVM_G_VECTOR(OFS_RETURN)[2] = r_refdef.fog_blue;
914                         break;
915                 case VF_FOG_ALPHA:
916                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_alpha;
917                         break;
918                 case VF_FOG_START:
919                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_start;
920                         break;
921                 case VF_FOG_END:
922                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_end;
923                         break;
924                 case VF_FOG_HEIGHT:
925                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_height;
926                         break;
927                 case VF_FOG_FADEDEPTH:
928                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_fadedepth;
929                         break;
930                 case VF_MINFPS_QUALITY:
931                         PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.quality;
932                         break;
933                 default:
934                         PRVM_G_FLOAT(OFS_RETURN) = 0;
935                         VM_Warning(prog, "VM_CL_R_GetView : unknown parm %i\n", c);
936                         return;
937                 }
938                 return;
939         }
940
941         f = PRVM_G_VECTOR(OFS_PARM1);
942         k = PRVM_G_FLOAT(OFS_PARM1);
943         switch(c)
944         {
945         case VF_MIN:
946                 r_refdef.view.x = (int)(f[0]);
947                 r_refdef.view.y = (int)(f[1]);
948                 DrawQ_RecalcView();
949                 break;
950         case VF_MIN_X:
951                 r_refdef.view.x = (int)(k);
952                 DrawQ_RecalcView();
953                 break;
954         case VF_MIN_Y:
955                 r_refdef.view.y = (int)(k);
956                 DrawQ_RecalcView();
957                 break;
958         case VF_SIZE:
959                 r_refdef.view.width = (int)(f[0]);
960                 r_refdef.view.height = (int)(f[1]);
961                 DrawQ_RecalcView();
962                 break;
963         case VF_SIZE_X:
964                 r_refdef.view.width = (int)(k);
965                 DrawQ_RecalcView();
966                 break;
967         case VF_SIZE_Y:
968                 r_refdef.view.height = (int)(k);
969                 DrawQ_RecalcView();
970                 break;
971         case VF_VIEWPORT:
972                 r_refdef.view.x = (int)(f[0]);
973                 r_refdef.view.y = (int)(f[1]);
974                 f = PRVM_G_VECTOR(OFS_PARM2);
975                 r_refdef.view.width = (int)(f[0]);
976                 r_refdef.view.height = (int)(f[1]);
977                 DrawQ_RecalcView();
978                 break;
979         case VF_FOV:
980                 r_refdef.view.frustum_x = tan(f[0] * M_PI / 360.0);r_refdef.view.ortho_x = f[0];
981                 r_refdef.view.frustum_y = tan(f[1] * M_PI / 360.0);r_refdef.view.ortho_y = f[1];
982                 break;
983         case VF_FOVX:
984                 r_refdef.view.frustum_x = tan(k * M_PI / 360.0);r_refdef.view.ortho_x = k;
985                 break;
986         case VF_FOVY:
987                 r_refdef.view.frustum_y = tan(k * M_PI / 360.0);r_refdef.view.ortho_y = k;
988                 break;
989         case VF_ORIGIN:
990                 VectorCopy(f, cl.csqc_vieworigin);
991                 CSQC_R_RecalcView();
992                 break;
993         case VF_ORIGIN_X:
994                 cl.csqc_vieworigin[0] = k;
995                 CSQC_R_RecalcView();
996                 break;
997         case VF_ORIGIN_Y:
998                 cl.csqc_vieworigin[1] = k;
999                 CSQC_R_RecalcView();
1000                 break;
1001         case VF_ORIGIN_Z:
1002                 cl.csqc_vieworigin[2] = k;
1003                 CSQC_R_RecalcView();
1004                 break;
1005         case VF_ANGLES:
1006                 VectorCopy(f, cl.csqc_viewangles);
1007                 CSQC_R_RecalcView();
1008                 break;
1009         case VF_ANGLES_X:
1010                 cl.csqc_viewangles[0] = k;
1011                 CSQC_R_RecalcView();
1012                 break;
1013         case VF_ANGLES_Y:
1014                 cl.csqc_viewangles[1] = k;
1015                 CSQC_R_RecalcView();
1016                 break;
1017         case VF_ANGLES_Z:
1018                 cl.csqc_viewangles[2] = k;
1019                 CSQC_R_RecalcView();
1020                 break;
1021         case VF_DRAWWORLD:
1022                 cl.csqc_vidvars.drawworld = ((k != 0) && r_drawworld.integer);
1023                 break;
1024         case VF_DRAWENGINESBAR:
1025                 cl.csqc_vidvars.drawenginesbar = k != 0;
1026                 break;
1027         case VF_DRAWCROSSHAIR:
1028                 cl.csqc_vidvars.drawcrosshair = k != 0;
1029                 break;
1030         case VF_CL_VIEWANGLES:
1031                 VectorCopy(f, cl.viewangles);
1032                 break;
1033         case VF_CL_VIEWANGLES_X:
1034                 cl.viewangles[0] = k;
1035                 break;
1036         case VF_CL_VIEWANGLES_Y:
1037                 cl.viewangles[1] = k;
1038                 break;
1039         case VF_CL_VIEWANGLES_Z:
1040                 cl.viewangles[2] = k;
1041                 break;
1042         case VF_PERSPECTIVE:
1043                 r_refdef.view.useperspective = k != 0;
1044                 break;
1045         case VF_CLEARSCREEN:
1046                 r_refdef.view.isoverlay = !k;
1047                 break;
1048         case VF_MAINVIEW:
1049                 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.ismain;
1050                 break;
1051         case VF_FOG_DENSITY:
1052                 r_refdef.fog_density = k;
1053                 break;
1054         case VF_FOG_COLOR:
1055                 r_refdef.fog_red = f[0];
1056                 r_refdef.fog_green = f[1];
1057                 r_refdef.fog_blue = f[2];
1058                 break;
1059         case VF_FOG_COLOR_R:
1060                 r_refdef.fog_red = k;
1061                 break;
1062         case VF_FOG_COLOR_G:
1063                 r_refdef.fog_green = k;
1064                 break;
1065         case VF_FOG_COLOR_B:
1066                 r_refdef.fog_blue = k;
1067                 break;
1068         case VF_FOG_ALPHA:
1069                 r_refdef.fog_alpha = k;
1070                 break;
1071         case VF_FOG_START:
1072                 r_refdef.fog_start = k;
1073                 break;
1074         case VF_FOG_END:
1075                 r_refdef.fog_end = k;
1076                 break;
1077         case VF_FOG_HEIGHT:
1078                 r_refdef.fog_height = k;
1079                 break;
1080         case VF_FOG_FADEDEPTH:
1081                 r_refdef.fog_fadedepth = k;
1082                 break;
1083         case VF_MINFPS_QUALITY:
1084                 r_refdef.view.quality = k;
1085                 break;
1086         default:
1087                 PRVM_G_FLOAT(OFS_RETURN) = 0;
1088                 VM_Warning(prog, "VM_CL_R_SetView : unknown parm %i\n", c);
1089                 return;
1090         }
1091         PRVM_G_FLOAT(OFS_RETURN) = 1;
1092 }
1093
1094 //#305 void(vector org, float radius, vector lightcolours[, float style, string cubemapname, float pflags]) adddynamiclight (EXT_CSQC)
1095 static void VM_CL_R_AddDynamicLight (prvm_prog_t *prog)
1096 {
1097         double t = Sys_DirtyTime();
1098         vec3_t org;
1099         float radius = 300;
1100         vec3_t col;
1101         int style = -1;
1102         const char *cubemapname = NULL;
1103         int pflags = PFLAGS_CORONA | PFLAGS_FULLDYNAMIC;
1104         float coronaintensity = 1;
1105         float coronasizescale = 0.25;
1106         qbool castshadow = true;
1107         float ambientscale = 0;
1108         float diffusescale = 1;
1109         float specularscale = 1;
1110         matrix4x4_t matrix;
1111         vec3_t forward, left, up;
1112         VM_SAFEPARMCOUNTRANGE(3, 8, VM_CL_R_AddDynamicLight);
1113
1114         // if we've run out of dlights, just return
1115         if (r_refdef.scene.numlights >= MAX_DLIGHTS)
1116                 return;
1117
1118         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
1119         radius = PRVM_G_FLOAT(OFS_PARM1);
1120         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), col);
1121         if (prog->argc >= 4)
1122         {
1123                 style = (int)PRVM_G_FLOAT(OFS_PARM3);
1124                 if (style >= MAX_LIGHTSTYLES)
1125                 {
1126                         Con_DPrintf("VM_CL_R_AddDynamicLight: out of bounds lightstyle index %i\n", style);
1127                         style = -1;
1128                 }
1129         }
1130         if (prog->argc >= 5)
1131                 cubemapname = PRVM_G_STRING(OFS_PARM4);
1132         if (prog->argc >= 6)
1133                 pflags = (int)PRVM_G_FLOAT(OFS_PARM5);
1134         coronaintensity = (pflags & PFLAGS_CORONA) != 0;
1135         castshadow = (pflags & PFLAGS_NOSHADOW) == 0;
1136
1137         VectorScale(PRVM_clientglobalvector(v_forward), radius, forward);
1138         VectorScale(PRVM_clientglobalvector(v_right), -radius, left);
1139         VectorScale(PRVM_clientglobalvector(v_up), radius, up);
1140         Matrix4x4_FromVectors(&matrix, forward, left, up, org);
1141
1142         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);
1143         r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
1144         t = Sys_DirtyTime() - t;if (t < 0 || t >= 1800) t = 0;
1145         prog->functions[PRVM_clientfunction(CSQC_UpdateView)].totaltime -= t;
1146 }
1147
1148 //============================================================================
1149
1150 //#310 vector (vector v) cs_unproject (EXT_CSQC)
1151 static void VM_CL_unproject (prvm_prog_t *prog)
1152 {
1153         vec3_t f;
1154         vec3_t temp;
1155         vec3_t result;
1156
1157         VM_SAFEPARMCOUNT(1, VM_CL_unproject);
1158         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), f);
1159         VectorSet(temp,
1160                 f[2],
1161                 (-1.0 + 2.0 * (f[0] / vid_conwidth.integer)) * f[2] * -r_refdef.view.frustum_x,
1162                 (-1.0 + 2.0 * (f[1] / vid_conheight.integer)) * f[2] * -r_refdef.view.frustum_y);
1163         if(v_flipped.integer)
1164                 temp[1] = -temp[1];
1165         Matrix4x4_Transform(&r_refdef.view.matrix, temp, result);
1166         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
1167 }
1168
1169 //#311 vector (vector v) cs_project (EXT_CSQC)
1170 static void VM_CL_project (prvm_prog_t *prog)
1171 {
1172         vec3_t f;
1173         vec3_t v;
1174         matrix4x4_t m;
1175
1176         VM_SAFEPARMCOUNT(1, VM_CL_project);
1177         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), f);
1178         Matrix4x4_Invert_Full(&m, &r_refdef.view.matrix);
1179         Matrix4x4_Transform(&m, f, v);
1180         if(v_flipped.integer)
1181                 v[1] = -v[1];
1182         VectorSet(PRVM_G_VECTOR(OFS_RETURN),
1183                 vid_conwidth.integer * (0.5*(1.0+v[1]/v[0]/-r_refdef.view.frustum_x)),
1184                 vid_conheight.integer * (0.5*(1.0+v[2]/v[0]/-r_refdef.view.frustum_y)),
1185                 v[0]);
1186         // explanation:
1187         // after transforming, relative position to viewport (0..1) = 0.5 * (1 + v[2]/v[0]/-frustum_{x \or y})
1188         // as 2D drawing honors the viewport too, to get the same pixel, we simply multiply this by conwidth/height
1189 }
1190
1191 //=============================================================================
1192 // Draw builtins (client & menu)
1193
1194 /*
1195 ========================
1196 VM_drawline
1197
1198 void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags)
1199 ========================
1200 */
1201 void VM_drawline (prvm_prog_t *prog)
1202 {
1203         prvm_vec_t      *c1, *c2, *rgb;
1204         float   alpha, width;
1205         unsigned char   flags;
1206
1207         VM_SAFEPARMCOUNT(6, VM_drawline);
1208
1209         // polygonbegin without draw2d arg has to guess
1210         prog->polygonbegin_guess2d = true;
1211
1212         width   = PRVM_G_FLOAT(OFS_PARM0);
1213         c1              = PRVM_G_VECTOR(OFS_PARM1);
1214         c2              = PRVM_G_VECTOR(OFS_PARM2);
1215         rgb             = PRVM_G_VECTOR(OFS_PARM3);
1216         alpha   = PRVM_G_FLOAT(OFS_PARM4);
1217         flags   = (int)PRVM_G_FLOAT(OFS_PARM5);
1218         DrawQ_Line(width, c1[0], c1[1], c2[0], c2[1], rgb[0], rgb[1], rgb[2], alpha, flags);
1219 }
1220
1221 /*
1222 =========
1223 VM_iscachedpic
1224
1225 float   iscachedpic(string pic)
1226 =========
1227 */
1228 void VM_iscachedpic(prvm_prog_t *prog)
1229 {
1230         VM_SAFEPARMCOUNT(1,VM_iscachedpic);
1231
1232         // drawq hasnt such a function, thus always return true
1233         PRVM_G_FLOAT(OFS_RETURN) = false;
1234 }
1235
1236 /*
1237 =========
1238 VM_precache_pic
1239
1240 string  precache_pic(string pic)
1241 =========
1242 */
1243 #define PRECACHE_PIC_FROMWAD 1 /* FTEQW, not supported here */
1244 #define PRECACHE_PIC_NOTPERSISTENT 2
1245 //#define PRECACHE_PIC_NOCLAMP 4
1246 #define PRECACHE_PIC_MIPMAP 8
1247 void VM_precache_pic(prvm_prog_t *prog)
1248 {
1249         const char      *s;
1250         int flags = CACHEPICFLAG_FAILONMISSING;
1251
1252         VM_SAFEPARMCOUNTRANGE(1, 2, VM_precache_pic);
1253
1254         s = PRVM_G_STRING(OFS_PARM0);
1255         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
1256         VM_CheckEmptyString(prog, s);
1257
1258         if(prog->argc >= 2)
1259         {
1260                 int f = PRVM_G_FLOAT(OFS_PARM1);
1261                 if(f & PRECACHE_PIC_NOTPERSISTENT)
1262                         flags |= CACHEPICFLAG_NOTPERSISTENT;
1263                 //if(f & PRECACHE_PIC_NOCLAMP)
1264                 //      flags |= CACHEPICFLAG_NOCLAMP;
1265                 if(f & PRECACHE_PIC_MIPMAP)
1266                         flags |= CACHEPICFLAG_MIPMAP;
1267         }
1268
1269         if( !Draw_IsPicLoaded(Draw_CachePic_Flags(s, flags | CACHEPICFLAG_QUIET)) )
1270                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1271 }
1272
1273 /*
1274 =========
1275 VM_freepic
1276
1277 freepic(string s)
1278 =========
1279 */
1280 void VM_freepic(prvm_prog_t *prog)
1281 {
1282         const char *s;
1283
1284         VM_SAFEPARMCOUNT(1,VM_freepic);
1285
1286         s = PRVM_G_STRING(OFS_PARM0);
1287         VM_CheckEmptyString(prog, s);
1288
1289         Draw_FreePic(s);
1290 }
1291
1292 static void getdrawfontscale(prvm_prog_t *prog, float *sx, float *sy)
1293 {
1294         vec3_t v;
1295         *sx = *sy = 1;
1296         VectorCopy(PRVM_drawglobalvector(drawfontscale), v);
1297         if(VectorLength2(v) > 0)
1298         {
1299                 *sx = v[0];
1300                 *sy = v[1];
1301         }
1302 }
1303
1304 static dp_font_t *getdrawfont(prvm_prog_t *prog)
1305 {
1306         int f = (int) PRVM_drawglobalfloat(drawfont);
1307         if(f < 0 || f >= dp_fonts.maxsize)
1308                 return FONT_DEFAULT;
1309         return &dp_fonts.f[f];
1310 }
1311
1312 /*
1313 =========
1314 VM_drawcharacter
1315
1316 float   drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
1317 =========
1318 */
1319 void VM_drawcharacter(prvm_prog_t *prog)
1320 {
1321         prvm_vec_t *pos,*scale,*rgb;
1322         char   character;
1323         int flag;
1324         float sx, sy;
1325         VM_SAFEPARMCOUNT(6,VM_drawcharacter);
1326
1327         // polygonbegin without draw2d arg has to guess
1328         prog->polygonbegin_guess2d = true;
1329
1330         character = (char) PRVM_G_FLOAT(OFS_PARM1);
1331         if(character == 0)
1332         {
1333                 PRVM_G_FLOAT(OFS_RETURN) = -1;
1334                 VM_Warning(prog, "VM_drawcharacter: %s passed null character !\n",prog->name);
1335                 return;
1336         }
1337
1338         pos = PRVM_G_VECTOR(OFS_PARM0);
1339         scale = PRVM_G_VECTOR(OFS_PARM2);
1340         rgb = PRVM_G_VECTOR(OFS_PARM3);
1341         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
1342
1343         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
1344         {
1345                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1346                 VM_Warning(prog, "VM_drawcharacter: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
1347                 return;
1348         }
1349
1350         if(pos[2] || scale[2])
1351                 VM_Warning(prog, "VM_drawcharacter: z value%c from %s discarded\n",(pos[2] && scale[2]) ? 's' : 0,((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
1352
1353         if(!scale[0] || !scale[1])
1354         {
1355                 PRVM_G_FLOAT(OFS_RETURN) = -3;
1356                 VM_Warning(prog, "VM_drawcharacter: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
1357                 return;
1358         }
1359
1360         getdrawfontscale(prog, &sx, &sy);
1361         DrawQ_String_Scale(pos[0], pos[1], &character, 1, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont(prog));
1362         PRVM_G_FLOAT(OFS_RETURN) = 1;
1363 }
1364
1365 /*
1366 =========
1367 VM_drawstring
1368
1369 float   drawstring(vector position, string text, vector scale, vector rgb, float alpha[, float flag])
1370 =========
1371 */
1372 void VM_drawstring(prvm_prog_t *prog)
1373 {
1374         prvm_vec_t *pos,*scale,*rgb;
1375         const char  *string;
1376         int flag = 0;
1377         float sx, sy;
1378         VM_SAFEPARMCOUNTRANGE(5,6,VM_drawstring);
1379
1380         // polygonbegin without draw2d arg has to guess
1381         prog->polygonbegin_guess2d = true;
1382
1383         string = PRVM_G_STRING(OFS_PARM1);
1384         pos = PRVM_G_VECTOR(OFS_PARM0);
1385         scale = PRVM_G_VECTOR(OFS_PARM2);
1386         rgb = PRVM_G_VECTOR(OFS_PARM3);
1387         if (prog->argc >= 6)
1388                 flag = (int)PRVM_G_FLOAT(OFS_PARM5);
1389
1390         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
1391         {
1392                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1393                 VM_Warning(prog, "VM_drawstring: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
1394                 return;
1395         }
1396
1397         if(!scale[0] || !scale[1])
1398         {
1399                 PRVM_G_FLOAT(OFS_RETURN) = -3;
1400                 VM_Warning(prog, "VM_drawstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
1401                 return;
1402         }
1403
1404         if(pos[2] || scale[2])
1405                 VM_Warning(prog, "VM_drawstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
1406
1407         getdrawfontscale(prog, &sx, &sy);
1408         DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont(prog));
1409         //Font_DrawString(pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true);
1410         PRVM_G_FLOAT(OFS_RETURN) = 1;
1411 }
1412
1413 /*
1414 =========
1415 VM_drawcolorcodedstring
1416
1417 float   drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag)
1418 /
1419 float   drawcolorcodedstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
1420 =========
1421 */
1422 void VM_drawcolorcodedstring(prvm_prog_t *prog)
1423 {
1424         prvm_vec_t *pos, *scale;
1425         const char  *string;
1426         int flag;
1427         vec3_t rgb;
1428         float sx, sy, alpha;
1429
1430         VM_SAFEPARMCOUNTRANGE(5,6,VM_drawcolorcodedstring);
1431
1432         // polygonbegin without draw2d arg has to guess
1433         prog->polygonbegin_guess2d = true;
1434
1435         if (prog->argc == 6) // full 6 parms, like normal drawstring
1436         {
1437                 pos = PRVM_G_VECTOR(OFS_PARM0);
1438                 string = PRVM_G_STRING(OFS_PARM1);
1439                 scale = PRVM_G_VECTOR(OFS_PARM2);
1440                 VectorCopy(PRVM_G_VECTOR(OFS_PARM3), rgb); 
1441                 alpha = PRVM_G_FLOAT(OFS_PARM4);
1442                 flag = (int)PRVM_G_FLOAT(OFS_PARM5);
1443         }
1444         else
1445         {
1446                 pos = PRVM_G_VECTOR(OFS_PARM0);
1447                 string = PRVM_G_STRING(OFS_PARM1);
1448                 scale = PRVM_G_VECTOR(OFS_PARM2);
1449                 rgb[0] = 1.0;
1450                 rgb[1] = 1.0;
1451                 rgb[2] = 1.0;
1452                 alpha = PRVM_G_FLOAT(OFS_PARM3);
1453                 flag = (int)PRVM_G_FLOAT(OFS_PARM4);
1454         }
1455
1456         if(flag < DRAWFLAG_NORMAL || flag >= DRAWFLAG_NUMFLAGS)
1457         {
1458                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1459                 VM_Warning(prog, "VM_drawcolorcodedstring: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
1460                 return;
1461         }
1462
1463         if(!scale[0] || !scale[1])
1464         {
1465                 PRVM_G_FLOAT(OFS_RETURN) = -3;
1466                 VM_Warning(prog, "VM_drawcolorcodedstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
1467                 return;
1468         }
1469
1470         if(pos[2] || scale[2])
1471                 VM_Warning(prog, "VM_drawcolorcodedstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
1472
1473         getdrawfontscale(prog, &sx, &sy);
1474         DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], alpha, flag, NULL, false, getdrawfont(prog));
1475         if (prog->argc == 6) // also return vector of last color
1476                 VectorCopy(DrawQ_Color, PRVM_G_VECTOR(OFS_RETURN));
1477         else
1478                 PRVM_G_FLOAT(OFS_RETURN) = 1;
1479 }
1480 /*
1481 =========
1482 VM_stringwidth
1483
1484 float   stringwidth(string text, float allowColorCodes, float size)
1485 =========
1486 */
1487 void VM_stringwidth(prvm_prog_t *prog)
1488 {
1489         const char  *string;
1490         vec2_t szv;
1491         float mult; // sz is intended font size so we can later add freetype support, mult is font size multiplier in pixels per character cell
1492         int colors;
1493         float sx, sy;
1494         size_t maxlen = 0;
1495         VM_SAFEPARMCOUNTRANGE(2, 3, VM_stringwidth);
1496
1497         getdrawfontscale(prog, &sx, &sy);
1498         if(prog->argc == 3)
1499         {
1500                 Vector2Copy(PRVM_G_VECTOR(OFS_PARM2), szv);
1501                 mult = 1;
1502         }
1503         else
1504         {
1505                 // we want the width for 8x8 font size, divided by 8
1506                 Vector2Set(szv, 8, 8);
1507                 mult = 0.125;
1508                 // to make sure snapping is turned off, ALWAYS use a nontrivial scale in this case
1509                 if(sx >= 0.9 && sx <= 1.1)
1510                 {
1511                         mult *= 2;
1512                         sx /= 2;
1513                         sy /= 2;
1514                 }
1515         }
1516
1517         string = PRVM_G_STRING(OFS_PARM0);
1518         colors = (int)PRVM_G_FLOAT(OFS_PARM1);
1519
1520         PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_UntilWidth_TrackColors_Scale(string, &maxlen, szv[0], szv[1], sx, sy, NULL, !colors, getdrawfont(prog), 1000000000) * mult;
1521 /*
1522         if(prog->argc == 3)
1523         {
1524                 mult = sz = PRVM_G_FLOAT(OFS_PARM2);
1525         }
1526         else
1527         {
1528                 sz = 8;
1529                 mult = 1;
1530         }
1531
1532         string = PRVM_G_STRING(OFS_PARM0);
1533         colors = (int)PRVM_G_FLOAT(OFS_PARM1);
1534
1535         PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth(string, 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw
1536 */
1537 }
1538
1539 /*
1540 =========
1541 VM_findfont
1542
1543 float findfont(string s)
1544 =========
1545 */
1546
1547 static float getdrawfontnum(const char *fontname)
1548 {
1549         int i;
1550
1551         for(i = 0; i < dp_fonts.maxsize; ++i)
1552                 if(!strcmp(dp_fonts.f[i].title, fontname))
1553                         return i;
1554         return -1;
1555 }
1556
1557 void VM_findfont(prvm_prog_t *prog)
1558 {
1559         VM_SAFEPARMCOUNT(1,VM_findfont);
1560         PRVM_G_FLOAT(OFS_RETURN) = getdrawfontnum(PRVM_G_STRING(OFS_PARM0));
1561 }
1562
1563 /*
1564 =========
1565 VM_loadfont
1566
1567 float loadfont(string fontname, string fontmaps, string sizes, float slot)
1568 =========
1569 */
1570
1571 void VM_loadfont(prvm_prog_t *prog)
1572 {
1573         const char *fontname, *filelist, *sizes, *c, *cm;
1574         char mainfont[MAX_QPATH];
1575         int i, numsizes;
1576         float sz, scale, voffset;
1577         dp_font_t *f;
1578
1579         VM_SAFEPARMCOUNTRANGE(3,6,VM_loadfont);
1580
1581         fontname = PRVM_G_STRING(OFS_PARM0);
1582         if (!fontname[0])
1583                 fontname = "default";
1584
1585         filelist = PRVM_G_STRING(OFS_PARM1);
1586         if (!filelist[0])
1587                 filelist = "gfx/conchars";
1588
1589         sizes = PRVM_G_STRING(OFS_PARM2);
1590         if (!sizes[0])
1591                 sizes = "10";
1592
1593         // find a font
1594         f = NULL;
1595         if (prog->argc >= 4)
1596         {
1597                 i = PRVM_G_FLOAT(OFS_PARM3);
1598                 if (i >= 0 && i < dp_fonts.maxsize)
1599                 {
1600                         f = &dp_fonts.f[i];
1601                         strlcpy(f->title, fontname, sizeof(f->title)); // replace name
1602                 }
1603         }
1604         if (!f)
1605                 f = FindFont(fontname, true);
1606         if (!f)
1607         {
1608                 PRVM_G_FLOAT(OFS_RETURN) = -1;
1609                 return; // something go wrong
1610         }
1611
1612         memset(f->fallbacks, 0, sizeof(f->fallbacks));
1613         memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
1614
1615         // first font is handled "normally"
1616         c = strchr(filelist, ':');
1617         cm = strchr(filelist, ',');
1618         if(c && (!cm || c < cm))
1619                 f->req_face = atoi(c+1);
1620         else
1621         {
1622                 f->req_face = 0;
1623                 c = cm;
1624         }
1625         if(!c || (c - filelist) > MAX_QPATH)
1626                 strlcpy(mainfont, filelist, sizeof(mainfont));
1627         else
1628         {
1629                 memcpy(mainfont, filelist, c - filelist);
1630                 mainfont[c - filelist] = 0;
1631         }
1632
1633         // handle fallbacks
1634         for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
1635         {
1636                 c = strchr(filelist, ',');
1637                 if(!c)
1638                         break;
1639                 filelist = c + 1;
1640                 if(!*filelist)
1641                         break;
1642                 c = strchr(filelist, ':');
1643                 cm = strchr(filelist, ',');
1644                 if(c && (!cm || c < cm))
1645                         f->fallback_faces[i] = atoi(c+1);
1646                 else
1647                 {
1648                         f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
1649                         c = cm;
1650                 }
1651                 if(!c || (c-filelist) > MAX_QPATH)
1652                 {
1653                         strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
1654                 }
1655                 else
1656                 {
1657                         memcpy(f->fallbacks[i], filelist, c - filelist);
1658                         f->fallbacks[i][c - filelist] = 0;
1659                 }
1660         }
1661
1662         // handle sizes
1663         for(i = 0; i < MAX_FONT_SIZES; ++i)
1664                 f->req_sizes[i] = -1;
1665         for (numsizes = 0,c = sizes;;)
1666         {
1667                 if (!COM_ParseToken_VM_Tokenize(&c, 0))
1668                         break;
1669                 sz = atof(com_token);
1670                 // detect crap size
1671                 if (sz < 0.001f || sz > 1000.0f)
1672                 {
1673                         VM_Warning(prog, "VM_loadfont: crap size %s", com_token);
1674                         continue;
1675                 }
1676                 // check overflow
1677                 if (numsizes == MAX_FONT_SIZES)
1678                 {
1679                         VM_Warning(prog, "VM_loadfont: MAX_FONT_SIZES = %i exceeded", MAX_FONT_SIZES);
1680                         break;
1681                 }
1682                 f->req_sizes[numsizes] = sz;
1683                 numsizes++;
1684         }
1685
1686         // additional scale/hoffset parms
1687         scale = 1;
1688         voffset = 0;
1689         if (prog->argc >= 5)
1690         {
1691                 scale = PRVM_G_FLOAT(OFS_PARM4);
1692                 if (scale <= 0)
1693                         scale = 1;
1694         }
1695         if (prog->argc >= 6)
1696                 voffset = PRVM_G_FLOAT(OFS_PARM5);
1697
1698         // load
1699         LoadFont(true, mainfont, f, scale, voffset);
1700
1701         // return index of loaded font
1702         PRVM_G_FLOAT(OFS_RETURN) = (f - dp_fonts.f);
1703 }
1704
1705 /*
1706 =========
1707 VM_drawpic
1708
1709 float   drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
1710 =========
1711 */
1712 void VM_drawpic(prvm_prog_t *prog)
1713 {
1714         const char *picname;
1715         prvm_vec_t *size, *pos, *rgb;
1716         int flag = 0;
1717
1718         VM_SAFEPARMCOUNTRANGE(5,6,VM_drawpic);
1719
1720         // polygonbegin without draw2d arg has to guess
1721         prog->polygonbegin_guess2d = true;
1722
1723         picname = PRVM_G_STRING(OFS_PARM1);
1724         VM_CheckEmptyString(prog, picname);
1725
1726         // is pic cached ? no function yet for that
1727         if(!1)
1728         {
1729                 PRVM_G_FLOAT(OFS_RETURN) = -4;
1730                 VM_Warning(prog, "VM_drawpic: %s: %s not cached !\n", prog->name, picname);
1731                 return;
1732         }
1733
1734         pos = PRVM_G_VECTOR(OFS_PARM0);
1735         size = PRVM_G_VECTOR(OFS_PARM2);
1736         rgb = PRVM_G_VECTOR(OFS_PARM3);
1737         if (prog->argc >= 6)
1738                 flag = (int) PRVM_G_FLOAT(OFS_PARM5);
1739
1740         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
1741         {
1742                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1743                 VM_Warning(prog, "VM_drawpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
1744                 return;
1745         }
1746
1747         if(pos[2] || size[2])
1748                 VM_Warning(prog, "VM_drawpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
1749
1750         DrawQ_Pic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
1751         PRVM_G_FLOAT(OFS_RETURN) = 1;
1752 }
1753 /*
1754 =========
1755 VM_drawrotpic
1756
1757 float   drawrotpic(vector position, string pic, vector size, vector org, float angle, vector rgb, float alpha, float flag)
1758 =========
1759 */
1760 void VM_drawrotpic(prvm_prog_t *prog)
1761 {
1762         const char *picname;
1763         prvm_vec_t *size, *pos, *org, *rgb;
1764         int flag;
1765
1766         VM_SAFEPARMCOUNT(8,VM_drawrotpic);
1767
1768         // polygonbegin without draw2d arg has to guess
1769         prog->polygonbegin_guess2d = true;
1770
1771         picname = PRVM_G_STRING(OFS_PARM1);
1772         VM_CheckEmptyString(prog, picname);
1773
1774         // is pic cached ? no function yet for that
1775         if(!1)
1776         {
1777                 PRVM_G_FLOAT(OFS_RETURN) = -4;
1778                 VM_Warning(prog, "VM_drawrotpic: %s: %s not cached !\n", prog->name, picname);
1779                 return;
1780         }
1781
1782         pos = PRVM_G_VECTOR(OFS_PARM0);
1783         size = PRVM_G_VECTOR(OFS_PARM2);
1784         org = PRVM_G_VECTOR(OFS_PARM3);
1785         rgb = PRVM_G_VECTOR(OFS_PARM5);
1786         flag = (int) PRVM_G_FLOAT(OFS_PARM7);
1787
1788         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
1789         {
1790                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1791                 VM_Warning(prog, "VM_drawrotpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
1792                 return;
1793         }
1794
1795         if(pos[2] || size[2] || org[2])
1796                 VM_Warning(prog, "VM_drawrotpic: z value from pos/size/org discarded\n");
1797
1798         DrawQ_RotPic(pos[0], pos[1], Draw_CachePic_Flags(picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], org[0], org[1], PRVM_G_FLOAT(OFS_PARM4), rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM6), flag);
1799         PRVM_G_FLOAT(OFS_RETURN) = 1;
1800 }
1801 /*
1802 =========
1803 VM_drawsubpic
1804
1805 float   drawsubpic(vector position, vector size, string pic, vector srcPos, vector srcSize, vector rgb, float alpha, float flag)
1806
1807 =========
1808 */
1809 void VM_drawsubpic(prvm_prog_t *prog)
1810 {
1811         const char *picname;
1812         prvm_vec_t *size, *pos, *rgb, *srcPos, *srcSize, alpha;
1813         int flag;
1814
1815         VM_SAFEPARMCOUNT(8,VM_drawsubpic);
1816
1817         // polygonbegin without draw2d arg has to guess
1818         prog->polygonbegin_guess2d = true;
1819
1820         picname = PRVM_G_STRING(OFS_PARM2);
1821         VM_CheckEmptyString(prog, picname);
1822
1823         // is pic cached ? no function yet for that
1824         if(!1)
1825         {
1826                 PRVM_G_FLOAT(OFS_RETURN) = -4;
1827                 VM_Warning(prog, "VM_drawsubpic: %s: %s not cached !\n", prog->name, picname);
1828                 return;
1829         }
1830
1831         pos = PRVM_G_VECTOR(OFS_PARM0);
1832         size = PRVM_G_VECTOR(OFS_PARM1);
1833         srcPos = PRVM_G_VECTOR(OFS_PARM3);
1834         srcSize = PRVM_G_VECTOR(OFS_PARM4);
1835         rgb = PRVM_G_VECTOR(OFS_PARM5);
1836         alpha = PRVM_G_FLOAT(OFS_PARM6);
1837         flag = (int) PRVM_G_FLOAT(OFS_PARM7);
1838
1839         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
1840         {
1841                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1842                 VM_Warning(prog, "VM_drawsubpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
1843                 return;
1844         }
1845
1846         if(pos[2] || size[2])
1847                 VM_Warning(prog, "VM_drawsubpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
1848
1849         DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT),
1850                 size[0], size[1],
1851                 srcPos[0],              srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
1852                 srcPos[0] + srcSize[0], srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
1853                 srcPos[0],              srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
1854                 srcPos[0] + srcSize[0], srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
1855                 flag);
1856         PRVM_G_FLOAT(OFS_RETURN) = 1;
1857 }
1858
1859 /*
1860 =========
1861 VM_drawfill
1862
1863 float drawfill(vector position, vector size, vector rgb, float alpha, float flag)
1864 =========
1865 */
1866 void VM_drawfill(prvm_prog_t *prog)
1867 {
1868         prvm_vec_t *size, *pos, *rgb;
1869         int flag;
1870
1871         VM_SAFEPARMCOUNT(5,VM_drawfill);
1872
1873         // polygonbegin without draw2d arg has to guess
1874         prog->polygonbegin_guess2d = true;
1875
1876         pos = PRVM_G_VECTOR(OFS_PARM0);
1877         size = PRVM_G_VECTOR(OFS_PARM1);
1878         rgb = PRVM_G_VECTOR(OFS_PARM2);
1879         flag = (int) PRVM_G_FLOAT(OFS_PARM4);
1880
1881         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
1882         {
1883                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1884                 VM_Warning(prog, "VM_drawfill: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
1885                 return;
1886         }
1887
1888         if(pos[2] || size[2])
1889                 VM_Warning(prog, "VM_drawfill: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
1890
1891         DrawQ_Fill(pos[0], pos[1], size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM3), flag);
1892         PRVM_G_FLOAT(OFS_RETURN) = 1;
1893 }
1894
1895 /*
1896 =========
1897 VM_drawsetcliparea
1898
1899 drawsetcliparea(float x, float y, float width, float height)
1900 =========
1901 */
1902 void VM_drawsetcliparea(prvm_prog_t *prog)
1903 {
1904         float x,y,w,h;
1905         VM_SAFEPARMCOUNT(4,VM_drawsetcliparea);
1906
1907         // polygonbegin without draw2d arg has to guess
1908         prog->polygonbegin_guess2d = true;
1909
1910         x = bound(0, PRVM_G_FLOAT(OFS_PARM0), vid_conwidth.integer);
1911         y = bound(0, PRVM_G_FLOAT(OFS_PARM1), vid_conheight.integer);
1912         w = bound(0, PRVM_G_FLOAT(OFS_PARM2) + PRVM_G_FLOAT(OFS_PARM0) - x, (vid_conwidth.integer  - x));
1913         h = bound(0, PRVM_G_FLOAT(OFS_PARM3) + PRVM_G_FLOAT(OFS_PARM1) - y, (vid_conheight.integer - y));
1914
1915         DrawQ_SetClipArea(x, y, w, h);
1916 }
1917
1918 /*
1919 =========
1920 VM_drawresetcliparea
1921
1922 drawresetcliparea()
1923 =========
1924 */
1925 void VM_drawresetcliparea(prvm_prog_t *prog)
1926 {
1927         VM_SAFEPARMCOUNT(0,VM_drawresetcliparea);
1928
1929         // polygonbegin without draw2d arg has to guess
1930         prog->polygonbegin_guess2d = true;
1931
1932         DrawQ_ResetClipArea();
1933 }
1934
1935 /*
1936 =========
1937 VM_getimagesize
1938
1939 vector  getimagesize(string pic)
1940 =========
1941 */
1942 void VM_getimagesize(prvm_prog_t *prog)
1943 {
1944         const char *p;
1945         cachepic_t *pic;
1946
1947         VM_SAFEPARMCOUNT(1,VM_getimagesize);
1948
1949         p = PRVM_G_STRING(OFS_PARM0);
1950         VM_CheckEmptyString(prog, p);
1951
1952         pic = Draw_CachePic_Flags (p, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOTPERSISTENT);
1953         if (!Draw_IsPicLoaded(pic))
1954         {
1955                 PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
1956                 PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
1957         }
1958         else
1959         {
1960                 PRVM_G_VECTOR(OFS_RETURN)[0] = Draw_GetPicWidth(pic);
1961                 PRVM_G_VECTOR(OFS_RETURN)[1] = Draw_GetPicHeight(pic);
1962         }
1963         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
1964 }
1965
1966 //#330 float(float stnum) getstatf (EXT_CSQC)
1967 static void VM_CL_getstatf (prvm_prog_t *prog)
1968 {
1969         int i;
1970         union
1971         {
1972                 float f;
1973                 int l;
1974         }dat;
1975         VM_SAFEPARMCOUNT(1, VM_CL_getstatf);
1976         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1977         if(i < 0 || i >= MAX_CL_STATS)
1978         {
1979                 VM_Warning(prog, "VM_CL_getstatf: index>=MAX_CL_STATS or index<0\n");
1980                 return;
1981         }
1982         dat.l = cl.stats[i];
1983         PRVM_G_FLOAT(OFS_RETURN) =  dat.f;
1984 }
1985
1986 //#331 float(float stnum) getstati (EXT_CSQC)
1987 static void VM_CL_getstati (prvm_prog_t *prog)
1988 {
1989         int i, index;
1990         int firstbit, bitcount;
1991
1992         VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_getstati);
1993
1994         index = (int)PRVM_G_FLOAT(OFS_PARM0);
1995         if (prog->argc > 1)
1996         {
1997                 firstbit = (int)PRVM_G_FLOAT(OFS_PARM1);
1998                 if (prog->argc > 2)
1999                         bitcount = (int)PRVM_G_FLOAT(OFS_PARM2);
2000                 else
2001                         bitcount = 1;
2002         }
2003         else
2004         {
2005                 firstbit = 0;
2006                 bitcount = 32;
2007         }
2008
2009         if(index < 0 || index >= MAX_CL_STATS)
2010         {
2011                 VM_Warning(prog, "VM_CL_getstati: index>=MAX_CL_STATS or index<0\n");
2012                 return;
2013         }
2014         i = cl.stats[index];
2015         if (bitcount != 32)     //32 causes the mask to overflow, so there's nothing to subtract from.
2016                 i = (((unsigned int)i)&(((1<<bitcount)-1)<<firstbit))>>firstbit;
2017         PRVM_G_FLOAT(OFS_RETURN) = i;
2018 }
2019
2020 //#332 string(float firststnum) getstats (EXT_CSQC)
2021 static void VM_CL_getstats (prvm_prog_t *prog)
2022 {
2023         int i;
2024         char t[17];
2025         VM_SAFEPARMCOUNT(1, VM_CL_getstats);
2026         i = (int)PRVM_G_FLOAT(OFS_PARM0);
2027         if(i < 0 || i > MAX_CL_STATS-4)
2028         {
2029                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2030                 VM_Warning(prog, "VM_CL_getstats: index>MAX_CL_STATS-4 or index<0\n");
2031                 return;
2032         }
2033         strlcpy(t, (char*)&cl.stats[i], sizeof(t));
2034         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
2035 }
2036
2037 //#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
2038 static void VM_CL_setmodelindex (prvm_prog_t *prog)
2039 {
2040         int                             i;
2041         prvm_edict_t    *t;
2042         struct model_s  *model;
2043
2044         VM_SAFEPARMCOUNT(2, VM_CL_setmodelindex);
2045
2046         t = PRVM_G_EDICT(OFS_PARM0);
2047
2048         i = (int)PRVM_G_FLOAT(OFS_PARM1);
2049
2050         PRVM_clientedictstring(t, model) = 0;
2051         PRVM_clientedictfloat(t, modelindex) = 0;
2052
2053         if (!i)
2054                 return;
2055
2056         model = CL_GetModelByIndex(i);
2057         if (!model)
2058         {
2059                 VM_Warning(prog, "VM_CL_setmodelindex: null model\n");
2060                 return;
2061         }
2062         PRVM_clientedictstring(t, model) = PRVM_SetEngineString(prog, model->name);
2063         PRVM_clientedictfloat(t, modelindex) = i;
2064
2065         // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
2066         if (model)
2067         {
2068                 SetMinMaxSize (prog, t, model->normalmins, model->normalmaxs);
2069         }
2070         else
2071                 SetMinMaxSize (prog, t, vec3_origin, vec3_origin);
2072 }
2073
2074 //#334 string(float mdlindex) modelnameforindex (EXT_CSQC)
2075 static void VM_CL_modelnameforindex (prvm_prog_t *prog)
2076 {
2077         model_t *model;
2078
2079         VM_SAFEPARMCOUNT(1, VM_CL_modelnameforindex);
2080
2081         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2082         model = CL_GetModelByIndex((int)PRVM_G_FLOAT(OFS_PARM0));
2083         PRVM_G_INT(OFS_RETURN) = model ? PRVM_SetEngineString(prog, model->name) : 0;
2084 }
2085
2086 //#335 float(string effectname) particleeffectnum (EXT_CSQC)
2087 static void VM_CL_particleeffectnum (prvm_prog_t *prog)
2088 {
2089         int                     i;
2090         VM_SAFEPARMCOUNT(1, VM_CL_particleeffectnum);
2091         i = CL_ParticleEffectIndexForName(PRVM_G_STRING(OFS_PARM0));
2092         if (i == 0)
2093                 i = -1;
2094         PRVM_G_FLOAT(OFS_RETURN) = i;
2095 }
2096
2097 // #336 void(entity ent, float effectnum, vector start, vector end[, float color]) trailparticles (EXT_CSQC)
2098 static void VM_CL_trailparticles (prvm_prog_t *prog)
2099 {
2100         int                             i;
2101         vec3_t                  start, end, velocity;
2102         prvm_edict_t    *t;
2103         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_trailparticles);
2104
2105         t = PRVM_G_EDICT(OFS_PARM0);
2106         i               = (int)PRVM_G_FLOAT(OFS_PARM1);
2107         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), start);
2108         VectorCopy(PRVM_G_VECTOR(OFS_PARM3), end);
2109         VectorCopy(PRVM_clientedictvector(t, velocity), velocity);
2110
2111         if (i < 0)
2112                 return;
2113         CL_ParticleTrail(i, 1, start, end, velocity, velocity, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0, true, true, NULL, NULL, 1);
2114 }
2115
2116 //#337 void(float effectnum, vector origin, vector dir, float count[, float color]) pointparticles (EXT_CSQC)
2117 static void VM_CL_pointparticles (prvm_prog_t *prog)
2118 {
2119         int                     i;
2120         float n;
2121         vec3_t f, v;
2122         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_pointparticles);
2123         i = (int)PRVM_G_FLOAT(OFS_PARM0);
2124         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f);
2125         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), v);
2126         n = PRVM_G_FLOAT(OFS_PARM3);
2127         if (i < 0)
2128                 return;
2129         CL_ParticleEffect(i, n, f, f, v, v, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0);
2130 }
2131
2132 //#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)
2133 static void VM_CL_boxparticles (prvm_prog_t *prog)
2134 {
2135         int effectnum;
2136         // prvm_edict_t *own;
2137         vec3_t origin_from, origin_to, dir_from, dir_to;
2138         float count;
2139         int flags;
2140         qbool istrail;
2141         float tintmins[4], tintmaxs[4], fade;
2142         VM_SAFEPARMCOUNTRANGE(7, 8, VM_CL_boxparticles);
2143
2144         effectnum = (int)PRVM_G_FLOAT(OFS_PARM0);
2145         if (effectnum < 0)
2146                 return;
2147         // own = PRVM_G_EDICT(OFS_PARM1); // TODO find use for this
2148         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin_from);
2149         VectorCopy(PRVM_G_VECTOR(OFS_PARM3), origin_to  );
2150         VectorCopy(PRVM_G_VECTOR(OFS_PARM4), dir_from   );
2151         VectorCopy(PRVM_G_VECTOR(OFS_PARM5), dir_to     );
2152         count = PRVM_G_FLOAT(OFS_PARM6);
2153         if(prog->argc >= 8)
2154                 flags = PRVM_G_FLOAT(OFS_PARM7);
2155         else
2156                 flags = 0;
2157
2158         Vector4Set(tintmins, 1, 1, 1, 1);
2159         Vector4Set(tintmaxs, 1, 1, 1, 1);
2160         fade = 1;
2161         istrail = false;
2162
2163         if(flags & 1) // read alpha
2164         {
2165                 tintmins[3] = PRVM_clientglobalfloat(particles_alphamin);
2166                 tintmaxs[3] = PRVM_clientglobalfloat(particles_alphamax);
2167         }
2168         if(flags & 2) // read color
2169         {
2170                 VectorCopy(PRVM_clientglobalvector(particles_colormin), tintmins);
2171                 VectorCopy(PRVM_clientglobalvector(particles_colormax), tintmaxs);
2172         }
2173         if(flags & 4) // read fade
2174         {
2175                 fade = PRVM_clientglobalfloat(particles_fade);
2176         }
2177         if(flags & 128) // draw as trail
2178         {
2179                 istrail = true;
2180         }
2181
2182         if (istrail)
2183                 CL_ParticleTrail(effectnum, count, origin_from, origin_to, dir_from, dir_to, NULL, 0, true, true, tintmins, tintmaxs, fade);
2184         else
2185                 CL_ParticleBox(effectnum, count, origin_from, origin_to, dir_from, dir_to, NULL, 0, true, true, tintmins, tintmaxs, fade);
2186 }
2187
2188 //#531 void(float pause) setpause
2189 static void VM_CL_setpause(prvm_prog_t *prog)
2190 {
2191         VM_SAFEPARMCOUNT(1, VM_CL_setpause);
2192         if(cl.islocalgame)
2193         {
2194                 if ((int)PRVM_G_FLOAT(OFS_PARM0) != 0)
2195                         host.paused = true;
2196                 else
2197                         host.paused = false;
2198         }
2199 }
2200
2201 //#343 void(float usecursor) setcursormode (DP_CSQC)
2202 static void VM_CL_setcursormode (prvm_prog_t *prog)
2203 {
2204         VM_SAFEPARMCOUNT(1, VM_CL_setcursormode);
2205         cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0) != 0;
2206         cl_ignoremousemoves = 2;
2207 }
2208
2209 //#344 vector() getmousepos (DP_CSQC)
2210 static void VM_CL_getmousepos(prvm_prog_t *prog)
2211 {
2212         VM_SAFEPARMCOUNT(0,VM_CL_getmousepos);
2213
2214         if (key_consoleactive || key_dest != key_game)
2215                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), 0, 0, 0);
2216         else if (cl.csqc_wantsmousemove)
2217                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_windowmouse_x * vid_conwidth.integer / vid.width, in_windowmouse_y * vid_conheight.integer / vid.height, 0);
2218         else
2219                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_mouse_x * vid_conwidth.integer / vid.width, in_mouse_y * vid_conheight.integer / vid.height, 0);
2220 }
2221
2222 //#345 float(float framenum) getinputstate (EXT_CSQC)
2223 static void VM_CL_getinputstate (prvm_prog_t *prog)
2224 {
2225         unsigned int i, frame;
2226         VM_SAFEPARMCOUNT(1, VM_CL_getinputstate);
2227         frame = (unsigned int)PRVM_G_FLOAT(OFS_PARM0);
2228         PRVM_G_FLOAT(OFS_RETURN) = false;
2229         for (i = 0;i < CL_MAX_USERCMDS;i++)
2230         {
2231                 if (cl.movecmd[i].sequence == frame)
2232                 {
2233                         VectorCopy(cl.movecmd[i].viewangles, PRVM_clientglobalvector(input_angles));
2234                         PRVM_clientglobalfloat(input_buttons) = cl.movecmd[i].buttons; // FIXME: this should not be directly exposed to csqc (translation layer needed?)
2235                         PRVM_clientglobalvector(input_movevalues)[0] = cl.movecmd[i].forwardmove;
2236                         PRVM_clientglobalvector(input_movevalues)[1] = cl.movecmd[i].sidemove;
2237                         PRVM_clientglobalvector(input_movevalues)[2] = cl.movecmd[i].upmove;
2238                         PRVM_clientglobalfloat(input_timelength) = cl.movecmd[i].frametime;
2239                         // this probably shouldn't be here
2240                         if(cl.movecmd[i].crouch)
2241                         {
2242                                 VectorCopy(cl.playercrouchmins, PRVM_clientglobalvector(pmove_mins));
2243                                 VectorCopy(cl.playercrouchmaxs, PRVM_clientglobalvector(pmove_maxs));
2244                         }
2245                         else
2246                         {
2247                                 VectorCopy(cl.playerstandmins, PRVM_clientglobalvector(pmove_mins));
2248                                 VectorCopy(cl.playerstandmaxs, PRVM_clientglobalvector(pmove_maxs));
2249                         }
2250                         PRVM_G_FLOAT(OFS_RETURN) = true;
2251                 }
2252         }
2253 }
2254
2255 //#346 void(float sens) setsensitivityscaler (EXT_CSQC)
2256 static void VM_CL_setsensitivityscale (prvm_prog_t *prog)
2257 {
2258         VM_SAFEPARMCOUNT(1, VM_CL_setsensitivityscale);
2259         cl.sensitivityscale = PRVM_G_FLOAT(OFS_PARM0);
2260 }
2261
2262 //#347 void() runstandardplayerphysics (EXT_CSQC)
2263 #define PMF_JUMP_HELD 1 // matches FTEQW
2264 #define PMF_LADDER 2 // not used by DP, FTEQW sets this in runplayerphysics but does not read it
2265 #define PMF_DUCKED 4 // FIXME FTEQW doesn't have this for Q1 like movement because Q1 cannot crouch
2266 #define PMF_ONGROUND 8 // FIXME FTEQW doesn't have this for Q1 like movement and expects CSQC code to do its own trace, this is stupid CPU waste
2267 static void VM_CL_runplayerphysics (prvm_prog_t *prog)
2268 {
2269         cl_clientmovement_state_t s;
2270         prvm_edict_t *ent;
2271
2272         memset(&s, 0, sizeof(s));
2273
2274         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_runplayerphysics);
2275
2276         ent = (prog->argc == 1 ? PRVM_G_EDICT(OFS_PARM0) : prog->edicts);
2277         if(ent == prog->edicts)
2278         {
2279                 // deprecated use
2280                 s.self = NULL;
2281                 VectorCopy(PRVM_clientglobalvector(pmove_org), s.origin);
2282                 VectorCopy(PRVM_clientglobalvector(pmove_vel), s.velocity);
2283                 VectorCopy(PRVM_clientglobalvector(pmove_mins), s.mins);
2284                 VectorCopy(PRVM_clientglobalvector(pmove_maxs), s.maxs);
2285                 s.crouched = 0;
2286                 s.waterjumptime = PRVM_clientglobalfloat(pmove_waterjumptime);
2287                 s.cmd.canjump = (int)PRVM_clientglobalfloat(pmove_jump_held) == 0;
2288         }
2289         else
2290         {
2291                 // new use
2292                 s.self = ent;
2293                 VectorCopy(PRVM_clientedictvector(ent, origin), s.origin);
2294                 VectorCopy(PRVM_clientedictvector(ent, velocity), s.velocity);
2295                 VectorCopy(PRVM_clientedictvector(ent, mins), s.mins);
2296                 VectorCopy(PRVM_clientedictvector(ent, maxs), s.maxs);
2297                 s.crouched = ((int)PRVM_clientedictfloat(ent, pmove_flags) & PMF_DUCKED) != 0;
2298                 s.waterjumptime = 0; // FIXME where do we get this from? FTEQW lacks support for this too
2299                 s.cmd.canjump = ((int)PRVM_clientedictfloat(ent, pmove_flags) & PMF_JUMP_HELD) == 0;
2300         }
2301
2302         VectorCopy(PRVM_clientglobalvector(input_angles), s.cmd.viewangles);
2303         s.cmd.forwardmove = PRVM_clientglobalvector(input_movevalues)[0];
2304         s.cmd.sidemove = PRVM_clientglobalvector(input_movevalues)[1];
2305         s.cmd.upmove = PRVM_clientglobalvector(input_movevalues)[2];
2306         s.cmd.buttons = PRVM_clientglobalfloat(input_buttons);
2307         s.cmd.frametime = PRVM_clientglobalfloat(input_timelength);
2308         s.cmd.jump = (s.cmd.buttons & 2) != 0;
2309         s.cmd.crouch = (s.cmd.buttons & 16) != 0;
2310
2311         CL_ClientMovement_PlayerMove_Frame(&s);
2312
2313         if(ent == prog->edicts)
2314         {
2315                 // deprecated use
2316                 VectorCopy(s.origin, PRVM_clientglobalvector(pmove_org));
2317                 VectorCopy(s.velocity, PRVM_clientglobalvector(pmove_vel));
2318                 PRVM_clientglobalfloat(pmove_jump_held) = !s.cmd.canjump;
2319                 PRVM_clientglobalfloat(pmove_waterjumptime) = s.waterjumptime;
2320         }
2321         else
2322         {
2323                 // new use
2324                 VectorCopy(s.origin, PRVM_clientedictvector(ent, origin));
2325                 VectorCopy(s.velocity, PRVM_clientedictvector(ent, velocity));
2326                 PRVM_clientedictfloat(ent, pmove_flags) =
2327                         (s.crouched ? PMF_DUCKED : 0) |
2328                         (s.cmd.canjump ? 0 : PMF_JUMP_HELD) |
2329                         (s.onground ? PMF_ONGROUND : 0);
2330         }
2331 }
2332
2333 //#348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
2334 static void VM_CL_getplayerkey (prvm_prog_t *prog)
2335 {
2336         int                     i;
2337         char            t[128];
2338         const char      *c;
2339
2340         VM_SAFEPARMCOUNT(2, VM_CL_getplayerkey);
2341
2342         i = (int)PRVM_G_FLOAT(OFS_PARM0);
2343         c = PRVM_G_STRING(OFS_PARM1);
2344         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2345         Sbar_SortFrags();
2346
2347         if (i < 0)
2348                 i = Sbar_GetSortedPlayerIndex(-1-i);
2349         if(i < 0 || i >= cl.maxclients)
2350                 return;
2351
2352         t[0] = 0;
2353
2354         if(!strcasecmp(c, "name"))
2355                 strlcpy(t, cl.scores[i].name, sizeof(t));
2356         else
2357                 if(!strcasecmp(c, "frags"))
2358                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].frags);
2359         else
2360                 if(!strcasecmp(c, "ping"))
2361                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_ping);
2362         else
2363                 if(!strcasecmp(c, "pl"))
2364                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_packetloss);
2365         else
2366                 if(!strcasecmp(c, "movementloss"))
2367                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_movementloss);
2368         else
2369                 if(!strcasecmp(c, "entertime"))
2370                         dpsnprintf(t, sizeof(t), "%f", cl.scores[i].qw_entertime);
2371         else
2372                 if(!strcasecmp(c, "colors"))
2373                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors);
2374         else
2375                 if(!strcasecmp(c, "topcolor"))
2376                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors & 0xf0);
2377         else
2378                 if(!strcasecmp(c, "bottomcolor"))
2379                         dpsnprintf(t, sizeof(t), "%i", (cl.scores[i].colors &15)<<4);
2380         else
2381                 if(!strcasecmp(c, "viewentity"))
2382                         dpsnprintf(t, sizeof(t), "%i", i+1);
2383         if(!t[0])
2384                 return;
2385         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
2386 }
2387
2388 //#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
2389 static void VM_CL_setlistener (prvm_prog_t *prog)
2390 {
2391         vec3_t origin, forward, left, up;
2392         VM_SAFEPARMCOUNT(4, VM_CL_setlistener);
2393         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), origin);
2394         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), forward);
2395         VectorNegate(PRVM_G_VECTOR(OFS_PARM2), left);
2396         VectorCopy(PRVM_G_VECTOR(OFS_PARM3), up);
2397         Matrix4x4_FromVectors(&cl.csqc_listenermatrix, forward, left, up, origin);
2398         cl.csqc_usecsqclistener = true; //use csqc listener at this frame
2399 }
2400
2401 //#352 void(string cmdname) registercommand (EXT_CSQC)
2402 static void VM_CL_registercmd (prvm_prog_t *prog)
2403 {
2404         VM_SAFEPARMCOUNT(1, VM_CL_registercmd);
2405         if(!Cmd_Exists(cmd_local, PRVM_G_STRING(OFS_PARM0)))
2406                 Cmd_AddCommand(CF_CLIENT, PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
2407 }
2408
2409 //#360 float() readbyte (EXT_CSQC)
2410 static void VM_CL_ReadByte (prvm_prog_t *prog)
2411 {
2412         VM_SAFEPARMCOUNT(0, VM_CL_ReadByte);
2413         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadByte(&cl_message);
2414 }
2415
2416 //#361 float() readchar (EXT_CSQC)
2417 static void VM_CL_ReadChar (prvm_prog_t *prog)
2418 {
2419         VM_SAFEPARMCOUNT(0, VM_CL_ReadChar);
2420         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadChar(&cl_message);
2421 }
2422
2423 //#362 float() readshort (EXT_CSQC)
2424 static void VM_CL_ReadShort (prvm_prog_t *prog)
2425 {
2426         VM_SAFEPARMCOUNT(0, VM_CL_ReadShort);
2427         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadShort(&cl_message);
2428 }
2429
2430 //#363 float() readlong (EXT_CSQC)
2431 static void VM_CL_ReadLong (prvm_prog_t *prog)
2432 {
2433         VM_SAFEPARMCOUNT(0, VM_CL_ReadLong);
2434         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadLong(&cl_message);
2435 }
2436
2437 //#364 float() readcoord (EXT_CSQC)
2438 static void VM_CL_ReadCoord (prvm_prog_t *prog)
2439 {
2440         VM_SAFEPARMCOUNT(0, VM_CL_ReadCoord);
2441         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadCoord(&cl_message, cls.protocol);
2442 }
2443
2444 //#365 float() readangle (EXT_CSQC)
2445 static void VM_CL_ReadAngle (prvm_prog_t *prog)
2446 {
2447         VM_SAFEPARMCOUNT(0, VM_CL_ReadAngle);
2448         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadAngle(&cl_message, cls.protocol);
2449 }
2450
2451 //#366 string() readstring (EXT_CSQC)
2452 static void VM_CL_ReadString (prvm_prog_t *prog)
2453 {
2454         VM_SAFEPARMCOUNT(0, VM_CL_ReadString);
2455         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2456 }
2457
2458 //#367 float() readfloat (EXT_CSQC)
2459 static void VM_CL_ReadFloat (prvm_prog_t *prog)
2460 {
2461         VM_SAFEPARMCOUNT(0, VM_CL_ReadFloat);
2462         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat(&cl_message);
2463 }
2464
2465 //#501 string() readpicture (DP_CSQC_READWRITEPICTURE)
2466 extern cvar_t cl_readpicture_force;
2467 static void VM_CL_ReadPicture (prvm_prog_t *prog)
2468 {
2469         const char *name;
2470         unsigned char *data;
2471         unsigned char *buf;
2472         unsigned short size;
2473         int i;
2474         cachepic_t *pic;
2475
2476         VM_SAFEPARMCOUNT(0, VM_CL_ReadPicture);
2477
2478         name = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2479         size = (unsigned short) MSG_ReadShort(&cl_message);
2480
2481         // check if a texture of that name exists
2482         // if yes, it is used and the data is discarded
2483         // if not, the (low quality) data is used to build a new texture, whose name will get returned
2484
2485         pic = Draw_CachePic_Flags(name, CACHEPICFLAG_NOTPERSISTENT | CACHEPICFLAG_FAILONMISSING);
2486
2487         if(size)
2488         {
2489                 if (Draw_IsPicLoaded(pic) && !cl_readpicture_force.integer)
2490                 {
2491                         // texture found and loaded
2492                         // skip over the jpeg as we don't need it
2493                         for(i = 0; i < size; ++i)
2494                                 (void) MSG_ReadByte(&cl_message);
2495                 }
2496                 else
2497                 {
2498                         // texture not found
2499                         // use the attached jpeg as texture
2500                         buf = (unsigned char *) Mem_Alloc(tempmempool, size);
2501                         MSG_ReadBytes(&cl_message, size, buf);
2502                         data = JPEG_LoadImage_BGRA(buf, size, NULL);
2503                         Mem_Free(buf);
2504                         Draw_NewPic(name, image_width, image_height, data, TEXTYPE_BGRA, TEXF_CLAMP);
2505                         Mem_Free(data);
2506                 }
2507         }
2508
2509         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, name);
2510 }
2511
2512 //////////////////////////////////////////////////////////
2513
2514 static void VM_CL_makestatic (prvm_prog_t *prog)
2515 {
2516         prvm_edict_t *ent;
2517
2518         VM_SAFEPARMCOUNT(1, VM_CL_makestatic);
2519
2520         ent = PRVM_G_EDICT(OFS_PARM0);
2521         if (ent == prog->edicts)
2522         {
2523                 VM_Warning(prog, "makestatic: can not modify world entity\n");
2524                 return;
2525         }
2526         if (ent->free)
2527         {
2528                 VM_Warning(prog, "makestatic: can not modify free entity\n");
2529                 return;
2530         }
2531
2532         if (cl.num_static_entities < cl.max_static_entities)
2533         {
2534                 int renderflags;
2535                 entity_t *staticent = &cl.static_entities[cl.num_static_entities++];
2536
2537                 // copy it to the current state
2538                 memset(staticent, 0, sizeof(*staticent));
2539                 staticent->render.model = CL_GetModelByIndex((int)PRVM_clientedictfloat(ent, modelindex));
2540                 staticent->render.framegroupblend[0].frame = (int)PRVM_clientedictfloat(ent, frame);
2541                 staticent->render.framegroupblend[0].lerp = 1;
2542                 // make torchs play out of sync
2543                 staticent->render.framegroupblend[0].start = lhrandom(-10, -1);
2544                 staticent->render.skinnum = (int)PRVM_clientedictfloat(ent, skin);
2545                 staticent->render.effects = (int)PRVM_clientedictfloat(ent, effects);
2546                 staticent->render.alpha = PRVM_clientedictfloat(ent, alpha);
2547                 staticent->render.scale = PRVM_clientedictfloat(ent, scale);
2548                 VectorCopy(PRVM_clientedictvector(ent, colormod), staticent->render.colormod);
2549                 VectorCopy(PRVM_clientedictvector(ent, glowmod), staticent->render.glowmod);
2550
2551                 // sanitize values
2552                 if (!staticent->render.alpha)
2553                         staticent->render.alpha = 1.0f;
2554                 if (!staticent->render.scale)
2555                         staticent->render.scale = 1.0f;
2556                 if (!VectorLength2(staticent->render.colormod))
2557                         VectorSet(staticent->render.colormod, 1, 1, 1);
2558                 if (!VectorLength2(staticent->render.glowmod))
2559                         VectorSet(staticent->render.glowmod, 1, 1, 1);
2560
2561                 renderflags = (int)PRVM_clientedictfloat(ent, renderflags);
2562                 if (renderflags & RF_USEAXIS)
2563                 {
2564                         vec3_t forward, left, up, origin;
2565                         VectorCopy(PRVM_clientglobalvector(v_forward), forward);
2566                         VectorNegate(PRVM_clientglobalvector(v_right), left);
2567                         VectorCopy(PRVM_clientglobalvector(v_up), up);
2568                         VectorCopy(PRVM_clientedictvector(ent, origin), origin);
2569                         Matrix4x4_FromVectors(&staticent->render.matrix, forward, left, up, origin);
2570                         Matrix4x4_Scale(&staticent->render.matrix, staticent->render.scale, 1);
2571                 }
2572                 else
2573                         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);
2574
2575                 // either fullbright or lit
2576                 if(!r_fullbright.integer)
2577                 {
2578                         if (!(staticent->render.effects & EF_FULLBRIGHT))
2579                                 staticent->render.flags |= RENDER_LIGHT;
2580                 }
2581                 // turn off shadows from transparent objects
2582                 if (!(staticent->render.effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST)) && (staticent->render.alpha >= 1))
2583                         staticent->render.flags |= RENDER_SHADOW;
2584                 if (staticent->render.effects & EF_NODEPTHTEST)
2585                         staticent->render.flags |= RENDER_NODEPTHTEST;
2586                 if (staticent->render.effects & EF_ADDITIVE)
2587                         staticent->render.flags |= RENDER_ADDITIVE;
2588                 if (staticent->render.effects & EF_DOUBLESIDED)
2589                         staticent->render.flags |= RENDER_DOUBLESIDED;
2590
2591                 staticent->render.allowdecals = true;
2592                 CL_UpdateRenderEntity(&staticent->render);
2593         }
2594         else
2595                 Con_Printf("Too many static entities");
2596
2597 // throw the entity away now
2598         PRVM_ED_Free(prog, ent);
2599 }
2600
2601 //=================================================================//
2602
2603 /*
2604 =================
2605 VM_CL_copyentity
2606
2607 copies data from one entity to another
2608
2609 copyentity(src, dst)
2610 =================
2611 */
2612 static void VM_CL_copyentity (prvm_prog_t *prog)
2613 {
2614         prvm_edict_t *in, *out;
2615         VM_SAFEPARMCOUNT(2, VM_CL_copyentity);
2616         in = PRVM_G_EDICT(OFS_PARM0);
2617         if (in == prog->edicts)
2618         {
2619                 VM_Warning(prog, "copyentity: can not read world entity\n");
2620                 return;
2621         }
2622         if (in->free)
2623         {
2624                 VM_Warning(prog, "copyentity: can not read free entity\n");
2625                 return;
2626         }
2627         out = PRVM_G_EDICT(OFS_PARM1);
2628         if (out == prog->edicts)
2629         {
2630                 VM_Warning(prog, "copyentity: can not modify world entity\n");
2631                 return;
2632         }
2633         if (out->free)
2634         {
2635                 VM_Warning(prog, "copyentity: can not modify free entity\n");
2636                 return;
2637         }
2638         memcpy(out->fields.fp, in->fields.fp, prog->entityfields * sizeof(prvm_vec_t));
2639
2640         if (VectorCompare(PRVM_clientedictvector(out, absmin), PRVM_clientedictvector(out, absmax)))
2641                 return;
2642         CL_LinkEdict(out);
2643 }
2644
2645 //=================================================================//
2646
2647 // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
2648 static void VM_CL_effect (prvm_prog_t *prog)
2649 {
2650         model_t *model;
2651         vec3_t org;
2652         VM_SAFEPARMCOUNT(5, VM_CL_effect);
2653         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
2654
2655         model = Mod_FindName(PRVM_G_STRING(OFS_PARM1), NULL);
2656         if(model->loaded)
2657                 CL_Effect(org, model, (int)PRVM_G_FLOAT(OFS_PARM2), (int)PRVM_G_FLOAT(OFS_PARM3), PRVM_G_FLOAT(OFS_PARM4));
2658         else
2659                 Con_Printf(CON_ERROR "VM_CL_effect: Could not load model '%s'\n", PRVM_G_STRING(OFS_PARM1));
2660 }
2661
2662 // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
2663 static void VM_CL_te_blood (prvm_prog_t *prog)
2664 {
2665         vec3_t pos, vel, pos2;
2666         VM_SAFEPARMCOUNT(3, VM_CL_te_blood);
2667         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
2668                 return;
2669         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2670         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), vel);
2671         CL_FindNonSolidLocation(pos, pos2, 4);
2672         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, vel, vel, NULL, 0);
2673 }
2674
2675 // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
2676 static void VM_CL_te_bloodshower (prvm_prog_t *prog)
2677 {
2678         vec_t speed;
2679         vec3_t mincorner, maxcorner, vel1, vel2;
2680         VM_SAFEPARMCOUNT(4, VM_CL_te_bloodshower);
2681         if (PRVM_G_FLOAT(OFS_PARM3) < 1)
2682                 return;
2683         speed = PRVM_G_FLOAT(OFS_PARM2);
2684         vel1[0] = -speed;
2685         vel1[1] = -speed;
2686         vel1[2] = -speed;
2687         vel2[0] = speed;
2688         vel2[1] = speed;
2689         vel2[2] = speed;
2690         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), mincorner);
2691         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), maxcorner);
2692         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM3), mincorner, maxcorner, vel1, vel2, NULL, 0);
2693 }
2694
2695 // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
2696 static void VM_CL_te_explosionrgb (prvm_prog_t *prog)
2697 {
2698         vec3_t          pos;
2699         vec3_t          pos2;
2700         matrix4x4_t     tempmatrix;
2701         VM_SAFEPARMCOUNT(2, VM_CL_te_explosionrgb);
2702         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2703         CL_FindNonSolidLocation(pos, pos2, 10);
2704         CL_ParticleExplosion(pos2);
2705         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
2706         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, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
2707 }
2708
2709 // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
2710 static void VM_CL_te_particlecube (prvm_prog_t *prog)
2711 {
2712         vec3_t mincorner, maxcorner, vel;
2713         VM_SAFEPARMCOUNT(7, VM_CL_te_particlecube);
2714         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), mincorner);
2715         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), maxcorner);
2716         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), vel);
2717         CL_ParticleCube(mincorner, maxcorner, vel, (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4), PRVM_G_FLOAT(OFS_PARM5), PRVM_G_FLOAT(OFS_PARM6));
2718 }
2719
2720 // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
2721 static void VM_CL_te_particlerain (prvm_prog_t *prog)
2722 {
2723         vec3_t mincorner, maxcorner, vel;
2724         VM_SAFEPARMCOUNT(5, VM_CL_te_particlerain);
2725         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), mincorner);
2726         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), maxcorner);
2727         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), vel);
2728         CL_ParticleRain(mincorner, maxcorner, vel, (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4), 0);
2729 }
2730
2731 // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
2732 static void VM_CL_te_particlesnow (prvm_prog_t *prog)
2733 {
2734         vec3_t mincorner, maxcorner, vel;
2735         VM_SAFEPARMCOUNT(5, VM_CL_te_particlesnow);
2736         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), mincorner);
2737         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), maxcorner);
2738         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), vel);
2739         CL_ParticleRain(mincorner, maxcorner, vel, (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4), 1);
2740 }
2741
2742 // #411 void(vector org, vector vel, float howmany) te_spark
2743 static void VM_CL_te_spark (prvm_prog_t *prog)
2744 {
2745         vec3_t pos, pos2, vel;
2746         VM_SAFEPARMCOUNT(3, VM_CL_te_spark);
2747
2748         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2749         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), vel);
2750         CL_FindNonSolidLocation(pos, pos2, 4);
2751         CL_ParticleEffect(EFFECT_TE_SPARK, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, vel, vel, NULL, 0);
2752 }
2753
2754 extern cvar_t cl_sound_ric_gunshot;
2755 // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
2756 static void VM_CL_te_gunshotquad (prvm_prog_t *prog)
2757 {
2758         vec3_t          pos, pos2;
2759         int                     rnd;
2760         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshotquad);
2761
2762         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2763         CL_FindNonSolidLocation(pos, pos2, 4);
2764         CL_ParticleEffect(EFFECT_TE_GUNSHOTQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2765         if(cl_sound_ric_gunshot.integer >= 2)
2766         {
2767                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2768                 else
2769                 {
2770                         rnd = rand() & 3;
2771                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2772                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2773                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2774                 }
2775         }
2776 }
2777
2778 // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
2779 static void VM_CL_te_spikequad (prvm_prog_t *prog)
2780 {
2781         vec3_t          pos, pos2;
2782         int                     rnd;
2783         VM_SAFEPARMCOUNT(1, VM_CL_te_spikequad);
2784
2785         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2786         CL_FindNonSolidLocation(pos, pos2, 4);
2787         CL_ParticleEffect(EFFECT_TE_SPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2788         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2789         else
2790         {
2791                 rnd = rand() & 3;
2792                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2793                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2794                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2795         }
2796 }
2797
2798 // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1)
2799 static void VM_CL_te_superspikequad (prvm_prog_t *prog)
2800 {
2801         vec3_t          pos, pos2;
2802         int                     rnd;
2803         VM_SAFEPARMCOUNT(1, VM_CL_te_superspikequad);
2804
2805         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2806         CL_FindNonSolidLocation(pos, pos2, 4);
2807         CL_ParticleEffect(EFFECT_TE_SUPERSPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2808         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
2809         else
2810         {
2811                 rnd = rand() & 3;
2812                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2813                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2814                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2815         }
2816 }
2817
2818 // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1)
2819 static void VM_CL_te_explosionquad (prvm_prog_t *prog)
2820 {
2821         vec3_t          pos, pos2;
2822         VM_SAFEPARMCOUNT(1, VM_CL_te_explosionquad);
2823
2824         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2825         CL_FindNonSolidLocation(pos, pos2, 10);
2826         CL_ParticleEffect(EFFECT_TE_EXPLOSIONQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2827         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2828 }
2829
2830 // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
2831 static void VM_CL_te_smallflash (prvm_prog_t *prog)
2832 {
2833         vec3_t          pos, pos2;
2834         VM_SAFEPARMCOUNT(1, VM_CL_te_smallflash);
2835
2836         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2837         CL_FindNonSolidLocation(pos, pos2, 10);
2838         CL_ParticleEffect(EFFECT_TE_SMALLFLASH, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2839 }
2840
2841 // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
2842 static void VM_CL_te_customflash (prvm_prog_t *prog)
2843 {
2844         vec3_t          pos, pos2;
2845         matrix4x4_t     tempmatrix;
2846         VM_SAFEPARMCOUNT(4, VM_CL_te_customflash);
2847
2848         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2849         CL_FindNonSolidLocation(pos, pos2, 4);
2850         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
2851         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), NULL, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
2852 }
2853
2854 // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
2855 static void VM_CL_te_gunshot (prvm_prog_t *prog)
2856 {
2857         vec3_t          pos, pos2;
2858         int                     rnd;
2859         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshot);
2860
2861         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2862         CL_FindNonSolidLocation(pos, pos2, 4);
2863         CL_ParticleEffect(EFFECT_TE_GUNSHOT, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2864         if(cl_sound_ric_gunshot.integer == 1 || cl_sound_ric_gunshot.integer == 3)
2865         {
2866                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2867                 else
2868                 {
2869                         rnd = rand() & 3;
2870                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2871                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2872                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2873                 }
2874         }
2875 }
2876
2877 // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
2878 static void VM_CL_te_spike (prvm_prog_t *prog)
2879 {
2880         vec3_t          pos, pos2;
2881         int                     rnd;
2882         VM_SAFEPARMCOUNT(1, VM_CL_te_spike);
2883
2884         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2885         CL_FindNonSolidLocation(pos, pos2, 4);
2886         CL_ParticleEffect(EFFECT_TE_SPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2887         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2888         else
2889         {
2890                 rnd = rand() & 3;
2891                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2892                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2893                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2894         }
2895 }
2896
2897 // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
2898 static void VM_CL_te_superspike (prvm_prog_t *prog)
2899 {
2900         vec3_t          pos, pos2;
2901         int                     rnd;
2902         VM_SAFEPARMCOUNT(1, VM_CL_te_superspike);
2903
2904         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2905         CL_FindNonSolidLocation(pos, pos2, 4);
2906         CL_ParticleEffect(EFFECT_TE_SUPERSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2907         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2908         else
2909         {
2910                 rnd = rand() & 3;
2911                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2912                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2913                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2914         }
2915 }
2916
2917 // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
2918 static void VM_CL_te_explosion (prvm_prog_t *prog)
2919 {
2920         vec3_t          pos, pos2;
2921         VM_SAFEPARMCOUNT(1, VM_CL_te_explosion);
2922
2923         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2924         CL_FindNonSolidLocation(pos, pos2, 10);
2925         CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2926         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2927 }
2928
2929 // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
2930 static void VM_CL_te_tarexplosion (prvm_prog_t *prog)
2931 {
2932         vec3_t          pos, pos2;
2933         VM_SAFEPARMCOUNT(1, VM_CL_te_tarexplosion);
2934
2935         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2936         CL_FindNonSolidLocation(pos, pos2, 10);
2937         CL_ParticleEffect(EFFECT_TE_TAREXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2938         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2939 }
2940
2941 // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
2942 static void VM_CL_te_wizspike (prvm_prog_t *prog)
2943 {
2944         vec3_t          pos, pos2;
2945         VM_SAFEPARMCOUNT(1, VM_CL_te_wizspike);
2946
2947         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2948         CL_FindNonSolidLocation(pos, pos2, 4);
2949         CL_ParticleEffect(EFFECT_TE_WIZSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2950         S_StartSound(-1, 0, cl.sfx_wizhit, pos2, 1, 1);
2951 }
2952
2953 // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
2954 static void VM_CL_te_knightspike (prvm_prog_t *prog)
2955 {
2956         vec3_t          pos, pos2;
2957         VM_SAFEPARMCOUNT(1, VM_CL_te_knightspike);
2958
2959         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2960         CL_FindNonSolidLocation(pos, pos2, 4);
2961         CL_ParticleEffect(EFFECT_TE_KNIGHTSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2962         S_StartSound(-1, 0, cl.sfx_knighthit, pos2, 1, 1);
2963 }
2964
2965 // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
2966 static void VM_CL_te_lavasplash (prvm_prog_t *prog)
2967 {
2968         vec3_t          pos;
2969         VM_SAFEPARMCOUNT(1, VM_CL_te_lavasplash);
2970         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2971         CL_ParticleEffect(EFFECT_TE_LAVASPLASH, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2972 }
2973
2974 // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
2975 static void VM_CL_te_teleport (prvm_prog_t *prog)
2976 {
2977         vec3_t          pos;
2978         VM_SAFEPARMCOUNT(1, VM_CL_te_teleport);
2979         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2980         CL_ParticleEffect(EFFECT_TE_TELEPORT, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2981 }
2982
2983 // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
2984 static void VM_CL_te_explosion2 (prvm_prog_t *prog)
2985 {
2986         vec3_t          pos, pos2, color;
2987         matrix4x4_t     tempmatrix;
2988         int                     colorStart, colorLength;
2989         unsigned char           *tempcolor;
2990         VM_SAFEPARMCOUNT(3, VM_CL_te_explosion2);
2991
2992         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2993         colorStart = (int)PRVM_G_FLOAT(OFS_PARM1);
2994         colorLength = (int)PRVM_G_FLOAT(OFS_PARM2);
2995         CL_FindNonSolidLocation(pos, pos2, 10);
2996         CL_ParticleExplosion2(pos2, colorStart, colorLength);
2997         tempcolor = palette_rgb[(rand()%colorLength) + colorStart];
2998         color[0] = tempcolor[0] * (2.0f / 255.0f);
2999         color[1] = tempcolor[1] * (2.0f / 255.0f);
3000         color[2] = tempcolor[2] * (2.0f / 255.0f);
3001         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
3002         CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
3003         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
3004 }
3005
3006
3007 // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
3008 static void VM_CL_te_lightning1 (prvm_prog_t *prog)
3009 {
3010         vec3_t          start, end;
3011         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning1);
3012         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), start);
3013         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), end);
3014         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), start, end, cl.model_bolt, true);
3015 }
3016
3017 // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
3018 static void VM_CL_te_lightning2 (prvm_prog_t *prog)
3019 {
3020         vec3_t          start, end;
3021         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning2);
3022         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), start);
3023         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), end);
3024         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), start, end, cl.model_bolt2, true);
3025 }
3026
3027 // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
3028 static void VM_CL_te_lightning3 (prvm_prog_t *prog)
3029 {
3030         vec3_t          start, end;
3031         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning3);
3032         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), start);
3033         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), end);
3034         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), start, end, cl.model_bolt3, false);
3035 }
3036
3037 // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
3038 static void VM_CL_te_beam (prvm_prog_t *prog)
3039 {
3040         vec3_t          start, end;
3041         VM_SAFEPARMCOUNT(3, VM_CL_te_beam);
3042         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), start);
3043         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), end);
3044         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), start, end, cl.model_beam, false);
3045 }
3046
3047 // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
3048 static void VM_CL_te_plasmaburn (prvm_prog_t *prog)
3049 {
3050         vec3_t          pos, pos2;
3051         VM_SAFEPARMCOUNT(1, VM_CL_te_plasmaburn);
3052
3053         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
3054         CL_FindNonSolidLocation(pos, pos2, 4);
3055         CL_ParticleEffect(EFFECT_TE_PLASMABURN, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
3056 }
3057
3058 // #457 void(vector org, vector velocity, float howmany) te_flamejet (DP_TE_FLAMEJET)
3059 static void VM_CL_te_flamejet (prvm_prog_t *prog)
3060 {
3061         vec3_t          pos, pos2, vel;
3062         VM_SAFEPARMCOUNT(3, VM_CL_te_flamejet);
3063         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
3064                 return;
3065         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
3066         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), vel);
3067         CL_FindNonSolidLocation(pos, pos2, 4);
3068         CL_ParticleEffect(EFFECT_TE_FLAMEJET, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, vel, vel, NULL, 0);
3069 }
3070
3071
3072 // #443 void(entity e, entity tagentity, string tagname) setattachment
3073 static void VM_CL_setattachment (prvm_prog_t *prog)
3074 {
3075         prvm_edict_t *e;
3076         prvm_edict_t *tagentity;
3077         const char *tagname;
3078         int modelindex;
3079         int tagindex;
3080         model_t *model;
3081         VM_SAFEPARMCOUNT(3, VM_CL_setattachment);
3082
3083         e = PRVM_G_EDICT(OFS_PARM0);
3084         tagentity = PRVM_G_EDICT(OFS_PARM1);
3085         tagname = PRVM_G_STRING(OFS_PARM2);
3086
3087         if (e == prog->edicts)
3088         {
3089                 VM_Warning(prog, "setattachment: can not modify world entity\n");
3090                 return;
3091         }
3092         if (e->free)
3093         {
3094                 VM_Warning(prog, "setattachment: can not modify free entity\n");
3095                 return;
3096         }
3097
3098         if (tagentity == NULL)
3099                 tagentity = prog->edicts;
3100
3101         tagindex = 0;
3102         if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0])
3103         {
3104                 modelindex = (int)PRVM_clientedictfloat(tagentity, modelindex);
3105                 model = CL_GetModelByIndex(modelindex);
3106                 if (model)
3107                 {
3108                         tagindex = Mod_Alias_GetTagIndexForName(model, (int)PRVM_clientedictfloat(tagentity, skin), tagname);
3109                         if (tagindex == 0)
3110                                 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);
3111                 }
3112                 else
3113                         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));
3114         }
3115
3116         PRVM_clientedictedict(e, tag_entity) = PRVM_EDICT_TO_PROG(tagentity);
3117         PRVM_clientedictfloat(e, tag_index) = tagindex;
3118 }
3119
3120 /////////////////////////////////////////
3121 // DP_MD3_TAGINFO extension coded by VorteX
3122
3123 static int CL_GetTagIndex (prvm_prog_t *prog, prvm_edict_t *e, const char *tagname)
3124 {
3125         model_t *model = CL_GetModelFromEdict(e);
3126         if (model)
3127                 return Mod_Alias_GetTagIndexForName(model, (int)PRVM_clientedictfloat(e, skin), tagname);
3128         else
3129                 return -1;
3130 }
3131
3132 static int CL_GetExtendedTagInfo (prvm_prog_t *prog, prvm_edict_t *e, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
3133 {
3134         int r;
3135         model_t *model;
3136
3137         *tagname = NULL;
3138         *parentindex = 0;
3139         Matrix4x4_CreateIdentity(tag_localmatrix);
3140
3141         if (tagindex >= 0
3142          && (model = CL_GetModelFromEdict(e))
3143          && model->animscenes)
3144         {
3145                 r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)PRVM_clientedictfloat(e, skin), e->priv.server->frameblend, &e->priv.server->skeleton, tagindex - 1, parentindex, tagname, tag_localmatrix);
3146
3147                 if(!r) // success?
3148                         *parentindex += 1;
3149
3150                 return r;
3151         }
3152
3153         return 1;
3154 }
3155
3156 int CL_GetPitchSign(prvm_prog_t *prog, prvm_edict_t *ent)
3157 {
3158         model_t *model;
3159         if ((model = CL_GetModelFromEdict(ent)) && model->type == mod_alias)
3160                 return -1;
3161         return 1;
3162 }
3163
3164 void CL_GetEntityMatrix (prvm_prog_t *prog, prvm_edict_t *ent, matrix4x4_t *out, qbool viewmatrix)
3165 {
3166         float scale;
3167         float pitchsign = 1;
3168
3169         scale = PRVM_clientedictfloat(ent, scale);
3170         if (!scale)
3171                 scale = 1.0f;
3172
3173         if(viewmatrix)
3174                 *out = r_refdef.view.matrix;
3175         else if ((int)PRVM_clientedictfloat(ent, renderflags) & RF_USEAXIS)
3176         {
3177                 vec3_t forward;
3178                 vec3_t left;
3179                 vec3_t up;
3180                 vec3_t origin;
3181                 VectorScale(PRVM_clientglobalvector(v_forward), scale, forward);
3182                 VectorScale(PRVM_clientglobalvector(v_right), -scale, left);
3183                 VectorScale(PRVM_clientglobalvector(v_up), scale, up);
3184                 VectorCopy(PRVM_clientedictvector(ent, origin), origin);
3185                 Matrix4x4_FromVectors(out, forward, left, up, origin);
3186         }
3187         else
3188         {
3189                 pitchsign = CL_GetPitchSign(prog, ent);
3190                 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);
3191         }
3192 }
3193
3194 static int CL_GetEntityLocalTagMatrix(prvm_prog_t *prog, prvm_edict_t *ent, int tagindex, matrix4x4_t *out)
3195 {
3196         model_t *model;
3197         if (tagindex >= 0
3198          && (model = CL_GetModelFromEdict(ent))
3199          && model->animscenes)
3200         {
3201                 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
3202                 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, cl.time);
3203                 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
3204                 return Mod_Alias_GetTagMatrix(model, ent->priv.server->frameblend, &ent->priv.server->skeleton, tagindex, out);
3205         }
3206         *out = identitymatrix;
3207         return 0;
3208 }
3209
3210 // Warnings/errors code:
3211 // 0 - normal (everything all-right)
3212 // 1 - world entity
3213 // 2 - free entity
3214 // 3 - null or non-precached model
3215 // 4 - no tags with requested index
3216 // 5 - runaway loop at attachment chain
3217 extern cvar_t cl_bob;
3218 extern cvar_t cl_bobcycle;
3219 extern cvar_t cl_bobup;
3220 int CL_GetTagMatrix (prvm_prog_t *prog, matrix4x4_t *out, prvm_edict_t *ent, int tagindex, prvm_vec_t *returnshadingorigin)
3221 {
3222         int ret;
3223         int attachloop;
3224         matrix4x4_t entitymatrix, tagmatrix, attachmatrix;
3225         model_t *model;
3226         vec3_t shadingorigin;
3227
3228         *out = identitymatrix; // warnings and errors return identical matrix
3229
3230         if (ent == prog->edicts)
3231                 return 1;
3232         if (ent->free)
3233                 return 2;
3234
3235         model = CL_GetModelFromEdict(ent);
3236         if(!model)
3237                 return 3;
3238
3239         tagmatrix = identitymatrix;
3240         attachloop = 0;
3241         for(;;)
3242         {
3243                 if(attachloop >= 256)
3244                         return 5;
3245                 // apply transformation by child's tagindex on parent entity and then
3246                 // by parent entity itself
3247                 ret = CL_GetEntityLocalTagMatrix(prog, ent, tagindex - 1, &attachmatrix);
3248                 if(ret && attachloop == 0)
3249                         return ret;
3250                 CL_GetEntityMatrix(prog, ent, &entitymatrix, false);
3251                 Matrix4x4_Concat(&tagmatrix, &attachmatrix, out);
3252                 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
3253                 // next iteration we process the parent entity
3254                 if (PRVM_clientedictedict(ent, tag_entity))
3255                 {
3256                         tagindex = (int)PRVM_clientedictfloat(ent, tag_index);
3257                         ent = PRVM_EDICT_NUM(PRVM_clientedictedict(ent, tag_entity));
3258                 }
3259                 else
3260                         break;
3261                 attachloop++;
3262         }
3263
3264         // RENDER_VIEWMODEL magic
3265         if ((int)PRVM_clientedictfloat(ent, renderflags) & RF_VIEWMODEL)
3266         {
3267                 Matrix4x4_Copy(&tagmatrix, out);
3268
3269                 CL_GetEntityMatrix(prog, prog->edicts, &entitymatrix, true);
3270                 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
3271
3272                 /*
3273                 // Cl_bob, ported from rendering code
3274                 if (PRVM_clientedictfloat(ent, health) > 0 && cl_bob.value && cl_bobcycle.value)
3275                 {
3276                         double bob, cycle;
3277                         // LadyHavoc: this code is *weird*, but not replacable (I think it
3278                         // should be done in QC on the server, but oh well, quake is quake)
3279                         // LadyHavoc: figured out bobup: the time at which the sin is at 180
3280                         // degrees (which allows lengthening or squishing the peak or valley)
3281                         cycle = cl.time/cl_bobcycle.value;
3282                         cycle -= (int)cycle;
3283                         if (cycle < cl_bobup.value)
3284                                 cycle = sin(M_PI * cycle / cl_bobup.value);
3285                         else
3286                                 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
3287                         // bob is proportional to velocity in the xy plane
3288                         // (don't count Z, or jumping messes it up)
3289                         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;
3290                         bob = bob*0.3 + bob*0.7*cycle;
3291                         Matrix4x4_AdjustOrigin(out, 0, 0, bound(-7, bob, 4));
3292                 }
3293                 */
3294
3295                 // return the origin of the view
3296                 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, shadingorigin);
3297         }
3298         else
3299         {
3300                 // return the origin of the root entity in the chain
3301                 Matrix4x4_OriginFromMatrix(out, shadingorigin);
3302         }
3303         if (returnshadingorigin)
3304                 VectorCopy(shadingorigin, returnshadingorigin);
3305         return 0;
3306 }
3307
3308 // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
3309 static void VM_CL_gettagindex (prvm_prog_t *prog)
3310 {
3311         prvm_edict_t *ent;
3312         const char *tag_name;
3313         int tag_index;
3314
3315         VM_SAFEPARMCOUNT(2, VM_CL_gettagindex);
3316
3317         ent = PRVM_G_EDICT(OFS_PARM0);
3318         tag_name = PRVM_G_STRING(OFS_PARM1);
3319         if (ent == prog->edicts)
3320         {
3321                 VM_Warning(prog, "VM_CL_gettagindex(entity #%i): can't affect world entity\n", PRVM_NUM_FOR_EDICT(ent));
3322                 return;
3323         }
3324         if (ent->free)
3325         {
3326                 VM_Warning(prog, "VM_CL_gettagindex(entity #%i): can't affect free entity\n", PRVM_NUM_FOR_EDICT(ent));
3327                 return;
3328         }
3329
3330         tag_index = 0;
3331         if (!CL_GetModelFromEdict(ent))
3332                 Con_DPrintf("VM_CL_gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
3333         else
3334         {
3335                 tag_index = CL_GetTagIndex(prog, ent, tag_name);
3336                 if (tag_index == 0)
3337                         if(developer_extra.integer)
3338                                 Con_DPrintf("VM_CL_gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
3339         }
3340         PRVM_G_FLOAT(OFS_RETURN) = tag_index;
3341 }
3342
3343 // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO)
3344 static void VM_CL_gettaginfo (prvm_prog_t *prog)
3345 {
3346         prvm_edict_t *e;
3347         int tagindex;
3348         matrix4x4_t tag_matrix;
3349         matrix4x4_t tag_localmatrix;
3350         int parentindex;
3351         const char *tagname;
3352         int returncode;
3353         vec3_t forward, left, up, origin;
3354         const model_t *model;
3355
3356         VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo);
3357
3358         e = PRVM_G_EDICT(OFS_PARM0);
3359         tagindex = (int)PRVM_G_FLOAT(OFS_PARM1);
3360         returncode = CL_GetTagMatrix(prog, &tag_matrix, e, tagindex, NULL);
3361         Matrix4x4_ToVectors(&tag_matrix, forward, left, up, origin);
3362         VectorCopy(forward, PRVM_clientglobalvector(v_forward));
3363         VectorScale(left, -1, PRVM_clientglobalvector(v_right));
3364         VectorCopy(up, PRVM_clientglobalvector(v_up));
3365         VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
3366         model = CL_GetModelFromEdict(e);
3367         VM_GenerateFrameGroupBlend(prog, e->priv.server->framegroupblend, e);
3368         VM_FrameBlendFromFrameGroupBlend(e->priv.server->frameblend, e->priv.server->framegroupblend, model, cl.time);
3369         VM_UpdateEdictSkeleton(prog, e, model, e->priv.server->frameblend);
3370         CL_GetExtendedTagInfo(prog, e, tagindex, &parentindex, &tagname, &tag_localmatrix);
3371         Matrix4x4_ToVectors(&tag_localmatrix, forward, left, up, origin);
3372
3373         PRVM_clientglobalfloat(gettaginfo_parent) = parentindex;
3374         PRVM_clientglobalstring(gettaginfo_name) = tagname ? PRVM_SetTempString(prog, tagname) : 0;
3375         VectorCopy(forward, PRVM_clientglobalvector(gettaginfo_forward));
3376         VectorScale(left, -1, PRVM_clientglobalvector(gettaginfo_right));
3377         VectorCopy(up, PRVM_clientglobalvector(gettaginfo_up));
3378         VectorCopy(origin, PRVM_clientglobalvector(gettaginfo_offset));
3379
3380         switch(returncode)
3381         {
3382                 case 1:
3383                         VM_Warning(prog, "gettagindex: can't affect world entity\n");
3384                         break;
3385                 case 2:
3386                         VM_Warning(prog, "gettagindex: can't affect free entity\n");
3387                         break;
3388                 case 3:
3389                         Con_DPrintf("CL_GetTagMatrix(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(e));
3390                         break;
3391                 case 4:
3392                         Con_DPrintf("CL_GetTagMatrix(entity #%i): model has no tag with requested index %i\n", PRVM_NUM_FOR_EDICT(e), tagindex);
3393                         break;
3394                 case 5:
3395                         Con_DPrintf("CL_GetTagMatrix(entity #%i): runaway loop at attachment chain\n", PRVM_NUM_FOR_EDICT(e));
3396                         break;
3397         }
3398 }
3399
3400 //============================================================================
3401
3402 //====================
3403 // DP_CSQC_SPAWNPARTICLE
3404 // a QC hook to engine's CL_NewParticle
3405 //====================
3406
3407 // particle theme struct
3408 typedef struct vmparticletheme_s
3409 {
3410         unsigned short typeindex;
3411         qbool initialized;
3412         pblend_t blendmode;
3413         porientation_t orientation;
3414         int color1;
3415         int color2;
3416         int tex;
3417         float size;
3418         float sizeincrease;
3419         float alpha;
3420         float alphafade;
3421         float gravity;
3422         float bounce;
3423         float airfriction;
3424         float liquidfriction;
3425         float originjitter;
3426         float velocityjitter;
3427         qbool qualityreduction;
3428         float lifetime;
3429         float stretch;
3430         int staincolor1;
3431         int staincolor2;
3432         int staintex;
3433         float stainalpha;
3434         float stainsize;
3435         float delayspawn;
3436         float delaycollision;
3437         float angle;
3438         float spin;
3439 }vmparticletheme_t;
3440
3441 // particle spawner
3442 typedef struct vmparticlespawner_s
3443 {
3444         mempool_t                       *pool;
3445         qbool                   initialized;
3446         qbool                   verified;
3447         vmparticletheme_t       *themes;
3448         int                                     max_themes;
3449 }vmparticlespawner_t;
3450
3451 vmparticlespawner_t vmpartspawner;
3452
3453 // TODO: automatic max_themes grow
3454 static void VM_InitParticleSpawner (prvm_prog_t *prog, int maxthemes)
3455 {
3456         // bound max themes to not be an insane value
3457         if (maxthemes < 4)
3458                 maxthemes = 4;
3459         if (maxthemes > 2048)
3460                 maxthemes = 2048;
3461         // allocate and set up structure
3462         if (vmpartspawner.initialized) // reallocate
3463         {
3464                 Mem_FreePool(&vmpartspawner.pool);
3465                 memset(&vmpartspawner, 0, sizeof(vmparticlespawner_t));
3466         }
3467         vmpartspawner.pool = Mem_AllocPool("VMPARTICLESPAWNER", 0, NULL);
3468         vmpartspawner.themes = (vmparticletheme_t *)Mem_Alloc(vmpartspawner.pool, sizeof(vmparticletheme_t)*maxthemes);
3469         vmpartspawner.max_themes = maxthemes;
3470         vmpartspawner.initialized = true;
3471         vmpartspawner.verified = true;
3472 }
3473
3474 // reset particle theme to default values
3475 static void VM_ResetParticleTheme (vmparticletheme_t *theme)
3476 {
3477         theme->initialized = true;
3478         theme->typeindex = pt_static;
3479         theme->blendmode = PBLEND_ADD;
3480         theme->orientation = PARTICLE_BILLBOARD;
3481         theme->color1 = 0x808080;
3482         theme->color2 = 0xFFFFFF;
3483         theme->tex = 63;
3484         theme->size = 2;
3485         theme->sizeincrease = 0;
3486         theme->alpha = 256;
3487         theme->alphafade = 512;
3488         theme->gravity = 0.0f;
3489         theme->bounce = 0.0f;
3490         theme->airfriction = 1.0f;
3491         theme->liquidfriction = 4.0f;
3492         theme->originjitter = 0.0f;
3493         theme->velocityjitter = 0.0f;
3494         theme->qualityreduction = false;
3495         theme->lifetime = 4;
3496         theme->stretch = 1;
3497         theme->staincolor1 = -1;
3498         theme->staincolor2 = -1;
3499         theme->staintex = -1;
3500         theme->delayspawn = 0.0f;
3501         theme->delaycollision = 0.0f;
3502         theme->angle = 0.0f;
3503         theme->spin = 0.0f;
3504 }
3505
3506 // particle theme -> QC globals
3507 static void VM_CL_ParticleThemeToGlobals(vmparticletheme_t *theme, prvm_prog_t *prog)
3508 {
3509         PRVM_clientglobalfloat(particle_type) = theme->typeindex;
3510         PRVM_clientglobalfloat(particle_blendmode) = theme->blendmode;
3511         PRVM_clientglobalfloat(particle_orientation) = theme->orientation;
3512         // VorteX: int only can store 0-255, not 0-256 which means 0 - 0,99609375...
3513         VectorSet(PRVM_clientglobalvector(particle_color1), (theme->color1 >> 16) & 0xFF, (theme->color1 >> 8) & 0xFF, (theme->color1 >> 0) & 0xFF);
3514         VectorSet(PRVM_clientglobalvector(particle_color2), (theme->color2 >> 16) & 0xFF, (theme->color2 >> 8) & 0xFF, (theme->color2 >> 0) & 0xFF);
3515         PRVM_clientglobalfloat(particle_tex) = (prvm_vec_t)theme->tex;
3516         PRVM_clientglobalfloat(particle_size) = theme->size;
3517         PRVM_clientglobalfloat(particle_sizeincrease) = theme->sizeincrease;
3518         PRVM_clientglobalfloat(particle_alpha) = theme->alpha/256;
3519         PRVM_clientglobalfloat(particle_alphafade) = theme->alphafade/256;
3520         PRVM_clientglobalfloat(particle_time) = theme->lifetime;
3521         PRVM_clientglobalfloat(particle_gravity) = theme->gravity;
3522         PRVM_clientglobalfloat(particle_bounce) = theme->bounce;
3523         PRVM_clientglobalfloat(particle_airfriction) = theme->airfriction;
3524         PRVM_clientglobalfloat(particle_liquidfriction) = theme->liquidfriction;
3525         PRVM_clientglobalfloat(particle_originjitter) = theme->originjitter;
3526         PRVM_clientglobalfloat(particle_velocityjitter) = theme->velocityjitter;
3527         PRVM_clientglobalfloat(particle_qualityreduction) = theme->qualityreduction;
3528         PRVM_clientglobalfloat(particle_stretch) = theme->stretch;
3529         VectorSet(PRVM_clientglobalvector(particle_staincolor1), ((int)theme->staincolor1 >> 16) & 0xFF, ((int)theme->staincolor1 >> 8) & 0xFF, ((int)theme->staincolor1 >> 0) & 0xFF);
3530         VectorSet(PRVM_clientglobalvector(particle_staincolor2), ((int)theme->staincolor2 >> 16) & 0xFF, ((int)theme->staincolor2 >> 8) & 0xFF, ((int)theme->staincolor2 >> 0) & 0xFF);
3531         PRVM_clientglobalfloat(particle_staintex) = (prvm_vec_t)theme->staintex;
3532         PRVM_clientglobalfloat(particle_stainalpha) = (prvm_vec_t)theme->stainalpha/256;
3533         PRVM_clientglobalfloat(particle_stainsize) = (prvm_vec_t)theme->stainsize;
3534         PRVM_clientglobalfloat(particle_delayspawn) = theme->delayspawn;
3535         PRVM_clientglobalfloat(particle_delaycollision) = theme->delaycollision;
3536         PRVM_clientglobalfloat(particle_angle) = theme->angle;
3537         PRVM_clientglobalfloat(particle_spin) = theme->spin;
3538 }
3539
3540 // QC globals ->  particle theme
3541 static void VM_CL_ParticleThemeFromGlobals(vmparticletheme_t *theme, prvm_prog_t *prog)
3542 {
3543         theme->typeindex = (unsigned short)PRVM_clientglobalfloat(particle_type);
3544         theme->blendmode = (pblend_t)(int)PRVM_clientglobalfloat(particle_blendmode);
3545         theme->orientation = (porientation_t)(int)PRVM_clientglobalfloat(particle_orientation);
3546         theme->color1 = ((int)PRVM_clientglobalvector(particle_color1)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color1)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color1)[2]);
3547         theme->color2 = ((int)PRVM_clientglobalvector(particle_color2)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color2)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color2)[2]);
3548         theme->tex = (int)PRVM_clientglobalfloat(particle_tex);
3549         theme->size = PRVM_clientglobalfloat(particle_size);
3550         theme->sizeincrease = PRVM_clientglobalfloat(particle_sizeincrease);
3551         theme->alpha = PRVM_clientglobalfloat(particle_alpha)*256;
3552         theme->alphafade = PRVM_clientglobalfloat(particle_alphafade)*256;
3553         theme->lifetime = PRVM_clientglobalfloat(particle_time);
3554         theme->gravity = PRVM_clientglobalfloat(particle_gravity);
3555         theme->bounce = PRVM_clientglobalfloat(particle_bounce);
3556         theme->airfriction = PRVM_clientglobalfloat(particle_airfriction);
3557         theme->liquidfriction = PRVM_clientglobalfloat(particle_liquidfriction);
3558         theme->originjitter = PRVM_clientglobalfloat(particle_originjitter);
3559         theme->velocityjitter = PRVM_clientglobalfloat(particle_velocityjitter);
3560         theme->qualityreduction = PRVM_clientglobalfloat(particle_qualityreduction) != 0 ? true : false;
3561         theme->stretch = PRVM_clientglobalfloat(particle_stretch);
3562         theme->staincolor1 = ((int)PRVM_clientglobalvector(particle_staincolor1)[0])*65536 + (int)(PRVM_clientglobalvector(particle_staincolor1)[1])*256 + (int)(PRVM_clientglobalvector(particle_staincolor1)[2]);
3563         theme->staincolor2 = (int)(PRVM_clientglobalvector(particle_staincolor2)[0])*65536 + (int)(PRVM_clientglobalvector(particle_staincolor2)[1])*256 + (int)(PRVM_clientglobalvector(particle_staincolor2)[2]);
3564         theme->staintex =(int)PRVM_clientglobalfloat(particle_staintex);
3565         theme->stainalpha = PRVM_clientglobalfloat(particle_stainalpha)*256;
3566         theme->stainsize = PRVM_clientglobalfloat(particle_stainsize);
3567         theme->delayspawn = PRVM_clientglobalfloat(particle_delayspawn);
3568         theme->delaycollision = PRVM_clientglobalfloat(particle_delaycollision);
3569         theme->angle = PRVM_clientglobalfloat(particle_angle);
3570         theme->spin = PRVM_clientglobalfloat(particle_spin);
3571 }
3572
3573 // init particle spawner interface
3574 // # float(float max_themes) initparticlespawner
3575 static void VM_CL_InitParticleSpawner (prvm_prog_t *prog)
3576 {
3577         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_InitParticleSpawner);
3578         VM_InitParticleSpawner(prog, (int)PRVM_G_FLOAT(OFS_PARM0));
3579         vmpartspawner.themes[0].initialized = true;
3580         VM_ResetParticleTheme(&vmpartspawner.themes[0]);
3581         PRVM_G_FLOAT(OFS_RETURN) = (vmpartspawner.verified == true) ? 1 : 0;
3582 }
3583
3584 // void() resetparticle
3585 static void VM_CL_ResetParticle (prvm_prog_t *prog)
3586 {
3587         VM_SAFEPARMCOUNT(0, VM_CL_ResetParticle);
3588         if (vmpartspawner.verified == false)
3589         {
3590                 VM_Warning(prog, "VM_CL_ResetParticle: particle spawner not initialized\n");
3591                 return;
3592         }
3593         VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0], prog);
3594 }
3595
3596 // void(float themenum) particletheme
3597 static void VM_CL_ParticleTheme (prvm_prog_t *prog)
3598 {
3599         int themenum;
3600
3601         VM_SAFEPARMCOUNT(1, VM_CL_ParticleTheme);
3602         if (vmpartspawner.verified == false)
3603         {
3604                 VM_Warning(prog, "VM_CL_ParticleTheme: particle spawner not initialized\n");
3605                 return;
3606         }
3607         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
3608         if (themenum < 0 || themenum >= vmpartspawner.max_themes)
3609         {
3610                 VM_Warning(prog, "VM_CL_ParticleTheme: bad theme number %i\n", themenum);
3611                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0], prog);
3612                 return;
3613         }
3614         if (vmpartspawner.themes[themenum].initialized == false)
3615         {
3616                 VM_Warning(prog, "VM_CL_ParticleTheme: theme #%i not exists\n", themenum);
3617                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0], prog);
3618                 return;
3619         }
3620         // load particle theme into globals
3621         VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[themenum], prog);
3622 }
3623
3624 // float() saveparticletheme
3625 // void(float themenum) updateparticletheme
3626 static void VM_CL_ParticleThemeSave (prvm_prog_t *prog)
3627 {
3628         int themenum;
3629
3630         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_ParticleThemeSave);
3631         if (vmpartspawner.verified == false)
3632         {
3633                 VM_Warning(prog, "VM_CL_ParticleThemeSave: particle spawner not initialized\n");
3634                 return;
3635         }
3636         // allocate new theme, save it and return
3637         if (prog->argc < 1)
3638         {
3639                 for (themenum = 0; themenum < vmpartspawner.max_themes; themenum++)
3640                         if (vmpartspawner.themes[themenum].initialized == false)
3641                                 break;
3642                 if (themenum >= vmpartspawner.max_themes)
3643                 {
3644                         if (vmpartspawner.max_themes == 2048)
3645                                 VM_Warning(prog, "VM_CL_ParticleThemeSave: no free theme slots\n");
3646                         else
3647                                 VM_Warning(prog, "VM_CL_ParticleThemeSave: no free theme slots, try initparticlespawner() with highter max_themes\n");
3648                         PRVM_G_FLOAT(OFS_RETURN) = -1;
3649                         return;
3650                 }
3651                 vmpartspawner.themes[themenum].initialized = true;
3652                 VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum], prog);
3653                 PRVM_G_FLOAT(OFS_RETURN) = themenum;
3654                 return;
3655         }
3656         // update existing theme
3657         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
3658         if (themenum < 0 || themenum >= vmpartspawner.max_themes)
3659         {
3660                 VM_Warning(prog, "VM_CL_ParticleThemeSave: bad theme number %i\n", themenum);
3661                 return;
3662         }
3663         vmpartspawner.themes[themenum].initialized = true;
3664         VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum], prog);
3665 }
3666
3667 // void(float themenum) freeparticletheme
3668 static void VM_CL_ParticleThemeFree (prvm_prog_t *prog)
3669 {
3670         int themenum;
3671
3672         VM_SAFEPARMCOUNT(1, VM_CL_ParticleThemeFree);
3673         if (vmpartspawner.verified == false)
3674         {
3675                 VM_Warning(prog, "VM_CL_ParticleThemeFree: particle spawner not initialized\n");
3676                 return;
3677         }
3678         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
3679         // check parms
3680         if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
3681         {
3682                 VM_Warning(prog, "VM_CL_ParticleThemeFree: bad theme number %i\n", themenum);
3683                 return;
3684         }
3685         if (vmpartspawner.themes[themenum].initialized == false)
3686         {
3687                 VM_Warning(prog, "VM_CL_ParticleThemeFree: theme #%i already freed\n", themenum);
3688                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0], prog);
3689                 return;
3690         }
3691         // free theme
3692         VM_ResetParticleTheme(&vmpartspawner.themes[themenum]);
3693         vmpartspawner.themes[themenum].initialized = false;
3694 }
3695
3696 // float(vector org, vector dir, [float theme]) particle
3697 // returns 0 if failed, 1 if succesful
3698 static void VM_CL_SpawnParticle (prvm_prog_t *prog)
3699 {
3700         vec3_t org, dir;
3701         vmparticletheme_t *theme;
3702         particle_t *part;
3703         int themenum;
3704
3705         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_SpawnParticle);
3706         if (vmpartspawner.verified == false)
3707         {
3708                 VM_Warning(prog, "VM_CL_SpawnParticle: particle spawner not initialized\n");
3709                 PRVM_G_FLOAT(OFS_RETURN) = 0;
3710                 return;
3711         }
3712         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
3713         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), dir);
3714
3715         if (prog->argc < 3) // global-set particle
3716         {
3717                 part = CL_NewParticle(org,
3718                         (unsigned short)PRVM_clientglobalfloat(particle_type),
3719                         ((int)PRVM_clientglobalvector(particle_color1)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color1)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color1)[2]),
3720                         ((int)PRVM_clientglobalvector(particle_color2)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color2)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color2)[2]),
3721                         (int)PRVM_clientglobalfloat(particle_tex),
3722                         PRVM_clientglobalfloat(particle_size),
3723                         PRVM_clientglobalfloat(particle_sizeincrease),
3724                         PRVM_clientglobalfloat(particle_alpha)*256,
3725                         PRVM_clientglobalfloat(particle_alphafade)*256,
3726                         PRVM_clientglobalfloat(particle_gravity),
3727                         PRVM_clientglobalfloat(particle_bounce),
3728                         org[0],
3729                         org[1],
3730                         org[2],
3731                         dir[0],
3732                         dir[1],
3733                         dir[2],
3734                         PRVM_clientglobalfloat(particle_airfriction),
3735                         PRVM_clientglobalfloat(particle_liquidfriction),
3736                         PRVM_clientglobalfloat(particle_originjitter),
3737                         PRVM_clientglobalfloat(particle_velocityjitter),
3738                         (PRVM_clientglobalfloat(particle_qualityreduction)) ? true : false,
3739                         PRVM_clientglobalfloat(particle_time),
3740                         PRVM_clientglobalfloat(particle_stretch),
3741                         (pblend_t)(int)PRVM_clientglobalfloat(particle_blendmode),
3742                         (porientation_t)(int)PRVM_clientglobalfloat(particle_orientation),
3743                         (int)(PRVM_clientglobalvector(particle_staincolor1)[0])*65536 + (int)(PRVM_clientglobalvector(particle_staincolor1)[1])*256 + (int)(PRVM_clientglobalvector(particle_staincolor1)[2]),
3744                         (int)(PRVM_clientglobalvector(particle_staincolor2)[0])*65536 + (int)(PRVM_clientglobalvector(particle_staincolor2)[1])*256 + (int)(PRVM_clientglobalvector(particle_staincolor2)[2]),
3745                         (int)PRVM_clientglobalfloat(particle_staintex),
3746                         PRVM_clientglobalfloat(particle_stainalpha)*256,
3747                         PRVM_clientglobalfloat(particle_stainsize),
3748                         PRVM_clientglobalfloat(particle_angle),
3749                         PRVM_clientglobalfloat(particle_spin),
3750                         NULL);
3751                 if (!part)
3752                 {
3753                         PRVM_G_FLOAT(OFS_RETURN) = 0;
3754                         return;
3755                 }
3756                 if (PRVM_clientglobalfloat(particle_delayspawn))
3757                         part->delayedspawn = cl.time + PRVM_clientglobalfloat(particle_delayspawn);
3758                 //if (PRVM_clientglobalfloat(particle_delaycollision))
3759                 //      part->delayedcollisions = cl.time + PRVM_clientglobalfloat(particle_delaycollision);
3760         }
3761         else // quick themed particle
3762         {
3763                 themenum = (int)PRVM_G_FLOAT(OFS_PARM2);
3764                 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
3765                 {
3766                         VM_Warning(prog, "VM_CL_SpawnParticle: bad theme number %i\n", themenum);
3767                         PRVM_G_FLOAT(OFS_RETURN) = 0;
3768                         return;
3769                 }
3770                 theme = &vmpartspawner.themes[themenum];
3771                 part = CL_NewParticle(org,
3772                         theme->typeindex,
3773                         theme->color1,
3774                         theme->color2,
3775                         theme->tex,
3776                         theme->size,
3777                         theme->sizeincrease,
3778                         theme->alpha,
3779                         theme->alphafade,
3780                         theme->gravity,
3781                         theme->bounce,
3782                         org[0],
3783                         org[1],
3784                         org[2],
3785                         dir[0],
3786                         dir[1],
3787                         dir[2],
3788                         theme->airfriction,
3789                         theme->liquidfriction,
3790                         theme->originjitter,
3791                         theme->velocityjitter,
3792                         theme->qualityreduction,
3793                         theme->lifetime,
3794                         theme->stretch,
3795                         theme->blendmode,
3796                         theme->orientation,
3797                         theme->staincolor1,
3798                         theme->staincolor2,
3799                         theme->staintex,
3800                         theme->stainalpha,
3801                         theme->stainsize,
3802                         theme->angle,
3803                         theme->spin,
3804                         NULL);
3805                 if (!part)
3806                 {
3807                         PRVM_G_FLOAT(OFS_RETURN) = 0;
3808                         return;
3809                 }
3810                 if (theme->delayspawn)
3811                         part->delayedspawn = cl.time + theme->delayspawn;
3812                 //if (theme->delaycollision)
3813                 //      part->delayedcollisions = cl.time + theme->delaycollision;
3814         }
3815         PRVM_G_FLOAT(OFS_RETURN) = 1;
3816 }
3817
3818 // float(vector org, vector dir, float spawndelay, float collisiondelay, [float theme]) delayedparticle
3819 // returns 0 if failed, 1 if success
3820 static void VM_CL_SpawnParticleDelayed (prvm_prog_t *prog)
3821 {
3822         vec3_t org, dir;
3823         vmparticletheme_t *theme;
3824         particle_t *part;
3825         int themenum;
3826
3827         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_SpawnParticleDelayed);
3828         if (vmpartspawner.verified == false)
3829         {
3830                 VM_Warning(prog, "VM_CL_SpawnParticleDelayed: particle spawner not initialized\n");
3831                 PRVM_G_FLOAT(OFS_RETURN) = 0;
3832                 return;
3833         }
3834         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
3835         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), dir);
3836         if (prog->argc < 5) // global-set particle
3837                 part = CL_NewParticle(org,
3838                         (unsigned short)PRVM_clientglobalfloat(particle_type),
3839                         ((int)PRVM_clientglobalvector(particle_color1)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color1)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color1)[2]),
3840                         ((int)PRVM_clientglobalvector(particle_color2)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color2)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color2)[2]),
3841                         (int)PRVM_clientglobalfloat(particle_tex),
3842                         PRVM_clientglobalfloat(particle_size),
3843                         PRVM_clientglobalfloat(particle_sizeincrease),
3844                         PRVM_clientglobalfloat(particle_alpha)*256,
3845                         PRVM_clientglobalfloat(particle_alphafade)*256,
3846                         PRVM_clientglobalfloat(particle_gravity),
3847                         PRVM_clientglobalfloat(particle_bounce),
3848                         org[0],
3849                         org[1],
3850                         org[2],
3851                         dir[0],
3852                         dir[1],
3853                         dir[2],
3854                         PRVM_clientglobalfloat(particle_airfriction),
3855                         PRVM_clientglobalfloat(particle_liquidfriction),
3856                         PRVM_clientglobalfloat(particle_originjitter),
3857                         PRVM_clientglobalfloat(particle_velocityjitter),
3858                         (PRVM_clientglobalfloat(particle_qualityreduction)) ? true : false,
3859                         PRVM_clientglobalfloat(particle_time),
3860                         PRVM_clientglobalfloat(particle_stretch),
3861                         (pblend_t)(int)PRVM_clientglobalfloat(particle_blendmode),
3862                         (porientation_t)(int)PRVM_clientglobalfloat(particle_orientation),
3863                         ((int)PRVM_clientglobalvector(particle_staincolor1)[0] << 16) + ((int)PRVM_clientglobalvector(particle_staincolor1)[1] << 8) + ((int)PRVM_clientglobalvector(particle_staincolor1)[2]),
3864                         ((int)PRVM_clientglobalvector(particle_staincolor2)[0] << 16) + ((int)PRVM_clientglobalvector(particle_staincolor2)[1] << 8) + ((int)PRVM_clientglobalvector(particle_staincolor2)[2]),
3865                         (int)PRVM_clientglobalfloat(particle_staintex),
3866                         PRVM_clientglobalfloat(particle_stainalpha)*256,
3867                         PRVM_clientglobalfloat(particle_stainsize),
3868                         PRVM_clientglobalfloat(particle_angle),
3869                         PRVM_clientglobalfloat(particle_spin),
3870                         NULL);
3871         else // themed particle
3872         {
3873                 themenum = (int)PRVM_G_FLOAT(OFS_PARM4);
3874                 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
3875                 {
3876                         VM_Warning(prog, "VM_CL_SpawnParticleDelayed: bad theme number %i\n", themenum);
3877                         PRVM_G_FLOAT(OFS_RETURN) = 0;
3878                         return;
3879                 }
3880                 theme = &vmpartspawner.themes[themenum];
3881                 part = CL_NewParticle(org,
3882                         theme->typeindex,
3883                         theme->color1,
3884                         theme->color2,
3885                         theme->tex,
3886                         theme->size,
3887                         theme->sizeincrease,
3888                         theme->alpha,
3889                         theme->alphafade,
3890                         theme->gravity,
3891                         theme->bounce,
3892                         org[0],
3893                         org[1],
3894                         org[2],
3895                         dir[0],
3896                         dir[1],
3897                         dir[2],
3898                         theme->airfriction,
3899                         theme->liquidfriction,
3900                         theme->originjitter,
3901                         theme->velocityjitter,
3902                         theme->qualityreduction,
3903                         theme->lifetime,
3904                         theme->stretch,
3905                         theme->blendmode,
3906                         theme->orientation,
3907                         theme->staincolor1,
3908                         theme->staincolor2,
3909                         theme->staintex,
3910                         theme->stainalpha,
3911                         theme->stainsize,
3912                         theme->angle,
3913                         theme->spin,
3914                         NULL);
3915         }
3916         if (!part)
3917         {
3918                 PRVM_G_FLOAT(OFS_RETURN) = 0;
3919                 return;
3920         }
3921         part->delayedspawn = cl.time + PRVM_G_FLOAT(OFS_PARM2);
3922         //part->delayedcollisions = cl.time + PRVM_G_FLOAT(OFS_PARM3);
3923         PRVM_G_FLOAT(OFS_RETURN) = 0;
3924 }
3925
3926 //====================
3927 //CSQC engine entities query
3928 //====================
3929
3930 // float(float entitynum, float whatfld) getentity;
3931 // vector(float entitynum, float whatfld) getentityvec;
3932 // querying engine-drawn entity
3933 // VorteX: currently it's only tested with whatfld = 1..7
3934 static void VM_CL_GetEntity (prvm_prog_t *prog)
3935 {
3936         int entnum, fieldnum;
3937         vec3_t forward, left, up, org;
3938         VM_SAFEPARMCOUNT(2, VM_CL_GetEntity);
3939
3940         entnum = PRVM_G_FLOAT(OFS_PARM0);
3941         if (entnum < 0 || entnum >= cl.num_entities)
3942         {
3943                 PRVM_G_FLOAT(OFS_RETURN) = 0;
3944                 return;
3945         }
3946         fieldnum = PRVM_G_FLOAT(OFS_PARM1);
3947         switch(fieldnum)
3948         {
3949                 case 0: // active state
3950                         PRVM_G_FLOAT(OFS_RETURN) = cl.entities_active[entnum];
3951                         break;
3952                 case 1: // origin
3953                         Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, org);
3954                         VectorCopy(org, PRVM_G_VECTOR(OFS_RETURN));
3955                         break;
3956                 case 2: // forward
3957                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, forward, left, up, org);
3958                         VectorCopy(forward, PRVM_G_VECTOR(OFS_RETURN));
3959                         break;
3960                 case 3: // right
3961                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, forward, left, up, org);
3962                         VectorNegate(left, PRVM_G_VECTOR(OFS_RETURN));
3963                         break;
3964                 case 4: // up
3965                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, forward, left, up, org);
3966                         VectorCopy(up, PRVM_G_VECTOR(OFS_RETURN));
3967                         break;
3968                 case 5: // scale
3969                         PRVM_G_FLOAT(OFS_RETURN) = Matrix4x4_ScaleFromMatrix(&cl.entities[entnum].render.matrix);
3970                         break;
3971                 case 6: // origin + v_forward, v_right, v_up
3972                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, forward, left, up, org);
3973                         VectorCopy(forward, PRVM_clientglobalvector(v_forward));
3974                         VectorNegate(left, PRVM_clientglobalvector(v_right));
3975                         VectorCopy(up, PRVM_clientglobalvector(v_up));
3976                         VectorCopy(org, PRVM_G_VECTOR(OFS_RETURN));
3977                         break;
3978                 case 7: // alpha
3979        &n