]> git.xonotic.org Git - xonotic/darkplaces.git/blob - csprogs.c
physics: fix and refactor unsticking
[xonotic/darkplaces.git] / csprogs.c
1 /*
2 Copyright (C) 2006-2021 DarkPlaces contributors
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "progsvm.h"
23 #include "clprogdefs.h"
24 #include "csprogs.h"
25 #include "cl_collision.h"
26 #include "snd_main.h"
27 #include "clvm_cmds.h"
28 #include "prvm_cmds.h"
29
30 //============================================================================
31 // Client prog handling
32 //[515]: omg !!! optimize it ! a lot of hacks here and there also :P
33
34 #define CSQC_RETURNVAL  prog->globals.fp[OFS_RETURN]
35
36 void CL_VM_PreventInformationLeaks(void)
37 {
38         prvm_prog_t *prog = CLVM_prog;
39
40         if(!prog->loaded)
41                 return;
42
43         VM_ClearTraceGlobals(prog);
44         PRVM_clientglobalfloat(trace_networkentity) = 0;
45 }
46
47
48 /** Previous DP versions declined to load csprogs if it lacked any of:
49  * CSQC_Init, CSQC_InputEvent, CSQC_UpdateView, CSQC_ConsoleCommand
50  * whereas in FTE and QSS-based engines the minimum is either CSQC_UpdateView
51  * or CSQC_DrawHud (only called in CSQC_SIMPLE aka hud-only mode)
52  * and the other funcs are optional, so we now behave the same here.
53  */
54 static void CL_CheckRequiredFuncs(prvm_prog_t *prog, const char *filename)
55 {
56         if (PRVM_ED_FindFunction(prog, "CSQC_UpdateView"))
57                 return;
58         else if (PRVM_ED_FindFunction(prog, "CSQC_DrawHud"))
59                 prog->flag |= PRVM_CSQC_SIMPLE;
60         else
61                 prog->error_cmd("%s: no CSQC_UpdateView (EXT_CSQC) or CSQC_DrawHud (CSQC_SIMPLE) function found in %s", prog->name, filename);
62 }
63
64 #define CL_REQFIELDS (sizeof(cl_reqfields) / sizeof(prvm_required_field_t))
65
66 prvm_required_field_t cl_reqfields[] =
67 {
68 #define PRVM_DECLARE_serverglobalfloat(x)
69 #define PRVM_DECLARE_serverglobalvector(x)
70 #define PRVM_DECLARE_serverglobalstring(x)
71 #define PRVM_DECLARE_serverglobaledict(x)
72 #define PRVM_DECLARE_serverglobalfunction(x)
73 #define PRVM_DECLARE_clientglobalfloat(x)
74 #define PRVM_DECLARE_clientglobalvector(x)
75 #define PRVM_DECLARE_clientglobalstring(x)
76 #define PRVM_DECLARE_clientglobaledict(x)
77 #define PRVM_DECLARE_clientglobalfunction(x)
78 #define PRVM_DECLARE_menuglobalfloat(x)
79 #define PRVM_DECLARE_menuglobalvector(x)
80 #define PRVM_DECLARE_menuglobalstring(x)
81 #define PRVM_DECLARE_menuglobaledict(x)
82 #define PRVM_DECLARE_menuglobalfunction(x)
83 #define PRVM_DECLARE_serverfieldfloat(x)
84 #define PRVM_DECLARE_serverfieldvector(x)
85 #define PRVM_DECLARE_serverfieldstring(x)
86 #define PRVM_DECLARE_serverfieldedict(x)
87 #define PRVM_DECLARE_serverfieldfunction(x)
88 #define PRVM_DECLARE_clientfieldfloat(x) {ev_float, #x },
89 #define PRVM_DECLARE_clientfieldvector(x) {ev_vector, #x },
90 #define PRVM_DECLARE_clientfieldstring(x) {ev_string, #x },
91 #define PRVM_DECLARE_clientfieldedict(x) {ev_entity, #x },
92 #define PRVM_DECLARE_clientfieldfunction(x) {ev_function, #x },
93 #define PRVM_DECLARE_menufieldfloat(x)
94 #define PRVM_DECLARE_menufieldvector(x)
95 #define PRVM_DECLARE_menufieldstring(x)
96 #define PRVM_DECLARE_menufieldedict(x)
97 #define PRVM_DECLARE_menufieldfunction(x)
98 #define PRVM_DECLARE_serverfunction(x)
99 #define PRVM_DECLARE_clientfunction(x)
100 #define PRVM_DECLARE_menufunction(x)
101 #define PRVM_DECLARE_field(x)
102 #define PRVM_DECLARE_global(x)
103 #define PRVM_DECLARE_function(x)
104 #include "prvm_offsets.h"
105 #undef PRVM_DECLARE_serverglobalfloat
106 #undef PRVM_DECLARE_serverglobalvector
107 #undef PRVM_DECLARE_serverglobalstring
108 #undef PRVM_DECLARE_serverglobaledict
109 #undef PRVM_DECLARE_serverglobalfunction
110 #undef PRVM_DECLARE_clientglobalfloat
111 #undef PRVM_DECLARE_clientglobalvector
112 #undef PRVM_DECLARE_clientglobalstring
113 #undef PRVM_DECLARE_clientglobaledict
114 #undef PRVM_DECLARE_clientglobalfunction
115 #undef PRVM_DECLARE_menuglobalfloat
116 #undef PRVM_DECLARE_menuglobalvector
117 #undef PRVM_DECLARE_menuglobalstring
118 #undef PRVM_DECLARE_menuglobaledict
119 #undef PRVM_DECLARE_menuglobalfunction
120 #undef PRVM_DECLARE_serverfieldfloat
121 #undef PRVM_DECLARE_serverfieldvector
122 #undef PRVM_DECLARE_serverfieldstring
123 #undef PRVM_DECLARE_serverfieldedict
124 #undef PRVM_DECLARE_serverfieldfunction
125 #undef PRVM_DECLARE_clientfieldfloat
126 #undef PRVM_DECLARE_clientfieldvector
127 #undef PRVM_DECLARE_clientfieldstring
128 #undef PRVM_DECLARE_clientfieldedict
129 #undef PRVM_DECLARE_clientfieldfunction
130 #undef PRVM_DECLARE_menufieldfloat
131 #undef PRVM_DECLARE_menufieldvector
132 #undef PRVM_DECLARE_menufieldstring
133 #undef PRVM_DECLARE_menufieldedict
134 #undef PRVM_DECLARE_menufieldfunction
135 #undef PRVM_DECLARE_serverfunction
136 #undef PRVM_DECLARE_clientfunction
137 #undef PRVM_DECLARE_menufunction
138 #undef PRVM_DECLARE_field
139 #undef PRVM_DECLARE_global
140 #undef PRVM_DECLARE_function
141 };
142
143 #define CL_REQGLOBALS (sizeof(cl_reqglobals) / sizeof(prvm_required_field_t))
144
145 prvm_required_field_t cl_reqglobals[] =
146 {
147 #define PRVM_DECLARE_serverglobalfloat(x)
148 #define PRVM_DECLARE_serverglobalvector(x)
149 #define PRVM_DECLARE_serverglobalstring(x)
150 #define PRVM_DECLARE_serverglobaledict(x)
151 #define PRVM_DECLARE_serverglobalfunction(x)
152 #define PRVM_DECLARE_clientglobalfloat(x) {ev_float, #x},
153 #define PRVM_DECLARE_clientglobalvector(x) {ev_vector, #x},
154 #define PRVM_DECLARE_clientglobalstring(x) {ev_string, #x},
155 #define PRVM_DECLARE_clientglobaledict(x) {ev_entity, #x},
156 #define PRVM_DECLARE_clientglobalfunction(x) {ev_function, #x},
157 #define PRVM_DECLARE_menuglobalfloat(x)
158 #define PRVM_DECLARE_menuglobalvector(x)
159 #define PRVM_DECLARE_menuglobalstring(x)
160 #define PRVM_DECLARE_menuglobaledict(x)
161 #define PRVM_DECLARE_menuglobalfunction(x)
162 #define PRVM_DECLARE_serverfieldfloat(x)
163 #define PRVM_DECLARE_serverfieldvector(x)
164 #define PRVM_DECLARE_serverfieldstring(x)
165 #define PRVM_DECLARE_serverfieldedict(x)
166 #define PRVM_DECLARE_serverfieldfunction(x)
167 #define PRVM_DECLARE_clientfieldfloat(x)
168 #define PRVM_DECLARE_clientfieldvector(x)
169 #define PRVM_DECLARE_clientfieldstring(x)
170 #define PRVM_DECLARE_clientfieldedict(x)
171 #define PRVM_DECLARE_clientfieldfunction(x)
172 #define PRVM_DECLARE_menufieldfloat(x)
173 #define PRVM_DECLARE_menufieldvector(x)
174 #define PRVM_DECLARE_menufieldstring(x)
175 #define PRVM_DECLARE_menufieldedict(x)
176 #define PRVM_DECLARE_menufieldfunction(x)
177 #define PRVM_DECLARE_serverfunction(x)
178 #define PRVM_DECLARE_clientfunction(x)
179 #define PRVM_DECLARE_menufunction(x)
180 #define PRVM_DECLARE_field(x)
181 #define PRVM_DECLARE_global(x)
182 #define PRVM_DECLARE_function(x)
183 #include "prvm_offsets.h"
184 #undef PRVM_DECLARE_serverglobalfloat
185 #undef PRVM_DECLARE_serverglobalvector
186 #undef PRVM_DECLARE_serverglobalstring
187 #undef PRVM_DECLARE_serverglobaledict
188 #undef PRVM_DECLARE_serverglobalfunction
189 #undef PRVM_DECLARE_clientglobalfloat
190 #undef PRVM_DECLARE_clientglobalvector
191 #undef PRVM_DECLARE_clientglobalstring
192 #undef PRVM_DECLARE_clientglobaledict
193 #undef PRVM_DECLARE_clientglobalfunction
194 #undef PRVM_DECLARE_menuglobalfloat
195 #undef PRVM_DECLARE_menuglobalvector
196 #undef PRVM_DECLARE_menuglobalstring
197 #undef PRVM_DECLARE_menuglobaledict
198 #undef PRVM_DECLARE_menuglobalfunction
199 #undef PRVM_DECLARE_serverfieldfloat
200 #undef PRVM_DECLARE_serverfieldvector
201 #undef PRVM_DECLARE_serverfieldstring
202 #undef PRVM_DECLARE_serverfieldedict
203 #undef PRVM_DECLARE_serverfieldfunction
204 #undef PRVM_DECLARE_clientfieldfloat
205 #undef PRVM_DECLARE_clientfieldvector
206 #undef PRVM_DECLARE_clientfieldstring
207 #undef PRVM_DECLARE_clientfieldedict
208 #undef PRVM_DECLARE_clientfieldfunction
209 #undef PRVM_DECLARE_menufieldfloat
210 #undef PRVM_DECLARE_menufieldvector
211 #undef PRVM_DECLARE_menufieldstring
212 #undef PRVM_DECLARE_menufieldedict
213 #undef PRVM_DECLARE_menufieldfunction
214 #undef PRVM_DECLARE_serverfunction
215 #undef PRVM_DECLARE_clientfunction
216 #undef PRVM_DECLARE_menufunction
217 #undef PRVM_DECLARE_field
218 #undef PRVM_DECLARE_global
219 #undef PRVM_DECLARE_function
220 };
221
222 void CL_VM_UpdateDmgGlobals (int dmg_take, int dmg_save, vec3_t dmg_origin)
223 {
224         prvm_prog_t *prog = CLVM_prog;
225
226         if(prog->loaded)
227         {
228                 PRVM_clientglobalfloat(dmg_take) = dmg_take;
229                 PRVM_clientglobalfloat(dmg_save) = dmg_save;
230                 VectorCopy(dmg_origin, PRVM_clientglobalvector(dmg_origin));
231         }
232 }
233
234 void CSQC_UpdateNetworkTimes(double newtime, double oldtime)
235 {
236         prvm_prog_t *prog = CLVM_prog;
237
238         if(!prog->loaded)
239                 return;
240
241         PRVM_clientglobalfloat(servertime) = newtime;
242         PRVM_clientglobalfloat(serverprevtime) = oldtime;
243         PRVM_clientglobalfloat(serverdeltatime) = newtime - oldtime;
244 }
245
246 //[515]: set globals before calling R_UpdateView, WEIRD CRAP
247 static void CSQC_SetGlobals (double frametime)
248 {
249         vec3_t pmove_org;
250         prvm_prog_t *prog = CLVM_prog;
251
252         PRVM_clientglobalfloat(time) = cl.time;
253         PRVM_clientglobalfloat(cltime) = host.realtime; // Spike named it that way.
254         PRVM_clientglobalfloat(frametime) = frametime;
255         PRVM_clientglobalfloat(servercommandframe) = cls.servermovesequence;
256         PRVM_clientglobalfloat(clientcommandframe) = cl.movecmd[0].sequence;
257         VectorCopy(cl.viewangles, PRVM_clientglobalvector(input_angles));
258         // // FIXME: this actually belongs into getinputstate().. [12/17/2007 Black]
259         PRVM_clientglobalfloat(input_buttons) = cl.movecmd[0].buttons;
260         VectorSet(PRVM_clientglobalvector(input_movevalues), cl.movecmd[0].forwardmove, cl.movecmd[0].sidemove, cl.movecmd[0].upmove);
261         VectorCopy(cl.csqc_vieworiginfromengine, cl.csqc_vieworigin);
262         VectorCopy(cl.csqc_viewanglesfromengine, cl.csqc_viewangles);
263
264         // LadyHavoc: Spike says not to do this, but without pmove_org the
265         // CSQC is useless as it can't alter the view origin without
266         // completely replacing it
267         Matrix4x4_OriginFromMatrix(&cl.entities[cl.viewentity].render.matrix, pmove_org);
268         VectorCopy(pmove_org, PRVM_clientglobalvector(pmove_org));
269         VectorCopy(cl.movement_velocity, PRVM_clientglobalvector(pmove_vel));
270         PRVM_clientglobalfloat(pmove_onground) = cl.onground;
271         PRVM_clientglobalfloat(pmove_inwater) = cl.inwater;
272
273         VectorCopy(cl.viewangles, PRVM_clientglobalvector(view_angles));
274         VectorCopy(cl.punchangle, PRVM_clientglobalvector(view_punchangle));
275         VectorCopy(cl.punchvector, PRVM_clientglobalvector(view_punchvector));
276         PRVM_clientglobalfloat(maxclients) = cl.maxclients;
277
278         PRVM_clientglobalfloat(player_localentnum) = cl.viewentity;
279
280         CSQC_R_RecalcView();
281 }
282
283 void CSQC_Predraw (prvm_edict_t *ed)
284 {
285         prvm_prog_t *prog = CLVM_prog;
286         int b;
287         if(!PRVM_clientedictfunction(ed, predraw))
288                 return;
289         b = PRVM_clientglobaledict(self);
290         PRVM_clientglobaledict(self) = PRVM_EDICT_TO_PROG(ed);
291         prog->ExecuteProgram(prog, PRVM_clientedictfunction(ed, predraw), "CSQC_Predraw: NULL function\n");
292         PRVM_clientglobaledict(self) = b;
293 }
294
295 void CSQC_Think (prvm_edict_t *ed)
296 {
297         prvm_prog_t *prog = CLVM_prog;
298         int b;
299         if(PRVM_clientedictfunction(ed, think))
300         if(PRVM_clientedictfloat(ed, nextthink) && PRVM_clientedictfloat(ed, nextthink) <= PRVM_clientglobalfloat(time))
301         {
302                 PRVM_clientedictfloat(ed, nextthink) = 0;
303                 b = PRVM_clientglobaledict(self);
304                 PRVM_clientglobaledict(self) = PRVM_EDICT_TO_PROG(ed);
305                 prog->ExecuteProgram(prog, PRVM_clientedictfunction(ed, think), "CSQC_Think: NULL function\n");
306                 PRVM_clientglobaledict(self) = b;
307         }
308 }
309
310 extern cvar_t cl_noplayershadow;
311 qbool CSQC_AddRenderEdict(prvm_edict_t *ed, int edictnum)
312 {
313         prvm_prog_t *prog = CLVM_prog;
314         int renderflags;
315         int c;
316         float scale;
317         entity_render_t *entrender;
318         model_t *model;
319         prvm_vec3_t modellight_origin;
320
321         model = CL_GetModelFromEdict(ed);
322         if (!model)
323                 return false;
324
325         if (edictnum)
326         {
327                 if (r_refdef.scene.numentities >= r_refdef.scene.maxentities)
328                         return false;
329                 entrender = cl.csqcrenderentities + edictnum;
330                 r_refdef.scene.entities[r_refdef.scene.numentities++] = entrender;
331                 entrender->entitynumber = edictnum + MAX_EDICTS;
332                 //entrender->shadertime = 0; // shadertime was set by spawn()
333                 entrender->flags = 0;
334                 entrender->effects = 0;
335                 entrender->alpha = 1;
336                 entrender->scale = 1;
337                 VectorSet(entrender->colormod, 1, 1, 1);
338                 VectorSet(entrender->glowmod, 1, 1, 1);
339                 entrender->allowdecals = true;
340         }
341         else
342         {
343                 entrender = CL_NewTempEntity(0);
344                 if (!entrender)
345                         return false;
346         }
347
348         entrender->userwavefunc_param[0] = PRVM_clientedictfloat(ed, userwavefunc_param0);
349         entrender->userwavefunc_param[1] = PRVM_clientedictfloat(ed, userwavefunc_param1);
350         entrender->userwavefunc_param[2] = PRVM_clientedictfloat(ed, userwavefunc_param2);
351         entrender->userwavefunc_param[3] = PRVM_clientedictfloat(ed, userwavefunc_param3);
352
353         entrender->model = model;
354         entrender->skinnum = (int)PRVM_clientedictfloat(ed, skin);
355         entrender->effects |= entrender->model->effects;
356         renderflags = (int)PRVM_clientedictfloat(ed, renderflags);
357         entrender->alpha = PRVM_clientedictfloat(ed, alpha);
358         entrender->scale = scale = PRVM_clientedictfloat(ed, scale);
359         VectorCopy(PRVM_clientedictvector(ed, colormod), entrender->colormod);
360         VectorCopy(PRVM_clientedictvector(ed, glowmod), entrender->glowmod);
361         if(PRVM_clientedictfloat(ed, effects))  entrender->effects |= (int)PRVM_clientedictfloat(ed, effects);
362         if (!entrender->alpha)
363                 entrender->alpha = 1.0f;
364         if (!entrender->scale)
365                 entrender->scale = scale = 1.0f;
366         if (!VectorLength2(entrender->colormod))
367                 VectorSet(entrender->colormod, 1, 1, 1);
368         if (!VectorLength2(entrender->glowmod))
369                 VectorSet(entrender->glowmod, 1, 1, 1);
370
371         // LadyHavoc: use the CL_GetTagMatrix function on self to ensure consistent behavior (duplicate code would be bad)
372         // this also sets the custommodellight_origin for us
373         CL_GetTagMatrix(prog, &entrender->matrix, ed, 0, modellight_origin);
374         VectorCopy(modellight_origin, entrender->custommodellight_origin);
375
376         // set up the animation data
377         VM_GenerateFrameGroupBlend(prog, ed->priv.server->framegroupblend, ed);
378         VM_FrameBlendFromFrameGroupBlend(ed->priv.server->frameblend, ed->priv.server->framegroupblend, model, cl.time);
379         VM_UpdateEdictSkeleton(prog, ed, model, ed->priv.server->frameblend);
380         if (PRVM_clientedictfloat(ed, shadertime)) // hack for csprogs.dat files that do not set shadertime, leaves the value at entity spawn time
381                 entrender->shadertime = PRVM_clientedictfloat(ed, shadertime);
382
383         // transparent offset
384         if (renderflags & RF_USETRANSPARENTOFFSET)
385                 entrender->transparent_offset = PRVM_clientglobalfloat(transparent_offset);
386
387         // model light
388         if (renderflags & RF_MODELLIGHT)
389         {
390                 if (PRVM_clientedictvector(ed, modellight_ambient)) VectorCopy(PRVM_clientedictvector(ed, modellight_ambient), entrender->custommodellight_ambient); else VectorClear(entrender->custommodellight_ambient);
391                 if (PRVM_clientedictvector(ed, modellight_diffuse)) VectorCopy(PRVM_clientedictvector(ed, modellight_diffuse), entrender->custommodellight_diffuse); else VectorClear(entrender->custommodellight_diffuse);
392                 if (PRVM_clientedictvector(ed, modellight_dir))     VectorCopy(PRVM_clientedictvector(ed, modellight_dir), entrender->custommodellight_lightdir);    else VectorClear(entrender->custommodellight_lightdir);
393                 entrender->flags |= RENDER_CUSTOMIZEDMODELLIGHT;
394         }
395
396         if(renderflags)
397         {
398                 if(renderflags & RF_VIEWMODEL) entrender->flags |= RENDER_VIEWMODEL | RENDER_NODEPTHTEST;
399                 if(renderflags & RF_EXTERNALMODEL) entrender->flags |= RENDER_EXTERIORMODEL;
400                 if(renderflags & RF_WORLDOBJECT) entrender->flags |= RENDER_WORLDOBJECT;
401                 if(renderflags & RF_DEPTHHACK) entrender->flags |= RENDER_NODEPTHTEST;
402                 if(renderflags & RF_ADDITIVE) entrender->flags |= RENDER_ADDITIVE;
403                 if(renderflags & RF_DYNAMICMODELLIGHT) entrender->flags |= RENDER_DYNAMICMODELLIGHT;
404         }
405
406         c = (int)PRVM_clientedictfloat(ed, colormap);
407         if (c <= 0)
408                 CL_SetEntityColormapColors(entrender, -1);
409         else if (c <= cl.maxclients && cl.scores != NULL)
410                 CL_SetEntityColormapColors(entrender, cl.scores[c-1].colors);
411         else
412                 CL_SetEntityColormapColors(entrender, c);
413
414         entrender->flags &= ~(RENDER_SHADOW | RENDER_LIGHT | RENDER_NOSELFSHADOW);
415         // either fullbright or lit
416         if(!r_fullbright.integer)
417         {
418                 if (!(entrender->effects & EF_FULLBRIGHT) && !(renderflags & RF_FULLBRIGHT))
419                         entrender->flags |= RENDER_LIGHT;
420         }
421         // hide player shadow during intermission or nehahra movie
422         if (!(entrender->effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST))
423          &&  (entrender->alpha >= 1)
424          && !(renderflags & RF_NOSHADOW)
425          && !(entrender->flags & RENDER_VIEWMODEL)
426          && (!(entrender->flags & RENDER_EXTERIORMODEL) || (!cl.intermission && cls.protocol != PROTOCOL_NEHAHRAMOVIE && !cl_noplayershadow.integer)))
427                 entrender->flags |= RENDER_SHADOW;
428         if (entrender->flags & RENDER_VIEWMODEL)
429                 entrender->flags |= RENDER_NOSELFSHADOW;
430         if (entrender->effects & EF_NOSELFSHADOW)
431                 entrender->flags |= RENDER_NOSELFSHADOW;
432         if (entrender->effects & EF_NODEPTHTEST)
433                 entrender->flags |= RENDER_NODEPTHTEST;
434         if (entrender->effects & EF_ADDITIVE)
435                 entrender->flags |= RENDER_ADDITIVE;
436         if (entrender->effects & EF_DOUBLESIDED)
437                 entrender->flags |= RENDER_DOUBLESIDED;
438         if (entrender->effects & EF_DYNAMICMODELLIGHT)
439                 entrender->flags |= RENDER_DYNAMICMODELLIGHT;
440
441         // make the other useful stuff
442         memcpy(entrender->framegroupblend, ed->priv.server->framegroupblend, sizeof(ed->priv.server->framegroupblend));
443         CL_UpdateRenderEntity(entrender);
444
445         // override animation data with full control
446         memcpy(entrender->frameblend, ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend));
447         if (ed->priv.server->skeleton.relativetransforms)
448                 entrender->skeleton = &ed->priv.server->skeleton;
449         else
450                 entrender->skeleton = NULL;
451
452         return true;
453 }
454
455 // 0 = keydown, key, character (EXT_CSQC)
456 // 1 = keyup, key, character (EXT_CSQC)
457 // 2 = mousemove relative, x, y (EXT_CSQC)
458 // 3 = mousemove absolute, x, y (DP_CSQC)
459 qbool CL_VM_InputEvent (int eventtype, float x, float y)
460 {
461         prvm_prog_t *prog = CLVM_prog;
462         qbool r;
463
464         if(!prog->loaded)
465                 return false;
466
467         if (!PRVM_clientfunction(CSQC_InputEvent))
468                 r = false;
469         else
470         {
471                 PRVM_clientglobalfloat(time) = cl.time;
472                 PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
473                 PRVM_G_FLOAT(OFS_PARM0) = eventtype;
474                 PRVM_G_FLOAT(OFS_PARM1) = x; // key or x
475                 PRVM_G_FLOAT(OFS_PARM2) = y; // ascii or y
476                 prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_InputEvent), "QC function CSQC_InputEvent is missing");
477                 r = CSQC_RETURNVAL != 0;
478         }
479         return r;
480 }
481
482 extern r_refdef_view_t csqc_original_r_refdef_view;
483 extern r_refdef_view_t csqc_main_r_refdef_view;
484 qbool CL_VM_UpdateView (double frametime)
485 {
486         prvm_prog_t *prog = CLVM_prog;
487         vec3_t emptyvector;
488         emptyvector[0] = 0;
489         emptyvector[1] = 0;
490         emptyvector[2] = 0;
491 //      vec3_t oldangles;
492
493         if(!prog->loaded)
494                 return false;
495
496         R_TimeReport("pre-UpdateView");
497
498         csqc_original_r_refdef_view = r_refdef.view;
499         csqc_main_r_refdef_view = r_refdef.view;
500         //VectorCopy(cl.viewangles, oldangles);
501         PRVM_clientglobalfloat(time) = cl.time;
502         PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
503         CSQC_SetGlobals(frametime);
504         // clear renderable entity and light lists to prevent crashes if the
505         // CSQC_UpdateView function does not call R_ClearScene as it should
506         r_refdef.scene.numentities = 0;
507         r_refdef.scene.numlights = 0;
508         // polygonbegin without draw2d arg has to guess
509         prog->polygonbegin_guess2d = false;
510         // free memory for resources that are no longer referenced
511         PRVM_GarbageCollection(prog);
512         // pass in width and height and menu/focus state as parameters (EXT_CSQC_1)
513         PRVM_G_FLOAT(OFS_PARM0) = vid.mode.width;
514         PRVM_G_FLOAT(OFS_PARM1) = vid.mode.height;
515         /*
516          * This should be fine for now but FTEQW uses flags for keydest
517          * and checks that an array called "eyeoffset" is 0
518          *
519          * Just a note in case there's compatibility problems later
520          */
521         PRVM_G_FLOAT(OFS_PARM2) = key_dest == key_game;
522         prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_UpdateView), "QC function CSQC_UpdateView is missing");
523         //VectorCopy(oldangles, cl.viewangles);
524         // Dresk : Reset Dmg Globals Here
525         CL_VM_UpdateDmgGlobals(0, 0, emptyvector);
526         r_refdef.view = csqc_main_r_refdef_view;
527         R_RenderView_UpdateViewVectors(); // we have to do this, as we undid the scene render doing this for us
528
529         R_TimeReport("UpdateView");
530         return true;
531 }
532
533 void CL_VM_DrawHud(double frametime)
534 {
535         prvm_prog_t *prog = CLVM_prog;
536
537         R_TimeReport("pre-DrawHud");
538
539         PRVM_clientglobalfloat(time) = cl.time;
540         PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
541         CSQC_SetGlobals(frametime);
542
543         PRVM_GarbageCollection(prog);
544
545         // width and height parameters are virtual in CSQC_SIMPLE engines
546         VectorSet(PRVM_G_VECTOR(OFS_PARM0), vid_conwidth.integer, vid_conheight.integer, 0);
547         PRVM_G_FLOAT(OFS_PARM1) = sb_showscores;
548         prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_DrawHud), "QC function CSQC_DrawHud is missing");
549
550         if (PRVM_clientfunction(CSQC_DrawScores))
551         {
552                 VectorSet(PRVM_G_VECTOR(OFS_PARM0), vid_conwidth.integer, vid_conheight.integer, 0);
553                 PRVM_G_FLOAT(OFS_PARM1) = sb_showscores;
554                 if (key_dest != key_menu)
555                         prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_DrawScores), "QC function CSQC_DrawScores is missing");
556         }
557         else if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
558                 if (!cl.islocalgame) // LadyHavoc: changed to draw the deathmatch overlays in any multiplayer mode
559                         Sbar_DeathmatchOverlay ();
560
561         R_TimeReport("DrawHud");
562 }
563
564
565 qbool CL_VM_ConsoleCommand(const char *text, size_t textlen)
566 {
567         prvm_prog_t *prog = CLVM_prog;
568         return PRVM_ConsoleCommand(prog, text, textlen, &prog->funcoffsets.CSQC_ConsoleCommand, false, cl.csqc_server2csqcentitynumber[cl.playerentity], cl.time, "QC function CSQC_ConsoleCommand is missing");
569 }
570
571 qbool CL_VM_Parse_TempEntity (void)
572 {
573         prvm_prog_t *prog = CLVM_prog;
574         int t;
575         qbool r = false;
576
577         if(!prog->loaded)
578                 return false;
579
580         if(PRVM_clientfunction(CSQC_Parse_TempEntity))
581         {
582                 t = cl_message.readcount;
583                 PRVM_clientglobalfloat(time) = cl.time;
584                 PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
585                 prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Parse_TempEntity), "QC function CSQC_Parse_TempEntity is missing");
586                 r = CSQC_RETURNVAL != 0;
587                 if(!r)
588                 {
589                         cl_message.readcount = t;
590                         cl_message.badread = false;
591                 }
592         }
593         return r;
594 }
595
596 void CL_VM_Parse_StuffCmd(const char *msg, size_t msg_len)
597 {
598         prvm_prog_t *prog = CLVM_prog;
599         int restorevm_tempstringsbuf_cursize;
600
601         if(msg[0] == 'c')
602         if(msg[1] == 's')
603         if(msg[2] == 'q')
604         if(msg[3] == 'c')
605         {
606                 // if this is setting a csqc variable, deprotect csqc_progcrc
607                 // temporarily so that it can be set by the cvar command,
608                 // and then reprotect it afterwards
609                 int crcflags = csqc_progcrc.flags;
610                 csqc_progcrc.flags &= ~CF_READONLY;
611                 csqc_progsize.flags &= ~CF_READONLY;
612                 Cmd_ExecuteString(cmd_local, msg, msg_len, src_local, true);
613                 csqc_progcrc.flags = csqc_progsize.flags = crcflags;
614                 return;
615         }
616
617         if(!prog->loaded)
618         {
619                 Cbuf_AddText(cmd_local, msg);
620                 return;
621         }
622
623         if(PRVM_clientfunction(CSQC_Parse_StuffCmd))
624         {
625                 PRVM_clientglobalfloat(time) = cl.time;
626                 PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
627                 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
628                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, msg, msg_len);
629                 prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Parse_StuffCmd), "QC function CSQC_Parse_StuffCmd is missing");
630                 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
631         }
632         else
633                 Cbuf_AddText(cmd_local, msg);
634 }
635
636 static void CL_VM_Parse_Print(const char *msg, size_t msg_len)
637 {
638         prvm_prog_t *prog = CLVM_prog;
639         int restorevm_tempstringsbuf_cursize;
640         PRVM_clientglobalfloat(time) = cl.time;
641         PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
642         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
643         PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, msg, msg_len);
644         prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Parse_Print), "QC function CSQC_Parse_Print is missing");
645         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
646 }
647
648 void CSQC_AddPrintText(const char *msg, size_t msg_len)
649 {
650         prvm_prog_t *prog = CLVM_prog;
651         char *start = cl.csqc_printtextbuf + cl.csqc_printtextbuf_len;
652         size_t writebytes = min(msg_len + 1, MAX_INPUTLINE - cl.csqc_printtextbuf_len);
653
654         if(prog->loaded && PRVM_clientfunction(CSQC_Parse_Print))
655         {
656                 if(msg[msg_len - 1] != '\n' && msg[msg_len - 1] != '\r')
657                 {
658                         if(cl.csqc_printtextbuf_len + msg_len + 1 >= MAX_INPUTLINE)
659                         {
660                                 CL_VM_Parse_Print(cl.csqc_printtextbuf, cl.csqc_printtextbuf_len);
661                                 cl.csqc_printtextbuf[0] = '\0';
662                                 cl.csqc_printtextbuf_len = 0;
663                         }
664                         else
665                         {
666                                 memcpy(start, msg, writebytes);
667                                 cl.csqc_printtextbuf_len += msg_len;
668                         }
669                         return;
670                 }
671                 memcpy(start, msg, writebytes);
672                 cl.csqc_printtextbuf_len += msg_len;
673                 CL_VM_Parse_Print(cl.csqc_printtextbuf, cl.csqc_printtextbuf_len);
674                 cl.csqc_printtextbuf[0] = '\0';
675                 cl.csqc_printtextbuf_len = 0;
676         }
677         else
678                 Con_Print(msg);
679 }
680
681 void CL_VM_Parse_CenterPrint(const char *msg, size_t msg_len)
682 {
683         prvm_prog_t *prog = CLVM_prog;
684         int restorevm_tempstringsbuf_cursize;
685
686         if(prog->loaded && PRVM_clientfunction(CSQC_Parse_CenterPrint))
687         {
688                 PRVM_clientglobalfloat(time) = cl.time;
689                 PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
690                 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
691                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, msg, msg_len);
692                 prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Parse_CenterPrint), "QC function CSQC_Parse_CenterPrint is missing");
693                 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
694         }
695         else
696                 SCR_CenterPrint(msg);
697 }
698
699 void CL_VM_UpdateIntermissionState (int intermission)
700 {
701         prvm_prog_t *prog = CLVM_prog;
702
703         if(prog->loaded)
704                 PRVM_clientglobalfloat(intermission) = intermission;
705 }
706 void CL_VM_UpdateShowingScoresState (int showingscores)
707 {
708         prvm_prog_t *prog = CLVM_prog;
709
710         if(prog->loaded)
711                 PRVM_clientglobalfloat(sb_showscores) = showingscores;
712 }
713 qbool CL_VM_Event_Sound(int sound_num, float fvolume, int channel, float attenuation, int ent, vec3_t pos, int flags, float speed)
714 {
715         prvm_prog_t *prog = CLVM_prog;
716         qbool r = false;
717
718         if(prog->loaded)
719         {
720                 if(PRVM_clientfunction(CSQC_Event_Sound))
721                 {
722                         PRVM_clientglobalfloat(time) = cl.time;
723                         PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
724                         PRVM_G_FLOAT(OFS_PARM0) = ent;
725                         PRVM_G_FLOAT(OFS_PARM1) = CHAN_ENGINE2USER(channel);
726                         PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(prog, cl.sound_name[sound_num], strlen(cl.sound_name[sound_num]));
727                         PRVM_G_FLOAT(OFS_PARM3) = fvolume;
728                         PRVM_G_FLOAT(OFS_PARM4) = attenuation;
729                         VectorCopy(pos, PRVM_G_VECTOR(OFS_PARM5) );
730                         PRVM_G_FLOAT(OFS_PARM6) = speed * 100.0f;
731                         PRVM_G_FLOAT(OFS_PARM7) = flags; // flags
732                         prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Event_Sound), "QC function CSQC_Event_Sound is missing");
733                         r = CSQC_RETURNVAL != 0;
734                 }
735         }
736
737         return r;
738 }
739 static void CL_VM_UpdateCoopDeathmatchGlobals (int gametype)
740 {
741         prvm_prog_t *prog = CLVM_prog;
742         // Avoid global names for clean(er) coding
743         int localcoop;
744         int localdeathmatch;
745
746         if(prog->loaded)
747         {
748                 if(gametype == GAME_COOP)
749                 {
750                         localcoop = 1;
751                         localdeathmatch = 0;
752                 }
753                 else if(gametype == GAME_DEATHMATCH)
754                 {
755                         localcoop = 0;
756                         localdeathmatch = 1;
757                 }
758                 else
759                 {
760                         // How did the ServerInfo send an unknown gametype?
761                         // Better just assign the globals as 0...
762                         localcoop = 0;
763                         localdeathmatch = 0;
764                 }
765                 PRVM_clientglobalfloat(coop) = localcoop;
766                 PRVM_clientglobalfloat(deathmatch) = localdeathmatch;
767         }
768 }
769 #if 0
770 static float CL_VM_Event (float event)          //[515]: needed ? I'd say "YES", but don't know for what :D
771 {
772         prvm_prog_t *prog = CLVM_prog;
773         float r = 0;
774
775         if(!prog->loaded)
776                 return 0;
777
778         if(PRVM_clientfunction(CSQC_Event))
779         {
780                 PRVM_clientglobalfloat(time) = cl.time;
781                 PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[cl.playerentity];
782                 PRVM_G_FLOAT(OFS_PARM0) = event;
783                 prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Event), "QC function CSQC_Event is missing");
784                 r = CSQC_RETURNVAL;
785         }
786         return r;
787 }
788 #endif
789
790 void CSQC_ReadEntities (void)
791 {
792         prvm_prog_t *prog = CLVM_prog;
793         unsigned short entnum, oldself, realentnum;
794
795         if(!prog->loaded)
796         {
797                 Host_Error ("CSQC_ReadEntities: CSQC is not loaded");
798                 return;
799         }
800
801         PRVM_clientglobalfloat(time) = cl.time;
802         oldself = PRVM_clientglobaledict(self);
803         while(1)
804         {
805                 entnum = MSG_ReadShort(&cl_message);
806                 if(!entnum || cl_message.badread)
807                         break;
808                 realentnum = entnum & 0x7FFF;
809                 PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[realentnum];
810                 if(entnum & 0x8000)
811                 {
812                         if(PRVM_clientglobaledict(self))
813                         {
814                                 prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Remove), "QC function CSQC_Ent_Remove is missing");
815                                 cl.csqc_server2csqcentitynumber[realentnum] = 0;
816                         }
817                         else
818                         {
819                                 // LadyHavoc: removing an entity that is already gone on
820                                 // the csqc side is possible for legitimate reasons (such
821                                 // as a repeat of the remove message), so no warning is
822                                 // needed
823                                 //Con_Printf("Bad csqc_server2csqcentitynumber map\n"); //[515]: never happens ?
824                         }
825                 }
826                 else
827                 {
828                         if(!PRVM_clientglobaledict(self))
829                         {
830                                 if(!PRVM_clientfunction(CSQC_Ent_Spawn))
831                                 {
832                                         prvm_edict_t    *ed;
833                                         ed = PRVM_ED_Alloc(prog);
834                                         PRVM_clientedictfloat(ed, entnum) = realentnum;
835                                         PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT_TO_PROG(ed);
836                                 }
837                                 else
838                                 {
839                                         // entity( float entnum ) CSQC_Ent_Spawn;
840                                         // the qc function should set entnum, too (this way it also can return world [2/1/2008 Andreas]
841                                         PRVM_G_FLOAT(OFS_PARM0) = (float) realentnum;
842                                         // make sure no one gets wrong ideas
843                                         PRVM_clientglobaledict(self) = 0;
844                                         prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Spawn), "QC function CSQC_Ent_Spawn is missing");
845                                         PRVM_clientglobaledict(self) = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT( PRVM_G_INT( OFS_RETURN ) );
846                                 }
847                                 PRVM_G_FLOAT(OFS_PARM0) = 1;
848                                 prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Update), "QC function CSQC_Ent_Update is missing");
849                         }
850                         else {
851                                 PRVM_G_FLOAT(OFS_PARM0) = 0;
852                                 prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Ent_Update), "QC function CSQC_Ent_Update is missing");
853                         }
854                 }
855         }
856         PRVM_clientglobaledict(self) = oldself;
857 }
858
859 static void CLVM_begin_increase_edicts(prvm_prog_t *prog)
860 {
861         // links don't survive the transition, so unlink everything
862         World_UnlinkAll(&cl.world);
863 }
864
865 static void CLVM_end_increase_edicts(prvm_prog_t *prog)
866 {
867         int i;
868         prvm_edict_t *ent;
869
870         // link every entity except world
871         for (i = 1, ent = prog->edicts;i < prog->num_edicts;i++, ent++)
872                 if (!ent->free)
873                         CL_LinkEdict(ent);
874 }
875
876 static void CLVM_init_edict(prvm_prog_t *prog, prvm_edict_t *e)
877 {
878         int edictnum = PRVM_NUM_FOR_EDICT(e);
879         entity_render_t *entrender;
880         CL_ExpandCSQCRenderEntities(edictnum);
881         entrender = cl.csqcrenderentities + edictnum;
882         e->priv.server->move = false; // don't move on first frame
883         memset(entrender, 0, sizeof(*entrender));
884         entrender->shadertime = cl.time;
885 }
886
887 static void CLVM_free_edict(prvm_prog_t *prog, prvm_edict_t *ed)
888 {
889         entity_render_t *entrender = cl.csqcrenderentities + PRVM_NUM_FOR_EDICT(ed);
890         R_DecalSystem_Reset(&entrender->decalsystem);
891         memset(entrender, 0, sizeof(*entrender));
892         World_UnlinkEdict(ed);
893         memset(ed->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
894         VM_RemoveEdictSkeleton(prog, ed);
895 #ifdef USEODE
896         World_Physics_RemoveFromEntity(&cl.world, ed);
897         World_Physics_RemoveJointFromEntity(&cl.world, ed);
898 #endif
899 }
900
901 static void CLVM_count_edicts(prvm_prog_t *prog)
902 {
903         int             i;
904         prvm_edict_t    *ent;
905         int             active = 0, models = 0, solid = 0;
906
907         for (i=0 ; i<prog->num_edicts ; i++)
908         {
909                 ent = PRVM_EDICT_NUM(i);
910                 if (ent->free)
911                         continue;
912                 active++;
913                 if (PRVM_clientedictfloat(ent, solid))
914                         solid++;
915                 if (PRVM_clientedictstring(ent, model))
916                         models++;
917         }
918
919         Con_Printf("num_edicts:%3i\n", prog->num_edicts);
920         Con_Printf("active    :%3i\n", active);
921         Con_Printf("view      :%3i\n", models);
922         Con_Printf("touch     :%3i\n", solid);
923 }
924
925 static qbool CLVM_load_edict(prvm_prog_t *prog, prvm_edict_t *ent)
926 {
927         return true;
928 }
929
930 // returns true if the packet is valid, false if end of file is reached
931 // used for dumping the CSQC download into demo files
932 qbool MakeDownloadPacket(const char *filename, unsigned char *data, size_t len, int crc, int cnt, sizebuf_t *buf, int protocol)
933 {
934         int packetsize = buf->maxsize - 7; // byte short long
935         int npackets = ((int)len + packetsize - 1) / (packetsize);
936         char vabuf[1024];
937
938         if(protocol == PROTOCOL_QUAKEWORLD)
939                 return false; // CSQC can't run in QW anyway
940
941         SZ_Clear(buf);
942         if(cnt == 0)
943         {
944                 MSG_WriteByte(buf, svc_stufftext);
945                 MSG_WriteString(buf, va(vabuf, sizeof(vabuf), "\ncl_downloadbegin %lu %s\n", (unsigned long)len, filename));
946                 return true;
947         }
948         else if(cnt >= 1 && cnt <= npackets)
949         {
950                 unsigned long thispacketoffset = (cnt - 1) * packetsize;
951                 int thispacketsize = (int)len - thispacketoffset;
952                 if(thispacketsize > packetsize)
953                         thispacketsize = packetsize;
954
955                 MSG_WriteByte(buf, svc_downloaddata);
956                 MSG_WriteLong(buf, thispacketoffset);
957                 MSG_WriteShort(buf, thispacketsize);
958                 SZ_Write(buf, data + thispacketoffset, thispacketsize);
959
960                 return true;
961         }
962         else if(cnt == npackets + 1)
963         {
964                 MSG_WriteByte(buf, svc_stufftext);
965                 MSG_WriteString(buf, va(vabuf, sizeof(vabuf), "\ncl_downloadfinished %lu %d\n", (unsigned long)len, crc));
966                 return true;
967         }
968         return false;
969 }
970
971 extern cvar_t csqc_usedemoprogs;
972 void CL_VM_Init (void)
973 {
974         prvm_prog_t *prog = CLVM_prog;
975         const char* csprogsfn = NULL;
976         unsigned char *csprogsdata = NULL;
977         fs_offset_t csprogsdatasize = 0;
978         int csprogsdatacrc, requiredcrc;
979         int requiredsize;
980         char vabuf[1024];
981
982         // reset csqc_progcrc after reading it, so that changing servers doesn't
983         // expect csqc on the next server
984         requiredcrc = csqc_progcrc.integer;
985         requiredsize = csqc_progsize.integer;
986         Cvar_SetValueQuick(&csqc_progcrc, -1);
987         Cvar_SetValueQuick(&csqc_progsize, -1);
988
989         // if the server is not requesting a csprogs, then we're done here
990         if (requiredcrc < 0)
991                 return;
992
993         // see if the requested csprogs.dat file matches the requested crc
994         if (!cls.demoplayback || csqc_usedemoprogs.integer)
995         {
996                 csprogsfn = va(vabuf, sizeof(vabuf), "dlcache/%s.%i.%i", csqc_progname.string, requiredsize, requiredcrc);
997                 if(cls.caughtcsprogsdata && cls.caughtcsprogsdatasize == requiredsize && CRC_Block(cls.caughtcsprogsdata, (size_t)cls.caughtcsprogsdatasize) == requiredcrc)
998                 {
999                         Con_DPrintf("Using buffered \"%s\"\n", csprogsfn);
1000                         csprogsdata = cls.caughtcsprogsdata;
1001                         csprogsdatasize = cls.caughtcsprogsdatasize;
1002                         cls.caughtcsprogsdata = NULL;
1003                         cls.caughtcsprogsdatasize = 0;
1004                 }
1005                 else
1006                 {
1007                         Con_DPrintf("Not using buffered \"%s\" (buffered: %p, %d)\n", csprogsfn, (void *)cls.caughtcsprogsdata, (int) cls.caughtcsprogsdatasize);
1008                         csprogsdata = FS_LoadFile(csprogsfn, tempmempool, true, &csprogsdatasize);
1009                 }
1010         }
1011         if (!csprogsdata)
1012         {
1013                 csprogsfn = csqc_progname.string;
1014                 csprogsdata = FS_LoadFile(csprogsfn, tempmempool, true, &csprogsdatasize);
1015         }
1016         if (csprogsdata)
1017         {
1018                 csprogsdatacrc = CRC_Block(csprogsdata, (size_t)csprogsdatasize);
1019                 if (csprogsdatacrc != requiredcrc || csprogsdatasize != requiredsize)
1020                 {
1021                         if (cls.demoplayback)
1022                         {
1023                                 Con_Printf(CON_WARN "Warning: 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);
1024                                 // Mem_Free(csprogsdata);
1025                                 // return;
1026                                 // We WANT to continue here, and play the demo with different csprogs!
1027                                 // After all, this is just a warning. Sure things may go wrong from here.
1028                         }
1029                         else
1030                         {
1031                                 Mem_Free(csprogsdata);
1032                                 CL_DisconnectEx(false, "Your %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);
1033                                 return;
1034                         }
1035                 }
1036         }
1037         else
1038         {
1039                 if (requiredcrc >= 0)
1040                         CL_DisconnectEx(false, CON_ERROR "CL_VM_Init: %s requires CSQC, but \"%s\" wasn't found\n", cls.demoplayback ? "demo" : "server", csqc_progname.string);
1041                 return;
1042         }
1043
1044         PRVM_Prog_Init(prog, cmd_local);
1045
1046         // allocate the mempools
1047         prog->progs_mempool = Mem_AllocPool(csqc_progname.string, 0, NULL);
1048         prog->edictprivate_size = 0; // no private struct used
1049         prog->name = "client";
1050         prog->num_edicts = 1;
1051         prog->max_edicts = 512;
1052         prog->limit_edicts = CL_MAX_EDICTS;
1053         prog->reserved_edicts = 0;
1054         prog->edictprivate_size = sizeof(edict_engineprivate_t);
1055         // TODO: add a shared extension string #define and add real support for csqc extension strings [12/5/2007 Black]
1056         prog->extensionstring = vm_sv_extensions;
1057         prog->builtins = vm_cl_builtins;
1058         prog->numbuiltins = vm_cl_numbuiltins;
1059
1060         // all callbacks must be defined (pointers are not checked before calling)
1061         prog->begin_increase_edicts = CLVM_begin_increase_edicts;
1062         prog->end_increase_edicts   = CLVM_end_increase_edicts;
1063         prog->init_edict            = CLVM_init_edict;
1064         prog->free_edict            = CLVM_free_edict;
1065         prog->count_edicts          = CLVM_count_edicts;
1066         prog->load_edict            = CLVM_load_edict;
1067         prog->init_cmd              = CLVM_init_cmd;
1068         prog->reset_cmd             = CLVM_reset_cmd;
1069         prog->error_cmd             = Host_Error;
1070         prog->ExecuteProgram        = CLVM_ExecuteProgram;
1071
1072         PRVM_Prog_Load(prog, csprogsfn, csprogsdata, csprogsdatasize, CL_CheckRequiredFuncs, CL_REQFIELDS, cl_reqfields, CL_REQGLOBALS, cl_reqglobals);
1073
1074         if (!prog->loaded)
1075         {
1076                 Mem_Free(csprogsdata);
1077                 Host_Error("CSQC %s failed to load\n", csprogsfn);
1078         }
1079
1080         if(cls.demorecording)
1081         {
1082                 if(cls.demo_lastcsprogssize != csprogsdatasize || cls.demo_lastcsprogscrc != csprogsdatacrc)
1083                 {
1084                         int i;
1085                         static char buf[NET_MAXMESSAGE];
1086                         sizebuf_t sb;
1087                         unsigned char *demobuf; fs_offset_t demofilesize;
1088
1089                         sb.data = (unsigned char *) buf;
1090                         sb.maxsize = sizeof(buf);
1091                         i = 0;
1092
1093                         CL_CutDemo(&demobuf, &demofilesize);
1094                         while(MakeDownloadPacket(csqc_progname.string, csprogsdata, (size_t)csprogsdatasize, csprogsdatacrc, i++, &sb, cls.protocol))
1095                                 CL_WriteDemoMessage(&sb);
1096                         CL_PasteDemo(&demobuf, &demofilesize);
1097
1098                         cls.demo_lastcsprogssize = csprogsdatasize;
1099                         cls.demo_lastcsprogscrc = csprogsdatacrc;
1100                 }
1101         }
1102         Mem_Free(csprogsdata);
1103
1104         // check if OP_STATE animation is possible in this dat file
1105         if (prog->fieldoffsets.nextthink >= 0 && prog->fieldoffsets.frame >= 0 && prog->fieldoffsets.think >= 0 && prog->globaloffsets.self >= 0)
1106                 prog->flag |= PRVM_OP_STATE;
1107
1108         // set time
1109         PRVM_clientglobalfloat(time) = cl.time;
1110         PRVM_clientglobaledict(self) = 0;
1111
1112         PRVM_clientglobalstring(mapname) = PRVM_SetEngineString(prog, cl.worldbasename);
1113         PRVM_clientglobalfloat(player_localnum) = cl.realplayerentity - 1;
1114         PRVM_clientglobalfloat(player_localentnum) = cl.viewentity;
1115
1116         // set map description (use world entity 0)
1117         PRVM_clientedictstring(prog->edicts, message) = PRVM_SetEngineString(prog, cl.worldmessage);
1118         VectorCopy(cl.world.mins, PRVM_clientedictvector(prog->edicts, mins));
1119         VectorCopy(cl.world.maxs, PRVM_clientedictvector(prog->edicts, maxs));
1120         VectorCopy(cl.world.mins, PRVM_clientedictvector(prog->edicts, absmin));
1121         VectorCopy(cl.world.maxs, PRVM_clientedictvector(prog->edicts, absmax));
1122         PRVM_clientedictfloat(prog->edicts, solid) = SOLID_BSP;
1123         PRVM_clientedictfloat(prog->edicts, modelindex) = 1;
1124         PRVM_clientedictstring(prog->edicts, model) = PRVM_SetEngineString(prog, cl.worldmodel->name);
1125
1126         // call the prog init if it exists
1127         if (PRVM_clientfunction(CSQC_Init))
1128         {
1129                 PRVM_G_FLOAT(OFS_PARM0) = 1.0f; // CSQC_SIMPLE engines always pass 0, FTE always passes 1
1130                 PRVM_G_INT(OFS_PARM1) = PRVM_SetEngineString(prog, gamename);
1131                 PRVM_G_FLOAT(OFS_PARM2) = 1.0f; // TODO DP versions...
1132                 prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Init), "QC function CSQC_Init is missing");
1133         }
1134
1135         // Once CSQC_Init was called, we consider csqc code fully initialized.
1136         prog->inittime = host.realtime;
1137
1138         cl.csqc_vidvars.drawcrosshair = false;
1139         cl.csqc_vidvars.drawenginesbar = false;
1140
1141         // Update Coop and Deathmatch Globals (at this point the client knows them from ServerInfo)
1142         CL_VM_UpdateCoopDeathmatchGlobals(cl.gametype);
1143 }
1144
1145 void CL_VM_ShutDown (void)
1146 {
1147         prvm_prog_t *prog = CLVM_prog;
1148         Cmd_ClearCSQCCommands(cmd_local);
1149
1150         //Cvar_SetValueQuick(&csqc_progcrc, -1);
1151         //Cvar_SetValueQuick(&csqc_progsize, -1);
1152         if (prog->loaded)
1153         {
1154                 PRVM_clientglobalfloat(time) = cl.time;
1155                 PRVM_clientglobaledict(self) = 0;
1156                 if (PRVM_clientfunction(CSQC_Shutdown))
1157                         prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Shutdown), "QC function CSQC_Shutdown is missing");
1158         }
1159         PRVM_Prog_Reset(prog);
1160         Con_DPrint("CSQC ^1unloaded\n");
1161 }
1162
1163 qbool CL_VM_GetEntitySoundOrigin(int entnum, vec3_t out)
1164 {
1165         prvm_prog_t *prog = CLVM_prog;
1166         prvm_edict_t *ed;
1167         model_t *mod;
1168         matrix4x4_t matrix;
1169         qbool r = 0;
1170
1171         ed = PRVM_EDICT_NUM(entnum - MAX_EDICTS);
1172
1173         if(!ed->free)
1174         {
1175                 mod = CL_GetModelFromEdict(ed);
1176                 VectorCopy(PRVM_clientedictvector(ed, origin), out);
1177                 if(CL_GetTagMatrix(prog, &matrix, ed, 0, NULL) == 0)
1178                         Matrix4x4_OriginFromMatrix(&matrix, out);
1179                 if (mod && mod->soundfromcenter)
1180                         VectorMAMAM(1.0f, out, 0.5f, mod->normalmins, 0.5f, mod->normalmaxs, out);
1181                 r = 1;
1182         }
1183
1184         return r;
1185 }
1186
1187 qbool CL_VM_TransformView(int entnum, matrix4x4_t *viewmatrix, mplane_t *clipplane, vec3_t visorigin)
1188 {
1189         prvm_prog_t *prog = CLVM_prog;
1190         qbool ret = false;
1191         prvm_edict_t *ed;
1192         vec3_t forward, left, up, origin, ang;
1193         matrix4x4_t mat, matq;
1194
1195         ed = PRVM_EDICT_NUM(entnum);
1196         // camera:
1197         //   camera_transform
1198         if(PRVM_clientedictfunction(ed, camera_transform))
1199         {
1200                 ret = true;
1201                 if(viewmatrix && clipplane && visorigin)
1202                 {
1203                         Matrix4x4_ToVectors(viewmatrix, forward, left, up, origin);
1204                         AnglesFromVectors(ang, forward, up, false);
1205                         PRVM_clientglobalfloat(time) = cl.time;
1206                         PRVM_clientglobaledict(self) = entnum;
1207                         VectorCopy(origin, PRVM_G_VECTOR(OFS_PARM0));
1208                         VectorCopy(ang, PRVM_G_VECTOR(OFS_PARM1));
1209                         VectorCopy(forward, PRVM_clientglobalvector(v_forward));
1210                         VectorScale(left, -1, PRVM_clientglobalvector(v_right));
1211                         VectorCopy(up, PRVM_clientglobalvector(v_up));
1212                         VectorCopy(origin, PRVM_clientglobalvector(trace_endpos));
1213                         prog->ExecuteProgram(prog, PRVM_clientedictfunction(ed, camera_transform), "QC function e.camera_transform is missing");
1214                         VectorCopy(PRVM_G_VECTOR(OFS_RETURN), origin);
1215                         VectorCopy(PRVM_clientglobalvector(v_forward), forward);
1216                         VectorScale(PRVM_clientglobalvector(v_right), -1, left);
1217                         VectorCopy(PRVM_clientglobalvector(v_up), up);
1218                         VectorCopy(PRVM_clientglobalvector(trace_endpos), visorigin);
1219                         Matrix4x4_Invert_Full(&mat, viewmatrix);
1220                         Matrix4x4_FromVectors(viewmatrix, forward, left, up, origin);
1221                         Matrix4x4_Concat(&matq, viewmatrix, &mat);
1222                         Matrix4x4_TransformPositivePlane(&matq, clipplane->normal[0], clipplane->normal[1], clipplane->normal[2], clipplane->dist, clipplane->normal_and_dist);
1223                 }
1224         }
1225
1226         return ret;
1227 }
1228
1229 int CL_VM_GetViewEntity(void)
1230 {
1231         if(cl.csqc_server2csqcentitynumber[cl.viewentity])
1232                 return cl.csqc_server2csqcentitynumber[cl.viewentity] + MAX_EDICTS;
1233         return cl.viewentity;
1234 }