ea2471e4415929d6785345c70d7ea16b8e5c2e62
[xonotic/darkplaces.git] / csprogs.c
1 #include "quakedef.h"
2 #include "progsvm.h"
3 #include "clprogdefs.h"
4 #include "csprogs.h"
5 #include "cl_collision.h"
6 #include "snd_main.h"
7 #include "clvm_cmds.h"
8 #include "prvm_cmds.h"
9
10 //============================================================================
11 // Client prog handling
12 //[515]: omg !!! optimize it ! a lot of hacks here and there also :P
13
14 #define CSQC_RETURNVAL  prog->globals.generic[OFS_RETURN]
15 #define CSQC_BEGIN              csqc_tmpprog=prog;prog=0;PRVM_SetProg(PRVM_CLIENTPROG);
16 #define CSQC_END                prog=csqc_tmpprog;
17
18 static prvm_prog_t *csqc_tmpprog;
19
20 void CL_VM_PreventInformationLeaks(void)
21 {
22         if(!cl.csqc_loaded)
23                 return;
24         CSQC_BEGIN
25                 VM_ClearTraceGlobals();
26                 PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.trace_networkentity) = 0;
27         CSQC_END
28 }
29
30 //[515]: these are required funcs
31 static const char *cl_required_func[] =
32 {
33         "CSQC_Init",
34         "CSQC_InputEvent",
35         "CSQC_UpdateView",
36         "CSQC_ConsoleCommand",
37 };
38
39 static int cl_numrequiredfunc = sizeof(cl_required_func) / sizeof(char*);
40
41 void CL_VM_Error (const char *format, ...) DP_FUNC_PRINTF(1);
42 void CL_VM_Error (const char *format, ...)      //[515]: hope it will be never executed =)
43 {
44         char errorstring[4096];
45         va_list argptr;
46
47         va_start (argptr, format);
48         dpvsnprintf (errorstring, sizeof(errorstring), format, argptr);
49         va_end (argptr);
50 //      Con_Printf( "CL_VM_Error: %s\n", errorstring );
51
52         PRVM_Crash();
53         cl.csqc_loaded = false;
54
55         Cvar_SetValueQuick(&csqc_progcrc, -1);
56         Cvar_SetValueQuick(&csqc_progsize, -1);
57
58 //      Host_AbortCurrentFrame();       //[515]: hmmm... if server says it needs csqc then client MUST disconnect
59         Host_Error("CL_VM_Error: %s", errorstring);
60 }
61 void CL_VM_UpdateDmgGlobals (int dmg_take, int dmg_save, vec3_t dmg_origin)
62 {
63         if(cl.csqc_loaded)
64         {
65                 CSQC_BEGIN
66                 PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.dmg_take) = dmg_take;
67                 PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.dmg_save) = dmg_save;
68                 VectorCopy(dmg_origin, PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.dmg_origin));
69                 CSQC_END
70         }
71 }
72
73 void CSQC_UpdateNetworkTimes(double newtime, double oldtime)
74 {
75         if(!cl.csqc_loaded)
76                 return;
77         CSQC_BEGIN
78         PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.servertime) = newtime;
79         PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.serverprevtime) = oldtime;
80         PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.serverdeltatime) = newtime - oldtime;
81         CSQC_END
82 }
83
84 //[515]: set globals before calling R_UpdateView, WEIRD CRAP
85 static void CSQC_SetGlobals (void)
86 {
87         CSQC_BEGIN
88                 prog->globals.client->time = cl.time;
89                 prog->globals.client->frametime = max(0, cl.time - cl.oldtime);
90                 prog->globals.client->servercommandframe = cls.servermovesequence;
91                 prog->globals.client->clientcommandframe = cl.movecmd[0].sequence;
92                 VectorCopy(cl.viewangles, prog->globals.client->input_angles);
93                 // // FIXME: this actually belongs into getinputstate().. [12/17/2007 Black]
94                 prog->globals.client->input_buttons = cl.movecmd[0].buttons;
95                 VectorSet(prog->globals.client->input_movevalues, cl.movecmd[0].forwardmove, cl.movecmd[0].sidemove, cl.movecmd[0].upmove);
96                 VectorCopy(cl.csqc_vieworiginfromengine, cl.csqc_vieworigin);
97                 VectorCopy(cl.csqc_viewanglesfromengine, cl.csqc_viewangles);
98
99                 // LordHavoc: Spike says not to do this, but without pmove_org the
100                 // CSQC is useless as it can't alter the view origin without
101                 // completely replacing it
102                 Matrix4x4_OriginFromMatrix(&cl.entities[cl.viewentity].render.matrix, prog->globals.client->pmove_org);
103                 VectorCopy(cl.movement_velocity, prog->globals.client->pmove_vel);
104                 PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.pmove_onground) = cl.onground;
105                 PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.pmove_inwater) = cl.inwater;
106
107                 VectorCopy(cl.viewangles, PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.view_angles));
108                 VectorCopy(cl.punchangle, PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.view_punchangle));
109                 VectorCopy(cl.punchvector, PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.view_punchvector));
110                 prog->globals.client->maxclients = cl.maxclients;
111         CSQC_END
112 }
113
114 void CSQC_Predraw (prvm_edict_t *ed)
115 {
116         int b;
117         if(!ed->fields.client->predraw)
118                 return;
119         b = prog->globals.client->self;
120         prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
121         PRVM_ExecuteProgram(ed->fields.client->predraw, "CSQC_Predraw: NULL function\n");
122         prog->globals.client->self = b;
123 }
124
125 void CSQC_Think (prvm_edict_t *ed)
126 {
127         int b;
128         if(ed->fields.client->think)
129         if(ed->fields.client->nextthink && ed->fields.client->nextthink <= prog->globals.client->time)
130         {
131                 ed->fields.client->nextthink = 0;
132                 b = prog->globals.client->self;
133                 prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
134                 PRVM_ExecuteProgram(ed->fields.client->think, "CSQC_Think: NULL function\n");
135                 prog->globals.client->self = b;
136         }
137 }
138
139 extern cvar_t cl_noplayershadow;
140 extern cvar_t r_equalize_entities_fullbright;
141 qboolean CSQC_AddRenderEdict(prvm_edict_t *ed, int edictnum)
142 {
143         int renderflags;
144         int c;
145         float scale;
146         entity_render_t *entrender;
147         dp_model_t *model;
148
149         model = CL_GetModelFromEdict(ed);
150         if (!model)
151                 return false;
152
153         if (edictnum)
154         {
155                 if (r_refdef.scene.numentities >= r_refdef.scene.maxentities)
156                         return false;
157                 entrender = cl.csqcrenderentities + edictnum;
158                 r_refdef.scene.entities[r_refdef.scene.numentities++] = entrender;
159                 entrender->entitynumber = edictnum + MAX_EDICTS;
160                 //entrender->shadertime = 0; // shadertime was set by spawn()
161                 entrender->flags = 0;
162                 entrender->alpha = 1;
163                 entrender->scale = 1;
164                 VectorSet(entrender->colormod, 1, 1, 1);
165                 VectorSet(entrender->glowmod, 1, 1, 1);
166                 entrender->allowdecals = true;
167         }
168         else
169         {
170                 entrender = CL_NewTempEntity(0);
171                 if (!entrender)
172                         return false;
173         }
174
175         entrender->userwavefunc_param[0] = PRVM_EDICTFIELDFLOAT(ed, prog->fieldoffsets.userwavefunc_param0);
176         entrender->userwavefunc_param[1] = PRVM_EDICTFIELDFLOAT(ed, prog->fieldoffsets.userwavefunc_param1);
177         entrender->userwavefunc_param[2] = PRVM_EDICTFIELDFLOAT(ed, prog->fieldoffsets.userwavefunc_param2);
178         entrender->userwavefunc_param[3] = PRVM_EDICTFIELDFLOAT(ed, prog->fieldoffsets.userwavefunc_param3);
179
180         entrender->model = model;
181         entrender->skinnum = (int)ed->fields.client->skin;
182         entrender->effects |= entrender->model->effects;
183         renderflags = (int)PRVM_EDICTFIELDFLOAT(ed, prog->fieldoffsets.renderflags);
184         entrender->alpha = PRVM_EDICTFIELDFLOAT(ed, prog->fieldoffsets.alpha);
185         entrender->scale = scale = PRVM_EDICTFIELDFLOAT(ed, prog->fieldoffsets.scale);
186         VectorCopy(PRVM_EDICTFIELDVECTOR(ed, prog->fieldoffsets.colormod), entrender->colormod);
187         VectorCopy(PRVM_EDICTFIELDVECTOR(ed, prog->fieldoffsets.glowmod), entrender->glowmod);
188         if(ed->fields.client->effects)  entrender->effects |= (int)ed->fields.client->effects;
189         if (!entrender->alpha)
190                 entrender->alpha = 1.0f;
191         if (!entrender->scale)
192                 entrender->scale = scale = 1.0f;
193         if (!VectorLength2(entrender->colormod))
194                 VectorSet(entrender->colormod, 1, 1, 1);
195         if (!VectorLength2(entrender->glowmod))
196                 VectorSet(entrender->glowmod, 1, 1, 1);
197
198         // LordHavoc: use the CL_GetTagMatrix function on self to ensure consistent behavior (duplicate code would be bad)
199         CL_GetTagMatrix(&entrender->matrix, ed, 0);
200
201         // set up the animation data
202         VM_GenerateFrameGroupBlend(ed->priv.server->framegroupblend, ed);
203         VM_FrameBlendFromFrameGroupBlend(ed->priv.server->frameblend, ed->priv.server->framegroupblend, model);
204         VM_UpdateEdictSkeleton(ed, model, ed->priv.server->frameblend);
205         entrender->shadertime = PRVM_EDICTFIELDFLOAT(ed, prog->fieldoffsets.shadertime);
206
207         // transparent offset
208         if (renderflags & RF_USETRANSPARENTOFFSET)
209                 entrender->transparent_offset = PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.transparent_offset);
210
211         if(renderflags)
212         {
213                 if(renderflags & RF_VIEWMODEL)  entrender->flags |= RENDER_VIEWMODEL | RENDER_NODEPTHTEST;
214                 if(renderflags & RF_EXTERNALMODEL)entrender->flags |= RENDER_EXTERIORMODEL;
215                 if(renderflags & RF_NOCULL)             entrender->flags |= RENDER_NOCULL;
216                 if(renderflags & RF_DEPTHHACK)  entrender->flags |= RENDER_NODEPTHTEST;
217                 if(renderflags & RF_ADDITIVE)           entrender->flags |= RENDER_ADDITIVE;
218         }
219
220         c = (int)ed->fields.client->colormap;
221         if (c <= 0)
222                 CL_SetEntityColormapColors(entrender, -1);
223         else if (c <= cl.maxclients && cl.scores != NULL)
224                 CL_SetEntityColormapColors(entrender, cl.scores[c-1].colors);
225         else
226                 CL_SetEntityColormapColors(entrender, c);
227
228         entrender->flags &= ~(RENDER_SHADOW | RENDER_LIGHT | RENDER_NOSELFSHADOW);
229         // either fullbright or lit
230         if(!r_fullbright.integer)
231         {
232                 if (!(entrender->effects & EF_FULLBRIGHT) && !(renderflags & RF_FULLBRIGHT))
233                         entrender->flags |= RENDER_LIGHT;
234                 else if(r_equalize_entities_fullbright.integer)
235                         entrender->flags |= RENDER_LIGHT | RENDER_EQUALIZE;
236         }
237         // hide player shadow during intermission or nehahra movie
238         if (!(entrender->effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST))
239          &&  (entrender->alpha >= 1)
240          && !(renderflags & RF_NOSHADOW)
241          && !(entrender->flags & RENDER_VIEWMODEL)
242          && (!(entrender->flags & RENDER_EXTERIORMODEL) || (!cl.intermission && cls.protocol != PROTOCOL_NEHAHRAMOVIE && !cl_noplayershadow.integer)))
243                 entrender->flags |= RENDER_SHADOW;
244         if (entrender->flags & RENDER_VIEWMODEL)
245                 entrender->flags |= RENDER_NOSELFSHADOW;
246         if (entrender->effects & EF_NOSELFSHADOW)
247                 entrender->flags |= RENDER_NOSELFSHADOW;
248         if (entrender->effects & EF_NODEPTHTEST)
249                 entrender->flags |= RENDER_NODEPTHTEST;
250         if (entrender->effects & EF_ADDITIVE)
251                 entrender->flags |= RENDER_ADDITIVE;
252         if (entrender->effects & EF_DOUBLESIDED)
253                 entrender->flags |= RENDER_DOUBLESIDED;
254
255         // make the other useful stuff
256         memcpy(entrender->framegroupblend, ed->priv.server->framegroupblend, sizeof(ed->priv.server->framegroupblend));
257         CL_UpdateRenderEntity(entrender);
258         // override animation data with full control
259         memcpy(entrender->frameblend, ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend));
260         if (ed->priv.server->skeleton.relativetransforms)
261                 entrender->skeleton = &ed->priv.server->skeleton;
262         else
263                 entrender->skeleton = NULL;
264
265         return true;
266 }
267
268 qboolean CL_VM_InputEvent (qboolean down, int key, int ascii)
269 {
270         qboolean r;
271
272         if(!cl.csqc_loaded)
273                 return false;
274
275         CSQC_BEGIN
276                 if (!prog->funcoffsets.CSQC_InputEvent)
277                         r = false;
278                 else
279                 {
280                         prog->globals.client->time = cl.time;
281                         prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
282                         PRVM_G_FLOAT(OFS_PARM0) = !down; // 0 is down, 1 is up
283                         PRVM_G_FLOAT(OFS_PARM1) = key;
284                         PRVM_G_FLOAT(OFS_PARM2) = ascii;
285                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_InputEvent, "QC function CSQC_InputEvent is missing");
286                         r = CSQC_RETURNVAL != 0;
287                 }
288         CSQC_END
289         return r;
290 }
291
292 qboolean CL_VM_UpdateView (void)
293 {
294         vec3_t emptyvector;
295         emptyvector[0] = 0;
296         emptyvector[1] = 0;
297         emptyvector[2] = 0;
298 //      vec3_t oldangles;
299         if(!cl.csqc_loaded)
300                 return false;
301         R_TimeReport("pre-UpdateView");
302         CSQC_BEGIN
303                 //VectorCopy(cl.viewangles, oldangles);
304                 prog->globals.client->time = cl.time;
305                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
306                 CSQC_SetGlobals();
307                 // clear renderable entity and light lists to prevent crashes if the
308                 // CSQC_UpdateView function does not call R_ClearScene as it should
309                 r_refdef.scene.numentities = 0;
310                 r_refdef.scene.numlights = 0;
311                 // pass in width and height as parameters (EXT_CSQC_1)
312                 PRVM_G_FLOAT(OFS_PARM0) = vid.width;
313                 PRVM_G_FLOAT(OFS_PARM1) = vid.height;
314                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_UpdateView, "QC function CSQC_UpdateView is missing");
315                 //VectorCopy(oldangles, cl.viewangles);
316                 // Dresk : Reset Dmg Globals Here
317                 CL_VM_UpdateDmgGlobals(0, 0, emptyvector);
318         CSQC_END
319         R_TimeReport("UpdateView");
320         return true;
321 }
322
323 extern sizebuf_t vm_tempstringsbuf;
324 qboolean CL_VM_ConsoleCommand (const char *cmd)
325 {
326         int restorevm_tempstringsbuf_cursize;
327         qboolean r = false;
328         if(!cl.csqc_loaded)
329                 return false;
330         CSQC_BEGIN
331         if (prog->funcoffsets.CSQC_ConsoleCommand)
332         {
333                 prog->globals.client->time = cl.time;
334                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
335                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
336                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(cmd);
337                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_ConsoleCommand, "QC function CSQC_ConsoleCommand is missing");
338                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
339                 r = CSQC_RETURNVAL != 0;
340         }
341         CSQC_END
342         return r;
343 }
344
345 qboolean CL_VM_Parse_TempEntity (void)
346 {
347         int                     t;
348         qboolean        r = false;
349         if(!cl.csqc_loaded)
350                 return false;
351         CSQC_BEGIN
352         if(prog->funcoffsets.CSQC_Parse_TempEntity)
353         {
354                 t = msg_readcount;
355                 prog->globals.client->time = cl.time;
356                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
357                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_TempEntity, "QC function CSQC_Parse_TempEntity is missing");
358                 r = CSQC_RETURNVAL != 0;
359                 if(!r)
360                 {
361                         msg_readcount = t;
362                         msg_badread = false;
363                 }
364         }
365         CSQC_END
366         return r;
367 }
368
369 void CL_VM_Parse_StuffCmd (const char *msg)
370 {
371         int restorevm_tempstringsbuf_cursize;
372         if(msg[0] == 'c')
373         if(msg[1] == 's')
374         if(msg[2] == 'q')
375         if(msg[3] == 'c')
376         {
377                 // if this is setting a csqc variable, deprotect csqc_progcrc
378                 // temporarily so that it can be set by the cvar command,
379                 // and then reprotect it afterwards
380                 int crcflags = csqc_progcrc.flags;
381                 int sizeflags = csqc_progcrc.flags;
382                 csqc_progcrc.flags &= ~CVAR_READONLY;
383                 csqc_progsize.flags &= ~CVAR_READONLY;
384                 Cmd_ExecuteString (msg, src_command);
385                 csqc_progcrc.flags = crcflags;
386                 csqc_progsize.flags = sizeflags;
387                 return;
388         }
389
390         if(cls.demoplayback)
391         if(!strncmp(msg, "curl --clear_autodownload\ncurl --pak --forthismap --as ", 55))
392         {
393                 // special handling for map download commands
394                 // run these commands IMMEDIATELY, instead of waiting for a client frame
395                 // that way, there is no black screen when playing back demos
396                 // I know this is a really ugly hack, but I can't think of any better way
397                 // FIXME find the actual CAUSE of this, and make demo playback WAIT
398                 // until all maps are loaded, then remove this hack
399
400                 char buf[MAX_INPUTLINE];
401                 const char *p, *q;
402                 size_t l;
403
404                 p = msg;
405
406                 for(;;)
407                 {
408                         q = strchr(p, '\n');
409                         if(q)
410                                 l = q - p;
411                         else
412                                 l = strlen(p);
413                         if(l > sizeof(buf) - 1)
414                                 l = sizeof(buf) - 1;
415                         strlcpy(buf, p, l + 1); // strlcpy needs a + 1 as it includes the newline!
416
417                         Cmd_ExecuteString(buf, src_command);
418
419                         p += l;
420                         if(*p == '\n')
421                                 ++p; // skip the newline and continue
422                         else
423                                 break; // end of string or overflow
424                 }
425                 Cmd_ExecuteString("curl --clear_autodownload", src_command); // don't inhibit CSQC loading
426                 return;
427         }
428
429         if(!cl.csqc_loaded)
430         {
431                 Cbuf_AddText(msg);
432                 return;
433         }
434         CSQC_BEGIN
435         if(prog->funcoffsets.CSQC_Parse_StuffCmd)
436         {
437                 prog->globals.client->time = cl.time;
438                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
439                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
440                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
441                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_StuffCmd, "QC function CSQC_Parse_StuffCmd is missing");
442                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
443         }
444         else
445                 Cbuf_AddText(msg);
446         CSQC_END
447 }
448
449 static void CL_VM_Parse_Print (const char *msg)
450 {
451         int restorevm_tempstringsbuf_cursize;
452         prog->globals.client->time = cl.time;
453         prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
454         restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
455         PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
456         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_Print, "QC function CSQC_Parse_Print is missing");
457         vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
458 }
459
460 void CSQC_AddPrintText (const char *msg)
461 {
462         size_t i;
463         if(!cl.csqc_loaded)
464         {
465                 Con_Print(msg);
466                 return;
467         }
468         CSQC_BEGIN
469         if(prog->funcoffsets.CSQC_Parse_Print)
470         {
471                 // FIXME: is this bugged?
472                 i = strlen(msg)-1;
473                 if(msg[i] != '\n' && msg[i] != '\r')
474                 {
475                         if(strlen(cl.csqc_printtextbuf)+i >= MAX_INPUTLINE)
476                         {
477                                 CL_VM_Parse_Print(cl.csqc_printtextbuf);
478                                 cl.csqc_printtextbuf[0] = 0;
479                         }
480                         else
481                                 strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
482                         return;
483                 }
484                 strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
485                 CL_VM_Parse_Print(cl.csqc_printtextbuf);
486                 cl.csqc_printtextbuf[0] = 0;
487         }
488         else
489                 Con_Print(msg);
490         CSQC_END
491 }
492
493 void CL_VM_Parse_CenterPrint (const char *msg)
494 {
495         int restorevm_tempstringsbuf_cursize;
496         if(!cl.csqc_loaded)
497         {
498                 SCR_CenterPrint(msg);
499                 return;
500         }
501         CSQC_BEGIN
502         if(prog->funcoffsets.CSQC_Parse_CenterPrint)
503         {
504                 prog->globals.client->time = cl.time;
505                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
506                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
507                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
508                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_CenterPrint, "QC function CSQC_Parse_CenterPrint is missing");
509                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
510         }
511         else
512                 SCR_CenterPrint(msg);
513         CSQC_END
514 }
515
516 void CL_VM_UpdateIntermissionState (int intermission)
517 {
518         if(cl.csqc_loaded)
519         {
520                 CSQC_BEGIN
521                 PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.intermission) = intermission;
522                 CSQC_END
523         }
524 }
525 void CL_VM_UpdateShowingScoresState (int showingscores)
526 {
527         if(cl.csqc_loaded)
528         {
529                 CSQC_BEGIN
530                 PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.sb_showscores) = showingscores;
531                 CSQC_END
532         }
533 }
534 qboolean CL_VM_Event_Sound(int sound_num, float volume, int channel, float attenuation, int ent, vec3_t pos)
535 {
536         qboolean r = false;
537         if(cl.csqc_loaded)
538         {
539                 CSQC_BEGIN
540                 if(prog->funcoffsets.CSQC_Event_Sound)
541                 {
542                         prog->globals.client->time = cl.time;
543                         prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
544                         PRVM_G_FLOAT(OFS_PARM0) = ent;
545                         PRVM_G_FLOAT(OFS_PARM1) = channel;
546                         PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(cl.sound_name[sound_num] );
547                         PRVM_G_FLOAT(OFS_PARM3) = volume;
548                         PRVM_G_FLOAT(OFS_PARM4) = attenuation;
549                         VectorCopy(pos, PRVM_G_VECTOR(OFS_PARM5) );
550                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Event_Sound, "QC function CSQC_Event_Sound is missing");
551                         r = CSQC_RETURNVAL != 0;
552                 }
553                 CSQC_END
554         }
555
556         return r;
557 }
558 void CL_VM_UpdateCoopDeathmatchGlobals (int gametype)
559 {
560         // Avoid global names for clean(er) coding
561         int localcoop;
562         int localdeathmatch;
563
564         if(cl.csqc_loaded)
565         {
566                 if(gametype == GAME_COOP)
567                 {
568                         localcoop = 1;
569                         localdeathmatch = 0;
570                 }
571                 else
572                 if(gametype == GAME_DEATHMATCH)
573                 {
574                         localcoop = 0;
575                         localdeathmatch = 1;
576                 }
577                 else
578                 {
579                         // How did the ServerInfo send an unknown gametype?
580                         // Better just assign the globals as 0...
581                         localcoop = 0;
582                         localdeathmatch = 0;
583                 }
584                 CSQC_BEGIN
585                 PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.coop) = localcoop;
586                 PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.deathmatch) = localdeathmatch;
587                 CSQC_END
588         }
589 }
590 float CL_VM_Event (float event)         //[515]: needed ? I'd say "YES", but don't know for what :D
591 {
592         float r = 0;
593         if(!cl.csqc_loaded)
594                 return 0;
595         CSQC_BEGIN
596         if(prog->funcoffsets.CSQC_Event)
597         {
598                 prog->globals.client->time = cl.time;
599                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
600                 PRVM_G_FLOAT(OFS_PARM0) = event;
601                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Event, "QC function CSQC_Event is missing");
602                 r = CSQC_RETURNVAL;
603         }
604         CSQC_END
605         return r;
606 }
607
608 void CSQC_ReadEntities (void)
609 {
610         unsigned short entnum, oldself, realentnum;
611         if(!cl.csqc_loaded)
612         {
613                 Host_Error ("CSQC_ReadEntities: CSQC is not loaded");
614                 return;
615         }
616
617         CSQC_BEGIN
618                 prog->globals.client->time = cl.time;
619                 oldself = prog->globals.client->self;
620                 while(1)
621                 {
622                         entnum = MSG_ReadShort();
623                         if(!entnum || msg_badread)
624                                 break;
625                         realentnum = entnum & 0x7FFF;
626                         prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum];
627                         if(entnum & 0x8000)
628                         {
629                                 if(prog->globals.client->self)
630                                 {
631                                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Remove, "QC function CSQC_Ent_Remove is missing");
632                                         cl.csqc_server2csqcentitynumber[realentnum] = 0;
633                                 }
634                                 else
635                                 {
636                                         // LordHavoc: removing an entity that is already gone on
637                                         // the csqc side is possible for legitimate reasons (such
638                                         // as a repeat of the remove message), so no warning is
639                                         // needed
640                                         //Con_Printf("Bad csqc_server2csqcentitynumber map\n"); //[515]: never happens ?
641                                 }
642                         }
643                         else
644                         {
645                                 if(!prog->globals.client->self)
646                                 {
647                                         if(!prog->funcoffsets.CSQC_Ent_Spawn)
648                                         {
649                                                 prvm_edict_t    *ed;
650                                                 ed = PRVM_ED_Alloc();
651                                                 ed->fields.client->entnum = realentnum;
652                                                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT_TO_PROG(ed);
653                                         }
654                                         else
655                                         {
656                                                 // entity( float entnum ) CSQC_Ent_Spawn;
657                                                 // the qc function should set entnum, too (this way it also can return world [2/1/2008 Andreas]
658                                                 PRVM_G_FLOAT(OFS_PARM0) = (float) realentnum;
659                                                 // make sure no one gets wrong ideas
660                                                 prog->globals.client->self = 0;
661                                                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Spawn, "QC function CSQC_Ent_Spawn is missing");
662                                                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT( PRVM_G_INT( OFS_RETURN ) );
663                                         }
664                                         PRVM_G_FLOAT(OFS_PARM0) = 1;
665                                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Update, "QC function CSQC_Ent_Update is missing");
666                                 }
667                                 else {
668                                         PRVM_G_FLOAT(OFS_PARM0) = 0;
669                                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Update, "QC function CSQC_Ent_Update is missing");
670                                 }
671                         }
672                 }
673                 prog->globals.client->self = oldself;
674         CSQC_END
675 }
676
677 void CL_VM_CB_BeginIncreaseEdicts(void)
678 {
679         // links don't survive the transition, so unlink everything
680         World_UnlinkAll(&cl.world);
681 }
682
683 void CL_VM_CB_EndIncreaseEdicts(void)
684 {
685         int i;
686         prvm_edict_t *ent;
687
688         // link every entity except world
689         for (i = 1, ent = prog->edicts;i < prog->num_edicts;i++, ent++)
690                 if (!ent->priv.server->free)
691                         CL_LinkEdict(ent);
692 }
693
694 void CL_VM_CB_InitEdict(prvm_edict_t *e)
695 {
696         int edictnum = PRVM_NUM_FOR_EDICT(e);
697         entity_render_t *entrender;
698         CL_ExpandCSQCRenderEntities(edictnum);
699         entrender = cl.csqcrenderentities + edictnum;
700         e->priv.server->move = false; // don't move on first frame
701         memset(entrender, 0, sizeof(*entrender));
702         entrender->shadertime = cl.time;
703 }
704
705 extern void R_DecalSystem_Reset(decalsystem_t *decalsystem);
706
707 void CL_VM_CB_FreeEdict(prvm_edict_t *ed)
708 {
709         entity_render_t *entrender = cl.csqcrenderentities + PRVM_NUM_FOR_EDICT(ed);
710         R_DecalSystem_Reset(&entrender->decalsystem);
711         memset(entrender, 0, sizeof(*entrender));
712         World_UnlinkEdict(ed);
713         memset(ed->fields.client, 0, sizeof(*ed->fields.client));
714         VM_RemoveEdictSkeleton(ed);
715         World_Physics_RemoveFromEntity(&cl.world, ed);
716         World_Physics_RemoveJointFromEntity(&cl.world, ed);
717 }
718
719 void CL_VM_CB_CountEdicts(void)
720 {
721         int             i;
722         prvm_edict_t    *ent;
723         int             active = 0, models = 0, solid = 0;
724
725         for (i=0 ; i<prog->num_edicts ; i++)
726         {
727                 ent = PRVM_EDICT_NUM(i);
728                 if (ent->priv.server->free)
729                         continue;
730                 active++;
731                 if (ent->fields.client->solid)
732                         solid++;
733                 if (ent->fields.client->model)
734                         models++;
735         }
736
737         Con_Printf("num_edicts:%3i\n", prog->num_edicts);
738         Con_Printf("active    :%3i\n", active);
739         Con_Printf("view      :%3i\n", models);
740         Con_Printf("touch     :%3i\n", solid);
741 }
742
743 qboolean CL_VM_CB_LoadEdict(prvm_edict_t *ent)
744 {
745         return true;
746 }
747
748 void Cmd_ClearCsqcFuncs (void);
749
750 // returns true if the packet is valid, false if end of file is reached
751 // used for dumping the CSQC download into demo files
752 qboolean MakeDownloadPacket(const char *filename, unsigned char *data, size_t len, int crc, int cnt, sizebuf_t *buf, int protocol)
753 {
754         int packetsize = buf->maxsize - 7; // byte short long
755         int npackets = (len + packetsize - 1) / (packetsize);
756
757         if(protocol == PROTOCOL_QUAKEWORLD)
758                 return false; // CSQC can't run in QW anyway
759
760         SZ_Clear(buf);
761         if(cnt == 0)
762         {
763                 MSG_WriteByte(buf, svc_stufftext);
764                 MSG_WriteString(buf, va("\ncl_downloadbegin %lu %s\n", (unsigned long)len, filename));
765                 return true;
766         }
767         else if(cnt >= 1 && cnt <= npackets)
768         {
769                 unsigned long thispacketoffset = (cnt - 1) * packetsize;
770                 int thispacketsize = len - thispacketoffset;
771                 if(thispacketsize > packetsize)
772                         thispacketsize = packetsize;
773
774                 MSG_WriteByte(buf, svc_downloaddata);
775                 MSG_WriteLong(buf, thispacketoffset);
776                 MSG_WriteShort(buf, thispacketsize);
777                 SZ_Write(buf, data + thispacketoffset, thispacketsize);
778
779                 return true;
780         }
781         else if(cnt == npackets + 1)
782         {
783                 MSG_WriteByte(buf, svc_stufftext);
784                 MSG_WriteString(buf, va("\ncl_downloadfinished %lu %d\n", (unsigned long)len, crc));
785                 return true;
786         }
787         return false;
788 }
789
790 #define CL_REQFIELDS (sizeof(cl_reqfields) / sizeof(prvm_required_field_t))
791
792 prvm_required_field_t cl_reqfields[] =
793 {
794         {ev_entity, "groundentity"},
795         {ev_entity, "tag_entity"},
796         {ev_float, "alpha"},
797         {ev_float, "dimension_hit"},
798         {ev_float, "dimension_solid"},
799         {ev_float, "dphitcontentsmask"},
800         {ev_float, "frame"},
801         {ev_float, "frame1time"},
802         {ev_float, "frame2"},
803         {ev_float, "frame2time"},
804         {ev_float, "frame3"},
805         {ev_float, "frame3time"},
806         {ev_float, "frame4"},
807         {ev_float, "frame4time"},
808         {ev_float, "gravity"},
809         {ev_float, "idealpitch"},
810         {ev_float, "lerpfrac"},
811         {ev_float, "lerpfrac3"},
812         {ev_float, "lerpfrac4"},
813         {ev_float, "movetype"}, // used by ODE code
814         {ev_float, "nextthink"},
815         {ev_float, "pitch_speed"},
816         {ev_float, "renderflags"},
817         {ev_float, "scale"},
818         {ev_float, "shadertime"},
819         {ev_float, "skeletonindex"},
820         {ev_float, "solid"}, // used by ODE code
821         {ev_float, "tag_index"},
822         {ev_float, "userwavefunc_param0"},
823         {ev_float, "userwavefunc_param1"},
824         {ev_float, "userwavefunc_param2"},
825         {ev_float, "userwavefunc_param3"},
826         {ev_function, "camera_transform"},
827         {ev_function, "think"},
828         {ev_string, "classname"},
829         {ev_vector, "colormod"},
830         {ev_vector, "glowmod"},
831
832         // physics
833         //{ev_float, "solid"},
834         //{ev_float, "movetype"},
835         //{ev_float, "modelindex"},
836         {ev_vector, "mass"},
837         //{ev_vector, "origin"},
838         //{ev_vector, "velocity"},
839         //{ev_vector, "axis_forward"},
840         //{ev_vector, "axis_left"},
841         //{ev_vector, "axis_up"},
842         //{ev_vector, "spinvelocity"},
843         //{ev_vector, "angles"},
844         //{ev_vector, "avelocity"},
845 };
846
847 #define CL_REQGLOBALS (sizeof(cl_reqglobals) / sizeof(prvm_required_field_t))
848
849 prvm_required_field_t cl_reqglobals[] =
850 {
851         {ev_entity, "self"},
852         {ev_entity, "trace_ent"},
853         {ev_entity, "trace_networkentity"},
854         {ev_float, "coop"},
855         {ev_float, "deathmatch"},
856         {ev_float, "dmg_save"},
857         {ev_float, "dmg_take"},
858         {ev_float, "drawfont"},
859         {ev_float, "drawfontscale"},
860         {ev_float, "gettaginfo_parent"},
861         {ev_float, "intermission"},
862         {ev_float, "particle_airfriction"},
863         {ev_float, "particle_alpha"},
864         {ev_float, "particle_alphafade"},
865         {ev_float, "particle_angle"},
866         {ev_float, "particle_blendmode"},
867         {ev_float, "particle_bounce"},
868         {ev_float, "particle_delaycollision"},
869         {ev_float, "particle_delayspawn"},
870         {ev_float, "particle_gravity"},
871         {ev_float, "particle_liquidfriction"},
872         {ev_float, "particle_orientation"},
873         {ev_float, "particle_originjitter"},
874         {ev_float, "particle_qualityreduction"},
875         {ev_float, "particle_size"},
876         {ev_float, "particle_sizeincrease"},
877         {ev_float, "particle_spin"},
878         {ev_float, "particle_stainalpha"},
879         {ev_float, "particle_stainsize"},
880         {ev_float, "particle_staintex"},
881         {ev_float, "particle_staintex"},
882         {ev_float, "particle_stretch"},
883         {ev_float, "particle_tex"},
884         {ev_float, "particle_time"},
885         {ev_float, "particle_type"},
886         {ev_float, "particle_velocityjitter"},
887         {ev_float, "particles_alphamax"},
888         {ev_float, "particles_alphamin"},
889         {ev_float, "pmove_onground"},
890         {ev_float, "pmove_inwater"},
891         {ev_float, "require_spawnfunc_prefix"},
892         {ev_float, "sb_showscores"},
893         {ev_float, "serverdeltatime"},
894         {ev_float, "serverprevtime"},
895         {ev_float, "servertime"},
896         {ev_float, "time"},
897         {ev_float, "trace_allsolid"},
898         {ev_float, "trace_dphitcontents"},
899         {ev_float, "trace_dphitq3surfaceflags"},
900         {ev_float, "trace_dpstartcontents"},
901         {ev_float, "trace_fraction"},
902         {ev_float, "trace_inopen"},
903         {ev_float, "trace_inwater"},
904         {ev_float, "trace_plane_dist"},
905         {ev_float, "trace_startsolid"},
906         {ev_float, "transparent_offset"},
907         {ev_string, "SV_InitCmd"},
908         {ev_string, "gettaginfo_name"},
909         {ev_string, "trace_dphittexturename"},
910         {ev_string, "worldstatus"},
911         {ev_string, "message"},
912         {ev_vector, "dmg_origin"},
913         {ev_vector, "gettaginfo_forward"},
914         {ev_vector, "gettaginfo_offset"},
915         {ev_vector, "gettaginfo_right"},
916         {ev_vector, "gettaginfo_up"},
917         {ev_vector, "particle_color1"},
918         {ev_vector, "particle_color2"},
919         {ev_vector, "particle_staincolor1"},
920         {ev_vector, "particle_staincolor2"},
921         {ev_vector, "particles_colormax"},
922         {ev_vector, "particles_colormin"},
923         {ev_vector, "trace_endpos"},
924         {ev_vector, "trace_plane_normal"},
925         {ev_vector, "v_forward"},
926         {ev_vector, "v_right"},
927         {ev_vector, "v_up"},
928         {ev_vector, "view_angles"},
929         {ev_vector, "view_punchangle"},
930         {ev_vector, "view_punchvector"},
931 };
932
933 void CL_VM_Init (void)
934 {
935         const char* csprogsfn;
936         unsigned char *csprogsdata;
937         fs_offset_t csprogsdatasize;
938         int csprogsdatacrc, requiredcrc;
939         int requiredsize;
940
941         // reset csqc_progcrc after reading it, so that changing servers doesn't
942         // expect csqc on the next server
943         requiredcrc = csqc_progcrc.integer;
944         requiredsize = csqc_progsize.integer;
945         Cvar_SetValueQuick(&csqc_progcrc, -1);
946         Cvar_SetValueQuick(&csqc_progsize, -1);
947
948         // if the server is not requesting a csprogs, then we're done here
949         if (requiredcrc < 0)
950                 return;
951
952         // see if the requested csprogs.dat file matches the requested crc
953         csprogsdatacrc = -1;
954         csprogsfn = va("dlcache/%s.%i.%i", csqc_progname.string, requiredsize, requiredcrc);
955         csprogsdata = FS_LoadFile(csprogsfn, tempmempool, true, &csprogsdatasize);
956         if (!csprogsdata)
957         {
958                 csprogsfn = csqc_progname.string;
959                 csprogsdata = FS_LoadFile(csprogsfn, tempmempool, true, &csprogsdatasize);
960         }
961         if (csprogsdata)
962         {
963                 csprogsdatacrc = CRC_Block(csprogsdata, (size_t)csprogsdatasize);
964                 if (csprogsdatacrc != requiredcrc || csprogsdatasize != requiredsize)
965                 {
966                         if (cls.demoplayback)
967                         {
968                                 Con_Printf("^1Warning: Your %s is not the same version as the demo was recorded with (CRC/size are %i/%i but should be %i/%i)\n", csqc_progname.string, csprogsdatacrc, (int)csprogsdatasize, requiredcrc, requiredsize);
969                                 // Mem_Free(csprogsdata);
970                                 // return;
971                                 // We WANT to continue here, and play the demo with different csprogs!
972                                 // After all, this is just a warning. Sure things may go wrong from here.
973                         }
974                         else
975                         {
976                                 Mem_Free(csprogsdata);
977                                 Con_Printf("^1Your %s is not the same version as the server (CRC is %i/%i but should be %i/%i)\n", csqc_progname.string, csprogsdatacrc, (int)csprogsdatasize, requiredcrc, requiredsize);
978                                 CL_Disconnect();
979                                 return;
980                         }
981                 }
982         }
983         else
984         {
985                 if (requiredcrc >= 0)
986                 {
987                         if (cls.demoplayback)
988                                 Con_Printf("CL_VM_Init: demo requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string);
989                         else
990                                 Con_Printf("CL_VM_Init: server requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string);
991                         CL_Disconnect();
992                 }
993                 return;
994         }
995
996         PRVM_Begin;
997         PRVM_InitProg(PRVM_CLIENTPROG);
998
999         // allocate the mempools
1000         prog->progs_mempool = Mem_AllocPool(csqc_progname.string, 0, NULL);
1001         prog->headercrc = CL_PROGHEADER_CRC;
1002         prog->edictprivate_size = 0; // no private struct used
1003         prog->name = CL_NAME;
1004         prog->num_edicts = 1;
1005         prog->max_edicts = 512;
1006         prog->limit_edicts = CL_MAX_EDICTS;
1007         prog->reserved_edicts = 0;
1008         prog->edictprivate_size = sizeof(edict_engineprivate_t);
1009         // TODO: add a shared extension string #define and add real support for csqc extension strings [12/5/2007 Black]
1010         prog->extensionstring = vm_sv_extensions;
1011         prog->builtins = vm_cl_builtins;
1012         prog->numbuiltins = vm_cl_numbuiltins;
1013         prog->begin_increase_edicts = CL_VM_CB_BeginIncreaseEdicts;
1014         prog->end_increase_edicts = CL_VM_CB_EndIncreaseEdicts;
1015         prog->init_edict = CL_VM_CB_InitEdict;
1016         prog->free_edict = CL_VM_CB_FreeEdict;
1017         prog->count_edicts = CL_VM_CB_CountEdicts;
1018         prog->load_edict = CL_VM_CB_LoadEdict;
1019         prog->init_cmd = VM_CL_Cmd_Init;
1020         prog->reset_cmd = VM_CL_Cmd_Reset;
1021         prog->error_cmd = CL_VM_Error;
1022         prog->ExecuteProgram = CLVM_ExecuteProgram;
1023
1024         PRVM_LoadProgs(csprogsfn, cl_numrequiredfunc, cl_required_func, CL_REQFIELDS, cl_reqfields, CL_REQGLOBALS, cl_reqglobals);
1025
1026         if (!prog->loaded)
1027         {
1028                 CL_VM_Error("CSQC %s ^2failed to load\n", csprogsfn);
1029                 if(!sv.active)
1030                         CL_Disconnect();
1031                 Mem_Free(csprogsdata);
1032                 return;
1033         }
1034
1035         Con_DPrintf("CSQC %s ^5loaded (crc=%i, size=%i)\n", csprogsfn, csprogsdatacrc, (int)csprogsdatasize);
1036
1037         if(cls.demorecording)
1038         {
1039                 if(cls.demo_lastcsprogssize != csprogsdatasize || cls.demo_lastcsprogscrc != csprogsdatacrc)
1040                 {
1041                         int i;
1042                         static char buf[NET_MAXMESSAGE];
1043                         sizebuf_t sb;
1044                         unsigned char *demobuf; fs_offset_t demofilesize;
1045
1046                         sb.data = (unsigned char *) buf;
1047                         sb.maxsize = sizeof(buf);
1048                         i = 0;
1049
1050                         CL_CutDemo(&demobuf, &demofilesize);
1051                         while(MakeDownloadPacket(csqc_progname.string, csprogsdata, (size_t)csprogsdatasize, csprogsdatacrc, i++, &sb, cls.protocol))
1052                                 CL_WriteDemoMessage(&sb);
1053                         CL_PasteDemo(&demobuf, &demofilesize);
1054
1055                         cls.demo_lastcsprogssize = csprogsdatasize;
1056                         cls.demo_lastcsprogscrc = csprogsdatacrc;
1057                 }
1058         }
1059         Mem_Free(csprogsdata);
1060
1061         // check if OP_STATE animation is possible in this dat file
1062         if (prog->fieldoffsets.nextthink >= 0 && prog->fieldoffsets.frame >= 0 && prog->fieldoffsets.think >= 0 && prog->globaloffsets.self >= 0)
1063                 prog->flag |= PRVM_OP_STATE;
1064
1065         // set time
1066         prog->globals.client->time = cl.time;
1067         prog->globals.client->self = 0;
1068
1069         prog->globals.client->mapname = PRVM_SetEngineString(cl.worldname);
1070         prog->globals.client->player_localentnum = cl.playerentity;
1071
1072         // set map description (use world entity 0)
1073         PRVM_EDICTFIELDSTRING(prog->edicts, prog->fieldoffsets.message) = PRVM_SetEngineString(cl.worldmessage);
1074         VectorCopy(cl.world.mins, prog->edicts->fields.client->mins);
1075         VectorCopy(cl.world.maxs, prog->edicts->fields.client->maxs);
1076
1077         // call the prog init
1078         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Init, "QC function CSQC_Init is missing");
1079
1080         PRVM_End;
1081         cl.csqc_loaded = true;
1082
1083         cl.csqc_vidvars.drawcrosshair = false;
1084         cl.csqc_vidvars.drawenginesbar = false;
1085
1086         // Update Coop and Deathmatch Globals (at this point the client knows them from ServerInfo)
1087         CL_VM_UpdateCoopDeathmatchGlobals(cl.gametype);
1088 }
1089
1090 void CL_VM_ShutDown (void)
1091 {
1092         Cmd_ClearCsqcFuncs();
1093         //Cvar_SetValueQuick(&csqc_progcrc, -1);
1094         //Cvar_SetValueQuick(&csqc_progsize, -1);
1095         if(!cl.csqc_loaded)
1096                 return;
1097         CSQC_BEGIN
1098                 prog->globals.client->time = cl.time;
1099                 prog->globals.client->self = 0;
1100                 if (prog->funcoffsets.CSQC_Shutdown)
1101                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Shutdown, "QC function CSQC_Shutdown is missing");
1102                 PRVM_ResetProg();
1103         CSQC_END
1104         Con_DPrint("CSQC ^1unloaded\n");
1105         cl.csqc_loaded = false;
1106 }
1107
1108 qboolean CL_VM_GetEntitySoundOrigin(int entnum, vec3_t out)
1109 {
1110         prvm_edict_t *ed;
1111         dp_model_t *mod;
1112         matrix4x4_t matrix;
1113         qboolean r = 0;
1114
1115         CSQC_BEGIN;
1116
1117         // FIXME consider attachments here!
1118
1119         ed = PRVM_EDICT_NUM(entnum - MAX_EDICTS);
1120
1121         if(!ed->priv.required->free)
1122         {
1123                 mod = CL_GetModelFromEdict(ed);
1124                 VectorCopy(ed->fields.client->origin, out);
1125                 if(CL_GetTagMatrix (&matrix, ed, 0) == 0)
1126                         Matrix4x4_OriginFromMatrix(&matrix, out);
1127                 if (mod && mod->soundfromcenter)
1128                         VectorMAMAM(1.0f, out, 0.5f, mod->normalmins, 0.5f, mod->normalmaxs, out);
1129                 r = 1;
1130         }
1131
1132         CSQC_END;
1133
1134         return r;
1135 }
1136
1137 qboolean CL_VM_TransformView(int entnum, matrix4x4_t *viewmatrix, mplane_t *clipplane, vec3_t visorigin)
1138 {
1139         qboolean ret = false;
1140         prvm_edict_t *ed;
1141         vec3_t forward, left, up, origin, ang;
1142         matrix4x4_t mat, matq;
1143
1144         CSQC_BEGIN
1145                 ed = PRVM_EDICT_NUM(entnum);
1146                 // camera:
1147                 //   camera_transform
1148                 if(PRVM_EDICTFIELDFUNCTION(ed, prog->fieldoffsets.camera_transform))
1149                 {
1150                         ret = true;
1151                         if(viewmatrix || clipplane || visorigin)
1152                         {
1153                                 Matrix4x4_ToVectors(viewmatrix, forward, left, up, origin);
1154                                 AnglesFromVectors(ang, forward, up, false);
1155                                 prog->globals.client->time = cl.time;
1156                                 prog->globals.client->self = entnum;
1157                                 VectorCopy(origin, PRVM_G_VECTOR(OFS_PARM0));
1158                                 VectorCopy(ang, PRVM_G_VECTOR(OFS_PARM1));
1159                                 VectorCopy(forward, PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.v_forward));
1160                                 VectorScale(left, -1, PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.v_right));
1161                                 VectorCopy(up, PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.v_up));
1162                                 VectorCopy(origin, PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.trace_endpos));
1163                                 PRVM_ExecuteProgram(PRVM_EDICTFIELDFUNCTION(ed, prog->fieldoffsets.camera_transform), "QC function e.camera_transform is missing");
1164                                 VectorCopy(PRVM_G_VECTOR(OFS_RETURN), origin);
1165                                 VectorCopy(PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.v_forward), forward);
1166                                 VectorScale(PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.v_right), -1, left);
1167                                 VectorCopy(PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.v_up), up);
1168                                 VectorCopy(PRVM_GLOBALFIELDVECTOR(prog->globaloffsets.trace_endpos), visorigin);
1169                                 Matrix4x4_Invert_Full(&mat, viewmatrix);
1170                                 Matrix4x4_FromVectors(viewmatrix, forward, left, up, origin);
1171                                 Matrix4x4_Concat(&matq, viewmatrix, &mat);
1172                                 Matrix4x4_TransformPositivePlane(&matq, clipplane->normal[0], clipplane->normal[1], clipplane->normal[2], clipplane->dist, &clipplane->normal[0]);
1173                         }
1174                 }
1175         CSQC_END
1176
1177         return ret;
1178 }