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