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