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