#include "g_local.h" #include "m_player.h" #define KICK_DELAY_TIME 2 //server frames (.1 sec each) #define DEFAULT_MOTD_TIME 15 //seconds itll remain #define MOTDMAX 26 void ClientUserinfoChanged (edict_t *ent, char *userinfo); void CopyToBodyQue (edict_t *ent); void SP_misc_teleporter_dest (edict_t *ent); edict_t * MakeNewBody (edict_t *ent); edict_t * ThrowClientHeadNew (edict_t *self); // // Gross, ugly, disgustuing hack section // // this function is an ugly as hell hack to fix some map flaws // // the coop spawn spots on some maps are SNAFU. There are coop spots // with the wrong targetname as well as spots with no name at all // // we use carnal knowledge of the maps to fix the coop spot targetnames to match // that of the nearest named single player spot static void SP_FixCoopSpots (edict_t *self) { edict_t *spot; vec3_t d; spot = NULL; while(1) { spot = G_Find(spot, FOFS(classname), "info_player_start"); if (!spot) return; if (!spot->targetname) continue; VectorSubtract(self->s.origin, spot->s.origin, d); if (VectorLength(d) < 384) { if ((!self->targetname) || Q_stricmp(self->targetname, spot->targetname) != 0) { // gi.dprintf("FixCoopSpots changed %s at %s targetname from %s to %s\n", self->classname, vtos(self->s.origin), self->targetname, spot->targetname); self->targetname = spot->targetname; } return; } } } // now if that one wasn't ugly enough for you then try this one on for size // some maps don't have any coop spots at all, so we need to create them // where they should have been static void SP_CreateCoopSpots (edict_t *self) { edict_t *spot; if(Q_stricmp(level.mapname, "security") == 0) { spot = G_Spawn(); spot->classname = "info_player_coop"; spot->s.origin[0] = 188 - 64; spot->s.origin[1] = -164; spot->s.origin[2] = 80; spot->targetname = "jail3"; spot->s.angles[1] = 90; spot = G_Spawn(); spot->classname = "info_player_coop"; spot->s.origin[0] = 188 + 64; spot->s.origin[1] = -164; spot->s.origin[2] = 80; spot->targetname = "jail3"; spot->s.angles[1] = 90; spot = G_Spawn(); spot->classname = "info_player_coop"; spot->s.origin[0] = 188 + 128; spot->s.origin[1] = -164; spot->s.origin[2] = 80; spot->targetname = "jail3"; spot->s.angles[1] = 90; return; } } //void SP_LensFlare (edict_t *flare) //void SP_SunFlare (edict_t *flare) static void SP_AddSunFlares (edict_t *self) { edict_t *newent; if(Q_stricmp(level.mapname, "base1") == 0) { newent = G_Spawn(); newent->s.origin[0] = -155; newent->s.origin[1] = 877; newent->s.origin[2] = 441; SP_SunFlare (newent); return; } } /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) The normal starting point for a level. */ void SP_info_player_start(edict_t *self) { if (!coop->value && !deathmatch->value) { self->think = SP_AddSunFlares; self->nextthink = level.time + FRAMETIME; return; } if (!coop->value) return; if(Q_stricmp(level.mapname, "security") == 0) { // invoke one of our gross, ugly, disgusting hacks self->think = SP_CreateCoopSpots; self->nextthink = level.time + FRAMETIME; } } /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) potential spawning position for deathmatch games */ void SP_info_player_deathmatch(edict_t *self) { if (!deathmatch->value) { G_FreeEdict (self); return; } SP_misc_spawn_dest (self); } /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32) potential spawning position for coop games */ void SP_info_player_coop(edict_t *self) { if (!coop->value) { G_FreeEdict (self); return; } if((Q_stricmp(level.mapname, "jail2") == 0) || (Q_stricmp(level.mapname, "jail4") == 0) || (Q_stricmp(level.mapname, "mine1") == 0) || (Q_stricmp(level.mapname, "mine2") == 0) || (Q_stricmp(level.mapname, "mine3") == 0) || (Q_stricmp(level.mapname, "mine4") == 0) || (Q_stricmp(level.mapname, "lab") == 0) || (Q_stricmp(level.mapname, "boss1") == 0) || (Q_stricmp(level.mapname, "fact3") == 0) || (Q_stricmp(level.mapname, "biggun") == 0) || (Q_stricmp(level.mapname, "space") == 0) || (Q_stricmp(level.mapname, "command") == 0) || (Q_stricmp(level.mapname, "power2") == 0) || (Q_stricmp(level.mapname, "strike") == 0)) { // invoke one of our gross, ugly, disgusting hacks self->think = SP_FixCoopSpots; self->nextthink = level.time + FRAMETIME; } } /*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32) The deathmatch intermission point will be at one of these Use 'angles' instead of 'angle', so you can set pitch or roll as well as yaw. 'pitch yaw roll' */ void SP_info_player_intermission(void) { } //======================================================================= void player_pain (edict_t *self, edict_t *other, float kick, int damage) { // player pain is handled at the end of the frame in P_DamageFeedback } qboolean IsFemale (edict_t *ent) { char *info; if (!ent->client) return false; info = Info_ValueForKey (ent->client->pers.userinfo, "gender"); if (info[0] == 'f' || info[0] == 'F') return true; return false; } qboolean IsNeutral (edict_t *ent) { char *info; if (!ent->client) return false; info = Info_ValueForKey (ent->client->pers.userinfo, "gender"); if (info[0] != 'f' && info[0] != 'F' && info[0] != 'm' && info[0] != 'M') return true; return false; } void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker) { int mod; char *message; char *message2; qboolean ff, headshot; if (coop->value && attacker->client) meansOfDeath |= MOD_FRIENDLY_FIRE; if (deathmatch->value || coop->value || true) { ff = meansOfDeath & MOD_FRIENDLY_FIRE; mod = meansOfDeath & ~MOD_FRIENDLY_FIRE & ~MOD_HEAD; message = NULL; message2 = ""; /* if (headShot) gi.centerprintf (self, make_green("(HeadshoT)")); else gi.centerprintf (self, make_green("(BodyshoT)"));//*/ switch (mod) { case MOD_SUICIDE: message = "suicides"; break; case MOD_FALLING: message = "cratered"; break; case MOD_CRUSH: message = "was squished"; break; case MOD_WATER: message = "sank like a rock"; break; case MOD_SLIME: message = "melted"; break; case MOD_LAVA: message = "does a back flip into the lava"; break; case MOD_EXPLOSIVE: case MOD_BARREL: message = "blew up"; break; case MOD_FLAME: message = "was torched"; break; case MOD_EXIT: message = "found a way out"; break; case MOD_TARGET_LASER: message = "saw the light"; break; case MOD_DISCHARGE: message = "felt the discharge"; break; case MOD_TARGET_BLASTER: message = "got blasted"; break; case MOD_BOMB: case MOD_SPLASH: case MOD_TRIGGER_HURT: message = "was in the wrong place"; break; } if (attacker == self) { switch (mod) { case MOD_HELD_GRENADE: message = "tried to put the pin back in"; break; case MOD_FIRE: if (IsNeutral(self)) message = "set itself aflame"; else if (IsFemale(self)) message = "set herself aflame"; else message = "set himself aflame"; break; case MOD_HG_SPLASH: case MOD_G_SPLASH: if (IsNeutral(self)) message = "tripped on its own grenade"; else if (IsFemale(self)) message = "tripped on her own grenade"; else message = "tripped on his own grenade"; break; case MOD_R_SPLASH: if (IsNeutral(self)) message = "blew itself up"; else if (IsFemale(self)) message = "blew herself up"; else message = "blew himself up"; break; case MOD_BFG_BLAST: message = "should have used a smaller gun"; break; default: if (IsNeutral(self)) message = "killed itself"; else if (IsFemale(self)) message = "killed herself"; else message = "killed himself"; break; } } if (message) { gi.bprintf (PRINT_MEDIUM, "%s %s.\n", make_white(self->client->pers.netname), message); if (deathmatch->value) self->client->resp.score--; self->enemy = NULL; return; } self->enemy = attacker; if (attacker && (attacker->client || attacker->svflags & SVF_MONSTER)) { if ( (headShot) && (mod==MOD_BLASTER2 ||mod==MOD_SHOTGUN2 || mod==MOD_SSHOTGUN2 ||mod==MOD_PUNCH || mod==MOD_CHAINGUN2 ||mod==MOD_ROCKET2 || mod==MOD_BLASTER ||mod==MOD_SHOTGUN || mod==MOD_SSHOTGUN ||mod==MOD_MACHINEGUN || mod==MOD_CHAINGUN ||mod==MOD_ROCKET || mod==MOD_FIRE ||mod==MOD_HYPERBLASTER || mod==MOD_RAILGUN ||mod==MOD_GRAPPLE || mod==MOD_KICK ||mod==MOD_MAGIC || mod==MOD_SMACK )) { switch (mod) { case MOD_BLASTER: message = "'s head was split open by"; message2 = "'s .50 Calibre Handgun"; break; case MOD_SHOTGUN: message = "was shot in the head by"; message2 = "'s AutoShotgun"; break; case MOD_SSHOTGUN: message = "was shot in the head by"; message2 = "'s 12 Gauge Shotgun"; break; case MOD_MACHINEGUN: message = "was shot in the head by"; message2 = "'s SubMachinegun"; break; case MOD_CHAINGUN: message = "'s head was mutilated by"; message2 = "'s Chaingun"; break; case MOD_ROCKET: message = "had their head split open courtesy of"; message2 = "'s Rocket"; break; case MOD_BLASTER2: message = "'s head was split open by"; message2 = "'s Laser Blaster"; break; case MOD_SHOTGUN2: message = "was shot in the head by"; message2 = "'s Autocannon"; break; case MOD_SSHOTGUN2: message = "was shot in the head by"; message2 = "'s .50 Calibre HandCannon"; break; case MOD_CHAINGUN2: message = "'s head was mutilated by"; message2 = "'s Heavy AutoShotgun"; break; case MOD_ROCKET2: message = "had their head split open courtesy of"; message2 = "'s Indirect Fire Rocket"; break; case MOD_HYPERBLASTER: message = "'s head was melted by"; message2 = "'s Flamethrower"; break; case MOD_RAILGUN: message = "took"; message2 =(IsNeutral(self))? "'s Uranium shell through its head": (IsFemale(self))? "'s Uranium shell through her head": "'s Uranium shell through his head"; break; case MOD_GRAPPLE: message = "'s head was torn open by"; message2 = "'s grappling hook"; break; case MOD_FIRE: message = "'s head was charred beyond recognition by"; message2 = "'s pyrotechnics"; break; case MOD_KICK: message = "'s head was smashed open by"; message2 = "'s foot"; break; case MOD_PUNCH: message = "'s head was smashed open by"; message2 = "'s iron fist"; break; case MOD_MAGIC: message = "'s head was ripped apart by"; message2 = (attacker->client)? "'s Magic(tm)" : "'s Turret"; break; case MOD_SMACK: message = "'s head smashed open by"; message2 = "'s Pistol"; break; default: break; } } else { switch (mod) { case MOD_HIT: message = "was beat to death by"; break; case MOD_BLASTER: message = "was shot down by"; message2 = "'s .50 caliber handgun"; break; case MOD_SHOTGUN: message = "was gunned down by"; break; case MOD_SSHOTGUN: message = "was filled with lead by"; message2 = "'s 12 Gauge shotgun"; break; case MOD_MACHINEGUN: message = "was machinegunned by"; break; case MOD_CHAINGUN: message = "was torn apart by"; message2 = "'s chaingun"; break; case MOD_GRENADE: message = "was popped by"; message2 = "'s grenade"; break; case MOD_G_SPLASH: message = "was shredded by"; message2 = "'s shrapnel"; break; case MOD_ROCKET: message = "ate"; message2 = "'s rocket"; break; case MOD_BLASTER2: message = "was shot down by"; message2 = "'s flare-cased Shell"; break; case MOD_SHOTGUN2: message = "was blown away by"; break; case MOD_SSHOTGUN2: message = "was filled with lead by"; message2 = "'s .50 Calibre HandCannon"; break; case MOD_CHAINGUN2: message = "was torn apart by"; message2 = "'s Heavy AutoShotgun"; break; case MOD_ROCKET2: message = "ate"; message2 = "'s Indirect-fire Rocket"; break; case MOD_R_SPLASH: message = "tried to dodge"; message2 = "'s rocket"; break; case MOD_HYPERBLASTER: message = "was torched by"; break; case MOD_RAILGUN: message = "took"; message2 =(IsNeutral(self))? "'s Uranium shell through its body": (IsFemale(self))? "'s Uranium shell through her body": "'s Uranium shell through his body"; break; case MOD_BFG_LASER: message = "was melted by"; message2 = "'s Heavy Laser"; break; case MOD_BFG_BLAST: message = "was disintegrated by"; message2 = "'s BFG blast"; break; case MOD_BFG_EFFECT: message = "couldn't hide from"; message2 = "'s BFG"; break; case MOD_ROCKET_BFG: message = "caught"; message2 = "'s BFG Rocket"; break; case MOD_R_BFG_SPLASH: message = "couldn't hide from"; message2 = "'s BFG Rocket"; break; case MOD_HANDGRENADE: message = "caught"; message2 = "'s handgrenade"; break; case MOD_HG_SPLASH: message = "didn't see"; message2 = "'s handgrenade"; break; case MOD_GRAPPLE: message = "was torn apart by"; message2 = "'s grappling hook"; break; case MOD_C4: message = "was liquidated by"; message2 = "'s slab of c4"; break; case MOD_HELD_GRENADE: message = "was grenaded by"; message2 = "'s grenade"; break; case MOD_GREN_HIT: message = "was smacked hard by"; message2 = "'s grenade"; break; case MOD_FIRE: message = "was incinerated by"; break; case MOD_KICK: message = " had a dose of"; message2 = "'s lethal roundhouse"; break; case MOD_PUNCH: message = " was beat down by"; break; case MOD_MAGIC: message = "was killed by"; message2 = (attacker->client)? "'s Magic(tm)" : "'s Turret"; break; case MOD_GASGREN: message = "was suffocated by"; message2 = "'s Mustard Gas"; break; case MOD_SMACK: message = "was beat down by"; message2 = "'s Pistol"; break; case MOD_GAS_BOOM: message = "went to hell courtesy of"; break; case MOD_TAZER: message = "was electrocuted by"; break; case MOD_TELEFRAG: message = "was telefragged by"; //message = "tried to invade"; //message2 = "'s personal space"; break; default: break; } } if (message) { gi.bprintf (PRINT_MEDIUM,"%s %s %s%s\n", make_green(self->client->pers.netname), message, make_white( (attacker->client)? attacker->client->pers.netname : (!Q_stricmp (attacker->classname, "monster_berserk"))? "a Berserker" : (!Q_stricmp (attacker->classname, "monster_gladiator"))? "a Gladiator" : (!Q_stricmp (attacker->classname, "monster_gunner"))? "a Pyro Gunner" : (!Q_stricmp (attacker->classname, "monster_infantry"))? "an Elite Infantryman" : (!Q_stricmp (attacker->classname, "monster_soldier_light"))? "a Light Soldier" : (!Q_stricmp (attacker->classname, "monster_soldier"))? "a Soldier" : (!Q_stricmp (attacker->classname, "monster_soldier_ss"))? "an Elite Soldier" : (!Q_stricmp (attacker->classname, "turret_driver"))? "a Gunner Infantryman" : (!Q_stricmp (attacker->classname, "monster_medic"))? "a Medic" : (!Q_stricmp (attacker->classname, "monster_chick"))? "a Strogg Cyber-Female" : (!Q_stricmp (attacker->classname, "monster_brain"))? "a Guardian" : (!Q_stricmp (attacker->classname, "monster_mutant"))? "a Mutant" : (!Q_stricmp (attacker->classname, "monster_flyer"))? "a Sentinel" : (!Q_stricmp (attacker->classname, "monster_floater"))? "a Brain" : (!Q_stricmp (attacker->classname, "monster_flipper"))? "a Pirhanna" : (!Q_stricmp (attacker->classname, "monster_hover"))? "an Air Gunner" : (!Q_stricmp (attacker->classname, "monster_parasite"))? "a Parasite" : (!Q_stricmp (attacker->classname, "monster_tank"))? "a Ground Tank" : (!Q_stricmp (attacker->classname, "monster_tank_commander"))? "a Tank Commander" : (!Q_stricmp (attacker->classname, "monster_supertank"))? "a Super Ground Tank" : (!Q_stricmp (attacker->classname, "monster_boss2"))? "an Air Tank" : (!Q_stricmp (attacker->classname, "monster_boss3_stand"))? "a Strogg Makron" : (!Q_stricmp (attacker->classname, "monster_makron"))? "a Strogg Makron" : (!Q_stricmp (attacker->classname, "monster_jorg"))? "a Strogg Jorg" : "an enemy"), message2); if (attacker->client) { if (ff) attacker->client->resp.score--; else { attacker->client->resp.score++; if (headShot) attacker->client->resp.headshots++; } } return; } } } if (self) if (self->client) make_white(self->client->pers.netname); if (attacker) if (attacker->client) make_white(attacker->client->pers.netname); gi.bprintf (PRINT_MEDIUM,"%s died.\n", make_white(self->client->pers.netname)); if (deathmatch->value) self->client->resp.score--; } void Touch_Item (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); void TossClientWeapon (edict_t *self) { gitem_t *item; edict_t *drop; qboolean quad; float spread; if (!deathmatch->value) return; item = self->client->pers.weapon; if (! self->client->pers.inventory[self->client->ammo_index] ) item = NULL; if (item && (strcmp (item->pickup_name, "Blaster") == 0)) item = NULL; if (!((int)(dmflags->value) & DF_QUAD_DROP)) quad = false; else quad = (self->client->quad_framenum > (level.framenum + 10)); if (item && quad) spread = 22.5; else spread = 0.0; if (item) { self->client->v_angle[YAW] -= spread; drop = Drop_Item (self, item); self->client->v_angle[YAW] += spread; drop->spawnflags = DROPPED_PLAYER_ITEM; } if (quad) { self->client->v_angle[YAW] += spread; drop = Drop_Item (self, FindItemByClassname ("item_quad")); self->client->v_angle[YAW] -= spread; drop->spawnflags |= DROPPED_PLAYER_ITEM; drop->touch = Touch_Item; drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME; drop->think = G_FreeEdict; } } /* ================== LookAtKiller ================== */ void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker) { vec3_t dir; if (attacker && attacker != world && attacker != self) { VectorSubtract (attacker->s.origin, self->s.origin, dir); } else if (inflictor && inflictor != world && inflictor != self) { VectorSubtract (inflictor->s.origin, self->s.origin, dir); } else { self->client->killer_yaw = self->s.angles[YAW]; return; } if (dir[0]) self->client->killer_yaw = 180/M_PI*atan2(dir[1], dir[0]); else { self->client->killer_yaw = 0; if (dir[1] > 0) self->client->killer_yaw = 90; else if (dir[1] < 0) self->client->killer_yaw = -90; } if (self->client->killer_yaw < 0) self->client->killer_yaw += 360; } /* ================== player_die ================== */ #define MOD_GRENADE 6 #define MOD_G_SPLASH 7 #define MOD_ROCKET 8 #define MOD_R_SPLASH 9 #define MOD_BFG_LASER 12 #define MOD_BFG_BLAST 13 #define MOD_HANDGRENADE 15 #define MOD_HG_SPLASH 16 #define MOD_LAVA 19 #define MOD_EXPLOSIVE 25 #define MOD_BARREL 26 #define MOD_BOMB 27 #define MOD_C4 35 #define MOD_GREN_HIT 36 #define MOD_FIRE 37 #define MOD_ROCKET_BFG 39 #define MOD_R_BFG_SPLASH 40 #define MOD_MAGIC 41 #define MOD_GRENADE2 47 #define MOD_G_SPLASH2 48 #define MOD_ROCKET2 49 #define MOD_FLAME 55 #define MOD_GAS_BOOM 56 #define MOD_DISCHARGE 58 void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { int mod = meansOfDeath & ~MOD_FRIENDLY_FIRE; edict_t *ent, *newBody; int n, j; switch (mod) { case MOD_ROCKET: case MOD_BFG_LASER: case MOD_BFG_BLAST: case MOD_HANDGRENADE: case MOD_HELD_GRENADE: case MOD_BOMB: case MOD_C4: case MOD_GAS_BOOM: self->health = -500; break; case MOD_FLAME: case MOD_DISCHARGE: case MOD_LAVA: self->health -= damage * 2; break; case MOD_HG_SPLASH: case MOD_R_SPLASH: case MOD_G_SPLASH: self->health -= damage * 5; break; case MOD_R_BFG_SPLASH: self->health -= damage * 10; break; } VectorClear (self->avelocity); self->takedamage = DAMAGE_YES; self->movetype = MOVETYPE_TOSS; self->s.modelindex2 = 0; // remove linked weapon model self->client->flashlight_on = 0; //turn off flashlight self->client->laser_on = 0; //turn off laser sight self->client->aquasuit = 0; //take off aqua suit self->client->goggles = 0; //take off goggles self->timer = 0; //take off goggles self->flaming = 0; self->client->jets = 0; //SET WEAP MODES self->client->gren_set = 0; self->client->rock_set = 0; self->client->chan_set = 0; self->client->shot_set = 0; self->client->mach_set = 0; self->client->blst_set = 0; self->client->hypr_set = 0; self->client->bfg_set = 0; self->client->rail_set = 0; //SET WEAP MODES self->client->ps.rdflags &= ~RDF_IRGOGGLES; self->client->climbing = 0; self->flashbanged = 0; self->client->isOnTurret = 0; self->client->weaphold = 0; //flag for laser sight self->s.angles[0] = 0; self->s.angles[2] = 0; self->s.sound = 0; self->client->weapon_sound = 0; self->maxs[2] = -8; self->floater = 1; // self->solid = SOLID_NOT; self->svflags |= SVF_DEADMONSTER; if (self->client->kami==666) { self->s.effects &= ~EF_TELEPORTER; self->s.renderfx &= ~RF_SHELL_DOUBLE; self->client->kami=0; self->health = -555; T_RadiusDamage(self, self, 2500, self, 400, MOD_C4); if (sv_serversideonly->value) T_FlashRadius(self, self, 2500, self, 400); target_earthquake_think (self); bigExplosion(self->s.origin, vec3_origin, 2); if (sv_serversideonly->value) ExplodeMark (self, self->s.origin, level.time + 5 + random()*2 ); } if (!self->deadflag) { self->client->respawn_time = level.time + 1.0; LookAtKiller (self, inflictor, attacker); self->client->ps.pmove.pm_type = PM_DEAD; ClientObituary (self, inflictor, attacker); TossClientWeapon (self); if (deathmatch->value) Cmd_Help_f (self); // show scores // clear inventory // this is kind of ugly, but it's how we want to handle keys in coop for (n = 0; n < game.num_items; n++) { if (coop->value && itemlist[n].flags & IT_KEY) self->client->resp.coop_respawn.inventory[n] = self->client->pers.inventory[n]; self->client->pers.inventory[n] = 0; } } //remove linked ents for (n= 0; n < self->linked_ents_num; n++) { if (self->linked_ents[n]) { self->linked_ents[n]->movetype = MOVETYPE_TOSS; self->linked_ents[n]->linkedto=NULL; VectorClear(self->linked_ents[n]->velocity); } } self->linked_ents_num = 0; // remove powerups self->client->quad_framenum = 0; self->client->invincible_framenum = 0; self->client->breather_framenum = 0; self->client->enviro_framenum = 0; self->flags &= ~FL_POWER_ARMOR; if (self->health <= -500) { // gib gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); for (n= 0; n < 2; n++) ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); for (n= 0; n < 4; n++) ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC); //ThrowClientHead (self, damage); newBody = ThrowClientHeadNew (self); self->takedamage = DAMAGE_NO; } else { // normal death if (!self->deadflag) { static int i; i = (i+1)%3; // start a death animation self->client->anim_priority = ANIM_DEATH; if (self->client->ps.pmove.pm_flags & PMF_DUCKED) { self->s.frame = FRAME_crdeath1; self->client->anim_end = FRAME_crdeath5; } else switch (i) { case 0: self->s.frame = FRAME_death101; self->client->anim_end = FRAME_death106; break; case 1: self->s.frame = FRAME_death201; self->client->anim_end = FRAME_death206; break; case 2: self->s.frame = FRAME_death301; self->client->anim_end = FRAME_death308; break; } gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0); } newBody=MakeNewBody (self); } self->deadflag = DEAD_DEAD; if (!deathmatch->value||self->viewcam_on ||true) { if (attacker!=self&&(attacker->client || (attacker->svflags & SVF_MONSTER))&&self->linked_flame!=attacker) { self->killer = attacker; self->solid = SOLID_NOT; self->movetype = MOVETYPE_NOCLIP; } else { self->killer = newBody; self->solid = SOLID_NOT; self->movetype = MOVETYPE_NOCLIP; } self->viewcam_on = 2; } if(self->linked_flame) { self->linked_flame->flamed = newBody; self->linked_flame=NULL; } for (j = 1; j <= game.maxentities; j++) { ent = &g_edicts[j]; if (ent->linkedto==self) { ent->linkedto = NULL; ent->movetype = MOVETYPE_TOSS; } if (newBody) { if (ent->killer==self) ent->killer=newBody; if (ent->enemy==self) ent->enemy=newBody; } } self->client->viewcam_dist = 0; CleanUpEnt (self); gi.linkentity (self); } //======================================================================= /* ============== InitClientPersistant This is only called when the game first initializes in single player, but is called after each death and level change in deathmatch ============== */ void InitClientPersistant (gclient_t *client) { gitem_t *item; int i; memset (&client->pers, 0, sizeof(client->pers)); //* //***My Section Start*** //* for (i=0;i>50+(random()*20)+(random()*20);i++) { if (random()>.9) i--; } if (sv_waterlevel->value) { item = FindItem("Flashlight"); client->pers.inventory[ITEM_INDEX(item)] = 1; item = FindItem("Laser Sight"); client->pers.inventory[ITEM_INDEX(item)] = 1; } else if (deathmatch->value) { item = FindItem("Flashlight"); client->pers.inventory[ITEM_INDEX(item)] = 1; if (sv_grapple->value) { item = FindItem("Grapple"); client->pers.inventory[ITEM_INDEX(item)] = 1; } if (random()<0.2 && (int)sv_banned_weapons->value!=-1) { item = FindItem("Laser Sight"); client->pers.inventory[ITEM_INDEX(item)] = 1; } else if (crandom()<0.2) { item = FindItem("Diving Mask"); client->pers.inventory[ITEM_INDEX(item)] = 1; } else if (crandom()<0.2) { item = FindItem("Jet Propulsion Unit"); client->pers.inventory[ITEM_INDEX(item)] = 1; } else if ((int)sv_banned_weapons->value!=-1) { item = FindItem("Regen-Stealth Suit"); client->pers.inventory[ITEM_INDEX(item)] = 1; } else { item = FindItem("Jet Propulsion Unit"); client->pers.inventory[ITEM_INDEX(item)] = 1; } } else if (coop->value) { if (sv_grapple->value) { item = FindItem("Grapple"); client->pers.inventory[ITEM_INDEX(item)] = 1; } } //***My Section End***/ if ((int)sv_banned_weapons->value!=-1) { int banned = sv_banned_weapons->value; if (!(banned&NO_BLASTER)) { item = FindItem("Bullets"); client->pers.inventory[ITEM_INDEX(item)] = 100; item = FindItem("Blaster"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; } else if (!(banned&NO_SHOTGUN)) { item = FindItem("Shells"); client->pers.inventory[ITEM_INDEX(item)] = 100; item = FindItem("Shotgun"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; } else if (!(banned&NO_SSHOTGUN)) { item = FindItem("Shells"); client->pers.inventory[ITEM_INDEX(item)] = 100; item = FindItem("Super Shotgun"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; } else if (!(banned&NO_MACHINEGUN)) { item = FindItem("Bullets"); client->pers.inventory[ITEM_INDEX(item)] = 100; item = FindItem("Mashinegun"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; } else if (!(banned&NO_CHAINGUN)) { item = FindItem("Shells"); client->pers.inventory[ITEM_INDEX(item)] = 100; item = FindItem("Chaingun"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; } else if (!(banned&NO_GRENADE_LAUNCHER)) { item = FindItem("grenades"); client->pers.inventory[ITEM_INDEX(item)] = 20; item = FindItem("Grenade Launcher"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; } else if (!(banned&NO_ROCKET_LAUNCHER)) { item = FindItem("rockets"); client->pers.inventory[ITEM_INDEX(item)] = 15; item = FindItem("Rocket Launcher"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; } else if (!(banned&NO_HYPERBLASTER)) { item = FindItem("Cells"); client->pers.inventory[ITEM_INDEX(item)] = 100; item = FindItem("HyperBlaster"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; } else if (!(banned&NO_RAILGUN) && !(banned&NO_TAZER)) { if (!(banned&NO_RAILGUN)) { item = FindItem("Slugs"); client->pers.inventory[ITEM_INDEX(item)] = 10; } if (!(banned&NO_TAZER)) { item = FindItem("Cells"); client->pers.inventory[ITEM_INDEX(item)] = 75; } item = FindItem("Railgun"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; } else if (!(banned&NO_ROCKETBFG) && !(banned&NO_BFGLASER)) { if (!(banned&NO_ROCKETBFG)) { item = FindItem("Rockets"); client->pers.inventory[ITEM_INDEX(item)] = 100; } if (!(banned&NO_BFGLASER)) { item = FindItem("Cells"); client->pers.inventory[ITEM_INDEX(item)] = 100; } item = FindItem("BFG10K"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; } else if (!(banned&NO_GRENADES)) { item = FindItem("Grenades"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 10; } base_weapon = item->pickup_name; } client->pers.weapon = item; client->pers.health = 100; client->pers.max_health = 100; client->pers.max_bullets = 200; client->pers.max_shells = 200; client->pers.max_rockets = 100; client->pers.max_grenades = 25; client->pers.max_cells = 100; client->pers.max_slugs = 10; client->pers.connected = true; } void InitClientResp (gclient_t *client) { memset (&client->resp, 0, sizeof(client->resp)); client->resp.enterframe = level.framenum; client->resp.coop_respawn = client->pers; } /* ================== SaveClientData Some information that should be persistant, like health, is still stored in the edict structure, so it needs to be mirrored out to the client structure before all the edicts are wiped. ================== */ void SaveClientData (void) { int i; edict_t *ent; for (i=0 ; iinuse) continue; game.clients[i].pers.chasetoggle = ent->client->chasetoggle; game.clients[i].pers.health = ent->health; game.clients[i].pers.max_health = ent->max_health; game.clients[i].pers.savedFlags = (ent->flags & (FL_GODMODE|FL_NOTARGET|FL_POWER_ARMOR)); game.clients[i].pers.grappleType = ent->grappleType; game.clients[i].pers.bfg_laser_type = ent->bfg_laser_type; game.clients[i].pers.Player_ID = ent->Player_ID; game.clients[i].pers.TeamName = ent->TeamName; game.clients[i].pers.viewcam_on = ent->viewcam_on; if (coop->value) { game.clients[i].pers.score = ent->client->resp.score; game.clients[i].pers.headshots = ent->client->resp.headshots; } } } void FetchClientEntData (edict_t *ent) { ent->health = ent->client->pers.health; ent->max_health = ent->client->pers.max_health; ent->flags |= ent->client->pers.savedFlags; if (!deathmatch->value&&!coop->value) { ent->grappleType = ent->client->pers.grappleType; ent->bfg_laser_type = ent->client->pers.bfg_laser_type; ent->Player_ID = ent->client->pers.Player_ID; ent->TeamName = ent->client->pers.TeamName; ent->viewcam_on = ent->client->pers.viewcam_on; } if (coop->value) { ent->client->resp.score = ent->client->pers.score; ent->client->resp.headshots = ent->client->pers.headshots; } } /* ======================================================================= SelectSpawnPoint ======================================================================= */ /* ================ PlayersRangeFromSpot Returns the distance to the nearest player from the given spot ================ */ float PlayersRangeFromSpot (edict_t *spot) { edict_t *player; float bestplayerdistance; vec3_t v; int n; float playerdistance; bestplayerdistance = 9999999; for (n = 1; n <= maxclients->value; n++) { player = &g_edicts[n]; if (!player->inuse) continue; if (player->health <= 0) continue; VectorSubtract (spot->s.origin, player->s.origin, v); playerdistance = VectorLength (v); if (playerdistance < bestplayerdistance) bestplayerdistance = playerdistance; } return bestplayerdistance; } /* ================ SelectRandomDeathmatchSpawnPoint go to a random point, but NOT the two points closest to other players ================ */ edict_t *SelectRandomDeathmatchSpawnPoint (void) { edict_t *spot, *spot1, *spot2; int count = 0; int selection; float range, range1, range2; spot = NULL; range1 = range2 = 99999; spot1 = spot2 = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { count++; range = PlayersRangeFromSpot(spot); if (range < range1) { range1 = range; spot1 = spot; } else if (range < range2) { range2 = range; spot2 = spot; } } if (!count) return NULL; if (count <= 2) { spot1 = spot2 = NULL; } else count -= 2; selection = rand() % count; spot = NULL; do { spot = G_Find (spot, FOFS(classname), "info_player_deathmatch"); if (spot == spot1 || spot == spot2) selection++; } while(selection--); return spot; } /* ================ SelectFarthestDeathmatchSpawnPoint ================ */ edict_t *SelectFarthestDeathmatchSpawnPoint (void) { edict_t *bestspot; float bestdistance, bestplayerdistance; edict_t *spot; spot = NULL; bestspot = NULL; bestdistance = 0; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { bestplayerdistance = PlayersRangeFromSpot (spot); if (bestplayerdistance > bestdistance) { bestspot = spot; bestdistance = bestplayerdistance; } } if (bestspot) { return bestspot; } // if there is a player just spawned on each and every start spot // we have no choice to turn one into a telefrag meltdown spot = G_Find (NULL, FOFS(classname), "info_player_deathmatch"); return spot; } edict_t *SelectDeathmatchSpawnPoint (void) { if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST) return SelectFarthestDeathmatchSpawnPoint (); else return SelectRandomDeathmatchSpawnPoint (); } edict_t *SelectCoopSpawnPoint (edict_t *ent) { int index; edict_t *spot = NULL; char *target; index = ent->client - game.clients; // player 0 starts in normal player spawn point if (!index) return NULL; spot = NULL; // assume there are four coop spots at each spawnpoint while (1) { spot = G_Find (spot, FOFS(classname), "info_player_coop"); if (!spot) return NULL; // we didn't have enough... target = spot->targetname; if (!target) target = ""; if ( Q_stricmp(game.spawnpoint, target) == 0 ) { // this is a coop spawn point for one of the clients here index--; if (!index) return spot; // this is it } } return spot; } /* =========== SelectSpawnPoint Chooses a player start, deathmatch start, coop start, etc ============ */ void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles) { edict_t *spot = NULL; if (deathmatch->value) spot = SelectDeathmatchSpawnPoint (); else if (coop->value) spot = SelectCoopSpawnPoint (ent); // find a single player start spot if (!spot) { while ((spot = G_Find (spot, FOFS(classname), "info_player_start")) != NULL) { if (!game.spawnpoint[0] && !spot->targetname) break; if (!game.spawnpoint[0] || !spot->targetname) continue; if (Q_stricmp(game.spawnpoint, spot->targetname) == 0) break; } if (!spot) { if (!game.spawnpoint[0]) { // there wasn't a spawnpoint without a target, so use any spot = G_Find (spot, FOFS(classname), "info_player_start"); } if (!spot) gi.error ("Couldn't find spawn point %s\n", game.spawnpoint); } } VectorCopy (spot->s.origin, origin); origin[2] += 9; VectorCopy (spot->s.angles, angles); } //====================================================================== void InitBodyQue (void) { int i; edict_t *ent; level.body_que = 0; for (i=0; iclassname = "bodyque"; } } void body_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { int n; int dmg = 0; if (self->health < -500) { gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0); for (n= 0; n < 2; n++) ThrowGib (self, "models/objects/gibs/bone/tris.md2", dmg, GIB_ORGANIC); for (n= 0; n < 4; n++) ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", dmg, GIB_ORGANIC); ThrowGib (self, "models/objects/gibs/chest/tris.md2", dmg, GIB_ORGANIC); self->takedamage = DAMAGE_NO; ThrowClientHeadNew (self); G_FreeEdict(self); return; } } static void body_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { if (!other->takedamage) { if (!(surf && (surf->flags & SURF_SKY))) gi.sound (ent, CHAN_VOICE, gi.soundindex ("world/land.wav"), 0.5, ATTN_NORM, 0); } } void body_think (edict_t *ent) { if (ent->s.frame==FRAME_crdeath5 || ent->s.frame==FRAME_death106 || ent->s.frame==FRAME_death206 || ent->s.frame==FRAME_death308) { ent->think=FadeDieSink; ent->nextthink=level.time+FADETIME+random()*5; return; } if (ent->s.frame>=135 && ent->s.frame<=172) ent->s.frame=FRAME_crdeath1; else if (ent->s.frame<135) { if ((int)(crandom()*3)==1) ent->s.frame=FRAME_death101; else if ((int)(crandom()*3)==1) ent->s.frame=FRAME_death201; else ent->s.frame=FRAME_death301; } else if ((ent->s.frame>=FRAME_crdeath1 && ent->s.frame<=FRAME_crdeath4)|| (ent->s.frame>=FRAME_death101 && ent->s.frame<=FRAME_death105)|| (ent->s.frame>=FRAME_death201 && ent->s.frame<=FRAME_death205)|| (ent->s.frame>=FRAME_death301 && ent->s.frame<=FRAME_death307)) { ent->s.frame++; } ent->nextthink=level.time+FRAMETIME; } edict_t * MakeNewBody (edict_t *ent) { edict_t *body; body=G_Spawn(); body->s = ent->s; body->s.number = body - g_edicts; VectorCopy (ent->mins, body->mins); VectorCopy (ent->maxs, body->maxs); VectorCopy (ent->absmin, body->absmin); VectorCopy (ent->absmax, body->absmax); VectorCopy (ent->size, body->size); VectorCopy (ent->velocity, body->velocity); body->solid = ent->solid; body->clipmask = ent->clipmask; body->owner = ent->owner; body->movetype = ent->movetype; body->svflags = SVF_DEADMONSTER; body->die = body_die; body->takedamage = DAMAGE_YES; body->movetype = MOVETYPE_TOSS; body->touch = body_touch; body->think=body_think; body->nextthink=level.time+FRAMETIME; body->floater=1; body->s.renderfx |= RF_IR_VISIBLE; if (ent->client) body->PlayerDeadName = ent->client->pers.netname; gi.linkentity (body); return body; } void CopyToBodyQue (edict_t *ent) { edict_t *body; // grab a body que and cycle to the next one body = &g_edicts[(int)maxclients->value + level.body_que + 1]; level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE; // FIXME: send an effect on the removed body gi.unlinkentity (ent); gi.unlinkentity (body); body->s = ent->s; body->s.number = body - g_edicts; body->svflags = ent->svflags; VectorCopy (ent->mins, body->mins); VectorCopy (ent->maxs, body->maxs); VectorCopy (ent->absmin, body->absmin); VectorCopy (ent->absmax, body->absmax); VectorCopy (ent->size, body->size); body->solid = ent->solid; body->clipmask = ent->clipmask; body->owner = ent->owner; body->movetype = ent->movetype; body->die = body_die; body->takedamage = DAMAGE_YES; body->think=FadeDieSink; body->nextthink=level.time+FADETIME+random()*5; body->floater=1; body->s.renderfx |= RF_IR_VISIBLE; gi.linkentity (body); } void respawn (edict_t *self) { if (deathmatch->value || coop->value) { // spectator's don't leave bodies // if (self->movetype != MOVETYPE_NOCLIP) // CopyToBodyQue (self); self->svflags &= ~SVF_NOCLIENT; PutClientInServer (self); // add a teleportation effect self->s.event = EV_PLAYER_TELEPORT; // hold in place briefly self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; self->client->ps.pmove.pm_time = 1.1; self->client->respawn_time = level.time; return; } // restart the entire server gi.AddCommandString ("menu_loadgame\n"); } /* * only called when pers.spectator changes * note that resp.spectator should be the opposite of pers.spectator here */ void spectator_respawn (edict_t *ent) { int i, numspec; // if the user wants to become a spectator, make sure he doesn't // exceed max_spectators if (ent->client->pers.spectator) { char *value = Info_ValueForKey (ent->client->pers.userinfo, "spectator"); if (*spectator_password->string && strcmp(spectator_password->string, "none") && strcmp(spectator_password->string, value)) { gi.cprintf(ent, PRINT_HIGH, "Spectator password incorrect.\n"); ent->client->pers.spectator = false; gi.WriteByte (svc_stufftext); gi.WriteString ("spectator 0\n"); gi.unicast(ent, true); return; } // count spectators for (i = 1, numspec = 0; i <= maxclients->value; i++) if (g_edicts[i].inuse && g_edicts[i].client->pers.spectator) numspec++; if (numspec >= maxspectators->value) { gi.cprintf(ent, PRINT_HIGH, "Server spectator limit is full."); ent->client->pers.spectator = false; // reset his spectator var gi.WriteByte (svc_stufftext); gi.WriteString ("spectator 0\n"); gi.unicast(ent, true); return; } } else { // he was a spectator and wants to join the game // he must have the right password char *value = Info_ValueForKey (ent->client->pers.userinfo, "password"); if (*password->string && strcmp(password->string, "none") && strcmp(password->string, value)) { gi.cprintf(ent, PRINT_HIGH, "Password incorrect.\n"); ent->client->pers.spectator = true; gi.WriteByte (svc_stufftext); gi.WriteString ("spectator 1\n"); gi.unicast(ent, true); return; } } // clear client on respawn ent->client->resp.score = ent->client->pers.score = 0; ent->client->resp.headshots = ent->client->pers.headshots = 0; ent->svflags &= ~SVF_NOCLIENT; PutClientInServer (ent); // add a teleportation effect if (!ent->client->pers.spectator) { // send effect gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGIN); gi.multicast (ent->s.origin, MULTICAST_PVS); // hold in place briefly ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; ent->client->ps.pmove.pm_time = 14; } ent->client->respawn_time = level.time; if (ent->client->pers.spectator) gi.bprintf (PRINT_HIGH, "%s has moved to the sidelines\n", ent->client->pers.netname); else gi.bprintf (PRINT_HIGH, "%s joined the game\n", ent->client->pers.netname); } //============================================================== /* =========== PutClientInServer Called when a player connects to a server or respawns in a deathmatch. ============ */ void PutClientInServer (edict_t *ent) { vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; client_persistant_t saved; client_respawn_t resp; gitem_t *item; int chasetoggle; char userinfo[MAX_INFO_STRING]; // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client SelectSpawnPoint (ent, spawn_origin, spawn_angles); index = ent-g_edicts-1; client = ent->client; chasetoggle = client->pers.chasetoggle; // deathmatch wipes most client data every spawn if (deathmatch->value) { char userinfo[MAX_INFO_STRING]; resp = client->resp; memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); InitClientPersistant (client); ClientUserinfoChanged (ent, userinfo); } else if (coop->value) { // int n; char userinfo[MAX_INFO_STRING]; resp = client->resp; memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); // this is kind of ugly, but it's how we want to handle keys in coop // for (n = 0; n < game.num_items; n++) // { // if (itemlist[n].flags & IT_KEY) // resp.coop_respawn.inventory[n] = client->pers.inventory[n]; // } resp.coop_respawn.game_helpchanged = client->pers.game_helpchanged; resp.coop_respawn.helpchanged = client->pers.helpchanged; client->pers = resp.coop_respawn; ClientUserinfoChanged (ent, userinfo); if (resp.score > client->pers.score) client->pers.score = resp.score; if (resp.headshots > client->pers.headshots) client->pers.headshots = resp.headshots; } else { memset (&resp, 0, sizeof(resp)); memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); ClientUserinfoChanged (ent, userinfo); } // clear everything but the persistant data saved = client->pers; memset (client, 0, sizeof(*client)); client->pers = saved; if (client->pers.health <= 0) InitClientPersistant(client); client->resp = resp; client->pers.chasetoggle = chasetoggle; if (coop->value) { client->pers.health = 100; ent->health = 100; } // copy some data from the client to the entity FetchClientEntData (ent); ent->linked_flame=NULL; // clear entity values ent->groundentity = NULL; ent->client = &game.clients[index]; ent->takedamage = DAMAGE_AIM; ent->movetype = MOVETYPE_WALK; ent->viewheight = 22; ent->inuse = true; ent->classname = "player"; ent->mass = 200; ent->solid = SOLID_BBOX; ent->deadflag = DEAD_NO; ent->air_finished = level.time + 12; ent->clipmask = MASK_PLAYERSOLID; ent->model = "players/male/tris.md2"; ent->pain = player_pain; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags &= ~FL_NO_KNOCKBACK; ent->svflags &= ~SVF_DEADMONSTER; ent->svflags &= ~SVF_NOCLIENT; //Turn off prediction ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; ent->killer = NULL; ent->flashbanged = 0; ent->floater = 1; VectorCopy (mins, ent->mins); VectorCopy (maxs, ent->maxs); VectorClear (ent->velocity); // clear playerstate values memset (&ent->client->ps, 0, sizeof(client->ps)); client->ps.pmove.origin[0] = spawn_origin[0]*8; client->ps.pmove.origin[1] = spawn_origin[1]*8; client->ps.pmove.origin[2] = spawn_origin[2]*8; if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) { client->ps.fov = 90; } else { client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov")); if (client->ps.fov < 1) client->ps.fov = 90; else if (client->ps.fov > 160) client->ps.fov = 160; } client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model); // clear entity state values ent->s.effects = 0; ent->s.modelindex = 255; // will use the skin specified model ent->s.modelindex2 = 255; // custom gun model // sknum is player num and weapon number // weapon number will be added in changeweapon ent->s.skinnum = ent - g_edicts - 1; ent->s.frame = 0; VectorCopy (spawn_origin, ent->s.origin); ent->s.origin[2] += 1; // make sure off ground VectorCopy (ent->s.origin, ent->s.old_origin); // set the delta angle for (i=0 ; i<3 ; i++) { client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]); } ent->s.angles[PITCH] = 0; ent->s.angles[YAW] = spawn_angles[YAW]; ent->s.angles[ROLL] = 0; VectorCopy (ent->s.angles, client->ps.viewangles); VectorCopy (ent->s.angles, client->v_angle); // spawn a spectator if (client->pers.spectator) { client->chase_target = NULL; client->resp.spectator = true; ent->movetype = MOVETYPE_NOCLIP; ent->solid = SOLID_NOT; ent->svflags |= SVF_NOCLIENT; ent->client->ps.gunindex = 0; gi.linkentity (ent); return; } else client->resp.spectator = false; if (!KillBox (ent)) { // could't spawn in? } gi.linkentity (ent); ent->killer = NULL; ent->client->chasetoggle = 0; //If chasetoggle set then turn on (delayed start of 5 frames - 0.5s) if (ent->client->pers.chasetoggle && !ent->client->chasetoggle) ent->client->delayedstart = 5; // force the current weapon up client->newweapon = client->pers.weapon; ChangeWeapon (ent); ValidateSelectedItem (ent); if (!deathmatch->value && !coop->value) client->resp.bullettime = B_TIME_MIN; } /* ===================== ClientBeginDeathmatch A client has just connected to the server in deathmatch mode, so clear everything out before starting them. ===================== */ char *make_green (char *s) { char * temp = s; unsigned char *p; p = (unsigned char *) temp; while (*p) { if ((*p >= 0x1b && *p <= 0x7f) || (*p >= 0x0a && *p <= 0x11)) // *p += (char) 0x80; p++; } return temp; } char *make_white (char *s) { char * temp = s; unsigned char *p; p = (unsigned char *) temp; while (*p) { if ((*p >= 0x9b && *p <= 0xff) || (*p >= 0x8a && *p <= 0x91)) *p -= (char) 0x80; p++; } return temp; } char * Marquee (edict_t * ent, char * string) { int i=0, j=0; char * temp = ent->client->MOTDmsg; unsigned char *p1, *p2, *p3; p1 = (unsigned char *) string; p2 = (unsigned char *) temp; p3 = (unsigned char *) string; while (*p3) { p3++; j++; } if (ent->client->MOTDrot<=-j) ent->client->MOTDrot = MOTDMAX; j=ent->client->MOTDrot; while (j<0) { p1++; j++; } while (i < ent->client->MOTDrot) { *p2 = ' '; p2++;i++; } while (*p1&&iclient->MOTDrot-= ent->client->MOTDrotChange; return temp; } void PrintMOTDmsg (edict_t *ent) { char string[1024]; int i, frags=0; gclient_t *cl; int motdLength=10; for (i=0 ; ivalue ; i++) { cl = game.clients + i; if (!g_edicts[i+1].inuse) continue; if (cl->resp.score >= fraglimit->value) { frags = cl->resp.score; } } ent->client->showhelp = true; Com_sprintf (string, sizeof(string), "xv 32 yv 8 picn help " // background "xv 202 yv 12 string2 \"%s\" " // DM/Coop "xv 0 yv 25 cstring2 \"%s\" " // Build Name "xv 0 yv 57 cstring2 \"%s\" " // MOTD "xv 0 yv 71 cstring2 \"%s %s%s\" "// help 2 "xv 0 yv 113 cstring2 \"%s\" " //email "xv 0 yv 127 cstring2 \"%s\" " //url "xv 50 yv 147 string2 \"%s\" " "xv 50 yv 167 string2 \" %3i/%s %i/%i %i/%i\" ", (sv_teams->value && deathmatch->value)? "Teams": (deathmatch->value)?"DM" : (sv_teams->value && coop->value)? "Coop":(coop->value)? "Battle" : "Single", make_green(GAMEVERSION), make_green(Marquee(ent, motd->string)), "Type", make_green("helpme "), "in the Console", "psychospaz@telefragged.com", "modscape.telefragged.com", make_green(" Client Time Frags"), ent-g_edicts, maxclients->string, (level.framenum/600), (int)timelimit->value, frags, (int)fraglimit->value); gi.WriteByte (svc_layout); gi.WriteString (string); gi.unicast (ent, true); } void ClientBeginDeathmatch (edict_t *ent) { G_InitEdict (ent); InitClientResp (ent->client); // locate ent at a spawn point PutClientInServer (ent); if (level.intermissiontime) { MoveClientToIntermission (ent); } else { // send effect gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGIN); gi.multicast (ent->s.origin, MULTICAST_PVS); } gi.bprintf (PRINT_HIGH, "%s%s%s%i Clients in game%s\n", make_green("["), make_white(ent->client->pers.netname), make_green("] entered the game ("), ent-g_edicts, make_green(")") ); //PrintMOTDmsg(ent); ent->client->MotdTime = (Q_stricmp ("", motd->string))?DEFAULT_MOTD_TIME:0; ent->client->MOTDrot = MOTDMAX; ClientEndServerFrame (ent); } /* =========== ClientBegin called when a client has finished connecting, and is ready to be placed into the game. This will happen every level load. ============ */ void stuffcmd(edict_t *ent, char *s) { gi.WriteByte (11); gi.WriteString (s); gi.unicast (ent, true); } void ClientBegin (edict_t *ent) { int i; ent->client = game.clients + (ent - g_edicts - 1); stuffcmd (ent, "alias +hook \"use grapple\"; alias -hook \"use grapple\";"); stuffcmd (ent, "alias +grapple \"use grapple\"; alias -grapple \"use grapple\";"); stuffcmd (ent, "alias +zoom \"fov 20\"; alias -zoom \"fov 90\";"); stuffcmd (ent, "alias +stunt \"stunton\"; alias -stunt \"stuntoff\";"); stuffcmd (ent, "alias exit \"quit\";"); stuffcmd (ent, "alias jetpack \"use Jet Propulsion Unit\""); if (deathmatch->value) { ClientBeginDeathmatch (ent); return; } // if there is already a body waiting for us (a loadgame), just // take it, otherwise spawn one from scratch if (ent->inuse == true) { // the client has cleared the client side viewangles upon // connecting to the server, which is different than the // state when the game is saved, so we need to compensate // with deltaangles for (i=0 ; i<3 ; i++) ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]); } else { // a spawn point will completely reinitialize the entity // except for the persistant data that was initialized at // ClientConnect() time G_InitEdict (ent); ent->classname = "player"; InitClientResp (ent->client); PutClientInServer (ent); } if (level.intermissiontime) { MoveClientToIntermission (ent); } else { // send effect if in a multiplayer game if (game.maxclients > 1) { gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGIN); gi.multicast (ent->s.origin, MULTICAST_PVS); gi.bprintf (PRINT_HIGH, "%s%s%s%i Clients in game%s\n", make_green("["), make_white(ent->client->pers.netname), make_green("] entered the game ("), ent-g_edicts, make_green(")") ); } } //PrintMOTDmsg(ent); ent->client->MotdTime = (Q_stricmp ("", motd->string))?DEFAULT_MOTD_TIME:0; ent->client->MOTDrot =MOTDMAX; SPClient = ent; // make sure all view stuff is valid ClientEndServerFrame (ent); } /* =========== ClientUserInfoChanged called whenever the player updates a userinfo variable. The game can override any of the settings in place (forcing skins or names, etc) before copying it off. ============ */ void ClientUserinfoChanged (edict_t *ent, char *userinfo) { char *s; int playernum; // check for malformed or illegal info strings if (!Info_Validate(userinfo)) { strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt"); } // set name s = Info_ValueForKey (userinfo, "name"); strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1); // set spectator s = Info_ValueForKey (userinfo, "spectator"); // spectators are only supported in deathmatch if (deathmatch->value && *s && strcmp(s, "0")) ent->client->pers.spectator = true; else ent->client->pers.spectator = false; // set skin s = Info_ValueForKey (userinfo, "skin"); playernum = ent-g_edicts-1; // combine name and skin into a configstring gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) ); // fov if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) { ent->client->ps.fov = 90; } else { ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov")); if (ent->client->ps.fov < 1) ent->client->ps.fov = 90; else if (ent->client->ps.fov > 360) ent->client->ps.fov = 360; } // handedness s = Info_ValueForKey (userinfo, "hand"); if (strlen(s)) { ent->client->pers.hand = atoi(s); } // save off the userinfo in case we want to check something later strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1); // ent->s.modelindex = gi.modelindex("players/male/tris.md2"); // ent->s.modelindex = gi.modelindex("players/male/tris.md2"); } /* =========== ClientConnect Called when a player begins connecting to the server. The game can refuse entrance to a client by returning false. If the client is allowed, the connection process will continue and eventually get to ClientBegin() Changing levels will NOT cause this to be called again, but loadgames will. ============ */ qboolean ClientConnect (edict_t *ent, char *userinfo) { char *value; // check to see if they are on the banned IP list value = Info_ValueForKey (userinfo, "ip"); if (SV_FilterPacket(value)) { Info_SetValueForKey(userinfo, "rejmsg", "Banned."); return false; } // check for a spectator value = Info_ValueForKey (userinfo, "spectator"); if (deathmatch->value && *value && strcmp(value, "0")) { int i, numspec; if (*spectator_password->string && strcmp(spectator_password->string, "none") && strcmp(spectator_password->string, value)) { Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect."); return false; } // count spectators for (i = numspec = 0; i < maxclients->value; i++) if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator) numspec++; if (numspec >= maxspectators->value) { Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full."); return false; } } else { // check for a password value = Info_ValueForKey (userinfo, "password"); if (*password->string && strcmp(password->string, "none") && strcmp(password->string, value)) { Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect."); return false; } } // they can connect ent->client = game.clients + (ent - g_edicts - 1); // if there is already a body waiting for us (a loadgame), just // take it, otherwise spawn one from scratch if (ent->inuse == false) { // clear the respawning variables InitClientResp (ent->client); if (!game.autosaved || !ent->client->pers.weapon) InitClientPersistant (ent->client); } ClientUserinfoChanged (ent, userinfo); if (game.maxclients > 1) gi.dprintf ("%s has joined the game.\n", ent->client->pers.netname); ent->svflags = 0; // make sure we start with known default ent->client->pers.connected = true; return true; } /* =========== ClientDisconnect Called when a player drops from the server. Will not be called between levels. ============ */ void ClientDisconnect (edict_t *ent) { char temp[3]; int playernum; if (!ent->client) return; if (ent->client->chasetoggle) ChasecamRemove (ent, OPTION_OFF); gi.bprintf (PRINT_HIGH, "%s %s%i Clients Left%s\n", make_green(ent->client->pers.netname), make_green("Disconnected ("), ent-g_edicts-1, make_green(")") ); // send effect gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGOUT); gi.multicast (ent->s.origin, MULTICAST_PVS); gi.unlinkentity (ent); ent->s.modelindex = 0; ent->solid = SOLID_NOT; ent->inuse = false; ent->classname = "disconnected"; ent->client->pers.connected = false; playernum = ent-g_edicts-1; gi.configstring (CS_PLAYERSKINS+playernum, ""); } //============================================================== edict_t *pm_passent; // pmove doesn't need to know about passent and contentmask #define BBOX_WIDTH 15 trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) { if (!deathmatch->value) { if (pm_passent->client && ( pm_passent->client->ps.pmove.pm_flags & PMF_DUCKED || pm_passent->client->stunts<5 )) { VectorSet(mins, -BBOX_WIDTH, -BBOX_WIDTH, -24); VectorSet(maxs, BBOX_WIDTH, BBOX_WIDTH, 4); } else { VectorSet(mins, -BBOX_WIDTH, -BBOX_WIDTH, -24); VectorSet(maxs, BBOX_WIDTH, BBOX_WIDTH, 32); } } if (pm_passent->health > 0) return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID); else return gi.trace (start, mins, maxs, end, pm_passent, MASK_DEADSOLID); } unsigned CheckBlock (void *b, int c) { int v,i; v = 0; for (i=0 ; is, sizeof(pm->s)); c2 = CheckBlock (&pm->cmd, sizeof(pm->cmd)); Com_Printf ("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2); } /* ============== ClientThink This will be called once for each client frame, which will usually be a couple times for each server frame. ============== */ void ClientThink (edict_t *ent, usercmd_t *ucmd) { gclient_t *client; edict_t *other; int i, j; pmove_t pm; level.current_entity = ent; client = ent->client; ent->client->resp.fps_counter++; if (ent->client->stunts<0) ucmd->upmove = 0; if (client->damage_div) { ucmd->forwardmove /= (client->damage_div+1); ucmd->sidemove /= (client->damage_div+1); } //added so i can manipulate movement speed... ent->Move_forward = ucmd->forwardmove; ent->Move_side = ucmd->sidemove; ent->Move_up = ucmd->upmove; ent->lightlevel = ucmd->lightlevel; if (level.intermissiontime) { if (client->chasetoggle) ChasecamRemove (ent, OPTION_OFF); client->ps.pmove.pm_type = PM_FREEZE; // can exit intermission after five seconds if (level.time > level.intermissiontime + 5.0 && (ucmd->buttons & BUTTON_ANY) ) level.exitintermission = true; return; } if (client->chasetoggle) ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; else ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; pm_passent = ent; if (ent->client->chase_target) { client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); } else { // set up for pmove memset (&pm, 0, sizeof(pm)); if (ent->movetype == MOVETYPE_NOCLIP) client->ps.pmove.pm_type = PM_SPECTATOR; else if (ent->s.modelindex != 255) client->ps.pmove.pm_type = PM_GIB; else if (ent->deadflag || client->isOnTurret) client->ps.pmove.pm_type = PM_DEAD; else client->ps.pmove.pm_type = PM_NORMAL; client->ps.pmove.gravity = (ent->client->jets||sv_waterlevel->value|| client->isOnTurret || abs(ent->client->wallrunning) || ent->client->climbing)? 0 :sv_gravity->value; pm.s = client->ps.pmove; for (i=0 ; i<3 ; i++) { pm.s.origin[i] = ent->client->cl_origin[i]*8; pm.s.velocity[i] = ent->velocity[i]*8; } if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s))) { pm.snapinitial = true; // gi.dprintf ("pmove changed!\n"); } pm.cmd = *ucmd; pm.trace = PM_trace; // adds default parms pm.pointcontents = gi.pointcontents; // perform a pmove gi.Pmove (&pm); // save results of pmove client->ps.pmove = pm.s; client->old_pmove = pm.s; if (!ent->client->isOnTurret) { for (i=0 ; i<3 ; i++) { ent->client->cl_origin[i] = pm.s.origin[i]*0.125; ent->velocity[i] = pm.s.velocity[i]*0.125; } if (client->ps.pmove.pm_flags & PMF_NO_PREDICTION) VectorCopy(client->cl_origin, ent->s.origin); } VectorCopy (pm.mins, ent->mins); VectorCopy (pm.maxs, ent->maxs); client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); if (!sv_waterlevel->value && ent->health>0 && !ent->deadflag) if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) { if (!(ent->client->stunts==-3 || ent->client->stunts==-4)) if (ent->client->aquasuit) { ent->velocity[2] *= 1.10; ent->velocity[1] *= 1.20; ent->velocity[0] *= 1.20; ent->s.event = EV_FOOTSTEP; ent->client->jumping = true; } else { gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 0.75, ATTN_NORM, 0); PlayerNoise(ent, ent->s.origin, PNOISE_SELF); ent->client->jumping = true; } if (ent->client->stunts==-3 || ent->client->stunts==-4) { ent->client->stunts=0; ent->velocity[2]=0; } else if (ent->client->stunt && sv_stunts->value) { vec3_t forward, strafe, up; int DiveSpeed = 300; AngleVectors (ent->client->v_angle, forward, strafe, up); VectorNormalize (forward); VectorNormalize (strafe); if (ent->Move_side>0) //dive right { VectorScale(strafe, DiveSpeed ,strafe); strafe[2] = 0; VectorAdd (strafe, ent->velocity, ent->velocity); ent->client->stunts=2; } else if (ent->Move_side<0) //dive left { VectorScale(strafe, -DiveSpeed ,strafe); strafe[2] = 0; VectorAdd (strafe, ent->velocity, ent->velocity); ent->client->stunts=-2; } else if (ent->Move_forward>0) //dive forward { VectorScale(forward, DiveSpeed ,forward); forward[2] = 0; VectorAdd (forward, ent->velocity, ent->velocity); ent->client->stunts=1; } else if (ent->Move_forward<0) //dive back { VectorScale(forward, -DiveSpeed ,forward); forward[2] = 0; VectorAdd (forward, ent->velocity, ent->velocity); ent->client->stunts=-1; } } } if (!ent->groundentity && (abs(ent->client->stunts)==1||abs(ent->client->stunts)==2)) CheckDiveGlassBreak(ent); //HEALTH REGENERATION if (ent->client->aquasuit) { ent->s.modelindex2 = 0; ent->flaming=0;//battlesuit nullifies fire } else if (sv_banned_weapons->value==-1) { ent->s.modelindex2 = 0; } else if (ent->health>0) ent->s.modelindex2 = 255; ent->viewheight = pm.viewheight; ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; ent->groundentity = pm.groundentity; if (pm.groundentity) ent->groundentity_linkcount = pm.groundentity->linkcount; if (ent->client->jets==1) ent->groundentity = NULL; if (ent->client->stunts<-2) ent->viewheight-=30; if (!ent->killer) { if (ent->deadflag) { client->ps.viewangles[ROLL] = 40; client->ps.viewangles[PITCH] = -15; client->ps.viewangles[YAW] = client->killer_yaw; } else { VectorCopy (pm.viewangles, client->v_angle); VectorCopy (pm.viewangles, client->ps.viewangles); } } else { VectorCopy (pm.viewangles, client->v_angle); VectorCopy (client->v_angle, ent->s.angles); VectorCopy (ent->s.angles, client->ps.viewangles); } gi.linkentity (ent); if (ent->movetype != MOVETYPE_NOCLIP) G_TouchTriggers (ent); // touch other objects for (i=0 ; itouch) continue; other->touch (other, ent, NULL, NULL); } } client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->oldbuttons; if (client->heldfire && !(client->buttons & BUTTON_ATTACK)) client->heldfire = false; // save light level the player is standing on for // monster sighting AI ent->light_level = ucmd->lightlevel; for (i=0; ent->linked_ents[i]; i++) VectorAdd(ent->linked_ents[i]->link_offset, ent->s.origin, ent->linked_ents[i]->s.origin); if (client->isOnTurret) { VectorClear(ent->velocity); if (pm.cmd.upmove) ent->client->isOnTurret=0; } else if (ent->client->aquasuit || sv_banned_weapons->value==-1) { if (client->buttons & BUTTON_ATTACK) { if (!client->weapon_thunk) client->weapon_thunk = true; if (ent->health>0) if (client->kicktime==0) { weapon_kick_fire (ent); client->kicktime=KICK_DELAY_TIME; } } } else { if (client->latched_buttons & BUTTON_ATTACK) { if (client->resp.spectator) { client->latched_buttons = 0; if (client->chase_target) { client->chase_target = NULL; client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; } else GetChaseTarget(ent); } else if (!client->weapon_thunk) { client->weapon_thunk = true; Think_Weapon (ent); } } } if (client->resp.spectator) { if (ucmd->upmove >= 10) { if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) { client->ps.pmove.pm_flags |= PMF_JUMP_HELD; if (client->chase_target) ChaseNext(ent); else GetChaseTarget(ent); } } else client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD; } if (ent->killer) UpdateChaseCamKiller(ent); // update chase cam if being followed for (i = 1; i <= maxclients->value; i++) { other = g_edicts + i; if (other->inuse && other->client->chase_target == ent) UpdateChaseCam(other); } if (ent->health && abs(ent->client->stunts) ) if (ent->client->stunts<-2 && !ent->waterlevel) { vec3_t forward, strafe; int RollSpeed = 300; VectorCopy(ent->client->v_angle, forward); forward[PITCH]=0; AngleVectors (forward, forward, strafe, NULL); VectorNormalize (forward);VectorNormalize (strafe); if (ent->client->stunts>-5) //forward or backward dive { ent->velocity[0]=0; ent->velocity[1]=0; } else if (ent->client->stunts>-30&&ent->client->stunts<-10) //right roll { VectorScale(strafe, RollSpeed ,strafe); strafe[2] = ent->velocity[2]; VectorCopy (strafe, ent->velocity); } else if (ent->client->stunts>-60&&ent->client->stunts<-40) //left roll { VectorScale(strafe, -RollSpeed ,strafe); strafe[2] = ent->velocity[2]; VectorCopy (strafe, ent->velocity); } else if (ent->client->stunts>-80&&ent->client->stunts<-70) //forward roll { VectorScale(forward, RollSpeed ,forward); forward[2] = ent->velocity[2]; VectorCopy (forward, ent->velocity); } else if (ent->client->stunts>-100&&ent->client->stunts<-90) //backward roll { VectorScale(forward, -RollSpeed ,forward); forward[2] = ent->velocity[2]; VectorCopy (forward, ent->velocity); } } } /* ============== ClientBeginServerFrame This will be called once for each server frame, before running any other entities in the world. ============== */ void ClientBeginServerFrame (edict_t *ent) { gclient_t *client; int buttonMask, i; qboolean DoRespawn; if (level.intermissiontime) return; client = ent->client; VectorCopy(client->cl_origin, ent->s.origin); if (deathmatch->value && client->pers.spectator != client->resp.spectator && (level.time - client->respawn_time) >= 5) { spectator_respawn(ent); return; } // run weapon animations if it hasn't been done by a ucmd_t if (!client->weapon_thunk && !client->resp.spectator) Think_Weapon (ent); else client->weapon_thunk = false; if (ent->deadflag) { // wait for any button just going down if ( level.time > client->respawn_time) { // in deathmatch, only wait for attack button // if (deathmatch->value) // buttonMask = BUTTON_ATTACK; // else // buttonMask = -1 && !BUTTON_ATTACK; // if (deathmatch->value) DoRespawn = client->latched_buttons & BUTTON_ATTACK ||(ent->viewcam_on&&!ent->killer); // else // DoRespawn = abs(ent->Move_up)||abs(ent->Move_side)||(ent->viewcam_on&&!ent->killer); if ( DoRespawn || (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN) ) ) { respawn(ent); client->latched_buttons = 0; } } return; } // add player trail so monsters can follow if (!deathmatch->value) if (!visible (ent, PlayerTrail_LastSpot() ) ) PlayerTrail_Add (ent->s.old_origin); client->latched_buttons = 0; }