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