1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, 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 28cvar_t cl_name = CVAR3("_cl_name", "player", true); 29cvar_t cl_color = CVAR3("_cl_color", "0", true); 30 31cvar_t cl_shownet = CVAR2("cl_shownet","0"); // can be 0, 1, or 2 32cvar_t cl_nolerp = CVAR2("cl_nolerp","0"); 33 34cvar_t lookspring = CVAR3("lookspring","0", true); 35cvar_t lookstrafe = CVAR3("lookstrafe","0", true); 36cvar_t sensitivity = CVAR3("sensitivity","3", true); 37 38cvar_t m_pitch = CVAR3("m_pitch","0.022", true); 39cvar_t m_yaw = CVAR3("m_yaw","0.022", true); 40cvar_t m_forward = CVAR3("m_forward","1", true); 41cvar_t m_side = CVAR3("m_side","0.8", true); 42 43 44client_static_t cls; 45client_state_t cl; 46// FIXME: put these on hunk? 47efrag_t cl_efrags[MAX_EFRAGS]; 48entity_t cl_entities[MAX_EDICTS]; 49entity_t cl_static_entities[MAX_STATIC_ENTITIES]; 50lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; 51dlight_t cl_dlights[MAX_DLIGHTS]; 52 53int cl_numvisedicts; 54entity_t *cl_visedicts[MAX_VISEDICTS]; 55 56/* 57===================== 58CL_ClearState 59 60===================== 61*/ 62void 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_efrags, 0, sizeof(cl_efrags)); 76 memset (cl_entities, 0, sizeof(cl_entities)); 77 memset (cl_dlights, 0, sizeof(cl_dlights)); 78 memset (cl_lightstyle, 0, sizeof(cl_lightstyle)); 79 memset (cl_temp_entities, 0, sizeof(cl_temp_entities)); 80 memset (cl_beams, 0, sizeof(cl_beams)); 81 82// 83// allocate the efrags and chain together into a free list 84// 85 cl.free_efrags = cl_efrags; 86 for (i=0 ; i<MAX_EFRAGS-1 ; i++) 87 cl.free_efrags[i].entnext = &cl.free_efrags[i+1]; 88 cl.free_efrags[i].entnext = NULL; 89} 90 91/* 92===================== 93CL_Disconnect 94 95Sends a disconnect message to the server 96This is also called on Host_Error, so it shouldn't cause any errors 97===================== 98*/ 99void CL_Disconnect (void) 100{ 101// stop sounds (especially looping!) 102 S_StopAllSounds (true); 103 104// bring the console down and fade the colors back to normal 105// SCR_BringDownConsole (); 106 107// if running a local server, shut it down 108 if (cls.demoplayback) 109 CL_StopPlayback (); 110 else if (cls.state == ca_connected) 111 { 112 if (cls.demorecording) 113 CL_Stop_f (); 114 115 Con_DPrintf ("Sending clc_disconnect\n"); 116 SZ_Clear (&cls.message); 117 MSG_WriteByte (&cls.message, clc_disconnect); 118 NET_SendUnreliableMessage (cls.netcon, &cls.message); 119 SZ_Clear (&cls.message); 120 NET_Close (cls.netcon); 121 122 cls.state = ca_disconnected; 123 if (sv.active) 124 Host_ShutdownServer(false); 125 } 126 127 cls.demoplayback = cls.timedemo = false; 128 cls.signon = 0; 129} 130 131void CL_Disconnect_f (void) 132{ 133 CL_Disconnect (); 134 if (sv.active) 135 Host_ShutdownServer (false); 136} 137 138 139 140 141/* 142===================== 143CL_EstablishConnection 144 145Host should be either "local" or a net address to be passed on 146===================== 147*/ 148void CL_EstablishConnection (const char *host) 149{ 150 if (cls.state == ca_dedicated) 151 return; 152 153 if (cls.demoplayback) 154 return; 155 156 CL_Disconnect (); 157 158 cls.netcon = NET_Connect (host); 159 if (!cls.netcon) 160 Host_Error ("CL_Connect: connect failed\n"); 161 Con_DPrintf ("CL_EstablishConnection: connected to %s\n", host); 162 163 cls.demonum = -1; // not in the demo loop now 164 cls.state = ca_connected; 165 cls.signon = 0; // need all the signon messages before playing 166} 167 168/* 169===================== 170CL_SignonReply 171 172An svc_signonnum has been received, perform a client side setup 173===================== 174*/ 175void CL_SignonReply (void) 176{ 177 char str[8192]; 178 179Con_DPrintf ("CL_SignonReply: %i\n", cls.signon); 180 181 switch (cls.signon) 182 { 183 case 1: 184 MSG_WriteByte (&cls.message, clc_stringcmd); 185 MSG_WriteString (&cls.message, "prespawn"); 186 break; 187 188 case 2: 189 MSG_WriteByte (&cls.message, clc_stringcmd); 190 MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string)); 191 192 MSG_WriteByte (&cls.message, clc_stringcmd); 193 MSG_WriteString (&cls.message, va("color %i %i\n", ((int)cl_color.value)>>4, ((int)cl_color.value)&15)); 194 195 MSG_WriteByte (&cls.message, clc_stringcmd); 196 sprintf (str, "spawn %s", cls.spawnparms); 197 MSG_WriteString (&cls.message, str); 198 break; 199 200 case 3: 201 MSG_WriteByte (&cls.message, clc_stringcmd); 202 MSG_WriteString (&cls.message, "begin"); 203 Cache_Report (); // print remaining memory 204 break; 205 206 case 4: 207 SCR_EndLoadingPlaque (); // allow normal screen updates 208 break; 209 } 210} 211 212/* 213===================== 214CL_NextDemo 215 216Called to play the next demo in the demo loop 217===================== 218*/ 219void CL_NextDemo (void) 220{ 221 char str[1024]; 222 223 if (cls.demonum == -1) 224 return; // don't play demos 225 226 SCR_BeginLoadingPlaque (); 227 228 if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS) 229 { 230 cls.demonum = 0; 231 if (!cls.demos[cls.demonum][0]) 232 { 233 Con_Printf ("No demos listed with startdemos\n"); 234 cls.demonum = -1; 235 return; 236 } 237 } 238 239 sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]); 240 Cbuf_InsertText (str); 241 cls.demonum++; 242} 243 244/* 245============== 246CL_PrintEntities_f 247============== 248*/ 249void CL_PrintEntities_f (void) 250{ 251 entity_t *ent; 252 int i; 253 254 for (i=0,ent=cl_entities ; i<cl.num_entities ; i++,ent++) 255 { 256 Con_Printf ("%3i:",i); 257 if (!ent->model) 258 { 259 Con_Printf ("EMPTY\n"); 260 continue; 261 } 262 Con_Printf ("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n" 263 ,ent->model->name,ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]); 264 } 265} 266 267 268/* 269=============== 270SetPal 271 272Debugging tool, just flashes the screen 273=============== 274*/ 275void SetPal (int i) 276{ 277#if 0 278 static int old; 279 byte pal[768]; 280 int c; 281 282 if (i == old) 283 return; 284 old = i; 285 286 if (i==0) 287 VID_SetPalette (host_basepal); 288 else if (i==1) 289 { 290 for (c=0 ; c<768 ; c+=3) 291 { 292 pal[c] = 0; 293 pal[c+1] = 255; 294 pal[c+2] = 0; 295 } 296 VID_SetPalette (pal); 297 } 298 else 299 { 300 for (c=0 ; c<768 ; c+=3) 301 { 302 pal[c] = 0; 303 pal[c+1] = 0; 304 pal[c+2] = 255; 305 } 306 VID_SetPalette (pal); 307 } 308#endif 309} 310 311/* 312=============== 313CL_AllocDlight 314 315=============== 316*/ 317dlight_t *CL_AllocDlight (int key) 318{ 319 int i; 320 dlight_t *dl; 321 322// first look for an exact key match 323 if (key) 324 { 325 dl = cl_dlights; 326 for (i=0 ; i<MAX_DLIGHTS ; i++, dl++) 327 { 328 if (dl->key == key) 329 { 330 memset (dl, 0, sizeof(*dl)); 331 dl->key = key; 332 return dl; 333 } 334 } 335 } 336 337// then look for anything else 338 dl = cl_dlights; 339 for (i=0 ; i<MAX_DLIGHTS ; i++, dl++) 340 { 341 if (dl->die < cl.time) 342 { 343 memset (dl, 0, sizeof(*dl)); 344 dl->key = key; 345 return dl; 346 } 347 } 348 349 dl = &cl_dlights[0]; 350 memset (dl, 0, sizeof(*dl)); 351 dl->key = key; 352 return dl; 353} 354 355 356/* 357=============== 358CL_DecayLights 359 360=============== 361*/ 362void CL_DecayLights (void) 363{ 364 int i; 365 dlight_t *dl; 366 float time; 367 368 time = cl.time - cl.oldtime; 369 370 dl = cl_dlights; 371 for (i=0 ; i<MAX_DLIGHTS ; i++, dl++) 372 { 373 if (dl->die < cl.time || !dl->radius) 374 continue; 375 376 dl->radius -= time*dl->decay; 377 if (dl->radius < 0) 378 dl->radius = 0; 379 } 380} 381 382 383/* 384=============== 385CL_LerpPoint 386 387Determines the fraction between the last two messages that the objects 388should be put at. 389=============== 390*/ 391float CL_LerpPoint (void) 392{ 393 float f, frac; 394 395 f = cl.mtime[0] - cl.mtime[1]; 396 397 if (!f || cl_nolerp.value || cls.timedemo || sv.active) 398 { 399 cl.time = cl.mtime[0]; 400 return 1; 401 } 402 403 if (f > 0.1) 404 { // dropped packet, or start of demo 405 cl.mtime[1] = cl.mtime[0] - 0.1; 406 f = 0.1; 407 } 408 frac = (cl.time - cl.mtime[1]) / f; 409//Con_Printf ("frac: %f\n",frac); 410 if (frac < 0) 411 { 412 if (frac < -0.01) 413 { 414SetPal(1); 415 cl.time = cl.mtime[1]; 416// Con_Printf ("low frac\n"); 417 } 418 frac = 0; 419 } 420 else if (frac > 1) 421 { 422 if (frac > 1.01) 423 { 424SetPal(2); 425 cl.time = cl.mtime[0]; 426// Con_Printf ("high frac\n"); 427 } 428 frac = 1; 429 } 430 else 431 SetPal(0); 432 433 return frac; 434} 435 436 437/* 438=============== 439CL_RelinkEntities 440=============== 441*/ 442void CL_RelinkEntities (void) 443{ 444 entity_t *ent; 445 int i, j; 446 float frac, f, d; 447 vec3_t delta; 448 float bobjrotate; 449 vec3_t oldorg; 450 dlight_t *dl; 451 452// determine partial update time 453 frac = CL_LerpPoint (); 454 455 cl_numvisedicts = 0; 456 457// 458// interpolate player info 459// 460 for (i=0 ; i<3 ; i++) 461 cl.velocity[i] = cl.mvelocity[1][i] + 462 frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]); 463 464 if (cls.demoplayback) 465 { 466 // interpolate the angles 467 for (j=0 ; j<3 ; j++) 468 { 469 d = cl.mviewangles[0][j] - cl.mviewangles[1][j]; 470 if (d > 180) 471 d -= 360; 472 else if (d < -180) 473 d += 360; 474 cl.viewangles[j] = cl.mviewangles[1][j] + frac*d; 475 } 476 } 477 478 bobjrotate = anglemod(100*cl.time); 479 480// start on the entity after the world 481 for (i=1,ent=cl_entities+1 ; i<cl.num_entities ; i++,ent++) 482 { 483 if (!ent->model) 484 { // empty slot 485 if (ent->forcelink) 486 R_RemoveEfrags (ent); // just became empty 487 continue; 488 } 489 490// if the object wasn't included in the last packet, remove it 491 if (ent->msgtime != cl.mtime[0]) 492 { 493 ent->model = NULL; 494 continue; 495 } 496 497 VectorCopy (ent->origin, oldorg); 498 499 if (ent->forcelink) 500 { // the entity was not updated in the last message 501 // so move to the final spot 502 VectorCopy (ent->msg_origins[0], ent->origin); 503 VectorCopy (ent->msg_angles[0], ent->angles); 504 } 505 else 506 { // if the delta is large, assume a teleport and don't lerp 507 f = frac; 508 for (j=0 ; j<3 ; j++) 509 { 510 delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j]; 511 if (delta[j] > 100 || delta[j] < -100) 512 f = 1; // assume a teleportation, not a motion 513 } 514 515 // interpolate the origin and angles 516 for (j=0 ; j<3 ; j++) 517 { 518 ent->origin[j] = ent->msg_origins[1][j] + f*delta[j]; 519 520 d = ent->msg_angles[0][j] - ent->msg_angles[1][j]; 521 if (d > 180) 522 d -= 360; 523 else if (d < -180) 524 d += 360; 525 ent->angles[j] = ent->msg_angles[1][j] + f*d; 526 } 527 528 } 529 530// rotate binary objects locally 531 if (ent->model->flags & EF_ROTATE) 532 ent->angles[1] = bobjrotate; 533 534 if (ent->effects & EF_BRIGHTFIELD) 535 R_EntityParticles (ent); 536#ifdef QUAKE2 537 if (ent->effects & EF_DARKFIELD) 538 R_DarkFieldParticles (ent); 539#endif 540 if (ent->effects & EF_MUZZLEFLASH) 541 { 542 vec3_t fv, rv, uv; 543 544 dl = CL_AllocDlight (i); 545 VectorCopy (ent->origin, dl->origin); 546 dl->origin[2] += 16; 547 AngleVectors (ent->angles, fv, rv, uv); 548 549 VectorMA (dl->origin, 18, fv, dl->origin); 550 dl->radius = 200 + (rand()&31); 551 dl->minlight = 32; 552 dl->die = cl.time + 0.1; 553 } 554 if (ent->effects & EF_BRIGHTLIGHT) 555 { 556 dl = CL_AllocDlight (i); 557 VectorCopy (ent->origin, dl->origin); 558 dl->origin[2] += 16; 559 dl->radius = 400 + (rand()&31); 560 dl->die = cl.time + 0.001; 561 } 562 if (ent->effects & EF_DIMLIGHT) 563 { 564 dl = CL_AllocDlight (i); 565 VectorCopy (ent->origin, dl->origin); 566 dl->radius = 200 + (rand()&31); 567 dl->die = cl.time + 0.001; 568 } 569#ifdef QUAKE2 570 if (ent->effects & EF_DARKLIGHT) 571 { 572 dl = CL_AllocDlight (i); 573 VectorCopy (ent->origin, dl->origin); 574 dl->radius = 200.0 + (rand()&31); 575 dl->die = cl.time + 0.001; 576 dl->dark = true; 577 } 578 if (ent->effects & EF_LIGHT) 579 { 580 dl = CL_AllocDlight (i); 581 VectorCopy (ent->origin, dl->origin); 582 dl->radius = 200; 583 dl->die = cl.time + 0.001; 584 } 585#endif 586 587 if (ent->model->flags & EF_GIB) 588 R_RocketTrail (oldorg, ent->origin, 2); 589 else if (ent->model->flags & EF_ZOMGIB) 590 R_RocketTrail (oldorg, ent->origin, 4); 591 else if (ent->model->flags & EF_TRACER) 592 R_RocketTrail (oldorg, ent->origin, 3); 593 else if (ent->model->flags & EF_TRACER2) 594 R_RocketTrail (oldorg, ent->origin, 5); 595 else if (ent->model->flags & EF_ROCKET) 596 { 597 R_RocketTrail (oldorg, ent->origin, 0); 598 dl = CL_AllocDlight (i); 599 VectorCopy (ent->origin, dl->origin); 600 dl->radius = 200; 601 dl->die = cl.time + 0.01; 602 } 603 else if (ent->model->flags & EF_GRENADE) 604 R_RocketTrail (oldorg, ent->origin, 1); 605 else if (ent->model->flags & EF_TRACER3) 606 R_RocketTrail (oldorg, ent->origin, 6); 607 608 ent->forcelink = false; 609 610 if (i == cl.viewentity && !chase_active.value) 611 continue; 612 613#ifdef QUAKE2 614 if ( ent->effects & EF_NODRAW ) 615 continue; 616#endif 617 if (cl_numvisedicts < MAX_VISEDICTS) 618 { 619 cl_visedicts[cl_numvisedicts] = ent; 620 cl_numvisedicts++; 621 } 622 } 623 624} 625 626 627/* 628=============== 629CL_ReadFromServer 630 631Read all incoming data from the server 632=============== 633*/ 634int CL_ReadFromServer (void) 635{ 636 int ret; 637 638 cl.oldtime = cl.time; 639 cl.time += host_frametime; 640 641 do 642 { 643 ret = CL_GetMessage (); 644 if (ret == -1) 645 Host_Error ("CL_ReadFromServer: lost server connection"); 646 if (!ret) 647 break; 648 649 cl.last_received_message = realtime; 650 CL_ParseServerMessage (); 651 } while (ret && cls.state == ca_connected); 652 653 if (cl_shownet.value) 654 Con_Printf ("\n"); 655 656 CL_RelinkEntities (); 657 CL_UpdateTEnts (); 658 659// 660// bring the links up to date 661// 662 return 0; 663} 664 665/* 666================= 667CL_SendCmd 668================= 669*/ 670void CL_SendCmd (void) 671{ 672 usercmd_t cmd; 673 674 if (cls.state != ca_connected) 675 return; 676 677 if (cls.signon == SIGNONS) 678 { 679 // get basic movement from keyboard 680 CL_BaseMove (&cmd); 681 682 // allow mice or other external controllers to add to the move 683 IN_Move (&cmd); 684 685 // send the unreliable message 686 CL_SendMove (&cmd); 687 688 } 689 690 if (cls.demoplayback) 691 { 692 SZ_Clear (&cls.message); 693 return; 694 } 695 696// send the reliable message 697 if (!cls.message.cursize) 698 return; // no message at all 699 700 if (!NET_CanSendMessage (cls.netcon)) 701 { 702 Con_DPrintf ("CL_WriteToServer: can't send\n"); 703 return; 704 } 705 706 if (NET_SendMessage (cls.netcon, &cls.message) == -1) 707 Host_Error ("CL_WriteToServer: lost server connection"); 708 709 SZ_Clear (&cls.message); 710} 711 712/* 713================= 714CL_Init 715================= 716*/ 717void CL_Init (void) 718{ 719 SZ_Alloc (&cls.message, 1024); 720 721 CL_InitInput (); 722 CL_InitTEnts (); 723 724// 725// register our commands 726// 727 Cvar_RegisterVariable (&cl_name); 728 Cvar_RegisterVariable (&cl_color); 729 Cvar_RegisterVariable (&cl_upspeed); 730 Cvar_RegisterVariable (&cl_forwardspeed); 731 Cvar_RegisterVariable (&cl_backspeed); 732 Cvar_RegisterVariable (&cl_sidespeed); 733 Cvar_RegisterVariable (&cl_movespeedkey); 734 Cvar_RegisterVariable (&cl_yawspeed); 735 Cvar_RegisterVariable (&cl_pitchspeed); 736 Cvar_RegisterVariable (&cl_anglespeedkey); 737 Cvar_RegisterVariable (&cl_shownet); 738 Cvar_RegisterVariable (&cl_nolerp); 739 Cvar_RegisterVariable (&lookspring); 740 Cvar_RegisterVariable (&lookstrafe); 741 Cvar_RegisterVariable (&sensitivity); 742 743 Cvar_RegisterVariable (&m_pitch); 744 Cvar_RegisterVariable (&m_yaw); 745 Cvar_RegisterVariable (&m_forward); 746 Cvar_RegisterVariable (&m_side); 747 748// Cvar_RegisterVariable (&cl_autofire); 749 750 Cmd_AddCommand ("entities", CL_PrintEntities_f); 751 Cmd_AddCommand ("disconnect", CL_Disconnect_f); 752 Cmd_AddCommand ("record", CL_Record_f); 753 Cmd_AddCommand ("stop", CL_Stop_f); 754 Cmd_AddCommand ("playdemo", CL_PlayDemo_f); 755 Cmd_AddCommand ("timedemo", CL_TimeDemo_f); 756} 757 758