|
|
1.1.1.3 ! root 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: */ 1.1 root 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]); 1.1.1.2 root 483: else { 1.1 root 484: self->client->killer_yaw = 0; 1.1.1.2 root 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: 1.1 root 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: { 1.1.1.2 root 984: // spectator's don't leave bodies 985: if (self->movetype != MOVETYPE_NOCLIP) 986: CopyToBodyQue (self); 987: self->svflags &= ~SVF_NOCLIENT; 1.1 root 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: 1.1.1.2 root 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: 1.1.1.3 ! root 1059: // clear client on respawn ! 1060: ent->client->resp.score = ent->client->pers.score = 0; 1.1.1.2 root 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: 1.1 root 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++) 1.1.1.3 ! root 1224: { 1.1 root 1225: client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]); 1.1.1.3 ! root 1226: } 1.1 root 1227: 1228: ent->s.angles[PITCH] = 0; 1229: ent->s.angles[YAW] = spawn_angles[YAW]; 1230: ent->s.angles[ROLL] = 0; 1231: VectorCopy (ent->s.angles, client->ps.viewangles); 1232: VectorCopy (ent->s.angles, client->v_angle); 1233: 1.1.1.2 root 1234: // spawn a spectator 1235: if (client->pers.spectator) { 1236: client->chase_target = NULL; 1237: 1238: client->resp.spectator = true; 1239: 1240: ent->movetype = MOVETYPE_NOCLIP; 1241: ent->solid = SOLID_NOT; 1242: ent->svflags |= SVF_NOCLIENT; 1243: ent->client->ps.gunindex = 0; 1244: gi.linkentity (ent); 1245: return; 1246: } else 1247: client->resp.spectator = false; 1248: 1.1 root 1249: if (!KillBox (ent)) 1250: { // could't spawn in? 1251: } 1252: 1253: gi.linkentity (ent); 1254: 1255: // force the current weapon up 1256: client->newweapon = client->pers.weapon; 1257: ChangeWeapon (ent); 1258: } 1259: 1260: /* 1261: ===================== 1262: ClientBeginDeathmatch 1263: 1264: A client has just connected to the server in 1265: deathmatch mode, so clear everything out before starting them. 1266: ===================== 1267: */ 1268: void ClientBeginDeathmatch (edict_t *ent) 1269: { 1270: G_InitEdict (ent); 1271: 1272: InitClientResp (ent->client); 1273: 1274: // locate ent at a spawn point 1275: PutClientInServer (ent); 1276: 1.1.1.3 ! root 1277: if (level.intermissiontime) ! 1278: { ! 1279: MoveClientToIntermission (ent); ! 1280: } ! 1281: else ! 1282: { ! 1283: // send effect ! 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: } 1.1 root 1289: 1290: gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); 1291: 1292: // make sure all view stuff is valid 1293: ClientEndServerFrame (ent); 1294: } 1295: 1296: 1297: /* 1298: =========== 1299: ClientBegin 1300: 1301: called when a client has finished connecting, and is ready 1302: to be placed into the game. This will happen every level load. 1303: ============ 1304: */ 1305: void ClientBegin (edict_t *ent) 1306: { 1307: int i; 1308: 1309: ent->client = game.clients + (ent - g_edicts - 1); 1310: 1311: if (deathmatch->value) 1312: { 1313: ClientBeginDeathmatch (ent); 1314: return; 1315: } 1316: 1317: // if there is already a body waiting for us (a loadgame), just 1318: // take it, otherwise spawn one from scratch 1319: if (ent->inuse == true) 1320: { 1321: // the client has cleared the client side viewangles upon 1322: // connecting to the server, which is different than the 1323: // state when the game is saved, so we need to compensate 1324: // with deltaangles 1325: for (i=0 ; i<3 ; i++) 1326: ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]); 1327: } 1328: else 1329: { 1330: // a spawn point will completely reinitialize the entity 1331: // except for the persistant data that was initialized at 1332: // ClientConnect() time 1333: G_InitEdict (ent); 1334: ent->classname = "player"; 1335: InitClientResp (ent->client); 1336: PutClientInServer (ent); 1337: } 1338: 1339: if (level.intermissiontime) 1340: { 1341: MoveClientToIntermission (ent); 1342: } 1343: else 1344: { 1345: // send effect if in a multiplayer game 1346: if (game.maxclients > 1) 1347: { 1348: gi.WriteByte (svc_muzzleflash); 1349: gi.WriteShort (ent-g_edicts); 1350: gi.WriteByte (MZ_LOGIN); 1351: gi.multicast (ent->s.origin, MULTICAST_PVS); 1352: 1353: gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); 1354: } 1355: } 1356: 1357: // make sure all view stuff is valid 1358: ClientEndServerFrame (ent); 1359: } 1360: 1361: /* 1362: =========== 1363: ClientUserInfoChanged 1364: 1365: called whenever the player updates a userinfo variable. 1366: 1367: The game can override any of the settings in place 1368: (forcing skins or names, etc) before copying it off. 1369: ============ 1370: */ 1371: void ClientUserinfoChanged (edict_t *ent, char *userinfo) 1372: { 1373: char *s; 1374: int playernum; 1375: 1376: // check for malformed or illegal info strings 1377: if (!Info_Validate(userinfo)) 1378: { 1379: strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt"); 1380: } 1381: 1382: // set name 1383: s = Info_ValueForKey (userinfo, "name"); 1384: strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1); 1385: 1.1.1.2 root 1386: // set spectator 1387: s = Info_ValueForKey (userinfo, "spectator"); 1388: // spectators are only supported in deathmatch 1389: if (deathmatch->value && *s && strcmp(s, "0")) 1390: ent->client->pers.spectator = true; 1391: else 1392: ent->client->pers.spectator = false; 1393: 1.1 root 1394: // set skin 1395: s = Info_ValueForKey (userinfo, "skin"); 1396: 1397: playernum = ent-g_edicts-1; 1398: 1399: // combine name and skin into a configstring 1400: gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) ); 1401: 1402: // fov 1403: if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) 1404: { 1405: ent->client->ps.fov = 90; 1406: } 1407: else 1408: { 1409: ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov")); 1410: if (ent->client->ps.fov < 1) 1411: ent->client->ps.fov = 90; 1412: else if (ent->client->ps.fov > 160) 1413: ent->client->ps.fov = 160; 1414: } 1415: 1416: // handedness 1417: s = Info_ValueForKey (userinfo, "hand"); 1418: if (strlen(s)) 1419: { 1420: ent->client->pers.hand = atoi(s); 1421: } 1422: 1423: // save off the userinfo in case we want to check something later 1424: strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1); 1425: } 1426: 1427: 1428: /* 1429: =========== 1430: ClientConnect 1431: 1432: Called when a player begins connecting to the server. 1433: The game can refuse entrance to a client by returning false. 1434: If the client is allowed, the connection process will continue 1435: and eventually get to ClientBegin() 1436: Changing levels will NOT cause this to be called again, but 1437: loadgames will. 1438: ============ 1439: */ 1440: qboolean ClientConnect (edict_t *ent, char *userinfo) 1441: { 1442: char *value; 1443: 1444: // check to see if they are on the banned IP list 1445: value = Info_ValueForKey (userinfo, "ip"); 1.1.1.2 root 1446: if (SV_FilterPacket(value)) { 1447: Info_SetValueForKey(userinfo, "rejmsg", "Banned."); 1.1 root 1448: return false; 1.1.1.2 root 1449: } 1450: 1451: // check for a spectator 1452: value = Info_ValueForKey (userinfo, "spectator"); 1453: if (deathmatch->value && *value && strcmp(value, "0")) { 1454: int i, numspec; 1455: 1456: if (*spectator_password->string && 1457: strcmp(spectator_password->string, "none") && 1458: strcmp(spectator_password->string, value)) { 1459: Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect."); 1460: return false; 1461: } 1462: 1463: // count spectators 1464: for (i = numspec = 0; i < maxclients->value; i++) 1465: if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator) 1466: numspec++; 1467: 1468: if (numspec >= maxspectators->value) { 1469: Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full."); 1470: return false; 1471: } 1472: } else { 1473: // check for a password 1474: value = Info_ValueForKey (userinfo, "password"); 1475: if (*password->string && strcmp(password->string, "none") && 1476: strcmp(password->string, value)) { 1477: Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect."); 1478: return false; 1479: } 1480: } 1.1 root 1481: 1482: 1483: // they can connect 1484: ent->client = game.clients + (ent - g_edicts - 1); 1485: 1486: // if there is already a body waiting for us (a loadgame), just 1487: // take it, otherwise spawn one from scratch 1488: if (ent->inuse == false) 1489: { 1490: // clear the respawning variables 1491: InitClientResp (ent->client); 1492: if (!game.autosaved || !ent->client->pers.weapon) 1493: InitClientPersistant (ent->client); 1494: } 1495: 1496: ClientUserinfoChanged (ent, userinfo); 1497: 1498: if (game.maxclients > 1) 1499: gi.dprintf ("%s connected\n", ent->client->pers.netname); 1500: 1.1.1.3 ! root 1501: ent->svflags = 0; // make sure we start with known default 1.1 root 1502: ent->client->pers.connected = true; 1503: return true; 1504: } 1505: 1506: /* 1507: =========== 1508: ClientDisconnect 1509: 1510: Called when a player drops from the server. 1511: Will not be called between levels. 1512: ============ 1513: */ 1514: void ClientDisconnect (edict_t *ent) 1515: { 1516: int playernum; 1517: 1518: if (!ent->client) 1519: return; 1520: 1521: gi.bprintf (PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname); 1522: 1523: // send effect 1524: gi.WriteByte (svc_muzzleflash); 1525: gi.WriteShort (ent-g_edicts); 1526: gi.WriteByte (MZ_LOGOUT); 1527: gi.multicast (ent->s.origin, MULTICAST_PVS); 1528: 1529: gi.unlinkentity (ent); 1530: ent->s.modelindex = 0; 1531: ent->solid = SOLID_NOT; 1532: ent->inuse = false; 1533: ent->classname = "disconnected"; 1534: ent->client->pers.connected = false; 1535: 1536: playernum = ent-g_edicts-1; 1537: gi.configstring (CS_PLAYERSKINS+playernum, ""); 1538: } 1539: 1540: 1541: //============================================================== 1542: 1543: 1544: edict_t *pm_passent; 1545: 1546: // pmove doesn't need to know about passent and contentmask 1547: trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) 1548: { 1549: if (pm_passent->health > 0) 1550: return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID); 1551: else 1552: return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID); 1553: } 1554: 1555: unsigned CheckBlock (void *b, int c) 1556: { 1557: int v,i; 1558: v = 0; 1559: for (i=0 ; i<c ; i++) 1560: v+= ((byte *)b)[i]; 1561: return v; 1562: } 1563: void PrintPmove (pmove_t *pm) 1564: { 1565: unsigned c1, c2; 1566: 1567: c1 = CheckBlock (&pm->s, sizeof(pm->s)); 1568: c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd)); 1569: Com_Printf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2); 1570: } 1571: 1572: /* 1573: ============== 1574: ClientThink 1575: 1576: This will be called once for each client frame, which will 1577: usually be a couple times for each server frame. 1578: ============== 1579: */ 1580: void ClientThink (edict_t *ent, usercmd_t *ucmd) 1581: { 1582: gclient_t *client; 1583: edict_t *other; 1584: int i, j; 1585: pmove_t pm; 1586: 1587: level.current_entity = ent; 1588: client = ent->client; 1589: 1590: if (level.intermissiontime) 1591: { 1592: client->ps.pmove.pm_type = PM_FREEZE; 1593: // can exit intermission after five seconds 1594: if (level.time > level.intermissiontime + 5.0 1595: && (ucmd->buttons & BUTTON_ANY) ) 1596: level.exitintermission = true; 1597: return; 1598: } 1599: 1600: pm_passent = ent; 1601: 1.1.1.2 root 1602: if (ent->client->chase_target) { 1.1 root 1603: 1.1.1.2 root 1604: client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); 1605: client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); 1606: client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); 1607: 1608: } else { 1609: 1610: // set up for pmove 1611: memset (&pm, 0, sizeof(pm)); 1612: 1613: if (ent->movetype == MOVETYPE_NOCLIP) 1614: client->ps.pmove.pm_type = PM_SPECTATOR; 1615: else if (ent->s.modelindex != 255) 1616: client->ps.pmove.pm_type = PM_GIB; 1617: else if (ent->deadflag) 1618: client->ps.pmove.pm_type = PM_DEAD; 1619: else 1620: client->ps.pmove.pm_type = PM_NORMAL; 1.1 root 1621: 1.1.1.2 root 1622: client->ps.pmove.gravity = sv_gravity->value; 1623: pm.s = client->ps.pmove; 1.1 root 1624: 1.1.1.2 root 1625: for (i=0 ; i<3 ; i++) 1626: { 1627: pm.s.origin[i] = ent->s.origin[i]*8; 1628: pm.s.velocity[i] = ent->velocity[i]*8; 1629: } 1.1 root 1630: 1.1.1.2 root 1631: if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s))) 1632: { 1633: pm.snapinitial = true; 1634: // gi.dprintf ("pmove changed!\n"); 1635: } 1.1 root 1636: 1.1.1.2 root 1637: pm.cmd = *ucmd; 1.1 root 1638: 1.1.1.2 root 1639: pm.trace = PM_trace; // adds default parms 1640: pm.pointcontents = gi.pointcontents; 1.1 root 1641: 1.1.1.2 root 1642: // perform a pmove 1643: gi.Pmove (&pm); 1.1 root 1644: 1.1.1.2 root 1645: // save results of pmove 1646: client->ps.pmove = pm.s; 1647: client->old_pmove = pm.s; 1.1 root 1648: 1.1.1.2 root 1649: for (i=0 ; i<3 ; i++) 1650: { 1651: ent->s.origin[i] = pm.s.origin[i]*0.125; 1652: ent->velocity[i] = pm.s.velocity[i]*0.125; 1653: } 1.1 root 1654: 1.1.1.2 root 1655: VectorCopy (pm.mins, ent->mins); 1656: VectorCopy (pm.maxs, ent->maxs); 1.1 root 1657: 1.1.1.2 root 1658: client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); 1659: client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); 1660: client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); 1.1 root 1661: 1.1.1.2 root 1662: if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) 1663: { 1664: gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0); 1665: PlayerNoise(ent, ent->s.origin, PNOISE_SELF); 1666: } 1.1 root 1667: 1.1.1.2 root 1668: ent->viewheight = pm.viewheight; 1669: ent->waterlevel = pm.waterlevel; 1670: ent->watertype = pm.watertype; 1671: ent->groundentity = pm.groundentity; 1672: if (pm.groundentity) 1673: ent->groundentity_linkcount = pm.groundentity->linkcount; 1.1 root 1674: 1.1.1.2 root 1675: if (ent->deadflag) 1676: { 1677: client->ps.viewangles[ROLL] = 40; 1678: client->ps.viewangles[PITCH] = -15; 1679: client->ps.viewangles[YAW] = client->killer_yaw; 1680: } 1681: else 1682: { 1683: VectorCopy (pm.viewangles, client->v_angle); 1684: VectorCopy (pm.viewangles, client->ps.viewangles); 1685: } 1.1 root 1686: 1.1.1.2 root 1687: gi.linkentity (ent); 1.1 root 1688: 1.1.1.2 root 1689: if (ent->movetype != MOVETYPE_NOCLIP) 1690: G_TouchTriggers (ent); 1.1 root 1691: 1.1.1.2 root 1692: // touch other objects 1693: for (i=0 ; i<pm.numtouch ; i++) 1694: { 1695: other = pm.touchents[i]; 1696: for (j=0 ; j<i ; j++) 1697: if (pm.touchents[j] == other) 1698: break; 1699: if (j != i) 1700: continue; // duplicated 1701: if (!other->touch) 1702: continue; 1703: other->touch (other, ent, NULL, NULL); 1704: } 1.1 root 1705: 1.1.1.2 root 1706: } 1.1 root 1707: 1708: client->oldbuttons = client->buttons; 1709: client->buttons = ucmd->buttons; 1710: client->latched_buttons |= client->buttons & ~client->oldbuttons; 1711: 1712: // save light level the player is standing on for 1713: // monster sighting AI 1714: ent->light_level = ucmd->lightlevel; 1715: 1716: // fire weapon from final position if needed 1717: if (client->latched_buttons & BUTTON_ATTACK) 1718: { 1.1.1.2 root 1719: if (client->resp.spectator) { 1720: 1721: client->latched_buttons = 0; 1722: 1723: if (client->chase_target) { 1724: client->chase_target = NULL; 1725: client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; 1726: } else 1727: GetChaseTarget(ent); 1728: 1729: } else if (!client->weapon_thunk) { 1.1 root 1730: client->weapon_thunk = true; 1731: Think_Weapon (ent); 1732: } 1733: } 1734: 1.1.1.2 root 1735: if (client->resp.spectator) { 1736: if (ucmd->upmove >= 10) { 1737: if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) { 1738: client->ps.pmove.pm_flags |= PMF_JUMP_HELD; 1739: if (client->chase_target) 1740: ChaseNext(ent); 1741: else 1742: GetChaseTarget(ent); 1743: } 1744: } else 1745: client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD; 1746: } 1.1 root 1747: 1.1.1.2 root 1748: // update chase cam if being followed 1749: for (i = 1; i <= maxclients->value; i++) { 1750: other = g_edicts + i; 1751: if (other->inuse && other->client->chase_target == ent) 1752: UpdateChaseCam(other); 1753: } 1.1 root 1754: } 1755: 1756: 1757: /* 1758: ============== 1759: ClientBeginServerFrame 1760: 1761: This will be called once for each server frame, before running 1762: any other entities in the world. 1763: ============== 1764: */ 1765: void ClientBeginServerFrame (edict_t *ent) 1766: { 1767: gclient_t *client; 1768: int buttonMask; 1769: 1770: if (level.intermissiontime) 1771: return; 1772: 1773: client = ent->client; 1774: 1.1.1.2 root 1775: if (deathmatch->value && 1776: client->pers.spectator != client->resp.spectator && 1777: (level.time - client->respawn_time) >= 5) { 1778: spectator_respawn(ent); 1779: return; 1780: } 1781: 1.1 root 1782: // run weapon animations if it hasn't been done by a ucmd_t 1.1.1.2 root 1783: if (!client->weapon_thunk && !client->resp.spectator) 1.1 root 1784: Think_Weapon (ent); 1785: else 1786: client->weapon_thunk = false; 1787: 1788: if (ent->deadflag) 1789: { 1790: // wait for any button just going down 1791: if ( level.time > client->respawn_time) 1792: { 1793: // in deathmatch, only wait for attack button 1794: if (deathmatch->value) 1795: buttonMask = BUTTON_ATTACK; 1796: else 1797: buttonMask = -1; 1798: 1799: if ( ( client->latched_buttons & buttonMask ) || 1800: (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) ) 1801: { 1802: respawn(ent); 1803: client->latched_buttons = 0; 1804: } 1805: } 1806: return; 1807: } 1808: 1809: // add player trail so monsters can follow 1810: if (!deathmatch->value) 1811: if (!visible (ent, PlayerTrail_LastSpot() ) ) 1812: PlayerTrail_Add (ent->s.old_origin); 1813: 1814: client->latched_buttons = 0; 1815: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.