/* =========== SelectHumanLockSpawnPoint Try to find a spawn point for human intermission otherwise use normal intermission spawn. ============ */ gentity_t *SelectHumanLockSpawnPoint( vec3_t origin, vec3_t angles ) { gentity_t *spot; spot = NULL; spot = G_Find( spot, FOFS( classname ), "info_human_intermission" ); if( !spot ) return SelectSpectatorSpawnPoint( origin, angles ); VectorCopy( spot->s.origin, origin ); VectorCopy( spot->s.angles, angles ); return spot; }
/* =========== 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, gentity_t *spawn, vec3_t origin, vec3_t angles ) { 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 = NULL; int flags; int savedPing; int teamLocal; int eventSequence; char userinfo[ MAX_INFO_STRING ]; vec3_t up = { 0.0f, 0.0f, 1.0f }; int maxAmmo, maxClips; weapon_t weapon; index = ent - g_entities; client = ent->client; teamLocal = client->pers.teamSelection; // only start client if chosen a class and joined a team if( client->pers.classSelection == PCL_NONE && teamLocal == TEAM_NONE ) client->sess.spectatorState = SPECTATOR_FREE; else if( client->pers.classSelection == PCL_NONE ) client->sess.spectatorState = SPECTATOR_LOCKED; if( origin != NULL ) VectorCopy( origin, spawn_origin ); if( angles != NULL ) VectorCopy( angles, spawn_angles ); // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if( client->sess.spectatorState != SPECTATOR_NOT ) { if( teamLocal == TEAM_NONE ) spawnPoint = SelectSpectatorSpawnPoint( spawn_origin, spawn_angles ); else if( teamLocal == TEAM_ALIENS ) spawnPoint = SelectAlienLockSpawnPoint( spawn_origin, spawn_angles ); else if( teamLocal == TEAM_HUMANS ) spawnPoint = SelectHumanLockSpawnPoint( spawn_origin, spawn_angles ); } else { if( spawn == NULL ) { G_Error( "ClientSpawn: spawn is NULL\n" ); return; } spawnPoint = spawn; if( ent != spawn ) { //start spawn animation on spawnPoint G_SetBuildableAnim( spawnPoint, BANIM_SPAWN1, qtrue ); if( spawnPoint->buildableTeam == TEAM_ALIENS ) spawnPoint->clientSpawnTime = ALIEN_SPAWN_REPEAT_TIME; else if( spawnPoint->buildableTeam == TEAM_HUMANS ) spawnPoint->clientSpawnTime = HUMAN_SPAWN_REPEAT_TIME; } } // toggle the teleport bit so the client knows to not lerp flags = ent->client->ps.eFlags & ( EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED ); flags ^= EF_TELEPORT_BIT; G_UnlaggedClear( ent ); // clear everything but the persistant data saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; for( i = 0; i < MAX_PERSISTANT; i++ ) persistant[ i ] = client->ps.persistant[ i ]; eventSequence = client->ps.eventSequence; memset( client, 0, sizeof( *client ) ); client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; 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_SPECSTATE ] = client->sess.spectatorState; client->airOutTime = level.time + 12000; trap_GetUserinfo( index, userinfo, sizeof( userinfo ) ); client->ps.eFlags = flags; //Com_Printf( "ent->client->pers->pclass = %i\n", ent->client->pers.classSelection ); 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; // calculate each client's acceleration ent->evaluateAcceleration = qtrue; client->ps.stats[ STAT_WEAPONS ] = 0; client->ps.stats[ STAT_WEAPONS2 ] = 0; client->ps.stats[ STAT_SLOTS ] = 0; client->ps.eFlags = flags; client->ps.clientNum = index; BG_ClassBoundingBox( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, NULL, NULL, NULL ); if( client->sess.spectatorState == SPECTATOR_NOT ) client->pers.maxHealth = client->ps.stats[ STAT_MAX_HEALTH ] = BG_Class( ent->client->pers.classSelection )->health; else client->pers.maxHealth = client->ps.stats[ STAT_MAX_HEALTH ] = 100; // clear entity values if( ent->client->pers.classSelection == PCL_HUMAN ) { BG_AddWeaponToInventory( WP_BLASTER, client->ps.stats ); BG_AddUpgradeToInventory( UP_MEDKIT, client->ps.stats ); weapon = client->pers.humanItemSelection; } else if( client->sess.spectatorState == SPECTATOR_NOT ) weapon = BG_Class( ent->client->pers.classSelection )->startWeapon; else weapon = WP_NONE; maxAmmo = BG_Weapon( weapon )->maxAmmo; maxClips = BG_Weapon( weapon )->maxClips; BG_AddWeaponToInventory( weapon, client->ps.stats ); client->ps.ammo = maxAmmo; client->ps.clips = maxClips; ent->client->ps.stats[ STAT_CLASS ] = ent->client->pers.classSelection; ent->client->ps.stats[ STAT_TEAM ] = ent->client->pers.teamSelection; ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE; ent->client->ps.stats[ STAT_STATE ] = 0; VectorSet( ent->client->ps.grapplePoint, 0.0f, 0.0f, 1.0f ); // health will count down towards max_health ent->health = client->ps.stats[ STAT_HEALTH ] = client->ps.stats[ STAT_MAX_HEALTH ]; //* 1.25; //if evolving scale health if( ent == spawn ) { ent->health *= ent->client->pers.evolveHealthFraction; client->ps.stats[ STAT_HEALTH ] *= ent->client->pers.evolveHealthFraction; } //clear the credits array for( i = 0; i < MAX_CLIENTS; i++ ) ent->credits[ i ] = 0; client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA; G_SetOrigin( ent, spawn_origin ); VectorCopy( spawn_origin, client->ps.origin ); #define UP_VEL 150.0f #define F_VEL 50.0f //give aliens some spawn velocity if( client->sess.spectatorState == SPECTATOR_NOT && client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { if( ent == spawn ) { //evolution particle system G_AddPredictableEvent( ent, EV_ALIEN_EVOLVE, DirToByte( up ) ); } else { spawn_angles[ YAW ] += 180.0f; AngleNormalize360( spawn_angles[ YAW ] ); if( spawnPoint->s.origin2[ 2 ] > 0.0f ) { vec3_t forward, dir; AngleVectors( spawn_angles, forward, NULL, NULL ); VectorScale( forward, F_VEL, forward ); VectorAdd( spawnPoint->s.origin2, forward, dir ); VectorNormalize( dir ); VectorScale( dir, UP_VEL, client->ps.velocity ); } G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 ); } } else if( client->sess.spectatorState == SPECTATOR_NOT && client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { spawn_angles[ YAW ] += 180.0f; AngleNormalize360( spawn_angles[ YAW ] ); } // 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( !( client->sess.spectatorState != SPECTATOR_NOT ) ) { trap_LinkEntity( ent ); // force the base weapon up client->ps.weapon = WP_NONE; 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->lastKillTime = 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 if( !spawn ) 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( BG_InventoryContainsWeapon( i, client->ps.stats ) ) { 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( client->sess.spectatorState == SPECTATOR_NOT ) { BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); } // must do this here so the number of active clients is calculated CalculateRanks( ); // 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 ); }
/* =========== 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 ); }
/* =========== 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]; 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 { 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->s.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; Com_Memset (client, 0, sizeof(*client)); client->ps.stats[STAT_ATTACKERCLIENT] = -1; client->ps.stats[STAT_INFOCLIENT] = -1; 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; 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; ent->s.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; // 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); } // 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; if ( level.intermissiontime ) { MoveClientToIntermission( ent ); } else { // fire the targets of the spawn point G_UseTargets( spawnPoint, ent ); } // 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 ); ent->s.modelindex = G_ModelIndex("models/human/coxswain.tik"); }
/* =========== 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; int savedTeam; qboolean update = qfalse; save_position_t *pos = NULL; index = ent - g_entities; client = ent->client; G_UpdateSpawnCounts(); client->pers.lastSpawnTime = level.time; if (client->sess.sessionTeam != TEAM_AXIS && client->sess.sessionTeam != TEAM_ALLIES) { spawnPoint = SelectSpectatorSpawnPoint(spawn_origin, spawn_angles); } else { spawnPoint = SelectPlayerSpawnPoint(client->sess.sessionTeam, spawn_origin, spawn_angles, client->sess.spawnObjectiveIndex); } client->pers.teamState.state = TEAM_ACTIVE; // toggle the teleport bit so the client knows to not lerp flags = ent->client->ps.eFlags & EF_TELEPORT_BIT; flags ^= EF_TELEPORT_BIT; flags |= (client->ps.eFlags & EF_VOTED); // clear everything but the persistant data ent->s.eFlags &= ~EF_MOUNTEDTANK; // Nico, notify timerun_stop notify_timerun_stop(ent, 0); ent->client->sess.timerunActive = qfalse; saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; savedTeam = client->ps.teamNum; for (i = 0 ; i < MAX_PERSISTANT ; i++) { persistant[i] = client->ps.persistant[i]; } memset(client, 0, sizeof (*client)); client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; client->ps.teamNum = savedTeam; for (i = 0 ; i < MAX_PERSISTANT ; i++) { client->ps.persistant[i] = persistant[i]; } // 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->ps.persistant[PERS_HWEAPON_USE] = 0; client->airOutTime = level.time + 12000; // 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; // DHM - Nerve :: Init to -1 on first spawn; ent->props_frame_state = -1; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; VectorCopy(playerMins, ent->r.mins); VectorCopy(playerMaxs, ent->r.maxs); // Ridah, setup the bounding boxes and viewheights for prediction VectorCopy(ent->r.mins, client->ps.mins); VectorCopy(ent->r.maxs, client->ps.maxs); client->ps.crouchViewHeight = CROUCH_VIEWHEIGHT; client->ps.standViewHeight = DEFAULT_VIEWHEIGHT; client->ps.deadViewHeight = DEAD_VIEWHEIGHT; client->ps.crouchMaxZ = client->ps.maxs[2] - (client->ps.standViewHeight - client->ps.crouchViewHeight); client->ps.runSpeedScale = 0.8; client->ps.sprintSpeedScale = 1.1; client->ps.crouchSpeedScale = 0.25; client->ps.weaponstate = WEAPON_READY; // Rafael client->ps.friction = 1.0; // done. // TTimo // retrieve from the persistant storage (we use this in pmoveExt_t beause we need it in bg_*) client->pmext.bAutoReload = client->pers.bAutoReloadAux; // done client->ps.clientNum = index; trap_GetUsercmd(client - level.clients, &ent->client->pers.cmd); // NERVE - SMF - moved this up here if (client->sess.playerType != client->sess.latchPlayerType) { update = qtrue; } client->sess.playerType = client->sess.latchPlayerType; if (client->sess.playerWeapon != client->sess.latchPlayerWeapon) { client->sess.playerWeapon = client->sess.latchPlayerWeapon; update = qtrue; } client->sess.playerWeapon2 = client->sess.latchPlayerWeapon2; if (update) { ClientUserinfoChanged(index); } G_UpdateCharacter(client); SetWolfSpawnWeapons(client); client->pers.maxHealth = 125; client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->pers.cmd.weapon = ent->client->ps.weapon; // dhm - end ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_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; SetClientViewAngle(ent, spawn_angles); if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { trap_LinkEntity(ent); } client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; client->latched_wbuttons = 0; //----(SA) added // fire the targets of the spawn point G_UseTargets(spawnPoint, ent); // 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); // set idle animation on weapon ent->client->ps.weapAnim = ((ent->client->ps.weapAnim & ANIM_TOGGLEBIT) ^ ANIM_TOGGLEBIT) | PM_IdleAnimForWeapon(ent->client->ps.weapon); // clear entity state values BG_PlayerStateToEntityState(&client->ps, &ent->s, qtrue); // show_bug.cgi?id=569 G_ResetMarkers(ent); // RF, start the scripting system if (client->sess.sessionTeam != TEAM_SPECTATOR) { // RF, call entity scripting event G_Script_ScriptEvent(ent, "playerstart", ""); } // Nico, autoload position if (ent->client->pers.autoLoad && !ent->client->sess.lastDieWasASelfkill && (ent->client->sess.sessionTeam == TEAM_AXIS || ent->client->sess.sessionTeam == TEAM_ALLIES)) { if (ent->client->sess.sessionTeam == TEAM_ALLIES) { pos = ent->client->sess.alliesSaves; } else { pos = ent->client->sess.axisSaves; } if (pos->valid) { VectorCopy(pos->origin, ent->client->ps.origin); // Nico, load angles if cg_loadViewAngles = 1 if (ent->client->pers.loadViewAngles) { SetClientViewAngle(ent, pos->vangles); } // Nico, load saved weapon if cg_loadWeapon = 1 if (ent->client->pers.loadWeapon) { ent->client->ps.weapon = pos->weapon; } VectorClear(ent->client->ps.velocity); if (ent->client->ps.stats[STAT_HEALTH] < 100 && ent->client->ps.stats[STAT_HEALTH] > 0) { ent->health = 100; } } } }
/** 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); }