qboolean CheckFlood( edict_t *ent, qboolean teamonly ) { int i; gclient_t *client = ent->r.client; assert( ent && client ); if( g_floodprotection_messages->modified ) { if( g_floodprotection_messages->integer < 0 ) trap_Cvar_Set( "g_floodprotection_messages", "0" ); if( g_floodprotection_messages->integer > MAX_FLOOD_MESSAGES ) trap_Cvar_Set( "g_floodprotection_messages", va( "%i", MAX_FLOOD_MESSAGES ) ); g_floodprotection_messages->modified = qfalse; } if( g_floodprotection_team->modified ) { if( g_floodprotection_team->integer < 0 ) trap_Cvar_Set( "g_floodprotection_team", "0" ); if( g_floodprotection_team->integer > MAX_FLOOD_MESSAGES ) trap_Cvar_Set( "g_floodprotection_team", va( "%i", MAX_FLOOD_MESSAGES ) ); g_floodprotection_team->modified = qfalse; } if( g_floodprotection_seconds->modified ) { if( g_floodprotection_seconds->value <= 0 ) trap_Cvar_Set( "g_floodprotection_seconds", "4" ); g_floodprotection_seconds->modified = qfalse; } if( g_floodprotection_penalty->modified ) { if( g_floodprotection_penalty->value < 0 ) trap_Cvar_Set( "g_floodprotection_penalty", "10" ); g_floodprotection_penalty->modified = qfalse; } // old protection still active if( !teamonly || g_floodprotection_team->integer ) { if( game.realtime < client->level.flood_locktill ) { G_PrintMsg( ent, "You can't talk for %d more seconds\n", (int)( ( client->level.flood_locktill - game.realtime ) / 1000.0f ) + 1 ); return qtrue; } } if( teamonly ) { if( g_floodprotection_team->integer && g_floodprotection_penalty->value > 0 ) { i = client->level.flood_team_whenhead - g_floodprotection_team->integer + 1; if( i < 0 ) i = MAX_FLOOD_MESSAGES + i; if( client->level.flood_team_when[i] && client->level.flood_team_when[i] <= game.realtime && ( game.realtime < client->level.flood_team_when[i] + g_floodprotection_seconds->integer * 1000 ) ) { client->level.flood_locktill = game.realtime + g_floodprotection_penalty->value * 1000; G_PrintMsg( ent, "Flood protection: You can't talk for %d seconds.\n", g_floodprotection_penalty->integer ); return qtrue; } } client->level.flood_team_whenhead = ( client->level.flood_team_whenhead + 1 ) % MAX_FLOOD_MESSAGES; client->level.flood_team_when[client->level.flood_team_whenhead] = game.realtime; } else { if( g_floodprotection_messages->integer && g_floodprotection_penalty->value > 0 ) { i = client->level.flood_whenhead - g_floodprotection_messages->integer + 1; if( i < 0 ) i = MAX_FLOOD_MESSAGES + i; if( client->level.flood_when[i] && client->level.flood_when[i] <= game.realtime && ( game.realtime < client->level.flood_when[i] + g_floodprotection_seconds->integer * 1000 ) ) { client->level.flood_locktill = game.realtime + g_floodprotection_penalty->value * 1000; G_PrintMsg( ent, "Flood protection: You can't talk for %d seconds.\n", g_floodprotection_penalty->integer ); return qtrue; } } client->level.flood_whenhead = ( client->level.flood_whenhead + 1 ) % MAX_FLOOD_MESSAGES; client->level.flood_when[client->level.flood_whenhead] = game.realtime; } return qfalse; }
/* * Cmd_ChaseCam_f */ void Cmd_ChaseCam_f( edict_t *ent ) { bool team_only; const char *arg1; if( ent->s.team != TEAM_SPECTATOR && !ent->r.client->teamstate.is_coach ) { // racesow - changed from G_Teams_JoinTeam( ent, TEAM_SPECTATOR ) as this makes // a second G_ChasePlayer call with followmode 0, thereby overriding the chasecam mode set here ent->r.client->team = TEAM_SPECTATOR; G_ClientRespawn( ent, true ); // !racesow if( !CheckFlood( ent, false )) { // prevent 'joined spectators' spam G_PrintMsg( NULL, "%s%s joined the %s%s team.\n", ent->r.client->netname, S_COLOR_WHITE, GS_TeamName( ent->s.team ), S_COLOR_WHITE ); } } // & 1 = scorelead // & 2 = powerups // & 4 = objectives // & 8 = fragger if( ent->r.client->teamstate.is_coach && GS_TeamBasedGametype() ) team_only = true; else team_only = false; arg1 = trap_Cmd_Argv( 1 ); if( trap_Cmd_Argc() < 2 ) { G_ChasePlayer( ent, NULL, team_only, 0 ); } else if( !Q_stricmp( arg1, "auto" ) ) { G_PrintMsg( ent, "Chasecam mode is 'auto'. It will follow the score leader when no powerup nor flag is carried.\n" ); G_ChasePlayer( ent, NULL, team_only, 7 ); } else if( !Q_stricmp( arg1, "carriers" ) ) { G_PrintMsg( ent, "Chasecam mode is 'carriers'. It will switch to flag or powerup carriers when any of these items is picked up.\n" ); G_ChasePlayer( ent, NULL, team_only, 6 ); } else if( !Q_stricmp( arg1, "powerups" ) ) { G_PrintMsg( ent, "Chasecam mode is 'powerups'. It will switch to powerup carriers when any of these items is picked up.\n" ); G_ChasePlayer( ent, NULL, team_only, 2 ); } else if( !Q_stricmp( arg1, "objectives" ) ) { G_PrintMsg( ent, "Chasecam mode is 'objectives'. It will switch to objectives carriers when any of these items is picked up.\n" ); G_ChasePlayer( ent, NULL, team_only, 4 ); } else if( !Q_stricmp( arg1, "score" ) ) { G_PrintMsg( ent, "Chasecam mode is 'score'. It will always follow the player with the best score.\n" ); G_ChasePlayer( ent, NULL, team_only, 1 ); } else if( !Q_stricmp( arg1, "fragger" ) ) { G_PrintMsg( ent, "Chasecam mode is 'fragger'. The last fragging player will be followed.\n" ); G_ChasePlayer( ent, NULL, team_only, 8 ); } else if( !Q_stricmp( arg1, "help" ) ) { G_PrintMsg( ent, "Chasecam modes:\n" ); G_PrintMsg( ent, "- 'auto': Chase the score leader unless there's an objective carrier or a powerup carrier.\n" ); G_PrintMsg( ent, "- 'carriers': User has pov control unless there's an objective carrier or a powerup carrier.\n" ); G_PrintMsg( ent, "- 'objectives': User has pov control unless there's an objective carrier.\n" ); G_PrintMsg( ent, "- 'powerups': User has pov control unless there's a flag carrier.\n" ); G_PrintMsg( ent, "- 'score': Always follow the score leader. User has no pov control.\n" ); G_PrintMsg( ent, "- 'none': Disable chasecam.\n" ); return; } else { G_ChasePlayer( ent, arg1, team_only, 0 ); } G_Teams_LeaveChallengersQueue( ent ); }
/* * Cmd_Position_f */ static void Cmd_Position_f( edict_t *ent ) { char *action; if( !sv_cheats->integer && GS_MatchState() > MATCH_STATE_WARMUP && ent->r.client->ps.pmove.pm_type != PM_SPECTATOR ) { G_PrintMsg( ent, "Position command is only available in warmup and in spectator mode.\n" ); return; } // flood protect if( ent->r.client->teamstate.position_lastcmd + 500 > game.realtime ) return; ent->r.client->teamstate.position_lastcmd = game.realtime; action = trap_Cmd_Argv( 1 ); if( !Q_stricmp( action, "save" ) ) { ent->r.client->teamstate.position_saved = qtrue; VectorCopy( ent->s.origin, ent->r.client->teamstate.position_origin ); VectorCopy( ent->s.angles, ent->r.client->teamstate.position_angles ); G_PrintMsg( ent, "Position saved.\n" ); } else if( !Q_stricmp( action, "load" ) ) { if( !ent->r.client->teamstate.position_saved ) { G_PrintMsg( ent, "No position saved.\n" ); } else { if( ent->r.client->resp.chase.active ) G_SpectatorMode( ent ); if( G_Teleport( ent, ent->r.client->teamstate.position_origin, ent->r.client->teamstate.position_angles ) ) G_PrintMsg( ent, "Position loaded.\n" ); else G_PrintMsg( ent, "Position not available.\n" ); } } else if( !Q_stricmp( action, "set" ) && trap_Cmd_Argc() == 7 ) { vec3_t origin, angles; int i, argnumber = 2; for( i = 0; i < 3; i++ ) origin[i] = atof( trap_Cmd_Argv( argnumber++ ) ); for( i = 0; i < 2; i++ ) angles[i] = atof( trap_Cmd_Argv( argnumber++ ) ); angles[2] = 0; if( ent->r.client->resp.chase.active ) G_SpectatorMode( ent ); if( G_Teleport( ent, origin, angles ) ) G_PrintMsg( ent, "Position not available.\n" ); else G_PrintMsg( ent, "Position set.\n" ); } else { char msg[MAX_STRING_CHARS]; msg[0] = 0; Q_strncatz( msg, "Usage:\nposition save - Save current position\n", sizeof( msg ) ); Q_strncatz( msg, "position load - Teleport to saved position\n", sizeof( msg ) ); Q_strncatz( msg, "position set <x> <y> <z> <pitch> <yaw> - Teleport to specified position\n", sizeof( msg ) ); Q_strncatz( msg, va( "Current position: %.4f %.4f %.4f %.4f %.4f\n", ent->s.origin[0], ent->s.origin[1], ent->s.origin[2], ent->s.angles[0], ent->s.angles[1] ), sizeof( msg ) ); G_PrintMsg( ent, msg ); } }
/* * G_MoveClientToTV * * Sends cmd to connect to a non-full TV server with round robin balancing */ void G_MoveClientToTV( edict_t *ent ) { int i; gclient_t *client, *best; static int last_tv = 0; bool isIPv6; char ip[MAX_INFO_VALUE]; int port; const char *p; if( !ent->r.client ) { return; } if( ent->r.svflags & SVF_FAKECLIENT ) { return; } if( ent->r.client->isTV ) { return; } best = NULL; for( i = 0; i < gs.maxclients; i++ ) { client = &game.clients[(last_tv + 1 + i) % gs.maxclients]; if( !client->isTV || client->connecting ) { // not a TV or not ready yet continue; } if( client->tv.numclients == client->tv.maxclients ) { // full continue; } if( !client->tv.channel ) { // invalid userinfo/channel number continue; } best = client; break; } if( !best ) { G_PrintMsg( ent, "Could not find a free TV server\n" ); return; } Q_strncpyz( ip, best->ip, sizeof( ip ) ); // check IP type p = strstr( ip, "::" ); isIPv6 = p != NULL; // strip port number from address string p = strrchr( ip, ':' ); if( p != NULL ) { ip[p - ip] = '\0'; } port = isIPv6 ? best->tv.port6 : best->tv.port; last_tv = best - game.clients; trap_GameCmd( ent, va( "memo tv_moveto \"%s\" %s:%hu#%i", COM_RemoveColorTokens( best->netname ), ip, port, best->tv.channel ) ); }
/* * G_ChasePlayer */ void G_ChasePlayer( edict_t *ent, const char *name, bool teamonly, int followmode ) { int i; edict_t *e; gclient_t *client; int targetNum = -1; int oldTarget; bool can_follow = true; char colorlessname[MAX_NAME_BYTES]; client = ent->r.client; oldTarget = client->resp.chase.target; if( teamonly && !client->teamstate.is_coach ) can_follow = false; if( !can_follow && followmode ) { G_PrintMsg( ent, "Chasecam follow mode unavailable\n" ); followmode = false; } if( ent->r.client->resp.chase.followmode && !followmode ) G_PrintMsg( ent, "Disabling chasecam follow mode\n" ); // always disable chasing as a start memset( &client->resp.chase, 0, sizeof( chasecam_t ) ); // locate the requested target if( name && name[0] ) { // find it by player names for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !G_Chase_IsValidTarget( ent, e, teamonly ) ) continue; Q_strncpyz( colorlessname, COM_RemoveColorTokens( e->r.client->netname ), sizeof(colorlessname) ); if( !Q_stricmp( COM_RemoveColorTokens( name ), colorlessname ) ) { targetNum = PLAYERNUM( e ); break; } } // didn't find it by name, try by numbers if( targetNum == -1 ) { i = atoi( name ); if( i >= 0 && i < gs.maxclients ) { e = game.edicts + 1 + i; if( G_Chase_IsValidTarget( ent, e, teamonly ) ) targetNum = PLAYERNUM( e ); } } if( targetNum == -1 ) G_PrintMsg( ent, "Requested chasecam target is not available\n" ); } // try to reuse old target if we didn't find a valid one if( targetNum == -1 && oldTarget > 0 && oldTarget < gs.maxclients ) { e = game.edicts + 1 + oldTarget; if( G_Chase_IsValidTarget( ent, e, teamonly ) ) targetNum = PLAYERNUM( e ); } // if we still don't have a target, just pick the first valid one if( targetNum == -1 ) { for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !G_Chase_IsValidTarget( ent, e, teamonly ) ) continue; targetNum = PLAYERNUM( e ); break; } } // make the client a ghost G_GhostClient( ent ); if( targetNum != -1 ) { // we found a target, set up the chasecam client->resp.chase.target = targetNum + 1; client->resp.chase.teamonly = teamonly; client->resp.chase.followmode = followmode; G_Chase_SetChaseActive( ent, true ); } else { // stay as observer if( !teamonly ) ent->movetype = MOVETYPE_NOCLIP; client->level.showscores = false; G_Chase_SetChaseActive( ent, false ); G_CenterPrintMsg( ent, "No one to chase" ); } }
/* * G_Match_CheckReadys */ void G_Match_CheckReadys( void ) { edict_t *e; bool allready; int readys, notreadys, teamsready; int team, i; if( GS_MatchState() != MATCH_STATE_WARMUP && GS_MatchState() != MATCH_STATE_COUNTDOWN ) return; if( GS_MatchState() == MATCH_STATE_COUNTDOWN && level.forceStart ) return; // never stop countdown if we have run out of warmup_timelimit teamsready = 0; for( team = TEAM_PLAYERS; team < GS_MAX_TEAMS; team++ ) { readys = notreadys = 0; for( i = 0; i < teamlist[team].numplayers; i++ ) { e = game.edicts + teamlist[team].playerIndices[i]; if( !e->r.inuse ) continue; if( e->s.team == TEAM_SPECTATOR ) //ignore spectators continue; if( level.ready[PLAYERNUM( e )] ) readys++; else notreadys++; } if( !notreadys && readys ) teamsready++; } // everyone has commited if( GS_TeamBasedGametype() ) { if( teamsready == GS_MAX_TEAMS - TEAM_ALPHA ) allready = true; else allready = false; } else { //ffa if( teamsready && teamlist[TEAM_PLAYERS].numplayers > 1 ) allready = true; else allready = false; } if( allready == true && GS_MatchState() != MATCH_STATE_COUNTDOWN ) { G_PrintMsg( NULL, "All players are ready. Match starting!\n" ); G_Match_LaunchState( MATCH_STATE_COUNTDOWN ); } else if( allready == false && GS_MatchState() == MATCH_STATE_COUNTDOWN ) { G_PrintMsg( NULL, "Countdown aborted.\n" ); G_CenterPrintMsg( NULL, "COUNTDOWN ABORTED" ); G_Match_Autorecord_Cancel(); G_Match_LaunchState( MATCH_STATE_WARMUP ); } }
/* * ClientConnect * Called when a player begins connecting to the server. * The game can refuse entrance to a client by returning false. * If the client is allowed, the connection process will continue * and eventually get to ClientBegin() * Changing levels will NOT cause this to be called again, but * loadgames will. */ bool ClientConnect( edict_t *ent, char *userinfo, bool fakeClient, bool tvClient ) { char *value; assert( ent ); assert( userinfo && Info_Validate( userinfo ) ); assert( Info_ValueForKey( userinfo, "ip" ) && Info_ValueForKey( userinfo, "socket" ) ); // verify that server gave us valid data if( !Info_Validate( userinfo ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Invalid userinfo" ); return false; } if( !Info_ValueForKey( userinfo, "ip" ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Error: Server didn't provide client IP" ); return false; } if( !Info_ValueForKey( userinfo, "ip" ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Error: Server didn't provide client socket" ); return false; } // check to see if they are on the banned IP list value = Info_ValueForKey( userinfo, "ip" ); if( SV_FilterPacket( value ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "You're banned from this server" ); return false; } // check for a password value = Info_ValueForKey( userinfo, "password" ); if( !fakeClient && ( *password->string && ( !value || strcmp( password->string, value ) ) ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_PASSWORD ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); if( value && value[0] ) { Info_SetValueForKey( userinfo, "rejmsg", "Incorrect password" ); } else { Info_SetValueForKey( userinfo, "rejmsg", "Password required" ); } return false; } // they can connect G_InitEdict( ent ); ent->s.modelindex = 0; ent->r.solid = SOLID_NOT; ent->r.client = game.clients + PLAYERNUM( ent ); ent->r.svflags = ( SVF_NOCLIENT | ( fakeClient ? SVF_FAKECLIENT : 0 ) ); memset( ent->r.client, 0, sizeof( gclient_t ) ); ent->r.client->ps.playerNum = PLAYERNUM( ent ); ent->r.client->connecting = true; ent->r.client->isTV = tvClient == true; ent->r.client->team = TEAM_SPECTATOR; G_Client_UpdateActivity( ent->r.client ); // activity detected ClientUserinfoChanged( ent, userinfo ); if( !fakeClient ) { char message[MAX_STRING_CHARS]; Q_snprintfz( message, sizeof( message ), "%s%s connected", ent->r.client->netname, S_COLOR_WHITE ); G_PrintMsg( NULL, "%s\n", message ); G_Printf( "%s%s connected from %s\n", ent->r.client->netname, S_COLOR_WHITE, ent->r.client->ip ); } // let the gametype scripts know this client just connected G_Gametype_ScoreEvent( ent->r.client, "connect", NULL ); G_CallVotes_ResetClient( PLAYERNUM( ent ) ); return true; }
/* * Cmd_PlayersExt_f */ static void Cmd_PlayersExt_f( edict_t *ent, bool onlyspecs ) { int i; int count = 0; int start = 0; char line[64]; char msg[1024]; if( trap_Cmd_Argc() > 1 ) start = atoi( trap_Cmd_Argv( 1 ) ); clamp( start, 0, gs.maxclients - 1 ); // print information msg[0] = 0; for( i = start; i < gs.maxclients; i++ ) { if( trap_GetClientState( i ) >= CS_SPAWNED ) { edict_t *clientEnt = &game.edicts[i+1]; gclient_t *cl; const char *login; if( onlyspecs && clientEnt->s.team != TEAM_SPECTATOR ) continue; cl = clientEnt->r.client; login = NULL; if( cl->mm_session > 0 ) { login = Info_ValueForKey( cl->userinfo, "cl_mm_login" ); } if( !login ) { login = ""; } Q_snprintfz( line, sizeof( line ), "%3i %s" S_COLOR_WHITE "%s%s%s%s\n", i, cl->netname, login[0] ? "(" S_COLOR_YELLOW : "", login, login[0] ? S_COLOR_WHITE ")" : "", cl->isoperator ? " op" : "" ); 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 ) ); G_PrintMsg( ent, "%s", msg ); if( i < gs.maxclients ) G_PrintMsg( ent, "Type '%s %i' for more %s\n", trap_Cmd_Argv( 0 ), i, trap_Cmd_Argv( 0 ) ); }
void G_AwardPlayerKilled( edict_t *self, edict_t *inflictor, edict_t *attacker, int mod ) { trace_t trace; score_stats_t *stats; loggedFrag_t *lfrag; if( self->r.svflags & SVF_CORPSE ) return; if( !attacker->r.client ) return; if( !self->r.client ) return; if( attacker == self ) return; if( attacker->s.team == self->s.team && attacker->s.team > TEAM_PLAYERS ) return; if( mod == MOD_ROCKET_W || mod == MOD_ROCKET_S ) { // direct hit attacker->r.client->resp.awardInfo.directrocket_count++; if( attacker->r.client->resp.awardInfo.directrocket_count == DIRECTROCKET_FOR_AWARD ) { attacker->r.client->resp.awardInfo.directrocket_count = 0; attacker->r.client->resp.awardInfo.directrocket_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Direct Rocket Hit!" ); } // Midair if( self->groundentity == NULL && !self->waterlevel ) { // check for height to the ground G_Trace( &trace, self->s.origin, self->r.mins, self->r.maxs, tv( self->s.origin[0], self->s.origin[1], self->s.origin[2] - 64 ), self, MASK_SOLID ); if( trace.fraction == 1.0f ) { attacker->r.client->resp.awardInfo.rl_midair_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Air Rocket!" ); } } } if( mod == MOD_GRENADE_W || mod == MOD_GRENADE_S ) { // direct hit attacker->r.client->resp.awardInfo.directgrenade_count++; if( attacker->r.client->resp.awardInfo.directgrenade_count == DIRECTGRENADE_FOR_AWARD ) { attacker->r.client->resp.awardInfo.directgrenade_count = 0; attacker->r.client->resp.awardInfo.directgrenade_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Direct Grenade Hit!" ); } // Midair if( self->groundentity == NULL && !self->waterlevel ) { // check for height to the ground G_Trace( &trace, self->s.origin, self->r.mins, self->r.maxs, tv( self->s.origin[0], self->s.origin[1], self->s.origin[2] - 64 ), self, MASK_SOLID ); if( trace.fraction == 1.0f ) { attacker->r.client->resp.awardInfo.gl_midair_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Air Grenade!" ); } } } // Multikill if( game.serverTime - attacker->r.client->resp.awardInfo.multifrag_timer < MULTIKILL_INTERVAL ) attacker->r.client->resp.awardInfo.multifrag_count++; else attacker->r.client->resp.awardInfo.multifrag_count = 1; attacker->r.client->resp.awardInfo.multifrag_timer = game.serverTime; if( attacker->r.client->resp.awardInfo.multifrag_count > 1 ) { char s[MAX_CONFIGSTRING_CHARS]; s[0] = 0; switch( attacker->r.client->resp.awardInfo.multifrag_count ) { case 0: case 1: break; case 2: Q_strncpyz( s, S_COLOR_GREEN "Double Frag!", sizeof( s ) ); break; case 3: Q_strncpyz( s, S_COLOR_GREEN "Triple Frag!", sizeof( s ) ); break; case 4: Q_strncpyz( s, S_COLOR_GREEN "Quadruple Frag!", sizeof( s ) ); break; default: Q_snprintfz( s, sizeof( s ), S_COLOR_GREEN "Extermination! %i in a row!", attacker->r.client->resp.awardInfo.multifrag_count ); break; } G_PlayerAward( attacker, s ); } // Sprees attacker->r.client->resp.awardInfo.frag_count++; if( attacker->r.client->resp.awardInfo.frag_count && ( attacker->r.client->resp.awardInfo.frag_count % 5 == 0 ) ) { char s[MAX_CONFIGSTRING_CHARS]; s[0] = 0; switch( (int)( attacker->r.client->resp.awardInfo.frag_count / 5 ) ) { case 1: Q_strncpyz( s, S_COLOR_YELLOW "On Fire!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is On Fire!\n", attacker->r.client->netname ); break; case 2: Q_strncpyz( s, S_COLOR_YELLOW "Raging!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is Raging!\n", attacker->r.client->netname ); break; case 3: Q_strncpyz( s, S_COLOR_YELLOW "Fraglord!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is the Fraglord!\n", attacker->r.client->netname ); break; case 4: Q_strncpyz( s, S_COLOR_YELLOW "Extermination!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is Exterminating!\n", attacker->r.client->netname ); break; default: Q_strncpyz( s, S_COLOR_YELLOW "God Mode!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is in God Mode!\n", attacker->r.client->netname ); break; } G_PlayerAward( attacker, s ); } if( teamlist[attacker->s.team].stats.frags == 1 ) { int i; for( i = TEAM_PLAYERS; i < GS_MAX_TEAMS; i++ ) { if( i == attacker->s.team ) continue; if( teamlist[i].stats.frags ) break; } if( i != GS_MAX_TEAMS ) G_PlayerAward( attacker, S_COLOR_YELLOW "First Frag!" ); } // ch : weapon specific frags if ( G_ModToAmmo( mod ) != AMMO_NONE ) attacker->r.client->level.stats.accuracy_frags[G_ModToAmmo( mod )-AMMO_GUNBLADE]++; if( GS_MatchState() == MATCH_STATE_PLAYTIME /* && !strcmp( "duel", gs.gametypeName ) */) { // ch : frag log stats = &attacker->r.client->level.stats; if( !stats->fragAllocator ) stats->fragAllocator = LinearAllocator( sizeof( loggedFrag_t ), 0, _G_LevelMalloc, _G_LevelFree ); lfrag = ( loggedFrag_t * )LA_Alloc( stats->fragAllocator ); lfrag->mm_attacker = attacker->r.client->mm_session; lfrag->mm_victim = self->r.client->mm_session; lfrag->weapon = G_ModToAmmo( mod ) - AMMO_GUNBLADE; lfrag->time = ( game.serverTime - GS_MatchStartTime() ) / 1000; } }
void G_AwardPlayerKilled( edict_t *self, edict_t *inflictor, edict_t *attacker, int mod ) { trace_t trace; if( self->r.svflags & SVF_CORPSE ) return; if( !attacker->r.client ) return; if( !self->r.client ) return; if( attacker == self ) return; if( attacker->s.team == self->s.team && attacker->s.team > TEAM_PLAYERS ) return; if( mod == MOD_ROCKET_W || mod == MOD_ROCKET_S ) { // direct hit attacker->r.client->resp.awardInfo.directrocket_count++; if( attacker->r.client->resp.awardInfo.directrocket_count == DIRECTROCKET_FOR_AWARD ) { attacker->r.client->resp.awardInfo.directrocket_count = 0; attacker->r.client->resp.awardInfo.directrocket_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Direct Rocket Hit!" ); } // Midair if( self->groundentity == NULL && !self->waterlevel ) { // check for height to the ground G_Trace( &trace, self->s.origin, self->r.mins, self->r.maxs, tv( self->s.origin[0], self->s.origin[1], self->s.origin[2] - 64 ), self, MASK_SOLID ); if( trace.fraction == 1.0f ) { attacker->r.client->resp.awardInfo.rl_midair_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Air Rocket!" ); } } } if( mod == MOD_GRENADE_W || mod == MOD_GRENADE_S ) { // direct hit attacker->r.client->resp.awardInfo.directgrenade_count++; if( attacker->r.client->resp.awardInfo.directgrenade_count == DIRECTGRENADE_FOR_AWARD ) { attacker->r.client->resp.awardInfo.directgrenade_count = 0; attacker->r.client->resp.awardInfo.directgrenade_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Direct Grenade Hit!" ); } // Midair if( self->groundentity == NULL && !self->waterlevel ) { // check for height to the ground G_Trace( &trace, self->s.origin, self->r.mins, self->r.maxs, tv( self->s.origin[0], self->s.origin[1], self->s.origin[2] - 64 ), self, MASK_SOLID ); if( trace.fraction == 1.0f ) { attacker->r.client->resp.awardInfo.gl_midair_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Air Grenade!" ); } } } // Multikill if( game.serverTime - attacker->r.client->resp.awardInfo.multifrag_timer < MULTIKILL_INTERVAL ) attacker->r.client->resp.awardInfo.multifrag_count++; else attacker->r.client->resp.awardInfo.multifrag_count = 1; attacker->r.client->resp.awardInfo.multifrag_timer = game.serverTime; if( attacker->r.client->resp.awardInfo.multifrag_count > 1 ) { char s[MAX_CONFIGSTRING_CHARS]; s[0] = 0; switch( attacker->r.client->resp.awardInfo.multifrag_count ) { case 0: case 1: break; case 2: Q_strncpyz( s, S_COLOR_GREEN "Double Frag!", sizeof( s ) ); break; case 3: Q_strncpyz( s, S_COLOR_GREEN "Triple Frag!", sizeof( s ) ); break; case 4: Q_strncpyz( s, S_COLOR_GREEN "Quadruple Frag!", sizeof( s ) ); break; default: Q_snprintfz( s, sizeof( s ), S_COLOR_GREEN "Extermination! %i in a row!", attacker->r.client->resp.awardInfo.multifrag_count ); break; } G_PlayerAward( attacker, s ); } // Sprees attacker->r.client->resp.awardInfo.frag_count++; if( attacker->r.client->resp.awardInfo.frag_count && ( attacker->r.client->resp.awardInfo.frag_count % 5 == 0 ) ) { char s[MAX_CONFIGSTRING_CHARS]; s[0] = 0; switch( (int)( attacker->r.client->resp.awardInfo.frag_count / 5 ) ) { case 1: Q_strncpyz( s, S_COLOR_YELLOW "On Fire!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is On Fire!\n", attacker->r.client->netname ); break; case 2: Q_strncpyz( s, S_COLOR_YELLOW "Raging!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is Raging!\n", attacker->r.client->netname ); break; case 3: Q_strncpyz( s, S_COLOR_YELLOW "Fraglord!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is the Fraglord!\n", attacker->r.client->netname ); break; case 4: Q_strncpyz( s, S_COLOR_YELLOW "Extermination!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is Exterminating!\n", attacker->r.client->netname ); break; default: Q_strncpyz( s, S_COLOR_YELLOW "God Mode!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is in God Mode!\n", attacker->r.client->netname ); break; } G_PlayerAward( attacker, s ); } }
/* * G_Timeout_Update * * Updates the timeout struct and informs clients about the status of the pause */ static void G_Timeout_Update( unsigned int msec ) { static int timeout_printtime = 0; static int timeout_last_endtime = 0; static int countdown_set = 1; if( !GS_MatchPaused() ) return; game.frametime = 0; if( timeout_last_endtime != level.timeout.endtime ) // force print when endtime is changed { timeout_printtime = 0; timeout_last_endtime = level.timeout.endtime; } level.timeout.time += msec; if( level.timeout.endtime && level.timeout.time >= level.timeout.endtime ) { level.timeout.time = 0; level.timeout.caller = -1; GS_GamestatSetFlag( GAMESTAT_FLAG_PAUSED, false ); timeout_printtime = 0; timeout_last_endtime = -1; G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_MATCH_RESUMED_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); G_CenterPrintMsg( NULL, "Match resumed" ); G_PrintMsg( NULL, "Match resumed\n" ); } else if( timeout_printtime == 0 || level.timeout.time - timeout_printtime >= 1000 ) { if( level.timeout.endtime ) { int seconds_left = (int)( ( level.timeout.endtime - level.timeout.time ) / 1000.0 + 0.5 ); if( seconds_left == ( TIMEIN_TIME * 2 ) / 1000 ) { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_READY_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, false, NULL ); countdown_set = ( rand()&1 )+1; } else if( seconds_left >= 1 && seconds_left <= 3 ) { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_COUNT_1_to_3_SET_1_to_2, seconds_left, countdown_set ) ), GS_MAX_TEAMS, false, NULL ); } if( seconds_left > 1 ) G_CenterPrintFormatMsg( NULL, "Match will resume in %s seconds", va( "%i", seconds_left ), NULL ); else G_CenterPrintMsg( NULL, "Match will resume in 1 second" ); } else { G_CenterPrintMsg( NULL, "Match paused" ); } timeout_printtime = level.timeout.time; } }
/* * Cmd_ChaseCam_f */ void Cmd_ChaseCam_f( edict_t *ent ) { qboolean team_only; if( ent->s.team != TEAM_SPECTATOR && !ent->r.client->teamstate.is_coach ) { G_Teams_JoinTeam( ent, TEAM_SPECTATOR ); G_PrintMsg( NULL, "%s%s joined the %s%s team.\n", ent->r.client->netname, S_COLOR_WHITE, GS_TeamName( ent->s.team ), S_COLOR_WHITE ); } // & 1 = scorelead // & 2 = powerups // & 4 = flags if( ent->r.client->teamstate.is_coach && GS_TeamBasedGametype() ) team_only = qtrue; else team_only = qfalse; if( trap_Cmd_Argc() < 2 ) { G_ChasePlayer( ent, NULL, team_only, 0 ); } else if( !Q_stricmp( trap_Cmd_Argv( 1 ), "auto" ) ) { G_PrintMsg( ent, "Chasecam mode is 'auto'. It will follow the score leader when no powerup nor flag is carried.\n" ); G_ChasePlayer( ent, NULL, team_only, 7 ); } else if( !Q_stricmp( trap_Cmd_Argv( 1 ), "carriers" ) ) { G_PrintMsg( ent, "Chasecam mode is 'carriers'. It will switch to flag or powerup carriers when any of these items is picked up.\n" ); G_ChasePlayer( ent, NULL, team_only, 6 ); } else if( !Q_stricmp( trap_Cmd_Argv( 1 ), "powerups" ) ) { G_PrintMsg( ent, "Chasecam mode is 'powerups'. It will switch to powerup carriers when any of these items is picked up.\n" ); G_ChasePlayer( ent, NULL, team_only, 2 ); } else if( !Q_stricmp( trap_Cmd_Argv( 1 ), "objectives" ) ) { G_PrintMsg( ent, "Chasecam mode is 'objectives'. It will switch to objectives carriers when any of these items is picked up.\n" ); G_ChasePlayer( ent, NULL, team_only, 4 ); } else if( !Q_stricmp( trap_Cmd_Argv( 1 ), "score" ) ) { G_PrintMsg( ent, "Chasecam mode is 'score'. It will always follow the highest fragger.\n" ); G_ChasePlayer( ent, NULL, team_only, 1 ); } else if( !Q_stricmp( trap_Cmd_Argv( 1 ), "help" ) ) { G_PrintMsg( ent, "Chasecam modes:\n" ); G_PrintMsg( ent, "- 'auto': Chase the score leader unless there's an objective carrier or a powerup carrier.\n" ); G_PrintMsg( ent, "- 'carriers': User has pov control unless there's an objective carrier or a powerup carrier.\n" ); G_PrintMsg( ent, "- 'objectives': User has pov control unless there's an objective carrier.\n" ); G_PrintMsg( ent, "- 'powerups': User has pov control unless there's a flag carrier.\n" ); G_PrintMsg( ent, "- 'score': Always follow the score leader. User has no pov control.\n" ); G_PrintMsg( ent, "- 'none': Disable chasecam.\n" ); return; } else { G_ChasePlayer( ent, trap_Cmd_Argv( 1 ), team_only, 0 ); } G_Teams_LeaveChallengersQueue( ent ); }
void G_DeathAwards( edict_t *ent ) { int frag_count = ent->r.client->resp.awardInfo.frag_count; if( frag_count >= 5 ) G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " made a spree of " S_COLOR_WHITE "%d" S_COLOR_YELLOW "!\n", ent->r.client->netname, frag_count ); }
/* * G_Match_CheckStateAbort */ static void G_Match_CheckStateAbort( void ) { bool any = false; bool enough; if( GS_MatchState() <= MATCH_STATE_NONE || GS_MatchState() >= MATCH_STATE_POSTMATCH || level.gametype.mathAbortDisabled ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, false ); return; } if( GS_TeamBasedGametype() ) { int team, emptyteams = 0; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { if( !teamlist[team].numplayers ) emptyteams++; else any = true; } enough = ( emptyteams == 0 ); } else { enough = ( teamlist[TEAM_PLAYERS].numplayers > 1 ); any = ( teamlist[TEAM_PLAYERS].numplayers > 0 ); } // if waiting, turn on match states when enough players joined if( GS_MatchWaiting() && enough ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, false ); G_UpdatePlayersMatchMsgs(); } // turn off active match states if not enough players left else if( GS_MatchState() == MATCH_STATE_WARMUP && !enough && GS_MatchDuration() ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, true ); G_UpdatePlayersMatchMsgs(); } else if( GS_MatchState() == MATCH_STATE_COUNTDOWN && !enough ) { if( any ) { G_PrintMsg( NULL, "Not enough players left. Countdown aborted.\n" ); G_CenterPrintMsg( NULL, "COUNTDOWN ABORTED" ); } G_Match_Autorecord_Cancel(); G_Match_LaunchState( MATCH_STATE_WARMUP ); GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, true ); G_UpdatePlayersMatchMsgs(); } // match running, but not enough players left else if( GS_MatchState() == MATCH_STATE_PLAYTIME && !enough ) { if( any ) { G_PrintMsg( NULL, "Not enough players left. Match aborted.\n" ); G_CenterPrintMsg( NULL, "MATCH ABORTED" ); } G_EndMatch(); } }
/* * Cmd_Give_f * * Give items to a client */ static void Cmd_Give_f( edict_t *ent ) { char *name; gsitem_t *it; int i; qboolean give_all; if( !sv_cheats->integer ) { G_PrintMsg( ent, "Cheats are not enabled on this server.\n" ); return; } name = trap_Cmd_Args(); if( !Q_stricmp( name, "all" ) ) give_all = qtrue; else give_all = qfalse; if( give_all || !Q_stricmp( trap_Cmd_Argv( 1 ), "health" ) ) { if( trap_Cmd_Argc() == 3 ) ent->health = atoi( trap_Cmd_Argv( 2 ) ); else ent->health = ent->max_health; if( !give_all ) return; } if( give_all || !Q_stricmp( name, "weapons" ) ) { for( i = 0; i < GS_MAX_ITEM_TAGS; i++ ) { it = GS_FindItemByTag( i ); if( !it ) continue; if( !( it->flags & ITFLAG_PICKABLE ) ) continue; if( !( it->type & IT_WEAPON ) ) continue; ent->r.client->ps.inventory[i] += 1; } if( !give_all ) return; } if( give_all || !Q_stricmp( name, "ammo" ) ) { for( i = 0; i < GS_MAX_ITEM_TAGS; i++ ) { it = GS_FindItemByTag( i ); if( !it ) continue; if( !( it->flags & ITFLAG_PICKABLE ) ) continue; if( !( it->type & IT_AMMO ) ) continue; Add_Ammo( ent->r.client, it, 1000, qtrue ); } if( !give_all ) return; } if( give_all || !Q_stricmp( name, "armor" ) ) { ent->r.client->resp.armor = GS_Armor_MaxCountForTag( ARMOR_RA ); if( !give_all ) return; } if( give_all ) { for( i = 0; i < GS_MAX_ITEM_TAGS; i++ ) { it = GS_FindItemByTag( i ); if( !it ) continue; if( !( it->flags & ITFLAG_PICKABLE ) ) continue; if( it->type & ( IT_ARMOR|IT_WEAPON|IT_AMMO ) ) continue; ent->r.client->ps.inventory[i] = 1; } return; } it = GS_FindItemByName( name ); if( !it ) { name = trap_Cmd_Argv( 1 ); it = GS_FindItemByName( name ); if( !it ) { G_PrintMsg( ent, "unknown item\n" ); return; } } if( !( it->flags & ITFLAG_PICKABLE ) ) { G_PrintMsg( ent, "non-pickup (givable) item\n" ); return; } if( it->type & IT_AMMO ) { if( trap_Cmd_Argc() == 3 ) ent->r.client->ps.inventory[it->tag] = atoi( trap_Cmd_Argv( 2 ) ); else ent->r.client->ps.inventory[it->tag] += it->quantity; } else { if( it->tag && ( it->tag > 0 ) && ( it->tag < GS_MAX_ITEM_TAGS ) ) { if( GS_FindItemByTag( it->tag ) != NULL ) ent->r.client->ps.inventory[it->tag]++; } else G_PrintMsg( ent, "non-pickup (givable) item\n" ); } }
/* * ClientUserinfoChanged * called whenever the player updates a userinfo variable. * * The game can override any of the settings in place * (forcing skins or names, etc) before copying it off. */ void ClientUserinfoChanged( edict_t *ent, char *userinfo ) { char *s; char oldname[MAX_INFO_VALUE]; gclient_t *cl; int rgbcolor, i; assert( ent && ent->r.client ); assert( userinfo && Info_Validate( userinfo ) ); // check for malformed or illegal info strings if( !Info_Validate( userinfo ) ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Invalid userinfo" ); return; } cl = ent->r.client; // ip s = Info_ValueForKey( userinfo, "ip" ); if( !s ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Server didn't provide client IP" ); return; } Q_strncpyz( cl->ip, s, sizeof( cl->ip ) ); // socket s = Info_ValueForKey( userinfo, "socket" ); if( !s ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Server didn't provide client socket" ); return; } Q_strncpyz( cl->socket, s, sizeof( cl->socket ) ); // color s = Info_ValueForKey( userinfo, "color" ); if( s ) rgbcolor = COM_ReadColorRGBString( s ); else rgbcolor = -1; if( rgbcolor != -1 ) { rgbcolor = COM_ValidatePlayerColor( rgbcolor ); Vector4Set( cl->color, COLOR_R( rgbcolor ), COLOR_G( rgbcolor ), COLOR_B( rgbcolor ), 255 ); } else { Vector4Set( cl->color, 255, 255, 255, 255 ); } // set name, it's validated and possibly changed first Q_strncpyz( oldname, cl->netname, sizeof( oldname ) ); G_SetName( ent, Info_ValueForKey( userinfo, "name" ) ); if( oldname[0] && Q_stricmp( oldname, cl->netname ) && !cl->isTV && !CheckFlood( ent, false ) ) G_PrintMsg( NULL, "%s%s is now known as %s%s\n", oldname, S_COLOR_WHITE, cl->netname, S_COLOR_WHITE ); if( !Info_SetValueForKey( userinfo, "name", cl->netname ) ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (name)" ); return; } // clan tag G_SetClan( ent, Info_ValueForKey( userinfo, "clan" ) ); // handedness s = Info_ValueForKey( userinfo, "hand" ); if( !s ) cl->hand = 2; else cl->hand = bound( atoi( s ), 0, 2 ); // handicap s = Info_ValueForKey( userinfo, "handicap" ); if( s ) { i = atoi( s ); if( i > 90 || i < 0 ) { G_PrintMsg( ent, "Handicap must be defined in the [0-90] range.\n" ); cl->handicap = 0; } else { cl->handicap = i; } } s = Info_ValueForKey( userinfo, "cg_movementStyle" ); if( s ) { i = bound( atoi( s ), 0, GS_MAXBUNNIES - 1 ); if( trap_GetClientState( PLAYERNUM(ent) ) < CS_SPAWNED ) { if( i != cl->movestyle ) cl->movestyle = cl->movestyle_latched = i; } else if( cl->movestyle_latched != cl->movestyle ) { G_PrintMsg( ent, "A movement style change is already in progress. Please wait.\n" ); } else if( i != cl->movestyle_latched ) { cl->movestyle_latched = i; if( cl->movestyle_latched != cl->movestyle ) { edict_t *switcher; switcher = G_Spawn(); switcher->think = think_MoveTypeSwitcher; switcher->nextThink = level.time + 10000; switcher->s.ownerNum = ENTNUM( ent ); G_PrintMsg( ent, "Movement style will change in 10 seconds.\n" ); } } } // update the movement features depending on the movestyle if( !G_ISGHOSTING( ent ) && g_allow_bunny->integer ) { if( cl->movestyle == GS_CLASSICBUNNY ) cl->ps.pmove.stats[PM_STAT_FEATURES] &= ~PMFEAT_FWDBUNNY; else cl->ps.pmove.stats[PM_STAT_FEATURES] |= PMFEAT_FWDBUNNY; } s = Info_ValueForKey( userinfo, "cg_noAutohop" ); if( s && s[0] ) { if( atoi( s ) != 0 ) cl->ps.pmove.stats[PM_STAT_FEATURES] &= ~PMFEAT_CONTINOUSJUMP; else cl->ps.pmove.stats[PM_STAT_FEATURES] |= PMFEAT_CONTINOUSJUMP; } #ifdef UCMDTIMENUDGE s = Info_ValueForKey( userinfo, "cl_ucmdTimeNudge" ); if( !s ) { cl->ucmdTimeNudge = 0; } else { cl->ucmdTimeNudge = atoi( s ); clamp( cl->ucmdTimeNudge, -MAX_UCMD_TIMENUDGE, MAX_UCMD_TIMENUDGE ); } #endif // mm session // TODO: remove the key after storing it to gclient_t ! s = Info_ValueForKey( userinfo, "cl_mm_session" ); cl->mm_session = ( s == NULL ) ? 0 : atoi( s ); s = Info_ValueForKey( userinfo, "mmflags" ); cl->mmflags = ( s == NULL ) ? 0 : strtoul( s, NULL, 10 ); // tv if( cl->isTV ) { s = Info_ValueForKey( userinfo, "tv_port" ); cl->tv.port = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "tv_port6" ); cl->tv.port6 = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "max_cl" ); cl->tv.maxclients = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "num_cl" ); cl->tv.numclients = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "chan" ); cl->tv.channel = s ? atoi( s ) : 0; } if( !G_ISGHOSTING( ent ) && trap_GetClientState( PLAYERNUM( ent ) ) >= CS_SPAWNED ) G_Client_AssignTeamSkin( ent, userinfo ); // save off the userinfo in case we want to check something later Q_strncpyz( cl->userinfo, userinfo, sizeof( cl->userinfo ) ); G_UpdatePlayerInfoString( PLAYERNUM( ent ) ); G_UpdateMMPlayerInfoString( PLAYERNUM( ent ) ); G_Gametype_ScoreEvent( cl, "userinfochanged", oldname ); }
/* * G_vsay_f */ static void G_vsay_f( edict_t *ent, qboolean team ) { edict_t *event = NULL; g_vsays_t *vsay; char *text = NULL; char *msg = trap_Cmd_Argv( 1 ); if( ent->r.client && ent->r.client->muted & 2 ) return; if( ( !GS_TeamBasedGametype() || GS_InvidualGameType() ) && ent->s.team != TEAM_SPECTATOR ) team = qfalse; if( !( ent->r.svflags & SVF_FAKECLIENT ) ) { // ignore flood checks on bots if( ent->r.client->level.last_vsay > game.realtime - 500 ) return; // ignore silently vsays in that come in rapid succession ent->r.client->level.last_vsay = game.realtime; if( CheckFlood( ent, qfalse ) ) return; } for( vsay = g_vsays; vsay->name; vsay++ ) { if( !Q_stricmp( msg, vsay->name ) ) { event = G_SpawnEvent( EV_VSAY, vsay->id, NULL ); text = vsay->message; break; } } if( event && text ) { char saystring[256]; event->r.svflags |= SVF_BROADCAST; // force sending even when not in PVS event->s.ownerNum = ent->s.number; if( team ) { event->s.team = ent->s.team; event->r.svflags |= SVF_ONLYTEAM; // send only to clients with the same ->s.team value } if( trap_Cmd_Argc() > 2 ) { int i; saystring[0] = 0; for( i = 2; i < trap_Cmd_Argc(); i++ ) { Q_strncatz( saystring, trap_Cmd_Argv( i ), sizeof( saystring ) ); Q_strncatz( saystring, " ", sizeof( saystring ) ); } text = saystring; } if( team ) G_Say_Team( ent, va( "(v) %s", text ), qfalse ); else G_ChatMsg( NULL, ent, qfalse, "(v) %s", text ); return; } // unknown token, print help { char string[MAX_STRING_CHARS]; // print information string[0] = 0; if( msg && strlen( msg ) > 0 ) Q_strncatz( string, va( "%sUnknown vsay token%s \"%s\"\n", S_COLOR_YELLOW, S_COLOR_WHITE, msg ), sizeof( string ) ); Q_strncatz( string, va( "%svsays:%s\n", S_COLOR_YELLOW, S_COLOR_WHITE ), sizeof( string ) ); for( vsay = g_vsays; vsay->name; vsay++ ) { if( strlen( vsay->name ) + strlen( string ) < sizeof( string ) - 6 ) { Q_strncatz( string, va( "%s ", vsay->name ), sizeof( string ) ); } } Q_strncatz( string, "\n", sizeof( string ) ); G_PrintMsg( ent, string ); } }
/* * G_Teams_JoinAnyTeam - find us a team since we are too lazy to do ourselves */ bool G_Teams_JoinAnyTeam( edict_t *ent, bool silent ) { int best_numplayers = gs.maxclients + 1, best_score = 999999; int i, team = -1; bool wasinqueue = ( ent->r.client->queueTimeStamp != 0 ); G_Teams_UpdateMembersList(); // make sure we have up-to-date data //depending on the gametype, of course if( !GS_TeamBasedGametype() ) { if( ent->s.team == TEAM_PLAYERS ) { if( !silent ) { G_PrintMsg( ent, "You are already in %s team\n", GS_TeamName( TEAM_PLAYERS ) ); } return false; } if( G_Teams_JoinTeam( ent, TEAM_PLAYERS ) ) { if( !silent ) { G_PrintMsg( NULL, "%s%s joined the %s team.\n", ent->r.client->netname, S_COLOR_WHITE, GS_TeamName( ent->s.team ) ); } } return true; } else { //team based //find the available team with smaller player count or worse score for( i = TEAM_ALPHA; i < GS_MAX_TEAMS; i++ ) { if( G_GameTypes_DenyJoinTeam( ent, i ) ) { continue; } if( team == -1 || teamlist[i].numplayers < best_numplayers || ( teamlist[i].numplayers == best_numplayers && teamlist[i].stats.score < best_score ) ) { best_numplayers = teamlist[i].numplayers; best_score = teamlist[i].stats.score; team = i; } } if( team == ent->s.team ) { // he is at the right team if( !silent ) { G_PrintMsg( ent, "%sCouldn't find a better team than team %s.\n", S_COLOR_WHITE, GS_TeamName( ent->s.team ) ); } return false; } if( team != -1 ) { if( G_Teams_JoinTeam( ent, team ) ) { if( !silent ) { G_PrintMsg( NULL, "%s%s joined the %s team.\n", ent->r.client->netname, S_COLOR_WHITE, GS_TeamName( ent->s.team ) ); } return true; } } if( GS_MatchState() <= MATCH_STATE_PLAYTIME && !silent ) G_Teams_JoinChallengersQueue( ent ); } // don't print message if we joined the queue if( !silent && ( !GS_HasChallengers() || wasinqueue || !ent->r.client->queueTimeStamp ) ) G_PrintMsg( ent, "You can't join the game now\n" ); return false; }