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