/* =========== 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) //ZOID if (ctf->value) spot = SelectCTFSpawnPoint(ent); else //ZOID 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); }
/* =========== ClientSpawn Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void ClientSpawn(gentity_t *ent) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; clientPersistant_t saved; clientSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; int flags; int savedPing; // char *savedAreaBits; int accuracy_hits, accuracy_shots; int eventSequence; char userinfo[MAX_INFO_STRING]; int weapon; index = ent - g_entities; client = ent->client; // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { spawnPoint = SelectSpectatorSpawnPoint ( spawn_origin, spawn_angles); } else if (g_gametype.integer >= GT_CTF ) { // all base oriented team games use the CTF spawn points spawnPoint = SelectCTFSpawnPoint ( client->sess.sessionTeam, client->pers.teamState.state, spawn_origin, spawn_angles); } else { do { // the first spawn should be at a good looking spot if ( !client->pers.initialSpawn && client->pers.localClient ) { client->pers.initialSpawn = qtrue; spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles ); } else { // don't spawn near existing origin if possible spawnPoint = SelectSpawnPoint ( client->ps.origin, spawn_origin, spawn_angles); } // Tim needs to prevent bots from spawning at the initial point // on q3dm0... if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } // just to be symetric, we have a nohumans option... if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } break; } while ( 1 ); } client->pers.teamState.state = TEAM_ACTIVE; // always clear the kamikaze flag ent->s.eFlags &= ~EF_KAMIKAZE; // toggle the teleport bit so the client knows to not lerp // and never clear the voted flag flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED); flags ^= EF_TELEPORT_BIT; // clear everything but the persistant data saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; // savedAreaBits = client->areabits; accuracy_hits = client->accuracy_hits; accuracy_shots = client->accuracy_shots; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { persistant[i] = client->ps.persistant[i]; } eventSequence = client->ps.eventSequence; memset (client, 0, sizeof(*client)); // bk FIXME: Com_Memset? client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; // client->areabits = savedAreaBits; client->accuracy_hits = accuracy_hits; client->accuracy_shots = accuracy_shots; client->lastkilled_client = -1; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { client->ps.persistant[i] = persistant[i]; } client->ps.eventSequence = eventSequence; // increment the spawncount so the client will detect the respawn client->ps.persistant[PERS_SPAWN_COUNT]++; client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; client->airOutTime = level.time + 12000; trap_GetUserinfo( index, userinfo, sizeof(userinfo) ); // set max health client->pers.maxHealth = atoi( Info_ValueForKey( userinfo, "handicap" ) ); if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } // clear entity values client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->ps.eFlags = flags; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; VectorCopy (playerMins, ent->r.mins); VectorCopy (playerMaxs, ent->r.maxs); client->ps.clientNum = index; if (IsBot(client->ps.clientNum)) { weapon = WP_LIGHTNING; client->ps.stats[STAT_WEAPONS] = ( 1 << weapon ); client->ps.ammo[weapon] = -1; } else { weapon = WP_MACHINEGUN; client->ps.stats[STAT_WEAPONS] = ( 1 << weapon ); client->ps.ammo[weapon] = 100; client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET ); client->ps.ammo[WP_GAUNTLET] = -1; client->ps.ammo[WP_GRAPPLING_HOOK] = -1; } // health will count down towards max_health ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25; G_SetOrigin( ent, spawn_origin ); VectorCopy( spawn_origin, client->ps.origin ); // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd ); SetClientViewAngle( ent, spawn_angles ); if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { } else { G_KillBox( ent ); trap_LinkEntity (ent); // force the base weapon up client->ps.weapon = weapon; client->ps.weaponstate = WEAPON_READY; } // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = TORSO_STAND; client->ps.legsAnim = LEGS_IDLE; if ( level.intermissiontime ) { MoveClientToIntermission( ent ); } else { // fire the targets of the spawn point G_UseTargets( spawnPoint, ent ); // select the highest weapon number available, after any // spawn given items have fired client->ps.weapon = weapon; for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) { if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) { client->ps.weapon = i; break; } } } // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.time - 100; ent->client->pers.cmd.serverTime = level.time; ClientThink( ent-g_entities ); // positively link the client, even if the command times are weird if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); } // run the presend to set anything else ClientEndFrame( ent ); // clear entity state values BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); }
/* =========== ClientSpawn Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void ClientSpawn(gentity_t *ent) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; clientPersistant_t saved; clientSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; int flags; int savedPing; // char *savedAreaBits; int accuracy_hits, accuracy_shots; int eventSequence; // char userinfo[MAX_INFO_STRING]; forcedata_t savedForce; void *ghoul2save; int saveSaberNum = ENTITYNUM_NONE; int wDisable = 0; index = ent - g_entities; client = ent->client; if (client->ps.fd.forceDoInit) { //force a reread of force powers WP_InitForcePowers( ent ); client->ps.fd.forceDoInit = 0; } // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { spawnPoint = SelectSpectatorSpawnPoint ( spawn_origin, spawn_angles); } else if (g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY) { // all base oriented team games use the CTF spawn points spawnPoint = SelectCTFSpawnPoint ( client->sess.sessionTeam, client->pers.teamState.state, spawn_origin, spawn_angles); } else if (g_gametype.integer == GT_SAGA) { spawnPoint = SelectSagaSpawnPoint ( client->sess.sessionTeam, client->pers.teamState.state, spawn_origin, spawn_angles); } else { do { // the first spawn should be at a good looking spot if ( !client->pers.initialSpawn && client->pers.localClient ) { client->pers.initialSpawn = qtrue; spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles ); } else { // don't spawn near existing origin if possible spawnPoint = SelectSpawnPoint ( client->ps.origin, spawn_origin, spawn_angles); } // Tim needs to prevent bots from spawning at the initial point // on q3dm0... if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } // just to be symetric, we have a nohumans option... if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) { continue; // try again } break; } while ( 1 ); } client->pers.teamState.state = TEAM_ACTIVE; // toggle the teleport bit so the client knows to not lerp // and never clear the voted flag flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED); flags ^= EF_TELEPORT_BIT; // clear everything but the persistant data saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; // savedAreaBits = client->areabits; accuracy_hits = client->accuracy_hits; accuracy_shots = client->accuracy_shots; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { persistant[i] = client->ps.persistant[i]; } eventSequence = client->ps.eventSequence; savedForce = client->ps.fd; ghoul2save = client->ghoul2; saveSaberNum = client->ps.saberEntityNum; memset (client, 0, sizeof(*client)); // bk FIXME: Com_Memset? //rww - Don't wipe the ghoul2 instance or the animation data client->ghoul2 = ghoul2save; //or the saber ent num client->ps.saberEntityNum = saveSaberNum; client->ps.fd = savedForce; client->ps.duelIndex = ENTITYNUM_NONE; client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; // client->areabits = savedAreaBits; client->accuracy_hits = accuracy_hits; client->accuracy_shots = accuracy_shots; client->lastkilled_client = -1; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { client->ps.persistant[i] = persistant[i]; } client->ps.eventSequence = eventSequence; // increment the spawncount so the client will detect the respawn client->ps.persistant[PERS_SPAWN_COUNT]++; client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; client->airOutTime = level.time + 12000; // trap_GetUserinfo( index, userinfo, sizeof(userinfo) ); // set max health client->pers.maxHealth = 100;//atoi( Info_ValueForKey( userinfo, "handicap" ) ); if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } // clear entity values client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->ps.eFlags = flags; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; VectorCopy (playerMins, ent->r.mins); VectorCopy (playerMaxs, ent->r.maxs); client->ps.clientNum = index; //give default weapons client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE ); if (g_gametype.integer == GT_HOLOCRON) { //always get free saber level 1 in holocron client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER ); //these are precached in g_items, ClearRegisteredItems() } else { if (client->ps.fd.forcePowerLevel[FP_SABERATTACK]) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER ); //these are precached in g_items, ClearRegisteredItems() } else { //if you don't have saber attack rank then you don't get a saber client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON); } } if (g_gametype.integer == GT_TOURNAMENT) { wDisable = g_duelWeaponDisable.integer; } else { wDisable = g_weaponDisable.integer; } if (!wDisable || !(wDisable & (1 << WP_BRYAR_PISTOL))) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL ); } else if (g_gametype.integer == GT_JEDIMASTER) { client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL ); } if (g_gametype.integer == GT_JEDIMASTER) { client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_SABER); client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON); } if (client->ps.stats[STAT_WEAPONS] & (1 << WP_BRYAR_PISTOL)) { client->ps.weapon = WP_BRYAR_PISTOL; } else if (client->ps.stats[STAT_WEAPONS] & (1 << WP_SABER)) { client->ps.weapon = WP_SABER; } else { client->ps.weapon = WP_STUN_BATON; } /* client->ps.stats[STAT_HOLDABLE_ITEMS] |= ( 1 << HI_BINOCULARS ); client->ps.stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(HI_BINOCULARS, IT_HOLDABLE); */ client->ps.stats[STAT_HOLDABLE_ITEMS] = 0; client->ps.stats[STAT_HOLDABLE_ITEM] = 0; if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { client->ps.stats[STAT_WEAPONS] = 0; client->ps.stats[STAT_HOLDABLE_ITEMS] = 0; client->ps.stats[STAT_HOLDABLE_ITEM] = 0; } client->ps.ammo[AMMO_BLASTER] = 100; //ammoData[AMMO_BLASTER].max; //100 seems fair. // client->ps.ammo[AMMO_POWERCELL] = ammoData[AMMO_POWERCELL].max; // client->ps.ammo[AMMO_FORCE] = ammoData[AMMO_FORCE].max; // client->ps.ammo[AMMO_METAL_BOLTS] = ammoData[AMMO_METAL_BOLTS].max; // client->ps.ammo[AMMO_ROCKETS] = ammoData[AMMO_ROCKETS].max; /* client->ps.stats[STAT_WEAPONS] = ( 1 << WP_BRYAR_PISTOL); if ( g_gametype.integer == GT_TEAM ) { client->ps.ammo[WP_BRYAR_PISTOL] = 50; } else { client->ps.ammo[WP_BRYAR_PISTOL] = 100; } */ client->ps.rocketLockIndex = MAX_CLIENTS; client->ps.rocketLockTime = 0; //rww - Set here to initialize the circling seeker drone to off. //A quick note about this so I don't forget how it works again: //ps.genericEnemyIndex is kept in sync between the server and client. //When it gets set then an entitystate value of the same name gets //set along with an entitystate flag in the shared bg code. Which //is why a value needs to be both on the player state and entity state. //(it doesn't seem to just carry over the entitystate value automatically //because entity state value is derived from player state data or some //such) client->ps.genericEnemyIndex = -1; client->ps.isJediMaster = qfalse; client->ps.fallingToDeath = 0; //Do per-spawn force power initialization WP_SpawnInitForcePowers( ent ); // health will count down towards max_health ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH];// * 1.25; Boot - no extra 25 hp on start. // Start with a small amount of armor as well. client->ps.stats[STAT_ARMOR] = client->ps.stats[STAT_MAX_HEALTH] * 0.25; G_SetOrigin( ent, spawn_origin ); VectorCopy( spawn_origin, client->ps.origin ); // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd ); SetClientViewAngle( ent, spawn_angles ); if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { } else { G_KillBox( ent ); trap_LinkEntity (ent); // force the base weapon up client->ps.weapon = WP_BRYAR_PISTOL; client->ps.weaponstate = FIRST_WEAPON; } // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = WeaponReadyAnim[client->ps.weapon]; client->ps.legsAnim = WeaponReadyAnim[client->ps.weapon]; if ( level.intermissiontime ) { MoveClientToIntermission( ent ); } else { // fire the targets of the spawn point G_UseTargets( spawnPoint, ent ); // select the highest weapon number available, after any // spawn given items have fired client->ps.weapon = 1; for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) { if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) { client->ps.weapon = i; break; } } } // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.time - 100; ent->client->pers.cmd.serverTime = level.time; ClientThink( ent-g_entities ); // positively link the client, even if the command times are weird if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); } if (g_spawnInvulnerability.integer) { ent->client->ps.eFlags |= EF_INVULNERABLE; ent->client->invulnerableTimer = level.time + g_spawnInvulnerability.integer; } // run the presend to set anything else ClientEndFrame( ent ); // clear entity state values BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); }
/* =========== PlayerSpawn Called every time a player is placed fresh in the world: after the first PlayerBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void PlayerSpawn(gentity_t *ent) { int index; vec3_t spawn_origin, spawn_angles; gplayer_t *player; int i; playerPersistant_t saved; playerSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; gentity_t *tent; int flags; int savedPing; // char *savedAreaBits; int accuracy_hits, accuracy_shots; int eventSequence; int startWeapon, startAmmo; index = ent - g_entities; player = ent->player; VectorClear(spawn_origin); // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this player if ( player->sess.sessionTeam == TEAM_SPECTATOR ) { spawnPoint = SelectSpectatorSpawnPoint ( spawn_origin, spawn_angles); } else if (g_gametype.integer >= GT_CTF ) { // all base oriented team games use the CTF spawn points spawnPoint = SelectCTFSpawnPoint ( player->sess.sessionTeam, player->pers.teamState.state, spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT)); } else { // the first spawn should be at a good looking spot if ( player->pers.initialSpawn && player->pers.localClient ) { spawnPoint = SelectInitialSpawnPoint(spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT)); } else { // don't spawn near existing origin if possible spawnPoint = SelectSpawnPoint ( player->ps.origin, spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT)); } } player->pers.teamState.state = TEAM_ACTIVE; // always clear the kamikaze flag ent->s.eFlags &= ~EF_KAMIKAZE; // toggle the teleport bit so the client knows to not lerp // and never clear the voted flag flags = ent->player->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED); flags ^= EF_TELEPORT_BIT; // clear everything but the persistant data saved = player->pers; savedSess = player->sess; savedPing = player->ps.ping; // savedAreaBits = player->areabits; accuracy_hits = player->accuracy_hits; accuracy_shots = player->accuracy_shots; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { persistant[i] = player->ps.persistant[i]; } eventSequence = player->ps.eventSequence; Com_Memset (player, 0, sizeof(*player)); player->pers = saved; player->sess = savedSess; player->ps.ping = savedPing; // player->areabits = savedAreaBits; player->accuracy_hits = accuracy_hits; player->accuracy_shots = accuracy_shots; player->lastkilled_player = -1; for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) { player->ps.persistant[i] = persistant[i]; } player->ps.eventSequence = eventSequence; // increment the spawncount so the client will detect the respawn player->ps.persistant[PERS_SPAWN_COUNT]++; player->ps.persistant[PERS_TEAM] = player->sess.sessionTeam; player->airOutTime = level.time + 12000; // set max health player->pers.maxHealth = PlayerHandicap( player ); // clear entity values player->ps.stats[STAT_MAX_HEALTH] = player->pers.maxHealth; player->ps.eFlags = flags; player->ps.contents = CONTENTS_BODY; player->ps.collisionType = ( g_playerCapsule.integer == 1 ) ? CT_CAPSULE : CT_AABB; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->player = &level.players[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; ent->player->headless = qfalse; VectorCopy (playerMins, player->ps.mins); VectorCopy (playerMaxs, player->ps.maxs); player->ps.playerNum = index; if ( g_instaGib.integer == 1 ) { startWeapon = WP_RAILGUN; startAmmo = 50; } else if ( g_instaGib.integer == 2 ) { startWeapon = WP_ROCKET_LAUNCHER; startAmmo = 50; } else { startWeapon = WP_MACHINEGUN; startAmmo = 100; } player->ps.stats[STAT_WEAPONS] = ( 1 << startWeapon ); player->ps.ammo[startWeapon] = startAmmo; player->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET ); player->ps.ammo[WP_GAUNTLET] = -1; player->ps.ammo[WP_GRAPPLING_HOOK] = -1; // health will count down towards max_health ent->health = player->ps.stats[STAT_HEALTH] = player->ps.stats[STAT_MAX_HEALTH] = MAX_HEALTH; G_SetOrigin( ent, spawn_origin ); VectorCopy( spawn_origin, player->ps.origin ); // the respawned flag will be cleared after the attack and jump keys come up player->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd( player - level.players, &ent->player->pers.cmd ); SetPlayerViewAngle( ent, spawn_angles ); // don't allow full run speed for a bit player->ps.pm_flags |= PMF_TIME_KNOCKBACK; player->ps.pm_time = 100; player->respawnTime = level.time; player->inactivityTime = level.time + g_inactivity.integer * 1000; player->latched_buttons = 0; // set default animations player->ps.torsoAnim = TORSO_STAND; player->ps.legsAnim = LEGS_IDLE; if (!level.intermissiontime) { if (ent->player->sess.sessionTeam != TEAM_SPECTATOR) { G_KillBox(ent); // force the base weapon up player->ps.weapon = startWeapon; player->ps.weaponstate = WEAPON_READY; // fire the targets of the spawn point G_UseTargets(spawnPoint, ent); // select the highest weapon number available, after any spawn given items have fired player->ps.weapon = 1; for (i = WP_NUM_WEAPONS - 1 ; i > 0 ; i--) { if (player->ps.stats[STAT_WEAPONS] & (1 << i)) { player->ps.weapon = i; break; } } // positively link the player, even if the command times are weird VectorCopy(ent->player->ps.origin, ent->r.currentOrigin); tent = G_TempEntity(ent->player->ps.origin, EV_PLAYER_TELEPORT_IN); tent->s.playerNum = ent->s.playerNum; trap_LinkEntity (ent); } } else { // move players to intermission MovePlayerToIntermission(ent); } // run a player frame to drop exactly to the floor, // initialize animations and other things player->ps.commandTime = level.time - 100; ent->player->pers.cmd.serverTime = level.time; PlayerThink( ent-g_entities ); // run the presend to set anything else, follow spectators wait // until all players have been reconnected after map_restart if ( ent->player->sess.spectatorState != SPECTATOR_FOLLOW ) { PlayerEndFrame( ent ); } // clear entity state values BG_PlayerStateToEntityState( &player->ps, &ent->s, qtrue ); // we don't want players being backward-reconciled to the place they died G_ResetHistory( ent ); }
/** Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState */ void ClientSpawn(gentity_t *ent) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; clientPersistant_t saved; clientSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; gentity_t *tent; int flags; int eventSequence; char userinfo[MAX_INFO_STRING]; index = ent - g_entities; client = ent->client; // follow other players when eliminated if (!level.warmupTime && g_gametype.integer == GT_ELIMINATION && client->sess.sessionTeam != TEAM_SPECTATOR) { if (level.roundStarted) { client->sess.spectatorState = SPECTATOR_FREE; client->eliminated = qtrue; return; } else { client->sess.spectatorState = SPECTATOR_NOT; client->eliminated = qfalse; } } VectorClear(spawn_origin); // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if (client->sess.sessionTeam == TEAM_SPECTATOR) { SelectSpectatorSpawnPoint(spawn_origin, spawn_angles); } else if (g_gametype.integer == GT_DEFRAG) { spawnPoint = SelectDefragSpawnPoint(spawn_origin, spawn_angles); } else if (g_gametype.integer == GT_CTF) { spawnPoint = SelectCTFSpawnPoint(client->sess.sessionTeam, spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT)); } else if (client->pers.lastKiller) { spawnPoint = SelectSpawnPoint(client->pers.lastKiller->ps.origin, spawn_origin, spawn_angles, ent->r.svFlags & SVF_BOT); } else { spawnPoint = SelectSpawnPoint(client->ps.origin, spawn_origin, spawn_angles, ent->r.svFlags & SVF_BOT); } if (!spawnPoint) { G_Error("Cannot find a spawn point.\n"); return; } // toggle the teleport bit so the client knows to not lerp flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT); flags ^= EF_TELEPORT_BIT; // unlagged - backward reconciliation #3 // we don't want players being backward-reconciled to the place they died G_ResetHistory( ent ); // and this is as good a time as any to clear the saved state ent->client->saved.leveltime = 0; // clear everything but the persistant data saved = client->pers; savedSess = client->sess; eventSequence = client->ps.eventSequence; Com_Memcpy(persistant, client->ps.persistant, sizeof persistant); Com_Memset(client, 0, sizeof(*client)); client->pers = saved; client->sess = savedSess; client->ps.eventSequence = eventSequence; Com_Memcpy(client->ps.persistant, persistant, sizeof client->ps.persistant); // increment the spawncount so the client will detect the respawn client->ps.persistant[PERS_SPAWN_COUNT]++; client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; client->airOutTime = level.time + 12000; trap_GetUserinfo(index, userinfo, sizeof(userinfo)); // set max health client->pers.maxHealth = atoi(Info_ValueForKey(userinfo, "handicap")); if (client->pers.maxHealth < 1 || client->pers.maxHealth > 100) { client->pers.maxHealth = 100; } // clear entity values client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->ps.eFlags = flags; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags &= ~FL_NO_KNOCKBACK; ent->flags &= ~FL_FORCE_GESTURE; VectorCopy(playerMins, ent->r.mins); VectorCopy(playerMaxs, ent->r.maxs); client->ps.clientNum = index; ClientGiveWeapons(client); if (g_gametype.integer == GT_ELIMINATION) { client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] * 2; client->ps.stats[STAT_ARMOR] = client->ps.stats[STAT_MAX_HEALTH] * 1.5; } else { client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25; } ent->health = client->ps.stats[STAT_HEALTH]; G_SetOrigin(ent, spawn_origin); VectorCopy(spawn_origin, client->ps.origin); // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd(client - level.clients, &ent->client->pers.cmd); SetClientViewAngle(ent, spawn_angles); // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = TORSO_STAND; client->ps.legsAnim = LEGS_IDLE; if (level.intermissiontime) { // move players to intermission MoveClientToIntermission(ent); } else if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { G_KillBox(ent); // fire the targets of the spawn point G_UseTargets(spawnPoint, ent); // positively link the client, even if the command times are weird VectorCopy(ent->client->ps.origin, ent->r.currentOrigin); tent = G_TempEntity(ent->client->ps.origin, EV_PLAYER_TELEPORT_IN); tent->s.clientNum = ent->s.clientNum; trap_LinkEntity (ent); } // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.totalTime - 100; client->pers.cmd.serverTime = level.totalTime; ClientThink(ent-g_entities); // run the presend to set anything else, follow spectators wait // until all clients have been reconnected after map_restart if (ent->client->sess.spectatorState != SPECTATOR_FOLLOW) { ClientEndFrame(ent); } // clear entity state values BG_PlayerStateToEntityState(&client->ps, &ent->s, qtrue); }