/* * G_Match_Ready */ void G_Match_Ready( edict_t *ent ) { if( ent->r.svflags & SVF_FAKECLIENT && level.ready[PLAYERNUM( ent )] == true ) return; if( ent->s.team == TEAM_SPECTATOR ) { G_PrintMsg( ent, "Join the game first\n" ); return; } if( GS_MatchState() != MATCH_STATE_WARMUP ) { if( !( ent->r.svflags & SVF_FAKECLIENT ) ) G_PrintMsg( ent, "We're not in warmup.\n" ); return; } if( level.ready[PLAYERNUM( ent )] ) { G_PrintMsg( ent, "You are already ready.\n" ); return; } level.ready[PLAYERNUM( ent )] = true; G_PrintMsg( NULL, "%s%s is ready!\n", ent->r.client->netname, S_COLOR_WHITE ); G_UpdatePlayerMatchMsg( ent ); G_Match_CheckReadys(); }
/* * G_EndServerFrames_UpdateChaseCam */ void G_EndServerFrames_UpdateChaseCam( void ) { int i, team; edict_t *ent; // do it by teams, so spectators can copy the chasecam information from players for( team = TEAM_PLAYERS; team < GS_MAX_TEAMS; team++ ) { for( i = 0; i < teamlist[team].numplayers; i++ ) { ent = game.edicts + teamlist[team].playerIndices[i]; if( trap_GetClientState( PLAYERNUM(ent) ) < CS_SPAWNED ) { G_Chase_SetChaseActive( ent, false ); continue; } G_EndFrame_UpdateChaseCam( ent ); } } // Do spectators last team = TEAM_SPECTATOR; for( i = 0; i < teamlist[team].numplayers; i++ ) { ent = game.edicts + teamlist[team].playerIndices[i]; if( trap_GetClientState( PLAYERNUM(ent) ) < CS_SPAWNED ) { G_Chase_SetChaseActive( ent, false ); continue; } G_EndFrame_UpdateChaseCam( ent ); } }
/* * TVM_ClientConnect */ void TVM_ClientConnect( tvm_relay_t *relay, edict_t *ent, char *userinfo ) { edict_t *spec; assert( relay ); assert( ent ); assert( userinfo ); // make sure we start with known default if( ent->relay->playernum < 0 ) spec = NULL; else spec = ent->relay->edicts + ent->relay->playernum + 1; ent->local = true; ent->relay = relay; ent->r.inuse = true; ent->r.svflags = SVF_NOCLIENT; ent->s.team = spec ? spec->s.team : 0; ent->r.client = relay->local_clients + PLAYERNUM( ent ); memset( ent->r.client, 0, sizeof( *ent->r.client ) ); ent->r.client->ps.playerNum = PLAYERNUM( ent ); InitClientPersistant( ent->r.client ); TVM_ClientUserinfoChanged( relay, ent, userinfo ); //TVM_Printf( "Connect: %s\n", ent->r.client->pers.netname ); ent->r.client->pers.connected = true; ent->r.client->pers.connecting = true; }
/* * G_Match_NotReady */ void G_Match_NotReady( edict_t *ent ) { if( ent->s.team == TEAM_SPECTATOR ) { G_PrintMsg( ent, "Join the game first\n" ); return; } if( GS_MatchState() != MATCH_STATE_WARMUP && GS_MatchState() != MATCH_STATE_COUNTDOWN ) { G_PrintMsg( ent, "A match is not being setup.\n" ); return; } if( !level.ready[PLAYERNUM( ent )] ) { G_PrintMsg( ent, "You weren't ready.\n" ); return; } level.ready[PLAYERNUM( ent )] = false; G_PrintMsg( NULL, "%s%s is no longer ready.\n", ent->r.client->netname, S_COLOR_WHITE ); G_UpdatePlayerMatchMsg( ent ); G_Match_CheckReadys(); }
void G_ScoreboardMessage_AddChasers( int entnum, int entnum_self ) { char entry[MAX_TOKEN_CHARS]; int i; edict_t *e; size_t len; len = strlen( scoreboardString ); if( !len ) return; // add personal spectators Q_strncpyz( entry, "&y ", sizeof( entry ) ); ADD_SCOREBOARD_ENTRY( scoreboardString, len, entry ); for( i = 0; i < teamlist[TEAM_SPECTATOR].numplayers; i++ ) { e = game.edicts + teamlist[TEAM_SPECTATOR].playerIndices[i]; if( ENTNUM( e ) == entnum_self ) continue; if( e->r.client->connecting || trap_GetClientState( PLAYERNUM( e ) ) < CS_SPAWNED ) continue; if( !e->r.client->resp.chase.active || e->r.client->resp.chase.target != entnum ) continue; Q_snprintfz( entry, sizeof( entry ), "%i ", PLAYERNUM( e ) ); ADD_SCOREBOARD_ENTRY( scoreboardString, len, entry ); } }
/* * G_Teams_JoinChallengersQueue */ void G_Teams_JoinChallengersQueue( edict_t *ent ) { int pos = 0; edict_t *e; if( !GS_HasChallengers() ) { ent->r.client->queueTimeStamp = 0; return; } if( ent->s.team != TEAM_SPECTATOR ) return; // enter the challengers queue if( !ent->r.client->queueTimeStamp ) { // enter the line ent->r.client->queueTimeStamp = game.realtime; for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !e->r.inuse || !e->r.client || trap_GetClientState( PLAYERNUM(e) ) < CS_SPAWNED ) continue; if( !e->r.client->queueTimeStamp || e->s.team != TEAM_SPECTATOR ) continue; // if there are other players with the same timestamp, increase ours if( e->r.client->queueTimeStamp >= ent->r.client->queueTimeStamp ) ent->r.client->queueTimeStamp = e->r.client->queueTimeStamp+1; if( e->r.client->queueTimeStamp < ent->r.client->queueTimeStamp ) pos++; } G_PrintMsg( ent, "%sYou entered the challengers queue in position %i\n", S_COLOR_CYAN, pos+1 ); G_UpdatePlayerMatchMsg( ent ); } }
/* * TVM_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 TVM_ClientUserinfoChanged( tvm_relay_t *relay, edict_t *ent, char *userinfo ) { gclient_t *cl; char *s; assert( ent && ent->local && ent->r.client ); cl = ent->r.client; // check for malformed or illegal info strings if( !Info_Validate( userinfo ) ) { trap_DropClient( relay, PLAYERNUM( ent ), DROP_TYPE_GENERAL, "Error: Invalid userinfo" ); return; } if( !Info_ValueForKey( userinfo, "name" ) ) { trap_DropClient( relay, PLAYERNUM( ent ), DROP_TYPE_GENERAL, "Error: No name set" ); return; } // set name, it's validated and possibly changed first Q_strncpyz( cl->pers.netname, Info_ValueForKey( userinfo, "name" ), sizeof( cl->pers.netname ) ); // fov s = Info_ValueForKey( userinfo, "fov" ); if( !s ) { cl->pers.fov = 100; } else { cl->pers.fov = atoi( s ); clamp( cl->pers.fov, 1, 140 ); } s = Info_ValueForKey( userinfo, "zoomfov" ); if( !s ) { cl->pers.zoomfov = 30; } else { cl->pers.zoomfov = atoi( s ); clamp( cl->pers.zoomfov, 1, 60 ); } // save off the userinfo in case we want to check something later Q_strncpyz( cl->pers.userinfo, userinfo, sizeof( cl->pers.userinfo ) ); }
/* * TVM_Cmd_PlayersExt_f */ static void TVM_Cmd_PlayersExt_f( edict_t *ent, bool onlyspecs ) { int i; int count = 0; int start = 0; char line[64]; char msg[1024]; edict_t *e; tvm_relay_t *relay = ent->relay; if( trap_Cmd_Argc() > 1 ) { start = atoi( trap_Cmd_Argv( 1 ) ); } clamp( start, 0, relay->maxclients - 1 ); // print information msg[0] = 0; for( i = start + 1; PLAYERNUM( ( relay->edicts + i ) ) < relay->maxclients; i++ ) { e = relay->edicts + i; if( e->r.inuse && !e->local && e->r.client ) { char *userinfo; userinfo = relay->configStrings[CS_PLAYERINFOS + PLAYERNUM( e )]; Q_snprintfz( line, sizeof( line ), "%3i %s\n", i, Info_ValueForKey( userinfo, "name" ) ); if( strlen( line ) + strlen( msg ) > sizeof( msg ) - 100 ) { // can't print all of them in one packet Q_strncatz( msg, "...\n", sizeof( msg ) ); break; } if( count == 0 ) { Q_strncatz( msg, "num name\n", sizeof( msg ) ); Q_strncatz( msg, "--- ---------------\n", sizeof( msg ) ); } Q_strncatz( msg, line, sizeof( msg ) ); count++; } } if( count ) { Q_strncatz( msg, "--- ---------------\n", sizeof( msg ) ); } Q_strncatz( msg, va( "%3i %s\n", count, trap_Cmd_Argv( 0 ) ), sizeof( msg ) ); TVM_PrintMsg( relay, ent, "%s", msg ); if( i < relay->maxclients ) { TVM_PrintMsg( relay, ent, "Type '%s %i' for more %s\n", trap_Cmd_Argv( 0 ), i, trap_Cmd_Argv( 0 ) ); } }
char *G_Gametype_GENERIC_ScoreboardMessage( void ) { char entry[MAX_TOKEN_CHARS]; size_t len; int i; edict_t *e; int carrierIcon; *scoreboardString = 0; len = 0; Q_snprintfz( entry, sizeof(entry), "&t %i 0 0 ", TEAM_PLAYERS ); if( SCOREBOARD_MSG_MAXSIZE - len > strlen( entry ) ) { Q_strncatz( scoreboardString, entry, sizeof( scoreboardString ) ); len = strlen( scoreboardString ); } // players for( i = 0; i < teamlist[TEAM_PLAYERS].numplayers; i++ ) { e = game.edicts + teamlist[TEAM_PLAYERS].playerIndices[i]; if( e->s.effects & EF_CARRIER ) carrierIcon = trap_ImageIndex( ( e->s.team == TEAM_BETA ) ? PATH_ALPHAFLAG_ICON : PATH_BETAFLAG_ICON ); else if( e->s.effects & EF_QUAD ) carrierIcon = trap_ImageIndex( PATH_QUAD_ICON ); else if( e->s.effects & EF_SHELL ) carrierIcon = trap_ImageIndex( PATH_SHELL_ICON ); else if( e->s.effects & EF_REGEN ) carrierIcon = trap_ImageIndex( PATH_REGEN_ICON ); else carrierIcon = 0; Q_snprintfz( entry, sizeof( entry ), "&p %i %i %i %i %i ", PLAYERNUM( e ), e->r.client->level.stats.score, e->r.client->r.ping > 999 ? 999 : e->r.client->r.ping, carrierIcon, ( level.ready[PLAYERNUM( e )] || GS_MatchState() >= MATCH_STATE_PLAYTIME ) ? trap_ImageIndex( PATH_VSAY_YES_ICON ) : 0 ); if( SCOREBOARD_MSG_MAXSIZE - len > strlen( entry ) ) { Q_strncatz( scoreboardString, entry, sizeof( scoreboardString ) ); len = strlen( scoreboardString ); } } return scoreboardString; }
//========================================== // AITools_Frame // Gives think time to the debug tools found // in this archive (those witch need it) //========================================== void AITools_Frame( void ) { edict_t *ent; for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients; ent++ ) { if( !ent->r.inuse ) continue; if( trap_GetClientState( PLAYERNUM( ent ) ) < CS_SPAWNED ) continue; AITools_DropNodes( ent ); AITools_ShowPlinks( ent ); } }
/* * ClientDisconnect * Called when a player drops from the server. * Will not be called between levels. */ void ClientDisconnect( edict_t *ent, const char *reason ) { int team; if( !ent->r.client || !ent->r.inuse ) return; // always report in RACE mode if( GS_RaceGametype() || ( ent->r.client->team != TEAM_SPECTATOR && ( GS_MatchState() == MATCH_STATE_PLAYTIME || GS_MatchState() == MATCH_STATE_POSTMATCH ) ) ) G_AddPlayerReport( ent, GS_MatchState() == MATCH_STATE_POSTMATCH ); for( team = TEAM_PLAYERS; team < GS_MAX_TEAMS; team++ ) G_Teams_UnInvitePlayer( team, ent ); if( !level.gametype.disableObituaries || !(ent->r.svflags & SVF_FAKECLIENT ) ) { if( !reason ) G_PrintMsg( NULL, "%s" S_COLOR_WHITE " disconnected\n", ent->r.client->netname ); else G_PrintMsg( NULL, "%s" S_COLOR_WHITE " disconnected (%s" S_COLOR_WHITE ")\n", ent->r.client->netname, reason ); } // send effect if( ent->s.team > TEAM_SPECTATOR ) G_TeleportEffect( ent, false ); ent->r.client->team = TEAM_SPECTATOR; G_ClientRespawn( ent, true ); // respawn as ghost ent->movetype = MOVETYPE_NOCLIP; // allow freefly // let the gametype scripts know this client just disconnected G_Gametype_ScoreEvent( ent->r.client, "disconnect", NULL ); G_FreeAI( ent ); AI_EnemyRemoved( ent ); ent->r.inuse = false; ent->r.svflags = SVF_NOCLIENT; memset( ent->r.client, 0, sizeof( *ent->r.client ) ); ent->r.client->ps.playerNum = PLAYERNUM( ent ); trap_ConfigString( CS_PLAYERINFOS+PLAYERNUM( ent ), "" ); GClip_UnlinkEntity( ent ); G_Match_CheckReadys(); }
void G_PlayerAward( edict_t *ent, const char *awardMsg ) { edict_t *other; char cmd[MAX_STRING_CHARS]; gameaward_t *ga; int i, size; score_stats_t *stats; if( !awardMsg || !awardMsg[0] || !ent->r.client ) return; Q_snprintfz( cmd, sizeof( cmd ), "aw \"%s\"", awardMsg ); trap_GameCmd( ent, cmd ); if( dedicated->integer ) G_Printf( "%s", COM_RemoveColorTokens( va( "%s receives a '%s' award.\n", ent->r.client->netname, awardMsg ) ) ); ent->r.client->level.stats.awards++; teamlist[ent->s.team].stats.awards++; G_Gametype_ScoreEvent( ent->r.client, "award", awardMsg ); stats = &ent->r.client->level.stats; if( !stats->awardAllocator ) stats->awardAllocator = LinearAllocator( sizeof( gameaward_t ), 0, _G_LevelMalloc, _G_LevelFree ); // ch : this doesnt work for race right? if( GS_MatchState() == MATCH_STATE_PLAYTIME || GS_MatchState() == MATCH_STATE_POSTMATCH ) { // ch : we store this locally to send to MM // first check if we already have this one on the clients list size = LA_Size( stats->awardAllocator ); ga = NULL; for( i = 0; i < size; i++ ) { ga = ( gameaward_t * )LA_Pointer( stats->awardAllocator, i ); if( !strncmp( ga->name, awardMsg, sizeof(ga->name)-1 ) ) break; } if( i >= size ) { ga = ( gameaward_t * )LA_Alloc( stats->awardAllocator ); memset( ga, 0, sizeof(*ga) ); ga->name = G_RegisterLevelString( awardMsg ); } if( ga ) ga->count++; } // add it to every player who's chasing this player for( other = game.edicts + 1; PLAYERNUM( other ) < gs.maxclients; other++ ) { if( !other->r.client || !other->r.inuse || !other->r.client->resp.chase.active ) continue; if( other->r.client->resp.chase.target == ENTNUM( ent ) ) trap_GameCmd( other, cmd ); } }
/* * G_Chase_IsValidTarget */ static bool G_Chase_IsValidTarget( edict_t *ent, edict_t *target, bool teamonly ) { if( !ent || !target ) { return false; } if( !target->r.inuse || !target->r.client || trap_GetClientState( PLAYERNUM( target ) ) < CS_SPAWNED ) { return false; } if( target->s.team < TEAM_PLAYERS || target->s.team >= GS_MAX_TEAMS || target == ent ) { return false; } if( teamonly && !ent->r.client->teamstate.is_coach && G_ISGHOSTING( target ) ) { return false; } if( teamonly && target->s.team != ent->s.team ) { return false; } if( G_ISGHOSTING( target ) && !target->deadflag && target->s.team != TEAM_SPECTATOR ) { return false; // ghosts that are neither dead, nor speccing (probably originating from gt-specific rules) } return true; }
/* * G_Teams_Invite_f */ void G_Teams_Invite_f( edict_t *ent ) { char *text; edict_t *toinvite; int team; if( ( !ent->r.inuse || !ent->r.client ) ) return; text = trap_Cmd_Argv( 1 ); if( !text || !strlen( text ) ) { int i; edict_t *e; char msg[1024]; msg[0] = 0; Q_strncatz( msg, "Usage: invite <player>\n", sizeof( msg ) ); Q_strncatz( msg, "- List of current players:\n", sizeof( msg ) ); for( i = 0, e = game.edicts+1; i < gs.maxclients; i++, e++ ) { if( !e->r.inuse ) continue; Q_strncatz( msg, va( "%3i: %s\n", PLAYERNUM( e ), e->r.client->netname ), sizeof( msg ) ); } G_PrintMsg( ent, "%s", msg ); return; } team = ent->s.team; if( !G_Teams_TeamIsLocked( team ) ) { G_PrintMsg( ent, "Your team is not locked.\n" ); return; } toinvite = G_PlayerForText( text ); if( !toinvite ) { G_PrintMsg( ent, "No such player.\n" ); return; } if( G_Teams_PlayerIsInvited( team, toinvite ) ) { G_PrintMsg( ent, "%s%s is already invited to your team.\n", toinvite->r.client->netname, S_COLOR_WHITE ); return; } G_Teams_InvitePlayer( team, toinvite ); G_PrintMsg( NULL, "%s%s invited %s%s to team %s%s.\n", ent->r.client->netname, S_COLOR_WHITE, toinvite->r.client->netname, S_COLOR_WHITE, GS_TeamName( team ), S_COLOR_WHITE ); }
/* * G_Match_ScorelimitHit */ bool G_Match_ScorelimitHit( void ) { edict_t *e; if( GS_MatchState() != MATCH_STATE_PLAYTIME ) return false; if( g_scorelimit->integer ) { if( !GS_TeamBasedGametype() ) { for( e = game.edicts+1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !e->r.inuse ) continue; if( e->r.client->level.stats.score >= g_scorelimit->integer ) return true; } } else { int team; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { if( teamlist[team].stats.score >= g_scorelimit->integer ) return true; } } } return false; }
/* * G_Match_ToggleReady */ void G_Match_ToggleReady( edict_t *ent ) { if( !level.ready[PLAYERNUM( ent )] ) G_Match_Ready( ent ); else G_Match_NotReady( ent ); }
/* * AI_InitEntitiesData */ void AI_InitEntitiesData( void ) { int newlinks, newjumplinks; edict_t *ent; if( !nav.num_nodes ) { if( g_numbots->integer ) trap_Cvar_Set( "g_numbots", "0" ); return; } // create nodes for navigable map entities ( must happen after finding teams ) for( ent = game.edicts + 1 + gs.maxclients; ENTNUM( ent ) < game.numentities; ent++ ) AI_AddNavigableEntity( ent ); // add all clients to goalEntities so they can be tracked as enemies for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients; ent++ ) AI_AddGoalEntity( ent ); // link all newly added nodes newlinks = AI_LinkServerNodes( nav.serverNodesStart ); newjumplinks = AI_LinkCloseNodes_JumpPass( nav.serverNodesStart ); if( developer->integer ) { G_Printf( " : added nodes:%i.\n", nav.num_nodes - nav.serverNodesStart ); G_Printf( " : total nodes:%i.\n", nav.num_nodes ); G_Printf( " : added links:%i.\n", newlinks ); G_Printf( " : added jump links:%i.\n", newjumplinks ); } G_Printf( " : AI Navigation Initialized.\n" ); nav.loaded = qtrue; }
/* * ClientCommand */ void ClientCommand( edict_t *ent ) { char *cmd; int i; if( !ent->r.client || trap_GetClientState( PLAYERNUM( ent ) ) < CS_SPAWNED ) return; // not fully in game yet cmd = trap_Cmd_Argv( 0 ); if( Q_stricmp( cmd, "cvarinfo" ) ) // skip cvarinfo cmds because they are automatic responses G_Client_UpdateActivity( ent->r.client ); // activity detected for( i = 0; i < MAX_GAMECOMMANDS; i++ ) { if( !g_Commands[i].name[0] ) break; if( !Q_stricmp( g_Commands[i].name, cmd ) ) { if( g_Commands[i].func ) g_Commands[i].func( ent ); else GT_asCallGameCommand( ent->r.client, cmd, trap_Cmd_Args(), trap_Cmd_Argc() - 1 ); return; } } G_PrintMsg( ent, "Bad user command: %s\n", cmd ); }
/* * G_Teams_InitLevel */ void G_Teams_Init( void ) { edict_t *ent; // set the team names with default ones trap_ConfigString( CS_TEAM_SPECTATOR_NAME, GS_DefaultTeamName( TEAM_SPECTATOR ) ); trap_ConfigString( CS_TEAM_PLAYERS_NAME, GS_DefaultTeamName( TEAM_PLAYERS ) ); trap_ConfigString( CS_TEAM_ALPHA_NAME, GS_DefaultTeamName( TEAM_ALPHA ) ); trap_ConfigString( CS_TEAM_BETA_NAME, GS_DefaultTeamName( TEAM_BETA ) ); g_teams_maxplayers = trap_Cvar_Get( "g_teams_maxplayers", "0", CVAR_ARCHIVE ); g_teams_allow_uneven = trap_Cvar_Get( "g_teams_allow_uneven", "1", CVAR_ARCHIVE ); //unlock all teams and clear up team lists memset( teamlist, 0, sizeof( teamlist ) ); for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients; ent++ ) { if( ent->r.inuse ) { memset( &ent->r.client->teamstate, 0, sizeof( ent->r.client->teamstate ) ); memset( &ent->r.client->resp, 0, sizeof( ent->r.client->resp ) ); ent->s.team = ent->r.client->team = TEAM_SPECTATOR; G_GhostClient( ent ); ent->movetype = MOVETYPE_NOCLIP; // allow freefly ent->r.client->teamstate.timeStamp = level.time; ent->r.client->resp.timeStamp = level.time; } } }
/* * G_Teams_SetTeam - sets clients team without any checking */ void G_Teams_SetTeam( edict_t *ent, int team ) { assert( ent && ent->r.inuse && ent->r.client ); assert( team >= TEAM_SPECTATOR && team < GS_MAX_TEAMS ); // if player was on a team, send partial report to matchmaker if( ent->r.client->team != TEAM_SPECTATOR && ent->r.client->team != team && GS_MatchState() == MATCH_STATE_PLAYTIME ) { G_Printf("Sending teamchange to MM, team %d to team %d\n", ent->r.client->team, team ); G_AddPlayerReport( ent, false ); // trap_MR_SendPartialReport(); } // clear scores at changing team memset( &ent->r.client->level.stats, 0, sizeof( ent->r.client->level.stats ) ); memset( &ent->r.client->teamstate, 0, sizeof( ent->r.client->teamstate ) ); ent->r.client->team = team; ent->r.client->teamstate.timeStamp = level.time; G_Teams_UnInvitePlayer( team, ent ); G_ClientRespawn( ent, true ); // make ghost using G_ClientRespawn so team is updated at ghosting G_SpawnQueue_AddClient( ent ); level.ready[PLAYERNUM( ent )] = false; G_Match_CheckReadys(); G_UpdatePlayerMatchMsg( ent ); }
/* * TVM_ClientBegin * * called when a client has finished connecting, and is ready * to be placed into the game. This will happen every level load. */ void TVM_ClientBegin( tvm_relay_t *relay, edict_t *ent ) { edict_t *spot, *other; int i, specs; char hostname[MAX_CONFIGSTRING_CHARS]; assert( ent && ent->local && ent->r.client ); //TVM_Printf( "Begin: %s\n", ent->r.client->pers.netname ); ent->r.client->pers.connecting = false; spot = TVM_SelectSpawnPoint( ent ); if( spot ) { VectorCopy( spot->s.origin, ent->s.origin ); VectorCopy( spot->s.origin, ent->s.old_origin ); VectorCopy( spot->s.angles, ent->s.angles ); VectorCopy( spot->s.origin, ent->r.client->ps.pmove.origin ); VectorCopy( spot->s.angles, ent->r.client->ps.viewangles ); } else { VectorClear( ent->s.origin ); VectorClear( ent->s.old_origin ); VectorClear( ent->s.angles ); VectorClear( ent->r.client->ps.pmove.origin ); VectorClear( ent->r.client->ps.viewangles ); } ent->s.teleported = true; // set the delta angle for( i = 0; i < 3; i++ ) ent->r.client->ps.pmove.delta_angles[i] = ANGLE2SHORT( ent->s.angles[i] ) - ent->r.client->pers.cmd_angles[i]; specs = 0; for( i = 0; i < relay->local_maxclients; i++ ) { other = relay->local_edicts + i; if( other == ent ) continue; if( !other->r.inuse || !other->r.client ) continue; if( trap_GetClientState( relay, PLAYERNUM( other ) ) != CS_SPAWNED ) continue; specs++; } Q_strncpyz( hostname, relay->configStrings[CS_HOSTNAME], sizeof( hostname ) ); TVM_PrintMsg( relay, ent, S_COLOR_ORANGE "Welcome to %s! There %s currently %i spectator%s on this channel.\n", COM_RemoveColorTokens( hostname ), (specs == 1 ? "is" : "are"), specs, (specs == 1 ? "" : "s") ); TVM_PrintMsg( relay, ent, S_COLOR_ORANGE "For more information about chase camera modes type 'chase help' at console.\n" ); if( ent->r.client->chase.active ) TVM_ChaseClientEndSnapFrame( ent ); else TVM_ClientEndSnapFrame( ent ); }
void G_Gametype_GENERIC_SetUpEndMatch( void ) { edict_t *ent; level.gametype.readyAnnouncementEnabled = false; level.gametype.scoreAnnouncementEnabled = false; level.gametype.pickableItemsMask = 0; // disallow item pickup level.gametype.countdownEnabled = false; for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients; ent++ ) { if( ent->r.inuse && trap_GetClientState( PLAYERNUM( ent ) ) >= CS_SPAWNED ) G_ClientRespawn( ent, true ); } G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_POSTMATCH_GAMEOVER_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); }
/* * G_ClientEndSnapFrame * * Called for each player at the end of the server frame * and right after spawning */ void G_ClientEndSnapFrame( edict_t *ent ) { gclient_t *client; int i; if( trap_GetClientState( PLAYERNUM( ent ) ) < CS_SPAWNED ) return; client = ent->r.client; // set fov if( !client->ps.pmove.stats[PM_STAT_ZOOMTIME] ) client->ps.fov = ent->r.client->fov; else { float frac = (float)client->ps.pmove.stats[PM_STAT_ZOOMTIME] / (float)ZOOMTIME; client->ps.fov = client->fov - ( (float)( client->fov - client->zoomfov ) * frac ); } // If the end of unit layout is displayed, don't give // the player any normal movement attributes if( GS_MatchState() >= MATCH_STATE_POSTMATCH ) { G_SetClientStats( ent ); } else { if( G_IsDead( ent ) && !level.gametype.customDeadBodyCam ) { G_Client_DeadView( ent ); } G_PlayerWorldEffects( ent ); // burn from lava, etc G_ClientDamageFeedback( ent ); // show damage taken along the snap G_SetClientStats( ent ); G_SetClientEffects( ent ); G_SetClientSound( ent ); G_SetClientFrame( ent ); client->ps.plrkeys = client->resp.snap.plrkeys; } G_ReleaseClientPSEvent( client ); // set the delta angle for( i = 0; i < 3; i++ ) client->ps.pmove.delta_angles[i] = ANGLE2SHORT( client->ps.viewangles[i] ) - client->ucmd.angles[i]; // this is pretty hackish. We exploit the fact that servers *do not* transmit // origin2/old_origin for ET_PLAYER/ET_CORPSE entities, and we use it for sending the player velocity if( !G_ISGHOSTING( ent ) ) { ent->r.svflags |= SVF_TRANSMITORIGIN2; VectorCopy( ent->velocity, ent->s.origin2 ); } else ent->r.svflags &= ~SVF_TRANSMITORIGIN2; }
/* * G_CheckNumBots */ static void G_CheckNumBots( void ) { edict_t *ent; int desiredNumBots; if( level.spawnedTimeStamp + 5000 > game.realtime ) return; // check sanity of g_numbots if( g_numbots->integer < 0 ) trap_Cvar_Set( "g_numbots", "0" ); if( g_numbots->integer > gs.maxclients ) trap_Cvar_Set( "g_numbots", va( "%i", gs.maxclients ) ); if( level.gametype.numBots > gs.maxclients ) level.gametype.numBots = gs.maxclients; desiredNumBots = level.gametype.numBots ? level.gametype.numBots : g_numbots->integer; if( desiredNumBots < game.numBots ) { // kick one bot for( ent = game.edicts + gs.maxclients; PLAYERNUM( ent ) >= 0; ent-- ) { if( !ent->r.inuse || !( ent->r.svflags & SVF_FAKECLIENT ) ) continue; if( AI_GetType( ent->ai ) == AI_ISBOT ) { trap_DropClient( ent, DROP_TYPE_GENERAL, NULL ); break; } } return; } if( desiredNumBots > game.numBots ) { // add a bot if there is room for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients && game.numBots < desiredNumBots; ent++ ) { if( !ent->r.inuse && trap_GetClientState( PLAYERNUM( ent ) ) == CS_FREE ) BOT_SpawnBot( NULL ); } } }
void G_AwardPlayerMissedLasergun( edict_t *self, int mod ) { int i; if( mod == MOD_LASERGUN_W || mod == MOD_LASERGUN_S ) { for( i = 0; i < gs.maxclients; i++ ) // cancelling lasergun combo award game.clients[i].resp.awardInfo.combo[PLAYERNUM( self )] &= ~COMBO_FLAG( WEAP_LASERGUN ); } }
/* * G_ClearSnap * We just run G_SnapFrame, the server just sent the snap to the clients, * it's now time to clean up snap specific data to start the next snap from clean. */ void G_ClearSnap( void ) { edict_t *ent; game.realtime = trap_Milliseconds(); // level.time etc. might not be real time // clear gametype's clock override gs.gameState.longstats[GAMELONG_CLOCKOVERRIDE] = 0; // clear all events in the snap for( ent = &game.edicts[0]; ENTNUM( ent ) < game.numentities; ent++ ) { if( ISEVENTENTITY( &ent->s ) ) // events do not persist after a snapshot { G_FreeEdict( ent ); continue; } // events only last for a single message ent->s.events[0] = ent->s.events[1] = 0; ent->s.eventParms[0] = ent->s.eventParms[1] = 0; ent->numEvents = 0; ent->eventPriority[0] = ent->eventPriority[1] = false; ent->s.teleported = qfalse; // remove teleported bit. // remove effect bits that are (most likely) added from gametypes ent->s.effects = ( ent->s.effects & (EF_TAKEDAMAGE|EF_CARRIER|EF_FLAG_TRAIL|EF_ROTATE_AND_BOB|EF_STRONG_WEAPON|EF_GHOST) ); } // recover some info, let players respawn and finally clear the snap structures for( ent = &game.edicts[0]; ENTNUM( ent ) < game.numentities; ent++ ) { if( !GS_MatchPaused() ) { // copy origin to old origin ( this old_origin is for snaps ) if( !( ent->r.svflags & SVF_TRANSMITORIGIN2 ) ) VectorCopy( ent->s.origin, ent->s.old_origin ); G_CheckClientRespawnClick( ent ); } if( GS_MatchPaused() ) ent->s.sound = entity_sound_backup[ENTNUM( ent )]; // clear the snap temp info memset( &ent->snap, 0, sizeof( ent->snap ) ); if( ent->r.client && trap_GetClientState( PLAYERNUM( ent ) ) >= CS_SPAWNED ) { memset( &ent->r.client->resp.snap, 0, sizeof( ent->r.client->resp.snap ) ); // set race stats to invisible RS_clearHUDStats( ent->r.client ); // racesow - clear with our function } } g_snapStarted = false; }
/* * G_StatsMessage * * Generates stats message for the entity * The returned string must be freed by the caller using G_Free * Note: This string must never contain " characters */ char *G_StatsMessage( edict_t *ent ) { gclient_t *client; gsitem_t *item; int i, shot_weak, hit_weak, shot_strong, hit_strong, shot_total, hit_total; static char entry[MAX_TOKEN_CHARS]; assert( ent && ent->r.client ); client = ent->r.client; // message header Q_snprintfz( entry, sizeof( entry ), "%d", PLAYERNUM( ent ) ); for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ ) { item = GS_FindItemByTag( i ); assert( item ); hit_weak = hit_strong = 0; shot_weak = shot_strong = 0; if( item->weakammo_tag != AMMO_NONE ) { hit_weak = client->level.stats.accuracy_hits[item->weakammo_tag - AMMO_GUNBLADE]; shot_weak = client->level.stats.accuracy_shots[item->weakammo_tag - AMMO_GUNBLADE]; } if( item->ammo_tag != AMMO_NONE ) { hit_strong = client->level.stats.accuracy_hits[item->ammo_tag - AMMO_GUNBLADE]; shot_strong = client->level.stats.accuracy_shots[item->ammo_tag - AMMO_GUNBLADE]; } hit_total = hit_weak + hit_strong; shot_total = shot_weak + shot_strong; Q_strncatz( entry, va( " %d", shot_total ), sizeof( entry ) ); if( shot_total < 1 ) continue; Q_strncatz( entry, va( " %d", hit_total ), sizeof( entry ) ); // strong Q_strncatz( entry, va( " %d", shot_strong ), sizeof( entry ) ); if( shot_strong != shot_total ) Q_strncatz( entry, va( " %d", hit_strong ), sizeof( entry ) ); } Q_strncatz( entry, va( " %d %d", client->level.stats.total_damage_given, client->level.stats.total_damage_received ), sizeof( entry ) ); Q_strncatz( entry, va( " %d %d", client->level.stats.health_taken, client->level.stats.armor_taken ), sizeof( entry ) ); // add enclosing quote Q_strncatz( entry, "\"", sizeof( entry ) ); return entry; }
/* * G_Match_Autorecord_Stats */ void G_Match_Autorecord_Stats( void ) { edict_t *ent; for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients; ent++ ) { if( !ent->r.inuse || ent->s.team == TEAM_SPECTATOR || ( ent->r.svflags & SVF_FAKECLIENT ) ) continue; trap_GameCmd( ent, va( "plstats 2 \"%s\"", G_StatsMessage( ent ) ) ); } }
void G_AwardResetPlayerComboStats( edict_t *ent ) { int i; int resetvalue; // combo from LB can be cancelled only if player's dead, if he missed or if he hasnt shot with LB for too long resetvalue = ( G_IsDead( ent ) ? 0 : COMBO_FLAG( WEAP_LASERGUN ) ); for( i = 0; i < gs.maxclients; i++ ) game.clients[i].resp.awardInfo.combo[PLAYERNUM( ent )] &= resetvalue; }
/* * G_TickOutPowerUps */ static void G_TickOutPowerUps( void ) { edict_t *ent; gsitem_t *item; int i; for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients; ent++ ) { if( ent->r.inuse && trap_GetClientState( PLAYERNUM( ent ) ) >= CS_SPAWNED ) { for( i = POWERUP_QUAD; i < POWERUP_TOTAL; i++ ) { item = GS_FindItemByTag( i ); if( item && item->quantity && ent->r.client->ps.inventory[item->tag] > 0 ) { ent->r.client->ps.inventory[item->tag]--; } } } } // also tick out dropped powerups for( ent = game.edicts + gs.maxclients + BODY_QUEUE_SIZE; ENTNUM( ent ) < game.numentities; ent++ ) { if( !ent->r.inuse || !ent->item ) continue; if( !( ent->item->type & IT_POWERUP ) ) continue; if( ent->spawnflags & DROPPED_ITEM ) { ent->count--; if( ent->count <= 0 ) { G_FreeEdict( ent ); continue; } } } }