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