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