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