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