eb2976804639de1230ecfe83ce99029e13350724
[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->priv.required->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         dp_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->priv.server->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         channel = CHAN_USER2ENGINE(channel);
223
224         if (!IS_CHAN(channel))
225         {
226                 VM_Warning(prog, "VM_CL_sound: channel must be in range 0-127\n");
227                 return;
228         }
229
230         CL_VM_GetEntitySoundOrigin(MAX_EDICTS + PRVM_NUM_FOR_EDICT(entity), org);
231         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);
232 }
233
234 // #483 void(vector origin, string sample, float volume, float attenuation) pointsound
235 static void VM_CL_pointsound(prvm_prog_t *prog)
236 {
237         const char                      *sample;
238         float                           fvolume;
239         float                           attenuation;
240         vec3_t                          org;
241
242         VM_SAFEPARMCOUNT(4, VM_CL_pointsound);
243
244         VectorCopy( PRVM_G_VECTOR(OFS_PARM0), org);
245         sample = PRVM_G_STRING(OFS_PARM1);
246         fvolume = PRVM_G_FLOAT(OFS_PARM2);
247         attenuation = PRVM_G_FLOAT(OFS_PARM3);
248
249         if (fvolume < 0 || fvolume > 1)
250         {
251                 VM_Warning(prog, "VM_CL_pointsound: volume must be in range 0-1\n");
252                 return;
253         }
254
255         if (attenuation < 0 || attenuation > 4)
256         {
257                 VM_Warning(prog, "VM_CL_pointsound: attenuation must be in range 0-4\n");
258                 return;
259         }
260
261         // Send World Entity as Entity to Play Sound (for CSQC, that is MAX_EDICTS)
262         S_StartSound(MAX_EDICTS, 0, S_FindName(sample), org, fvolume, attenuation);
263 }
264
265 // #14 entity() spawn
266 static void VM_CL_spawn (prvm_prog_t *prog)
267 {
268         prvm_edict_t *ed;
269         ed = PRVM_ED_Alloc(prog);
270         VM_RETURN_EDICT(ed);
271 }
272
273 static void CL_VM_SetTraceGlobals(prvm_prog_t *prog, const trace_t *trace, int svent)
274 {
275         VM_SetTraceGlobals(prog, trace);
276         PRVM_clientglobalfloat(trace_networkentity) = svent;
277 }
278
279 #define CL_HitNetworkBrushModels(move) !((move) == MOVE_WORLDONLY)
280 #define CL_HitNetworkPlayers(move)     !((move) == MOVE_WORLDONLY || (move) == MOVE_NOMONSTERS)
281
282 // #16 void(vector v1, vector v2, float movetype, entity ignore) traceline
283 static void VM_CL_traceline (prvm_prog_t *prog)
284 {
285         vec3_t  v1, v2;
286         trace_t trace;
287         int             move, svent;
288         prvm_edict_t    *ent;
289
290 //      R_TimeReport("pretraceline");
291
292         VM_SAFEPARMCOUNTRANGE(4, 4, VM_CL_traceline);
293
294         prog->xfunction->builtinsprofile += 30;
295
296         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), v1);
297         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), v2);
298         move = (int)PRVM_G_FLOAT(OFS_PARM2);
299         ent = PRVM_G_EDICT(OFS_PARM3);
300
301         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]))
302                 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));
303
304         trace = CL_TraceLine(v1, v2, move, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendtracelinelength.value, CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true, false);
305
306         CL_VM_SetTraceGlobals(prog, &trace, svent);
307 //      R_TimeReport("traceline");
308 }
309
310 /*
311 =================
312 VM_CL_tracebox
313
314 Used for use tracing and shot targeting
315 Traces are blocked by bbox and exact bsp entityes, and also slide box entities
316 if the tryents flag is set.
317
318 tracebox (vector1, vector mins, vector maxs, vector2, tryents)
319 =================
320 */
321 // LadyHavoc: added this for my own use, VERY useful, similar to traceline
322 static void VM_CL_tracebox (prvm_prog_t *prog)
323 {
324         vec3_t  v1, v2, m1, m2;
325         trace_t trace;
326         int             move, svent;
327         prvm_edict_t    *ent;
328
329 //      R_TimeReport("pretracebox");
330         VM_SAFEPARMCOUNTRANGE(6, 8, VM_CL_tracebox); // allow more parameters for future expansion
331
332         prog->xfunction->builtinsprofile += 30;
333
334         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), v1);
335         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), m1);
336         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), m2);
337         VectorCopy(PRVM_G_VECTOR(OFS_PARM3), v2);
338         move = (int)PRVM_G_FLOAT(OFS_PARM4);
339         ent = PRVM_G_EDICT(OFS_PARM5);
340
341         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]))
342                 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));
343
344         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);
345
346         CL_VM_SetTraceGlobals(prog, &trace, svent);
347 //      R_TimeReport("tracebox");
348 }
349
350 static trace_t CL_Trace_Toss (prvm_prog_t *prog, prvm_edict_t *tossent, prvm_edict_t *ignore, int *svent)
351 {
352         int i;
353         float gravity;
354         vec3_t start, end, mins, maxs, move;
355         vec3_t original_origin;
356         vec3_t original_velocity;
357         vec3_t original_angles;
358         vec3_t original_avelocity;
359         trace_t trace;
360
361         VectorCopy(PRVM_clientedictvector(tossent, origin)   , original_origin   );
362         VectorCopy(PRVM_clientedictvector(tossent, velocity) , original_velocity );
363         VectorCopy(PRVM_clientedictvector(tossent, angles)   , original_angles   );
364         VectorCopy(PRVM_clientedictvector(tossent, avelocity), original_avelocity);
365
366         gravity = PRVM_clientedictfloat(tossent, gravity);
367         if (!gravity)
368                 gravity = 1.0f;
369         gravity *= cl.movevars_gravity * 0.05;
370
371         for (i = 0;i < 200;i++) // LadyHavoc: sanity check; never trace more than 10 seconds
372         {
373                 PRVM_clientedictvector(tossent, velocity)[2] -= gravity;
374                 VectorMA (PRVM_clientedictvector(tossent, angles), 0.05, PRVM_clientedictvector(tossent, avelocity), PRVM_clientedictvector(tossent, angles));
375                 VectorScale (PRVM_clientedictvector(tossent, velocity), 0.05, move);
376                 VectorAdd (PRVM_clientedictvector(tossent, origin), move, end);
377                 VectorCopy(PRVM_clientedictvector(tossent, origin), start);
378                 VectorCopy(PRVM_clientedictvector(tossent, mins), mins);
379                 VectorCopy(PRVM_clientedictvector(tossent, maxs), maxs);
380                 trace = CL_TraceBox(start, mins, maxs, end, MOVE_NORMAL, tossent, CL_GenericHitSuperContentsMask(tossent), 0, 0, collision_extendmovelength.value, true, true, NULL, true);
381                 VectorCopy (trace.endpos, PRVM_clientedictvector(tossent, origin));
382
383                 if (trace.fraction < 1)
384                         break;
385         }
386
387         VectorCopy(original_origin   , PRVM_clientedictvector(tossent, origin)   );
388         VectorCopy(original_velocity , PRVM_clientedictvector(tossent, velocity) );
389         VectorCopy(original_angles   , PRVM_clientedictvector(tossent, angles)   );
390         VectorCopy(original_avelocity, PRVM_clientedictvector(tossent, avelocity));
391
392         return trace;
393 }
394
395 static void VM_CL_tracetoss (prvm_prog_t *prog)
396 {
397         trace_t trace;
398         prvm_edict_t    *ent;
399         prvm_edict_t    *ignore;
400         int svent = 0;
401
402         prog->xfunction->builtinsprofile += 600;
403
404         VM_SAFEPARMCOUNT(2, VM_CL_tracetoss);
405
406         ent = PRVM_G_EDICT(OFS_PARM0);
407         if (ent == prog->edicts)
408         {
409                 VM_Warning(prog, "tracetoss: can not use world entity\n");
410                 return;
411         }
412         ignore = PRVM_G_EDICT(OFS_PARM1);
413
414         trace = CL_Trace_Toss (prog, ent, ignore, &svent);
415
416         CL_VM_SetTraceGlobals(prog, &trace, svent);
417 }
418
419
420 // #20 void(string s) precache_model
421 static void VM_CL_precache_model (prvm_prog_t *prog)
422 {
423         const char      *name;
424         int                     i;
425         dp_model_t              *m;
426
427         VM_SAFEPARMCOUNT(1, VM_CL_precache_model);
428
429         name = PRVM_G_STRING(OFS_PARM0);
430         for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
431         {
432                 if(!strcmp(cl.csqc_model_precache[i]->name, name))
433                 {
434                         PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
435                         return;
436                 }
437         }
438         PRVM_G_FLOAT(OFS_RETURN) = 0;
439         m = Mod_ForName(name, false, false, name[0] == '*' ? cl.model_name[1] : NULL);
440         if(m && m->loaded)
441         {
442                 for (i = 0;i < MAX_MODELS;i++)
443                 {
444                         if (!cl.csqc_model_precache[i])
445                         {
446                                 cl.csqc_model_precache[i] = (dp_model_t*)m;
447                                 PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
448                                 return;
449                         }
450                 }
451                 VM_Warning(prog, "VM_CL_precache_model: no free models\n");
452                 return;
453         }
454         VM_Warning(prog, "VM_CL_precache_model: model \"%s\" not found\n", name);
455 }
456
457 // #22 entity(vector org, float rad) findradius
458 static void VM_CL_findradius (prvm_prog_t *prog)
459 {
460         prvm_edict_t    *ent, *chain;
461         vec_t                   radius, radius2;
462         vec3_t                  org, eorg, mins, maxs;
463         int                             i, numtouchedicts;
464         static prvm_edict_t     *touchedicts[MAX_EDICTS];
465         int             chainfield;
466
467         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_findradius);
468
469         if(prog->argc == 3)
470                 chainfield = PRVM_G_INT(OFS_PARM2);
471         else
472                 chainfield = prog->fieldoffsets.chain;
473         if(chainfield < 0)
474                 prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
475
476         chain = (prvm_edict_t *)prog->edicts;
477
478         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
479         radius = PRVM_G_FLOAT(OFS_PARM1);
480         radius2 = radius * radius;
481
482         mins[0] = org[0] - (radius + 1);
483         mins[1] = org[1] - (radius + 1);
484         mins[2] = org[2] - (radius + 1);
485         maxs[0] = org[0] + (radius + 1);
486         maxs[1] = org[1] + (radius + 1);
487         maxs[2] = org[2] + (radius + 1);
488         numtouchedicts = World_EntitiesInBox(&cl.world, mins, maxs, MAX_EDICTS, touchedicts);
489         if (numtouchedicts > MAX_EDICTS)
490         {
491                 // this never happens   //[515]: for what then ?
492                 Con_Printf("CSQC_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
493                 numtouchedicts = MAX_EDICTS;
494         }
495         for (i = 0;i < numtouchedicts;i++)
496         {
497                 ent = touchedicts[i];
498                 // Quake did not return non-solid entities but darkplaces does
499                 // (note: this is the reason you can't blow up fallen zombies)
500                 if (PRVM_clientedictfloat(ent, solid) == SOLID_NOT && !sv_gameplayfix_blowupfallenzombies.integer)
501                         continue;
502                 // LadyHavoc: compare against bounding box rather than center so it
503                 // doesn't miss large objects, and use DotProduct instead of Length
504                 // for a major speedup
505                 VectorSubtract(org, PRVM_clientedictvector(ent, origin), eorg);
506                 if (sv_gameplayfix_findradiusdistancetobox.integer)
507                 {
508                         eorg[0] -= bound(PRVM_clientedictvector(ent, mins)[0], eorg[0], PRVM_clientedictvector(ent, maxs)[0]);
509                         eorg[1] -= bound(PRVM_clientedictvector(ent, mins)[1], eorg[1], PRVM_clientedictvector(ent, maxs)[1]);
510                         eorg[2] -= bound(PRVM_clientedictvector(ent, mins)[2], eorg[2], PRVM_clientedictvector(ent, maxs)[2]);
511                 }
512                 else
513                         VectorMAMAM(1, eorg, -0.5f, PRVM_clientedictvector(ent, mins), -0.5f, PRVM_clientedictvector(ent, maxs), eorg);
514                 if (DotProduct(eorg, eorg) < radius2)
515                 {
516                         PRVM_EDICTFIELDEDICT(ent, chainfield) = PRVM_EDICT_TO_PROG(chain);
517                         chain = ent;
518                 }
519         }
520
521         VM_RETURN_EDICT(chain);
522 }
523
524 // #34 float() droptofloor
525 static void VM_CL_droptofloor (prvm_prog_t *prog)
526 {
527         prvm_edict_t            *ent;
528         vec3_t                          start, end, mins, maxs;
529         trace_t                         trace;
530
531         VM_SAFEPARMCOUNTRANGE(0, 2, VM_CL_droptofloor); // allow 2 parameters because the id1 defs.qc had an incorrect prototype
532
533         // assume failure if it returns early
534         PRVM_G_FLOAT(OFS_RETURN) = 0;
535
536         ent = PRVM_PROG_TO_EDICT(PRVM_clientglobaledict(self));
537         if (ent == prog->edicts)
538         {
539                 VM_Warning(prog, "droptofloor: can not modify world entity\n");
540                 return;
541         }
542         if (ent->priv.server->free)
543         {
544                 VM_Warning(prog, "droptofloor: can not modify free entity\n");
545                 return;
546         }
547
548         VectorCopy(PRVM_clientedictvector(ent, origin), start);
549         VectorCopy(PRVM_clientedictvector(ent, mins), mins);
550         VectorCopy(PRVM_clientedictvector(ent, maxs), maxs);
551         VectorCopy(PRVM_clientedictvector(ent, origin), end);
552         end[2] -= 256;
553
554         trace = CL_TraceBox(start, mins, maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, NULL, true);
555
556         if (trace.fraction != 1)
557         {
558                 VectorCopy (trace.endpos, PRVM_clientedictvector(ent, origin));
559                 PRVM_clientedictfloat(ent, flags) = (int)PRVM_clientedictfloat(ent, flags) | FL_ONGROUND;
560                 PRVM_clientedictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
561                 PRVM_G_FLOAT(OFS_RETURN) = 1;
562                 // if support is destroyed, keep suspended (gross hack for floating items in various maps)
563 //              ent->priv.server->suspendedinairflag = true;
564         }
565 }
566
567 // #35 void(float style, string value) lightstyle
568 static void VM_CL_lightstyle (prvm_prog_t *prog)
569 {
570         int                     i;
571         const char      *c;
572
573         VM_SAFEPARMCOUNT(2, VM_CL_lightstyle);
574
575         i = (int)PRVM_G_FLOAT(OFS_PARM0);
576         c = PRVM_G_STRING(OFS_PARM1);
577         if (i >= cl.max_lightstyle)
578         {
579                 VM_Warning(prog, "VM_CL_lightstyle >= MAX_LIGHTSTYLES\n");
580                 return;
581         }
582         strlcpy (cl.lightstyle[i].map, c, sizeof (cl.lightstyle[i].map));
583         cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
584         cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map);
585 }
586
587 // #40 float(entity e) checkbottom
588 static void VM_CL_checkbottom (prvm_prog_t *prog)
589 {
590         static int              cs_yes, cs_no;
591         prvm_edict_t    *ent;
592         vec3_t                  mins, maxs, start, stop;
593         trace_t                 trace;
594         int                             x, y;
595         float                   mid, bottom;
596
597         VM_SAFEPARMCOUNT(1, VM_CL_checkbottom);
598         ent = PRVM_G_EDICT(OFS_PARM0);
599         PRVM_G_FLOAT(OFS_RETURN) = 0;
600
601         VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
602         VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
603
604 // if all of the points under the corners are solid world, don't bother
605 // with the tougher checks
606 // the corners must be within 16 of the midpoint
607         start[2] = mins[2] - 1;
608         for     (x=0 ; x<=1 ; x++)
609                 for     (y=0 ; y<=1 ; y++)
610                 {
611                         start[0] = x ? maxs[0] : mins[0];
612                         start[1] = y ? maxs[1] : mins[1];
613                         if (!(CL_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
614                                 goto realcheck;
615                 }
616
617         cs_yes++;
618         PRVM_G_FLOAT(OFS_RETURN) = true;
619         return;         // we got out easy
620
621 realcheck:
622         cs_no++;
623 //
624 // check it for real...
625 //
626         start[2] = mins[2];
627
628 // the midpoint must be within 16 of the bottom
629         start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
630         start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
631         stop[2] = start[2] - 2*sv_stepheight.value;
632         trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, NULL, true, false);
633
634         if (trace.fraction == 1.0)
635                 return;
636
637         mid = bottom = trace.endpos[2];
638
639 // the corners must be within 16 of the midpoint
640         for     (x=0 ; x<=1 ; x++)
641                 for     (y=0 ; y<=1 ; y++)
642                 {
643                         start[0] = stop[0] = x ? maxs[0] : mins[0];
644                         start[1] = stop[1] = y ? maxs[1] : mins[1];
645
646                         trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, NULL, true, false);
647
648                         if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
649                                 bottom = trace.endpos[2];
650                         if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
651                                 return;
652                 }
653
654         cs_yes++;
655         PRVM_G_FLOAT(OFS_RETURN) = true;
656 }
657
658 // #41 float(vector v) pointcontents
659 static void VM_CL_pointcontents (prvm_prog_t *prog)
660 {
661         vec3_t point;
662         VM_SAFEPARMCOUNT(1, VM_CL_pointcontents);
663         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), point);
664         PRVM_G_FLOAT(OFS_RETURN) = Mod_Q1BSP_NativeContentsFromSuperContents(CL_PointSuperContents(point));
665 }
666
667 // #48 void(vector o, vector d, float color, float count) particle
668 static void VM_CL_particle (prvm_prog_t *prog)
669 {
670         vec3_t org, dir;
671         int             count;
672         unsigned char   color;
673         VM_SAFEPARMCOUNT(4, VM_CL_particle);
674
675         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
676         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), dir);
677         color = (int)PRVM_G_FLOAT(OFS_PARM2);
678         count = (int)PRVM_G_FLOAT(OFS_PARM3);
679         CL_ParticleEffect(EFFECT_SVC_PARTICLE, count, org, org, dir, dir, NULL, color);
680 }
681
682 // #74 void(vector pos, string samp, float vol, float atten) ambientsound
683 static void VM_CL_ambientsound (prvm_prog_t *prog)
684 {
685         vec3_t f;
686         sfx_t   *s;
687         VM_SAFEPARMCOUNT(4, VM_CL_ambientsound);
688         s = S_FindName(PRVM_G_STRING(OFS_PARM0));
689         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f);
690         S_StaticSound (s, f, PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3)*64);
691 }
692
693 // #92 vector(vector org[, float lpflag]) getlight (DP_QC_GETLIGHT)
694 static void VM_CL_getlight (prvm_prog_t *prog)
695 {
696         vec3_t ambientcolor, diffusecolor, diffusenormal;
697         vec3_t p;
698         int flags = prog->argc >= 2 ? PRVM_G_FLOAT(OFS_PARM1) : LP_LIGHTMAP;
699
700         VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_getlight);
701
702         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), p);
703         R_CompleteLightPoint(ambientcolor, diffusecolor, diffusenormal, p, flags, r_refdef.scene.lightmapintensity, r_refdef.scene.ambientintensity);
704         VectorMA(ambientcolor, 0.5, diffusecolor, PRVM_G_VECTOR(OFS_RETURN));
705         if (PRVM_clientglobalvector(getlight_ambient))
706                 VectorCopy(ambientcolor, PRVM_clientglobalvector(getlight_ambient));
707         if (PRVM_clientglobalvector(getlight_diffuse))
708                 VectorCopy(diffusecolor, PRVM_clientglobalvector(getlight_diffuse));
709         if (PRVM_clientglobalvector(getlight_dir))
710                 VectorCopy(diffusenormal, PRVM_clientglobalvector(getlight_dir));
711 }
712
713 //============================================================================
714 //[515]: SCENE MANAGER builtins
715
716 extern cvar_t v_yshearing;
717 void CSQC_R_RecalcView (void)
718 {
719         extern matrix4x4_t viewmodelmatrix_nobob;
720         extern matrix4x4_t viewmodelmatrix_withbob;
721         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);
722         if (v_yshearing.value > 0)
723                 Matrix4x4_QuakeToDuke3D(&r_refdef.view.matrix, &r_refdef.view.matrix, v_yshearing.value);
724         Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
725         Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
726         Matrix4x4_Concat(&viewmodelmatrix_withbob, &r_refdef.view.matrix, &cl.csqc_viewmodelmatrixfromengine);
727 }
728
729 //#300 void() clearscene (EXT_CSQC)
730 static void VM_CL_R_ClearScene (prvm_prog_t *prog)
731 {
732         VM_SAFEPARMCOUNT(0, VM_CL_R_ClearScene);
733         // clear renderable entity and light lists
734         r_refdef.scene.numentities = 0;
735         r_refdef.scene.numlights = 0;
736         // restore the view settings to the values that VM_CL_UpdateView received from the client code
737         r_refdef.view = csqc_original_r_refdef_view;
738         // polygonbegin without draw2d arg has to guess
739         prog->polygonbegin_guess2d = false;
740         VectorCopy(cl.csqc_vieworiginfromengine, cl.csqc_vieworigin);
741         VectorCopy(cl.csqc_viewanglesfromengine, cl.csqc_viewangles);
742         cl.csqc_vidvars.drawworld = r_drawworld.integer != 0;
743         cl.csqc_vidvars.drawenginesbar = false;
744         cl.csqc_vidvars.drawcrosshair = false;
745         CSQC_R_RecalcView();
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->priv.required->free)
765                         continue;
766                 CSQC_Think(ed);
767                 if(ed->priv.required->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->priv.required->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         qboolean 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 //#330 float(float stnum) getstatf (EXT_CSQC)
1192 static void VM_CL_getstatf (prvm_prog_t *prog)
1193 {
1194         int i;
1195         union
1196         {
1197                 float f;
1198                 int l;
1199         }dat;
1200         VM_SAFEPARMCOUNT(1, VM_CL_getstatf);
1201         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1202         if(i < 0 || i >= MAX_CL_STATS)
1203         {
1204                 VM_Warning(prog, "VM_CL_getstatf: index>=MAX_CL_STATS or index<0\n");
1205                 return;
1206         }
1207         dat.l = cl.stats[i];
1208         PRVM_G_FLOAT(OFS_RETURN) =  dat.f;
1209 }
1210
1211 //#331 float(float stnum) getstati (EXT_CSQC)
1212 static void VM_CL_getstati (prvm_prog_t *prog)
1213 {
1214         int i, index;
1215         int firstbit, bitcount;
1216
1217         VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_getstati);
1218
1219         index = (int)PRVM_G_FLOAT(OFS_PARM0);
1220         if (prog->argc > 1)
1221         {
1222                 firstbit = (int)PRVM_G_FLOAT(OFS_PARM1);
1223                 if (prog->argc > 2)
1224                         bitcount = (int)PRVM_G_FLOAT(OFS_PARM2);
1225                 else
1226                         bitcount = 1;
1227         }
1228         else
1229         {
1230                 firstbit = 0;
1231                 bitcount = 32;
1232         }
1233
1234         if(index < 0 || index >= MAX_CL_STATS)
1235         {
1236                 VM_Warning(prog, "VM_CL_getstati: index>=MAX_CL_STATS or index<0\n");
1237                 return;
1238         }
1239         i = cl.stats[index];
1240         if (bitcount != 32)     //32 causes the mask to overflow, so there's nothing to subtract from.
1241                 i = (((unsigned int)i)&(((1<<bitcount)-1)<<firstbit))>>firstbit;
1242         PRVM_G_FLOAT(OFS_RETURN) = i;
1243 }
1244
1245 //#332 string(float firststnum) getstats (EXT_CSQC)
1246 static void VM_CL_getstats (prvm_prog_t *prog)
1247 {
1248         int i;
1249         char t[17];
1250         VM_SAFEPARMCOUNT(1, VM_CL_getstats);
1251         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1252         if(i < 0 || i > MAX_CL_STATS-4)
1253         {
1254                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1255                 VM_Warning(prog, "VM_CL_getstats: index>MAX_CL_STATS-4 or index<0\n");
1256                 return;
1257         }
1258         strlcpy(t, (char*)&cl.stats[i], sizeof(t));
1259         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
1260 }
1261
1262 //#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
1263 static void VM_CL_setmodelindex (prvm_prog_t *prog)
1264 {
1265         int                             i;
1266         prvm_edict_t    *t;
1267         struct model_s  *model;
1268
1269         VM_SAFEPARMCOUNT(2, VM_CL_setmodelindex);
1270
1271         t = PRVM_G_EDICT(OFS_PARM0);
1272
1273         i = (int)PRVM_G_FLOAT(OFS_PARM1);
1274
1275         PRVM_clientedictstring(t, model) = 0;
1276         PRVM_clientedictfloat(t, modelindex) = 0;
1277
1278         if (!i)
1279                 return;
1280
1281         model = CL_GetModelByIndex(i);
1282         if (!model)
1283         {
1284                 VM_Warning(prog, "VM_CL_setmodelindex: null model\n");
1285                 return;
1286         }
1287         PRVM_clientedictstring(t, model) = PRVM_SetEngineString(prog, model->name);
1288         PRVM_clientedictfloat(t, modelindex) = i;
1289
1290         // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
1291         if (model)
1292         {
1293                 SetMinMaxSize (prog, t, model->normalmins, model->normalmaxs);
1294         }
1295         else
1296                 SetMinMaxSize (prog, t, vec3_origin, vec3_origin);
1297 }
1298
1299 //#334 string(float mdlindex) modelnameforindex (EXT_CSQC)
1300 static void VM_CL_modelnameforindex (prvm_prog_t *prog)
1301 {
1302         dp_model_t *model;
1303
1304         VM_SAFEPARMCOUNT(1, VM_CL_modelnameforindex);
1305
1306         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1307         model = CL_GetModelByIndex((int)PRVM_G_FLOAT(OFS_PARM0));
1308         PRVM_G_INT(OFS_RETURN) = model ? PRVM_SetEngineString(prog, model->name) : 0;
1309 }
1310
1311 //#335 float(string effectname) particleeffectnum (EXT_CSQC)
1312 static void VM_CL_particleeffectnum (prvm_prog_t *prog)
1313 {
1314         int                     i;
1315         VM_SAFEPARMCOUNT(1, VM_CL_particleeffectnum);
1316         i = CL_ParticleEffectIndexForName(PRVM_G_STRING(OFS_PARM0));
1317         if (i == 0)
1318                 i = -1;
1319         PRVM_G_FLOAT(OFS_RETURN) = i;
1320 }
1321
1322 // #336 void(entity ent, float effectnum, vector start, vector end[, float color]) trailparticles (EXT_CSQC)
1323 static void VM_CL_trailparticles (prvm_prog_t *prog)
1324 {
1325         int                             i;
1326         vec3_t                  start, end, velocity;
1327         prvm_edict_t    *t;
1328         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_trailparticles);
1329
1330         t = PRVM_G_EDICT(OFS_PARM0);
1331         i               = (int)PRVM_G_FLOAT(OFS_PARM1);
1332         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), start);
1333         VectorCopy(PRVM_G_VECTOR(OFS_PARM3), end);
1334         VectorCopy(PRVM_clientedictvector(t, velocity), velocity);
1335
1336         if (i < 0)
1337                 return;
1338         CL_ParticleTrail(i, 1, start, end, velocity, velocity, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0, true, true, NULL, NULL, 1);
1339 }
1340
1341 //#337 void(float effectnum, vector origin, vector dir, float count[, float color]) pointparticles (EXT_CSQC)
1342 static void VM_CL_pointparticles (prvm_prog_t *prog)
1343 {
1344         int                     i;
1345         float n;
1346         vec3_t f, v;
1347         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_pointparticles);
1348         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1349         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f);
1350         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), v);
1351         n = PRVM_G_FLOAT(OFS_PARM3);
1352         if (i < 0)
1353                 return;
1354         CL_ParticleEffect(i, n, f, f, v, v, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0);
1355 }
1356
1357 //#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)
1358 static void VM_CL_boxparticles (prvm_prog_t *prog)
1359 {
1360         int effectnum;
1361         // prvm_edict_t *own;
1362         vec3_t origin_from, origin_to, dir_from, dir_to;
1363         float count;
1364         int flags;
1365         qboolean istrail;
1366         float tintmins[4], tintmaxs[4], fade;
1367         VM_SAFEPARMCOUNTRANGE(7, 8, VM_CL_boxparticles);
1368
1369         effectnum = (int)PRVM_G_FLOAT(OFS_PARM0);
1370         if (effectnum < 0)
1371                 return;
1372         // own = PRVM_G_EDICT(OFS_PARM1); // TODO find use for this
1373         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin_from);
1374         VectorCopy(PRVM_G_VECTOR(OFS_PARM3), origin_to  );
1375         VectorCopy(PRVM_G_VECTOR(OFS_PARM4), dir_from   );
1376         VectorCopy(PRVM_G_VECTOR(OFS_PARM5), dir_to     );
1377         count = PRVM_G_FLOAT(OFS_PARM6);
1378         if(prog->argc >= 8)
1379                 flags = PRVM_G_FLOAT(OFS_PARM7);
1380         else
1381                 flags = 0;
1382
1383         Vector4Set(tintmins, 1, 1, 1, 1);
1384         Vector4Set(tintmaxs, 1, 1, 1, 1);
1385         fade = 1;
1386         istrail = false;
1387
1388         if(flags & 1) // read alpha
1389         {
1390                 tintmins[3] = PRVM_clientglobalfloat(particles_alphamin);
1391                 tintmaxs[3] = PRVM_clientglobalfloat(particles_alphamax);
1392         }
1393         if(flags & 2) // read color
1394         {
1395                 VectorCopy(PRVM_clientglobalvector(particles_colormin), tintmins);
1396                 VectorCopy(PRVM_clientglobalvector(particles_colormax), tintmaxs);
1397         }
1398         if(flags & 4) // read fade
1399         {
1400                 fade = PRVM_clientglobalfloat(particles_fade);
1401         }
1402         if(flags & 128) // draw as trail
1403         {
1404                 istrail = true;
1405         }
1406
1407         if (istrail)
1408                 CL_ParticleTrail(effectnum, count, origin_from, origin_to, dir_from, dir_to, NULL, 0, true, true, tintmins, tintmaxs, fade);
1409         else
1410                 CL_ParticleBox(effectnum, count, origin_from, origin_to, dir_from, dir_to, NULL, 0, true, true, tintmins, tintmaxs, fade);
1411 }
1412
1413 //#531 void(float pause) setpause
1414 static void VM_CL_setpause(prvm_prog_t *prog)
1415 {
1416         VM_SAFEPARMCOUNT(1, VM_CL_setpause);
1417         if ((int)PRVM_G_FLOAT(OFS_PARM0) != 0)
1418                 cl.csqc_paused = true;
1419         else
1420                 cl.csqc_paused = false;
1421 }
1422
1423 //#343 void(float usecursor) setcursormode (DP_CSQC)
1424 static void VM_CL_setcursormode (prvm_prog_t *prog)
1425 {
1426         VM_SAFEPARMCOUNT(1, VM_CL_setcursormode);
1427         cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0) != 0;
1428         cl_ignoremousemoves = 2;
1429 }
1430
1431 //#344 vector() getmousepos (DP_CSQC)
1432 static void VM_CL_getmousepos(prvm_prog_t *prog)
1433 {
1434         VM_SAFEPARMCOUNT(0,VM_CL_getmousepos);
1435
1436         if (key_consoleactive || key_dest != key_game)
1437                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), 0, 0, 0);
1438         else if (cl.csqc_wantsmousemove)
1439                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_windowmouse_x * vid_conwidth.integer / vid.width, in_windowmouse_y * vid_conheight.integer / vid.height, 0);
1440         else
1441                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_mouse_x * vid_conwidth.integer / vid.width, in_mouse_y * vid_conheight.integer / vid.height, 0);
1442 }
1443
1444 //#345 float(float framenum) getinputstate (EXT_CSQC)
1445 static void VM_CL_getinputstate (prvm_prog_t *prog)
1446 {
1447         unsigned int i, frame;
1448         VM_SAFEPARMCOUNT(1, VM_CL_getinputstate);
1449         frame = (unsigned int)PRVM_G_FLOAT(OFS_PARM0);
1450         PRVM_G_FLOAT(OFS_RETURN) = false;
1451         for (i = 0;i < CL_MAX_USERCMDS;i++)
1452         {
1453                 if (cl.movecmd[i].sequence == frame)
1454                 {
1455                         VectorCopy(cl.movecmd[i].viewangles, PRVM_clientglobalvector(input_angles));
1456                         PRVM_clientglobalfloat(input_buttons) = cl.movecmd[i].buttons; // FIXME: this should not be directly exposed to csqc (translation layer needed?)
1457                         PRVM_clientglobalvector(input_movevalues)[0] = cl.movecmd[i].forwardmove;
1458                         PRVM_clientglobalvector(input_movevalues)[1] = cl.movecmd[i].sidemove;
1459                         PRVM_clientglobalvector(input_movevalues)[2] = cl.movecmd[i].upmove;
1460                         PRVM_clientglobalfloat(input_timelength) = cl.movecmd[i].frametime;
1461                         // this probably shouldn't be here
1462                         if(cl.movecmd[i].crouch)
1463                         {
1464                                 VectorCopy(cl.playercrouchmins, PRVM_clientglobalvector(pmove_mins));
1465                                 VectorCopy(cl.playercrouchmaxs, PRVM_clientglobalvector(pmove_maxs));
1466                         }
1467                         else
1468                         {
1469                                 VectorCopy(cl.playerstandmins, PRVM_clientglobalvector(pmove_mins));
1470                                 VectorCopy(cl.playerstandmaxs, PRVM_clientglobalvector(pmove_maxs));
1471                         }
1472                         PRVM_G_FLOAT(OFS_RETURN) = true;
1473                 }
1474         }
1475 }
1476
1477 //#346 void(float sens) setsensitivityscaler (EXT_CSQC)
1478 static void VM_CL_setsensitivityscale (prvm_prog_t *prog)
1479 {
1480         VM_SAFEPARMCOUNT(1, VM_CL_setsensitivityscale);
1481         cl.sensitivityscale = PRVM_G_FLOAT(OFS_PARM0);
1482 }
1483
1484 //#347 void() runstandardplayerphysics (EXT_CSQC)
1485 #define PMF_JUMP_HELD 1 // matches FTEQW
1486 #define PMF_LADDER 2 // not used by DP, FTEQW sets this in runplayerphysics but does not read it
1487 #define PMF_DUCKED 4 // FIXME FTEQW doesn't have this for Q1 like movement because Q1 cannot crouch
1488 #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
1489 static void VM_CL_runplayerphysics (prvm_prog_t *prog)
1490 {
1491         cl_clientmovement_state_t s;
1492         prvm_edict_t *ent;
1493
1494         memset(&s, 0, sizeof(s));
1495
1496         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_runplayerphysics);
1497
1498         ent = (prog->argc == 1 ? PRVM_G_EDICT(OFS_PARM0) : prog->edicts);
1499         if(ent == prog->edicts)
1500         {
1501                 // deprecated use
1502                 s.self = NULL;
1503                 VectorCopy(PRVM_clientglobalvector(pmove_org), s.origin);
1504                 VectorCopy(PRVM_clientglobalvector(pmove_vel), s.velocity);
1505                 VectorCopy(PRVM_clientglobalvector(pmove_mins), s.mins);
1506                 VectorCopy(PRVM_clientglobalvector(pmove_maxs), s.maxs);
1507                 s.crouched = 0;
1508                 s.waterjumptime = PRVM_clientglobalfloat(pmove_waterjumptime);
1509                 s.cmd.canjump = (int)PRVM_clientglobalfloat(pmove_jump_held) == 0;
1510         }
1511         else
1512         {
1513                 // new use
1514                 s.self = ent;
1515                 VectorCopy(PRVM_clientedictvector(ent, origin), s.origin);
1516                 VectorCopy(PRVM_clientedictvector(ent, velocity), s.velocity);
1517                 VectorCopy(PRVM_clientedictvector(ent, mins), s.mins);
1518                 VectorCopy(PRVM_clientedictvector(ent, maxs), s.maxs);
1519                 s.crouched = ((int)PRVM_clientedictfloat(ent, pmove_flags) & PMF_DUCKED) != 0;
1520                 s.waterjumptime = 0; // FIXME where do we get this from? FTEQW lacks support for this too
1521                 s.cmd.canjump = ((int)PRVM_clientedictfloat(ent, pmove_flags) & PMF_JUMP_HELD) == 0;
1522         }
1523
1524         VectorCopy(PRVM_clientglobalvector(input_angles), s.cmd.viewangles);
1525         s.cmd.forwardmove = PRVM_clientglobalvector(input_movevalues)[0];
1526         s.cmd.sidemove = PRVM_clientglobalvector(input_movevalues)[1];
1527         s.cmd.upmove = PRVM_clientglobalvector(input_movevalues)[2];
1528         s.cmd.buttons = PRVM_clientglobalfloat(input_buttons);
1529         s.cmd.frametime = PRVM_clientglobalfloat(input_timelength);
1530         s.cmd.jump = (s.cmd.buttons & 2) != 0;
1531         s.cmd.crouch = (s.cmd.buttons & 16) != 0;
1532
1533         CL_ClientMovement_PlayerMove_Frame(&s);
1534
1535         if(ent == prog->edicts)
1536         {
1537                 // deprecated use
1538                 VectorCopy(s.origin, PRVM_clientglobalvector(pmove_org));
1539                 VectorCopy(s.velocity, PRVM_clientglobalvector(pmove_vel));
1540                 PRVM_clientglobalfloat(pmove_jump_held) = !s.cmd.canjump;
1541                 PRVM_clientglobalfloat(pmove_waterjumptime) = s.waterjumptime;
1542         }
1543         else
1544         {
1545                 // new use
1546                 VectorCopy(s.origin, PRVM_clientedictvector(ent, origin));
1547                 VectorCopy(s.velocity, PRVM_clientedictvector(ent, velocity));
1548                 PRVM_clientedictfloat(ent, pmove_flags) =
1549                         (s.crouched ? PMF_DUCKED : 0) |
1550                         (s.cmd.canjump ? 0 : PMF_JUMP_HELD) |
1551                         (s.onground ? PMF_ONGROUND : 0);
1552         }
1553 }
1554
1555 //#348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
1556 static void VM_CL_getplayerkey (prvm_prog_t *prog)
1557 {
1558         int                     i;
1559         char            t[128];
1560         const char      *c;
1561
1562         VM_SAFEPARMCOUNT(2, VM_CL_getplayerkey);
1563
1564         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1565         c = PRVM_G_STRING(OFS_PARM1);
1566         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1567         Sbar_SortFrags();
1568
1569         if (i < 0)
1570                 i = Sbar_GetSortedPlayerIndex(-1-i);
1571         if(i < 0 || i >= cl.maxclients)
1572                 return;
1573
1574         t[0] = 0;
1575
1576         if(!strcasecmp(c, "name"))
1577                 strlcpy(t, cl.scores[i].name, sizeof(t));
1578         else
1579                 if(!strcasecmp(c, "frags"))
1580                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].frags);
1581         else
1582                 if(!strcasecmp(c, "ping"))
1583                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_ping);
1584         else
1585                 if(!strcasecmp(c, "pl"))
1586                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_packetloss);
1587         else
1588                 if(!strcasecmp(c, "movementloss"))
1589                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_movementloss);
1590         else
1591                 if(!strcasecmp(c, "entertime"))
1592                         dpsnprintf(t, sizeof(t), "%f", cl.scores[i].qw_entertime);
1593         else
1594                 if(!strcasecmp(c, "colors"))
1595                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors);
1596         else
1597                 if(!strcasecmp(c, "topcolor"))
1598                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors & 0xf0);
1599         else
1600                 if(!strcasecmp(c, "bottomcolor"))
1601                         dpsnprintf(t, sizeof(t), "%i", (cl.scores[i].colors &15)<<4);
1602         else
1603                 if(!strcasecmp(c, "viewentity"))
1604                         dpsnprintf(t, sizeof(t), "%i", i+1);
1605         if(!t[0])
1606                 return;
1607         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
1608 }
1609
1610 //#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
1611 static void VM_CL_setlistener (prvm_prog_t *prog)
1612 {
1613         vec3_t origin, forward, left, up;
1614         VM_SAFEPARMCOUNT(4, VM_CL_setlistener);
1615         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), origin);
1616         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), forward);
1617         VectorNegate(PRVM_G_VECTOR(OFS_PARM2), left);
1618         VectorCopy(PRVM_G_VECTOR(OFS_PARM3), up);
1619         Matrix4x4_FromVectors(&cl.csqc_listenermatrix, forward, left, up, origin);
1620         cl.csqc_usecsqclistener = true; //use csqc listener at this frame
1621 }
1622
1623 //#352 void(string cmdname) registercommand (EXT_CSQC)
1624 static void VM_CL_registercmd (prvm_prog_t *prog)
1625 {
1626         VM_SAFEPARMCOUNT(1, VM_CL_registercmd);
1627         if(!Cmd_Exists(&cmd_client, PRVM_G_STRING(OFS_PARM0)))
1628                 Cmd_AddCommand(&cmd_client, PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
1629 }
1630
1631 //#360 float() readbyte (EXT_CSQC)
1632 static void VM_CL_ReadByte (prvm_prog_t *prog)
1633 {
1634         VM_SAFEPARMCOUNT(0, VM_CL_ReadByte);
1635         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadByte(&cl_message);
1636 }
1637
1638 //#361 float() readchar (EXT_CSQC)
1639 static void VM_CL_ReadChar (prvm_prog_t *prog)
1640 {
1641         VM_SAFEPARMCOUNT(0, VM_CL_ReadChar);
1642         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadChar(&cl_message);
1643 }
1644
1645 //#362 float() readshort (EXT_CSQC)
1646 static void VM_CL_ReadShort (prvm_prog_t *prog)
1647 {
1648         VM_SAFEPARMCOUNT(0, VM_CL_ReadShort);
1649         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadShort(&cl_message);
1650 }
1651
1652 //#363 float() readlong (EXT_CSQC)
1653 static void VM_CL_ReadLong (prvm_prog_t *prog)
1654 {
1655         VM_SAFEPARMCOUNT(0, VM_CL_ReadLong);
1656         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadLong(&cl_message);
1657 }
1658
1659 //#364 float() readcoord (EXT_CSQC)
1660 static void VM_CL_ReadCoord (prvm_prog_t *prog)
1661 {
1662         VM_SAFEPARMCOUNT(0, VM_CL_ReadCoord);
1663         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadCoord(&cl_message, cls.protocol);
1664 }
1665
1666 //#365 float() readangle (EXT_CSQC)
1667 static void VM_CL_ReadAngle (prvm_prog_t *prog)
1668 {
1669         VM_SAFEPARMCOUNT(0, VM_CL_ReadAngle);
1670         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadAngle(&cl_message, cls.protocol);
1671 }
1672
1673 //#366 string() readstring (EXT_CSQC)
1674 static void VM_CL_ReadString (prvm_prog_t *prog)
1675 {
1676         VM_SAFEPARMCOUNT(0, VM_CL_ReadString);
1677         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
1678 }
1679
1680 //#367 float() readfloat (EXT_CSQC)
1681 static void VM_CL_ReadFloat (prvm_prog_t *prog)
1682 {
1683         VM_SAFEPARMCOUNT(0, VM_CL_ReadFloat);
1684         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat(&cl_message);
1685 }
1686
1687 //#501 string() readpicture (DP_CSQC_READWRITEPICTURE)
1688 extern cvar_t cl_readpicture_force;
1689 static void VM_CL_ReadPicture (prvm_prog_t *prog)
1690 {
1691         const char *name;
1692         unsigned char *data;
1693         unsigned char *buf;
1694         unsigned short size;
1695         int i;
1696         cachepic_t *pic;
1697
1698         VM_SAFEPARMCOUNT(0, VM_CL_ReadPicture);
1699
1700         name = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
1701         size = (unsigned short) MSG_ReadShort(&cl_message);
1702
1703         // check if a texture of that name exists
1704         // if yes, it is used and the data is discarded
1705         // if not, the (low quality) data is used to build a new texture, whose name will get returned
1706
1707         pic = Draw_CachePic_Flags(name, CACHEPICFLAG_NOTPERSISTENT | CACHEPICFLAG_FAILONMISSING);
1708
1709         if(size)
1710         {
1711                 if (Draw_IsPicLoaded(pic) && !cl_readpicture_force.integer)
1712                 {
1713                         // texture found and loaded
1714                         // skip over the jpeg as we don't need it
1715                         for(i = 0; i < size; ++i)
1716                                 (void) MSG_ReadByte(&cl_message);
1717                 }
1718                 else
1719                 {
1720                         // texture not found
1721                         // use the attached jpeg as texture
1722                         buf = (unsigned char *) Mem_Alloc(tempmempool, size);
1723                         MSG_ReadBytes(&cl_message, size, buf);
1724                         data = JPEG_LoadImage_BGRA(buf, size, NULL);
1725                         Mem_Free(buf);
1726                         Draw_NewPic(name, image_width, image_height, data, TEXTYPE_BGRA, TEXF_CLAMP);
1727                         Mem_Free(data);
1728                 }
1729         }
1730
1731         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, name);
1732 }
1733
1734 //////////////////////////////////////////////////////////
1735
1736 static void VM_CL_makestatic (prvm_prog_t *prog)
1737 {
1738         prvm_edict_t *ent;
1739
1740         VM_SAFEPARMCOUNT(1, VM_CL_makestatic);
1741
1742         ent = PRVM_G_EDICT(OFS_PARM0);
1743         if (ent == prog->edicts)
1744         {
1745                 VM_Warning(prog, "makestatic: can not modify world entity\n");
1746                 return;
1747         }
1748         if (ent->priv.server->free)
1749         {
1750                 VM_Warning(prog, "makestatic: can not modify free entity\n");
1751                 return;
1752         }
1753
1754         if (cl.num_static_entities < cl.max_static_entities)
1755         {
1756                 int renderflags;
1757                 entity_t *staticent = &cl.static_entities[cl.num_static_entities++];
1758
1759                 // copy it to the current state
1760                 memset(staticent, 0, sizeof(*staticent));
1761                 staticent->render.model = CL_GetModelByIndex((int)PRVM_clientedictfloat(ent, modelindex));
1762                 staticent->render.framegroupblend[0].frame = (int)PRVM_clientedictfloat(ent, frame);
1763                 staticent->render.framegroupblend[0].lerp = 1;
1764                 // make torchs play out of sync
1765                 staticent->render.framegroupblend[0].start = lhrandom(-10, -1);
1766                 staticent->render.skinnum = (int)PRVM_clientedictfloat(ent, skin);
1767                 staticent->render.effects = (int)PRVM_clientedictfloat(ent, effects);
1768                 staticent->render.alpha = PRVM_clientedictfloat(ent, alpha);
1769                 staticent->render.scale = PRVM_clientedictfloat(ent, scale);
1770                 VectorCopy(PRVM_clientedictvector(ent, colormod), staticent->render.colormod);
1771                 VectorCopy(PRVM_clientedictvector(ent, glowmod), staticent->render.glowmod);
1772
1773                 // sanitize values
1774                 if (!staticent->render.alpha)
1775                         staticent->render.alpha = 1.0f;
1776                 if (!staticent->render.scale)
1777                         staticent->render.scale = 1.0f;
1778                 if (!VectorLength2(staticent->render.colormod))
1779                         VectorSet(staticent->render.colormod, 1, 1, 1);
1780                 if (!VectorLength2(staticent->render.glowmod))
1781                         VectorSet(staticent->render.glowmod, 1, 1, 1);
1782
1783                 renderflags = (int)PRVM_clientedictfloat(ent, renderflags);
1784                 if (renderflags & RF_USEAXIS)
1785                 {
1786                         vec3_t forward, left, up, origin;
1787                         VectorCopy(PRVM_clientglobalvector(v_forward), forward);
1788                         VectorNegate(PRVM_clientglobalvector(v_right), left);
1789                         VectorCopy(PRVM_clientglobalvector(v_up), up);
1790                         VectorCopy(PRVM_clientedictvector(ent, origin), origin);
1791                         Matrix4x4_FromVectors(&staticent->render.matrix, forward, left, up, origin);
1792                         Matrix4x4_Scale(&staticent->render.matrix, staticent->render.scale, 1);
1793                 }
1794                 else
1795                         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);
1796
1797                 // either fullbright or lit
1798                 if(!r_fullbright.integer)
1799                 {
1800                         if (!(staticent->render.effects & EF_FULLBRIGHT))
1801                                 staticent->render.flags |= RENDER_LIGHT;
1802                 }
1803                 // turn off shadows from transparent objects
1804                 if (!(staticent->render.effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST)) && (staticent->render.alpha >= 1))
1805                         staticent->render.flags |= RENDER_SHADOW;
1806                 if (staticent->render.effects & EF_NODEPTHTEST)
1807                         staticent->render.flags |= RENDER_NODEPTHTEST;
1808                 if (staticent->render.effects & EF_ADDITIVE)
1809                         staticent->render.flags |= RENDER_ADDITIVE;
1810                 if (staticent->render.effects & EF_DOUBLESIDED)
1811                         staticent->render.flags |= RENDER_DOUBLESIDED;
1812
1813                 staticent->render.allowdecals = true;
1814                 CL_UpdateRenderEntity(&staticent->render);
1815         }
1816         else
1817                 Con_Printf("Too many static entities");
1818
1819 // throw the entity away now
1820         PRVM_ED_Free(prog, ent);
1821 }
1822
1823 //=================================================================//
1824
1825 /*
1826 =================
1827 VM_CL_copyentity
1828
1829 copies data from one entity to another
1830
1831 copyentity(src, dst)
1832 =================
1833 */
1834 static void VM_CL_copyentity (prvm_prog_t *prog)
1835 {
1836         prvm_edict_t *in, *out;
1837         VM_SAFEPARMCOUNT(2, VM_CL_copyentity);
1838         in = PRVM_G_EDICT(OFS_PARM0);
1839         if (in == prog->edicts)
1840         {
1841                 VM_Warning(prog, "copyentity: can not read world entity\n");
1842                 return;
1843         }
1844         if (in->priv.server->free)
1845         {
1846                 VM_Warning(prog, "copyentity: can not read free entity\n");
1847                 return;
1848         }
1849         out = PRVM_G_EDICT(OFS_PARM1);
1850         if (out == prog->edicts)
1851         {
1852                 VM_Warning(prog, "copyentity: can not modify world entity\n");
1853                 return;
1854         }
1855         if (out->priv.server->free)
1856         {
1857                 VM_Warning(prog, "copyentity: can not modify free entity\n");
1858                 return;
1859         }
1860         memcpy(out->fields.fp, in->fields.fp, prog->entityfields * sizeof(prvm_vec_t));
1861         CL_LinkEdict(out);
1862 }
1863
1864 //=================================================================//
1865
1866 // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
1867 static void VM_CL_effect (prvm_prog_t *prog)
1868 {
1869 #if 1
1870         Con_Printf("WARNING: VM_CL_effect not implemented\n"); // FIXME: this needs to take modelname not modelindex, the csqc defs has it as string and so it shall be
1871 #else
1872         vec3_t org;
1873         VM_SAFEPARMCOUNT(5, VM_CL_effect);
1874         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
1875         CL_Effect(org, (int)PRVM_G_FLOAT(OFS_PARM1), (int)PRVM_G_FLOAT(OFS_PARM2), (int)PRVM_G_FLOAT(OFS_PARM3), PRVM_G_FLOAT(OFS_PARM4));
1876 #endif
1877 }
1878
1879 // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
1880 static void VM_CL_te_blood (prvm_prog_t *prog)
1881 {
1882         vec3_t pos, vel, pos2;
1883         VM_SAFEPARMCOUNT(3, VM_CL_te_blood);
1884         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
1885                 return;
1886         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
1887         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), vel);
1888         CL_FindNonSolidLocation(pos, pos2, 4);
1889         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, vel, vel, NULL, 0);
1890 }
1891
1892 // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
1893 static void VM_CL_te_bloodshower (prvm_prog_t *prog)
1894 {
1895         vec_t speed;
1896         vec3_t mincorner, maxcorner, vel1, vel2;
1897         VM_SAFEPARMCOUNT(4, VM_CL_te_bloodshower);
1898         if (PRVM_G_FLOAT(OFS_PARM3) < 1)
1899                 return;
1900         speed = PRVM_G_FLOAT(OFS_PARM2);
1901         vel1[0] = -speed;
1902         vel1[1] = -speed;
1903         vel1[2] = -speed;
1904         vel2[0] = speed;
1905         vel2[1] = speed;
1906         vel2[2] = speed;
1907         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), mincorner);
1908         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), maxcorner);
1909         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM3), mincorner, maxcorner, vel1, vel2, NULL, 0);
1910 }
1911
1912 // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
1913 static void VM_CL_te_explosionrgb (prvm_prog_t *prog)
1914 {
1915         vec3_t          pos;
1916         vec3_t          pos2;
1917         matrix4x4_t     tempmatrix;
1918         VM_SAFEPARMCOUNT(2, VM_CL_te_explosionrgb);
1919         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
1920         CL_FindNonSolidLocation(pos, pos2, 10);
1921         CL_ParticleExplosion(pos2);
1922         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1923         CL_AllocLightFlash(NULL, &tempmatrix, 350, PRVM_G_VECTOR(OFS_PARM1)[0], PRVM_G_VECTOR(OFS_PARM1)[1], PRVM_G_VECTOR(OFS_PARM1)[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1924 }
1925
1926 // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
1927 static void VM_CL_te_particlecube (prvm_prog_t *prog)
1928 {
1929         vec3_t mincorner, maxcorner, vel;
1930         VM_SAFEPARMCOUNT(7, VM_CL_te_particlecube);
1931         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), mincorner);
1932         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), maxcorner);
1933         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), vel);
1934         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));
1935 }
1936
1937 // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
1938 static void VM_CL_te_particlerain (prvm_prog_t *prog)
1939 {
1940         vec3_t mincorner, maxcorner, vel;
1941         VM_SAFEPARMCOUNT(5, VM_CL_te_particlerain);
1942         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), mincorner);
1943         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), maxcorner);
1944         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), vel);
1945         CL_ParticleRain(mincorner, maxcorner, vel, (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4), 0);
1946 }
1947
1948 // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
1949 static void VM_CL_te_particlesnow (prvm_prog_t *prog)
1950 {
1951         vec3_t mincorner, maxcorner, vel;
1952         VM_SAFEPARMCOUNT(5, VM_CL_te_particlesnow);
1953         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), mincorner);
1954         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), maxcorner);
1955         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), vel);
1956         CL_ParticleRain(mincorner, maxcorner, vel, (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4), 1);
1957 }
1958
1959 // #411 void(vector org, vector vel, float howmany) te_spark
1960 static void VM_CL_te_spark (prvm_prog_t *prog)
1961 {
1962         vec3_t pos, pos2, vel;
1963         VM_SAFEPARMCOUNT(3, VM_CL_te_spark);
1964
1965         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
1966         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), vel);
1967         CL_FindNonSolidLocation(pos, pos2, 4);
1968         CL_ParticleEffect(EFFECT_TE_SPARK, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, vel, vel, NULL, 0);
1969 }
1970
1971 extern cvar_t cl_sound_ric_gunshot;
1972 // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
1973 static void VM_CL_te_gunshotquad (prvm_prog_t *prog)
1974 {
1975         vec3_t          pos, pos2;
1976         int                     rnd;
1977         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshotquad);
1978
1979         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
1980         CL_FindNonSolidLocation(pos, pos2, 4);
1981         CL_ParticleEffect(EFFECT_TE_GUNSHOTQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1982         if(cl_sound_ric_gunshot.integer >= 2)
1983         {
1984                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1985                 else
1986                 {
1987                         rnd = rand() & 3;
1988                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1989                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1990                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1991                 }
1992         }
1993 }
1994
1995 // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
1996 static void VM_CL_te_spikequad (prvm_prog_t *prog)
1997 {
1998         vec3_t          pos, pos2;
1999         int                     rnd;
2000         VM_SAFEPARMCOUNT(1, VM_CL_te_spikequad);
2001
2002         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2003         CL_FindNonSolidLocation(pos, pos2, 4);
2004         CL_ParticleEffect(EFFECT_TE_SPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2005         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2006         else
2007         {
2008                 rnd = rand() & 3;
2009                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2010                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2011                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2012         }
2013 }
2014
2015 // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1)
2016 static void VM_CL_te_superspikequad (prvm_prog_t *prog)
2017 {
2018         vec3_t          pos, pos2;
2019         int                     rnd;
2020         VM_SAFEPARMCOUNT(1, VM_CL_te_superspikequad);
2021
2022         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2023         CL_FindNonSolidLocation(pos, pos2, 4);
2024         CL_ParticleEffect(EFFECT_TE_SUPERSPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2025         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
2026         else
2027         {
2028                 rnd = rand() & 3;
2029                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2030                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2031                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2032         }
2033 }
2034
2035 // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1)
2036 static void VM_CL_te_explosionquad (prvm_prog_t *prog)
2037 {
2038         vec3_t          pos, pos2;
2039         VM_SAFEPARMCOUNT(1, VM_CL_te_explosionquad);
2040
2041         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2042         CL_FindNonSolidLocation(pos, pos2, 10);
2043         CL_ParticleEffect(EFFECT_TE_EXPLOSIONQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2044         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2045 }
2046
2047 // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
2048 static void VM_CL_te_smallflash (prvm_prog_t *prog)
2049 {
2050         vec3_t          pos, pos2;
2051         VM_SAFEPARMCOUNT(1, VM_CL_te_smallflash);
2052
2053         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2054         CL_FindNonSolidLocation(pos, pos2, 10);
2055         CL_ParticleEffect(EFFECT_TE_SMALLFLASH, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2056 }
2057
2058 // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
2059 static void VM_CL_te_customflash (prvm_prog_t *prog)
2060 {
2061         vec3_t          pos, pos2;
2062         matrix4x4_t     tempmatrix;
2063         VM_SAFEPARMCOUNT(4, VM_CL_te_customflash);
2064
2065         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2066         CL_FindNonSolidLocation(pos, pos2, 4);
2067         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
2068         CL_AllocLightFlash(NULL, &tempmatrix, PRVM_G_FLOAT(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM3)[0], PRVM_G_VECTOR(OFS_PARM3)[1], PRVM_G_VECTOR(OFS_PARM3)[2], PRVM_G_FLOAT(OFS_PARM1) / PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM2), 0, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
2069 }
2070
2071 // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
2072 static void VM_CL_te_gunshot (prvm_prog_t *prog)
2073 {
2074         vec3_t          pos, pos2;
2075         int                     rnd;
2076         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshot);
2077
2078         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2079         CL_FindNonSolidLocation(pos, pos2, 4);
2080         CL_ParticleEffect(EFFECT_TE_GUNSHOT, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2081         if(cl_sound_ric_gunshot.integer == 1 || cl_sound_ric_gunshot.integer == 3)
2082         {
2083                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2084                 else
2085                 {
2086                         rnd = rand() & 3;
2087                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2088                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2089                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2090                 }
2091         }
2092 }
2093
2094 // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
2095 static void VM_CL_te_spike (prvm_prog_t *prog)
2096 {
2097         vec3_t          pos, pos2;
2098         int                     rnd;
2099         VM_SAFEPARMCOUNT(1, VM_CL_te_spike);
2100
2101         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2102         CL_FindNonSolidLocation(pos, pos2, 4);
2103         CL_ParticleEffect(EFFECT_TE_SPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2104         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2105         else
2106         {
2107                 rnd = rand() & 3;
2108                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2109                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2110                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2111         }
2112 }
2113
2114 // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
2115 static void VM_CL_te_superspike (prvm_prog_t *prog)
2116 {
2117         vec3_t          pos, pos2;
2118         int                     rnd;
2119         VM_SAFEPARMCOUNT(1, VM_CL_te_superspike);
2120
2121         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2122         CL_FindNonSolidLocation(pos, pos2, 4);
2123         CL_ParticleEffect(EFFECT_TE_SUPERSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2124         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2125         else
2126         {
2127                 rnd = rand() & 3;
2128                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2129                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2130                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2131         }
2132 }
2133
2134 // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
2135 static void VM_CL_te_explosion (prvm_prog_t *prog)
2136 {
2137         vec3_t          pos, pos2;
2138         VM_SAFEPARMCOUNT(1, VM_CL_te_explosion);
2139
2140         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2141         CL_FindNonSolidLocation(pos, pos2, 10);
2142         CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2143         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2144 }
2145
2146 // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
2147 static void VM_CL_te_tarexplosion (prvm_prog_t *prog)
2148 {
2149         vec3_t          pos, pos2;
2150         VM_SAFEPARMCOUNT(1, VM_CL_te_tarexplosion);
2151
2152         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2153         CL_FindNonSolidLocation(pos, pos2, 10);
2154         CL_ParticleEffect(EFFECT_TE_TAREXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2155         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2156 }
2157
2158 // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
2159 static void VM_CL_te_wizspike (prvm_prog_t *prog)
2160 {
2161         vec3_t          pos, pos2;
2162         VM_SAFEPARMCOUNT(1, VM_CL_te_wizspike);
2163
2164         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2165         CL_FindNonSolidLocation(pos, pos2, 4);
2166         CL_ParticleEffect(EFFECT_TE_WIZSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2167         S_StartSound(-1, 0, cl.sfx_wizhit, pos2, 1, 1);
2168 }
2169
2170 // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
2171 static void VM_CL_te_knightspike (prvm_prog_t *prog)
2172 {
2173         vec3_t          pos, pos2;
2174         VM_SAFEPARMCOUNT(1, VM_CL_te_knightspike);
2175
2176         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2177         CL_FindNonSolidLocation(pos, pos2, 4);
2178         CL_ParticleEffect(EFFECT_TE_KNIGHTSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2179         S_StartSound(-1, 0, cl.sfx_knighthit, pos2, 1, 1);
2180 }
2181
2182 // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
2183 static void VM_CL_te_lavasplash (prvm_prog_t *prog)
2184 {
2185         vec3_t          pos;
2186         VM_SAFEPARMCOUNT(1, VM_CL_te_lavasplash);
2187         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2188         CL_ParticleEffect(EFFECT_TE_LAVASPLASH, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2189 }
2190
2191 // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
2192 static void VM_CL_te_teleport (prvm_prog_t *prog)
2193 {
2194         vec3_t          pos;
2195         VM_SAFEPARMCOUNT(1, VM_CL_te_teleport);
2196         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2197         CL_ParticleEffect(EFFECT_TE_TELEPORT, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2198 }
2199
2200 // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
2201 static void VM_CL_te_explosion2 (prvm_prog_t *prog)
2202 {
2203         vec3_t          pos, pos2, color;
2204         matrix4x4_t     tempmatrix;
2205         int                     colorStart, colorLength;
2206         unsigned char           *tempcolor;
2207         VM_SAFEPARMCOUNT(3, VM_CL_te_explosion2);
2208
2209         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2210         colorStart = (int)PRVM_G_FLOAT(OFS_PARM1);
2211         colorLength = (int)PRVM_G_FLOAT(OFS_PARM2);
2212         CL_FindNonSolidLocation(pos, pos2, 10);
2213         CL_ParticleExplosion2(pos2, colorStart, colorLength);
2214         tempcolor = palette_rgb[(rand()%colorLength) + colorStart];
2215         color[0] = tempcolor[0] * (2.0f / 255.0f);
2216         color[1] = tempcolor[1] * (2.0f / 255.0f);
2217         color[2] = tempcolor[2] * (2.0f / 255.0f);
2218         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
2219         CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
2220         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2221 }
2222
2223
2224 // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
2225 static void VM_CL_te_lightning1 (prvm_prog_t *prog)
2226 {
2227         vec3_t          start, end;
2228         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning1);
2229         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), start);
2230         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), end);
2231         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), start, end, cl.model_bolt, true);
2232 }
2233
2234 // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
2235 static void VM_CL_te_lightning2 (prvm_prog_t *prog)
2236 {
2237         vec3_t          start, end;
2238         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning2);
2239         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), start);
2240         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), end);
2241         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), start, end, cl.model_bolt2, true);
2242 }
2243
2244 // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
2245 static void VM_CL_te_lightning3 (prvm_prog_t *prog)
2246 {
2247         vec3_t          start, end;
2248         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning3);
2249         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), start);
2250         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), end);
2251         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), start, end, cl.model_bolt3, false);
2252 }
2253
2254 // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
2255 static void VM_CL_te_beam (prvm_prog_t *prog)
2256 {
2257         vec3_t          start, end;
2258         VM_SAFEPARMCOUNT(3, VM_CL_te_beam);
2259         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), start);
2260         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), end);
2261         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), start, end, cl.model_beam, false);
2262 }
2263
2264 // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
2265 static void VM_CL_te_plasmaburn (prvm_prog_t *prog)
2266 {
2267         vec3_t          pos, pos2;
2268         VM_SAFEPARMCOUNT(1, VM_CL_te_plasmaburn);
2269
2270         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2271         CL_FindNonSolidLocation(pos, pos2, 4);
2272         CL_ParticleEffect(EFFECT_TE_PLASMABURN, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2273 }
2274
2275 // #457 void(vector org, vector velocity, float howmany) te_flamejet (DP_TE_FLAMEJET)
2276 static void VM_CL_te_flamejet (prvm_prog_t *prog)
2277 {
2278         vec3_t          pos, pos2, vel;
2279         VM_SAFEPARMCOUNT(3, VM_CL_te_flamejet);
2280         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
2281                 return;
2282         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), pos);
2283         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), vel);
2284         CL_FindNonSolidLocation(pos, pos2, 4);
2285         CL_ParticleEffect(EFFECT_TE_FLAMEJET, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, vel, vel, NULL, 0);
2286 }
2287
2288
2289 // #443 void(entity e, entity tagentity, string tagname) setattachment
2290 static void VM_CL_setattachment (prvm_prog_t *prog)
2291 {
2292         prvm_edict_t *e;
2293         prvm_edict_t *tagentity;
2294         const char *tagname;
2295         int modelindex;
2296         int tagindex;
2297         dp_model_t *model;
2298         VM_SAFEPARMCOUNT(3, VM_CL_setattachment);
2299
2300         e = PRVM_G_EDICT(OFS_PARM0);
2301         tagentity = PRVM_G_EDICT(OFS_PARM1);
2302         tagname = PRVM_G_STRING(OFS_PARM2);
2303
2304         if (e == prog->edicts)
2305         {
2306                 VM_Warning(prog, "setattachment: can not modify world entity\n");
2307                 return;
2308         }
2309         if (e->priv.server->free)
2310         {
2311                 VM_Warning(prog, "setattachment: can not modify free entity\n");
2312                 return;
2313         }
2314
2315         if (tagentity == NULL)
2316                 tagentity = prog->edicts;
2317
2318         tagindex = 0;
2319         if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0])
2320         {
2321                 modelindex = (int)PRVM_clientedictfloat(tagentity, modelindex);
2322                 model = CL_GetModelByIndex(modelindex);
2323                 if (model)
2324                 {
2325                         tagindex = Mod_Alias_GetTagIndexForName(model, (int)PRVM_clientedictfloat(tagentity, skin), tagname);
2326                         if (tagindex == 0)
2327                                 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);
2328                 }
2329                 else
2330                         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));
2331         }
2332
2333         PRVM_clientedictedict(e, tag_entity) = PRVM_EDICT_TO_PROG(tagentity);
2334         PRVM_clientedictfloat(e, tag_index) = tagindex;
2335 }
2336
2337 /////////////////////////////////////////
2338 // DP_MD3_TAGINFO extension coded by VorteX
2339
2340 static int CL_GetTagIndex (prvm_prog_t *prog, prvm_edict_t *e, const char *tagname)
2341 {
2342         dp_model_t *model = CL_GetModelFromEdict(e);
2343         if (model)
2344                 return Mod_Alias_GetTagIndexForName(model, (int)PRVM_clientedictfloat(e, skin), tagname);
2345         else
2346                 return -1;
2347 }
2348
2349 static int CL_GetExtendedTagInfo (prvm_prog_t *prog, prvm_edict_t *e, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
2350 {
2351         int r;
2352         dp_model_t *model;
2353
2354         *tagname = NULL;
2355         *parentindex = 0;
2356         Matrix4x4_CreateIdentity(tag_localmatrix);
2357
2358         if (tagindex >= 0
2359          && (model = CL_GetModelFromEdict(e))
2360          && model->animscenes)
2361         {
2362                 r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)PRVM_clientedictfloat(e, skin), e->priv.server->frameblend, &e->priv.server->skeleton, tagindex - 1, parentindex, tagname, tag_localmatrix);
2363
2364                 if(!r) // success?
2365                         *parentindex += 1;
2366
2367                 return r;
2368         }
2369
2370         return 1;
2371 }
2372
2373 int CL_GetPitchSign(prvm_prog_t *prog, prvm_edict_t *ent)
2374 {
2375         dp_model_t *model;
2376         if ((model = CL_GetModelFromEdict(ent)) && model->type == mod_alias)
2377                 return -1;
2378         return 1;
2379 }
2380
2381 void CL_GetEntityMatrix (prvm_prog_t *prog, prvm_edict_t *ent, matrix4x4_t *out, qboolean viewmatrix)
2382 {
2383         float scale;
2384         float pitchsign = 1;
2385
2386         scale = PRVM_clientedictfloat(ent, scale);
2387         if (!scale)
2388                 scale = 1.0f;
2389
2390         if(viewmatrix)
2391                 *out = r_refdef.view.matrix;
2392         else if ((int)PRVM_clientedictfloat(ent, renderflags) & RF_USEAXIS)
2393         {
2394                 vec3_t forward;
2395                 vec3_t left;
2396                 vec3_t up;
2397                 vec3_t origin;
2398                 VectorScale(PRVM_clientglobalvector(v_forward), scale, forward);
2399                 VectorScale(PRVM_clientglobalvector(v_right), -scale, left);
2400                 VectorScale(PRVM_clientglobalvector(v_up), scale, up);
2401                 VectorCopy(PRVM_clientedictvector(ent, origin), origin);
2402                 Matrix4x4_FromVectors(out, forward, left, up, origin);
2403         }
2404         else
2405         {
2406                 pitchsign = CL_GetPitchSign(prog, ent);
2407                 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);
2408         }
2409 }
2410
2411 static int CL_GetEntityLocalTagMatrix(prvm_prog_t *prog, prvm_edict_t *ent, int tagindex, matrix4x4_t *out)
2412 {
2413         dp_model_t *model;
2414         if (tagindex >= 0
2415          && (model = CL_GetModelFromEdict(ent))
2416          && model->animscenes)
2417         {
2418                 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
2419                 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, cl.time);
2420                 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
2421                 return Mod_Alias_GetTagMatrix(model, ent->priv.server->frameblend, &ent->priv.server->skeleton, tagindex, out);
2422         }
2423         *out = identitymatrix;
2424         return 0;
2425 }
2426
2427 // Warnings/errors code:
2428 // 0 - normal (everything all-right)
2429 // 1 - world entity
2430 // 2 - free entity
2431 // 3 - null or non-precached model
2432 // 4 - no tags with requested index
2433 // 5 - runaway loop at attachment chain
2434 extern cvar_t cl_bob;
2435 extern cvar_t cl_bobcycle;
2436 extern cvar_t cl_bobup;
2437 int CL_GetTagMatrix (prvm_prog_t *prog, matrix4x4_t *out, prvm_edict_t *ent, int tagindex, prvm_vec_t *returnshadingorigin)
2438 {
2439         int ret;
2440         int attachloop;
2441         matrix4x4_t entitymatrix, tagmatrix, attachmatrix;
2442         dp_model_t *model;
2443         vec3_t shadingorigin;
2444
2445         *out = identitymatrix; // warnings and errors return identical matrix
2446
2447         if (ent == prog->edicts)
2448                 return 1;
2449         if (ent->priv.server->free)
2450                 return 2;
2451
2452         model = CL_GetModelFromEdict(ent);
2453         if(!model)
2454                 return 3;
2455
2456         tagmatrix = identitymatrix;
2457         attachloop = 0;
2458         for(;;)
2459         {
2460                 if(attachloop >= 256)
2461                         return 5;
2462                 // apply transformation by child's tagindex on parent entity and then
2463                 // by parent entity itself
2464                 ret = CL_GetEntityLocalTagMatrix(prog, ent, tagindex - 1, &attachmatrix);
2465                 if(ret && attachloop == 0)
2466                         return ret;
2467                 CL_GetEntityMatrix(prog, ent, &entitymatrix, false);
2468                 Matrix4x4_Concat(&tagmatrix, &attachmatrix, out);
2469                 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2470                 // next iteration we process the parent entity
2471                 if (PRVM_clientedictedict(ent, tag_entity))
2472                 {
2473                         tagindex = (int)PRVM_clientedictfloat(ent, tag_index);
2474                         ent = PRVM_EDICT_NUM(PRVM_clientedictedict(ent, tag_entity));
2475                 }
2476                 else
2477                         break;
2478                 attachloop++;
2479         }
2480
2481         // RENDER_VIEWMODEL magic
2482         if ((int)PRVM_clientedictfloat(ent, renderflags) & RF_VIEWMODEL)
2483         {
2484                 Matrix4x4_Copy(&tagmatrix, out);
2485
2486                 CL_GetEntityMatrix(prog, prog->edicts, &entitymatrix, true);
2487                 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2488
2489                 /*
2490                 // Cl_bob, ported from rendering code
2491                 if (PRVM_clientedictfloat(ent, health) > 0 && cl_bob.value && cl_bobcycle.value)
2492                 {
2493                         double bob, cycle;
2494                         // LadyHavoc: this code is *weird*, but not replacable (I think it
2495                         // should be done in QC on the server, but oh well, quake is quake)
2496                         // LadyHavoc: figured out bobup: the time at which the sin is at 180
2497                         // degrees (which allows lengthening or squishing the peak or valley)
2498                         cycle = cl.time/cl_bobcycle.value;
2499                         cycle -= (int)cycle;
2500                         if (cycle < cl_bobup.value)
2501                                 cycle = sin(M_PI * cycle / cl_bobup.value);
2502                         else
2503                                 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
2504                         // bob is proportional to velocity in the xy plane
2505                         // (don't count Z, or jumping messes it up)
2506                         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;
2507                         bob = bob*0.3 + bob*0.7*cycle;
2508                         Matrix4x4_AdjustOrigin(out, 0, 0, bound(-7, bob, 4));
2509                 }
2510                 */
2511
2512                 // return the origin of the view
2513                 Matrix4x4_OriginFromMatrix(&r_refdef.view.matrix, shadingorigin);
2514         }
2515         else
2516         {
2517                 // return the origin of the root entity in the chain
2518                 Matrix4x4_OriginFromMatrix(out, shadingorigin);
2519         }
2520         if (returnshadingorigin)
2521                 VectorCopy(shadingorigin, returnshadingorigin);
2522         return 0;
2523 }
2524
2525 // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
2526 static void VM_CL_gettagindex (prvm_prog_t *prog)
2527 {
2528         prvm_edict_t *ent;
2529         const char *tag_name;
2530         int tag_index;
2531
2532         VM_SAFEPARMCOUNT(2, VM_CL_gettagindex);
2533
2534         ent = PRVM_G_EDICT(OFS_PARM0);
2535         tag_name = PRVM_G_STRING(OFS_PARM1);
2536         if (ent == prog->edicts)
2537         {
2538                 VM_Warning(prog, "VM_CL_gettagindex(entity #%i): can't affect world entity\n", PRVM_NUM_FOR_EDICT(ent));
2539                 return;
2540         }
2541         if (ent->priv.server->free)
2542         {
2543                 VM_Warning(prog, "VM_CL_gettagindex(entity #%i): can't affect free entity\n", PRVM_NUM_FOR_EDICT(ent));
2544                 return;
2545         }
2546
2547         tag_index = 0;
2548         if (!CL_GetModelFromEdict(ent))
2549                 Con_DPrintf("VM_CL_gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
2550         else
2551         {
2552                 tag_index = CL_GetTagIndex(prog, ent, tag_name);
2553                 if (tag_index == 0)
2554                         if(developer_extra.integer)
2555                                 Con_DPrintf("VM_CL_gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
2556         }
2557         PRVM_G_FLOAT(OFS_RETURN) = tag_index;
2558 }
2559
2560 // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO)
2561 static void VM_CL_gettaginfo (prvm_prog_t *prog)
2562 {
2563         prvm_edict_t *e;
2564         int tagindex;
2565         matrix4x4_t tag_matrix;
2566         matrix4x4_t tag_localmatrix;
2567         int parentindex;
2568         const char *tagname;
2569         int returncode;
2570         vec3_t forward, left, up, origin;
2571         const dp_model_t *model;
2572
2573         VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo);
2574
2575         e = PRVM_G_EDICT(OFS_PARM0);
2576         tagindex = (int)PRVM_G_FLOAT(OFS_PARM1);
2577         returncode = CL_GetTagMatrix(prog, &tag_matrix, e, tagindex, NULL);
2578         Matrix4x4_ToVectors(&tag_matrix, forward, left, up, origin);
2579         VectorCopy(forward, PRVM_clientglobalvector(v_forward));
2580         VectorScale(left, -1, PRVM_clientglobalvector(v_right));
2581         VectorCopy(up, PRVM_clientglobalvector(v_up));
2582         VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
2583         model = CL_GetModelFromEdict(e);
2584         VM_GenerateFrameGroupBlend(prog, e->priv.server->framegroupblend, e);
2585         VM_FrameBlendFromFrameGroupBlend(e->priv.server->frameblend, e->priv.server->framegroupblend, model, cl.time);
2586         VM_UpdateEdictSkeleton(prog, e, model, e->priv.server->frameblend);
2587         CL_GetExtendedTagInfo(prog, e, tagindex, &parentindex, &tagname, &tag_localmatrix);
2588         Matrix4x4_ToVectors(&tag_localmatrix, forward, left, up, origin);
2589
2590         PRVM_clientglobalfloat(gettaginfo_parent) = parentindex;
2591         PRVM_clientglobalstring(gettaginfo_name) = tagname ? PRVM_SetTempString(prog, tagname) : 0;
2592         VectorCopy(forward, PRVM_clientglobalvector(gettaginfo_forward));
2593         VectorScale(left, -1, PRVM_clientglobalvector(gettaginfo_right));
2594         VectorCopy(up, PRVM_clientglobalvector(gettaginfo_up));
2595         VectorCopy(origin, PRVM_clientglobalvector(gettaginfo_offset));
2596
2597         switch(returncode)
2598         {
2599                 case 1:
2600                         VM_Warning(prog, "gettagindex: can't affect world entity\n");
2601                         break;
2602                 case 2:
2603                         VM_Warning(prog, "gettagindex: can't affect free entity\n");
2604                         break;
2605                 case 3:
2606                         Con_DPrintf("CL_GetTagMatrix(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(e));
2607                         break;
2608                 case 4:
2609                         Con_DPrintf("CL_GetTagMatrix(entity #%i): model has no tag with requested index %i\n", PRVM_NUM_FOR_EDICT(e), tagindex);
2610                         break;
2611                 case 5:
2612                         Con_DPrintf("CL_GetTagMatrix(entity #%i): runaway loop at attachment chain\n", PRVM_NUM_FOR_EDICT(e));
2613                         break;
2614         }
2615 }
2616
2617 //============================================================================
2618
2619 //====================
2620 // DP_CSQC_SPAWNPARTICLE
2621 // a QC hook to engine's CL_NewParticle
2622 //====================
2623
2624 // particle theme struct
2625 typedef struct vmparticletheme_s
2626 {
2627         unsigned short typeindex;
2628         qboolean initialized;
2629         pblend_t blendmode;
2630         porientation_t orientation;
2631         int color1;
2632         int color2;
2633         int tex;
2634         float size;
2635         float sizeincrease;
2636         float alpha;
2637         float alphafade;
2638         float gravity;
2639         float bounce;
2640         float airfriction;
2641         float liquidfriction;
2642         float originjitter;
2643         float velocityjitter;
2644         qboolean qualityreduction;
2645         float lifetime;
2646         float stretch;
2647         int staincolor1;
2648         int staincolor2;
2649         int staintex;
2650         float stainalpha;
2651         float stainsize;
2652         float delayspawn;
2653         float delaycollision;
2654         float angle;
2655         float spin;
2656 }vmparticletheme_t;
2657
2658 // particle spawner
2659 typedef struct vmparticlespawner_s
2660 {
2661         mempool_t                       *pool;
2662         qboolean                        initialized;
2663         qboolean                        verified;
2664         vmparticletheme_t       *themes;
2665         int                                     max_themes;
2666 }vmparticlespawner_t;
2667
2668 vmparticlespawner_t vmpartspawner;
2669
2670 // TODO: automatic max_themes grow
2671 static void VM_InitParticleSpawner (prvm_prog_t *prog, int maxthemes)
2672 {
2673         // bound max themes to not be an insane value
2674         if (maxthemes < 4)
2675                 maxthemes = 4;
2676         if (maxthemes > 2048)
2677                 maxthemes = 2048;
2678         // allocate and set up structure
2679         if (vmpartspawner.initialized) // reallocate
2680         {
2681                 Mem_FreePool(&vmpartspawner.pool);
2682                 memset(&vmpartspawner, 0, sizeof(vmparticlespawner_t));
2683         }
2684         vmpartspawner.pool = Mem_AllocPool("VMPARTICLESPAWNER", 0, NULL);
2685         vmpartspawner.themes = (vmparticletheme_t *)Mem_Alloc(vmpartspawner.pool, sizeof(vmparticletheme_t)*maxthemes);
2686         vmpartspawner.max_themes = maxthemes;
2687         vmpartspawner.initialized = true;
2688         vmpartspawner.verified = true;
2689 }
2690
2691 // reset particle theme to default values
2692 static void VM_ResetParticleTheme (vmparticletheme_t *theme)
2693 {
2694         theme->initialized = true;
2695         theme->typeindex = pt_static;
2696         theme->blendmode = PBLEND_ADD;
2697         theme->orientation = PARTICLE_BILLBOARD;
2698         theme->color1 = 0x808080;
2699         theme->color2 = 0xFFFFFF;
2700         theme->tex = 63;
2701         theme->size = 2;
2702         theme->sizeincrease = 0;
2703         theme->alpha = 256;
2704         theme->alphafade = 512;
2705         theme->gravity = 0.0f;
2706         theme->bounce = 0.0f;
2707         theme->airfriction = 1.0f;
2708         theme->liquidfriction = 4.0f;
2709         theme->originjitter = 0.0f;
2710         theme->velocityjitter = 0.0f;
2711         theme->qualityreduction = false;
2712         theme->lifetime = 4;
2713         theme->stretch = 1;
2714         theme->staincolor1 = -1;
2715         theme->staincolor2 = -1;
2716         theme->staintex = -1;
2717         theme->delayspawn = 0.0f;
2718         theme->delaycollision = 0.0f;
2719         theme->angle = 0.0f;
2720         theme->spin = 0.0f;
2721 }
2722
2723 // particle theme -> QC globals
2724 static void VM_CL_ParticleThemeToGlobals(vmparticletheme_t *theme, prvm_prog_t *prog)
2725 {
2726         PRVM_clientglobalfloat(particle_type) = theme->typeindex;
2727         PRVM_clientglobalfloat(particle_blendmode) = theme->blendmode;
2728         PRVM_clientglobalfloat(particle_orientation) = theme->orientation;
2729         // VorteX: int only can store 0-255, not 0-256 which means 0 - 0,99609375...
2730         VectorSet(PRVM_clientglobalvector(particle_color1), (theme->color1 >> 16) & 0xFF, (theme->color1 >> 8) & 0xFF, (theme->color1 >> 0) & 0xFF);
2731         VectorSet(PRVM_clientglobalvector(particle_color2), (theme->color2 >> 16) & 0xFF, (theme->color2 >> 8) & 0xFF, (theme->color2 >> 0) & 0xFF);
2732         PRVM_clientglobalfloat(particle_tex) = (prvm_vec_t)theme->tex;
2733         PRVM_clientglobalfloat(particle_size) = theme->size;
2734         PRVM_clientglobalfloat(particle_sizeincrease) = theme->sizeincrease;
2735         PRVM_clientglobalfloat(particle_alpha) = theme->alpha/256;
2736         PRVM_clientglobalfloat(particle_alphafade) = theme->alphafade/256;
2737         PRVM_clientglobalfloat(particle_time) = theme->lifetime;
2738         PRVM_clientglobalfloat(particle_gravity) = theme->gravity;
2739         PRVM_clientglobalfloat(particle_bounce) = theme->bounce;
2740         PRVM_clientglobalfloat(particle_airfriction) = theme->airfriction;
2741         PRVM_clientglobalfloat(particle_liquidfriction) = theme->liquidfriction;
2742         PRVM_clientglobalfloat(particle_originjitter) = theme->originjitter;
2743         PRVM_clientglobalfloat(particle_velocityjitter) = theme->velocityjitter;
2744         PRVM_clientglobalfloat(particle_qualityreduction) = theme->qualityreduction;
2745         PRVM_clientglobalfloat(particle_stretch) = theme->stretch;
2746         VectorSet(PRVM_clientglobalvector(particle_staincolor1), ((int)theme->staincolor1 >> 16) & 0xFF, ((int)theme->staincolor1 >> 8) & 0xFF, ((int)theme->staincolor1 >> 0) & 0xFF);
2747         VectorSet(PRVM_clientglobalvector(particle_staincolor2), ((int)theme->staincolor2 >> 16) & 0xFF, ((int)theme->staincolor2 >> 8) & 0xFF, ((int)theme->staincolor2 >> 0) & 0xFF);
2748         PRVM_clientglobalfloat(particle_staintex) = (prvm_vec_t)theme->staintex;
2749         PRVM_clientglobalfloat(particle_stainalpha) = (prvm_vec_t)theme->stainalpha/256;
2750         PRVM_clientglobalfloat(particle_stainsize) = (prvm_vec_t)theme->stainsize;
2751         PRVM_clientglobalfloat(particle_delayspawn) = theme->delayspawn;
2752         PRVM_clientglobalfloat(particle_delaycollision) = theme->delaycollision;
2753         PRVM_clientglobalfloat(particle_angle) = theme->angle;
2754         PRVM_clientglobalfloat(particle_spin) = theme->spin;
2755 }
2756
2757 // QC globals ->  particle theme
2758 static void VM_CL_ParticleThemeFromGlobals(vmparticletheme_t *theme, prvm_prog_t *prog)
2759 {
2760         theme->typeindex = (unsigned short)PRVM_clientglobalfloat(particle_type);
2761         theme->blendmode = (pblend_t)(int)PRVM_clientglobalfloat(particle_blendmode);
2762         theme->orientation = (porientation_t)(int)PRVM_clientglobalfloat(particle_orientation);
2763         theme->color1 = ((int)PRVM_clientglobalvector(particle_color1)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color1)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color1)[2]);
2764         theme->color2 = ((int)PRVM_clientglobalvector(particle_color2)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color2)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color2)[2]);
2765         theme->tex = (int)PRVM_clientglobalfloat(particle_tex);
2766         theme->size = PRVM_clientglobalfloat(particle_size);
2767         theme->sizeincrease = PRVM_clientglobalfloat(particle_sizeincrease);
2768         theme->alpha = PRVM_clientglobalfloat(particle_alpha)*256;
2769         theme->alphafade = PRVM_clientglobalfloat(particle_alphafade)*256;
2770         theme->lifetime = PRVM_clientglobalfloat(particle_time);
2771         theme->gravity = PRVM_clientglobalfloat(particle_gravity);
2772         theme->bounce = PRVM_clientglobalfloat(particle_bounce);
2773         theme->airfriction = PRVM_clientglobalfloat(particle_airfriction);
2774         theme->liquidfriction = PRVM_clientglobalfloat(particle_liquidfriction);
2775         theme->originjitter = PRVM_clientglobalfloat(particle_originjitter);
2776         theme->velocityjitter = PRVM_clientglobalfloat(particle_velocityjitter);
2777         theme->qualityreduction = PRVM_clientglobalfloat(particle_qualityreduction) != 0 ? true : false;
2778         theme->stretch = PRVM_clientglobalfloat(particle_stretch);
2779         theme->staincolor1 = ((int)PRVM_clientglobalvector(particle_staincolor1)[0])*65536 + (int)(PRVM_clientglobalvector(particle_staincolor1)[1])*256 + (int)(PRVM_clientglobalvector(particle_staincolor1)[2]);
2780         theme->staincolor2 = (int)(PRVM_clientglobalvector(particle_staincolor2)[0])*65536 + (int)(PRVM_clientglobalvector(particle_staincolor2)[1])*256 + (int)(PRVM_clientglobalvector(particle_staincolor2)[2]);
2781         theme->staintex =(int)PRVM_clientglobalfloat(particle_staintex);
2782         theme->stainalpha = PRVM_clientglobalfloat(particle_stainalpha)*256;
2783         theme->stainsize = PRVM_clientglobalfloat(particle_stainsize);
2784         theme->delayspawn = PRVM_clientglobalfloat(particle_delayspawn);
2785         theme->delaycollision = PRVM_clientglobalfloat(particle_delaycollision);
2786         theme->angle = PRVM_clientglobalfloat(particle_angle);
2787         theme->spin = PRVM_clientglobalfloat(particle_spin);
2788 }
2789
2790 // init particle spawner interface
2791 // # float(float max_themes) initparticlespawner
2792 static void VM_CL_InitParticleSpawner (prvm_prog_t *prog)
2793 {
2794         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_InitParticleSpawner);
2795         VM_InitParticleSpawner(prog, (int)PRVM_G_FLOAT(OFS_PARM0));
2796         vmpartspawner.themes[0].initialized = true;
2797         VM_ResetParticleTheme(&vmpartspawner.themes[0]);
2798         PRVM_G_FLOAT(OFS_RETURN) = (vmpartspawner.verified == true) ? 1 : 0;
2799 }
2800
2801 // void() resetparticle
2802 static void VM_CL_ResetParticle (prvm_prog_t *prog)
2803 {
2804         VM_SAFEPARMCOUNT(0, VM_CL_ResetParticle);
2805         if (vmpartspawner.verified == false)
2806         {
2807                 VM_Warning(prog, "VM_CL_ResetParticle: particle spawner not initialized\n");
2808                 return;
2809         }
2810         VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0], prog);
2811 }
2812
2813 // void(float themenum) particletheme
2814 static void VM_CL_ParticleTheme (prvm_prog_t *prog)
2815 {
2816         int themenum;
2817
2818         VM_SAFEPARMCOUNT(1, VM_CL_ParticleTheme);
2819         if (vmpartspawner.verified == false)
2820         {
2821                 VM_Warning(prog, "VM_CL_ParticleTheme: particle spawner not initialized\n");
2822                 return;
2823         }
2824         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2825         if (themenum < 0 || themenum >= vmpartspawner.max_themes)
2826         {
2827                 VM_Warning(prog, "VM_CL_ParticleTheme: bad theme number %i\n", themenum);
2828                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0], prog);
2829                 return;
2830         }
2831         if (vmpartspawner.themes[themenum].initialized == false)
2832         {
2833                 VM_Warning(prog, "VM_CL_ParticleTheme: theme #%i not exists\n", themenum);
2834                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0], prog);
2835                 return;
2836         }
2837         // load particle theme into globals
2838         VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[themenum], prog);
2839 }
2840
2841 // float() saveparticletheme
2842 // void(float themenum) updateparticletheme
2843 static void VM_CL_ParticleThemeSave (prvm_prog_t *prog)
2844 {
2845         int themenum;
2846
2847         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_ParticleThemeSave);
2848         if (vmpartspawner.verified == false)
2849         {
2850                 VM_Warning(prog, "VM_CL_ParticleThemeSave: particle spawner not initialized\n");
2851                 return;
2852         }
2853         // allocate new theme, save it and return
2854         if (prog->argc < 1)
2855         {
2856                 for (themenum = 0; themenum < vmpartspawner.max_themes; themenum++)
2857                         if (vmpartspawner.themes[themenum].initialized == false)
2858                                 break;
2859                 if (themenum >= vmpartspawner.max_themes)
2860                 {
2861                         if (vmpartspawner.max_themes == 2048)
2862                                 VM_Warning(prog, "VM_CL_ParticleThemeSave: no free theme slots\n");
2863                         else
2864                                 VM_Warning(prog, "VM_CL_ParticleThemeSave: no free theme slots, try initparticlespawner() with highter max_themes\n");
2865                         PRVM_G_FLOAT(OFS_RETURN) = -1;
2866                         return;
2867                 }
2868                 vmpartspawner.themes[themenum].initialized = true;
2869                 VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum], prog);
2870                 PRVM_G_FLOAT(OFS_RETURN) = themenum;
2871                 return;
2872         }
2873         // update existing theme
2874         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2875         if (themenum < 0 || themenum >= vmpartspawner.max_themes)
2876         {
2877                 VM_Warning(prog, "VM_CL_ParticleThemeSave: bad theme number %i\n", themenum);
2878                 return;
2879         }
2880         vmpartspawner.themes[themenum].initialized = true;
2881         VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum], prog);
2882 }
2883
2884 // void(float themenum) freeparticletheme
2885 static void VM_CL_ParticleThemeFree (prvm_prog_t *prog)
2886 {
2887         int themenum;
2888
2889         VM_SAFEPARMCOUNT(1, VM_CL_ParticleThemeFree);
2890         if (vmpartspawner.verified == false)
2891         {
2892                 VM_Warning(prog, "VM_CL_ParticleThemeFree: particle spawner not initialized\n");
2893                 return;
2894         }
2895         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2896         // check parms
2897         if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2898         {
2899                 VM_Warning(prog, "VM_CL_ParticleThemeFree: bad theme number %i\n", themenum);
2900                 return;
2901         }
2902         if (vmpartspawner.themes[themenum].initialized == false)
2903         {
2904                 VM_Warning(prog, "VM_CL_ParticleThemeFree: theme #%i already freed\n", themenum);
2905                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0], prog);
2906                 return;
2907         }
2908         // free theme
2909         VM_ResetParticleTheme(&vmpartspawner.themes[themenum]);
2910         vmpartspawner.themes[themenum].initialized = false;
2911 }
2912
2913 // float(vector org, vector dir, [float theme]) particle
2914 // returns 0 if failed, 1 if succesful
2915 static void VM_CL_SpawnParticle (prvm_prog_t *prog)
2916 {
2917         vec3_t org, dir;
2918         vmparticletheme_t *theme;
2919         particle_t *part;
2920         int themenum;
2921
2922         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_SpawnParticle2);
2923         if (vmpartspawner.verified == false)
2924         {
2925                 VM_Warning(prog, "VM_CL_SpawnParticle: particle spawner not initialized\n");
2926                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
2927                 return;
2928         }
2929         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
2930         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), dir);
2931         
2932         if (prog->argc < 3) // global-set particle
2933         {
2934                 part = CL_NewParticle(org,
2935                         (unsigned short)PRVM_clientglobalfloat(particle_type),
2936                         ((int)PRVM_clientglobalvector(particle_color1)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color1)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color1)[2]),
2937                         ((int)PRVM_clientglobalvector(particle_color2)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color2)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color2)[2]),
2938                         (int)PRVM_clientglobalfloat(particle_tex),
2939                         PRVM_clientglobalfloat(particle_size),
2940                         PRVM_clientglobalfloat(particle_sizeincrease),
2941                         PRVM_clientglobalfloat(particle_alpha)*256,
2942                         PRVM_clientglobalfloat(particle_alphafade)*256,
2943                         PRVM_clientglobalfloat(particle_gravity),
2944                         PRVM_clientglobalfloat(particle_bounce),
2945                         org[0],
2946                         org[1],
2947                         org[2],
2948                         dir[0],
2949                         dir[1],
2950                         dir[2],
2951                         PRVM_clientglobalfloat(particle_airfriction),
2952                         PRVM_clientglobalfloat(particle_liquidfriction),
2953                         PRVM_clientglobalfloat(particle_originjitter),
2954                         PRVM_clientglobalfloat(particle_velocityjitter),
2955                         (PRVM_clientglobalfloat(particle_qualityreduction)) ? true : false,
2956                         PRVM_clientglobalfloat(particle_time),
2957                         PRVM_clientglobalfloat(particle_stretch),
2958                         (pblend_t)(int)PRVM_clientglobalfloat(particle_blendmode),
2959                         (porientation_t)(int)PRVM_clientglobalfloat(particle_orientation),
2960                         (int)(PRVM_clientglobalvector(particle_staincolor1)[0])*65536 + (int)(PRVM_clientglobalvector(particle_staincolor1)[1])*256 + (int)(PRVM_clientglobalvector(particle_staincolor1)[2]),
2961                         (int)(PRVM_clientglobalvector(particle_staincolor2)[0])*65536 + (int)(PRVM_clientglobalvector(particle_staincolor2)[1])*256 + (int)(PRVM_clientglobalvector(particle_staincolor2)[2]),
2962                         (int)PRVM_clientglobalfloat(particle_staintex),
2963                         PRVM_clientglobalfloat(particle_stainalpha)*256,
2964                         PRVM_clientglobalfloat(particle_stainsize),
2965                         PRVM_clientglobalfloat(particle_angle),
2966                         PRVM_clientglobalfloat(particle_spin),
2967                         NULL);
2968                 if (!part)
2969                 {
2970                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
2971                         return;
2972                 }
2973                 if (PRVM_clientglobalfloat(particle_delayspawn))
2974                         part->delayedspawn = cl.time + PRVM_clientglobalfloat(particle_delayspawn);
2975                 //if (PRVM_clientglobalfloat(particle_delaycollision))
2976                 //      part->delayedcollisions = cl.time + PRVM_clientglobalfloat(particle_delaycollision);
2977         }
2978         else // quick themed particle
2979         {
2980                 themenum = (int)PRVM_G_FLOAT(OFS_PARM2);
2981                 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2982                 {
2983                         VM_Warning(prog, "VM_CL_SpawnParticle: bad theme number %i\n", themenum);
2984                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
2985                         return;
2986                 }
2987                 theme = &vmpartspawner.themes[themenum];
2988                 part = CL_NewParticle(org,
2989                         theme->typeindex,
2990                         theme->color1,
2991                         theme->color2,
2992                         theme->tex,
2993                         theme->size,
2994                         theme->sizeincrease,
2995                         theme->alpha,
2996                         theme->alphafade,
2997                         theme->gravity,
2998                         theme->bounce,
2999                         org[0],
3000                         org[1],
3001                         org[2],
3002                         dir[0],
3003                         dir[1],
3004                         dir[2],
3005                         theme->airfriction,
3006                         theme->liquidfriction,
3007                         theme->originjitter,
3008                         theme->velocityjitter,
3009                         theme->qualityreduction,
3010                         theme->lifetime,
3011                         theme->stretch,
3012                         theme->blendmode,
3013                         theme->orientation,
3014                         theme->staincolor1,
3015                         theme->staincolor2,
3016                         theme->staintex,
3017                         theme->stainalpha,
3018                         theme->stainsize,
3019                         theme->angle,
3020                         theme->spin,
3021                         NULL);
3022                 if (!part)
3023                 {
3024                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
3025                         return;
3026                 }
3027                 if (theme->delayspawn)
3028                         part->delayedspawn = cl.time + theme->delayspawn;
3029                 //if (theme->delaycollision)
3030                 //      part->delayedcollisions = cl.time + theme->delaycollision;
3031         }
3032         PRVM_G_FLOAT(OFS_RETURN) = 1; 
3033 }
3034
3035 // float(vector org, vector dir, float spawndelay, float collisiondelay, [float theme]) delayedparticle
3036 // returns 0 if failed, 1 if success
3037 static void VM_CL_SpawnParticleDelayed (prvm_prog_t *prog)
3038 {
3039         vec3_t org, dir;
3040         vmparticletheme_t *theme;
3041         particle_t *part;
3042         int themenum;
3043
3044         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_SpawnParticle2);
3045         if (vmpartspawner.verified == false)
3046         {
3047                 VM_Warning(prog, "VM_CL_SpawnParticle: particle spawner not initialized\n");
3048                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
3049                 return;
3050         }
3051         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
3052         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), dir);
3053         if (prog->argc < 5) // global-set particle
3054                 part = CL_NewParticle(org,
3055                         (unsigned short)PRVM_clientglobalfloat(particle_type),
3056                         ((int)PRVM_clientglobalvector(particle_color1)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color1)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color1)[2]),
3057                         ((int)PRVM_clientglobalvector(particle_color2)[0] << 16) + ((int)PRVM_clientglobalvector(particle_color2)[1] << 8) + ((int)PRVM_clientglobalvector(particle_color2)[2]),
3058                         (int)PRVM_clientglobalfloat(particle_tex),
3059                         PRVM_clientglobalfloat(particle_size),
3060                         PRVM_clientglobalfloat(particle_sizeincrease),
3061                         PRVM_clientglobalfloat(particle_alpha)*256,
3062                         PRVM_clientglobalfloat(particle_alphafade)*256,
3063                         PRVM_clientglobalfloat(particle_gravity),
3064                         PRVM_clientglobalfloat(particle_bounce),
3065                         org[0],
3066                         org[1],
3067                         org[2],
3068                         dir[0],
3069                         dir[1],
3070                         dir[2],
3071                         PRVM_clientglobalfloat(particle_airfriction),
3072                         PRVM_clientglobalfloat(particle_liquidfriction),
3073                         PRVM_clientglobalfloat(particle_originjitter),
3074                         PRVM_clientglobalfloat(particle_velocityjitter),
3075                         (PRVM_clientglobalfloat(particle_qualityreduction)) ? true : false,
3076                         PRVM_clientglobalfloat(particle_time),
3077                         PRVM_clientglobalfloat(particle_stretch),
3078                         (pblend_t)(int)PRVM_clientglobalfloat(particle_blendmode),
3079                         (porientation_t)(int)PRVM_clientglobalfloat(particle_orientation),
3080                         ((int)PRVM_clientglobalvector(particle_staincolor1)[0] << 16) + ((int)PRVM_clientglobalvector(particle_staincolor1)[1] << 8) + ((int)PRVM_clientglobalvector(particle_staincolor1)[2]),
3081                         ((int)PRVM_clientglobalvector(particle_staincolor2)[0] << 16) + ((int)PRVM_clientglobalvector(particle_staincolor2)[1] << 8) + ((int)PRVM_clientglobalvector(particle_staincolor2)[2]),
3082                         (int)PRVM_clientglobalfloat(particle_staintex),
3083                         PRVM_clientglobalfloat(particle_stainalpha)*256,
3084                         PRVM_clientglobalfloat(particle_stainsize),
3085                         PRVM_clientglobalfloat(particle_angle),
3086                         PRVM_clientglobalfloat(particle_spin),
3087                         NULL);
3088         else // themed particle
3089         {
3090                 themenum = (int)PRVM_G_FLOAT(OFS_PARM4);
3091                 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
3092                 {
3093                         VM_Warning(prog, "VM_CL_SpawnParticle: bad theme number %i\n", themenum);
3094                         PRVM_G_FLOAT(OFS_RETURN) = 0;  
3095                         return;
3096                 }
3097                 theme = &vmpartspawner.themes[themenum];
3098                 part = CL_NewParticle(org,
3099                         theme->typeindex,
3100                         theme->color1,
3101                         theme->color2,
3102                         theme->tex,
3103                         theme->size,
3104                         theme->sizeincrease,
3105                         theme->alpha,
3106                         theme->alphafade,
3107                         theme->gravity,
3108                         theme->bounce,
3109                         org[0],
3110                         org[1],
3111                         org[2],
3112                         dir[0],
3113                         dir[1],
3114                         dir[2],
3115                         theme->airfriction,
3116                         theme->liquidfriction,
3117                         theme->originjitter,
3118                         theme->velocityjitter,
3119                         theme->qualityreduction,
3120                         theme->lifetime,
3121                         theme->stretch,
3122                         theme->blendmode,
3123                         theme->orientation,
3124                         theme->staincolor1,
3125                         theme->staincolor2,
3126                         theme->staintex,
3127                         theme->stainalpha,
3128                         theme->stainsize,
3129                         theme->angle,
3130                         theme->spin,
3131                         NULL);
3132         }
3133         if (!part) 
3134         { 
3135                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
3136                 return; 
3137         }
3138         part->delayedspawn = cl.time + PRVM_G_FLOAT(OFS_PARM2);
3139         //part->delayedcollisions = cl.time + PRVM_G_FLOAT(OFS_PARM3);
3140         PRVM_G_FLOAT(OFS_RETURN) = 0;
3141 }
3142
3143 //====================
3144 //CSQC engine entities query
3145 //====================
3146
3147 // float(float entitynum, float whatfld) getentity;
3148 // vector(float entitynum, float whatfld) getentityvec;
3149 // querying engine-drawn entity
3150 // VorteX: currently it's only tested with whatfld = 1..7
3151 static void VM_CL_GetEntity (prvm_prog_t *prog)
3152 {
3153         int entnum, fieldnum;
3154         vec3_t forward, left, up, org;
3155         VM_SAFEPARMCOUNT(2, VM_CL_GetEntityVec);
3156
3157         entnum = PRVM_G_FLOAT(OFS_PARM0);
3158         if (entnum < 0 || entnum >= cl.num_entities)
3159         {
3160                 PRVM_G_FLOAT(OFS_RETURN) = 0;
3161                 return;
3162         }
3163         fieldnum = PRVM_G_FLOAT(OFS_PARM1);
3164         switch(fieldnum)
3165         {
3166                 case 0: // active state
3167                         PRVM_G_FLOAT(OFS_RETURN) = cl.entities_active[entnum];
3168                         break;
3169                 case 1: // origin
3170                         Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, org);
3171                         VectorCopy(org, PRVM_G_VECTOR(OFS_RETURN));
3172                         break; 
3173                 case 2: // forward
3174                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, forward, left, up, org);
3175                         VectorCopy(forward, PRVM_G_VECTOR(OFS_RETURN));
3176                         break;
3177                 case 3: // right
3178                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, forward, left, up, org);
3179                         VectorNegate(left, PRVM_G_VECTOR(OFS_RETURN));
3180                         break;
3181                 case 4: // up
3182                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, forward, left, up, org);
3183                         VectorCopy(up, PRVM_G_VECTOR(OFS_RETURN));
3184                         break;
3185                 case 5: // scale
3186                         PRVM_G_FLOAT(OFS_RETURN) = Matrix4x4_ScaleFromMatrix(&cl.entities[entnum].render.matrix);
3187                         break;  
3188                 case 6: // origin + v_forward, v_right, v_up
3189                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, forward, left, up, org);
3190                         VectorCopy(forward, PRVM_clientglobalvector(v_forward));
3191                         VectorNegate(left, PRVM_clientglobalvector(v_right));
3192                         VectorCopy(up, PRVM_clientglobalvector(v_up));
3193                         VectorCopy(org, PRVM_G_VECTOR(OFS_RETURN));
3194                         break;  
3195                 case 7: // alpha
3196                         PRVM_G_FLOAT(OFS_RETURN) = cl.entities[entnum].render.alpha;
3197                         break;  
3198                 case 8: // colormor
3199                         VectorCopy(cl.entities[entnum].render.colormod, PRVM_G_VECTOR(OFS_RETURN));
3200                         break;
3201                 case 9: // pants colormod
3202                         VectorCopy(cl.entities[entnum].render.colormap_pantscolor, PRVM_G_VECTOR(OFS_RETURN));
3203                         break;
3204                 case 10: // shirt colormod
3205                         VectorCopy(cl.entities[entnum].render.colormap_shirtcolor, PRVM_G_VECTOR(OFS_RETURN));
3206                         break;
3207                 case 11: // skinnum
3208                         PRVM_G_FLOAT(OFS_RETURN) = cl.entities[entnum].render.skinnum;
3209                         break;  
3210                 case 12: // mins
3211                         VectorCopy(cl.entities[entnum].render.mins, PRVM_G_VECTOR(OFS_RETURN));         
3212                         break;  
3213                 case 13: // maxs
3214                         VectorCopy(cl.entities[entnum].render.maxs, PRVM_G_VECTOR(OFS_RETURN));         
3215                         break;  
3216                 case 14: // absmin
3217                         Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, org);
3218                         VectorAdd(cl.entities[entnum].render.mins, org, PRVM_G_VECTOR(OFS_RETURN));             
3219                         break;  
3220                 case 15: // absmax
3221                         Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, org);
3222                         VectorAdd(cl.entities[entnum].render.maxs, org, PRVM_G_VECTOR(OFS_RETURN));             
3223                         break;
3224                 case 16: // light
3225                         VectorMA(cl.entities[entnum].render.render_modellight_ambient, 0.5, cl.entities[entnum].render.render_modellight_diffuse, PRVM_G_VECTOR(OFS_RETURN));
3226                         break;  
3227                 default:
3228                         PRVM_G_FLOAT(OFS_RETURN) = 0;
3229                         break;
3230         }
3231 }
3232
3233 //====================
3234 //QC POLYGON functions
3235 //====================
3236
3237 //#304 void() renderscene (EXT_CSQC)
3238 // moved that here to reset the polygons,
3239 // resetting them earlier causes R_Mesh_Draw to be called with numvertices = 0
3240 // --blub
3241 static void VM_CL_R_RenderScene (prvm_prog_t *prog)
3242 {
3243         qboolean ismain = r_refdef.view.ismain;
3244         double t = Sys_DirtyTime();
3245         VM_SAFEPARMCOUNT(0, VM_CL_R_RenderScene);
3246
3247         // update the views
3248         if(ismain)
3249         {
3250                 // set the main view
3251                 csqc_main_r_refdef_view = r_refdef.view;
3252         }
3253
3254         // we need to update any RENDER_VIEWMODEL entities at this point because
3255         // csqc supplies its own view matrix
3256         CL_UpdateViewEntities();
3257         CL_MeshEntities_AddToScene();
3258         CL_UpdateEntityShading();
3259
3260         // now draw stuff!
3261         R_RenderView(0, NULL, NULL, r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
3262
3263         Mod_Mesh_Reset(CL_Mesh_CSQC());
3264
3265         // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView
3266         t = Sys_DirtyTime() - t;if (t < 0 || t >= 1800) t = 0;
3267         prog->functions[PRVM_clientfunction(CSQC_UpdateView)].totaltime -= t;
3268
3269         // polygonbegin without draw2d arg has to guess
3270         prog->polygonbegin_guess2d = false;
3271
3272         // update the views
3273         if (ismain)
3274         {
3275                 // clear the flags so no other view becomes "main" unless CSQC sets VF_MAINVIEW
3276                 r_refdef.view.ismain = false;
3277                 csqc_original_r_refdef_view.ismain = false;
3278         }
3279 }
3280
3281 //void(string texturename, float flag[, float is2d]) R_BeginPolygon
3282 static void VM_CL_R_PolygonBegin (prvm_prog_t *prog)
3283 {
3284         const char *texname;
3285         int drawflags;
3286         qboolean draw2d;
3287         dp_model_t *mod;
3288
3289         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_R_PolygonBegin);
3290
3291         texname = PRVM_G_STRING(OFS_PARM0);
3292         drawflags = (int)PRVM_G_FLOAT(OFS_PARM1);
3293         if (prog->argc >= 3)
3294                 draw2d = PRVM_G_FLOAT(OFS_PARM2) != 0;
3295         else
3296         {
3297                 // weird hacky way to figure out if this is a 2D HUD polygon or a scene
3298                 // polygon, for compatibility with mods aimed at old darkplaces versions
3299                 // - polygonbegin_guess2d is 0 if the most recent major call was
3300                 // clearscene, 1 if the most recent major call was drawpic (and similar)
3301                 // or renderscene
3302                 draw2d = prog->polygonbegin_guess2d;
3303         }
3304
3305         // we need to remember whether this is a 2D or 3D mesh we're adding to
3306         mod = draw2d ? CL_Mesh_UI() : CL_Mesh_CSQC();
3307         prog->polygonbegin_model = mod;
3308         Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, texname, drawflags, TEXF_ALPHA, MATERIALFLAG_WALL | MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX), false);
3309 }
3310
3311 //void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
3312 static void VM_CL_R_PolygonVertex (prvm_prog_t *prog)
3313 {
3314         const prvm_vec_t *v = PRVM_G_VECTOR(OFS_PARM0);
3315         const prvm_vec_t *tc = PRVM_G_VECTOR(OFS_PARM1);
3316         const prvm_vec_t *c = PRVM_G_VECTOR(OFS_PARM2);
3317         const prvm_vec_t a = PRVM_G_FLOAT(OFS_PARM3);
3318         dp_model_t *mod = prog->polygonbegin_model;
3319         int e0, e1, e2;
3320         msurface_t *surf;
3321
3322         VM_SAFEPARMCOUNT(4, VM_CL_R_PolygonVertex);
3323
3324         if (!mod || mod->num_surfaces == 0)
3325         {
3326                 VM_Warning(prog, "VM_CL_R_PolygonVertex: VM_CL_R_PolygonBegin wasn't called\n");
3327                 return;
3328         }
3329
3330         surf = &mod->data_surfaces[mod->num_surfaces - 1];
3331         e2 = Mod_Mesh_IndexForVertex(mod, surf, v[0], v[1], v[2], 0, 0, 0, tc[0], tc[1], 0, 0, c[0], c[1], c[2], a);
3332         if (surf->num_vertices >= 3)
3333         {
3334                 // the first element is the start of the triangle fan
3335                 e0 = surf->num_firstvertex;
3336                 // the second element is the previous vertex
3337                 e1 = e0 + 1;
3338                 if (surf->num_triangles > 0)
3339                         e1 = mod->surfmesh.data_element3i[(surf->num_firsttriangle + surf->num_triangles) * 3 - 1];
3340                 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
3341         }
3342 }
3343
3344 //void() R_EndPolygon
3345 static void VM_CL_R_PolygonEnd (prvm_prog_t *prog)
3346 {
3347         dp_model_t *mod = prog->polygonbegin_model;
3348         msurface_t *surf;
3349
3350         VM_SAFEPARMCOUNT(0, VM_CL_R_PolygonEnd);
3351         if (!mod || mod->num_surfaces == 0)
3352         {
3353                 VM_Warning(prog, "VM_CL_R_PolygonEnd: VM_CL_R_PolygonBegin wasn't called\n");
3354                 return;
3355         }
3356         surf = &mod->data_surfaces[mod->num_surfaces - 1];
3357         Mod_BuildNormals(surf->num_firstvertex, surf->num_vertices, surf->num_triangles, mod->surfmesh.data_vertex3f, mod->surfmesh.data_element3i + 3 * surf->num_firsttriangle, mod->surfmesh.data_normal3f, true);
3358         prog->polygonbegin_model = NULL;
3359 }
3360
3361 /*
3362 =============
3363 CL_CheckBottom
3364
3365 Returns false if any part of the bottom of the entity is off an edge that
3366 is not a staircase.
3367
3368 =============
3369 */
3370 static qboolean CL_CheckBottom (prvm_edict_t *ent)
3371 {
3372         prvm_prog_t *prog = CLVM_prog;
3373         vec3_t  mins, maxs, start, stop;
3374         trace_t trace;
3375         int             x, y;
3376         float   mid, bottom;
3377
3378         VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
3379         VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
3380
3381 // if all of the points under the corners are solid world, don't bother
3382 // with the tougher checks
3383 // the corners must be within 16 of the midpoint
3384         start[2] = mins[2] - 1;
3385         for     (x=0 ; x<=1 ; x++)
3386                 for     (y=0 ; y<=1 ; y++)
3387                 {
3388                         start[0] = x ? maxs[0] : mins[0];
3389                         start[1] = y ? maxs[1] : mins[1];
3390                         if (!(CL_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
3391                                 goto realcheck;
3392                 }
3393
3394         return true;            // we got out easy
3395
3396 realcheck:
3397 //
3398 // check it for real...
3399 //
3400         start[2] = mins[2];
3401
3402 // the midpoint must be within 16 of the bottom
3403         start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
3404         start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
3405         stop[2] = start[2] - 2*sv_stepheight.value;
3406         trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, false, NULL, true, false);
3407
3408         if (trace.fraction == 1.0)
3409                 return false;
3410         mid = bottom = trace.endpos[2];
3411
3412 // the corners must be within 16 of the midpoint
3413         for     (x=0 ; x<=1 ; x++)
3414                 for     (y=0 ; y<=1 ; y++)
3415                 {
3416                         start[0] = stop[0] = x ? maxs[0] : mins[0];
3417                         start[1] = stop[1] = y ? maxs[1] : mins[1];
3418
3419                         trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, false, NULL, true, false);
3420
3421                         if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
3422                                 bottom = trace.endpos[2];
3423                         if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
3424                                 return false;
3425                 }
3426
3427         return true;
3428 }
3429
3430 /*
3431 =============
3432 CL_movestep
3433
3434 Called by monster program code.
3435 The move will be adjusted for slopes and stairs, but if the move isn't
3436 possible, no move is done and false is returned
3437 =============
3438 */
3439 static qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean settrace)
3440 {
3441         prvm_prog_t *prog = CLVM_prog;
3442         float           dz;
3443         vec3_t          oldorg, neworg, end, traceendpos;
3444         vec3_t          mins, maxs, start;
3445         trace_t         trace;
3446         int                     i, svent;
3447         prvm_edict_t            *enemy;
3448
3449 // try the move
3450         VectorCopy(PRVM_clientedictvector(ent, mins), mins);
3451         VectorCopy(PRVM_clientedictvector(ent, maxs), maxs);
3452         VectorCopy (PRVM_clientedictvector(ent, origin), oldorg);
3453         VectorAdd (PRVM_clientedictvector(ent, origin), move, neworg);
3454
3455 // flying monsters don't step up
3456         if ( (int)PRVM_clientedictfloat(ent, flags) & (FL_SWIM | FL_FLY) )
3457         {
3458         // try one move with vertical motion, then one without
3459                 for (i=0 ; i<2 ; i++)
3460                 {
3461                         VectorAdd (PRVM_clientedictvector(ent, origin), move, neworg);
3462                         enemy = PRVM_PROG_TO_EDICT(PRVM_clientedictedict(ent, enemy));
3463                         if (i == 0 && enemy != prog->edicts)
3464                         {
3465                                 dz = PRVM_clientedictvector(ent, origin)[2] - PRVM_clientedictvector(PRVM_PROG_TO_EDICT(PRVM_clientedictedict(ent, enemy)), origin)[2];
3466                                 if (dz > 40)
3467                                         neworg[2] -= 8;
3468                                 if (dz < 30)
3469                                         neworg[2] += 8;
3470                         }
3471                         VectorCopy(PRVM_clientedictvector(ent, origin), start);
3472                         trace = CL_TraceBox(start, mins, maxs, neworg, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, &svent, true);
3473                         if (settrace)
3474                                 CL_VM_SetTraceGlobals(prog, &trace, svent);
3475
3476                         if (trace.fraction == 1)
3477                         {
3478                                 VectorCopy(trace.endpos, traceendpos);
3479                                 if (((int)PRVM_clientedictfloat(ent, flags) & FL_SWIM) && !(CL_PointSuperContents(traceendpos) & SUPERCONTENTS_LIQUIDSMASK))
3480                                         return false;   // swim monster left water
3481
3482                                 VectorCopy (traceendpos, PRVM_clientedictvector(ent, origin));
3483                                 if (relink)
3484                                         CL_LinkEdict(ent);
3485                                 return true;
3486                         }
3487
3488                         if (enemy == prog->edicts)
3489                                 break;
3490                 }
3491
3492                 return false;
3493         }
3494
3495 // push down from a step height above the wished position
3496         neworg[2] += sv_stepheight.value;
3497         VectorCopy (neworg, end);
3498         end[2] -= sv_stepheight.value*2;
3499
3500         trace = CL_TraceBox(neworg, mins, maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, &svent, true);
3501         if (settrace)
3502                 CL_VM_SetTraceGlobals(prog, &trace, svent);
3503
3504         if (trace.startsolid)
3505         {
3506                 neworg[2] -= sv_stepheight.value;
3507                 trace = CL_TraceBox(neworg, mins, maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value, true, true, &svent, true);
3508                 if (settrace)
3509                         CL_VM_SetTraceGlobals(prog, &trace, svent);
3510                 if (trace.startsolid)
3511                         return false;
3512         }
3513         if (trace.fraction == 1)
3514         {
3515         // if monster had the ground pulled out, go ahead and fall
3516                 if ( (int)PRVM_clientedictfloat(ent, flags) & FL_PARTIALGROUND )
3517                 {
3518                         VectorAdd (PRVM_clientedictvector(ent, origin), move, PRVM_clientedictvector(ent, origin));
3519                         if (relink)
3520                                 CL_LinkEdict(ent);
3521                         PRVM_clientedictfloat(ent, flags) = (int)PRVM_clientedictfloat(ent, flags) & ~FL_ONGROUND;
3522                         return true;
3523                 }
3524
3525                 return false;           // walked off an edge
3526         }
3527
3528 // check point traces down for dangling corners
3529         VectorCopy (trace.endpos, PRVM_clientedictvector(ent, origin));
3530
3531         if (!CL_CheckBottom (ent))
3532         {
3533                 if ( (int)PRVM_clientedictfloat(ent, flags) & FL_PARTIALGROUND )
3534                 {       // entity had floor mostly pulled out from underneath it
3535                         // and is trying to correct
3536                         if (relink)
3537                                 CL_LinkEdict(ent);
3538                         return true;
3539                 }
3540                 VectorCopy (oldorg, PRVM_clientedictvector(ent, origin));
3541                 return false;
3542         }
3543
3544         if ( (int)PRVM_clientedictfloat(ent, flags) & FL_PARTIALGROUND )
3545                 PRVM_clientedictfloat(ent, flags) = (int)PRVM_clientedictfloat(ent, flags) & ~FL_PARTIALGROUND;
3546
3547         PRVM_clientedictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
3548
3549 // the move is ok
3550         if (relink)
3551                 CL_LinkEdict(ent);
3552         return true;
3553 }
3554
3555 /*
3556 ===============
3557 VM_CL_walkmove
3558
3559 float(float yaw, float dist[, settrace]) walkmove
3560 ===============
3561 */
3562 static void VM_CL_walkmove (prvm_prog_t *prog)
3563 {
3564         prvm_edict_t    *ent;
3565         float   yaw, dist;
3566         vec3_t  move;
3567         mfunction_t     *oldf;
3568         int     oldself;
3569         qboolean        settrace;
3570
3571         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_walkmove);
3572
3573         // assume failure if it returns early
3574         PRVM_G_FLOAT(OFS_RETURN) = 0;
3575
3576         ent = PRVM_PROG_TO_EDICT(PRVM_clientglobaledict(self));
3577         if (ent == prog->edicts)
3578         {
3579                 VM_Warning(prog, "walkmove: can not modify world entity\n");
3580                 return;
3581         }
3582         if (ent->priv.server->free)
3583         {
3584                 VM_Warning(prog, "walkmove: can not modify free entity\n");
3585                 return;
3586         }
3587         yaw = PRVM_G_FLOAT(OFS_PARM0);
3588         dist = PRVM_G_FLOAT(OFS_PARM1);
3589         settrace = prog->argc >= 3 && PRVM_G_FLOAT(OFS_PARM2);
3590
3591         if ( !( (int)PRVM_clientedictfloat(ent, flags) & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
3592                 return;
3593
3594         yaw = yaw*M_PI*2 / 360;
3595
3596         move[0] = cos(yaw)*dist;
3597         move[1] = sin(yaw)*dist;
3598         move[2] = 0;
3599
3600 // save program state, because CL_movestep may call other progs
3601         oldf = prog->xfunction;
3602         oldself = PRVM_clientglobaledict(self);
3603
3604         PRVM_G_FLOAT(OFS_RETURN) = CL_movestep(ent, move, true, false, settrace);
3605
3606
3607 // restore program state
3608         prog->xfunction = oldf;
3609         PRVM_clientglobaledict(self) = oldself;
3610 }
3611
3612 /*
3613 ===============
3614 VM_CL_serverkey
3615
3616 string(string key) serverkey
3617 ===============
3618 */
3619 static void VM_CL_serverkey(prvm_prog_t *prog)
3620 {
3621         char string[VM_STRINGTEMP_LENGTH];
3622         VM_SAFEPARMCOUNT(1, VM_CL_serverkey);
3623         InfoString_GetValue(cl.qw_serverinfo, PRVM_G_STRING(OFS_PARM0), string, sizeof(string));
3624         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
3625 }
3626
3627 /*
3628 =================
3629 VM_CL_checkpvs
3630
3631 Checks if an entity is in a point's PVS.
3632 Should be fast but can be inexact.
3633
3634 float checkpvs(vector viewpos, entity viewee) = #240;
3635 =================
3636 */
3637 static void VM_CL_checkpvs (prvm_prog_t *prog)
3638 {
3639         vec3_t viewpos;
3640         prvm_edict_t *viewee;
3641         vec3_t mi, ma;
3642 #if 1
3643         unsigned char *pvs;
3644 #else
3645         int fatpvsbytes;
3646         unsigned char fatpvs[MAX_MAP_LEAFS/8];
3647 #endif
3648
3649         VM_SAFEPARMCOUNT(2, VM_SV_checkpvs);
3650         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), viewpos);
3651         viewee = PRVM_G_EDICT(OFS_PARM1);
3652
3653         if(viewee->priv.required->free)
3654         {
3655                 VM_Warning(prog, "checkpvs: can not check free entity\n");
3656                 PRVM_G_FLOAT(OFS_RETURN) = 4;
3657                 return;
3658         }
3659
3660         VectorAdd(PRVM_serveredictvector(viewee, origin), PRVM_serveredictvector(viewee, mins), mi);
3661         VectorAdd(PRVM_serveredictvector(viewee, origin), PRVM_serveredictvector(viewee, maxs), ma);
3662
3663 #if 1
3664         if(!cl.worldmodel || !cl.worldmodel->brush.GetPVS || !cl.worldmodel->brush.BoxTouchingPVS)
3665         {
3666                 // no PVS support on this worldmodel... darn
3667                 PRVM_G_FLOAT(OFS_RETURN) = 3;
3668                 return;
3669         }
3670         pvs = cl.worldmodel->brush.GetPVS(cl.worldmodel, viewpos);
3671         if(!pvs)
3672         {
3673                 // viewpos isn't in any PVS... darn
3674                 PRVM_G_FLOAT(OFS_RETURN) = 2;
3675                 return;
3676         }
3677         PRVM_G_FLOAT(OFS_RETURN) = cl.worldmodel->brush.BoxTouchingPVS(cl.worldmodel, pvs, mi, ma);
3678 #else
3679         // using fat PVS like FTEQW does (slow)
3680         if(!cl.worldmodel || !cl.worldmodel->brush.FatPVS || !cl.worldmodel->brush.BoxTouchingPVS)
3681         {
3682                 // no PVS support on this worldmodel... darn
3683                 PRVM_G_FLOAT(OFS_RETURN) = 3;
3684                 return;
3685         }
3686         fatpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, viewpos, 8, fatpvs, sizeof(fatpvs), false);
3687         if(!fatpvsbytes)
3688         {
3689                 // viewpos isn't in any PVS... darn
3690                 PRVM_G_FLOAT(OFS_RETURN) = 2;
3691                 return;
3692         }
3693         PRVM_G_FLOAT(OFS_RETURN) = cl.worldmodel->brush.BoxTouchingPVS(cl.worldmodel, fatpvs, mi, ma);
3694 #endif
3695 }
3696
3697 // #263 float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) create a skeleton (be sure to assign this value into .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure  (for example if the modelindex is not skeletal), it is recommended that you create a new skeleton if you change modelindex.
3698 static void VM_CL_skel_create(prvm_prog_t *prog)
3699 {
3700         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
3701         dp_model_t *model = CL_GetModelByIndex(modelindex);
3702         skeleton_t *skeleton;
3703         int i;
3704         PRVM_G_FLOAT(OFS_RETURN) = 0;
3705         if (!model || !model->num_bones)
3706                 return;
3707         for (i = 0;i < MAX_EDICTS;i++)
3708                 if (!prog->skeletons[i])
3709                         break;
3710         if (i == MAX_EDICTS)
3711                 return;
3712         prog->skeletons[i] = skeleton = (skeleton_t *)Mem_Alloc(cls.levelmempool, sizeof(skeleton_t) + model->num_bones * sizeof(matrix4x4_t));
3713         PRVM_G_FLOAT(OFS_RETURN) = i + 1;
3714         skeleton->model = model;
3715         skeleton->relativetransforms = (matrix4x4_t *)(skeleton+1);
3716         // initialize to identity matrices
3717         for (i = 0;i < skeleton->model->num_bones;i++)
3718                 skeleton->relativetransforms[i] = identitymatrix;
3719 }
3720
3721 // #264 float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .frame3, .frame4, .lerpfrac, .lerpfrac3, .lerpfrac4, .frame1time, .frame2time, .frame3time, .frame4time), returns skel on success, 0 on failure
3722 static void VM_CL_skel_build(prvm_prog_t *prog)
3723 {
3724         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3725         skeleton_t *skeleton;
3726         prvm_edict_t *ed = PRVM_G_EDICT(OFS_PARM1);
3727         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM2);
3728         float retainfrac = PRVM_G_FLOAT(OFS_PARM3);
3729         int firstbone = PRVM_G_FLOAT(OFS_PARM4) - 1;
3730         int lastbone = PRVM_G_FLOAT(OFS_PARM5) - 1;
3731         dp_model_t *model = CL_GetModelByIndex(modelindex);
3732         int numblends;
3733         int bonenum;
3734         int blendindex;
3735         framegroupblend_t framegroupblend[MAX_FRAMEGROUPBLENDS];
3736         frameblend_t frameblend[MAX_FRAMEBLENDS];
3737         matrix4x4_t bonematrix;
3738         matrix4x4_t matrix;
3739         PRVM_G_FLOAT(OFS_RETURN) = 0;
3740         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3741                 return;
3742         firstbone = max(0, firstbone);
3743         lastbone = min(lastbone, model->num_bones - 1);
3744         lastbone = min(lastbone, skeleton->model->num_bones - 1);
3745         VM_GenerateFrameGroupBlend(prog, framegroupblend, ed);
3746         VM_FrameBlendFromFrameGroupBlend(frameblend, framegroupblend, model, cl.time);
3747         for (numblends = 0;numblends < MAX_FRAMEBLENDS && frameblend[numblends].lerp;numblends++)
3748                 ;
3749         for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
3750         {
3751                 memset(&bonematrix, 0, sizeof(bonematrix));
3752                 for (blendindex = 0;blendindex < numblends;blendindex++)
3753                 {
3754                         Matrix4x4_FromBonePose7s(&matrix, model->num_posescale, model->data_poses7s + 7 * (frameblend[blendindex].subframe * model->num_bones + bonenum));
3755                         Matrix4x4_Accumulate(&bonematrix, &matrix, frameblend[blendindex].lerp);
3756                 }
3757                 Matrix4x4_Normalize3(&bonematrix, &bonematrix);
3758                 Matrix4x4_Interpolate(&skeleton->relativetransforms[bonenum], &bonematrix, &skeleton->relativetransforms[bonenum], retainfrac);
3759         }
3760         PRVM_G_FLOAT(OFS_RETURN) = skeletonindex + 1;
3761 }
3762
3763 // #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
3764 static void VM_CL_skel_get_numbones(prvm_prog_t *prog)
3765 {
3766         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3767         skeleton_t *skeleton;
3768         PRVM_G_FLOAT(OFS_RETURN) = 0;
3769         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3770                 return;
3771         PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->num_bones;
3772 }
3773
3774 // #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
3775 static void VM_CL_skel_get_bonename(prvm_prog_t *prog)
3776 {
3777         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3778         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3779         skeleton_t *skeleton;
3780         PRVM_G_INT(OFS_RETURN) = 0;
3781         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3782                 return;
3783         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3784                 return;
3785         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, skeleton->model->data_bones[bonenum].name);
3786 }
3787
3788 // #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) returns parent num for supplied bonenum, 0 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
3789 static void VM_CL_skel_get_boneparent(prvm_prog_t *prog)
3790 {
3791         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3792         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3793         skeleton_t *skeleton;
3794         PRVM_G_FLOAT(OFS_RETURN) = 0;
3795         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3796                 return;
3797         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3798                 return;
3799         PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->data_bones[bonenum].parent + 1;
3800 }
3801
3802 // #268 float(float skel, string tagname) skel_find_bone = #268; // (FTE_CSQC_SKELETONOBJECTS) get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex
3803 static void VM_CL_skel_find_bone(prvm_prog_t *prog)
3804 {
3805         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3806         const char *tagname = PRVM_G_STRING(OFS_PARM1);
3807         skeleton_t *skeleton;
3808         PRVM_G_FLOAT(OFS_RETURN) = 0;
3809         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3810                 return;
3811         PRVM_G_FLOAT(OFS_RETURN) = Mod_Alias_GetTagIndexForName(skeleton->model, 0, tagname);
3812 }
3813
3814 // #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
3815 static void VM_CL_skel_get_bonerel(prvm_prog_t *prog)
3816 {
3817         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3818         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3819         skeleton_t *skeleton;
3820         matrix4x4_t matrix;
3821         vec3_t forward, left, up, origin;
3822         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
3823         VectorClear(PRVM_clientglobalvector(v_forward));
3824         VectorClear(PRVM_clientglobalvector(v_right));
3825         VectorClear(PRVM_clientglobalvector(v_up));
3826         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3827                 return;
3828         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3829                 return;
3830         matrix = skeleton->relativetransforms[bonenum];
3831         Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
3832         VectorCopy(forward, PRVM_clientglobalvector(v_forward));
3833         VectorNegate(left, PRVM_clientglobalvector(v_right));
3834         VectorCopy(up, PRVM_clientglobalvector(v_up));
3835         VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
3836 }
3837
3838 // #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
3839 static void VM_CL_skel_get_boneabs(prvm_prog_t *prog)
3840 {
3841         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3842         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3843         skeleton_t *skeleton;
3844         matrix4x4_t matrix;
3845         matrix4x4_t temp;
3846         vec3_t forward, left, up, origin;
3847         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
3848         VectorClear(PRVM_clientglobalvector(v_forward));
3849         VectorClear(PRVM_clientglobalvector(v_right));
3850         VectorClear(PRVM_clientglobalvector(v_up));
3851         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3852                 return;
3853         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3854                 return;
3855         matrix = skeleton->relativetransforms[bonenum];
3856         // convert to absolute
3857         while ((bonenum = skeleton->model->data_bones[bonenum].parent) >= 0)
3858         {
3859                 temp = matrix;
3860                 Matrix4x4_Concat(&matrix, &skeleton->relativetransforms[bonenum], &temp);
3861         }
3862         Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
3863         VectorCopy(forward, PRVM_clientglobalvector(v_forward));
3864         VectorNegate(left, PRVM_clientglobalvector(v_right));
3865         VectorCopy(up, PRVM_clientglobalvector(v_up));
3866         VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
3867 }
3868
3869 // #271 void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
3870 static void VM_CL_skel_set_bone(prvm_prog_t *prog)
3871 {
3872         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3873         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3874         vec3_t forward, left, up, origin;
3875         skeleton_t *skeleton;
3876         matrix4x4_t matrix;
3877         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3878                 return;
3879         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3880                 return;
3881         VectorCopy(PRVM_clientglobalvector(v_forward), forward);
3882         VectorNegate(PRVM_clientglobalvector(v_right), left);
3883         VectorCopy(PRVM_clientglobalvector(v_up), up);
3884         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
3885         Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
3886         skeleton->relativetransforms[bonenum] = matrix;
3887 }
3888
3889 // #272 void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
3890 static void VM_CL_skel_mul_bone(prvm_prog_t *prog)
3891 {
3892         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3893         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3894         vec3_t forward, left, up, origin;
3895         skeleton_t *skeleton;
3896         matrix4x4_t matrix;
3897         matrix4x4_t temp;
3898         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3899                 return;
3900         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3901                 return;
3902         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);