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