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 = CVAR3("_cl_name", "player", true); 29 cvar_t cl_color = CVAR3("_cl_color", "0", true); 30 31 cvar_t cl_shownet = CVAR2("cl_shownet","0"); // can be 0, 1, or 2 32 cvar_t cl_nolerp = CVAR2("cl_nolerp","0"); 33 34 cvar_t lookspring = CVAR3("lookspring","0", true); 35 cvar_t lookstrafe = CVAR3("lookstrafe","0", true); 36 cvar_t sensitivity = CVAR3("sensitivity","3", true); 37 38 cvar_t m_pitch = CVAR3("m_pitch","0.022", true); 39 cvar_t m_yaw = CVAR3("m_yaw","0.022", true); 40 cvar_t m_forward = CVAR3("m_forward","1", true); 41 cvar_t m_side = CVAR3("m_side","0.8", true); 42 43 44 client_static_t cls; 45 client_state_t cl; 46 // FIXME: put these on hunk? 47 efrag_t cl_efrags[MAX_EFRAGS]; 48 entity_t cl_entities[MAX_EDICTS]; 49 entity_t cl_static_entities[MAX_STATIC_ENTITIES]; 50 lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; 51 dlight_t cl_dlights[MAX_DLIGHTS]; 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_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 ===================== 93 CL_Disconnect 94 95 Sends a disconnect message to the server 96 This is also called on Host_Error, so it shouldn't cause any errors 97 ===================== 98 */ 99 void 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 131 void CL_Disconnect_f (void) 132 { 133 CL_Disconnect (); 134 if (sv.active) 135 Host_ShutdownServer (false); 136 } 137 138 139 140 141 /* 142 ===================== 143 CL_EstablishConnection 144 145 Host should be either "local" or a net address to be passed on 146 ===================== 147 */ 148 void 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 ===================== 170 CL_SignonReply 171 172 An svc_signonnum has been received, perform a client side setup 173 ===================== 174 */ 175 void CL_SignonReply (void) 176 { 177 char str[8192]; 178 179 Con_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 ===================== 214 CL_NextDemo 215 216 Called to play the next demo in the demo loop 217 ===================== 218 */ 219 void 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 ============== 246 CL_PrintEntities_f 247 ============== 248 */ 249 void 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 =============== 270 SetPal 271 272 Debugging tool, just flashes the screen 273 =============== 274 */ 275 void 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 =============== 313 CL_AllocDlight 314 315 =============== 316 */ 317 dlight_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 =============== 358 CL_DecayLights 359 360 =============== 361 */ 362 void 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 =============== 385 CL_LerpPoint 386 387 Determines the fraction between the last two messages that the objects 388 should be put at. 389 =============== 390 */ 391 float 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 { 414 SetPal(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 { 424 SetPal(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 =============== 439 CL_RelinkEntities 440 =============== 441 */ 442 void 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 =============== 629 CL_ReadFromServer 630 631 Read all incoming data from the server 632 =============== 633 */ 634 int 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 ================= 667 CL_SendCmd 668 ================= 669 */ 670 void 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 ================= 714 CL_Init 715 ================= 716 */ 717 void 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