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