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