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