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