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