p_client.c (42397B)
1 /* 2 Copyright (C) 1997-2001 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 #include "g_local.h" 21 #include "m_player.h" 22 23 void ClientUserinfoChanged (edict_t *ent, char *userinfo); 24 25 void SP_misc_teleporter_dest (edict_t *ent); 26 27 // 28 // Gross, ugly, disgustuing hack section 29 // 30 31 // this function is an ugly as hell hack to fix some map flaws 32 // 33 // the coop spawn spots on some maps are SNAFU. There are coop spots 34 // with the wrong targetname as well as spots with no name at all 35 // 36 // we use carnal knowledge of the maps to fix the coop spot targetnames to match 37 // that of the nearest named single player spot 38 39 static void SP_FixCoopSpots (edict_t *self) 40 { 41 edict_t *spot; 42 vec3_t d; 43 44 spot = NULL; 45 46 while(1) 47 { 48 spot = G_Find(spot, FOFS(classname), "info_player_start"); 49 if (!spot) 50 return; 51 if (!spot->targetname) 52 continue; 53 VectorSubtract(self->s.origin, spot->s.origin, d); 54 if (VectorLength(d) < 384) 55 { 56 if ((!self->targetname) || Q_stricmp(self->targetname, spot->targetname) != 0) 57 { 58 // gi.dprintf("FixCoopSpots changed %s at %s targetname from %s to %s\n", self->classname, vtos(self->s.origin), self->targetname, spot->targetname); 59 self->targetname = spot->targetname; 60 } 61 return; 62 } 63 } 64 } 65 66 // now if that one wasn't ugly enough for you then try this one on for size 67 // some maps don't have any coop spots at all, so we need to create them 68 // where they should have been 69 70 static void SP_CreateCoopSpots (edict_t *self) 71 { 72 edict_t *spot; 73 74 if(Q_stricmp(level.mapname, "security") == 0) 75 { 76 spot = G_Spawn(); 77 spot->classname = "info_player_coop"; 78 spot->s.origin[0] = 188 - 64; 79 spot->s.origin[1] = -164; 80 spot->s.origin[2] = 80; 81 spot->targetname = "jail3"; 82 spot->s.angles[1] = 90; 83 84 spot = G_Spawn(); 85 spot->classname = "info_player_coop"; 86 spot->s.origin[0] = 188 + 64; 87 spot->s.origin[1] = -164; 88 spot->s.origin[2] = 80; 89 spot->targetname = "jail3"; 90 spot->s.angles[1] = 90; 91 92 spot = G_Spawn(); 93 spot->classname = "info_player_coop"; 94 spot->s.origin[0] = 188 + 128; 95 spot->s.origin[1] = -164; 96 spot->s.origin[2] = 80; 97 spot->targetname = "jail3"; 98 spot->s.angles[1] = 90; 99 100 return; 101 } 102 } 103 104 105 /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) 106 The normal starting point for a level. 107 */ 108 void SP_info_player_start(edict_t *self) 109 { 110 if (!coop->value) 111 return; 112 if(Q_stricmp(level.mapname, "security") == 0) 113 { 114 // invoke one of our gross, ugly, disgusting hacks 115 self->think = SP_CreateCoopSpots; 116 self->nextthink = level.time + FRAMETIME; 117 } 118 } 119 120 /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) 121 potential spawning position for deathmatch games 122 */ 123 void SP_info_player_deathmatch(edict_t *self) 124 { 125 if (!deathmatch->value) 126 { 127 G_FreeEdict (self); 128 return; 129 } 130 SP_misc_teleporter_dest (self); 131 } 132 133 /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32) 134 potential spawning position for coop games 135 */ 136 137 void SP_info_player_coop(edict_t *self) 138 { 139 if (!coop->value) 140 { 141 G_FreeEdict (self); 142 return; 143 } 144 145 if((Q_stricmp(level.mapname, "jail2") == 0) || 146 (Q_stricmp(level.mapname, "jail4") == 0) || 147 (Q_stricmp(level.mapname, "mine1") == 0) || 148 (Q_stricmp(level.mapname, "mine2") == 0) || 149 (Q_stricmp(level.mapname, "mine3") == 0) || 150 (Q_stricmp(level.mapname, "mine4") == 0) || 151 (Q_stricmp(level.mapname, "lab") == 0) || 152 (Q_stricmp(level.mapname, "boss1") == 0) || 153 (Q_stricmp(level.mapname, "fact3") == 0) || 154 (Q_stricmp(level.mapname, "biggun") == 0) || 155 (Q_stricmp(level.mapname, "space") == 0) || 156 (Q_stricmp(level.mapname, "command") == 0) || 157 (Q_stricmp(level.mapname, "power2") == 0) || 158 (Q_stricmp(level.mapname, "strike") == 0)) 159 { 160 // invoke one of our gross, ugly, disgusting hacks 161 self->think = SP_FixCoopSpots; 162 self->nextthink = level.time + FRAMETIME; 163 } 164 } 165 166 167 /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32) 168 The deathmatch intermission point will be at one of these 169 Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw. 'pitch yaw roll' 170 */ 171 void SP_info_player_intermission(void) 172 { 173 } 174 175 176 //======================================================================= 177 178 179 void player_pain (edict_t *self, edict_t *other, float kick, int damage) 180 { 181 // player pain is handled at the end of the frame in P_DamageFeedback 182 } 183 184 185 qboolean IsFemale (edict_t *ent) 186 { 187 char *info; 188 189 if (!ent->client) 190 return false; 191 192 info = Info_ValueForKey (ent->client->pers.userinfo, "gender"); 193 if (info[0] == 'f' || info[0] == 'F') 194 return true; 195 return false; 196 } 197 198 qboolean IsNeutral (edict_t *ent) 199 { 200 char *info; 201 202 if (!ent->client) 203 return false; 204 205 info = Info_ValueForKey (ent->client->pers.userinfo, "gender"); 206 if (info[0] != 'f' && info[0] != 'F' && info[0] != 'm' && info[0] != 'M') 207 return true; 208 return false; 209 } 210 211 void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker) 212 { 213 int mod; 214 char *message; 215 char *message2; 216 qboolean ff; 217 218 if (coop->value && attacker->client) 219 meansOfDeath |= MOD_FRIENDLY_FIRE; 220 221 if (deathmatch->value || coop->value) 222 { 223 ff = meansOfDeath & MOD_FRIENDLY_FIRE; 224 mod = meansOfDeath & ~MOD_FRIENDLY_FIRE; 225 message = NULL; 226 message2 = ""; 227 228 switch (mod) 229 { 230 case MOD_SUICIDE: 231 message = "suicides"; 232 break; 233 case MOD_FALLING: 234 message = "cratered"; 235 break; 236 case MOD_CRUSH: 237 message = "was squished"; 238 break; 239 case MOD_WATER: 240 message = "sank like a rock"; 241 break; 242 case MOD_SLIME: 243 message = "melted"; 244 break; 245 case MOD_LAVA: 246 message = "does a back flip into the lava"; 247 break; 248 case MOD_EXPLOSIVE: 249 case MOD_BARREL: 250 message = "blew up"; 251 break; 252 case MOD_EXIT: 253 message = "found a way out"; 254 break; 255 case MOD_TARGET_LASER: 256 message = "saw the light"; 257 break; 258 case MOD_TARGET_BLASTER: 259 message = "got blasted"; 260 break; 261 case MOD_BOMB: 262 case MOD_SPLASH: 263 case MOD_TRIGGER_HURT: 264 message = "was in the wrong place"; 265 break; 266 } 267 if (attacker == self) 268 { 269 switch (mod) 270 { 271 case MOD_HELD_GRENADE: 272 message = "tried to put the pin back in"; 273 break; 274 case MOD_HG_SPLASH: 275 case MOD_G_SPLASH: 276 if (IsNeutral(self)) 277 message = "tripped on its own grenade"; 278 else if (IsFemale(self)) 279 message = "tripped on her own grenade"; 280 else 281 message = "tripped on his own grenade"; 282 break; 283 case MOD_R_SPLASH: 284 if (IsNeutral(self)) 285 message = "blew itself up"; 286 else if (IsFemale(self)) 287 message = "blew herself up"; 288 else 289 message = "blew himself up"; 290 break; 291 case MOD_BFG_BLAST: 292 message = "should have used a smaller gun"; 293 break; 294 default: 295 if (IsNeutral(self)) 296 message = "killed itself"; 297 else if (IsFemale(self)) 298 message = "killed herself"; 299 else 300 message = "killed himself"; 301 break; 302 } 303 } 304 if (message) 305 { 306 gi.bprintf (PRINT_MEDIUM, "%s %s.\n", self->client->pers.netname, message); 307 if (deathmatch->value) 308 self->client->resp.score--; 309 self->enemy = NULL; 310 return; 311 } 312 313 self->enemy = attacker; 314 if (attacker && attacker->client) 315 { 316 switch (mod) 317 { 318 case MOD_BLASTER: 319 message = "was blasted by"; 320 break; 321 case MOD_SHOTGUN: 322 message = "was gunned down by"; 323 break; 324 case MOD_SSHOTGUN: 325 message = "was blown away by"; 326 message2 = "'s super shotgun"; 327 break; 328 case MOD_MACHINEGUN: 329 message = "was machinegunned by"; 330 break; 331 case MOD_CHAINGUN: 332 message = "was cut in half by"; 333 message2 = "'s chaingun"; 334 break; 335 case MOD_GRENADE: 336 message = "was popped by"; 337 message2 = "'s grenade"; 338 break; 339 case MOD_G_SPLASH: 340 message = "was shredded by"; 341 message2 = "'s shrapnel"; 342 break; 343 case MOD_ROCKET: 344 message = "ate"; 345 message2 = "'s rocket"; 346 break; 347 case MOD_R_SPLASH: 348 message = "almost dodged"; 349 message2 = "'s rocket"; 350 break; 351 case MOD_HYPERBLASTER: 352 message = "was melted by"; 353 message2 = "'s hyperblaster"; 354 break; 355 case MOD_RAILGUN: 356 message = "was railed by"; 357 break; 358 case MOD_BFG_LASER: 359 message = "saw the pretty lights from"; 360 message2 = "'s BFG"; 361 break; 362 case MOD_BFG_BLAST: 363 message = "was disintegrated by"; 364 message2 = "'s BFG blast"; 365 break; 366 case MOD_BFG_EFFECT: 367 message = "couldn't hide from"; 368 message2 = "'s BFG"; 369 break; 370 case MOD_HANDGRENADE: 371 message = "caught"; 372 message2 = "'s handgrenade"; 373 break; 374 case MOD_HG_SPLASH: 375 message = "didn't see"; 376 message2 = "'s handgrenade"; 377 break; 378 case MOD_HELD_GRENADE: 379 message = "feels"; 380 message2 = "'s pain"; 381 break; 382 case MOD_TELEFRAG: 383 message = "tried to invade"; 384 message2 = "'s personal space"; 385 break; 386 } 387 if (message) 388 { 389 gi.bprintf (PRINT_MEDIUM,"%s %s %s%s\n", self->client->pers.netname, message, attacker->client->pers.netname, message2); 390 if (deathmatch->value) 391 { 392 if (ff) 393 attacker->client->resp.score--; 394 else 395 attacker->client->resp.score++; 396 } 397 return; 398 } 399 } 400 } 401 402 gi.bprintf (PRINT_MEDIUM,"%s died.\n", self->client->pers.netname); 403 if (deathmatch->value) 404 self->client->resp.score--; 405 } 406 407 408 void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); 409 410 void TossClientWeapon (edict_t *self) 411 { 412 gitem_t *item; 413 edict_t *drop; 414 qboolean quad; 415 float spread; 416 417 if (!deathmatch->value) 418 return; 419 420 item = self->client->pers.weapon; 421 if (! self->client->pers.inventory[self->client->ammo_index] ) 422 item = NULL; 423 if (item && (strcmp (item->pickup_name, "Blaster") == 0)) 424 item = NULL; 425 426 if (!((int)(dmflags->value) & DF_QUAD_DROP)) 427 quad = false; 428 else 429 quad = (self->client->quad_framenum > (level.framenum + 10)); 430 431 if (item && quad) 432 spread = 22.5; 433 else 434 spread = 0.0; 435 436 if (item) 437 { 438 self->client->v_angle[YAW] -= spread; 439 drop = Drop_Item (self, item); 440 self->client->v_angle[YAW] += spread; 441 drop->spawnflags = DROPPED_PLAYER_ITEM; 442 } 443 444 if (quad) 445 { 446 self->client->v_angle[YAW] += spread; 447 drop = Drop_Item (self, FindItemByClassname ("item_quad")); 448 self->client->v_angle[YAW] -= spread; 449 drop->spawnflags |= DROPPED_PLAYER_ITEM; 450 451 drop->touch = Touch_Item; 452 drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME; 453 drop->think = G_FreeEdict; 454 } 455 } 456 457 458 /* 459 ================== 460 LookAtKiller 461 ================== 462 */ 463 void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker) 464 { 465 vec3_t dir; 466 467 if (attacker && attacker != world && attacker != self) 468 { 469 VectorSubtract (attacker->s.origin, self->s.origin, dir); 470 } 471 else if (inflictor && inflictor != world && inflictor != self) 472 { 473 VectorSubtract (inflictor->s.origin, self->s.origin, dir); 474 } 475 else 476 { 477 self->client->killer_yaw = self->s.angles[YAW]; 478 return; 479 } 480 481 if (dir[0]) 482 self->client->killer_yaw = 180/M_PI*atan2(dir[1], dir[0]); 483 else { 484 self->client->killer_yaw = 0; 485 if (dir[1] > 0) 486 self->client->killer_yaw = 90; 487 else if (dir[1] < 0) 488 self->client->killer_yaw = -90; 489 } 490 if (self->client->killer_yaw < 0) 491 self->client->killer_yaw += 360; 492 493 494 } 495 496 /* 497 ================== 498 player_die 499 ================== 500 */ 501 void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) 502 { 503 int n; 504 505 VectorClear (self->avelocity); 506 507 self->takedamage = DAMAGE_YES; 508 self->movetype = MOVETYPE_TOSS; 509 510 self->s.modelindex2 = 0; // remove linked weapon model 511 512 self->s.angles[0] = 0; 513 self->s.angles[2] = 0; 514 515 self->s.sound = 0; 516 self->client->weapon_sound = 0; 517 518 self->maxs[2] = -8; 519 520 // self->solid = SOLID_NOT; 521 self->svflags |= SVF_DEADMONSTER; 522 523 if (!self->deadflag) 524 { 525 self->client->respawn_time = level.time + 1.0; 526 LookAtKiller (self, inflictor, attacker); 527 self->client->ps.pmove.pm_type = PM_DEAD; 528 ClientObituary (self, inflictor, attacker); 529 TossClientWeapon (self); 530 if (deathmatch->value) 531 Cmd_Help_f (self); // show scores 532 533 // clear inventory 534 // this is kind of ugly, but it's how we want to handle keys in coop 535 for (n = 0; n < game.num_items; n++) 536 { 537 if (coop->value && itemlist[n].flags & IT_KEY) 538 self->client->resp.coop_respawn.inventory[n] = self->client->pers.inventory[n]; 539 self->client->pers.inventory[n] = 0; 540 } 541 } 542 543 // remove powerups 544 self->client->quad_framenum = 0; 545 self->client->invincible_framenum = 0; 546 self->client->breather_framenum = 0; 547 self->client->enviro_framenum = 0; 548 self->flags &= ~FL_POWER_ARMOR; 549 550 if (self->health < -40) 551 { // gib 552 gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); 553 for (n= 0; n < 4; n++) 554 ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); 555 ThrowClientHead (self, damage); 556 557 self->takedamage = DAMAGE_NO; 558 } 559 else 560 { // normal death 561 if (!self->deadflag) 562 { 563 static int i; 564 565 i = (i+1)%3; 566 // start a death animation 567 self->client->anim_priority = ANIM_DEATH; 568 if (self->client->ps.pmove.pm_flags & PMF_DUCKED) 569 { 570 self->s.frame = FRAME_crdeath1-1; 571 self->client->anim_end = FRAME_crdeath5; 572 } 573 else switch (i) 574 { 575 case 0: 576 self->s.frame = FRAME_death101-1; 577 self->client->anim_end = FRAME_death106; 578 break; 579 case 1: 580 self->s.frame = FRAME_death201-1; 581 self->client->anim_end = FRAME_death206; 582 break; 583 case 2: 584 self->s.frame = FRAME_death301-1; 585 self->client->anim_end = FRAME_death308; 586 break; 587 } 588 gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0); 589 } 590 } 591 592 self->deadflag = DEAD_DEAD; 593 594 gi.linkentity (self); 595 } 596 597 //======================================================================= 598 599 /* 600 ============== 601 InitClientPersistant 602 603 This is only called when the game first initializes in single player, 604 but is called after each death and level change in deathmatch 605 ============== 606 */ 607 void InitClientPersistant (gclient_t *client) 608 { 609 gitem_t *item; 610 611 memset (&client->pers, 0, sizeof(client->pers)); 612 613 item = FindItem("Blaster"); 614 client->pers.selected_item = ITEM_INDEX(item); 615 client->pers.inventory[client->pers.selected_item] = 1; 616 617 client->pers.weapon = item; 618 619 client->pers.health = 100; 620 client->pers.max_health = 100; 621 622 client->pers.max_bullets = 200; 623 client->pers.max_shells = 100; 624 client->pers.max_rockets = 50; 625 client->pers.max_grenades = 50; 626 client->pers.max_cells = 200; 627 client->pers.max_slugs = 50; 628 629 client->pers.connected = true; 630 } 631 632 633 void InitClientResp (gclient_t *client) 634 { 635 memset (&client->resp, 0, sizeof(client->resp)); 636 client->resp.enterframe = level.framenum; 637 client->resp.coop_respawn = client->pers; 638 } 639 640 /* 641 ================== 642 SaveClientData 643 644 Some information that should be persistant, like health, 645 is still stored in the edict structure, so it needs to 646 be mirrored out to the client structure before all the 647 edicts are wiped. 648 ================== 649 */ 650 void SaveClientData (void) 651 { 652 int i; 653 edict_t *ent; 654 655 for (i=0 ; i<game.maxclients ; i++) 656 { 657 ent = &g_edicts[1+i]; 658 if (!ent->inuse) 659 continue; 660 game.clients[i].pers.health = ent->health; 661 game.clients[i].pers.max_health = ent->max_health; 662 game.clients[i].pers.savedFlags = (ent->flags & (FL_GODMODE|FL_NOTARGET|FL_POWER_ARMOR)); 663 if (coop->value) 664 game.clients[i].pers.score = ent->client->resp.score; 665 } 666 } 667 668 void FetchClientEntData (edict_t *ent) 669 { 670 ent->health = ent->client->pers.health; 671 ent->max_health = ent->client->pers.max_health; 672 ent->flags |= ent->client->pers.savedFlags; 673 if (coop->value) 674 ent->client->resp.score = ent->client->pers.score; 675 } 676 677 678 679 /* 680 ======================================================================= 681 682 SelectSpawnPoint 683 684 ======================================================================= 685 */ 686 687 /* 688 ================ 689 PlayersRangeFromSpot 690 691 Returns the distance to the nearest player from the given spot 692 ================ 693 */ 694 float PlayersRangeFromSpot (edict_t *spot) 695 { 696 edict_t *player; 697 float bestplayerdistance; 698 vec3_t v; 699 int n; 700 float playerdistance; 701 702 703 bestplayerdistance = 9999999; 704 705 for (n = 1; n <= maxclients->value; n++) 706 { 707 player = &g_edicts[n]; 708 709 if (!player->inuse) 710 continue; 711 712 if (player->health <= 0) 713 continue; 714 715 VectorSubtract (spot->s.origin, player->s.origin, v); 716 playerdistance = VectorLength (v); 717 718 if (playerdistance < bestplayerdistance) 719 bestplayerdistance = playerdistance; 720 } 721 722 return bestplayerdistance; 723 } 724 725 /* 726 ================ 727 SelectRandomDeathmatchSpawnPoint 728 729 go to a random point, but NOT the two points closest 730 to other players 731 ================ 732 */ 733 edict_t *SelectRandomDeathmatchSpawnPoint (void) 734 { 735 edict_t *spot, *spot1, *spot2; 736 int count = 0; 737 int selection; 738 float range, range1, range2; 739 740 spot = NULL; 741 range1 = range2 = 99999; 742 spot1 = spot2 = NULL; 743 744 while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) 745 { 746 count++; 747 range = PlayersRangeFromSpot(spot); 748 if (range < range1) 749 { 750 range1 = range; 751 spot1 = spot; 752 } 753 else if (range < range2) 754 { 755 range2 = range; 756 spot2 = spot; 757 } 758 } 759 760 if (!count) 761 return NULL; 762 763 if (count <= 2) 764 { 765 spot1 = spot2 = NULL; 766 } 767 else 768 count -= 2; 769 770 selection = rand() % count; 771 772 spot = NULL; 773 do 774 { 775 spot = G_Find (spot, FOFS(classname), "info_player_deathmatch"); 776 if (spot == spot1 || spot == spot2) 777 selection++; 778 } while(selection--); 779 780 return spot; 781 } 782 783 /* 784 ================ 785 SelectFarthestDeathmatchSpawnPoint 786 787 ================ 788 */ 789 edict_t *SelectFarthestDeathmatchSpawnPoint (void) 790 { 791 edict_t *bestspot; 792 float bestdistance, bestplayerdistance; 793 edict_t *spot; 794 795 796 spot = NULL; 797 bestspot = NULL; 798 bestdistance = 0; 799 while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) 800 { 801 bestplayerdistance = PlayersRangeFromSpot (spot); 802 803 if (bestplayerdistance > bestdistance) 804 { 805 bestspot = spot; 806 bestdistance = bestplayerdistance; 807 } 808 } 809 810 if (bestspot) 811 { 812 return bestspot; 813 } 814 815 // if there is a player just spawned on each and every start spot 816 // we have no choice to turn one into a telefrag meltdown 817 spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch"); 818 819 return spot; 820 } 821 822 edict_t *SelectDeathmatchSpawnPoint (void) 823 { 824 if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST) 825 return SelectFarthestDeathmatchSpawnPoint (); 826 else 827 return SelectRandomDeathmatchSpawnPoint (); 828 } 829 830 831 edict_t *SelectCoopSpawnPoint (edict_t *ent) 832 { 833 int index; 834 edict_t *spot = NULL; 835 char *target; 836 837 index = ent->client - game.clients; 838 839 // player 0 starts in normal player spawn point 840 if (!index) 841 return NULL; 842 843 spot = NULL; 844 845 // assume there are four coop spots at each spawnpoint 846 while (1) 847 { 848 spot = G_Find (spot, FOFS(classname), "info_player_coop"); 849 if (!spot) 850 return NULL; // we didn't have enough... 851 852 target = spot->targetname; 853 if (!target) 854 target = ""; 855 if ( Q_stricmp(game.spawnpoint, target) == 0 ) 856 { // this is a coop spawn point for one of the clients here 857 index--; 858 if (!index) 859 return spot; // this is it 860 } 861 } 862 863 864 return spot; 865 } 866 867 868 /* 869 =========== 870 SelectSpawnPoint 871 872 Chooses a player start, deathmatch start, coop start, etc 873 ============ 874 */ 875 void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles) 876 { 877 edict_t *spot = NULL; 878 879 if (deathmatch->value) 880 spot = SelectDeathmatchSpawnPoint (); 881 else if (coop->value) 882 spot = SelectCoopSpawnPoint (ent); 883 884 // find a single player start spot 885 if (!spot) 886 { 887 while ((spot = G_Find (spot, FOFS(classname), "info_player_start")) != NULL) 888 { 889 if (!game.spawnpoint[0] && !spot->targetname) 890 break; 891 892 if (!game.spawnpoint[0] || !spot->targetname) 893 continue; 894 895 if (Q_stricmp(game.spawnpoint, spot->targetname) == 0) 896 break; 897 } 898 899 if (!spot) 900 { 901 if (!game.spawnpoint[0]) 902 { // there wasn't a spawnpoint without a target, so use any 903 spot = G_Find (spot, FOFS(classname), "info_player_start"); 904 } 905 if (!spot) 906 gi.error ("Couldn't find spawn point %s\n", game.spawnpoint); 907 } 908 } 909 910 VectorCopy (spot->s.origin, origin); 911 origin[2] += 9; 912 VectorCopy (spot->s.angles, angles); 913 } 914 915 //====================================================================== 916 917 918 void InitBodyQue (void) 919 { 920 int i; 921 edict_t *ent; 922 923 level.body_que = 0; 924 for (i=0; i<BODY_QUEUE_SIZE ; i++) 925 { 926 ent = G_Spawn(); 927 ent->classname = "bodyque"; 928 } 929 } 930 931 void body_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) 932 { 933 int n; 934 935 if (self->health < -40) 936 { 937 gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); 938 for (n= 0; n < 4; n++) 939 ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); 940 self->s.origin[2] -= 48; 941 ThrowClientHead (self, damage); 942 self->takedamage = DAMAGE_NO; 943 } 944 } 945 946 void CopyToBodyQue (edict_t *ent) 947 { 948 edict_t *body; 949 950 // grab a body que and cycle to the next one 951 body = &g_edicts[(int)maxclients->value + level.body_que + 1]; 952 level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE; 953 954 // FIXME: send an effect on the removed body 955 956 gi.unlinkentity (ent); 957 958 gi.unlinkentity (body); 959 body->s = ent->s; 960 body->s.number = body - g_edicts; 961 962 body->svflags = ent->svflags; 963 VectorCopy (ent->mins, body->mins); 964 VectorCopy (ent->maxs, body->maxs); 965 VectorCopy (ent->absmin, body->absmin); 966 VectorCopy (ent->absmax, body->absmax); 967 VectorCopy (ent->size, body->size); 968 body->solid = ent->solid; 969 body->clipmask = ent->clipmask; 970 body->owner = ent->owner; 971 body->movetype = ent->movetype; 972 973 body->die = body_die; 974 body->takedamage = DAMAGE_YES; 975 976 gi.linkentity (body); 977 } 978 979 980 void respawn (edict_t *self) 981 { 982 if (deathmatch->value || coop->value) 983 { 984 // spectator's don't leave bodies 985 if (self->movetype != MOVETYPE_NOCLIP) 986 CopyToBodyQue (self); 987 self->svflags &= ~SVF_NOCLIENT; 988 PutClientInServer (self); 989 990 // add a teleportation effect 991 self->s.event = EV_PLAYER_TELEPORT; 992 993 // hold in place briefly 994 self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; 995 self->client->ps.pmove.pm_time = 14; 996 997 self->client->respawn_time = level.time; 998 999 return; 1000 } 1001 1002 // restart the entire server 1003 gi.AddCommandString ("menu_loadgame\n"); 1004 } 1005 1006 /* 1007 * only called when pers.spectator changes 1008 * note that resp.spectator should be the opposite of pers.spectator here 1009 */ 1010 void spectator_respawn (edict_t *ent) 1011 { 1012 int i, numspec; 1013 1014 // if the user wants to become a spectator, make sure he doesn't 1015 // exceed max_spectators 1016 1017 if (ent->client->pers.spectator) { 1018 char *value = Info_ValueForKey (ent->client->pers.userinfo, "spectator"); 1019 if (*spectator_password->string && 1020 strcmp(spectator_password->string, "none") && 1021 strcmp(spectator_password->string, value)) { 1022 gi.cprintf(ent, PRINT_HIGH, "Spectator password incorrect.\n"); 1023 ent->client->pers.spectator = false; 1024 gi.WriteByte (svc_stufftext); 1025 gi.WriteString ("spectator 0\n"); 1026 gi.unicast(ent, true); 1027 return; 1028 } 1029 1030 // count spectators 1031 for (i = 1, numspec = 0; i <= maxclients->value; i++) 1032 if (g_edicts[i].inuse && g_edicts[i].client->pers.spectator) 1033 numspec++; 1034 1035 if (numspec >= maxspectators->value) { 1036 gi.cprintf(ent, PRINT_HIGH, "Server spectator limit is full."); 1037 ent->client->pers.spectator = false; 1038 // reset his spectator var 1039 gi.WriteByte (svc_stufftext); 1040 gi.WriteString ("spectator 0\n"); 1041 gi.unicast(ent, true); 1042 return; 1043 } 1044 } else { 1045 // he was a spectator and wants to join the game 1046 // he must have the right password 1047 char *value = Info_ValueForKey (ent->client->pers.userinfo, "password"); 1048 if (*password->string && strcmp(password->string, "none") && 1049 strcmp(password->string, value)) { 1050 gi.cprintf(ent, PRINT_HIGH, "Password incorrect.\n"); 1051 ent->client->pers.spectator = true; 1052 gi.WriteByte (svc_stufftext); 1053 gi.WriteString ("spectator 1\n"); 1054 gi.unicast(ent, true); 1055 return; 1056 } 1057 } 1058 1059 // clear score on respawn 1060 ent->client->pers.score = ent->client->resp.score = 0; 1061 1062 ent->svflags &= ~SVF_NOCLIENT; 1063 PutClientInServer (ent); 1064 1065 // add a teleportation effect 1066 if (!ent->client->pers.spectator) { 1067 // send effect 1068 gi.WriteByte (svc_muzzleflash); 1069 gi.WriteShort (ent-g_edicts); 1070 gi.WriteByte (MZ_LOGIN); 1071 gi.multicast (ent->s.origin, MULTICAST_PVS); 1072 1073 // hold in place briefly 1074 ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; 1075 ent->client->ps.pmove.pm_time = 14; 1076 } 1077 1078 ent->client->respawn_time = level.time; 1079 1080 if (ent->client->pers.spectator) 1081 gi.bprintf (PRINT_HIGH, "%s has moved to the sidelines\n", ent->client->pers.netname); 1082 else 1083 gi.bprintf (PRINT_HIGH, "%s joined the game\n", ent->client->pers.netname); 1084 } 1085 1086 //============================================================== 1087 1088 1089 /* 1090 =========== 1091 PutClientInServer 1092 1093 Called when a player connects to a server or respawns in 1094 a deathmatch. 1095 ============ 1096 */ 1097 void PutClientInServer (edict_t *ent) 1098 { 1099 vec3_t mins = {-16, -16, -24}; 1100 vec3_t maxs = {16, 16, 32}; 1101 int index; 1102 vec3_t spawn_origin, spawn_angles; 1103 gclient_t *client; 1104 int i; 1105 client_persistant_t saved; 1106 client_respawn_t resp; 1107 1108 // find a spawn point 1109 // do it before setting health back up, so farthest 1110 // ranging doesn't count this client 1111 SelectSpawnPoint (ent, spawn_origin, spawn_angles); 1112 1113 index = ent-g_edicts-1; 1114 client = ent->client; 1115 1116 // deathmatch wipes most client data every spawn 1117 if (deathmatch->value) 1118 { 1119 char userinfo[MAX_INFO_STRING]; 1120 1121 resp = client->resp; 1122 memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); 1123 InitClientPersistant (client); 1124 ClientUserinfoChanged (ent, userinfo); 1125 } 1126 else if (coop->value) 1127 { 1128 // int n; 1129 char userinfo[MAX_INFO_STRING]; 1130 1131 resp = client->resp; 1132 memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); 1133 // this is kind of ugly, but it's how we want to handle keys in coop 1134 // for (n = 0; n < game.num_items; n++) 1135 // { 1136 // if (itemlist[n].flags & IT_KEY) 1137 // resp.coop_respawn.inventory[n] = client->pers.inventory[n]; 1138 // } 1139 resp.coop_respawn.game_helpchanged = client->pers.game_helpchanged; 1140 resp.coop_respawn.helpchanged = client->pers.helpchanged; 1141 client->pers = resp.coop_respawn; 1142 ClientUserinfoChanged (ent, userinfo); 1143 if (resp.score > client->pers.score) 1144 client->pers.score = resp.score; 1145 } 1146 else 1147 { 1148 memset (&resp, 0, sizeof(resp)); 1149 } 1150 1151 // clear everything but the persistant data 1152 saved = client->pers; 1153 memset (client, 0, sizeof(*client)); 1154 client->pers = saved; 1155 if (client->pers.health <= 0) 1156 InitClientPersistant(client); 1157 client->resp = resp; 1158 1159 // copy some data from the client to the entity 1160 FetchClientEntData (ent); 1161 1162 // clear entity values 1163 ent->groundentity = NULL; 1164 ent->client = &game.clients[index]; 1165 ent->takedamage = DAMAGE_AIM; 1166 ent->movetype = MOVETYPE_WALK; 1167 ent->viewheight = 22; 1168 ent->inuse = true; 1169 ent->classname = "player"; 1170 ent->mass = 200; 1171 ent->solid = SOLID_BBOX; 1172 ent->deadflag = DEAD_NO; 1173 ent->air_finished = level.time + 12; 1174 ent->clipmask = MASK_PLAYERSOLID; 1175 ent->model = "players/male/tris.md2"; 1176 ent->pain = player_pain; 1177 ent->die = player_die; 1178 ent->waterlevel = 0; 1179 ent->watertype = 0; 1180 ent->flags &= ~FL_NO_KNOCKBACK; 1181 ent->svflags &= ~SVF_DEADMONSTER; 1182 1183 VectorCopy (mins, ent->mins); 1184 VectorCopy (maxs, ent->maxs); 1185 VectorClear (ent->velocity); 1186 1187 // clear playerstate values 1188 memset (&ent->client->ps, 0, sizeof(client->ps)); 1189 1190 client->ps.pmove.origin[0] = spawn_origin[0]*8; 1191 client->ps.pmove.origin[1] = spawn_origin[1]*8; 1192 client->ps.pmove.origin[2] = spawn_origin[2]*8; 1193 1194 if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) 1195 { 1196 client->ps.fov = 90; 1197 } 1198 else 1199 { 1200 client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov")); 1201 if (client->ps.fov < 1) 1202 client->ps.fov = 90; 1203 else if (client->ps.fov > 160) 1204 client->ps.fov = 160; 1205 } 1206 1207 client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model); 1208 1209 // clear entity state values 1210 ent->s.effects = 0; 1211 ent->s.modelindex = 255; // will use the skin specified model 1212 ent->s.modelindex2 = 255; // custom gun model 1213 // sknum is player num and weapon number 1214 // weapon number will be added in changeweapon 1215 ent->s.skinnum = ent - g_edicts - 1; 1216 1217 ent->s.frame = 0; 1218 VectorCopy (spawn_origin, ent->s.origin); 1219 ent->s.origin[2] += 1; // make sure off ground 1220 VectorCopy (ent->s.origin, ent->s.old_origin); 1221 1222 // set the delta angle 1223 for (i=0 ; i<3 ; i++) 1224 client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]); 1225 1226 ent->s.angles[PITCH] = 0; 1227 ent->s.angles[YAW] = spawn_angles[YAW]; 1228 ent->s.angles[ROLL] = 0; 1229 VectorCopy (ent->s.angles, client->ps.viewangles); 1230 VectorCopy (ent->s.angles, client->v_angle); 1231 1232 // spawn a spectator 1233 if (client->pers.spectator) { 1234 client->chase_target = NULL; 1235 1236 client->resp.spectator = true; 1237 1238 ent->movetype = MOVETYPE_NOCLIP; 1239 ent->solid = SOLID_NOT; 1240 ent->svflags |= SVF_NOCLIENT; 1241 ent->client->ps.gunindex = 0; 1242 gi.linkentity (ent); 1243 return; 1244 } else 1245 client->resp.spectator = false; 1246 1247 if (!KillBox (ent)) 1248 { // could't spawn in? 1249 } 1250 1251 gi.linkentity (ent); 1252 1253 // force the current weapon up 1254 client->newweapon = client->pers.weapon; 1255 ChangeWeapon (ent); 1256 } 1257 1258 /* 1259 ===================== 1260 ClientBeginDeathmatch 1261 1262 A client has just connected to the server in 1263 deathmatch mode, so clear everything out before starting them. 1264 ===================== 1265 */ 1266 void ClientBeginDeathmatch (edict_t *ent) 1267 { 1268 G_InitEdict (ent); 1269 1270 InitClientResp (ent->client); 1271 1272 // locate ent at a spawn point 1273 PutClientInServer (ent); 1274 1275 // send effect 1276 gi.WriteByte (svc_muzzleflash); 1277 gi.WriteShort (ent-g_edicts); 1278 gi.WriteByte (MZ_LOGIN); 1279 gi.multicast (ent->s.origin, MULTICAST_PVS); 1280 1281 gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); 1282 1283 // make sure all view stuff is valid 1284 ClientEndServerFrame (ent); 1285 } 1286 1287 1288 /* 1289 =========== 1290 ClientBegin 1291 1292 called when a client has finished connecting, and is ready 1293 to be placed into the game. This will happen every level load. 1294 ============ 1295 */ 1296 void ClientBegin (edict_t *ent) 1297 { 1298 int i; 1299 1300 ent->client = game.clients + (ent - g_edicts - 1); 1301 1302 if (deathmatch->value) 1303 { 1304 ClientBeginDeathmatch (ent); 1305 return; 1306 } 1307 1308 // if there is already a body waiting for us (a loadgame), just 1309 // take it, otherwise spawn one from scratch 1310 if (ent->inuse == true) 1311 { 1312 // the client has cleared the client side viewangles upon 1313 // connecting to the server, which is different than the 1314 // state when the game is saved, so we need to compensate 1315 // with deltaangles 1316 for (i=0 ; i<3 ; i++) 1317 ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]); 1318 } 1319 else 1320 { 1321 // a spawn point will completely reinitialize the entity 1322 // except for the persistant data that was initialized at 1323 // ClientConnect() time 1324 G_InitEdict (ent); 1325 ent->classname = "player"; 1326 InitClientResp (ent->client); 1327 PutClientInServer (ent); 1328 } 1329 1330 if (level.intermissiontime) 1331 { 1332 MoveClientToIntermission (ent); 1333 } 1334 else 1335 { 1336 // send effect if in a multiplayer game 1337 if (game.maxclients > 1) 1338 { 1339 gi.WriteByte (svc_muzzleflash); 1340 gi.WriteShort (ent-g_edicts); 1341 gi.WriteByte (MZ_LOGIN); 1342 gi.multicast (ent->s.origin, MULTICAST_PVS); 1343 1344 gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); 1345 } 1346 } 1347 1348 // make sure all view stuff is valid 1349 ClientEndServerFrame (ent); 1350 } 1351 1352 /* 1353 =========== 1354 ClientUserInfoChanged 1355 1356 called whenever the player updates a userinfo variable. 1357 1358 The game can override any of the settings in place 1359 (forcing skins or names, etc) before copying it off. 1360 ============ 1361 */ 1362 void ClientUserinfoChanged (edict_t *ent, char *userinfo) 1363 { 1364 char *s; 1365 int playernum; 1366 1367 // check for malformed or illegal info strings 1368 if (!Info_Validate(userinfo)) 1369 { 1370 strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt"); 1371 } 1372 1373 // set name 1374 s = Info_ValueForKey (userinfo, "name"); 1375 strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1); 1376 1377 // set spectator 1378 s = Info_ValueForKey (userinfo, "spectator"); 1379 // spectators are only supported in deathmatch 1380 if (deathmatch->value && *s && strcmp(s, "0")) 1381 ent->client->pers.spectator = true; 1382 else 1383 ent->client->pers.spectator = false; 1384 1385 // set skin 1386 s = Info_ValueForKey (userinfo, "skin"); 1387 1388 playernum = ent-g_edicts-1; 1389 1390 // combine name and skin into a configstring 1391 gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) ); 1392 1393 // fov 1394 if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) 1395 { 1396 ent->client->ps.fov = 90; 1397 } 1398 else 1399 { 1400 ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov")); 1401 if (ent->client->ps.fov < 1) 1402 ent->client->ps.fov = 90; 1403 else if (ent->client->ps.fov > 160) 1404 ent->client->ps.fov = 160; 1405 } 1406 1407 // handedness 1408 s = Info_ValueForKey (userinfo, "hand"); 1409 if (strlen(s)) 1410 { 1411 ent->client->pers.hand = atoi(s); 1412 } 1413 1414 // save off the userinfo in case we want to check something later 1415 strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1); 1416 } 1417 1418 1419 /* 1420 =========== 1421 ClientConnect 1422 1423 Called when a player begins connecting to the server. 1424 The game can refuse entrance to a client by returning false. 1425 If the client is allowed, the connection process will continue 1426 and eventually get to ClientBegin() 1427 Changing levels will NOT cause this to be called again, but 1428 loadgames will. 1429 ============ 1430 */ 1431 qboolean ClientConnect (edict_t *ent, char *userinfo) 1432 { 1433 char *value; 1434 1435 // check to see if they are on the banned IP list 1436 value = Info_ValueForKey (userinfo, "ip"); 1437 if (SV_FilterPacket(value)) { 1438 Info_SetValueForKey(userinfo, "rejmsg", "Banned."); 1439 return false; 1440 } 1441 1442 // check for a spectator 1443 value = Info_ValueForKey (userinfo, "spectator"); 1444 if (deathmatch->value && *value && strcmp(value, "0")) { 1445 int i, numspec; 1446 1447 if (*spectator_password->string && 1448 strcmp(spectator_password->string, "none") && 1449 strcmp(spectator_password->string, value)) { 1450 Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect."); 1451 return false; 1452 } 1453 1454 // count spectators 1455 for (i = numspec = 0; i < maxclients->value; i++) 1456 if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator) 1457 numspec++; 1458 1459 if (numspec >= maxspectators->value) { 1460 Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full."); 1461 return false; 1462 } 1463 } else { 1464 // check for a password 1465 value = Info_ValueForKey (userinfo, "password"); 1466 if (*password->string && strcmp(password->string, "none") && 1467 strcmp(password->string, value)) { 1468 Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect."); 1469 return false; 1470 } 1471 } 1472 1473 1474 // they can connect 1475 ent->client = game.clients + (ent - g_edicts - 1); 1476 1477 // if there is already a body waiting for us (a loadgame), just 1478 // take it, otherwise spawn one from scratch 1479 if (ent->inuse == false) 1480 { 1481 // clear the respawning variables 1482 InitClientResp (ent->client); 1483 if (!game.autosaved || !ent->client->pers.weapon) 1484 InitClientPersistant (ent->client); 1485 } 1486 1487 ClientUserinfoChanged (ent, userinfo); 1488 1489 if (game.maxclients > 1) 1490 gi.dprintf ("%s connected\n", ent->client->pers.netname); 1491 1492 ent->client->pers.connected = true; 1493 return true; 1494 } 1495 1496 /* 1497 =========== 1498 ClientDisconnect 1499 1500 Called when a player drops from the server. 1501 Will not be called between levels. 1502 ============ 1503 */ 1504 void ClientDisconnect (edict_t *ent) 1505 { 1506 int playernum; 1507 1508 if (!ent->client) 1509 return; 1510 1511 gi.bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname); 1512 1513 // send effect 1514 gi.WriteByte (svc_muzzleflash); 1515 gi.WriteShort (ent-g_edicts); 1516 gi.WriteByte (MZ_LOGOUT); 1517 gi.multicast (ent->s.origin, MULTICAST_PVS); 1518 1519 gi.unlinkentity (ent); 1520 ent->s.modelindex = 0; 1521 ent->solid = SOLID_NOT; 1522 ent->inuse = false; 1523 ent->classname = "disconnected"; 1524 ent->client->pers.connected = false; 1525 1526 playernum = ent-g_edicts-1; 1527 gi.configstring (CS_PLAYERSKINS+playernum, ""); 1528 } 1529 1530 1531 //============================================================== 1532 1533 1534 edict_t *pm_passent; 1535 1536 // pmove doesn't need to know about passent and contentmask 1537 trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) 1538 { 1539 if (pm_passent->health > 0) 1540 return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID); 1541 else 1542 return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID); 1543 } 1544 1545 unsigned CheckBlock (void *b, int c) 1546 { 1547 int v,i; 1548 v = 0; 1549 for (i=0 ; i<c ; i++) 1550 v+= ((byte *)b)[i]; 1551 return v; 1552 } 1553 void PrintPmove (pmove_t *pm) 1554 { 1555 unsigned c1, c2; 1556 1557 c1 = CheckBlock (&pm->s, sizeof(pm->s)); 1558 c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd)); 1559 Com_Printf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2); 1560 } 1561 1562 /* 1563 ============== 1564 ClientThink 1565 1566 This will be called once for each client frame, which will 1567 usually be a couple times for each server frame. 1568 ============== 1569 */ 1570 void ClientThink (edict_t *ent, usercmd_t *ucmd) 1571 { 1572 gclient_t *client; 1573 edict_t *other; 1574 int i, j; 1575 pmove_t pm; 1576 1577 level.current_entity = ent; 1578 client = ent->client; 1579 1580 if (level.intermissiontime) 1581 { 1582 client->ps.pmove.pm_type = PM_FREEZE; 1583 // can exit intermission after five seconds 1584 if (level.time > level.intermissiontime + 5.0 1585 && (ucmd->buttons & BUTTON_ANY) ) 1586 level.exitintermission = true; 1587 return; 1588 } 1589 1590 pm_passent = ent; 1591 1592 if (ent->client->chase_target) { 1593 1594 client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); 1595 client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); 1596 client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); 1597 1598 } else { 1599 1600 // set up for pmove 1601 memset (&pm, 0, sizeof(pm)); 1602 1603 if (ent->movetype == MOVETYPE_NOCLIP) 1604 client->ps.pmove.pm_type = PM_SPECTATOR; 1605 else if (ent->s.modelindex != 255) 1606 client->ps.pmove.pm_type = PM_GIB; 1607 else if (ent->deadflag) 1608 client->ps.pmove.pm_type = PM_DEAD; 1609 else 1610 client->ps.pmove.pm_type = PM_NORMAL; 1611 1612 client->ps.pmove.gravity = sv_gravity->value; 1613 pm.s = client->ps.pmove; 1614 1615 for (i=0 ; i<3 ; i++) 1616 { 1617 pm.s.origin[i] = ent->s.origin[i]*8; 1618 pm.s.velocity[i] = ent->velocity[i]*8; 1619 } 1620 1621 if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s))) 1622 { 1623 pm.snapinitial = true; 1624 // gi.dprintf ("pmove changed!\n"); 1625 } 1626 1627 pm.cmd = *ucmd; 1628 1629 pm.trace = PM_trace; // adds default parms 1630 pm.pointcontents = gi.pointcontents; 1631 1632 // perform a pmove 1633 gi.Pmove (&pm); 1634 1635 // save results of pmove 1636 client->ps.pmove = pm.s; 1637 client->old_pmove = pm.s; 1638 1639 for (i=0 ; i<3 ; i++) 1640 { 1641 ent->s.origin[i] = pm.s.origin[i]*0.125; 1642 ent->velocity[i] = pm.s.velocity[i]*0.125; 1643 } 1644 1645 VectorCopy (pm.mins, ent->mins); 1646 VectorCopy (pm.maxs, ent->maxs); 1647 1648 client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); 1649 client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); 1650 client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); 1651 1652 if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) 1653 { 1654 gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0); 1655 PlayerNoise(ent, ent->s.origin, PNOISE_SELF); 1656 } 1657 1658 ent->viewheight = pm.viewheight; 1659 ent->waterlevel = pm.waterlevel; 1660 ent->watertype = pm.watertype; 1661 ent->groundentity = pm.groundentity; 1662 if (pm.groundentity) 1663 ent->groundentity_linkcount = pm.groundentity->linkcount; 1664 1665 if (ent->deadflag) 1666 { 1667 client->ps.viewangles[ROLL] = 40; 1668 client->ps.viewangles[PITCH] = -15; 1669 client->ps.viewangles[YAW] = client->killer_yaw; 1670 } 1671 else 1672 { 1673 VectorCopy (pm.viewangles, client->v_angle); 1674 VectorCopy (pm.viewangles, client->ps.viewangles); 1675 } 1676 1677 gi.linkentity (ent); 1678 1679 if (ent->movetype != MOVETYPE_NOCLIP) 1680 G_TouchTriggers (ent); 1681 1682 // touch other objects 1683 for (i=0 ; i<pm.numtouch ; i++) 1684 { 1685 other = pm.touchents[i]; 1686 for (j=0 ; j<i ; j++) 1687 if (pm.touchents[j] == other) 1688 break; 1689 if (j != i) 1690 continue; // duplicated 1691 if (!other->touch) 1692 continue; 1693 other->touch (other, ent, NULL, NULL); 1694 } 1695 1696 } 1697 1698 client->oldbuttons = client->buttons; 1699 client->buttons = ucmd->buttons; 1700 client->latched_buttons |= client->buttons & ~client->oldbuttons; 1701 1702 // save light level the player is standing on for 1703 // monster sighting AI 1704 ent->light_level = ucmd->lightlevel; 1705 1706 // fire weapon from final position if needed 1707 if (client->latched_buttons & BUTTON_ATTACK) 1708 { 1709 if (client->resp.spectator) { 1710 1711 client->latched_buttons = 0; 1712 1713 if (client->chase_target) { 1714 client->chase_target = NULL; 1715 client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; 1716 } else 1717 GetChaseTarget(ent); 1718 1719 } else if (!client->weapon_thunk) { 1720 client->weapon_thunk = true; 1721 Think_Weapon (ent); 1722 } 1723 } 1724 1725 if (client->resp.spectator) { 1726 if (ucmd->upmove >= 10) { 1727 if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) { 1728 client->ps.pmove.pm_flags |= PMF_JUMP_HELD; 1729 if (client->chase_target) 1730 ChaseNext(ent); 1731 else 1732 GetChaseTarget(ent); 1733 } 1734 } else 1735 client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD; 1736 } 1737 1738 // update chase cam if being followed 1739 for (i = 1; i <= maxclients->value; i++) { 1740 other = g_edicts + i; 1741 if (other->inuse && other->client->chase_target == ent) 1742 UpdateChaseCam(other); 1743 } 1744 } 1745 1746 1747 /* 1748 ============== 1749 ClientBeginServerFrame 1750 1751 This will be called once for each server frame, before running 1752 any other entities in the world. 1753 ============== 1754 */ 1755 void ClientBeginServerFrame (edict_t *ent) 1756 { 1757 gclient_t *client; 1758 int buttonMask; 1759 1760 if (level.intermissiontime) 1761 return; 1762 1763 client = ent->client; 1764 1765 if (deathmatch->value && 1766 client->pers.spectator != client->resp.spectator && 1767 (level.time - client->respawn_time) >= 5) { 1768 spectator_respawn(ent); 1769 return; 1770 } 1771 1772 // run weapon animations if it hasn't been done by a ucmd_t 1773 if (!client->weapon_thunk && !client->resp.spectator) 1774 Think_Weapon (ent); 1775 else 1776 client->weapon_thunk = false; 1777 1778 if (ent->deadflag) 1779 { 1780 // wait for any button just going down 1781 if ( level.time > client->respawn_time) 1782 { 1783 // in deathmatch, only wait for attack button 1784 if (deathmatch->value) 1785 buttonMask = BUTTON_ATTACK; 1786 else 1787 buttonMask = -1; 1788 1789 if ( ( client->latched_buttons & buttonMask ) || 1790 (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) ) 1791 { 1792 respawn(ent); 1793 client->latched_buttons = 0; 1794 } 1795 } 1796 return; 1797 } 1798 1799 // add player trail so monsters can follow 1800 if (!deathmatch->value) 1801 if (!visible (ent, PlayerTrail_LastSpot() ) ) 1802 PlayerTrail_Add (ent->s.old_origin); 1803 1804 client->latched_buttons = 0; 1805 }