diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cgame/cg_draw.c | 31 | ||||
-rw-r--r-- | src/game/g_active.c | 31 | ||||
-rw-r--r-- | src/game/g_client.c | 11 | ||||
-rw-r--r-- | src/game/g_cmds.c | 255 | ||||
-rw-r--r-- | src/game/g_local.h | 2 | ||||
-rw-r--r-- | src/game/g_main.c | 4 |
6 files changed, 298 insertions, 36 deletions
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c index fa046dbb..2158379c 100644 --- a/src/cgame/cg_draw.c +++ b/src/cgame/cg_draw.c @@ -2844,6 +2844,36 @@ static void CG_DrawIntermission( void ) cg.scoreBoardShowing = CG_DrawScoreboard( ); } +#define FOLLOWING_STRING "following " + +/* +================= +CG_DrawFollow +================= +*/ +static qboolean CG_DrawFollow( void ) +{ + float w; + vec4_t color; + char buffer[ MAX_STRING_CHARS ]; + + if( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) + return qfalse; + + color[ 0 ] = 1; + color[ 1 ] = 1; + color[ 2 ] = 1; + color[ 3 ] = 1; + + strcpy( buffer, FOLLOWING_STRING ); + strcat( buffer, cgs.clientinfo[ cg.snap->ps.clientNum ].name ); + + w = CG_Text_Width( buffer, 0.7f, 0 ); + CG_Text_Paint( 320 - w / 2, 400, 0.7f, color, buffer, 0, 0, ITEM_TEXTSTYLE_SHADOWED ); + + return qtrue; +} + //================================================================================== #define SPECTATOR_STRING "SPECTATOR" @@ -2901,6 +2931,7 @@ static void CG_Draw2D( void ) CG_DrawVote( ); CG_DrawTeamVote( ); + CG_DrawFollow( ); // don't draw center string if scoreboard is up cg.scoreBoardShowing = CG_DrawScoreboard( ); diff --git a/src/game/g_active.c b/src/game/g_active.c index cad4a2cf..7ef3c810 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -317,6 +317,9 @@ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) client = ent->client; + client->oldbuttons = client->buttons; + client->buttons = ucmd->buttons; + if( client->sess.spectatorState != SPECTATOR_FOLLOW ) { if( client->sess.spectatorState == SPECTATOR_LOCKED ) @@ -342,23 +345,23 @@ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) G_TouchTriggers( ent ); trap_UnlinkEntity( ent ); - } - - client->oldbuttons = client->buttons; - client->buttons = ucmd->buttons; - - if( ( client->buttons & BUTTON_ATTACK ) && !( client->oldbuttons & BUTTON_ATTACK ) ) - { - if( client->pers.classSelection == PCL_NONE ) + + if( ( client->buttons & BUTTON_ATTACK ) && !( client->oldbuttons & BUTTON_ATTACK ) ) { - if( client->pers.teamSelection == PTE_NONE ) - G_TriggerMenu( client->ps.clientNum, MN_TEAM ); - else if( client->pers.teamSelection == PTE_ALIENS ) - G_TriggerMenu( client->ps.clientNum, MN_A_CLASS ); - else if( client->pers.teamSelection == PTE_HUMANS ) - G_TriggerMenu( client->ps.clientNum, MN_H_SPAWN ); + if( client->pers.classSelection == PCL_NONE ) + { + if( client->pers.teamSelection == PTE_NONE ) + G_TriggerMenu( client->ps.clientNum, MN_TEAM ); + else if( client->pers.teamSelection == PTE_ALIENS ) + G_TriggerMenu( client->ps.clientNum, MN_A_CLASS ); + else if( client->pers.teamSelection == PTE_HUMANS ) + G_TriggerMenu( client->ps.clientNum, MN_H_SPAWN ); + } } } + + if( ( client->buttons & BUTTON_ATTACK2 ) && !( client->oldbuttons & BUTTON_ATTACK2 ) ) + Cmd_FollowCycle_f( ent, 1 ); } diff --git a/src/game/g_client.c b/src/game/g_client.c index 4645780c..35b68f1a 100644 --- a/src/game/g_client.c +++ b/src/game/g_client.c @@ -1497,6 +1497,17 @@ void ClientDisconnect( int clientNum ) if( !ent->client ) return; + // stop any following clients + for( i = 0; i < level.maxclients; i++ ) + { + if( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR && + level.clients[ i ].sess.spectatorState == SPECTATOR_FOLLOW && + level.clients[ i ].sess.spectatorClient == clientNum ) + { + G_StopFollowing( &g_entities[ i ] ); + } + } + // send effect if they were completely connected if( ent->client->pers.connected == CON_CONNECTED && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index d9d9922c..1145bad1 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -18,6 +18,90 @@ /* ================== +G_SanitiseName + +Remove case and control characters from a player name +================== +*/ +void G_SanitiseName( char *in, char *out ) +{ + while( *in ) + { + if( *in == 27 ) + { + in += 2; // skip color code + continue; + } + + if( *in < 32 ) + { + in++; + continue; + } + + *out++ = tolower( *in++ ); + } + + *out = 0; +} + +/* +================== +G_ClientNumberFromString + +Returns a player number for either a number or name string +Returns -1 if invalid +================== +*/ +int G_ClientNumberFromString( gentity_t *to, char *s ) +{ + gclient_t *cl; + int idnum; + char s2[ MAX_STRING_CHARS ]; + char n2[ MAX_STRING_CHARS ]; + + // numeric values are just slot numbers + if( s[ 0 ] >= '0' && s[ 0 ] <= '9' ) + { + idnum = atoi( s ); + + if( idnum < 0 || idnum >= level.maxclients ) + { + trap_SendServerCommand( to - g_entities, va( "print \"Bad client slot: %i\n\"", idnum ) ); + return -1; + } + + cl = &level.clients[ idnum ]; + + if( cl->pers.connected != CON_CONNECTED ) + { + trap_SendServerCommand( to - g_entities, va( "print \"Client %i is not active\n\"", idnum ) ); + return -1; + } + + return idnum; + } + + // check for a name match + G_SanitiseName( s, s2 ); + + for( idnum = 0, cl = level.clients; idnum < level.maxclients; idnum++, cl++ ) + { + if( cl->pers.connected != CON_CONNECTED ) + continue; + + G_SanitiseName( cl->pers.netname, n2 ); + + if( !strcmp( n2, s2 ) ) + return idnum; + } + + trap_SendServerCommand( to - g_entities, va( "print \"User %s is not on the server\n\"", s ) ); + return -1; +} + +/* +================== ScoreboardMessage ================== @@ -336,17 +420,36 @@ void Cmd_Kill_f( gentity_t *ent ) /* ================= -Cmd_Team_f +G_ChangeTeam ================= */ -void Cmd_Team_f( gentity_t *ent ) +void G_ChangeTeam( gentity_t *ent, pTeam_t newTeam ) { - int oldTeam; - char s[MAX_TOKEN_CHARS]; + pTeam_t oldTeam = ent->client->pers.teamSelection; - //TA: rip out the q3a team system :) + ent->client->pers.teamSelection = newTeam; - oldTeam = ent->client->pers.teamSelection; + if( oldTeam != newTeam ) + { + level.bankCredits[ ent->client->ps.clientNum ] = 0; + ent->client->ps.persistant[ PERS_CREDIT ] = 0; + ent->client->pers.classSelection = PCL_NONE; + ClientSpawn( ent, NULL, NULL, NULL ); + } + + //update ClientInfo + ClientUserinfoChanged( ent->client->ps.clientNum ); +} + +/* +================= +Cmd_Team_f +================= +*/ +void Cmd_Team_f( gentity_t *ent ) +{ + pTeam_t team; + char s[ MAX_TOKEN_CHARS ]; trap_Argv( 1, s, sizeof( s ) ); @@ -357,7 +460,7 @@ void Cmd_Team_f( gentity_t *ent ) } if( !Q_stricmp( s, "spectate" ) ) - ent->client->pers.teamSelection = PTE_NONE; + team = PTE_NONE; else if( !Q_stricmp( s, "aliens" ) ) { if( g_teamForceBalance.integer && level.numAlienClients > level.numHumanClients ) @@ -367,7 +470,7 @@ void Cmd_Team_f( gentity_t *ent ) return; } - ent->client->pers.teamSelection = PTE_ALIENS; + team = PTE_ALIENS; } else if( !Q_stricmp( s, "humans" ) ) { @@ -378,28 +481,19 @@ void Cmd_Team_f( gentity_t *ent ) return; } - ent->client->pers.teamSelection = PTE_HUMANS; + team = PTE_HUMANS; } else if( !Q_stricmp( s, "auto" ) ) { if( level.numHumanClients > level.numAlienClients ) - ent->client->pers.teamSelection = PTE_ALIENS; + team = PTE_ALIENS; else if( level.numHumanClients < level.numAlienClients ) - ent->client->pers.teamSelection = PTE_HUMANS; + team = PTE_HUMANS; else - ent->client->pers.teamSelection = PTE_ALIENS + ( rand( ) % 2 ); + team = PTE_ALIENS + ( rand( ) % 2 ); } - if( oldTeam != ent->client->pers.teamSelection ) - { - level.bankCredits[ ent->client->ps.clientNum ] = 0; - ent->client->ps.persistant[ PERS_CREDIT ] = 0; - ent->client->pers.classSelection = PCL_NONE; - ClientSpawn( ent, NULL, NULL, NULL ); - } - - //update ClientInfo - ClientUserinfoChanged( ent->client->ps.clientNum ); + G_ChangeTeam( ent, team ); //FIXME: put some team change broadcast code here. } @@ -1715,6 +1809,117 @@ void Cmd_Reload_f( gentity_t *ent ) /* ================= +G_StopFollowing + +If the client being followed leaves the game, or you just want to drop +to free floating spectator mode +================= +*/ +void G_StopFollowing( gentity_t *ent ) +{ + ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR; + ent->client->sess.sessionTeam = TEAM_SPECTATOR; + ent->client->sess.spectatorState = SPECTATOR_FREE; + ent->client->ps.pm_flags &= ~PMF_FOLLOW; + ent->r.svFlags &= ~SVF_BOT; + ent->client->ps.clientNum = ent - g_entities; +} + +/* +================= +Cmd_Follow_f +================= +*/ +void Cmd_Follow_f( gentity_t *ent ) +{ + int i; + char arg[ MAX_TOKEN_CHARS ]; + + if( trap_Argc( ) != 2 ) + { + if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) + G_StopFollowing( ent ); + + return; + } + + trap_Argv( 1, arg, sizeof( arg ) ); + i = G_ClientNumberFromString( ent, arg ); + + if( i == -1 ) + return; + + // can't follow self + if( &level.clients[ i ] == ent->client ) + return; + + // can't follow another spectator + if( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) + return; + + // first set them to spectator + if( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) + G_ChangeTeam( ent, PTE_NONE ); + + ent->client->sess.spectatorState = SPECTATOR_FOLLOW; + ent->client->sess.spectatorClient = i; +} + +/* +================= +Cmd_FollowCycle_f +================= +*/ +void Cmd_FollowCycle_f( gentity_t *ent, int dir ) +{ + int clientnum; + int original; + + // first set them to spectator + if( ent->client->sess.spectatorState == SPECTATOR_NOT ) + G_ChangeTeam( ent, PTE_NONE ); + + if( dir != 1 && dir != -1 ) + G_Error( "Cmd_FollowCycle_f: bad dir %i", dir ); + + clientnum = ent->client->sess.spectatorClient; + original = clientnum; + + do + { + clientnum += dir; + + if( clientnum >= level.maxclients ) + clientnum = 0; + + if( clientnum < 0 ) + clientnum = level.maxclients - 1; + + // can't follow self + if( &level.clients[ clientnum ] == ent->client ) + continue; + + // can only follow connected clients + if( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) + continue; + + // can't follow another spectator + if( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR ) + continue; + + // this is good, we can use it + ent->client->sess.spectatorClient = clientnum; + ent->client->sess.spectatorState = SPECTATOR_FOLLOW; + return; + + } while( clientnum != original ); + + // leave it where it was +} + + +/* +================= Cmd_Test_f ================= */ @@ -1826,6 +2031,12 @@ void ClientCommand( int clientNum ) Cmd_Vote_f( ent ); else if( Q_stricmp( cmd, "callteamvote" ) == 0 ) Cmd_CallTeamVote_f( ent ); + else if( Q_stricmp( cmd, "follow" ) == 0 ) + Cmd_Follow_f( ent ); + else if( Q_stricmp (cmd, "follownext") == 0) + Cmd_FollowCycle_f( ent, 1 ); + else if( Q_stricmp( cmd, "followprev" ) == 0 ) + Cmd_FollowCycle_f( ent, -1 ); else if( Q_stricmp( cmd, "teamvote" ) == 0 ) Cmd_TeamVote_f( ent ); else if( Q_stricmp( cmd, "setviewpos" ) == 0 ) diff --git a/src/game/g_local.h b/src/game/g_local.h index 926cecb0..99f6ebf4 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -564,6 +564,8 @@ char *G_NewString( const char *string ); // g_cmds.c // void Cmd_Score_f( gentity_t *ent ); +void G_StopFollowing( gentity_t *ent ); +void Cmd_FollowCycle_f( gentity_t *ent, int dir ); // // g_physics.c diff --git a/src/game/g_main.c b/src/game/g_main.c index eb81594d..37cd9a01 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -1153,6 +1153,10 @@ If a new client connects, this will be called after the spawn function. */ void MoveClientToIntermission( gentity_t *ent ) { + // take out of follow mode if needed + if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) + G_StopFollowing( ent ); + // move to the spot VectorCopy( level.intermission_origin, ent->s.origin ); VectorCopy( level.intermission_origin, ent->client->ps.origin ); |