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