]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_main.c
-safe now does something approximately close to correct. Not a full fix
[divverent/darkplaces.git] / cl_main.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
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 // cl_main.c  -- client main loop
21
22 #include "quakedef.h"
23
24 // we need to declare some mouse variables here, because the menu system
25 // references them even when on a unix system.
26
27 // these two are not intended to be set directly
28 cvar_t  cl_name = {CVAR_SAVE, "_cl_name", "player"};
29 cvar_t  cl_color = {CVAR_SAVE, "_cl_color", "0"};
30 cvar_t  cl_pmodel = {CVAR_SAVE, "_cl_pmodel", "0"};
31
32 cvar_t  cl_shownet = {0, "cl_shownet","0"};
33 cvar_t  cl_nolerp = {0, "cl_nolerp", "0"};
34
35 cvar_t  lookspring = {CVAR_SAVE, "lookspring","0"};
36 cvar_t  lookstrafe = {CVAR_SAVE, "lookstrafe","0"};
37 cvar_t  sensitivity = {CVAR_SAVE, "sensitivity","3", 1, 30};
38
39 cvar_t  m_pitch = {CVAR_SAVE, "m_pitch","0.022"};
40 cvar_t  m_yaw = {CVAR_SAVE, "m_yaw","0.022"};
41 cvar_t  m_forward = {CVAR_SAVE, "m_forward","1"};
42 cvar_t  m_side = {CVAR_SAVE, "m_side","0.8"};
43
44 cvar_t freelook = {CVAR_SAVE, "freelook", "1"};
45
46 client_static_t cls;
47 client_state_t  cl;
48 // FIXME: put these on hunk?
49 entity_t                cl_entities[MAX_EDICTS];
50 entity_t                cl_static_entities[MAX_STATIC_ENTITIES];
51 lightstyle_t    cl_lightstyle[MAX_LIGHTSTYLES];
52
53 int                             cl_numvisedicts;
54 entity_t                *cl_visedicts[MAX_VISEDICTS];
55
56 /*
57 =====================
58 CL_ClearState
59
60 =====================
61 */
62 void CL_ClearState (void)
63 {
64         int                     i;
65
66         if (!sv.active)
67                 Host_ClearMemory ();
68
69 // wipe the entire cl structure
70         memset (&cl, 0, sizeof(cl));
71
72         SZ_Clear (&cls.message);
73
74 // clear other arrays
75         memset (cl_entities, 0, sizeof(cl_entities));
76         memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
77         memset (cl_temp_entities, 0, sizeof(cl_temp_entities));
78         memset (cl_beams, 0, sizeof(cl_beams));
79         // LordHavoc: have to set up the baseline info for alpha and other stuff
80         for (i = 0;i < MAX_EDICTS;i++)
81         {
82                 ClearStateToDefault(&cl_entities[i].state_baseline);
83                 ClearStateToDefault(&cl_entities[i].state_previous);
84                 ClearStateToDefault(&cl_entities[i].state_current);
85         }
86 }
87
88 void CL_LerpUpdate(entity_t *e, int frame, int modelindex)
89 {
90         entity_persistent_t *p;
91         entity_render_t *r;
92         p = &e->persistent;
93         r = &e->render;
94
95         if (p->modelindex != modelindex)
96         {
97                 // reset all interpolation information
98                 p->modelindex = modelindex;
99                 p->frame1 = p->frame2 = frame;
100                 p->frame1time = p->frame2time = cl.time;
101                 p->framelerp = 1;
102         }
103         else if (p->frame2 != frame)
104         {
105                 // transition to new frame
106                 p->frame1 = p->frame2;
107                 p->frame1time = p->frame2time;
108                 p->frame2 = frame;
109                 p->frame2time = cl.time;
110                 p->framelerp = 0;
111         }
112         else
113         {
114                 // update transition
115                 p->framelerp = (cl.time - p->frame2time) * 10;
116                 p->framelerp = bound(0, p->framelerp, 1);
117         }
118
119         r->frame1 = p->frame1;
120         r->frame2 = p->frame2;
121         r->framelerp = p->framelerp;
122         r->frame1time = p->frame1time;
123         r->frame2time = p->frame2time;
124 }
125
126 /*
127 =====================
128 CL_Disconnect
129
130 Sends a disconnect message to the server
131 This is also called on Host_Error, so it shouldn't cause any errors
132 =====================
133 */
134 void CL_Disconnect (void)
135 {
136 // stop sounds (especially looping!)
137         S_StopAllSounds (true);
138
139         // clear contents blends
140         cl.cshifts[0].percent = 0;
141         cl.cshifts[1].percent = 0;
142         cl.cshifts[2].percent = 0;
143         cl.cshifts[3].percent = 0;
144
145 // if running a local server, shut it down
146         if (cls.demoplayback)
147                 CL_StopPlayback ();
148         else if (cls.state == ca_connected)
149         {
150                 if (cls.demorecording)
151                         CL_Stop_f ();
152
153                 Con_DPrintf ("Sending clc_disconnect\n");
154                 SZ_Clear (&cls.message);
155                 MSG_WriteByte (&cls.message, clc_disconnect);
156                 NET_SendUnreliableMessage (cls.netcon, &cls.message);
157                 SZ_Clear (&cls.message);
158                 NET_Close (cls.netcon);
159
160                 cls.state = ca_disconnected;
161                 if (sv.active)
162                         Host_ShutdownServer(false);
163         }
164
165         cls.demoplayback = cls.timedemo = false;
166         cls.signon = 0;
167 }
168
169 void CL_Disconnect_f (void)
170 {
171         CL_Disconnect ();
172         if (sv.active)
173                 Host_ShutdownServer (false);
174 }
175
176
177
178
179 /*
180 =====================
181 CL_EstablishConnection
182
183 Host should be either "local" or a net address to be passed on
184 =====================
185 */
186 void CL_EstablishConnection (char *host)
187 {
188         if (cls.state == ca_dedicated)
189                 return;
190
191         if (cls.demoplayback)
192                 return;
193
194         CL_Disconnect ();
195
196         cls.netcon = NET_Connect (host);
197         if (!cls.netcon)
198                 Host_Error ("CL_Connect: connect failed\n");
199         Con_DPrintf ("CL_EstablishConnection: connected to %s\n", host);
200         
201         cls.demonum = -1;                       // not in the demo loop now
202         cls.state = ca_connected;
203         cls.signon = 0;                         // need all the signon messages before playing
204 }
205
206 /*
207 =====================
208 CL_SignonReply
209
210 An svc_signonnum has been received, perform a client side setup
211 =====================
212 */
213 void CL_SignonReply (void)
214 {
215         char    str[8192];
216
217 Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
218
219         switch (cls.signon)
220         {
221         case 1:
222                 MSG_WriteByte (&cls.message, clc_stringcmd);
223                 MSG_WriteString (&cls.message, "prespawn");
224                 break;
225
226         case 2:
227                 MSG_WriteByte (&cls.message, clc_stringcmd);
228                 MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string));
229
230                 MSG_WriteByte (&cls.message, clc_stringcmd);
231                 MSG_WriteString (&cls.message, va("color %i %i\n", ((int)cl_color.value)>>4, ((int)cl_color.value)&15));
232         
233                 if (cl_pmodel.value)
234                 {
235                         MSG_WriteByte (&cls.message, clc_stringcmd);
236                         MSG_WriteString (&cls.message, va("pmodel %f\n", cl_pmodel.value));
237                 }
238
239                 MSG_WriteByte (&cls.message, clc_stringcmd);
240                 sprintf (str, "spawn %s", cls.spawnparms);
241                 MSG_WriteString (&cls.message, str);
242                 break;
243                 
244         case 3: 
245                 MSG_WriteByte (&cls.message, clc_stringcmd);
246                 MSG_WriteString (&cls.message, "begin");
247                 Cache_Report ();                // print remaining memory
248                 break;
249                 
250         case 4:
251 //              SCR_EndLoadingPlaque ();                // allow normal screen updates
252                 Con_ClearNotify();
253                 break;
254         }
255 }
256
257 /*
258 =====================
259 CL_NextDemo
260
261 Called to play the next demo in the demo loop
262 =====================
263 */
264 void CL_NextDemo (void)
265 {
266         char    str[1024];
267
268         if (cls.demonum == -1)
269                 return;         // don't play demos
270
271 //      SCR_BeginLoadingPlaque ();
272
273         if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
274         {
275                 cls.demonum = 0;
276                 if (!cls.demos[cls.demonum][0])
277                 {
278                         Con_Printf ("No demos listed with startdemos\n");
279                         cls.demonum = -1;
280                         return;
281                 }
282         }
283
284         sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]);
285         Cbuf_InsertText (str);
286         cls.demonum++;
287 }
288
289 /*
290 ==============
291 CL_PrintEntities_f
292 ==============
293 */
294 void CL_PrintEntities_f (void)
295 {
296         entity_t        *ent;
297         int                     i, j;
298         char            name[32];
299         
300         for (i = 0, ent = cl_entities;i < MAX_EDICTS /*cl.num_entities*/;i++, ent++)
301         {
302                 if (!ent->state_current.active)
303                         continue;
304                 if (!ent->render.model)
305                         continue;
306
307                 Con_Printf ("%3i:", i);
308                 if (!ent->render.model)
309                 {
310                         Con_Printf ("EMPTY\n");
311                         continue;
312                 }
313                 strncpy(name, ent->render.model->name, 30);
314                 name[30] = 0;
315                 for (j = strlen(name);j < 30;j++)
316                         name[j] = ' ';
317                 Con_Printf ("%s:%04i (%5i %5i %5i) [%3i %3i %3i]\n", name, ent->render.frame, (int) ent->render.origin[0], (int) ent->render.origin[1], (int) ent->render.origin[2], (int) ent->render.angles[0] % 360, (int) ent->render.angles[1] % 360, (int) ent->render.angles[2] % 360);
318         }
319 }
320
321
322 /*
323 ===============
324 CL_LerpPoint
325
326 Determines the fraction between the last two messages that the objects
327 should be put at.
328 ===============
329 */
330 float   CL_LerpPoint (void)
331 {
332         float   f, frac;
333
334         f = cl.mtime[0] - cl.mtime[1];
335
336         // LordHavoc: lerp in listen games as the server is being capped below the client (usually)
337         if (!f || cl_nolerp.value || cls.timedemo || (sv.active && svs.maxclients == 1))
338         {
339                 cl.time = cl.mtime[0];
340                 return 1;
341         }
342                 
343         if (f > 0.1)
344         {       // dropped packet, or start of demo
345                 cl.mtime[1] = cl.mtime[0] - 0.1;
346                 f = 0.1;
347         }
348         frac = (cl.time - cl.mtime[1]) / f;
349 //      Con_Printf ("frac: %f\n",frac);
350         if (frac < 0)
351         {
352                 if (frac < -0.01)
353                 {
354                         cl.time = cl.mtime[1];
355 //                      Con_Printf ("low frac\n");
356                 }
357                 frac = 0;
358         }
359         else if (frac > 1)
360         {
361                 if (frac > 1.01)
362                 {
363                         cl.time = cl.mtime[0];
364 //                      Con_Printf ("high frac\n");
365                 }
366                 frac = 1;
367         }
368                 
369         return frac;
370 }
371
372 float CL_EntityLerpPoint (entity_t *ent)
373 {
374         float   f;
375
376         if (cl_nolerp.value || cls.timedemo || (sv.active && svs.maxclients == 1))
377                 return 1;
378
379         f = ent->state_current.time - ent->state_previous.time;
380 //      Con_Printf(" %g-%g=%g", ent->state_current.time, ent->state_previous.time, f);
381
382         if (f <= 0)
383                 return 1;
384         if (f >= 0.1)
385                 f = 0.1;
386
387 //      Con_Printf(" %g-%g/%g=%f", cl.time, ent->state_previous.time, f, (cl.time - ent->state_previous.time) / f);
388         f = (cl.time - ent->state_previous.time) / f;
389         return bound(0, f, 1);
390 }
391
392 void CL_RelinkStaticEntities(void)
393 {
394         int i;
395         for (i = 0;i < cl.num_statics && cl_numvisedicts < MAX_VISEDICTS;i++)
396                 cl_visedicts[cl_numvisedicts++] = &cl_static_entities[i];
397 }
398
399 /*
400 ===============
401 CL_RelinkEntities
402 ===============
403 */
404 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent);
405 void CL_RelinkNetworkEntities()
406 {
407         entity_t        *ent;
408         int                     i, j, glowcolor, effects;
409         float           f, d, bobjrotate/*, bobjoffset*/, dlightradius, glowsize;
410         vec3_t          oldorg, neworg, delta, dlightcolor;
411
412         bobjrotate = ANGLEMOD(100*cl.time);
413 //      bobjoffset = cos(180 * cl.time * M_PI / 180) * 4.0f + 4.0f;
414
415         CL_RelinkStaticEntities();
416
417 // start on the entity after the world
418         for (i = 1, ent = cl_entities + 1;i < MAX_EDICTS /*cl.num_entities*/;i++, ent++)
419         {
420                 // if the object wasn't included in the latest packet, remove it
421                 if (!ent->state_current.active)
422                         continue;
423
424                 VectorCopy(ent->persistent.trail_origin, oldorg);
425
426                 if (!ent->state_previous.active)
427                 {
428                         // only one state available
429                         VectorCopy (ent->state_current.origin, neworg);
430                         VectorCopy (ent->state_current.angles, ent->render.angles);
431                 }
432                 else
433                 {
434                         // if the delta is large, assume a teleport and don't lerp
435                         VectorSubtract(ent->state_current.origin, ent->state_previous.origin, delta);
436                         // LordHavoc: increased tolerance from 100 to 200
437                         if (DotProduct(delta, delta) > 200*200)
438                                 f = 1;
439                         else
440                                 f = CL_EntityLerpPoint(ent);
441                         if (f >= 1)
442                         {
443                                 // no interpolation
444                                 VectorCopy (ent->state_current.origin, neworg);
445                                 VectorCopy (ent->state_current.angles, ent->render.angles);
446                         }
447                         else
448                         {
449                                 // interpolate the origin and angles
450                                 for (j = 0;j < 3;j++)
451                                 {
452                                         neworg[j] = ent->state_previous.origin[j] + f*delta[j];
453
454                                         d = ent->state_current.angles[j] - ent->state_previous.angles[j];
455                                         if (d > 180)
456                                                 d -= 360;
457                                         else if (d < -180)
458                                                 d += 360;
459                                         ent->render.angles[j] = ent->state_previous.angles[j] + f*d;
460                                 }
461                         }
462                 }
463
464                 VectorCopy (neworg, ent->persistent.trail_origin);
465                 // persistent.modelindex will be updated by CL_LerpUpdate
466                 if (ent->state_current.modelindex != ent->persistent.modelindex)
467                         VectorCopy(neworg, oldorg);
468
469                 VectorCopy (neworg, ent->render.origin);
470                 ent->render.flags = ent->state_current.flags;
471                 ent->render.effects = effects = ent->state_current.effects;
472                 ent->render.model = cl.model_precache[ent->state_current.modelindex];
473                 ent->render.frame = ent->state_current.frame;
474                 if (cl.scores == NULL || !ent->state_current.colormap)
475                         ent->render.colormap = -1; // no special coloring
476                 else
477                         ent->render.colormap = cl.scores[ent->state_current.colormap - 1].colors; // color it
478                 ent->render.skinnum = ent->state_current.skin;
479                 ent->render.alpha = ent->state_current.alpha * (1.0f / 255.0f); // FIXME: interpolate?
480                 ent->render.scale = ent->state_current.scale * (1.0f / 16.0f); // FIXME: interpolate?
481                 glowsize = ent->state_current.glowsize * 4.0f; // FIXME: interpolate?
482                 glowcolor = ent->state_current.glowcolor;
483                 ent->render.colormod[0] = (float) ((ent->state_current.colormod >> 5) & 7) * (1.0f / 7.0f);
484                 ent->render.colormod[1] = (float) ((ent->state_current.colormod >> 2) & 7) * (1.0f / 7.0f);
485                 ent->render.colormod[2] = (float) (ent->state_current.colormod & 3) * (1.0f / 3.0f);
486
487                 // update interpolation info
488                 CL_LerpUpdate(ent, ent->state_current.frame, ent->state_current.modelindex);
489
490                 // handle effects now...
491                 dlightradius = 0;
492                 dlightcolor[0] = 0;
493                 dlightcolor[1] = 0;
494                 dlightcolor[2] = 0;
495
496                 // LordHavoc: if the entity has no effects, don't check each
497                 if (effects)
498                 {
499                         if (effects & EF_BRIGHTFIELD)
500                                 R_EntityParticles (ent);
501                         if (effects & EF_MUZZLEFLASH)
502                         {
503                                 vec3_t v, v2;
504
505                                 AngleVectors (ent->render.angles, v, NULL, NULL);
506
507                                 v2[0] = v[0] * 18 + neworg[0];
508                                 v2[1] = v[1] * 18 + neworg[1];
509                                 v2[2] = v[2] * 18 + neworg[2] + 16;
510                                 TraceLine(neworg, v2, v, NULL, 0);
511
512                                 CL_AllocDlight (NULL, v, 100, 1, 1, 1, 0, 0.1);
513                         }
514                         if (effects & EF_DIMLIGHT)
515                         {
516                                 dlightcolor[0] += 200.0f;
517                                 dlightcolor[1] += 200.0f;
518                                 dlightcolor[2] += 200.0f;
519                         }
520                         if (effects & EF_BRIGHTLIGHT)
521                         {
522                                 dlightcolor[0] += 400.0f;
523                                 dlightcolor[1] += 400.0f;
524                                 dlightcolor[2] += 400.0f;
525                         }
526                         // LordHavoc: added EF_RED and EF_BLUE
527                         if (effects & EF_RED) // red
528                         {
529                                 dlightcolor[0] += 200.0f;
530                                 dlightcolor[1] +=  20.0f;
531                                 dlightcolor[2] +=  20.0f;
532                         }
533                         if (effects & EF_BLUE) // blue
534                         {
535                                 dlightcolor[0] +=  20.0f;
536                                 dlightcolor[1] +=  20.0f;
537                                 dlightcolor[2] += 200.0f;
538                         }
539                         if (effects & EF_FLAME)
540                         {
541                                 if (ent->render.model)
542                                 {
543                                         vec3_t mins, maxs;
544                                         int temp;
545                                         if (ent->render.angles[0] || ent->render.angles[2])
546                                         {
547                                                 VectorAdd(neworg, ent->render.model->rotatedmins, mins);
548                                                 VectorAdd(neworg, ent->render.model->rotatedmaxs, maxs);
549                                         }
550                                         else if (ent->render.angles[1])
551                                         {
552                                                 VectorAdd(neworg, ent->render.model->yawmins, mins);
553                                                 VectorAdd(neworg, ent->render.model->yawmaxs, maxs);
554                                         }
555                                         else
556                                         {
557                                                 VectorAdd(neworg, ent->render.model->normalmins, mins);
558                                                 VectorAdd(neworg, ent->render.model->normalmaxs, maxs);
559                                         }
560                                         // how many flames to make
561                                         temp = (int) (cl.time * 300) - (int) (cl.oldtime * 300);
562                                         R_FlameCube(mins, maxs, temp);
563                                 }
564                                 d = lhrandom(200, 250);
565                                 dlightcolor[0] += d * 1.0f;
566                                 dlightcolor[1] += d * 0.7f;
567                                 dlightcolor[2] += d * 0.3f;
568                         }
569                 }
570
571                 // LordHavoc: if the model has no flags, don't check each
572                 if (ent->render.model && ent->render.model->flags)
573                 {
574                         if (ent->render.model->flags & EF_ROTATE)
575                         {
576                                 ent->render.angles[1] = bobjrotate;
577 //                              ent->render.origin[2] += bobjoffset;
578                         }
579                         // only do trails if present in the previous frame as well
580                         if (ent->state_previous.active)
581                         {
582                                 if (ent->render.model->flags & EF_GIB)
583                                         R_RocketTrail (oldorg, neworg, 2, ent);
584                                 else if (ent->render.model->flags & EF_ZOMGIB)
585                                         R_RocketTrail (oldorg, neworg, 4, ent);
586                                 else if (ent->render.model->flags & EF_TRACER)
587                                         R_RocketTrail (oldorg, neworg, 3, ent);
588                                 else if (ent->render.model->flags & EF_TRACER2)
589                                         R_RocketTrail (oldorg, neworg, 5, ent);
590                                 else if (ent->render.model->flags & EF_ROCKET)
591                                 {
592                                         R_RocketTrail (oldorg, ent->render.origin, 0, ent);
593                                         dlightcolor[0] += 200.0f;
594                                         dlightcolor[1] += 160.0f;
595                                         dlightcolor[2] +=  80.0f;
596                                 }
597                                 else if (ent->render.model->flags & EF_GRENADE)
598                                 {
599                                         if (ent->render.alpha == -1) // LordHavoc: Nehahra dem compatibility
600                                                 R_RocketTrail (oldorg, neworg, 7, ent);
601                                         else
602                                                 R_RocketTrail (oldorg, neworg, 1, ent);
603                                 }
604                                 else if (ent->render.model->flags & EF_TRACER3)
605                                         R_RocketTrail (oldorg, neworg, 6, ent);
606                         }
607                 }
608                 // LordHavoc: customizable glow
609                 if (glowsize)
610                 {
611                         byte *tempcolor = (byte *)&d_8to24table[glowcolor];
612                         dlightcolor[0] += glowsize * tempcolor[0] * (1.0f / 255.0f);
613                         dlightcolor[1] += glowsize * tempcolor[1] * (1.0f / 255.0f);
614                         dlightcolor[2] += glowsize * tempcolor[2] * (1.0f / 255.0f);
615                 }
616                 // LordHavoc: customizable trail
617                 if (ent->render.flags & RENDER_GLOWTRAIL)
618                         R_RocketTrail2 (oldorg, neworg, glowcolor, ent);
619
620                 if (dlightcolor[0] || dlightcolor[1] || dlightcolor[2])
621                 {
622                         vec3_t vec;
623                         dlightradius = VectorLength(dlightcolor);
624                         d = 1.0f / dlightradius;
625                         VectorCopy(neworg, vec);
626                         // hack to make glowing player light shine on their gun
627                         if (i == cl.viewentity && !chase_active.value)
628                                 vec[2] += 30;
629                         CL_AllocDlight (&ent->render, vec, dlightradius, dlightcolor[0] * d, dlightcolor[1] * d, dlightcolor[2] * d, 0, 0);
630                 }
631
632                 if (chase_active.value)
633                 {
634                         if (ent->render.flags & RENDER_VIEWMODEL)
635                                 continue;
636                 }
637                 else
638                 {
639                         if (i == cl.viewentity || (ent->render.flags & RENDER_EXTERIORMODEL))
640                                 continue;
641                 }
642
643                 if (ent->render.model == NULL)
644                         continue;
645                 if (effects & EF_NODRAW)
646                         continue;
647                 if (cl_numvisedicts < MAX_VISEDICTS)
648                         cl_visedicts[cl_numvisedicts++] = ent;
649         }
650 }
651
652 void CL_LerpPlayerVelocity (void)
653 {
654         int i;
655         float frac, d;
656
657         // fraction from previous network update to current
658         frac = CL_LerpPoint ();
659
660         for (i = 0;i < 3;i++)
661                 cl.velocity[i] = cl.mvelocity[1][i] + frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]);
662
663         if (cls.demoplayback)
664         {
665                 // interpolate the angles
666                 for (i = 0;i < 3;i++)
667                 {
668                         d = cl.mviewangles[0][i] - cl.mviewangles[1][i];
669                         if (d > 180)
670                                 d -= 360;
671                         else if (d < -180)
672                                 d += 360;
673                         cl.viewangles[i] = cl.mviewangles[1][i] + frac*d;
674                 }
675         }
676 }
677
678 void CL_RelinkEntities (void)
679 {
680         cl_numvisedicts = 0;
681
682         CL_LerpPlayerVelocity();
683         CL_RelinkNetworkEntities();
684 }
685
686
687 /*
688 ===============
689 CL_ReadFromServer
690
691 Read all incoming data from the server
692 ===============
693 */
694 int CL_ReadFromServer (void)
695 {
696         int ret, netshown;
697
698         cl.oldtime = cl.time;
699         cl.time += cl.frametime;
700         
701         netshown = false;
702         do
703         {
704                 ret = CL_GetMessage ();
705                 if (ret == -1)
706                         Host_Error ("CL_ReadFromServer: lost server connection");
707                 if (!ret)
708                         break;
709                 
710                 cl.last_received_message = realtime;
711
712                 if (cl_shownet.value)
713                         netshown = true;
714
715                 CL_ParseServerMessage ();
716         }
717         while (ret && cls.state == ca_connected);
718         
719         if (netshown)
720                 Con_Printf ("\n");
721
722         CL_RelinkEntities ();
723         CL_UpdateTEnts ();
724         CL_DoEffects ();
725
726 //
727 // bring the links up to date
728 //
729         return 0;
730 }
731
732 /*
733 =================
734 CL_SendCmd
735 =================
736 */
737 void CL_SendCmd (void)
738 {
739         usercmd_t               cmd;
740
741         if (cls.state != ca_connected)
742                 return;
743
744         if (cls.signon == SIGNONS)
745         {
746         // get basic movement from keyboard
747                 CL_BaseMove (&cmd);
748
749         // allow mice or other external controllers to add to the move
750                 IN_Move (&cmd);
751         
752         // send the unreliable message
753                 CL_SendMove (&cmd);
754         }
755
756         if (cls.demoplayback)
757         {
758                 SZ_Clear (&cls.message);
759                 return;
760         }
761         
762 // send the reliable message
763         if (!cls.message.cursize)
764                 return;         // no message at all
765         
766         if (!NET_CanSendMessage (cls.netcon))
767         {
768                 Con_DPrintf ("CL_WriteToServer: can't send\n");
769                 return;
770         }
771
772         if (NET_SendMessage (cls.netcon, &cls.message) == -1)
773                 Host_Error ("CL_WriteToServer: lost server connection");
774
775         SZ_Clear (&cls.message);
776 }
777
778 // LordHavoc: pausedemo command
779 void CL_PauseDemo_f (void)
780 {
781         cls.demopaused = !cls.demopaused;
782         if (cls.demopaused)
783                 Con_Printf("Demo paused\n");
784         else
785                 Con_Printf("Demo unpaused\n");
786 }
787
788 /*
789 ======================
790 CL_PModel_f
791 LordHavoc: Intended for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
792 ======================
793 */
794 void CL_PModel_f (void)
795 {
796         int i;
797         eval_t *val;
798
799         if (Cmd_Argc () == 1)
800         {
801                 Con_Printf ("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
802                 return;
803         }
804         i = atoi(Cmd_Argv(1));
805
806         if (cmd_source == src_command)
807         {
808                 if (cl_pmodel.value == i)
809                         return;
810                 Cvar_SetValue ("_cl_pmodel", i);
811                 if (cls.state == ca_connected)
812                         Cmd_ForwardToServer ();
813                 return;
814         }
815
816         host_client->pmodel = i;
817         if ((val = GETEDICTFIELDVALUE(host_client->edict, eval_pmodel)))
818                 val->_float = i;
819 }
820
821 /*
822 ======================
823 CL_Fog_f
824 ======================
825 */
826 void CL_Fog_f (void)
827 {
828         if (Cmd_Argc () == 1)
829         {
830                 Con_Printf ("\"fog\" is \"%f %f %f %f\"\n", fog_density, fog_red, fog_green, fog_blue);
831                 return;
832         }
833         fog_density = atof(Cmd_Argv(1));
834         fog_red = atof(Cmd_Argv(2));
835         fog_green = atof(Cmd_Argv(3));
836         fog_blue = atof(Cmd_Argv(4));
837 }
838
839 /*
840 =================
841 CL_Init
842 =================
843 */
844 void CL_Init (void)
845 {       
846         SZ_Alloc (&cls.message, 1024);
847
848         CL_InitInput ();
849         CL_InitTEnts ();
850         
851 //
852 // register our commands
853 //
854         Cvar_RegisterVariable (&cl_name);
855         Cvar_RegisterVariable (&cl_color);
856         Cvar_RegisterVariable (&cl_pmodel);
857         Cvar_RegisterVariable (&cl_upspeed);
858         Cvar_RegisterVariable (&cl_forwardspeed);
859         Cvar_RegisterVariable (&cl_backspeed);
860         Cvar_RegisterVariable (&cl_sidespeed);
861         Cvar_RegisterVariable (&cl_movespeedkey);
862         Cvar_RegisterVariable (&cl_yawspeed);
863         Cvar_RegisterVariable (&cl_pitchspeed);
864         Cvar_RegisterVariable (&cl_anglespeedkey);
865         Cvar_RegisterVariable (&cl_shownet);
866         Cvar_RegisterVariable (&cl_nolerp);
867         Cvar_RegisterVariable (&lookspring);
868         Cvar_RegisterVariable (&lookstrafe);
869         Cvar_RegisterVariable (&sensitivity);
870         Cvar_RegisterVariable (&freelook);
871
872         Cvar_RegisterVariable (&m_pitch);
873         Cvar_RegisterVariable (&m_yaw);
874         Cvar_RegisterVariable (&m_forward);
875         Cvar_RegisterVariable (&m_side);
876
877 //      Cvar_RegisterVariable (&cl_autofire);
878         
879         Cmd_AddCommand ("entities", CL_PrintEntities_f);
880         Cmd_AddCommand ("bitprofile", CL_BitProfile_f);
881         Cmd_AddCommand ("disconnect", CL_Disconnect_f);
882         Cmd_AddCommand ("record", CL_Record_f);
883         Cmd_AddCommand ("stop", CL_Stop_f);
884         Cmd_AddCommand ("playdemo", CL_PlayDemo_f);
885         Cmd_AddCommand ("timedemo", CL_TimeDemo_f);
886
887         Cmd_AddCommand ("fog", CL_Fog_f);
888
889         // LordHavoc: added pausedemo
890         Cmd_AddCommand ("pausedemo", CL_PauseDemo_f);
891         // LordHavoc: added pmodel command (like name, etc, only intended for Nehahra)
892         Cmd_AddCommand ("pmodel", CL_PModel_f);
893
894         CL_Parse_Init();
895 }