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