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