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