we don't need ALL those fields I marked with ???
[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                 default:
915                         PRVM_G_FLOAT(OFS_RETURN) = 0;
916                         VM_Warning(prog, "VM_CL_R_GetView : unknown parm %i\n", c);
917                         return;
918                 }
919                 return;
920         }
921
922         f = PRVM_G_VECTOR(OFS_PARM1);
923         k = PRVM_G_FLOAT(OFS_PARM1);
924         switch(c)
925         {
926         case VF_MIN:
927                 r_refdef.view.x = (int)(f[0]);
928                 r_refdef.view.y = (int)(f[1]);
929                 DrawQ_RecalcView();
930                 break;
931         case VF_MIN_X:
932                 r_refdef.view.x = (int)(k);
933                 DrawQ_RecalcView();
934                 break;
935         case VF_MIN_Y:
936                 r_refdef.view.y = (int)(k);
937                 DrawQ_RecalcView();
938                 break;
939         case VF_SIZE:
940                 r_refdef.view.width = (int)(f[0]);
941                 r_refdef.view.height = (int)(f[1]);
942                 DrawQ_RecalcView();
943                 break;
944         case VF_SIZE_X:
945                 r_refdef.view.width = (int)(k);
946                 DrawQ_RecalcView();
947                 break;
948         case VF_SIZE_Y:
949                 r_refdef.view.height = (int)(k);
950                 DrawQ_RecalcView();
951                 break;
952         case VF_VIEWPORT:
953                 r_refdef.view.x = (int)(f[0]);
954                 r_refdef.view.y = (int)(f[1]);
955                 f = PRVM_G_VECTOR(OFS_PARM2);
956                 r_refdef.view.width = (int)(f[0]);
957                 r_refdef.view.height = (int)(f[1]);
958                 DrawQ_RecalcView();
959                 break;
960         case VF_FOV:
961                 r_refdef.view.frustum_x = tan(f[0] * M_PI / 360.0);r_refdef.view.ortho_x = f[0];
962                 r_refdef.view.frustum_y = tan(f[1] * M_PI / 360.0);r_refdef.view.ortho_y = f[1];
963                 break;
964         case VF_FOVX:
965                 r_refdef.view.frustum_x = tan(k * M_PI / 360.0);r_refdef.view.ortho_x = k;
966                 break;
967         case VF_FOVY:
968                 r_refdef.view.frustum_y = tan(k * M_PI / 360.0);r_refdef.view.ortho_y = k;
969                 break;
970         case VF_ORIGIN:
971                 VectorCopy(f, cl.csqc_vieworigin);
972                 CSQC_R_RecalcView();
973                 break;
974         case VF_ORIGIN_X:
975                 cl.csqc_vieworigin[0] = k;
976                 CSQC_R_RecalcView();
977                 break;
978         case VF_ORIGIN_Y:
979                 cl.csqc_vieworigin[1] = k;
980                 CSQC_R_RecalcView();
981                 break;
982         case VF_ORIGIN_Z:
983                 cl.csqc_vieworigin[2] = k;
984                 CSQC_R_RecalcView();
985                 break;
986         case VF_ANGLES:
987                 VectorCopy(f, cl.csqc_viewangles);
988                 CSQC_R_RecalcView();
989                 break;
990         case VF_ANGLES_X:
991                 cl.csqc_viewangles[0] = k;
992                 CSQC_R_RecalcView();
993                 break;
994         case VF_ANGLES_Y:
995                 cl.csqc_viewangles[1] = k;
996                 CSQC_R_RecalcView();
997                 break;
998         case VF_ANGLES_Z:
999                 cl.csqc_viewangles[2] = k;
1000                 CSQC_R_RecalcView();
1001                 break;
1002         case VF_DRAWWORLD:
1003                 cl.csqc_vidvars.drawworld = ((k != 0) && r_drawworld.integer);
1004                 break;
1005         case VF_DRAWENGINESBAR:
1006                 cl.csqc_vidvars.drawenginesbar = k != 0;
1007                 break;
1008         case VF_DRAWCROSSHAIR:
1009                 cl.csqc_vidvars.drawcrosshair = k != 0;
1010                 break;
1011         case VF_CL_VIEWANGLES:
1012                 VectorCopy(f, cl.viewangles);
1013                 break;
1014         case VF_CL_VIEWANGLES_X:
1015                 cl.viewangles[0] = k;
1016                 break;
1017         case VF_CL_VIEWANGLES_Y:
1018                 cl.viewangles[1] = k;
1019                 break;
1020         case VF_CL_VIEWANGLES_Z:
1021                 cl.viewangles[2] = k;
1022                 break;
1023         case VF_PERSPECTIVE:
1024                 r_refdef.view.useperspective = k != 0;
1025                 break;
1026         case VF_CLEARSCREEN:
1027                 r_refdef.view.isoverlay = !k;
1028                 break;
1029         case VF_MAINVIEW:
1030                 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.ismain;
1031                 break;
1032         case VF_FOG_DENSITY:
1033                 r_refdef.fog_density = k;
1034                 break;
1035         case VF_FOG_COLOR:
1036                 r_refdef.fog_red = f[0];
1037                 r_refdef.fog_green = f[1];
1038                 r_refdef.fog_blue = f[2];
1039                 break;
1040         case VF_FOG_COLOR_R:
1041                 r_refdef.fog_red = k;
1042                 break;
1043         case VF_FOG_COLOR_G:
1044                 r_refdef.fog_green = k;
1045                 break;
1046         case VF_FOG_COLOR_B:
1047                 r_refdef.fog_blue = k;
1048                 break;
1049         case VF_FOG_ALPHA:
1050                 r_refdef.fog_alpha = k;
1051                 break;
1052         case VF_FOG_START:
1053                 r_refdef.fog_start = k;
1054                 break;
1055         case VF_FOG_END:
1056                 r_refdef.fog_end = k;
1057                 break;
1058         case VF_FOG_HEIGHT:
1059                 r_refdef.fog_height = k;
1060                 break;
1061         case VF_FOG_FADEDEPTH:
1062                 r_refdef.fog_fadedepth = k;
1063                 break;
1064         default:
1065                 PRVM_G_FLOAT(OFS_RETURN) = 0;
1066                 VM_Warning(prog, "VM_CL_R_SetView : unknown parm %i\n", c);
1067                 return;
1068         }
1069         PRVM_G_FLOAT(OFS_RETURN) = 1;
1070 }
1071
1072 //#305 void(vector org, float radius, vector lightcolours[, float style, string cubemapname, float pflags]) adddynamiclight (EXT_CSQC)
1073 static void VM_CL_R_AddDynamicLight (prvm_prog_t *prog)
1074 {
1075         double t = Sys_DirtyTime();
1076         vec_t *org;
1077         float radius = 300;
1078         vec_t *col;
1079         int style = -1;
1080         const char *cubemapname = NULL;
1081         int pflags = PFLAGS_CORONA | PFLAGS_FULLDYNAMIC;
1082         float coronaintensity = 1;
1083         float coronasizescale = 0.25;
1084         qboolean castshadow = true;
1085         float ambientscale = 0;
1086         float diffusescale = 1;
1087         float specularscale = 1;
1088         matrix4x4_t matrix;
1089         vec3_t forward, left, up;
1090         VM_SAFEPARMCOUNTRANGE(3, 8, VM_CL_R_AddDynamicLight);
1091
1092         // if we've run out of dlights, just return
1093         if (r_refdef.scene.numlights >= MAX_DLIGHTS)
1094                 return;
1095
1096         org = PRVM_G_VECTOR(OFS_PARM0);
1097         radius = PRVM_G_FLOAT(OFS_PARM1);
1098         col = PRVM_G_VECTOR(OFS_PARM2);
1099         if (prog->argc >= 4)
1100         {
1101                 style = (int)PRVM_G_FLOAT(OFS_PARM3);
1102                 if (style >= MAX_LIGHTSTYLES)
1103                 {
1104                         Con_DPrintf("VM_CL_R_AddDynamicLight: out of bounds lightstyle index %i\n", style);
1105                         style = -1;
1106                 }
1107         }
1108         if (prog->argc >= 5)
1109                 cubemapname = PRVM_G_STRING(OFS_PARM4);
1110         if (prog->argc >= 6)
1111                 pflags = (int)PRVM_G_FLOAT(OFS_PARM5);
1112         coronaintensity = (pflags & PFLAGS_CORONA) != 0;
1113         castshadow = (pflags & PFLAGS_NOSHADOW) == 0;
1114
1115         VectorScale(PRVM_clientglobalvector(v_forward), radius, forward);
1116         VectorScale(PRVM_clientglobalvector(v_right), -radius, left);
1117         VectorScale(PRVM_clientglobalvector(v_up), radius, up);
1118         Matrix4x4_FromVectors(&matrix, forward, left, up, org);
1119
1120         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);
1121         r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
1122         t = Sys_DirtyTime() - t;if (t < 0 || t >= 1800) t = 0;
1123         prog->functions[PRVM_clientfunction(CSQC_UpdateView)].totaltime -= t;
1124 }
1125
1126 //============================================================================
1127
1128 //#310 vector (vector v) cs_unproject (EXT_CSQC)
1129 static void VM_CL_unproject (prvm_prog_t *prog)
1130 {
1131         float   *f;
1132         vec3_t  temp;
1133
1134         VM_SAFEPARMCOUNT(1, VM_CL_unproject);
1135         f = PRVM_G_VECTOR(OFS_PARM0);
1136         VectorSet(temp,
1137                 f[2],
1138                 (-1.0 + 2.0 * (f[0] / vid_conwidth.integer)) * f[2] * -r_refdef.view.frustum_x,
1139                 (-1.0 + 2.0 * (f[1] / vid_conheight.integer)) * f[2] * -r_refdef.view.frustum_y);
1140         if(v_flipped.integer)
1141                 temp[1] = -temp[1];
1142         Matrix4x4_Transform(&r_refdef.view.matrix, temp, PRVM_G_VECTOR(OFS_RETURN));
1143 }
1144
1145 //#311 vector (vector v) cs_project (EXT_CSQC)
1146 static void VM_CL_project (prvm_prog_t *prog)
1147 {
1148         float   *f;
1149         vec3_t  v;
1150         matrix4x4_t m;
1151
1152         VM_SAFEPARMCOUNT(1, VM_CL_project);
1153         f = PRVM_G_VECTOR(OFS_PARM0);
1154         Matrix4x4_Invert_Simple(&m, &r_refdef.view.matrix);
1155         Matrix4x4_Transform(&m, f, v);
1156         if(v_flipped.integer)
1157                 v[1] = -v[1];
1158         VectorSet(PRVM_G_VECTOR(OFS_RETURN),
1159                 vid_conwidth.integer * (0.5*(1.0+v[1]/v[0]/-r_refdef.view.frustum_x)),
1160                 vid_conheight.integer * (0.5*(1.0+v[2]/v[0]/-r_refdef.view.frustum_y)),
1161                 v[0]);
1162         // explanation:
1163         // after transforming, relative position to viewport (0..1) = 0.5 * (1 + v[2]/v[0]/-frustum_{x \or y})
1164         // as 2D drawing honors the viewport too, to get the same pixel, we simply multiply this by conwidth/height
1165 }
1166
1167 //#330 float(float stnum) getstatf (EXT_CSQC)
1168 static void VM_CL_getstatf (prvm_prog_t *prog)
1169 {
1170         int i;
1171         union
1172         {
1173                 float f;
1174                 int l;
1175         }dat;
1176         VM_SAFEPARMCOUNT(1, VM_CL_getstatf);
1177         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1178         if(i < 0 || i >= MAX_CL_STATS)
1179         {
1180                 VM_Warning(prog, "VM_CL_getstatf: index>=MAX_CL_STATS or index<0\n");
1181                 return;
1182         }
1183         dat.l = cl.stats[i];
1184         PRVM_G_FLOAT(OFS_RETURN) =  dat.f;
1185 }
1186
1187 //#331 float(float stnum) getstati (EXT_CSQC)
1188 static void VM_CL_getstati (prvm_prog_t *prog)
1189 {
1190         int i, index;
1191         int firstbit, bitcount;
1192
1193         VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_getstati);
1194
1195         index = (int)PRVM_G_FLOAT(OFS_PARM0);
1196         if (prog->argc > 1)
1197         {
1198                 firstbit = (int)PRVM_G_FLOAT(OFS_PARM1);
1199                 if (prog->argc > 2)
1200                         bitcount = (int)PRVM_G_FLOAT(OFS_PARM2);
1201                 else
1202                         bitcount = 1;
1203         }
1204         else
1205         {
1206                 firstbit = 0;
1207                 bitcount = 32;
1208         }
1209
1210         if(index < 0 || index >= MAX_CL_STATS)
1211         {
1212                 VM_Warning(prog, "VM_CL_getstati: index>=MAX_CL_STATS or index<0\n");
1213                 return;
1214         }
1215         i = cl.stats[index];
1216         if (bitcount != 32)     //32 causes the mask to overflow, so there's nothing to subtract from.
1217                 i = (((unsigned int)i)&(((1<<bitcount)-1)<<firstbit))>>firstbit;
1218         PRVM_G_FLOAT(OFS_RETURN) = i;
1219 }
1220
1221 //#332 string(float firststnum) getstats (EXT_CSQC)
1222 static void VM_CL_getstats (prvm_prog_t *prog)
1223 {
1224         int i;
1225         char t[17];
1226         VM_SAFEPARMCOUNT(1, VM_CL_getstats);
1227         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1228         if(i < 0 || i > MAX_CL_STATS-4)
1229         {
1230                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1231                 VM_Warning(prog, "VM_CL_getstats: index>MAX_CL_STATS-4 or index<0\n");
1232                 return;
1233         }
1234         strlcpy(t, (char*)&cl.stats[i], sizeof(t));
1235         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
1236 }
1237
1238 //#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
1239 static void VM_CL_setmodelindex (prvm_prog_t *prog)
1240 {
1241         int                             i;
1242         prvm_edict_t    *t;
1243         struct model_s  *model;
1244
1245         VM_SAFEPARMCOUNT(2, VM_CL_setmodelindex);
1246
1247         t = PRVM_G_EDICT(OFS_PARM0);
1248
1249         i = (int)PRVM_G_FLOAT(OFS_PARM1);
1250
1251         PRVM_clientedictstring(t, model) = 0;
1252         PRVM_clientedictfloat(t, modelindex) = 0;
1253
1254         if (!i)
1255                 return;
1256
1257         model = CL_GetModelByIndex(i);
1258         if (!model)
1259         {
1260                 VM_Warning(prog, "VM_CL_setmodelindex: null model\n");
1261                 return;
1262         }
1263         PRVM_clientedictstring(t, model) = PRVM_SetEngineString(prog, model->name);
1264         PRVM_clientedictfloat(t, modelindex) = i;
1265
1266         // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
1267         if (model)
1268         {
1269                 SetMinMaxSize (prog, t, model->normalmins, model->normalmaxs);
1270         }
1271         else
1272                 SetMinMaxSize (prog, t, vec3_origin, vec3_origin);
1273 }
1274
1275 //#334 string(float mdlindex) modelnameforindex (EXT_CSQC)
1276 static void VM_CL_modelnameforindex (prvm_prog_t *prog)
1277 {
1278         dp_model_t *model;
1279
1280         VM_SAFEPARMCOUNT(1, VM_CL_modelnameforindex);
1281
1282         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1283         model = CL_GetModelByIndex((int)PRVM_G_FLOAT(OFS_PARM0));
1284         PRVM_G_INT(OFS_RETURN) = model ? PRVM_SetEngineString(prog, model->name) : 0;
1285 }
1286
1287 //#335 float(string effectname) particleeffectnum (EXT_CSQC)
1288 static void VM_CL_particleeffectnum (prvm_prog_t *prog)
1289 {
1290         int                     i;
1291         VM_SAFEPARMCOUNT(1, VM_CL_particleeffectnum);
1292         i = CL_ParticleEffectIndexForName(PRVM_G_STRING(OFS_PARM0));
1293         if (i == 0)
1294                 i = -1;
1295         PRVM_G_FLOAT(OFS_RETURN) = i;
1296 }
1297
1298 // #336 void(entity ent, float effectnum, vector start, vector end[, float color]) trailparticles (EXT_CSQC)
1299 static void VM_CL_trailparticles (prvm_prog_t *prog)
1300 {
1301         int                             i;
1302         float                   *start, *end;
1303         prvm_edict_t    *t;
1304         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_trailparticles);
1305
1306         t = PRVM_G_EDICT(OFS_PARM0);
1307         i               = (int)PRVM_G_FLOAT(OFS_PARM1);
1308         start   = PRVM_G_VECTOR(OFS_PARM2);
1309         end             = PRVM_G_VECTOR(OFS_PARM3);
1310
1311         if (i < 0)
1312                 return;
1313         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);
1314 }
1315
1316 //#337 void(float effectnum, vector origin, vector dir, float count[, float color]) pointparticles (EXT_CSQC)
1317 static void VM_CL_pointparticles (prvm_prog_t *prog)
1318 {
1319         int                     i;
1320         float n;
1321         float           *f, *v;
1322         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_pointparticles);
1323         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1324         f = PRVM_G_VECTOR(OFS_PARM1);
1325         v = PRVM_G_VECTOR(OFS_PARM2);
1326         n = PRVM_G_FLOAT(OFS_PARM3);
1327         if (i < 0)
1328                 return;
1329         CL_ParticleEffect(i, n, f, f, v, v, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0);
1330 }
1331
1332 //#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)
1333 static void VM_CL_boxparticles (prvm_prog_t *prog)
1334 {
1335         int effectnum;
1336         // prvm_edict_t *own;
1337         float *origin_from, *origin_to, *dir_from, *dir_to;
1338         float count;
1339         int flags;
1340         float tintmins[4], tintmaxs[4];
1341         VM_SAFEPARMCOUNTRANGE(7, 8, VM_CL_boxparticles);
1342
1343         effectnum = (int)PRVM_G_FLOAT(OFS_PARM0);
1344         // own = PRVM_G_EDICT(OFS_PARM1); // TODO find use for this
1345         origin_from = PRVM_G_VECTOR(OFS_PARM2);
1346         origin_to = PRVM_G_VECTOR(OFS_PARM3);
1347         dir_from = PRVM_G_VECTOR(OFS_PARM4);
1348         dir_to = PRVM_G_VECTOR(OFS_PARM5);
1349         count = PRVM_G_FLOAT(OFS_PARM6);
1350         if(prog->argc >= 8)
1351                 flags = PRVM_G_FLOAT(OFS_PARM7);
1352         else
1353                 flags = 0;
1354         Vector4Set(tintmins, 1, 1, 1, 1);
1355         Vector4Set(tintmaxs, 1, 1, 1, 1);
1356         if(flags & 1) // read alpha
1357         {
1358                 tintmins[3] = PRVM_clientglobalfloat(particles_alphamin);
1359                 tintmaxs[3] = PRVM_clientglobalfloat(particles_alphamax);
1360         }
1361         if(flags & 2) // read color
1362         {
1363                 VectorCopy(PRVM_clientglobalvector(particles_colormin), tintmins);
1364                 VectorCopy(PRVM_clientglobalvector(particles_colormax), tintmaxs);
1365         }
1366         if (effectnum < 0)
1367                 return;
1368         CL_ParticleTrail(effectnum, count, origin_from, origin_to, dir_from, dir_to, NULL, 0, true, true, tintmins, tintmaxs);
1369 }
1370
1371 //#531 void(float pause) setpause
1372 static void VM_CL_setpause(prvm_prog_t *prog)
1373 {
1374         VM_SAFEPARMCOUNT(1, VM_CL_setpause);
1375         if ((int)PRVM_G_FLOAT(OFS_PARM0) != 0)
1376                 cl.csqc_paused = true;
1377         else
1378                 cl.csqc_paused = false;
1379 }
1380
1381 //#343 void(float usecursor) setcursormode (DP_CSQC)
1382 static void VM_CL_setcursormode (prvm_prog_t *prog)
1383 {
1384         VM_SAFEPARMCOUNT(1, VM_CL_setcursormode);
1385         cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0) != 0;
1386         cl_ignoremousemoves = 2;
1387 }
1388
1389 //#344 vector() getmousepos (DP_CSQC)
1390 static void VM_CL_getmousepos(prvm_prog_t *prog)
1391 {
1392         VM_SAFEPARMCOUNT(0,VM_CL_getmousepos);
1393
1394         if (key_consoleactive || key_dest != key_game)
1395                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), 0, 0, 0);
1396         else if (cl.csqc_wantsmousemove)
1397                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_windowmouse_x * vid_conwidth.integer / vid.width, in_windowmouse_y * vid_conheight.integer / vid.height, 0);
1398         else
1399                 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_mouse_x * vid_conwidth.integer / vid.width, in_mouse_y * vid_conheight.integer / vid.height, 0);
1400 }
1401
1402 //#345 float(float framenum) getinputstate (EXT_CSQC)
1403 static void VM_CL_getinputstate (prvm_prog_t *prog)
1404 {
1405         int i, frame;
1406         VM_SAFEPARMCOUNT(1, VM_CL_getinputstate);
1407         frame = (int)PRVM_G_FLOAT(OFS_PARM0);
1408         PRVM_G_FLOAT(OFS_RETURN) = false;
1409         for (i = 0;i < CL_MAX_USERCMDS;i++)
1410         {
1411                 if (cl.movecmd[i].sequence == frame)
1412                 {
1413                         VectorCopy(cl.movecmd[i].viewangles, PRVM_clientglobalvector(input_angles));
1414                         PRVM_clientglobalfloat(input_buttons) = cl.movecmd[i].buttons; // FIXME: this should not be directly exposed to csqc (translation layer needed?)
1415                         PRVM_clientglobalvector(input_movevalues)[0] = cl.movecmd[i].forwardmove;
1416                         PRVM_clientglobalvector(input_movevalues)[1] = cl.movecmd[i].sidemove;
1417                         PRVM_clientglobalvector(input_movevalues)[2] = cl.movecmd[i].upmove;
1418                         PRVM_clientglobalfloat(input_timelength) = cl.movecmd[i].frametime;
1419                         if(cl.movecmd[i].crouch)
1420                         {
1421                                 VectorCopy(cl.playercrouchmins, PRVM_clientglobalvector(pmove_mins));
1422                                 VectorCopy(cl.playercrouchmaxs, PRVM_clientglobalvector(pmove_maxs));
1423                         }
1424                         else
1425                         {
1426                                 VectorCopy(cl.playerstandmins, PRVM_clientglobalvector(pmove_mins));
1427                                 VectorCopy(cl.playerstandmaxs, PRVM_clientglobalvector(pmove_maxs));
1428                         }
1429                         PRVM_G_FLOAT(OFS_RETURN) = true;
1430                 }
1431         }
1432 }
1433
1434 //#346 void(float sens) setsensitivityscaler (EXT_CSQC)
1435 static void VM_CL_setsensitivityscale (prvm_prog_t *prog)
1436 {
1437         VM_SAFEPARMCOUNT(1, VM_CL_setsensitivityscale);
1438         cl.sensitivityscale = PRVM_G_FLOAT(OFS_PARM0);
1439 }
1440
1441 //#347 void() runstandardplayerphysics (EXT_CSQC)
1442 static void VM_CL_runplayerphysics (prvm_prog_t *prog)
1443 {
1444         cl_clientmovement_state_t s;
1445         prvm_edict_t *ent;
1446
1447         VM_SAFEPARMCOUNT(1, VM_CL_runplayerphysics);
1448
1449         ent = PRVM_G_EDICT(OFS_PARM0);
1450         VectorCopy(PRVM_clientedictvector(ent, origin), s.origin);
1451         VectorCopy(PRVM_clientedictvector(ent, velocity), s.velocity);
1452         VectorCopy(PRVM_clientglobalvector(pmove_mins), s.mins);
1453         VectorCopy(PRVM_clientglobalvector(pmove_maxs), s.maxs);
1454         s.crouched = 0; // FIXME we need the current crouched status
1455         s.waterjumptime = 0; // FIXME where do we get this from?
1456         VectorCopy(PRVM_clientglobalvector(input_angles), s.cmd.viewangles);
1457         s.cmd.forwardmove = PRVM_clientglobalvector(input_movevalues)[0];
1458         s.cmd.sidemove = PRVM_clientglobalvector(input_movevalues)[1];
1459         s.cmd.upmove = PRVM_clientglobalvector(input_movevalues)[2];
1460         s.cmd.buttons = PRVM_clientglobalfloat(input_buttons);
1461         s.cmd.frametime = PRVM_clientglobalfloat(input_timelength);
1462         s.cmd.canjump = 1; // FIXME we need the current canjump status
1463         s.cmd.jump = (s.cmd.buttons & 2) != 0;
1464         s.cmd.crouch = (s.cmd.buttons & 16) != 0;
1465
1466         CL_ClientMovement_PlayerMove(&s);
1467
1468         VectorCopy(s.origin, PRVM_clientedictvector(ent, origin));
1469         VectorCopy(s.velocity, PRVM_clientedictvector(ent, velocity));
1470 }
1471
1472 //#348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
1473 static void VM_CL_getplayerkey (prvm_prog_t *prog)
1474 {
1475         int                     i;
1476         char            t[128];
1477         const char      *c;
1478
1479         VM_SAFEPARMCOUNT(2, VM_CL_getplayerkey);
1480
1481         i = (int)PRVM_G_FLOAT(OFS_PARM0);
1482         c = PRVM_G_STRING(OFS_PARM1);
1483         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1484         Sbar_SortFrags();
1485
1486         if (i < 0)
1487                 i = Sbar_GetSortedPlayerIndex(-1-i);
1488         if(i < 0 || i >= cl.maxclients)
1489                 return;
1490
1491         t[0] = 0;
1492
1493         if(!strcasecmp(c, "name"))
1494                 strlcpy(t, cl.scores[i].name, sizeof(t));
1495         else
1496                 if(!strcasecmp(c, "frags"))
1497                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].frags);
1498         else
1499                 if(!strcasecmp(c, "ping"))
1500                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_ping);
1501         else
1502                 if(!strcasecmp(c, "pl"))
1503                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_packetloss);
1504         else
1505                 if(!strcasecmp(c, "movementloss"))
1506                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_movementloss);
1507         else
1508                 if(!strcasecmp(c, "entertime"))
1509                         dpsnprintf(t, sizeof(t), "%f", cl.scores[i].qw_entertime);
1510         else
1511                 if(!strcasecmp(c, "colors"))
1512                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors);
1513         else
1514                 if(!strcasecmp(c, "topcolor"))
1515                         dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors & 0xf0);
1516         else
1517                 if(!strcasecmp(c, "bottomcolor"))
1518                         dpsnprintf(t, sizeof(t), "%i", (cl.scores[i].colors &15)<<4);
1519         else
1520                 if(!strcasecmp(c, "viewentity"))
1521                         dpsnprintf(t, sizeof(t), "%i", i+1);
1522         else
1523                 if(gamemode == GAME_XONOTIC && !strcasecmp(c, "TEMPHACK_origin"))
1524                 {
1525                         // PLEASE REMOVE THIS once deltalisten() of EXT_CSQC_1
1526                         // is implemented, or Xonotic uses CSQC-networked
1527                         // players, whichever comes first
1528                         entity_t *e = cl.entities + (i+1);
1529                         if(e->state_current.active)
1530                         {
1531                                 vec3_t origin;
1532                                 Matrix4x4_OriginFromMatrix(&e->render.matrix, origin);
1533                                 dpsnprintf(t, sizeof(t), "%.9g %.9g %.9g", origin[0], origin[1], origin[2]);
1534                         }
1535                 }
1536         if(!t[0])
1537                 return;
1538         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
1539 }
1540
1541 //#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
1542 static void VM_CL_setlistener (prvm_prog_t *prog)
1543 {
1544         VM_SAFEPARMCOUNT(4, VM_CL_setlistener);
1545         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));
1546         cl.csqc_usecsqclistener = true; //use csqc listener at this frame
1547 }
1548
1549 //#352 void(string cmdname) registercommand (EXT_CSQC)
1550 static void VM_CL_registercmd (prvm_prog_t *prog)
1551 {
1552         char *t;
1553         VM_SAFEPARMCOUNT(1, VM_CL_registercmd);
1554         if(!Cmd_Exists(PRVM_G_STRING(OFS_PARM0)))
1555         {
1556                 size_t alloclen;
1557
1558                 alloclen = strlen(PRVM_G_STRING(OFS_PARM0)) + 1;
1559                 t = (char *)Z_Malloc(alloclen);
1560                 memcpy(t, PRVM_G_STRING(OFS_PARM0), alloclen);
1561                 Cmd_AddCommand(t, NULL, "console command created by QuakeC");
1562         }
1563         else
1564                 Cmd_AddCommand(PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
1565
1566 }
1567
1568 //#360 float() readbyte (EXT_CSQC)
1569 static void VM_CL_ReadByte (prvm_prog_t *prog)
1570 {
1571         VM_SAFEPARMCOUNT(0, VM_CL_ReadByte);
1572         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadByte(&cl_message);
1573 }
1574
1575 //#361 float() readchar (EXT_CSQC)
1576 static void VM_CL_ReadChar (prvm_prog_t *prog)
1577 {
1578         VM_SAFEPARMCOUNT(0, VM_CL_ReadChar);
1579         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadChar(&cl_message);
1580 }
1581
1582 //#362 float() readshort (EXT_CSQC)
1583 static void VM_CL_ReadShort (prvm_prog_t *prog)
1584 {
1585         VM_SAFEPARMCOUNT(0, VM_CL_ReadShort);
1586         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadShort(&cl_message);
1587 }
1588
1589 //#363 float() readlong (EXT_CSQC)
1590 static void VM_CL_ReadLong (prvm_prog_t *prog)
1591 {
1592         VM_SAFEPARMCOUNT(0, VM_CL_ReadLong);
1593         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadLong(&cl_message);
1594 }
1595
1596 //#364 float() readcoord (EXT_CSQC)
1597 static void VM_CL_ReadCoord (prvm_prog_t *prog)
1598 {
1599         VM_SAFEPARMCOUNT(0, VM_CL_ReadCoord);
1600         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadCoord(&cl_message, cls.protocol);
1601 }
1602
1603 //#365 float() readangle (EXT_CSQC)
1604 static void VM_CL_ReadAngle (prvm_prog_t *prog)
1605 {
1606         VM_SAFEPARMCOUNT(0, VM_CL_ReadAngle);
1607         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadAngle(&cl_message, cls.protocol);
1608 }
1609
1610 //#366 string() readstring (EXT_CSQC)
1611 static void VM_CL_ReadString (prvm_prog_t *prog)
1612 {
1613         VM_SAFEPARMCOUNT(0, VM_CL_ReadString);
1614         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
1615 }
1616
1617 //#367 float() readfloat (EXT_CSQC)
1618 static void VM_CL_ReadFloat (prvm_prog_t *prog)
1619 {
1620         VM_SAFEPARMCOUNT(0, VM_CL_ReadFloat);
1621         PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat(&cl_message);
1622 }
1623
1624 //#501 string() readpicture (DP_CSQC_READWRITEPICTURE)
1625 extern cvar_t cl_readpicture_force;
1626 static void VM_CL_ReadPicture (prvm_prog_t *prog)
1627 {
1628         const char *name;
1629         unsigned char *data;
1630         unsigned char *buf;
1631         int size;
1632         int i;
1633         cachepic_t *pic;
1634
1635         VM_SAFEPARMCOUNT(0, VM_CL_ReadPicture);
1636
1637         name = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
1638         size = MSG_ReadShort(&cl_message);
1639
1640         // check if a texture of that name exists
1641         // if yes, it is used and the data is discarded
1642         // if not, the (low quality) data is used to build a new texture, whose name will get returned
1643
1644         pic = Draw_CachePic_Flags (name, CACHEPICFLAG_NOTPERSISTENT);
1645
1646         if(size)
1647         {
1648                 if(pic->tex == r_texture_notexture)
1649                         pic->tex = NULL; // don't overwrite the notexture by Draw_NewPic
1650                 if(pic->tex && !cl_readpicture_force.integer)
1651                 {
1652                         // texture found and loaded
1653                         // skip over the jpeg as we don't need it
1654                         for(i = 0; i < size; ++i)
1655                                 (void) MSG_ReadByte(&cl_message);
1656                 }
1657                 else
1658                 {
1659                         // texture not found
1660                         // use the attached jpeg as texture
1661                         buf = (unsigned char *) Mem_Alloc(tempmempool, size);
1662                         MSG_ReadBytes(&cl_message, size, buf);
1663                         data = JPEG_LoadImage_BGRA(buf, size, NULL);
1664                         Mem_Free(buf);
1665                         Draw_NewPic(name, image_width, image_height, false, data);
1666                         Mem_Free(data);
1667                 }
1668         }
1669
1670         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, name);
1671 }
1672
1673 //////////////////////////////////////////////////////////
1674
1675 static void VM_CL_makestatic (prvm_prog_t *prog)
1676 {
1677         prvm_edict_t *ent;
1678
1679         VM_SAFEPARMCOUNT(1, VM_CL_makestatic);
1680
1681         ent = PRVM_G_EDICT(OFS_PARM0);
1682         if (ent == prog->edicts)
1683         {
1684                 VM_Warning(prog, "makestatic: can not modify world entity\n");
1685                 return;
1686         }
1687         if (ent->priv.server->free)
1688         {
1689                 VM_Warning(prog, "makestatic: can not modify free entity\n");
1690                 return;
1691         }
1692
1693         if (cl.num_static_entities < cl.max_static_entities)
1694         {
1695                 int renderflags;
1696                 entity_t *staticent = &cl.static_entities[cl.num_static_entities++];
1697
1698                 // copy it to the current state
1699                 memset(staticent, 0, sizeof(*staticent));
1700                 staticent->render.model = CL_GetModelByIndex((int)PRVM_clientedictfloat(ent, modelindex));
1701                 staticent->render.framegroupblend[0].frame = (int)PRVM_clientedictfloat(ent, frame);
1702                 staticent->render.framegroupblend[0].lerp = 1;
1703                 // make torchs play out of sync
1704                 staticent->render.framegroupblend[0].start = lhrandom(-10, -1);
1705                 staticent->render.skinnum = (int)PRVM_clientedictfloat(ent, skin);
1706                 staticent->render.effects = (int)PRVM_clientedictfloat(ent, effects);
1707                 staticent->render.alpha = PRVM_clientedictfloat(ent, alpha);
1708                 staticent->render.scale = PRVM_clientedictfloat(ent, scale);
1709                 VectorCopy(PRVM_clientedictvector(ent, colormod), staticent->render.colormod);
1710                 VectorCopy(PRVM_clientedictvector(ent, glowmod), staticent->render.glowmod);
1711
1712                 // sanitize values
1713                 if (!staticent->render.alpha)
1714                         staticent->render.alpha = 1.0f;
1715                 if (!staticent->render.scale)
1716                         staticent->render.scale = 1.0f;
1717                 if (!VectorLength2(staticent->render.colormod))
1718                         VectorSet(staticent->render.colormod, 1, 1, 1);
1719                 if (!VectorLength2(staticent->render.glowmod))
1720                         VectorSet(staticent->render.glowmod, 1, 1, 1);
1721
1722                 renderflags = (int)PRVM_clientedictfloat(ent, renderflags);
1723                 if (renderflags & RF_USEAXIS)
1724                 {
1725                         vec3_t left;
1726                         VectorNegate(PRVM_clientglobalvector(v_right), left);
1727                         Matrix4x4_FromVectors(&staticent->render.matrix, PRVM_clientglobalvector(v_forward), left, PRVM_clientglobalvector(v_up), PRVM_clientedictvector(ent, origin));
1728                         Matrix4x4_Scale(&staticent->render.matrix, staticent->render.scale, 1);
1729                 }
1730                 else
1731                         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);
1732
1733                 // either fullbright or lit
1734                 if(!r_fullbright.integer)
1735                 {
1736                         if (!(staticent->render.effects & EF_FULLBRIGHT))
1737                                 staticent->render.flags |= RENDER_LIGHT;
1738                         else if(r_equalize_entities_fullbright.integer)
1739                                 staticent->render.flags |= RENDER_LIGHT | RENDER_EQUALIZE;
1740                 }
1741                 // turn off shadows from transparent objects
1742                 if (!(staticent->render.effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST)) && (staticent->render.alpha >= 1))
1743                         staticent->render.flags |= RENDER_SHADOW;
1744                 if (staticent->render.effects & EF_NODEPTHTEST)
1745                         staticent->render.flags |= RENDER_NODEPTHTEST;
1746                 if (staticent->render.effects & EF_ADDITIVE)
1747                         staticent->render.flags |= RENDER_ADDITIVE;
1748                 if (staticent->render.effects & EF_DOUBLESIDED)
1749                         staticent->render.flags |= RENDER_DOUBLESIDED;
1750
1751                 staticent->render.allowdecals = true;
1752                 CL_UpdateRenderEntity(&staticent->render);
1753         }
1754         else
1755                 Con_Printf("Too many static entities");
1756
1757 // throw the entity away now
1758         PRVM_ED_Free(prog, ent);
1759 }
1760
1761 //=================================================================//
1762
1763 /*
1764 =================
1765 VM_CL_copyentity
1766
1767 copies data from one entity to another
1768
1769 copyentity(src, dst)
1770 =================
1771 */
1772 static void VM_CL_copyentity (prvm_prog_t *prog)
1773 {
1774         prvm_edict_t *in, *out;
1775         VM_SAFEPARMCOUNT(2, VM_CL_copyentity);
1776         in = PRVM_G_EDICT(OFS_PARM0);
1777         if (in == prog->edicts)
1778         {
1779                 VM_Warning(prog, "copyentity: can not read world entity\n");
1780                 return;
1781         }
1782         if (in->priv.server->free)
1783         {
1784                 VM_Warning(prog, "copyentity: can not read free entity\n");
1785                 return;
1786         }
1787         out = PRVM_G_EDICT(OFS_PARM1);
1788         if (out == prog->edicts)
1789         {
1790                 VM_Warning(prog, "copyentity: can not modify world entity\n");
1791                 return;
1792         }
1793         if (out->priv.server->free)
1794         {
1795                 VM_Warning(prog, "copyentity: can not modify free entity\n");
1796                 return;
1797         }
1798         memcpy(out->fields.vp, in->fields.vp, prog->entityfields * 4);
1799         CL_LinkEdict(out);
1800 }
1801
1802 //=================================================================//
1803
1804 // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
1805 static void VM_CL_effect (prvm_prog_t *prog)
1806 {
1807         VM_SAFEPARMCOUNT(5, VM_CL_effect);
1808         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));
1809 }
1810
1811 // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
1812 static void VM_CL_te_blood (prvm_prog_t *prog)
1813 {
1814         float   *pos;
1815         vec3_t  pos2;
1816         VM_SAFEPARMCOUNT(3, VM_CL_te_blood);
1817         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
1818                 return;
1819         pos = PRVM_G_VECTOR(OFS_PARM0);
1820         CL_FindNonSolidLocation(pos, pos2, 4);
1821         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1822 }
1823
1824 // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
1825 static void VM_CL_te_bloodshower (prvm_prog_t *prog)
1826 {
1827         vec_t speed;
1828         vec3_t vel1, vel2;
1829         VM_SAFEPARMCOUNT(4, VM_CL_te_bloodshower);
1830         if (PRVM_G_FLOAT(OFS_PARM3) < 1)
1831                 return;
1832         speed = PRVM_G_FLOAT(OFS_PARM2);
1833         vel1[0] = -speed;
1834         vel1[1] = -speed;
1835         vel1[2] = -speed;
1836         vel2[0] = speed;
1837         vel2[1] = speed;
1838         vel2[2] = speed;
1839         CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM3), PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), vel1, vel2, NULL, 0);
1840 }
1841
1842 // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
1843 static void VM_CL_te_explosionrgb (prvm_prog_t *prog)
1844 {
1845         float           *pos;
1846         vec3_t          pos2;
1847         matrix4x4_t     tempmatrix;
1848         VM_SAFEPARMCOUNT(2, VM_CL_te_explosionrgb);
1849         pos = PRVM_G_VECTOR(OFS_PARM0);
1850         CL_FindNonSolidLocation(pos, pos2, 10);
1851         CL_ParticleExplosion(pos2);
1852         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1853         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);
1854 }
1855
1856 // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
1857 static void VM_CL_te_particlecube (prvm_prog_t *prog)
1858 {
1859         VM_SAFEPARMCOUNT(7, VM_CL_te_particlecube);
1860         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));
1861 }
1862
1863 // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
1864 static void VM_CL_te_particlerain (prvm_prog_t *prog)
1865 {
1866         VM_SAFEPARMCOUNT(5, VM_CL_te_particlerain);
1867         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);
1868 }
1869
1870 // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
1871 static void VM_CL_te_particlesnow (prvm_prog_t *prog)
1872 {
1873         VM_SAFEPARMCOUNT(5, VM_CL_te_particlesnow);
1874         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);
1875 }
1876
1877 // #411 void(vector org, vector vel, float howmany) te_spark
1878 static void VM_CL_te_spark (prvm_prog_t *prog)
1879 {
1880         float           *pos;
1881         vec3_t          pos2;
1882         VM_SAFEPARMCOUNT(3, VM_CL_te_spark);
1883
1884         pos = PRVM_G_VECTOR(OFS_PARM0);
1885         CL_FindNonSolidLocation(pos, pos2, 4);
1886         CL_ParticleEffect(EFFECT_TE_SPARK, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1887 }
1888
1889 extern cvar_t cl_sound_ric_gunshot;
1890 // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
1891 static void VM_CL_te_gunshotquad (prvm_prog_t *prog)
1892 {
1893         float           *pos;
1894         vec3_t          pos2;
1895         int                     rnd;
1896         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshotquad);
1897
1898         pos = PRVM_G_VECTOR(OFS_PARM0);
1899         CL_FindNonSolidLocation(pos, pos2, 4);
1900         CL_ParticleEffect(EFFECT_TE_GUNSHOTQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1901         if(cl_sound_ric_gunshot.integer >= 2)
1902         {
1903                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1904                 else
1905                 {
1906                         rnd = rand() & 3;
1907                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1908                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1909                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1910                 }
1911         }
1912 }
1913
1914 // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
1915 static void VM_CL_te_spikequad (prvm_prog_t *prog)
1916 {
1917         float           *pos;
1918         vec3_t          pos2;
1919         int                     rnd;
1920         VM_SAFEPARMCOUNT(1, VM_CL_te_spikequad);
1921
1922         pos = PRVM_G_VECTOR(OFS_PARM0);
1923         CL_FindNonSolidLocation(pos, pos2, 4);
1924         CL_ParticleEffect(EFFECT_TE_SPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1925         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1926         else
1927         {
1928                 rnd = rand() & 3;
1929                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1930                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1931                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1932         }
1933 }
1934
1935 // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1)
1936 static void VM_CL_te_superspikequad (prvm_prog_t *prog)
1937 {
1938         float           *pos;
1939         vec3_t          pos2;
1940         int                     rnd;
1941         VM_SAFEPARMCOUNT(1, VM_CL_te_superspikequad);
1942
1943         pos = PRVM_G_VECTOR(OFS_PARM0);
1944         CL_FindNonSolidLocation(pos, pos2, 4);
1945         CL_ParticleEffect(EFFECT_TE_SUPERSPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1946         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
1947         else
1948         {
1949                 rnd = rand() & 3;
1950                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1951                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1952                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1953         }
1954 }
1955
1956 // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1)
1957 static void VM_CL_te_explosionquad (prvm_prog_t *prog)
1958 {
1959         float           *pos;
1960         vec3_t          pos2;
1961         VM_SAFEPARMCOUNT(1, VM_CL_te_explosionquad);
1962
1963         pos = PRVM_G_VECTOR(OFS_PARM0);
1964         CL_FindNonSolidLocation(pos, pos2, 10);
1965         CL_ParticleEffect(EFFECT_TE_EXPLOSIONQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1966         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1967 }
1968
1969 // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
1970 static void VM_CL_te_smallflash (prvm_prog_t *prog)
1971 {
1972         float           *pos;
1973         vec3_t          pos2;
1974         VM_SAFEPARMCOUNT(1, VM_CL_te_smallflash);
1975
1976         pos = PRVM_G_VECTOR(OFS_PARM0);
1977         CL_FindNonSolidLocation(pos, pos2, 10);
1978         CL_ParticleEffect(EFFECT_TE_SMALLFLASH, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1979 }
1980
1981 // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
1982 static void VM_CL_te_customflash (prvm_prog_t *prog)
1983 {
1984         float           *pos;
1985         vec3_t          pos2;
1986         matrix4x4_t     tempmatrix;
1987         VM_SAFEPARMCOUNT(4, VM_CL_te_customflash);
1988
1989         pos = PRVM_G_VECTOR(OFS_PARM0);
1990         CL_FindNonSolidLocation(pos, pos2, 4);
1991         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1992         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);
1993 }
1994
1995 // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
1996 static void VM_CL_te_gunshot (prvm_prog_t *prog)
1997 {
1998         float           *pos;
1999         vec3_t          pos2;
2000         int                     rnd;
2001         VM_SAFEPARMCOUNT(1, VM_CL_te_gunshot);
2002
2003         pos = PRVM_G_VECTOR(OFS_PARM0);
2004         CL_FindNonSolidLocation(pos, pos2, 4);
2005         CL_ParticleEffect(EFFECT_TE_GUNSHOT, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2006         if(cl_sound_ric_gunshot.integer == 1 || cl_sound_ric_gunshot.integer == 3)
2007         {
2008                 if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2009                 else
2010                 {
2011                         rnd = rand() & 3;
2012                         if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2013                         else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2014                         else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2015                 }
2016         }
2017 }
2018
2019 // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
2020 static void VM_CL_te_spike (prvm_prog_t *prog)
2021 {
2022         float           *pos;
2023         vec3_t          pos2;
2024         int                     rnd;
2025         VM_SAFEPARMCOUNT(1, VM_CL_te_spike);
2026
2027         pos = PRVM_G_VECTOR(OFS_PARM0);
2028         CL_FindNonSolidLocation(pos, pos2, 4);
2029         CL_ParticleEffect(EFFECT_TE_SPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2030         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2031         else
2032         {
2033                 rnd = rand() & 3;
2034                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2035                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2036                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2037         }
2038 }
2039
2040 // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
2041 static void VM_CL_te_superspike (prvm_prog_t *prog)
2042 {
2043         float           *pos;
2044         vec3_t          pos2;
2045         int                     rnd;
2046         VM_SAFEPARMCOUNT(1, VM_CL_te_superspike);
2047
2048         pos = PRVM_G_VECTOR(OFS_PARM0);
2049         CL_FindNonSolidLocation(pos, pos2, 4);
2050         CL_ParticleEffect(EFFECT_TE_SUPERSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2051         if (rand() % 5)                 S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2052         else
2053         {
2054                 rnd = rand() & 3;
2055                 if (rnd == 1)           S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2056                 else if (rnd == 2)      S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2057                 else                            S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2058         }
2059 }
2060
2061 // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
2062 static void VM_CL_te_explosion (prvm_prog_t *prog)
2063 {
2064         float           *pos;
2065         vec3_t          pos2;
2066         VM_SAFEPARMCOUNT(1, VM_CL_te_explosion);
2067
2068         pos = PRVM_G_VECTOR(OFS_PARM0);
2069         CL_FindNonSolidLocation(pos, pos2, 10);
2070         CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2071         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2072 }
2073
2074 // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
2075 static void VM_CL_te_tarexplosion (prvm_prog_t *prog)
2076 {
2077         float           *pos;
2078         vec3_t          pos2;
2079         VM_SAFEPARMCOUNT(1, VM_CL_te_tarexplosion);
2080
2081         pos = PRVM_G_VECTOR(OFS_PARM0);
2082         CL_FindNonSolidLocation(pos, pos2, 10);
2083         CL_ParticleEffect(EFFECT_TE_TAREXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2084         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2085 }
2086
2087 // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
2088 static void VM_CL_te_wizspike (prvm_prog_t *prog)
2089 {
2090         float           *pos;
2091         vec3_t          pos2;
2092         VM_SAFEPARMCOUNT(1, VM_CL_te_wizspike);
2093
2094         pos = PRVM_G_VECTOR(OFS_PARM0);
2095         CL_FindNonSolidLocation(pos, pos2, 4);
2096         CL_ParticleEffect(EFFECT_TE_WIZSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2097         S_StartSound(-1, 0, cl.sfx_wizhit, pos2, 1, 1);
2098 }
2099
2100 // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
2101 static void VM_CL_te_knightspike (prvm_prog_t *prog)
2102 {
2103         float           *pos;
2104         vec3_t          pos2;
2105         VM_SAFEPARMCOUNT(1, VM_CL_te_knightspike);
2106
2107         pos = PRVM_G_VECTOR(OFS_PARM0);
2108         CL_FindNonSolidLocation(pos, pos2, 4);
2109         CL_ParticleEffect(EFFECT_TE_KNIGHTSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2110         S_StartSound(-1, 0, cl.sfx_knighthit, pos2, 1, 1);
2111 }
2112
2113 // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
2114 static void VM_CL_te_lavasplash (prvm_prog_t *prog)
2115 {
2116         VM_SAFEPARMCOUNT(1, VM_CL_te_lavasplash);
2117         CL_ParticleEffect(EFFECT_TE_LAVASPLASH, 1, PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM0), vec3_origin, vec3_origin, NULL, 0);
2118 }
2119
2120 // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
2121 static void VM_CL_te_teleport (prvm_prog_t *prog)
2122 {
2123         VM_SAFEPARMCOUNT(1, VM_CL_te_teleport);
2124         CL_ParticleEffect(EFFECT_TE_TELEPORT, 1, PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM0), vec3_origin, vec3_origin, NULL, 0);
2125 }
2126
2127 // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
2128 static void VM_CL_te_explosion2 (prvm_prog_t *prog)
2129 {
2130         float           *pos;
2131         vec3_t          pos2, color;
2132         matrix4x4_t     tempmatrix;
2133         int                     colorStart, colorLength;
2134         unsigned char           *tempcolor;
2135         VM_SAFEPARMCOUNT(3, VM_CL_te_explosion2);
2136
2137         pos = PRVM_G_VECTOR(OFS_PARM0);
2138         colorStart = (int)PRVM_G_FLOAT(OFS_PARM1);
2139         colorLength = (int)PRVM_G_FLOAT(OFS_PARM2);
2140         CL_FindNonSolidLocation(pos, pos2, 10);
2141         CL_ParticleExplosion2(pos2, colorStart, colorLength);
2142         tempcolor = palette_rgb[(rand()%colorLength) + colorStart];
2143         color[0] = tempcolor[0] * (2.0f / 255.0f);
2144         color[1] = tempcolor[1] * (2.0f / 255.0f);
2145         color[2] = tempcolor[2] * (2.0f / 255.0f);
2146         Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
2147         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);
2148         S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2149 }
2150
2151
2152 // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
2153 static void VM_CL_te_lightning1 (prvm_prog_t *prog)
2154 {
2155         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning1);
2156         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt, true);
2157 }
2158
2159 // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
2160 static void VM_CL_te_lightning2 (prvm_prog_t *prog)
2161 {
2162         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning2);
2163         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt2, true);
2164 }
2165
2166 // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
2167 static void VM_CL_te_lightning3 (prvm_prog_t *prog)
2168 {
2169         VM_SAFEPARMCOUNT(3, VM_CL_te_lightning3);
2170         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt3, false);
2171 }
2172
2173 // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
2174 static void VM_CL_te_beam (prvm_prog_t *prog)
2175 {
2176         VM_SAFEPARMCOUNT(3, VM_CL_te_beam);
2177         CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_beam, false);
2178 }
2179
2180 // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
2181 static void VM_CL_te_plasmaburn (prvm_prog_t *prog)
2182 {
2183         float           *pos;
2184         vec3_t          pos2;
2185         VM_SAFEPARMCOUNT(1, VM_CL_te_plasmaburn);
2186
2187         pos = PRVM_G_VECTOR(OFS_PARM0);
2188         CL_FindNonSolidLocation(pos, pos2, 4);
2189         CL_ParticleEffect(EFFECT_TE_PLASMABURN, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2190 }
2191
2192 // #457 void(vector org, vector velocity, float howmany) te_flamejet (DP_TE_FLAMEJET)
2193 static void VM_CL_te_flamejet (prvm_prog_t *prog)
2194 {
2195         float *pos;
2196         vec3_t pos2;
2197         VM_SAFEPARMCOUNT(3, VM_CL_te_flamejet);
2198         if (PRVM_G_FLOAT(OFS_PARM2) < 1)
2199                 return;
2200         pos = PRVM_G_VECTOR(OFS_PARM0);
2201         CL_FindNonSolidLocation(pos, pos2, 4);
2202         CL_ParticleEffect(EFFECT_TE_FLAMEJET, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
2203 }
2204
2205
2206 // #443 void(entity e, entity tagentity, string tagname) setattachment
2207 static void VM_CL_setattachment (prvm_prog_t *prog)
2208 {
2209         prvm_edict_t *e;
2210         prvm_edict_t *tagentity;
2211         const char *tagname;
2212         int modelindex;
2213         int tagindex;
2214         dp_model_t *model;
2215         VM_SAFEPARMCOUNT(3, VM_CL_setattachment);
2216
2217         e = PRVM_G_EDICT(OFS_PARM0);
2218         tagentity = PRVM_G_EDICT(OFS_PARM1);
2219         tagname = PRVM_G_STRING(OFS_PARM2);
2220
2221         if (e == prog->edicts)
2222         {
2223                 VM_Warning(prog, "setattachment: can not modify world entity\n");
2224                 return;
2225         }
2226         if (e->priv.server->free)
2227         {
2228                 VM_Warning(prog, "setattachment: can not modify free entity\n");
2229                 return;
2230         }
2231
2232         if (tagentity == NULL)
2233                 tagentity = prog->edicts;
2234
2235         tagindex = 0;
2236         if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0])
2237         {
2238                 modelindex = (int)PRVM_clientedictfloat(tagentity, modelindex);
2239                 model = CL_GetModelByIndex(modelindex);
2240                 if (model)
2241                 {
2242                         tagindex = Mod_Alias_GetTagIndexForName(model, (int)PRVM_clientedictfloat(tagentity, skin), tagname);
2243                         if (tagindex == 0)
2244                                 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);
2245                 }
2246                 else
2247                         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));
2248         }
2249
2250         PRVM_clientedictedict(e, tag_entity) = PRVM_EDICT_TO_PROG(tagentity);
2251         PRVM_clientedictfloat(e, tag_index) = tagindex;
2252 }
2253
2254 /////////////////////////////////////////
2255 // DP_MD3_TAGINFO extension coded by VorteX
2256
2257 static int CL_GetTagIndex (prvm_prog_t *prog, prvm_edict_t *e, const char *tagname)
2258 {
2259         dp_model_t *model = CL_GetModelFromEdict(e);
2260         if (model)
2261                 return Mod_Alias_GetTagIndexForName(model, (int)PRVM_clientedictfloat(e, skin), tagname);
2262         else
2263                 return -1;
2264 }
2265
2266 static int CL_GetExtendedTagInfo (prvm_prog_t *prog, prvm_edict_t *e, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
2267 {
2268         int r;
2269         dp_model_t *model;
2270
2271         *tagname = NULL;
2272         *parentindex = 0;
2273         Matrix4x4_CreateIdentity(tag_localmatrix);
2274
2275         if (tagindex >= 0
2276          && (model = CL_GetModelFromEdict(e))
2277          && model->animscenes)
2278         {
2279                 r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)PRVM_clientedictfloat(e, skin), e->priv.server->frameblend, &e->priv.server->skeleton, tagindex - 1, parentindex, tagname, tag_localmatrix);
2280
2281                 if(!r) // success?
2282                         *parentindex += 1;
2283
2284                 return r;
2285         }
2286
2287         return 1;
2288 }
2289
2290 int CL_GetPitchSign(prvm_prog_t *prog, prvm_edict_t *ent)
2291 {
2292         dp_model_t *model;
2293         if ((model = CL_GetModelFromEdict(ent)) && model->type == mod_alias)
2294                 return -1;
2295         return 1;
2296 }
2297
2298 void CL_GetEntityMatrix (prvm_prog_t *prog, prvm_edict_t *ent, matrix4x4_t *out, qboolean viewmatrix)
2299 {
2300         float scale;
2301         float pitchsign = 1;
2302
2303         scale = PRVM_clientedictfloat(ent, scale);
2304         if (!scale)
2305                 scale = 1.0f;
2306
2307         if(viewmatrix)
2308                 *out = r_refdef.view.matrix;
2309         else if ((int)PRVM_clientedictfloat(ent, renderflags) & RF_USEAXIS)
2310         {
2311                 vec3_t forward;
2312                 vec3_t left;
2313                 vec3_t up;
2314                 vec3_t origin;
2315                 VectorScale(PRVM_clientglobalvector(v_forward), scale, forward);
2316                 VectorScale(PRVM_clientglobalvector(v_right), -scale, left);
2317                 VectorScale(PRVM_clientglobalvector(v_up), scale, up);
2318                 VectorCopy(PRVM_clientedictvector(ent, origin), origin);
2319                 Matrix4x4_FromVectors(out, forward, left, up, origin);
2320         }
2321         else
2322         {
2323                 pitchsign = CL_GetPitchSign(prog, ent);
2324                 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);
2325         }
2326 }
2327
2328 static int CL_GetEntityLocalTagMatrix(prvm_prog_t *prog, prvm_edict_t *ent, int tagindex, matrix4x4_t *out)
2329 {
2330         dp_model_t *model;
2331         if (tagindex >= 0
2332          && (model = CL_GetModelFromEdict(ent))
2333          && model->animscenes)
2334         {
2335                 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
2336                 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
2337                 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
2338                 return Mod_Alias_GetTagMatrix(model, ent->priv.server->frameblend, &ent->priv.server->skeleton, tagindex, out);
2339         }
2340         *out = identitymatrix;
2341         return 0;
2342 }
2343
2344 // Warnings/errors code:
2345 // 0 - normal (everything all-right)
2346 // 1 - world entity
2347 // 2 - free entity
2348 // 3 - null or non-precached model
2349 // 4 - no tags with requested index
2350 // 5 - runaway loop at attachment chain
2351 extern cvar_t cl_bob;
2352 extern cvar_t cl_bobcycle;
2353 extern cvar_t cl_bobup;
2354 int CL_GetTagMatrix (prvm_prog_t *prog, matrix4x4_t *out, prvm_edict_t *ent, int tagindex)
2355 {
2356         int ret;
2357         int attachloop;
2358         matrix4x4_t entitymatrix, tagmatrix, attachmatrix;
2359         dp_model_t *model;
2360
2361         *out = identitymatrix; // warnings and errors return identical matrix
2362
2363         if (ent == prog->edicts)
2364                 return 1;
2365         if (ent->priv.server->free)
2366                 return 2;
2367
2368         model = CL_GetModelFromEdict(ent);
2369         if(!model)
2370                 return 3;
2371
2372         tagmatrix = identitymatrix;
2373         attachloop = 0;
2374         for(;;)
2375         {
2376                 if(attachloop >= 256)
2377                         return 5;
2378                 // apply transformation by child's tagindex on parent entity and then
2379                 // by parent entity itself
2380                 ret = CL_GetEntityLocalTagMatrix(prog, ent, tagindex - 1, &attachmatrix);
2381                 if(ret && attachloop == 0)
2382                         return ret;
2383                 CL_GetEntityMatrix(prog, ent, &entitymatrix, false);
2384                 Matrix4x4_Concat(&tagmatrix, &attachmatrix, out);
2385                 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2386                 // next iteration we process the parent entity
2387                 if (PRVM_clientedictedict(ent, tag_entity))
2388                 {
2389                         tagindex = (int)PRVM_clientedictfloat(ent, tag_index);
2390                         ent = PRVM_EDICT_NUM(PRVM_clientedictedict(ent, tag_entity));
2391                 }
2392                 else
2393                         break;
2394                 attachloop++;
2395         }
2396
2397         // RENDER_VIEWMODEL magic
2398         if ((int)PRVM_clientedictfloat(ent, renderflags) & RF_VIEWMODEL)
2399         {
2400                 Matrix4x4_Copy(&tagmatrix, out);
2401
2402                 CL_GetEntityMatrix(prog, prog->edicts, &entitymatrix, true);
2403                 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2404
2405                 /*
2406                 // Cl_bob, ported from rendering code
2407                 if (PRVM_clientedictfloat(ent, health) > 0 && cl_bob.value && cl_bobcycle.value)
2408                 {
2409                         double bob, cycle;
2410                         // LordHavoc: this code is *weird*, but not replacable (I think it
2411                         // should be done in QC on the server, but oh well, quake is quake)
2412                         // LordHavoc: figured out bobup: the time at which the sin is at 180
2413                         // degrees (which allows lengthening or squishing the peak or valley)
2414                         cycle = cl.time/cl_bobcycle.value;
2415                         cycle -= (int)cycle;
2416                         if (cycle < cl_bobup.value)
2417                                 cycle = sin(M_PI * cycle / cl_bobup.value);
2418                         else
2419                                 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
2420                         // bob is proportional to velocity in the xy plane
2421                         // (don't count Z, or jumping messes it up)
2422                         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;
2423                         bob = bob*0.3 + bob*0.7*cycle;
2424                         Matrix4x4_AdjustOrigin(out, 0, 0, bound(-7, bob, 4));
2425                 }
2426                 */
2427         }
2428         return 0;
2429 }
2430
2431 // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
2432 static void VM_CL_gettagindex (prvm_prog_t *prog)
2433 {
2434         prvm_edict_t *ent;
2435         const char *tag_name;
2436         int tag_index;
2437
2438         VM_SAFEPARMCOUNT(2, VM_CL_gettagindex);
2439
2440         ent = PRVM_G_EDICT(OFS_PARM0);
2441         tag_name = PRVM_G_STRING(OFS_PARM1);
2442         if (ent == prog->edicts)
2443         {
2444                 VM_Warning(prog, "VM_CL_gettagindex(entity #%i): can't affect world entity\n", PRVM_NUM_FOR_EDICT(ent));
2445                 return;
2446         }
2447         if (ent->priv.server->free)
2448         {
2449                 VM_Warning(prog, "VM_CL_gettagindex(entity #%i): can't affect free entity\n", PRVM_NUM_FOR_EDICT(ent));
2450                 return;
2451         }
2452
2453         tag_index = 0;
2454         if (!CL_GetModelFromEdict(ent))
2455                 Con_DPrintf("VM_CL_gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
2456         else
2457         {
2458                 tag_index = CL_GetTagIndex(prog, ent, tag_name);
2459                 if (tag_index == 0)
2460                         Con_DPrintf("VM_CL_gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
2461         }
2462         PRVM_G_FLOAT(OFS_RETURN) = tag_index;
2463 }
2464
2465 // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO)
2466 static void VM_CL_gettaginfo (prvm_prog_t *prog)
2467 {
2468         prvm_edict_t *e;
2469         int tagindex;
2470         matrix4x4_t tag_matrix;
2471         matrix4x4_t tag_localmatrix;
2472         int parentindex;
2473         const char *tagname;
2474         int returncode;
2475         vec3_t fo, le, up, trans;
2476         const dp_model_t *model;
2477
2478         VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo);
2479
2480         e = PRVM_G_EDICT(OFS_PARM0);
2481         tagindex = (int)PRVM_G_FLOAT(OFS_PARM1);
2482         returncode = CL_GetTagMatrix(prog, &tag_matrix, e, tagindex);
2483         Matrix4x4_ToVectors(&tag_matrix, PRVM_clientglobalvector(v_forward), le, PRVM_clientglobalvector(v_up), PRVM_G_VECTOR(OFS_RETURN));
2484         VectorScale(le, -1, PRVM_clientglobalvector(v_right));
2485         model = CL_GetModelFromEdict(e);
2486         VM_GenerateFrameGroupBlend(prog, e->priv.server->framegroupblend, e);
2487         VM_FrameBlendFromFrameGroupBlend(e->priv.server->frameblend, e->priv.server->framegroupblend, model);
2488         VM_UpdateEdictSkeleton(prog, e, model, e->priv.server->frameblend);
2489         CL_GetExtendedTagInfo(prog, e, tagindex, &parentindex, &tagname, &tag_localmatrix);
2490         Matrix4x4_ToVectors(&tag_localmatrix, fo, le, up, trans);
2491
2492         PRVM_clientglobalfloat(gettaginfo_parent) = parentindex;
2493         PRVM_clientglobalstring(gettaginfo_name) = tagname ? PRVM_SetTempString(prog, tagname) : 0;
2494         VectorCopy(trans, PRVM_clientglobalvector(gettaginfo_offset));
2495         VectorCopy(fo, PRVM_clientglobalvector(gettaginfo_forward));
2496         VectorScale(le, -1, PRVM_clientglobalvector(gettaginfo_right));
2497         VectorCopy(up, PRVM_clientglobalvector(gettaginfo_up));
2498
2499         switch(returncode)
2500         {
2501                 case 1:
2502                         VM_Warning(prog, "gettagindex: can't affect world entity\n");
2503                         break;
2504                 case 2:
2505                         VM_Warning(prog, "gettagindex: can't affect free entity\n");
2506                         break;
2507                 case 3:
2508                         Con_DPrintf("CL_GetTagMatrix(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(e));
2509                         break;
2510                 case 4:
2511                         Con_DPrintf("CL_GetTagMatrix(entity #%i): model has no tag with requested index %i\n", PRVM_NUM_FOR_EDICT(e), tagindex);
2512                         break;
2513                 case 5:
2514                         Con_DPrintf("CL_GetTagMatrix(entity #%i): runaway loop at attachment chain\n", PRVM_NUM_FOR_EDICT(e));
2515                         break;
2516         }
2517 }
2518
2519 //============================================================================
2520
2521 //====================
2522 // DP_CSQC_SPAWNPARTICLE
2523 // a QC hook to engine's CL_NewParticle
2524 //====================
2525
2526 // particle theme struct
2527 typedef struct vmparticletheme_s
2528 {
2529         unsigned short typeindex;
2530         qboolean initialized;
2531         pblend_t blendmode;
2532         porientation_t orientation;
2533         int color1;
2534         int color2;
2535         int tex;
2536         float size;
2537         float sizeincrease;
2538         float alpha;
2539         float alphafade;
2540         float gravity;
2541         float bounce;
2542         float airfriction;
2543         float liquidfriction;
2544         float originjitter;
2545         float velocityjitter;
2546         qboolean qualityreduction;
2547         float lifetime;
2548         float stretch;
2549         int staincolor1;
2550         int staincolor2;
2551         int staintex;
2552         float stainalpha;
2553         float stainsize;
2554         float delayspawn;
2555         float delaycollision;
2556         float angle;
2557         float spin;
2558 }vmparticletheme_t;
2559
2560 // particle spawner
2561 typedef struct vmparticlespawner_s
2562 {
2563         mempool_t                       *pool;
2564         qboolean                        initialized;
2565         qboolean                        verified;
2566         vmparticletheme_t       *themes;
2567         int                                     max_themes;
2568         // global addresses
2569         float *particle_type;
2570         float *particle_blendmode; 
2571         float *particle_orientation;
2572         float *particle_color1;
2573         float *particle_color2;
2574         float *particle_tex;
2575         float *particle_size;
2576         float *particle_sizeincrease;
2577         float *particle_alpha;
2578         float *particle_alphafade;
2579         float *particle_time;
2580         float *particle_gravity;
2581         float *particle_bounce;
2582         float *particle_airfriction;
2583         float *particle_liquidfriction;
2584         float *particle_originjitter;
2585         float *particle_velocityjitter;
2586         float *particle_qualityreduction;
2587         float *particle_stretch;
2588         float *particle_staincolor1;
2589         float *particle_staincolor2;
2590         float *particle_stainalpha;
2591         float *particle_stainsize;
2592         float *particle_staintex;
2593         float *particle_delayspawn;
2594         float *particle_delaycollision;
2595         float *particle_angle;
2596         float *particle_spin;
2597 }vmparticlespawner_t;
2598
2599 vmparticlespawner_t vmpartspawner;
2600
2601 // TODO: automatic max_themes grow
2602 static void VM_InitParticleSpawner (prvm_prog_t *prog, int maxthemes)
2603 {
2604         // bound max themes to not be an insane value
2605         if (maxthemes < 4)
2606                 maxthemes = 4;
2607         if (maxthemes > 2048)
2608                 maxthemes = 2048;
2609         // allocate and set up structure
2610         if (vmpartspawner.initialized) // reallocate
2611         {
2612                 Mem_FreePool(&vmpartspawner.pool);
2613                 memset(&vmpartspawner, 0, sizeof(vmparticlespawner_t));
2614         }
2615         vmpartspawner.pool = Mem_AllocPool("VMPARTICLESPAWNER", 0, NULL);
2616         vmpartspawner.themes = (vmparticletheme_t *)Mem_Alloc(vmpartspawner.pool, sizeof(vmparticletheme_t)*maxthemes);
2617         vmpartspawner.max_themes = maxthemes;
2618         vmpartspawner.initialized = true;
2619         vmpartspawner.verified = true;
2620         // get field addresses for fast querying (we can do 1000 calls of spawnparticle in a frame)
2621         vmpartspawner.particle_type = &PRVM_clientglobalfloat(particle_type);
2622         vmpartspawner.particle_blendmode = &PRVM_clientglobalfloat(particle_blendmode);
2623         vmpartspawner.particle_orientation = &PRVM_clientglobalfloat(particle_orientation);
2624         vmpartspawner.particle_color1 = PRVM_clientglobalvector(particle_color1);
2625         vmpartspawner.particle_color2 = PRVM_clientglobalvector(particle_color2);
2626         vmpartspawner.particle_tex = &PRVM_clientglobalfloat(particle_tex);
2627         vmpartspawner.particle_size = &PRVM_clientglobalfloat(particle_size);
2628         vmpartspawner.particle_sizeincrease = &PRVM_clientglobalfloat(particle_sizeincrease);
2629         vmpartspawner.particle_alpha = &PRVM_clientglobalfloat(particle_alpha);
2630         vmpartspawner.particle_alphafade = &PRVM_clientglobalfloat(particle_alphafade);
2631         vmpartspawner.particle_time = &PRVM_clientglobalfloat(particle_time);
2632         vmpartspawner.particle_gravity = &PRVM_clientglobalfloat(particle_gravity);
2633         vmpartspawner.particle_bounce = &PRVM_clientglobalfloat(particle_bounce);
2634         vmpartspawner.particle_airfriction = &PRVM_clientglobalfloat(particle_airfriction);
2635         vmpartspawner.particle_liquidfriction = &PRVM_clientglobalfloat(particle_liquidfriction);
2636         vmpartspawner.particle_originjitter = &PRVM_clientglobalfloat(particle_originjitter);
2637         vmpartspawner.particle_velocityjitter = &PRVM_clientglobalfloat(particle_velocityjitter);
2638         vmpartspawner.particle_qualityreduction = &PRVM_clientglobalfloat(particle_qualityreduction);
2639         vmpartspawner.particle_stretch = &PRVM_clientglobalfloat(particle_stretch);
2640         vmpartspawner.particle_staincolor1 = PRVM_clientglobalvector(particle_staincolor1);
2641         vmpartspawner.particle_staincolor2 = PRVM_clientglobalvector(particle_staincolor2);
2642         vmpartspawner.particle_stainalpha = &PRVM_clientglobalfloat(particle_stainalpha);
2643         vmpartspawner.particle_stainsize = &PRVM_clientglobalfloat(particle_stainsize);
2644         vmpartspawner.particle_staintex = &PRVM_clientglobalfloat(particle_staintex);
2645         vmpartspawner.particle_staintex = &PRVM_clientglobalfloat(particle_staintex);
2646         vmpartspawner.particle_delayspawn = &PRVM_clientglobalfloat(particle_delayspawn);
2647         vmpartspawner.particle_delaycollision = &PRVM_clientglobalfloat(particle_delaycollision);
2648         vmpartspawner.particle_angle = &PRVM_clientglobalfloat(particle_angle);
2649         vmpartspawner.particle_spin = &PRVM_clientglobalfloat(particle_spin);
2650         #undef getglobal
2651         #undef getglobalvector
2652 }
2653
2654 // reset particle theme to default values
2655 static void VM_ResetParticleTheme (vmparticletheme_t *theme)
2656 {
2657         theme->initialized = true;
2658         theme->typeindex = pt_static;
2659         theme->blendmode = PBLEND_ADD;
2660         theme->orientation = PARTICLE_BILLBOARD;
2661         theme->color1 = 0x808080;
2662         theme->color2 = 0xFFFFFF;
2663         theme->tex = 63;
2664         theme->size = 2;
2665         theme->sizeincrease = 0;
2666         theme->alpha = 256;
2667         theme->alphafade = 512;
2668         theme->gravity = 0.0f;
2669         theme->bounce = 0.0f;
2670         theme->airfriction = 1.0f;
2671         theme->liquidfriction = 4.0f;
2672         theme->originjitter = 0.0f;
2673         theme->velocityjitter = 0.0f;
2674         theme->qualityreduction = false;
2675         theme->lifetime = 4;
2676         theme->stretch = 1;
2677         theme->staincolor1 = -1;
2678         theme->staincolor2 = -1;
2679         theme->staintex = -1;
2680         theme->delayspawn = 0.0f;
2681         theme->delaycollision = 0.0f;
2682         theme->angle = 0.0f;
2683         theme->spin = 0.0f;
2684 }
2685
2686 // particle theme -> QC globals
2687 static void VM_CL_ParticleThemeToGlobals(vmparticletheme_t *theme)
2688 {
2689         *vmpartspawner.particle_type = theme->typeindex;
2690         *vmpartspawner.particle_blendmode = theme->blendmode;
2691         *vmpartspawner.particle_orientation = theme->orientation;
2692         vmpartspawner.particle_color1[0] = (theme->color1 >> 16) & 0xFF; // VorteX: int only can store 0-255, not 0-256 which means 0 - 0,99609375...
2693         vmpartspawner.particle_color1[1] = (theme->color1 >> 8) & 0xFF;
2694         vmpartspawner.particle_color1[2] = (theme->color1 >> 0) & 0xFF;
2695         vmpartspawner.particle_color2[0] = (theme->color2 >> 16) & 0xFF;
2696         vmpartspawner.particle_color2[1] = (theme->color2 >> 8) & 0xFF;
2697         vmpartspawner.particle_color2[2] = (theme->color2 >> 0) & 0xFF;
2698         *vmpartspawner.particle_tex = (float)theme->tex;
2699         *vmpartspawner.particle_size = theme->size;
2700         *vmpartspawner.particle_sizeincrease = theme->sizeincrease;
2701         *vmpartspawner.particle_alpha = theme->alpha/256;
2702         *vmpartspawner.particle_alphafade = theme->alphafade/256;
2703         *vmpartspawner.particle_time = theme->lifetime;
2704         *vmpartspawner.particle_gravity = theme->gravity;
2705         *vmpartspawner.particle_bounce = theme->bounce;
2706         *vmpartspawner.particle_airfriction = theme->airfriction;
2707         *vmpartspawner.particle_liquidfriction = theme->liquidfriction;
2708         *vmpartspawner.particle_originjitter = theme->originjitter;
2709         *vmpartspawner.particle_velocityjitter = theme->velocityjitter;
2710         *vmpartspawner.particle_qualityreduction = theme->qualityreduction;
2711         *vmpartspawner.particle_stretch = theme->stretch;
2712         vmpartspawner.particle_staincolor1[0] = ((int)theme->staincolor1 >> 16) & 0xFF;
2713         vmpartspawner.particle_staincolor1[1] = ((int)theme->staincolor1 >> 8) & 0xFF;
2714         vmpartspawner.particle_staincolor1[2] = ((int)theme->staincolor1 >> 0) & 0xFF;
2715         vmpartspawner.particle_staincolor2[0] = ((int)theme->staincolor2 >> 16) & 0xFF;
2716         vmpartspawner.particle_staincolor2[1] = ((int)theme->staincolor2 >> 8) & 0xFF;
2717         vmpartspawner.particle_staincolor2[2] = ((int)theme->staincolor2 >> 0) & 0xFF;
2718         *vmpartspawner.particle_staintex = (float)theme->staintex;
2719         *vmpartspawner.particle_stainalpha = (float)theme->stainalpha/256;
2720         *vmpartspawner.particle_stainsize = (float)theme->stainsize;
2721         *vmpartspawner.particle_delayspawn = theme->delayspawn;
2722         *vmpartspawner.particle_delaycollision = theme->delaycollision;
2723         *vmpartspawner.particle_angle = theme->angle;
2724         *vmpartspawner.particle_spin = theme->spin;
2725 }
2726
2727 // QC globals ->  particle theme
2728 static void VM_CL_ParticleThemeFromGlobals(vmparticletheme_t *theme)
2729 {
2730         theme->typeindex = (unsigned short)*vmpartspawner.particle_type;
2731         theme->blendmode = (pblend_t)(int)*vmpartspawner.particle_blendmode;
2732         theme->orientation = (porientation_t)(int)*vmpartspawner.particle_orientation;
2733         theme->color1 = ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]);
2734         theme->color2 = ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]);
2735         theme->tex = (int)*vmpartspawner.particle_tex;
2736         theme->size = *vmpartspawner.particle_size;
2737         theme->sizeincrease = *vmpartspawner.particle_sizeincrease;
2738         theme->alpha = *vmpartspawner.particle_alpha*256;
2739         theme->alphafade = *vmpartspawner.particle_alphafade*256;
2740         theme->lifetime = *vmpartspawner.particle_time;
2741         theme->gravity = *vmpartspawner.particle_gravity;
2742         theme->bounce = *vmpartspawner.particle_bounce;
2743         theme->airfriction = *vmpartspawner.particle_airfriction;
2744         theme->liquidfriction = *vmpartspawner.particle_liquidfriction;
2745         theme->originjitter = *vmpartspawner.particle_originjitter;
2746         theme->velocityjitter = *vmpartspawner.particle_velocityjitter;
2747         theme->qualityreduction = (*vmpartspawner.particle_qualityreduction) ? true : false;
2748         theme->stretch = *vmpartspawner.particle_stretch;
2749         theme->staincolor1 = ((int)vmpartspawner.particle_staincolor1[0])*65536 + (int)(vmpartspawner.particle_staincolor1[1])*256 + (int)(vmpartspawner.particle_staincolor1[2]);
2750         theme->staincolor2 = (int)(vmpartspawner.particle_staincolor2[0])*65536 + (int)(vmpartspawner.particle_staincolor2[1])*256 + (int)(vmpartspawner.particle_staincolor2[2]);
2751         theme->staintex =(int)*vmpartspawner.particle_staintex;
2752         theme->stainalpha = *vmpartspawner.particle_stainalpha*256;
2753         theme->stainsize = *vmpartspawner.particle_stainsize;
2754         theme->delayspawn = *vmpartspawner.particle_delayspawn;
2755         theme->delaycollision = *vmpartspawner.particle_delaycollision;
2756         theme->angle = *vmpartspawner.particle_angle;
2757         theme->spin = *vmpartspawner.particle_spin;
2758 }
2759
2760 // init particle spawner interface
2761 // # float(float max_themes) initparticlespawner
2762 static void VM_CL_InitParticleSpawner (prvm_prog_t *prog)
2763 {
2764         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_InitParticleSpawner);
2765         VM_InitParticleSpawner(prog, (int)PRVM_G_FLOAT(OFS_PARM0));
2766         vmpartspawner.themes[0].initialized = true;
2767         VM_ResetParticleTheme(&vmpartspawner.themes[0]);
2768         PRVM_G_FLOAT(OFS_RETURN) = (vmpartspawner.verified == true) ? 1 : 0;
2769 }
2770
2771 // void() resetparticle
2772 static void VM_CL_ResetParticle (prvm_prog_t *prog)
2773 {
2774         VM_SAFEPARMCOUNT(0, VM_CL_ResetParticle);
2775         if (vmpartspawner.verified == false)
2776         {
2777                 VM_Warning(prog, "VM_CL_ResetParticle: particle spawner not initialized\n");
2778                 return;
2779         }
2780         VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2781 }
2782
2783 // void(float themenum) particletheme
2784 static void VM_CL_ParticleTheme (prvm_prog_t *prog)
2785 {
2786         int themenum;
2787
2788         VM_SAFEPARMCOUNT(1, VM_CL_ParticleTheme);
2789         if (vmpartspawner.verified == false)
2790         {
2791                 VM_Warning(prog, "VM_CL_ParticleTheme: particle spawner not initialized\n");
2792                 return;
2793         }
2794         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2795         if (themenum < 0 || themenum >= vmpartspawner.max_themes)
2796         {
2797                 VM_Warning(prog, "VM_CL_ParticleTheme: bad theme number %i\n", themenum);
2798                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2799                 return;
2800         }
2801         if (vmpartspawner.themes[themenum].initialized == false)
2802         {
2803                 VM_Warning(prog, "VM_CL_ParticleTheme: theme #%i not exists\n", themenum);
2804                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2805                 return;
2806         }
2807         // load particle theme into globals
2808         VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[themenum]);
2809 }
2810
2811 // float() saveparticletheme
2812 // void(float themenum) updateparticletheme
2813 static void VM_CL_ParticleThemeSave (prvm_prog_t *prog)
2814 {
2815         int themenum;
2816
2817         VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_ParticleThemeSave);
2818         if (vmpartspawner.verified == false)
2819         {
2820                 VM_Warning(prog, "VM_CL_ParticleThemeSave: particle spawner not initialized\n");
2821                 return;
2822         }
2823         // allocate new theme, save it and return
2824         if (prog->argc < 1)
2825         {
2826                 for (themenum = 0; themenum < vmpartspawner.max_themes; themenum++)
2827                         if (vmpartspawner.themes[themenum].initialized == false)
2828                                 break;
2829                 if (themenum >= vmpartspawner.max_themes)
2830                 {
2831                         if (vmpartspawner.max_themes == 2048)
2832                                 VM_Warning(prog, "VM_CL_ParticleThemeSave: no free theme slots\n");
2833                         else
2834                                 VM_Warning(prog, "VM_CL_ParticleThemeSave: no free theme slots, try initparticlespawner() with highter max_themes\n");
2835                         PRVM_G_FLOAT(OFS_RETURN) = -1;
2836                         return;
2837                 }
2838                 vmpartspawner.themes[themenum].initialized = true;
2839                 VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]);
2840                 PRVM_G_FLOAT(OFS_RETURN) = themenum;
2841                 return;
2842         }
2843         // update existing theme
2844         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2845         if (themenum < 0 || themenum >= vmpartspawner.max_themes)
2846         {
2847                 VM_Warning(prog, "VM_CL_ParticleThemeSave: bad theme number %i\n", themenum);
2848                 return;
2849         }
2850         vmpartspawner.themes[themenum].initialized = true;
2851         VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]);
2852 }
2853
2854 // void(float themenum) freeparticletheme
2855 static void VM_CL_ParticleThemeFree (prvm_prog_t *prog)
2856 {
2857         int themenum;
2858
2859         VM_SAFEPARMCOUNT(1, VM_CL_ParticleThemeFree);
2860         if (vmpartspawner.verified == false)
2861         {
2862                 VM_Warning(prog, "VM_CL_ParticleThemeFree: particle spawner not initialized\n");
2863                 return;
2864         }
2865         themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2866         // check parms
2867         if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2868         {
2869                 VM_Warning(prog, "VM_CL_ParticleThemeFree: bad theme number %i\n", themenum);
2870                 return;
2871         }
2872         if (vmpartspawner.themes[themenum].initialized == false)
2873         {
2874                 VM_Warning(prog, "VM_CL_ParticleThemeFree: theme #%i already freed\n", themenum);
2875                 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2876                 return;
2877         }
2878         // free theme
2879         VM_ResetParticleTheme(&vmpartspawner.themes[themenum]);
2880         vmpartspawner.themes[themenum].initialized = false;
2881 }
2882
2883 // float(vector org, vector dir, [float theme]) particle
2884 // returns 0 if failed, 1 if succesful
2885 static void VM_CL_SpawnParticle (prvm_prog_t *prog)
2886 {
2887         float *org, *dir;
2888         vmparticletheme_t *theme;
2889         particle_t *part;
2890         int themenum;
2891
2892         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_SpawnParticle2);
2893         if (vmpartspawner.verified == false)
2894         {
2895                 VM_Warning(prog, "VM_CL_SpawnParticle: particle spawner not initialized\n");
2896                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
2897                 return;
2898         }
2899         org = PRVM_G_VECTOR(OFS_PARM0);
2900         dir = PRVM_G_VECTOR(OFS_PARM1);
2901         
2902         if (prog->argc < 3) // global-set particle
2903         {
2904                 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);
2905                 if (!part)
2906                 {
2907                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
2908                         return;
2909                 }
2910                 if (*vmpartspawner.particle_delayspawn)
2911                         part->delayedspawn = cl.time + *vmpartspawner.particle_delayspawn;
2912                 //if (*vmpartspawner.particle_delaycollision)
2913                 //      part->delayedcollisions = cl.time + *vmpartspawner.particle_delaycollision;
2914         }
2915         else // quick themed particle
2916         {
2917                 themenum = (int)PRVM_G_FLOAT(OFS_PARM2);
2918                 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2919                 {
2920                         VM_Warning(prog, "VM_CL_SpawnParticle: bad theme number %i\n", themenum);
2921                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
2922                         return;
2923                 }
2924                 theme = &vmpartspawner.themes[themenum];
2925                 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);
2926                 if (!part)
2927                 {
2928                         PRVM_G_FLOAT(OFS_RETURN) = 0; 
2929                         return;
2930                 }
2931                 if (theme->delayspawn)
2932                         part->delayedspawn = cl.time + theme->delayspawn;
2933                 //if (theme->delaycollision)
2934                 //      part->delayedcollisions = cl.time + theme->delaycollision;
2935         }
2936         PRVM_G_FLOAT(OFS_RETURN) = 1; 
2937 }
2938
2939 // float(vector org, vector dir, float spawndelay, float collisiondelay, [float theme]) delayedparticle
2940 // returns 0 if failed, 1 if success
2941 static void VM_CL_SpawnParticleDelayed (prvm_prog_t *prog)
2942 {
2943         float *org, *dir;
2944         vmparticletheme_t *theme;
2945         particle_t *part;
2946         int themenum;
2947
2948         VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_SpawnParticle2);
2949         if (vmpartspawner.verified == false)
2950         {
2951                 VM_Warning(prog, "VM_CL_SpawnParticle: particle spawner not initialized\n");
2952                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
2953                 return;
2954         }
2955         org = PRVM_G_VECTOR(OFS_PARM0);
2956         dir = PRVM_G_VECTOR(OFS_PARM1);
2957         if (prog->argc < 5) // global-set particle
2958                 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);
2959         else // themed particle
2960         {
2961                 themenum = (int)PRVM_G_FLOAT(OFS_PARM4);
2962                 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2963                 {
2964                         VM_Warning(prog, "VM_CL_SpawnParticle: bad theme number %i\n", themenum);
2965                         PRVM_G_FLOAT(OFS_RETURN) = 0;  
2966                         return;
2967                 }
2968                 theme = &vmpartspawner.themes[themenum];
2969                 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);
2970         }
2971         if (!part) 
2972         { 
2973                 PRVM_G_FLOAT(OFS_RETURN) = 0; 
2974                 return; 
2975         }
2976         part->delayedspawn = cl.time + PRVM_G_FLOAT(OFS_PARM2);
2977         //part->delayedcollisions = cl.time + PRVM_G_FLOAT(OFS_PARM3);
2978         PRVM_G_FLOAT(OFS_RETURN) = 0;
2979 }
2980
2981 //====================
2982 //CSQC engine entities query
2983 //====================
2984
2985 // float(float entitynum, float whatfld) getentity;
2986 // vector(float entitynum, float whatfld) getentityvec;
2987 // querying engine-drawn entity
2988 // VorteX: currently it's only tested with whatfld = 1..7
2989 static void VM_CL_GetEntity (prvm_prog_t *prog)
2990 {
2991         int entnum, fieldnum;
2992         float org[3], v1[3], v2[3];
2993         VM_SAFEPARMCOUNT(2, VM_CL_GetEntityVec);
2994
2995         entnum = PRVM_G_FLOAT(OFS_PARM0);
2996         if (entnum < 0 || entnum >= cl.num_entities)
2997         {
2998                 PRVM_G_FLOAT(OFS_RETURN) = 0;
2999                 return;
3000         }
3001         fieldnum = PRVM_G_FLOAT(OFS_PARM1);
3002         switch(fieldnum)
3003         {
3004                 case 0: // active state
3005                         PRVM_G_FLOAT(OFS_RETURN) = cl.entities_active[entnum];
3006                         break;
3007                 case 1: // origin
3008                         Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, PRVM_G_VECTOR(OFS_RETURN));
3009                         break; 
3010                 case 2: // forward
3011                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, PRVM_G_VECTOR(OFS_RETURN), v1, v2, org);        
3012                         break;
3013                 case 3: // right
3014                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, v1, PRVM_G_VECTOR(OFS_RETURN), v2, org);        
3015                         break;
3016                 case 4: // up
3017                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, v1, v2, PRVM_G_VECTOR(OFS_RETURN), org);        
3018                         break;
3019                 case 5: // scale
3020                         PRVM_G_FLOAT(OFS_RETURN) = Matrix4x4_ScaleFromMatrix(&cl.entities[entnum].render.matrix);
3021                         break;  
3022                 case 6: // origin + v_forward, v_right, v_up
3023                         Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, PRVM_clientglobalvector(v_forward), PRVM_clientglobalvector(v_right), PRVM_clientglobalvector(v_up), PRVM_G_VECTOR(OFS_RETURN));        
3024                         break;  
3025                 case 7: // alpha
3026                         PRVM_G_FLOAT(OFS_RETURN) = cl.entities[entnum].render.alpha;
3027                         break;  
3028                 case 8: // colormor
3029                         VectorCopy(cl.entities[entnum].render.colormod, PRVM_G_VECTOR(OFS_RETURN));
3030                         break;
3031                 case 9: // pants colormod
3032                         VectorCopy(cl.entities[entnum].render.colormap_pantscolor, PRVM_G_VECTOR(OFS_RETURN));
3033                         break;
3034                 case 10: // shirt colormod
3035                         VectorCopy(cl.entities[entnum].render.colormap_shirtcolor, PRVM_G_VECTOR(OFS_RETURN));
3036                         break;
3037                 case 11: // skinnum
3038                         PRVM_G_FLOAT(OFS_RETURN) = cl.entities[entnum].render.skinnum;
3039                         break;  
3040                 case 12: // mins
3041                         VectorCopy(cl.entities[entnum].render.mins, PRVM_G_VECTOR(OFS_RETURN));         
3042                         break;  
3043                 case 13: // maxs
3044                         VectorCopy(cl.entities[entnum].render.maxs, PRVM_G_VECTOR(OFS_RETURN));         
3045                         break;  
3046                 case 14: // absmin
3047                         Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, org);
3048                         VectorAdd(cl.entities[entnum].render.mins, org, PRVM_G_VECTOR(OFS_RETURN));             
3049                         break;  
3050                 case 15: // absmax
3051                         Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, org);
3052                         VectorAdd(cl.entities[entnum].render.maxs, org, PRVM_G_VECTOR(OFS_RETURN));             
3053                         break;
3054                 case 16: // light
3055                         VectorMA(cl.entities[entnum].render.modellight_ambient, 0.5, cl.entities[entnum].render.modellight_diffuse, PRVM_G_VECTOR(OFS_RETURN));
3056                         break;  
3057                 default:
3058                         PRVM_G_FLOAT(OFS_RETURN) = 0;
3059                         break;
3060         }
3061 }
3062
3063 //====================
3064 //QC POLYGON functions
3065 //====================
3066
3067 //#304 void() renderscene (EXT_CSQC)
3068 // moved that here to reset the polygons,
3069 // resetting them earlier causes R_Mesh_Draw to be called with numvertices = 0
3070 // --blub
3071 static void VM_CL_R_RenderScene (prvm_prog_t *prog)
3072 {
3073         double t = Sys_DirtyTime();
3074         vmpolygons_t *polys = &prog->vmpolygons;
3075         VM_SAFEPARMCOUNT(0, VM_CL_R_RenderScene);
3076
3077         // update the views
3078         if(r_refdef.view.ismain)
3079         {
3080                 // set the main view
3081                 csqc_main_r_refdef_view = r_refdef.view;
3082
3083                 // clear the flags so no other view becomes "main" unless CSQC sets VF_MAINVIEW
3084                 r_refdef.view.ismain = false;
3085                 csqc_original_r_refdef_view.ismain = false;
3086         }
3087
3088         // we need to update any RENDER_VIEWMODEL entities at this point because
3089         // csqc supplies its own view matrix
3090         CL_UpdateViewEntities();
3091
3092         // now draw stuff!
3093         R_RenderView();
3094
3095         polys->num_vertices = polys->num_triangles = 0;
3096
3097         // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView
3098         t = Sys_DirtyTime() - t;if (t < 0 || t >= 1800) t = 0;
3099         prog->functions[PRVM_clientfunction(CSQC_UpdateView)].totaltime -= t;
3100 }
3101
3102 static void VM_ResizePolygons(vmpolygons_t *polys)
3103 {
3104         float *oldvertex3f = polys->data_vertex3f;
3105         float *oldcolor4f = polys->data_color4f;
3106         float *oldtexcoord2f = polys->data_texcoord2f;
3107         vmpolygons_triangle_t *oldtriangles = polys->data_triangles;
3108         unsigned short *oldsortedelement3s = polys->data_sortedelement3s;
3109         polys->max_vertices = min(polys->max_triangles*3, 65536);
3110         polys->data_vertex3f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[3]));
3111         polys->data_color4f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[4]));
3112         polys->data_texcoord2f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[2]));
3113         polys->data_triangles = (vmpolygons_triangle_t *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(vmpolygons_triangle_t));
3114         polys->data_sortedelement3s = (unsigned short *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(unsigned short[3]));
3115         if (polys->num_vertices)
3116         {
3117                 memcpy(polys->data_vertex3f, oldvertex3f, polys->num_vertices*sizeof(float[3]));
3118                 memcpy(polys->data_color4f, oldcolor4f, polys->num_vertices*sizeof(float[4]));
3119                 memcpy(polys->data_texcoord2f, oldtexcoord2f, polys->num_vertices*sizeof(float[2]));
3120         }
3121         if (polys->num_triangles)
3122         {
3123                 memcpy(polys->data_triangles, oldtriangles, polys->num_triangles*sizeof(vmpolygons_triangle_t));
3124                 memcpy(polys->data_sortedelement3s, oldsortedelement3s, polys->num_triangles*sizeof(unsigned short[3]));
3125         }
3126         if (oldvertex3f)
3127                 Mem_Free(oldvertex3f);
3128         if (oldcolor4f)
3129                 Mem_Free(oldcolor4f);
3130         if (oldtexcoord2f)
3131                 Mem_Free(oldtexcoord2f);
3132         if (oldtriangles)
3133                 Mem_Free(oldtriangles);
3134         if (oldsortedelement3s)
3135                 Mem_Free(oldsortedelement3s);
3136 }
3137
3138 static void VM_InitPolygons (vmpolygons_t* polys)
3139 {
3140         memset(polys, 0, sizeof(*polys));
3141         polys->pool = Mem_AllocPool("VMPOLY", 0, NULL);
3142         polys->max_triangles = 1024;
3143         VM_ResizePolygons(polys);
3144         polys->initialized = true;
3145 }
3146
3147 static void VM_DrawPolygonCallback (const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3148 {
3149         int surfacelistindex;
3150         vmpolygons_t *polys = (vmpolygons_t *)ent;
3151 //      R_Mesh_ResetTextureState();
3152         R_EntityMatrix(&identitymatrix);
3153         GL_CullFace(GL_NONE);
3154         GL_DepthTest(true); // polys in 3D space shall always have depth test
3155         GL_DepthRange(0, 1);
3156         R_Mesh_PrepareVertices_Generic_Arrays(polys->num_vertices, polys->data_vertex3f, polys->data_color4f, polys->data_texcoord2f);
3157
3158         for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
3159         {
3160                 int numtriangles = 0;
3161                 rtexture_t *tex = polys->data_triangles[surfacelist[surfacelistindex]].texture;
3162                 int drawflag = polys->data_triangles[surfacelist[surfacelistindex]].drawflag;
3163                 DrawQ_ProcessDrawFlag(drawflag, polys->data_triangles[surfacelist[surfacelistindex]].hasalpha);
3164                 R_SetupShader_Generic(tex, NULL, GL_MODULATE, 1, false, false, false);
3165                 numtriangles = 0;
3166                 for (;surfacelistindex < numsurfaces;surfacelistindex++)
3167                 {
3168                         if (polys->data_triangles[surfacelist[surfacelistindex]].texture != tex || polys->data_triangles[surfacelist[surfacelistindex]].drawflag != drawflag)
3169                                 break;
3170                         VectorCopy(polys->data_triangles[surfacelist[surfacelistindex]].elements, polys->data_sortedelement3s + 3*numtriangles);
3171                         numtriangles++;
3172                 }
3173                 R_Mesh_Draw(0, polys->num_vertices, 0, numtriangles, NULL, NULL, 0, polys->data_sortedelement3s, NULL, 0);
3174         }
3175 }
3176
3177 static void VMPolygons_Store(vmpolygons_t *polys)
3178 {
3179         qboolean hasalpha;
3180         int i;
3181
3182         // detect if we have alpha
3183         hasalpha = polys->begin_texture_hasalpha;
3184         for(i = 0; !hasalpha && (i < polys->begin_vertices); ++i)
3185                 if(polys->begin_color[i][3] < 1)
3186                         hasalpha = true;
3187
3188         if (polys->begin_draw2d)
3189         {
3190                 // draw the polygon as 2D immediately
3191                 drawqueuemesh_t mesh;
3192                 mesh.texture = polys->begin_texture;
3193                 mesh.num_vertices = polys->begin_vertices;
3194                 mesh.num_triangles = polys->begin_vertices-2;
3195                 mesh.data_element3i = polygonelement3i;
3196                 mesh.data_element3s = polygonelement3s;
3197                 mesh.data_vertex3f = polys->begin_vertex[0];
3198                 mesh.data_color4f = polys->begin_color[0];
3199                 mesh.data_texcoord2f = polys->begin_texcoord[0];
3200                 DrawQ_Mesh(&mesh, polys->begin_drawflag, hasalpha);
3201         }
3202         else
3203         {
3204                 // queue the polygon as 3D for sorted transparent rendering later
3205                 int i;
3206                 if (polys->max_triangles < polys->num_triangles + polys->begin_vertices-2)
3207                 {
3208                         while (polys->max_triangles < polys->num_triangles + polys->begin_vertices-2)
3209                                 polys->max_triangles *= 2;
3210                         VM_ResizePolygons(polys);
3211                 }
3212                 if (polys->num_vertices + polys->begin_vertices <= polys->max_vertices)
3213                 {
3214                         // needle in a haystack!
3215                         // polys->num_vertices was used for copying where we actually want to copy begin_vertices
3216                         // that also caused it to not render the first polygon that is added
3217                         // --blub
3218                         memcpy(polys->data_vertex3f + polys->num_vertices * 3, polys->begin_vertex[0], polys->begin_vertices * sizeof(float[3]));
3219                         memcpy(polys->data_color4f + polys->num_vertices * 4, polys->begin_color[0], polys->begin_vertices * sizeof(float[4]));
3220                         memcpy(polys->data_texcoord2f + polys->num_vertices * 2, polys->begin_texcoord[0], polys->begin_vertices * sizeof(float[2]));
3221                         for (i = 0;i < polys->begin_vertices-2;i++)
3222                         {
3223                                 polys->data_triangles[polys->num_triangles].texture = polys->begin_texture;
3224                                 polys->data_triangles[polys->num_triangles].drawflag = polys->begin_drawflag;
3225                                 polys->data_triangles[polys->num_triangles].elements[0] = polys->num_vertices;
3226                                 polys->data_triangles[polys->num_triangles].elements[1] = polys->num_vertices + i+1;
3227                                 polys->data_triangles[polys->num_triangles].elements[2] = polys->num_vertices + i+2;
3228                                 polys->data_triangles[polys->num_triangles].hasalpha = hasalpha;
3229                                 polys->num_triangles++;
3230                         }
3231                         polys->num_vertices += polys->begin_vertices;
3232                 }
3233         }
3234         polys->begin_active = false;
3235 }
3236
3237 // TODO: move this into the client code and clean-up everything else, too! [1/6/2008 Black]
3238 // LordHavoc: agreed, this is a mess
3239 void VM_CL_AddPolygonsToMeshQueue (prvm_prog_t *prog)
3240 {
3241         int i;
3242         vmpolygons_t *polys = &prog->vmpolygons;
3243         vec3_t center;
3244
3245         // only add polygons of the currently active prog to the queue - if there is none, we're done
3246         if( !prog )
3247                 return;
3248
3249         if (!polys->num_triangles)
3250                 return;
3251
3252         for (i = 0;i < polys->num_triangles;i++)
3253         {
3254                 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);
3255                 R_MeshQueue_AddTransparent(MESHQUEUE_SORT_DISTANCE, center, VM_DrawPolygonCallback, (entity_render_t *)polys, i, NULL);
3256         }
3257
3258         /*polys->num_triangles = 0; // now done after rendering the scene,
3259           polys->num_vertices = 0;  // otherwise it's not rendered at all and prints an error message --blub */
3260 }
3261
3262 //void(string texturename, float flag[, float is2d]) R_BeginPolygon
3263 static void VM_CL_R_PolygonBegin (prvm_prog_t *prog)
3264 {
3265         const char              *picname;
3266         skinframe_t     *sf;
3267         vmpolygons_t *polys = &prog->vmpolygons;
3268         int tf;
3269
3270         // TODO instead of using skinframes here (which provides the benefit of
3271         // better management of flags, and is more suited for 3D rendering), what
3272         // about supporting Q3 shaders?
3273
3274         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_R_PolygonBegin);
3275
3276         if (!polys->initialized)
3277                 VM_InitPolygons(polys);
3278         if (polys->begin_active)
3279         {
3280                 VM_Warning(prog, "VM_CL_R_PolygonBegin: called twice without VM_CL_R_PolygonBegin after first\n");
3281                 return;
3282         }
3283         picname = PRVM_G_STRING(OFS_PARM0);
3284
3285         sf = NULL;
3286         if(*picname)
3287         {
3288                 tf = TEXF_ALPHA;
3289                 if((int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MIPMAP)
3290                         tf |= TEXF_MIPMAP;
3291
3292                 do
3293                 {
3294                         sf = R_SkinFrame_FindNextByName(sf, picname);
3295                 }
3296                 while(sf && sf->textureflags != tf);
3297
3298                 if(!sf || !sf->base)
3299                         sf = R_SkinFrame_LoadExternal(picname, tf, true);
3300
3301                 if(sf)
3302                         R_SkinFrame_MarkUsed(sf);
3303         }
3304
3305         polys->begin_texture = (sf && sf->base) ? sf->base : r_texture_white;
3306         polys->begin_texture_hasalpha = (sf && sf->base) ? sf->hasalpha : false;
3307         polys->begin_drawflag = (int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MASK;
3308         polys->begin_vertices = 0;
3309         polys->begin_active = true;
3310         polys->begin_draw2d = (prog->argc >= 3 ? (int)PRVM_G_FLOAT(OFS_PARM2) : r_refdef.draw2dstage);
3311 }
3312
3313 //void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
3314 static void VM_CL_R_PolygonVertex (prvm_prog_t *prog)
3315 {
3316         vmpolygons_t *polys = &prog->vmpolygons;
3317
3318         VM_SAFEPARMCOUNT(4, VM_CL_R_PolygonVertex);
3319
3320         if (!polys->begin_active)
3321         {
3322                 VM_Warning(prog, "VM_CL_R_PolygonVertex: VM_CL_R_PolygonBegin wasn't called\n");
3323                 return;
3324         }
3325
3326         if (polys->begin_vertices >= VMPOLYGONS_MAXPOINTS)
3327         {
3328                 VM_Warning(prog, "VM_CL_R_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
3329                 return;
3330         }
3331
3332         polys->begin_vertex[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM0)[0];
3333         polys->begin_vertex[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM0)[1];
3334         polys->begin_vertex[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM0)[2];
3335         polys->begin_texcoord[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM1)[0];
3336         polys->begin_texcoord[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM1)[1];
3337         polys->begin_color[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM2)[0];
3338         polys->begin_color[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM2)[1];
3339         polys->begin_color[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM2)[2];
3340         polys->begin_color[polys->begin_vertices][3] = PRVM_G_FLOAT(OFS_PARM3);
3341         polys->begin_vertices++;
3342 }
3343
3344 //void() R_EndPolygon
3345 static void VM_CL_R_PolygonEnd (prvm_prog_t *prog)
3346 {
3347         vmpolygons_t *polys = &prog->vmpolygons;
3348
3349         VM_SAFEPARMCOUNT(0, VM_CL_R_PolygonEnd);
3350         if (!polys->begin_active)
3351         {
3352                 VM_Warning(prog, "VM_CL_R_PolygonEnd: VM_CL_R_PolygonBegin wasn't called\n");
3353                 return;
3354         }
3355         polys->begin_active = false;
3356         if (polys->begin_vertices >= 3)
3357                 VMPolygons_Store(polys);
3358         else
3359                 VM_Warning(prog, "VM_CL_R_PolygonEnd: %i vertices isn't a good choice\n", polys->begin_vertices);
3360 }
3361
3362 static vmpolygons_t debugPolys;
3363
3364 void Debug_PolygonBegin(const char *picname, int drawflag)
3365 {
3366         if(!debugPolys.initialized)
3367                 VM_InitPolygons(&debugPolys);
3368         if(debugPolys.begin_active)
3369         {
3370                 Con_Printf("Debug_PolygonBegin: called twice without Debug_PolygonEnd after first\n");
3371                 return;
3372         }
3373         debugPolys.begin_texture = picname[0] ? Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT)->tex : r_texture_white;
3374         debugPolys.begin_drawflag = drawflag;
3375         debugPolys.begin_vertices = 0;
3376         debugPolys.begin_active = true;
3377 }
3378
3379 void Debug_PolygonVertex(float x, float y, float z, float s, float t, float r, float g, float b, float a)
3380 {
3381         if(!debugPolys.begin_active)
3382         {
3383                 Con_Printf("Debug_PolygonVertex: Debug_PolygonBegin wasn't called\n");
3384                 return;
3385         }
3386
3387         if(debugPolys.begin_vertices > VMPOLYGONS_MAXPOINTS)
3388         {
3389                 Con_Printf("Debug_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
3390                 return;
3391         }
3392
3393         debugPolys.begin_vertex[debugPolys.begin_vertices][0] = x;
3394         debugPolys.begin_vertex[debugPolys.begin_vertices][1] = y;
3395         debugPolys.begin_vertex[debugPolys.begin_vertices][2] = z;
3396         debugPolys.begin_texcoord[debugPolys.begin_vertices][0] = s;
3397         debugPolys.begin_texcoord[debugPolys.begin_vertices][1] = t;
3398         debugPolys.begin_color[debugPolys.begin_vertices][0] = r;
3399         debugPolys.begin_color[debugPolys.begin_vertices][1] = g;
3400         debugPolys.begin_color[debugPolys.begin_vertices][2] = b;
3401         debugPolys.begin_color[debugPolys.begin_vertices][3] = a;
3402         debugPolys.begin_vertices++;
3403 }
3404
3405 void Debug_PolygonEnd(void)
3406 {
3407         if (!debugPolys.begin_active)
3408         {
3409                 Con_Printf("Debug_PolygonEnd: Debug_PolygonBegin wasn't called\n");
3410                 return;
3411         }
3412         debugPolys.begin_active = false;
3413         if (debugPolys.begin_vertices >= 3)
3414                 VMPolygons_Store(&debugPolys);
3415         else
3416                 Con_Printf("Debug_PolygonEnd: %i vertices isn't a good choice\n", debugPolys.begin_vertices);
3417 }
3418
3419 /*
3420 =============
3421 CL_CheckBottom
3422
3423 Returns false if any part of the bottom of the entity is off an edge that
3424 is not a staircase.
3425
3426 =============
3427 */
3428 static qboolean CL_CheckBottom (prvm_edict_t *ent)
3429 {
3430         prvm_prog_t *prog = CLVM_prog;
3431         vec3_t  mins, maxs, start, stop;
3432         trace_t trace;
3433         int             x, y;
3434         float   mid, bottom;
3435
3436         VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
3437         VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
3438
3439 // if all of the points under the corners are solid world, don't bother
3440 // with the tougher checks
3441 // the corners must be within 16 of the midpoint
3442         start[2] = mins[2] - 1;
3443         for     (x=0 ; x<=1 ; x++)
3444                 for     (y=0 ; y<=1 ; y++)
3445                 {
3446                         start[0] = x ? maxs[0] : mins[0];
3447                         start[1] = y ? maxs[1] : mins[1];
3448                         if (!(CL_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
3449                                 goto realcheck;
3450                 }
3451
3452         return true;            // we got out easy
3453
3454 realcheck:
3455 //
3456 // check it for real...
3457 //
3458         start[2] = mins[2];
3459
3460 // the midpoint must be within 16 of the bottom
3461         start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
3462         start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
3463         stop[2] = start[2] - 2*sv_stepheight.value;
3464         trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true, false);
3465
3466         if (trace.fraction == 1.0)
3467                 return false;
3468         mid = bottom = trace.endpos[2];
3469
3470 // the corners must be within 16 of the midpoint
3471         for     (x=0 ; x<=1 ; x++)
3472                 for     (y=0 ; y<=1 ; y++)
3473                 {
3474                         start[0] = stop[0] = x ? maxs[0] : mins[0];
3475                         start[1] = stop[1] = y ? maxs[1] : mins[1];
3476
3477                         trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true, false);
3478
3479                         if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
3480                                 bottom = trace.endpos[2];
3481                         if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
3482                                 return false;
3483                 }
3484
3485         return true;
3486 }
3487
3488 /*
3489 =============
3490 CL_movestep
3491
3492 Called by monster program code.
3493 The move will be adjusted for slopes and stairs, but if the move isn't
3494 possible, no move is done and false is returned
3495 =============
3496 */
3497 static qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean settrace)
3498 {
3499         prvm_prog_t *prog = CLVM_prog;
3500         float           dz;
3501         vec3_t          oldorg, neworg, end, traceendpos;
3502         trace_t         trace;
3503         int                     i, svent;
3504         prvm_edict_t            *enemy;
3505
3506 // try the move
3507         VectorCopy (PRVM_clientedictvector(ent, origin), oldorg);
3508         VectorAdd (PRVM_clientedictvector(ent, origin), move, neworg);
3509
3510 // flying monsters don't step up
3511         if ( (int)PRVM_clientedictfloat(ent, flags) & (FL_SWIM | FL_FLY) )
3512         {
3513         // try one move with vertical motion, then one without
3514                 for (i=0 ; i<2 ; i++)
3515                 {
3516                         VectorAdd (PRVM_clientedictvector(ent, origin), move, neworg);
3517                         enemy = PRVM_PROG_TO_EDICT(PRVM_clientedictedict(ent, enemy));
3518                         if (i == 0 && enemy != prog->edicts)
3519                         {
3520                                 dz = PRVM_clientedictvector(ent, origin)[2] - PRVM_clientedictvector(PRVM_PROG_TO_EDICT(PRVM_clientedictedict(ent, enemy)), origin)[2];
3521                                 if (dz > 40)
3522                                         neworg[2] -= 8;
3523                                 if (dz < 30)
3524                                         neworg[2] += 8;
3525                         }
3526                         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);
3527                         if (settrace)
3528                                 CL_VM_SetTraceGlobals(prog, &trace, svent);
3529
3530                         if (trace.fraction == 1)
3531                         {
3532                                 VectorCopy(trace.endpos, traceendpos);
3533                                 if (((int)PRVM_clientedictfloat(ent, flags) & FL_SWIM) && !(CL_PointSuperContents(traceendpos) & SUPERCONTENTS_LIQUIDSMASK))
3534                                         return false;   // swim monster left water
3535
3536                                 VectorCopy (traceendpos, PRVM_clientedictvector(ent, origin));
3537                                 if (relink)
3538                                         CL_LinkEdict(ent);
3539                                 return true;
3540                         }
3541
3542                         if (enemy == prog->edicts)
3543                                 break;
3544                 }
3545
3546                 return false;
3547         }
3548
3549 // push down from a step height above the wished position
3550         neworg[2] += sv_stepheight.value;
3551         VectorCopy (neworg, end);
3552         end[2] -= sv_stepheight.value*2;
3553
3554         trace = CL_TraceBox(neworg, PRVM_clientedictvector(ent, mins), PRVM_clientedictvector(ent, maxs), end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
3555         if (settrace)
3556                 CL_VM_SetTraceGlobals(prog, &trace, svent);
3557
3558         if (trace.startsolid)
3559         {
3560                 neworg[2] -= sv_stepheight.value;
3561                 trace = CL_TraceBox(neworg, PRVM_clientedictvector(ent, mins), PRVM_clientedictvector(ent, maxs), end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
3562                 if (settrace)
3563                         CL_VM_SetTraceGlobals(prog, &trace, svent);
3564                 if (trace.startsolid)
3565                         return false;
3566         }
3567         if (trace.fraction == 1)
3568         {
3569         // if monster had the ground pulled out, go ahead and fall
3570                 if ( (int)PRVM_clientedictfloat(ent, flags) & FL_PARTIALGROUND )
3571                 {
3572                         VectorAdd (PRVM_clientedictvector(ent, origin), move, PRVM_clientedictvector(ent, origin));
3573                         if (relink)
3574                                 CL_LinkEdict(ent);
3575                         PRVM_clientedictfloat(ent, flags) = (int)PRVM_clientedictfloat(ent, flags) & ~FL_ONGROUND;
3576                         return true;
3577                 }
3578
3579                 return false;           // walked off an edge
3580         }
3581
3582 // check point traces down for dangling corners
3583         VectorCopy (trace.endpos, PRVM_clientedictvector(ent, origin));
3584
3585         if (!CL_CheckBottom (ent))
3586         {
3587                 if ( (int)PRVM_clientedictfloat(ent, flags) & FL_PARTIALGROUND )
3588                 {       // entity had floor mostly pulled out from underneath it
3589                         // and is trying to correct
3590                         if (relink)
3591                                 CL_LinkEdict(ent);
3592                         return true;
3593                 }
3594                 VectorCopy (oldorg, PRVM_clientedictvector(ent, origin));
3595                 return false;
3596         }
3597
3598         if ( (int)PRVM_clientedictfloat(ent, flags) & FL_PARTIALGROUND )
3599                 PRVM_clientedictfloat(ent, flags) = (int)PRVM_clientedictfloat(ent, flags) & ~FL_PARTIALGROUND;
3600
3601         PRVM_clientedictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
3602
3603 // the move is ok
3604         if (relink)
3605                 CL_LinkEdict(ent);
3606         return true;
3607 }
3608
3609 /*
3610 ===============
3611 VM_CL_walkmove
3612
3613 float(float yaw, float dist[, settrace]) walkmove
3614 ===============
3615 */
3616 static void VM_CL_walkmove (prvm_prog_t *prog)
3617 {
3618         prvm_edict_t    *ent;
3619         float   yaw, dist;
3620         vec3_t  move;
3621         mfunction_t     *oldf;
3622         int     oldself;
3623         qboolean        settrace;
3624
3625         VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_walkmove);
3626
3627         // assume failure if it returns early
3628         PRVM_G_FLOAT(OFS_RETURN) = 0;
3629
3630         ent = PRVM_PROG_TO_EDICT(PRVM_clientglobaledict(self));
3631         if (ent == prog->edicts)
3632         {
3633                 VM_Warning(prog, "walkmove: can not modify world entity\n");
3634                 return;
3635         }
3636         if (ent->priv.server->free)
3637         {
3638                 VM_Warning(prog, "walkmove: can not modify free entity\n");
3639                 return;
3640         }
3641         yaw = PRVM_G_FLOAT(OFS_PARM0);
3642         dist = PRVM_G_FLOAT(OFS_PARM1);
3643         settrace = prog->argc >= 3 && PRVM_G_FLOAT(OFS_PARM2);
3644
3645         if ( !( (int)PRVM_clientedictfloat(ent, flags) & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
3646                 return;
3647
3648         yaw = yaw*M_PI*2 / 360;
3649
3650         move[0] = cos(yaw)*dist;
3651         move[1] = sin(yaw)*dist;
3652         move[2] = 0;
3653
3654 // save program state, because CL_movestep may call other progs
3655         oldf = prog->xfunction;
3656         oldself = PRVM_clientglobaledict(self);
3657
3658         PRVM_G_FLOAT(OFS_RETURN) = CL_movestep(ent, move, true, false, settrace);
3659
3660
3661 // restore program state
3662         prog->xfunction = oldf;
3663         PRVM_clientglobaledict(self) = oldself;
3664 }
3665
3666 /*
3667 ===============
3668 VM_CL_serverkey
3669
3670 string(string key) serverkey
3671 ===============
3672 */
3673 static void VM_CL_serverkey(prvm_prog_t *prog)
3674 {
3675         char string[VM_STRINGTEMP_LENGTH];
3676         VM_SAFEPARMCOUNT(1, VM_CL_serverkey);
3677         InfoString_GetValue(cl.qw_serverinfo, PRVM_G_STRING(OFS_PARM0), string, sizeof(string));
3678         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
3679 }
3680
3681 /*
3682 =================
3683 VM_CL_checkpvs
3684
3685 Checks if an entity is in a point's PVS.
3686 Should be fast but can be inexact.
3687
3688 float checkpvs(vector viewpos, entity viewee) = #240;
3689 =================
3690 */
3691 static void VM_CL_checkpvs (prvm_prog_t *prog)
3692 {
3693         vec3_t viewpos;
3694         prvm_edict_t *viewee;
3695         vec3_t mi, ma;
3696 #if 1
3697         unsigned char *pvs;
3698 #else
3699         int fatpvsbytes;
3700         unsigned char fatpvs[MAX_MAP_LEAFS/8];
3701 #endif
3702
3703         VM_SAFEPARMCOUNT(2, VM_SV_checkpvs);
3704         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), viewpos);
3705         viewee = PRVM_G_EDICT(OFS_PARM1);
3706
3707         if(viewee->priv.required->free)
3708         {
3709                 VM_Warning(prog, "checkpvs: can not check free entity\n");
3710                 PRVM_G_FLOAT(OFS_RETURN) = 4;
3711                 return;
3712         }
3713
3714         VectorAdd(PRVM_serveredictvector(viewee, origin), PRVM_serveredictvector(viewee, mins), mi);
3715         VectorAdd(PRVM_serveredictvector(viewee, origin), PRVM_serveredictvector(viewee, maxs), ma);
3716
3717 #if 1
3718         if(!sv.worldmodel->brush.GetPVS || !sv.worldmodel->brush.BoxTouchingPVS)
3719         {
3720                 // no PVS support on this worldmodel... darn
3721                 PRVM_G_FLOAT(OFS_RETURN) = 3;
3722                 return;
3723         }
3724         pvs = sv.worldmodel->brush.GetPVS(sv.worldmodel, viewpos);
3725         if(!pvs)
3726         {
3727                 // viewpos isn't in any PVS... darn
3728                 PRVM_G_FLOAT(OFS_RETURN) = 2;
3729                 return;
3730         }
3731         PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, pvs, mi, ma);
3732 #else
3733         // using fat PVS like FTEQW does (slow)
3734         if(!sv.worldmodel->brush.FatPVS || !sv.worldmodel->brush.BoxTouchingPVS)
3735         {
3736                 // no PVS support on this worldmodel... darn
3737                 PRVM_G_FLOAT(OFS_RETURN) = 3;
3738                 return;
3739         }
3740         fatpvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, viewpos, 8, fatpvs, sizeof(fatpvs), false);
3741         if(!fatpvsbytes)
3742         {
3743                 // viewpos isn't in any PVS... darn
3744                 PRVM_G_FLOAT(OFS_RETURN) = 2;
3745                 return;
3746         }
3747         PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, fatpvs, mi, ma);
3748 #endif
3749 }
3750
3751 // #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.
3752 static void VM_CL_skel_create(prvm_prog_t *prog)
3753 {
3754         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
3755         dp_model_t *model = CL_GetModelByIndex(modelindex);
3756         skeleton_t *skeleton;
3757         int i;
3758         PRVM_G_FLOAT(OFS_RETURN) = 0;
3759         if (!model || !model->num_bones)
3760                 return;
3761         for (i = 0;i < MAX_EDICTS;i++)
3762                 if (!prog->skeletons[i])
3763                         break;
3764         if (i == MAX_EDICTS)
3765                 return;
3766         prog->skeletons[i] = skeleton = (skeleton_t *)Mem_Alloc(cls.levelmempool, sizeof(skeleton_t) + model->num_bones * sizeof(matrix4x4_t));
3767         PRVM_G_FLOAT(OFS_RETURN) = i + 1;
3768         skeleton->model = model;
3769         skeleton->relativetransforms = (matrix4x4_t *)(skeleton+1);
3770         // initialize to identity matrices
3771         for (i = 0;i < skeleton->model->num_bones;i++)
3772                 skeleton->relativetransforms[i] = identitymatrix;
3773 }
3774
3775 // #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
3776 static void VM_CL_skel_build(prvm_prog_t *prog)
3777 {
3778         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3779         skeleton_t *skeleton;
3780         prvm_edict_t *ed = PRVM_G_EDICT(OFS_PARM1);
3781         int modelindex = (int)PRVM_G_FLOAT(OFS_PARM2);
3782         float retainfrac = PRVM_G_FLOAT(OFS_PARM3);
3783         int firstbone = PRVM_G_FLOAT(OFS_PARM4) - 1;
3784         int lastbone = PRVM_G_FLOAT(OFS_PARM5) - 1;
3785         dp_model_t *model = CL_GetModelByIndex(modelindex);
3786         float blendfrac;
3787         int numblends;
3788         int bonenum;
3789         int blendindex;
3790         framegroupblend_t framegroupblend[MAX_FRAMEGROUPBLENDS];
3791         frameblend_t frameblend[MAX_FRAMEBLENDS];
3792         matrix4x4_t blendedmatrix;
3793         matrix4x4_t matrix;
3794         PRVM_G_FLOAT(OFS_RETURN) = 0;
3795         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3796                 return;
3797         firstbone = max(0, firstbone);
3798         lastbone = min(lastbone, model->num_bones - 1);
3799         lastbone = min(lastbone, skeleton->model->num_bones - 1);
3800         VM_GenerateFrameGroupBlend(prog, framegroupblend, ed);
3801         VM_FrameBlendFromFrameGroupBlend(frameblend, framegroupblend, model);
3802         blendfrac = 1.0f - retainfrac;
3803         for (numblends = 0;numblends < MAX_FRAMEBLENDS && frameblend[numblends].lerp;numblends++)
3804                 frameblend[numblends].lerp *= blendfrac;
3805         for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
3806         {
3807                 memset(&blendedmatrix, 0, sizeof(blendedmatrix));
3808                 Matrix4x4_Accumulate(&blendedmatrix, &skeleton->relativetransforms[bonenum], retainfrac);
3809                 for (blendindex = 0;blendindex < numblends;blendindex++)
3810                 {
3811                         Matrix4x4_FromBonePose6s(&matrix, model->num_posescale, model->data_poses6s + 6 * (frameblend[blendindex].subframe * model->num_bones + bonenum));
3812                         Matrix4x4_Accumulate(&blendedmatrix, &matrix, frameblend[blendindex].lerp);
3813                 }
3814                 skeleton->relativetransforms[bonenum] = blendedmatrix;
3815         }
3816         PRVM_G_FLOAT(OFS_RETURN) = skeletonindex + 1;
3817 }
3818
3819 // #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
3820 static void VM_CL_skel_get_numbones(prvm_prog_t *prog)
3821 {
3822         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3823         skeleton_t *skeleton;
3824         PRVM_G_FLOAT(OFS_RETURN) = 0;
3825         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3826                 return;
3827         PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->num_bones;
3828 }
3829
3830 // #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
3831 static void VM_CL_skel_get_bonename(prvm_prog_t *prog)
3832 {
3833         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3834         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3835         skeleton_t *skeleton;
3836         PRVM_G_INT(OFS_RETURN) = 0;
3837         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3838                 return;
3839         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3840                 return;
3841         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, skeleton->model->data_bones[bonenum].name);
3842 }
3843
3844 // #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)
3845 static void VM_CL_skel_get_boneparent(prvm_prog_t *prog)
3846 {
3847         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3848         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3849         skeleton_t *skeleton;
3850         PRVM_G_FLOAT(OFS_RETURN) = 0;
3851         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3852                 return;
3853         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3854                 return;
3855         PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->data_bones[bonenum].parent + 1;
3856 }
3857
3858 // #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
3859 static void VM_CL_skel_find_bone(prvm_prog_t *prog)
3860 {
3861         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3862         const char *tagname = PRVM_G_STRING(OFS_PARM1);
3863         skeleton_t *skeleton;
3864         PRVM_G_FLOAT(OFS_RETURN) = 0;
3865         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3866                 return;
3867         PRVM_G_FLOAT(OFS_RETURN) = Mod_Alias_GetTagIndexForName(skeleton->model, 0, tagname);
3868 }
3869
3870 // #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
3871 static void VM_CL_skel_get_bonerel(prvm_prog_t *prog)
3872 {
3873         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3874         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3875         skeleton_t *skeleton;
3876         matrix4x4_t matrix;
3877         vec3_t forward, left, up, origin;
3878         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
3879         VectorClear(PRVM_clientglobalvector(v_forward));
3880         VectorClear(PRVM_clientglobalvector(v_right));
3881         VectorClear(PRVM_clientglobalvector(v_up));
3882         if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3883                 return;
3884         if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3885                 return;
3886         matrix = skeleton->relativetransforms[bonenum];
3887         Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
3888         VectorCopy(forward, PRVM_clientglobalvector(v_forward));
3889         VectorNegate(left, PRVM_clientglobalvector(v_right));
3890         VectorCopy(up, PRVM_clientglobalvector(v_up));
3891         VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
3892 }
3893
3894 // #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
3895 static void VM_CL_skel_get_boneabs(prvm_prog_t *prog)
3896 {
3897         int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3898         int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3899         skeleton_t *skeleton;
3900         matrix4x4_t matrix;
3901         matrix4x4_t temp;
3902         vec3_t forward, left, up, origin;
3903         VectorClear(PRVM_G_VECTOR(OFS_RETURN));