Add full support for csqc's sensitivityscale (no clamping yet, so you can actually...
[divverent/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
8 //============================================================================
9 // Client prog handling
10 //[515]: omg !!! optimize it ! a lot of hacks here and there also :P
11
12 #define CSQC_RETURNVAL  prog->globals.generic[OFS_RETURN]
13 #define CSQC_BEGIN              csqc_tmpprog=prog;prog=0;PRVM_SetProg(PRVM_CLIENTPROG);
14 #define CSQC_END                prog=csqc_tmpprog;
15 static prvm_prog_t *csqc_tmpprog;
16
17 //[515]: these are required funcs
18 static char *cl_required_func[] =
19 {
20         "CSQC_Init",
21         "CSQC_InputEvent",
22         "CSQC_UpdateView",
23         "CSQC_ConsoleCommand",
24 };
25
26 static int cl_numrequiredfunc = sizeof(cl_required_func) / sizeof(char*);
27
28 void CL_VM_Error (const char *format, ...) DP_FUNC_PRINTF(1);
29 void CL_VM_Error (const char *format, ...)      //[515]: hope it will be never executed =)
30 {
31         char errorstring[4096];
32         va_list argptr;
33
34         va_start (argptr, format);
35         dpvsnprintf (errorstring, sizeof(errorstring), format, argptr);
36         va_end (argptr);
37 //      Con_Printf( "CL_VM_Error: %s\n", errorstring );
38
39         PRVM_Crash();
40         cl.csqc_loaded = false;
41
42         Cvar_SetValueQuick(&csqc_progcrc, -1);
43         Cvar_SetValueQuick(&csqc_progsize, -1);
44
45 //      Host_AbortCurrentFrame();       //[515]: hmmm... if server says it needs csqc then client MUST disconnect
46         Host_Error(va("CL_VM_Error: %s", errorstring));
47 }
48 void CL_VM_UpdateDmgGlobals (int dmg_take, int dmg_save, vec3_t dmg_origin)
49 {
50         prvm_eval_t *val;
51         if(cl.csqc_loaded)
52         {
53                 CSQC_BEGIN
54                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.dmg_take);
55                 if(val)
56                         val->_float = dmg_take;
57                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.dmg_save);
58                 if(val)
59                         val->_float = dmg_save;
60                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.dmg_origin);
61                 if(val)
62                 {
63                         val->vector[0] = dmg_origin[0];
64                         val->vector[1] = dmg_origin[1];
65                         val->vector[2] = dmg_origin[2];
66                 }
67                 CSQC_END
68         }
69 }
70 //[515]: set globals before calling R_UpdateView, WEIRD CRAP
71 static void CSQC_SetGlobals (void)
72 {
73         prvm_eval_t *val;
74         CSQC_BEGIN
75                 prog->globals.client->time = cl.time;
76                 prog->globals.client->frametime = max(0, cl.time - cl.oldtime);
77                 prog->globals.client->servercommandframe = cls.servermovesequence;
78                 prog->globals.client->clientcommandframe = cl.movecmd[0].sequence;
79                 VectorCopy(cl.viewangles, prog->globals.client->input_angles);
80                 VectorCopy(cl.viewangles, cl.csqc_angles);
81                 // // FIXME: this actually belongs into getinputstate().. [12/17/2007 Black]
82                 prog->globals.client->input_buttons = cl.movecmd[0].buttons;
83                 VectorSet(prog->globals.client->input_movevalues, cl.movecmd[0].forwardmove, cl.movecmd[0].sidemove, cl.movecmd[0].upmove);
84                 //VectorCopy(cl.movement_origin, cl.csqc_origin);
85                 Matrix4x4_OriginFromMatrix(&cl.entities[cl.viewentity].render.matrix, cl.csqc_origin);
86
87                 // LordHavoc: Spike says not to do this, but without pmove_org the
88                 // CSQC is useless as it can't alter the view origin without
89                 // completely replacing it
90                 VectorCopy(cl.csqc_origin, prog->globals.client->pmove_org);
91                 VectorCopy(cl.velocity, prog->globals.client->pmove_vel);
92
93                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.view_angles)))
94                         VectorCopy(cl.viewangles, val->vector);
95                 prog->globals.client->maxclients = cl.maxclients;
96         CSQC_END
97 }
98
99 void CSQC_Predraw (prvm_edict_t *ed)
100 {
101         int b;
102         if(!ed->fields.client->predraw)
103                 return;
104         b = prog->globals.client->self;
105         prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
106         PRVM_ExecuteProgram(ed->fields.client->predraw, "CSQC_Predraw: NULL function\n");
107         prog->globals.client->self = b;
108 }
109
110 void CSQC_Think (prvm_edict_t *ed)
111 {
112         int b;
113         if(ed->fields.client->think)
114         if(ed->fields.client->nextthink && ed->fields.client->nextthink <= prog->globals.client->time)
115         {
116                 ed->fields.client->nextthink = 0;
117                 b = prog->globals.client->self;
118                 prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
119                 PRVM_ExecuteProgram(ed->fields.client->think, "CSQC_Think: NULL function\n");
120                 prog->globals.client->self = b;
121         }
122 }
123
124 extern cvar_t cl_noplayershadow;
125 qboolean CSQC_AddRenderEdict(prvm_edict_t *ed)
126 {
127         int renderflags;
128         float scale;
129         prvm_eval_t *val;
130         entity_t *e;
131         model_t *model;
132         matrix4x4_t tagmatrix, matrix2;
133
134         model = CL_GetModelFromEdict(ed);
135         if (!model)
136                 return false;
137
138         e = CL_NewTempEntity();
139         if (!e)
140                 return false;
141
142         e->render.model = model;
143         e->render.colormap = (int)ed->fields.client->colormap;
144         e->render.skinnum = (int)ed->fields.client->skin;
145         e->render.effects |= e->render.model->effects;
146         scale = 1;
147         renderflags = 0;
148         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.renderflags)) && val->_float)     renderflags = (int)val->_float;
149         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.alpha)) && val->_float)           e->render.alpha = val->_float;
150         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.scale)) && val->_float)           e->render.scale = scale = val->_float;
151         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.colormod)) && VectorLength2(val->vector)) VectorCopy(val->vector, e->render.colormod);
152         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.effects)) && val->_float) e->render.effects |= (int)val->_float;
153         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.tag_entity)) && val->edict)
154         {
155                 int tagentity;
156                 int tagindex = 0;
157                 tagentity = val->edict;
158                 if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.tag_index)) && val->_float)
159                         tagindex = (int)val->_float;
160                 // FIXME: calculate tag matrix
161                 Matrix4x4_CreateIdentity(&tagmatrix);
162         }
163         else
164                 Matrix4x4_CreateIdentity(&tagmatrix);
165
166         if (renderflags & RF_USEAXIS)
167         {
168                 vec3_t left;
169                 VectorNegate(prog->globals.client->v_right, left);
170                 Matrix4x4_FromVectors(&matrix2, prog->globals.client->v_forward, left, prog->globals.client->v_up, ed->fields.client->origin);
171                 Matrix4x4_Scale(&matrix2, scale, 1);
172         }
173         else
174         {
175                 vec3_t angles;
176                 VectorCopy(ed->fields.client->angles, angles);
177                 // if model is alias, reverse pitch direction
178                 if (e->render.model->type == mod_alias)
179                         angles[0] = -angles[0];
180
181                 // set up the render matrix
182                 Matrix4x4_CreateFromQuakeEntity(&matrix2, ed->fields.client->origin[0], ed->fields.client->origin[1], ed->fields.client->origin[2], angles[0], angles[1], angles[2], scale);
183         }
184
185         // set up the animation data
186         // self.frame is the interpolation target (new frame)
187         // self.frame1time is the animation base time for the interpolation target
188         // self.frame2 is the interpolation start (previous frame)
189         // self.frame2time is the animation base time for the interpolation start
190         e->render.frame1 = e->render.frame2 = ed->fields.client->frame;
191         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2))) e->render.frame2 = val->_float;
192         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame1time))) e->render.frame2time = val->_float;
193         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2time))) e->render.frame1time = val->_float;
194         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac))) e->render.framelerp = val->_float;
195
196         // concat the matrices to make the entity relative to its tag
197         Matrix4x4_Concat(&e->render.matrix, &tagmatrix, &matrix2);
198         // make the other useful stuff
199         CL_UpdateRenderEntity(&e->render);
200
201         if(renderflags)
202         {
203                 if(renderflags & RF_VIEWMODEL)  e->render.flags |= RENDER_VIEWMODEL;
204                 if(renderflags & RF_EXTERNALMODEL)e->render.flags |= RENDER_EXTERIORMODEL;
205                 if(renderflags & RF_DEPTHHACK)  e->render.effects |= EF_NODEPTHTEST;
206                 if(renderflags & RF_ADDITIVE)           e->render.effects |= EF_ADDITIVE;
207         }
208
209         if ((e->render.colormap > 0 && e->render.colormap <= cl.maxclients) || e->render.colormap >= 1024)
210         {
211                 unsigned char *cbcolor;
212                 int palcol;
213                 if (e->render.colormap >= 1024)
214                         palcol = (unsigned char)(e->render.colormap-1024);
215                 else
216                         palcol = cl.scores[e->render.colormap-1].colors;
217
218                 cbcolor = palette_rgb_pantscolormap[palcol & 0xF];
219                 e->render.colormap_pantscolor[0] = cbcolor[0] * (1.0f / 255.0f);
220                 e->render.colormap_pantscolor[1] = cbcolor[1] * (1.0f / 255.0f);
221                 e->render.colormap_pantscolor[2] = cbcolor[2] * (1.0f / 255.0f);
222                 cbcolor = palette_rgb_shirtcolormap[(palcol & 0xF0) >> 4];
223                 e->render.colormap_shirtcolor[0] = cbcolor[0] * (1.0f / 255.0f);
224                 e->render.colormap_shirtcolor[1] = cbcolor[1] * (1.0f / 255.0f);
225                 e->render.colormap_shirtcolor[2] = cbcolor[2] * (1.0f / 255.0f);
226         }
227
228         // either fullbright or lit
229         if (!(e->render.effects & EF_FULLBRIGHT) && !r_fullbright.integer)
230                 e->render.flags |= RENDER_LIGHT;
231         // hide player shadow during intermission or nehahra movie
232         if (!(e->render.effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST))
233          &&  (e->render.alpha >= 1)
234          && !(e->render.flags & RENDER_VIEWMODEL)
235          && (!(e->render.flags & RENDER_EXTERIORMODEL) || (!cl.intermission && cls.protocol != PROTOCOL_NEHAHRAMOVIE && !cl_noplayershadow.integer)))
236                 e->render.flags |= RENDER_SHADOW;
237         if (e->render.flags & RENDER_VIEWMODEL)
238                 e->render.flags |= RENDER_NOSELFSHADOW;
239
240         return true;
241 }
242
243 qboolean CL_VM_InputEvent (qboolean down, int key)
244 {
245         qboolean r;
246         if(!cl.csqc_loaded)
247                 return false;
248         CSQC_BEGIN
249                 if (!prog->funcoffsets.CSQC_InputEvent)
250                         r = false;
251                 else
252                 {
253                         prog->globals.client->time = cl.time;
254                         PRVM_G_FLOAT(OFS_PARM0) = !down; // 0 is down, 1 is up
255                         PRVM_G_FLOAT(OFS_PARM1) = key;
256                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_InputEvent, "QC function CSQC_InputEvent is missing");
257                         r = CSQC_RETURNVAL;
258                 }
259         CSQC_END
260         return r;
261 }
262
263 qboolean CL_VM_UpdateView (void)
264 {
265         vec3_t emptyvector;
266         emptyvector[0] = 0;
267         emptyvector[1] = 0;
268         emptyvector[2] = 0;
269 //      vec3_t oldangles;
270         if(!cl.csqc_loaded)
271                 return false;
272         CSQC_BEGIN
273                 //VectorCopy(cl.viewangles, oldangles);
274                 prog->globals.client->time = cl.time;
275                 CSQC_SetGlobals();
276                 // clear renderable entity and light lists to prevent crashes if the
277                 // CSQC_UpdateView function does not call R_ClearScene as it should
278                 r_refdef.numentities = 0;
279                 r_refdef.numlights = 0;
280                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_UpdateView, "QC function CSQC_UpdateView is missing");
281                 //VectorCopy(oldangles, cl.viewangles);
282                 // Dresk : Reset Dmg Globals Here
283                 CL_VM_UpdateDmgGlobals(0, 0, emptyvector);
284         CSQC_END
285         return true;
286 }
287
288 extern sizebuf_t vm_tempstringsbuf;
289 qboolean CL_VM_ConsoleCommand (const char *cmd)
290 {
291         int restorevm_tempstringsbuf_cursize;
292         qboolean r = false;
293         if(!cl.csqc_loaded)
294                 return false;
295         CSQC_BEGIN
296         if (prog->funcoffsets.CSQC_ConsoleCommand)
297         {
298                 prog->globals.client->time = cl.time;
299                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
300                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(cmd);
301                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_ConsoleCommand, "QC function CSQC_ConsoleCommand is missing");
302                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
303                 r = CSQC_RETURNVAL;
304         }
305         CSQC_END
306         return r;
307 }
308
309 qboolean CL_VM_Parse_TempEntity (void)
310 {
311         int                     t;
312         qboolean        r = false;
313         if(!cl.csqc_loaded)
314                 return false;
315         CSQC_BEGIN
316         if(prog->funcoffsets.CSQC_Parse_TempEntity)
317         {
318                 t = msg_readcount;
319                 prog->globals.client->time = cl.time;
320                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_TempEntity, "QC function CSQC_Parse_TempEntity is missing");
321                 r = CSQC_RETURNVAL;
322                 if(!r)
323                 {
324                         msg_readcount = t;
325                         msg_badread = false;
326                 }
327         }
328         CSQC_END
329         return r;
330 }
331
332 void CL_VM_Parse_StuffCmd (const char *msg)
333 {
334         int restorevm_tempstringsbuf_cursize;
335         if(msg[0] == 'c')
336         if(msg[1] == 's')
337         if(msg[2] == 'q')
338         if(msg[3] == 'c')
339         {
340                 // if this is setting a csqc variable, deprotect csqc_progcrc
341                 // temporarily so that it can be set by the cvar command,
342                 // and then reprotect it afterwards
343                 int crcflags = csqc_progcrc.flags;
344                 int sizeflags = csqc_progcrc.flags;
345                 csqc_progcrc.flags &= ~CVAR_READONLY;
346                 csqc_progsize.flags &= ~CVAR_READONLY;
347                 Cmd_ExecuteString (msg, src_command);
348                 csqc_progcrc.flags = crcflags;
349                 csqc_progsize.flags = sizeflags;
350                 return;
351         }
352         if(!cl.csqc_loaded)
353         {
354                 Cbuf_AddText(msg);
355                 return;
356         }
357         CSQC_BEGIN
358         if(prog->funcoffsets.CSQC_Parse_StuffCmd)
359         {
360                 prog->globals.client->time = cl.time;
361                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
362                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
363                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_StuffCmd, "QC function CSQC_Parse_StuffCmd is missing");
364                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
365         }
366         else
367                 Cbuf_AddText(msg);
368         CSQC_END
369 }
370
371 static void CL_VM_Parse_Print (const char *msg)
372 {
373         int restorevm_tempstringsbuf_cursize;
374         prog->globals.client->time = cl.time;
375         restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
376         PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
377         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_Print, "QC function CSQC_Parse_Print is missing");
378         vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
379 }
380
381 void CSQC_AddPrintText (const char *msg)
382 {
383         size_t i;
384         if(!cl.csqc_loaded)
385         {
386                 Con_Print(msg);
387                 return;
388         }
389         CSQC_BEGIN
390         if(prog->funcoffsets.CSQC_Parse_Print)
391         {
392                 // FIXME: is this bugged?
393                 i = strlen(msg)-1;
394                 if(msg[i] != '\n' && msg[i] != '\r')
395                 {
396                         if(strlen(cl.csqc_printtextbuf)+i >= MAX_INPUTLINE)
397                         {
398                                 CL_VM_Parse_Print(cl.csqc_printtextbuf);
399                                 cl.csqc_printtextbuf[0] = 0;
400                         }
401                         else
402                                 strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
403                         return;
404                 }
405                 strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
406                 CL_VM_Parse_Print(cl.csqc_printtextbuf);
407                 cl.csqc_printtextbuf[0] = 0;
408         }
409         else
410                 Con_Print(msg);
411         CSQC_END
412 }
413
414 void CL_VM_Parse_CenterPrint (const char *msg)
415 {
416         int restorevm_tempstringsbuf_cursize;
417         if(!cl.csqc_loaded)
418         {
419                 SCR_CenterPrint((char*)msg);
420                 return;
421         }
422         CSQC_BEGIN
423         if(prog->funcoffsets.CSQC_Parse_CenterPrint)
424         {
425                 prog->globals.client->time = cl.time;
426                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
427                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
428                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_CenterPrint, "QC function CSQC_Parse_CenterPrint is missing");
429                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
430         }
431         else
432                 SCR_CenterPrint((char*)msg);
433         CSQC_END
434 }
435
436 void CL_VM_UpdateIntermissionState (int intermission)
437 {
438         prvm_eval_t *val;
439         if(cl.csqc_loaded)
440         {
441                 CSQC_BEGIN
442                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.intermission);
443                 if(val)
444                         val->_float = intermission;
445                 CSQC_END
446         }
447 }
448 void CL_VM_UpdateShowingScoresState (int showingscores)
449 {
450         prvm_eval_t *val;
451         if(cl.csqc_loaded)
452         {
453                 CSQC_BEGIN
454                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.sb_showscores);
455                 if(val)
456                         val->_float = showingscores;
457                 CSQC_END
458         }
459 }
460 qboolean CL_VM_Event_Sound(int sound_num, int volume, int channel, float attenuation, int ent, vec3_t pos)
461 {
462         qboolean r = false;
463         if(cl.csqc_loaded)
464         {
465                 CSQC_BEGIN
466                 if(prog->funcoffsets.CSQC_Event_Sound)
467                 {
468                         prog->globals.client->time = cl.time;
469                         PRVM_G_FLOAT(OFS_PARM0) = ent;
470                         PRVM_G_FLOAT(OFS_PARM1) = channel;
471                         PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(cl.sound_name[sound_num] );
472                         PRVM_G_FLOAT(OFS_PARM3) = volume;
473                         PRVM_G_FLOAT(OFS_PARM4) = attenuation;
474                         VectorCopy(pos, PRVM_G_VECTOR(OFS_PARM5) );
475                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Event_Sound, "QC function CSQC_Event_Sound is missing");
476                         r = CSQC_RETURNVAL;
477                 }
478                 CSQC_END
479         }
480
481         return r;
482 }
483 void CL_VM_UpdateCoopDeathmatchGlobals (int gametype)
484 {
485         // Avoid global names for clean(er) coding
486         int localcoop;
487         int localdeathmatch;
488
489         prvm_eval_t *val;
490         if(cl.csqc_loaded)
491         {
492                 if(gametype == GAME_COOP)
493                 {
494                         localcoop = 1;
495                         localdeathmatch = 0;
496                 }
497                 else
498                 if(gametype == GAME_DEATHMATCH)
499                 {
500                         localcoop = 0;
501                         localdeathmatch = 1;
502                 }
503                 else
504                 {
505                         // How did the ServerInfo send an unknown gametype?
506                         // Better just assign the globals as 0...
507                         localcoop = 0;
508                         localdeathmatch = 0;
509                 }
510                 CSQC_BEGIN
511                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.coop);
512                 if(val)
513                         val->_float = localcoop;
514                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.deathmatch);
515                 if(val)
516                         val->_float = localdeathmatch;
517                 CSQC_END
518         }
519 }
520 float CL_VM_Event (float event)         //[515]: needed ? I'd say "YES", but don't know for what :D
521 {
522         float r = 0;
523         if(!cl.csqc_loaded)
524                 return 0;
525         CSQC_BEGIN
526         if(prog->funcoffsets.CSQC_Event)
527         {
528                 prog->globals.client->time = cl.time;
529                 PRVM_G_FLOAT(OFS_PARM0) = event;
530                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Event, "QC function CSQC_Event is missing");
531                 r = CSQC_RETURNVAL;
532         }
533         CSQC_END
534         return r;
535 }
536
537 void CSQC_ReadEntities (void)
538 {
539         unsigned short entnum, oldself, realentnum;
540         if(!cl.csqc_loaded)
541         {
542                 Host_Error ("CSQC_ReadEntities: CSQC is not loaded");
543                 return;
544         }
545
546         CSQC_BEGIN
547                 prog->globals.client->time = cl.time;
548                 oldself = prog->globals.client->self;
549                 while(1)
550                 {
551                         entnum = MSG_ReadShort();
552                         if(!entnum || msg_badread)
553                                 return;
554                         realentnum = entnum & 0x7FFF;
555                         prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum];
556                         if(entnum & 0x8000)
557                         {
558                                 if(prog->globals.client->self)
559                                 {
560                                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Remove, "QC function CSQC_Ent_Remove is missing");
561                                         cl.csqc_server2csqcentitynumber[realentnum] = 0;
562                                 }
563                                 else
564                                         Con_Printf("Smth bad happens in csqc...\n");    //[515]: never happens ?
565                         }
566                         else
567                         {
568                                 if(!prog->globals.client->self)
569                                 {
570                                         prvm_edict_t    *ed;
571                                         ed = PRVM_ED_Alloc();
572                                         ed->fields.client->entnum = realentnum;
573                                         prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT_TO_PROG(ed);
574                                         PRVM_G_FLOAT(OFS_PARM0) = 1;
575                                 }
576                                 else
577                                         PRVM_G_FLOAT(OFS_PARM0) = 0;
578                                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Update, "QC function CSQC_Ent_Update is missing");
579                         }
580                 }
581                 prog->globals.client->self = oldself;
582         CSQC_END
583 }
584
585 void CL_VM_CB_BeginIncreaseEdicts(void)
586 {
587         int i;
588         prvm_edict_t *ent;
589
590         // links don't survive the transition, so unlink everything
591         for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
592         {
593                 if (!ent->priv.server->free)
594                         World_UnlinkEdict(prog->edicts + i);
595                 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
596         }
597         World_Clear(&cl.world);
598 }
599
600 void CL_VM_CB_EndIncreaseEdicts(void)
601 {
602         int i;
603         prvm_edict_t *ent;
604
605         // link every entity except world
606         for (i = 1, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
607                 if (!ent->priv.server->free)
608                         CL_LinkEdict(ent);
609 }
610
611 void CL_VM_CB_InitEdict(prvm_edict_t *e)
612 {
613         e->priv.server->move = false; // don't move on first frame
614 }
615
616 void CL_VM_CB_FreeEdict(prvm_edict_t *ed)
617 {
618         World_UnlinkEdict(ed);
619         memset(ed->fields.client, 0, sizeof(*ed->fields.client));
620 }
621
622 void CL_VM_CB_CountEdicts(void)
623 {
624         int             i;
625         prvm_edict_t    *ent;
626         int             active = 0, models = 0, solid = 0;
627
628         for (i=0 ; i<prog->num_edicts ; i++)
629         {
630                 ent = PRVM_EDICT_NUM(i);
631                 if (ent->priv.server->free)
632                         continue;
633                 active++;
634                 if (ent->fields.client->solid)
635                         solid++;
636                 if (ent->fields.client->model)
637                         models++;
638         }
639
640         Con_Printf("num_edicts:%3i\n", prog->num_edicts);
641         Con_Printf("active    :%3i\n", active);
642         Con_Printf("view      :%3i\n", models);
643         Con_Printf("touch     :%3i\n", solid);
644 }
645
646 qboolean CL_VM_CB_LoadEdict(prvm_edict_t *ent)
647 {
648         return true;
649 }
650
651 void Cmd_ClearCsqcFuncs (void);
652
653 void CL_VM_Init (void)
654 {
655         unsigned char *csprogsdata;
656         fs_offset_t csprogsdatasize;
657         int csprogsdatacrc, requiredcrc;
658         int requiredsize;
659         prvm_eval_t *val;
660
661         // reset csqc_progcrc after reading it, so that changing servers doesn't
662         // expect csqc on the next server
663         requiredcrc = csqc_progcrc.integer;
664         requiredsize = csqc_progsize.integer;
665         Cvar_SetValueQuick(&csqc_progcrc, -1);
666         Cvar_SetValueQuick(&csqc_progsize, -1);
667
668         // if the server is not requesting a csprogs, then we're done here
669         if (requiredcrc < 0)
670                 return;
671
672         // see if the requested csprogs.dat file matches the requested crc
673         csprogsdatacrc = -1;
674         csprogsdata = FS_LoadFile(va("dlcache/%s.%i.%i", csqc_progname.string, requiredsize, requiredcrc), tempmempool, true, &csprogsdatasize);
675         if (!csprogsdata)
676                 csprogsdata = FS_LoadFile(csqc_progname.string, tempmempool, true, &csprogsdatasize);
677         if (csprogsdata)
678         {
679                 csprogsdatacrc = CRC_Block(csprogsdata, csprogsdatasize);
680                 Mem_Free(csprogsdata);
681                 if (csprogsdatacrc != requiredcrc || csprogsdatasize != requiredsize)
682                 {
683                         if (cls.demoplayback)
684                         {
685                                 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);
686                                 return;
687                         }
688                         else
689                         {
690                                 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);
691                                 CL_Disconnect();
692                                 return;
693                         }
694                 }
695         }
696         else
697         {
698                 if (requiredcrc >= 0)
699                 {
700                         if (cls.demoplayback)
701                                 Con_Printf("CL_VM_Init: demo requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string);
702                         else
703                                 Con_Printf("CL_VM_Init: server requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string);
704                         CL_Disconnect();
705                 }
706                 return;
707         }
708
709         PRVM_Begin;
710         PRVM_InitProg(PRVM_CLIENTPROG);
711
712         // allocate the mempools
713         prog->progs_mempool = Mem_AllocPool(csqc_progname.string, 0, NULL);
714         prog->headercrc = CL_PROGHEADER_CRC;
715         prog->edictprivate_size = 0; // no private struct used
716         prog->name = CL_NAME;
717         prog->num_edicts = 1;
718         prog->max_edicts = 512;
719         prog->limit_edicts = CL_MAX_EDICTS;
720         prog->reserved_edicts = 0;
721         prog->edictprivate_size = sizeof(edict_engineprivate_t);
722         // TODO: add a shared extension string #define and add real support for csqc extension strings [12/5/2007 Black]
723         prog->extensionstring = vm_sv_extensions;
724         prog->builtins = vm_cl_builtins;
725         prog->numbuiltins = vm_cl_numbuiltins;
726         prog->begin_increase_edicts = CL_VM_CB_BeginIncreaseEdicts;
727         prog->end_increase_edicts = CL_VM_CB_EndIncreaseEdicts;
728         prog->init_edict = CL_VM_CB_InitEdict;
729         prog->free_edict = CL_VM_CB_FreeEdict;
730         prog->count_edicts = CL_VM_CB_CountEdicts;
731         prog->load_edict = CL_VM_CB_LoadEdict;
732         prog->init_cmd = VM_CL_Cmd_Init;
733         prog->reset_cmd = VM_CL_Cmd_Reset;
734         prog->error_cmd = CL_VM_Error;
735
736         PRVM_LoadProgs(csqc_progname.string, cl_numrequiredfunc, cl_required_func, 0, NULL, 0, NULL);
737
738         if (!prog->loaded)
739         {
740                 CL_VM_Error("CSQC ^2failed to load\n");
741                 if(!sv.active)
742                         CL_Disconnect();
743                 return;
744         }
745
746         Con_Printf("CSQC ^5loaded (crc=%i, size=%i)\n", csprogsdatacrc, (int)csprogsdatasize);
747
748         // check if OP_STATE animation is possible in this dat file
749         if (prog->fieldoffsets.nextthink >= 0 && prog->fieldoffsets.frame >= 0 && prog->fieldoffsets.think >= 0 && prog->globaloffsets.self >= 0)
750                 prog->flag |= PRVM_OP_STATE;
751
752         // set time
753         prog->globals.client->time = cl.time;
754
755         prog->globals.client->mapname = PRVM_SetEngineString(cl.worldmodel->name);
756         prog->globals.client->player_localentnum = cl.playerentity;
757
758         // set map description (use world entity 0)
759         val = PRVM_EDICTFIELDVALUE(prog->edicts, prog->fieldoffsets.message);
760         if(val)
761                 val->string = PRVM_SetEngineString(cl.levelname);
762
763         // call the prog init
764         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Init, "QC function CSQC_Init is missing");
765
766         PRVM_End;
767         cl.csqc_loaded = true;
768
769         cl.csqc_vidvars.drawcrosshair = false;
770         cl.csqc_vidvars.drawenginesbar = false;
771
772         // Update Coop and Deathmatch Globals (at this point the client knows them from ServerInfo)
773         CL_VM_UpdateCoopDeathmatchGlobals(cl.gametype);
774 }
775
776 void CL_VM_ShutDown (void)
777 {
778         Cmd_ClearCsqcFuncs();
779         Cvar_SetValueQuick(&csqc_progcrc, -1);
780         Cvar_SetValueQuick(&csqc_progsize, -1);
781         if(!cl.csqc_loaded)
782                 return;
783         CSQC_BEGIN
784                 prog->globals.client->time = cl.time;
785                 if (prog->funcoffsets.CSQC_Shutdown)
786                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Shutdown, "QC function CSQC_Shutdown is missing");
787                 PRVM_ResetProg();
788         CSQC_END
789         Con_Print("CSQC ^1unloaded\n");
790         cl.csqc_loaded = false;
791 }