/* * Cmd_Join_f */ static void Cmd_Join_f( edict_t *ent ) { if( CheckFlood( ent, false ) ) return; G_Teams_Join_Cmd( ent ); }
/* * Cmd_Say_f */ void Cmd_Say_f( edict_t *ent, bool arg0, bool checkflood ) { char *p; char text[2048]; size_t arg0len = 0; #ifdef AUTHED_SAY if( sv_mm_enable->integer && ent->r.client && ent->r.client->mm_session <= 0 ) { // unauthed players are only allowed to chat to public at non play-time if( GS_MatchState() == MATCH_STATE_PLAYTIME ) { G_PrintMsg( ent, "%s", S_COLOR_YELLOW "You must authenticate to be able to communicate to other players during the match.\n"); return; } } #endif if( checkflood ) { if( CheckFlood( ent, false ) ) return; } if( ent->r.client && ( ent->r.client->muted & 1 ) ) return; if( trap_Cmd_Argc() < 2 && !arg0 ) return; text[0] = 0; if( arg0 ) { Q_strncatz( text, trap_Cmd_Argv( 0 ), sizeof( text ) ); Q_strncatz( text, " ", sizeof( text ) ); arg0len = strlen( text ); Q_strncatz( text, trap_Cmd_Args(), sizeof( text ) ); } else { p = trap_Cmd_Args(); if( *p == '"' ) { if( p[strlen( p )-1] == '"' ) p[strlen( p )-1] = 0; p++; } Q_strncatz( text, p, sizeof( text ) ); } // don't let text be too long for malicious reasons text[arg0len + (MAX_CHAT_BYTES - 1)] = 0; if( !Q_stricmp( text, "gg" ) || !Q_stricmp( text, "good game" ) ) G_AwardFairPlay( ent ); G_ChatMsg( NULL, ent, false, "%s", text ); }
/* * 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 ) { G_Teams_JoinTeam( ent, TEAM_SPECTATOR ); 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_Say_f ================== */ void Cmd_Say_f(edict_t *ent, qboolean team, qboolean arg0){ int j; edict_t *other; char *p; char text[2048]; if(gi.argc() < 2 && !arg0) return; if(!((int)(dmflags->value) &(DF_MODELTEAMS | DF_SKINTEAMS))) team = false; if(team) Com_sprintf(text, sizeof(text), "(%s): ", ent->client->pers.netname); else Com_sprintf(text, sizeof(text), "%s: ", ent->client->pers.netname); if(arg0){ strcat(text, gi.argv(0)); strcat(text, " "); strcat(text, gi.args()); } else { p = gi.args(); if(*p == '"'){ p++; p[strlen(p) - 1] = 0; } strcat(text, p); } // don't let text be too long for malicious reasons if(strlen(text) > 150) text[150] = 0; strcat(text, "\n"); if(CheckFlood(ent)) return; if(dedicated->value) gi.cprintf(NULL, PRINT_CHAT, "%s", text); for(j = 1; j <= game.maxclients; j++){ other = &g_edicts[j]; if(!other->inuse) continue; if(!other->client) continue; if(team){ if(!OnSameTeam(ent, other)) continue; } gi.cprintf(other, PRINT_CHAT, "%s", text); } }
/* * Cmd_Say_f */ void Cmd_Say_f( edict_t *ent, qboolean arg0, qboolean checkflood ) { char *p; char text[2048]; if( checkflood ) { if( CheckFlood( ent, qfalse ) ) return; } if( ent->r.client && ent->r.client->muted & 1 ) return; if( trap_Cmd_Argc() < 2 && !arg0 ) return; text[0] = 0; if( arg0 ) { Q_strncatz( text, trap_Cmd_Argv( 0 ), sizeof( text ) ); Q_strncatz( text, " ", sizeof( text ) ); Q_strncatz( text, trap_Cmd_Args(), sizeof( text ) ); } else { p = trap_Cmd_Args(); if( *p == '"' ) { if( p[strlen( p )-1] == '"' ) p[strlen( p )-1] = 0; p++; } Q_strncatz( text, p, sizeof( text ) ); } // don't let text be too long for malicious reasons if( strlen( text ) > 150 ) text[150] = 0; G_ChatMsg( NULL, ent, qfalse, "%s", text ); // racesow RS_ircSendMessage( va( "%s", COM_RemoveColorTokens(( ent->r.client->netname ) )), va( "%s", COM_RemoveColorTokens(( text)) ) ); // !racesow }
/* * Cmd_Say_f */ void Cmd_Say_f( edict_t *ent, bool arg0, bool checkflood ) { char *p; char text[2048]; if( checkflood ) { if( CheckFlood( ent, false ) ) return; } if( ent->r.client && ent->r.client->muted & 1 ) return; if( trap_Cmd_Argc() < 2 && !arg0 ) return; text[0] = 0; if( arg0 ) { Q_strncatz( text, trap_Cmd_Argv( 0 ), sizeof( text ) ); Q_strncatz( text, " ", sizeof( text ) ); Q_strncatz( text, trap_Cmd_Args(), sizeof( text ) ); } else { p = trap_Cmd_Args(); if( *p == '"' ) { if( p[strlen( p )-1] == '"' ) p[strlen( p )-1] = 0; p++; } Q_strncatz( text, p, sizeof( text ) ); } // don't let text be too long for malicious reasons if( strlen( text ) > 150 ) text[150] = 0; G_ChatMsg( NULL, ent, false, "%s", text ); }
static void Cmd_CoinToss_f( edict_t *ent ) { bool qtails; char *s; char upper[MAX_STRING_CHARS]; if( GS_MatchState() > MATCH_STATE_WARMUP && !GS_MatchPaused() ) { G_PrintMsg( ent, "You can only toss coins during warmup or timeouts\n" ); return; } if( CheckFlood( ent, false ) ) return; if( trap_Cmd_Argc() < 2 || ( Q_stricmp( "heads", trap_Cmd_Argv( 1 ) ) && Q_stricmp( "tails", trap_Cmd_Argv( 1 ) ) ) ) { //it isn't a valid token G_PrintMsg( ent, "You have to choose heads or tails when tossing a coin\n" ); return; } Q_strncpyz( upper, trap_Cmd_Argv( 1 ), sizeof( upper ) ); s = upper; while( *s ) { *s = toupper( *s ); s++; } qtails = ( Q_stricmp( "heads", trap_Cmd_Argv( 1 ) ) != 0 ) ? true : false; if( qtails == ( rand() & 1 ) ) { G_PrintMsg( NULL, S_COLOR_YELLOW "COINTOSS %s: " S_COLOR_WHITE "It was %s! %s " S_COLOR_WHITE "tossed a coin and " S_COLOR_GREEN "won!\n", upper, trap_Cmd_Argv( 1 ), ent->r.client->netname ); return; } G_PrintMsg( NULL, S_COLOR_YELLOW "COINTOSS %s: " S_COLOR_WHITE "It was %s! %s " S_COLOR_WHITE "tossed a coin and " S_COLOR_RED "lost!\n", upper, qtails ? "heads" : "tails", ent->r.client->netname ); }
/* * TV_Cmd_Say_f */ void TV_Cmd_Say_f( client_t *client, bool arg0 ) { char *p; char text[256]; if( CheckFlood( client ) ) return; if( Cmd_Argc() < 2 && !arg0 ) return; text[0] = 0; if( arg0 ) { Q_strncatz( text, Cmd_Argv( 0 ), sizeof( text ) ); Q_strncatz( text, " ", sizeof( text ) ); Q_strncatz( text, Cmd_Args(), sizeof( text ) ); } else { p = Cmd_Args(); if( *p == '"' ) { if( p[strlen( p )-1] == '"' ) p[strlen( p )-1] = 0; p++; } Q_strncatz( text, p, sizeof( text ) ); } // don't let text be too long for malicious reasons text[MAX_CHAT_BYTES - 1] = 0; Q_strncatz( text, "\n", sizeof( text ) ); TV_Downstream_Msg( NULL, client->relay, client, true, "%s", text ); }
/* * G_vsay_f */ static void G_vsay_f( edict_t *ent, bool team ) { edict_t *event = NULL; g_vsays_t *vsay; const 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 = false; 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, false ) ) 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 ), false ); else G_ChatMsg( NULL, ent, false, "(v) %s", text ); return; } // unknown token, print help { char string[MAX_STRING_CHARS]; // print information string[0] = 0; if( msg && msg[0] != '\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 ); } }
void G_Say_Team( edict_t *who, char *msg, bool checkflood ) { char outmsg[256]; char buf[256]; char *p; char current_color[3]; if( who->s.team != TEAM_SPECTATOR && ( !GS_TeamBasedGametype() || GS_InvidualGameType() ) ) { Cmd_Say_f( who, false, true ); return; } if( checkflood ) { if( CheckFlood( who, true ) ) return; } if( *msg == '\"' ) { msg[strlen( msg ) - 1] = 0; msg++; } if( who->s.team == TEAM_SPECTATOR ) { // if speccing, also check for non-team flood if( checkflood ) { if( CheckFlood( who, false ) ) return; } G_ChatMsg( NULL, who, true, "%s", msg ); return; } #ifdef AUTHED_SAY if( sv_mm_enable->integer && who->r.client && who->r.client->mm_session <= 0 ) { // unauthed players are only allowed to chat to public at non play-time // they are allowed to team-chat at any time if( GS_MatchState() == MATCH_STATE_PLAYTIME ) { G_PrintMsg( who, "%s", S_COLOR_YELLOW "You must authenticate to be able to chat with other players during the match.\n"); return; } } #endif Q_strncpyz( current_color, S_COLOR_WHITE, sizeof( current_color ) ); memset( outmsg, 0, sizeof( outmsg ) ); UpdatePoint( who ); for( p = outmsg; *msg && (size_t)( p - outmsg ) < sizeof( outmsg ) - 3; msg++ ) { if( *msg == '%' ) { buf[0] = 0; switch( *++msg ) { case 'l': Say_Team_Location( who, buf, sizeof( buf ), current_color ); break; case 'a': Say_Team_Armor( who, buf, sizeof( buf ), current_color ); break; case 'h': Say_Team_Health( who, buf, sizeof( buf ), current_color ); break; case 'b': Say_Team_Best_Weapons( who, buf, sizeof( buf ), current_color ); break; case 'w': Say_Team_Current_Weapon( who, buf, sizeof( buf ), current_color ); break; case 'x': Say_Team_Point( who, buf, sizeof( buf ), current_color ); break; case 'y': Say_Team_Point_Location( who, buf, sizeof( buf ), current_color ); break; case 'X': Say_Team_Pickup( who, buf, sizeof( buf ), current_color ); break; case 'Y': Say_Team_Pickup_Location( who, buf, sizeof( buf ), current_color ); break; case 'd': Say_Team_Drop( who, buf, sizeof( buf ), current_color ); break; case 'D': Say_Team_Drop_Location( who, buf, sizeof( buf ), current_color ); break; case '%': *p++ = *msg; break; default: // Maybe add a warning here? *p++ = '%'; *p++ = *msg; break; } if( strlen( buf ) + ( p - outmsg ) < sizeof( outmsg ) - 3 ) { Q_strncatz( outmsg, buf, sizeof( outmsg ) ); p += strlen( buf ); } } else if( *msg == '^' ) { *p++ = *msg++; *p++ = *msg; Q_strncpyz( current_color, p-2, sizeof( current_color ) ); } else { *p++ = *msg; } } *p = 0; G_ChatMsg( NULL, who, true, "%s", outmsg ); }
/* * 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 ); }
void G_Say_Team( edict_t *who, char *msg, bool checkflood ) { char outmsg[256]; char buf[256]; char *p; char current_color[3]; if( who->s.team != TEAM_SPECTATOR && ( !GS_TeamBasedGametype() || GS_InvidualGameType() ) ) { Cmd_Say_f( who, false, true ); return; } if( checkflood ) { if( CheckFlood( who, true ) ) return; } if( *msg == '\"' ) { msg[strlen( msg ) - 1] = 0; msg++; } if( who->s.team == TEAM_SPECTATOR ) { // if speccing, also check for non-team flood if( checkflood ) { if( CheckFlood( who, false ) ) return; } G_ChatMsg( NULL, who, true, "%s", msg ); return; } Q_strncpyz( current_color, S_COLOR_WHITE, sizeof( current_color ) ); memset( outmsg, 0, sizeof( outmsg ) ); UpdatePoint( who ); for( p = outmsg; *msg && (size_t)( p - outmsg ) < sizeof( outmsg ) - 3; msg++ ) { if( *msg == '%' ) { buf[0] = 0; switch( *++msg ) { case 'l': Say_Team_Location( who, buf, sizeof( buf ), current_color ); break; case 'a': Say_Team_Armor( who, buf, sizeof( buf ), current_color ); break; case 'h': Say_Team_Health( who, buf, sizeof( buf ), current_color ); break; case 'b': Say_Team_Best_Weapons( who, buf, sizeof( buf ), current_color ); break; case 'w': Say_Team_Current_Weapon( who, buf, sizeof( buf ), current_color ); break; case 'x': Say_Team_Point( who, buf, sizeof( buf ), current_color ); break; case 'y': Say_Team_Point_Location( who, buf, sizeof( buf ), current_color ); break; case 'X': Say_Team_Pickup( who, buf, sizeof( buf ), current_color ); break; case 'Y': Say_Team_Pickup_Location( who, buf, sizeof( buf ), current_color ); break; case 'd': Say_Team_Drop( who, buf, sizeof( buf ), current_color ); break; case 'D': Say_Team_Drop_Location( who, buf, sizeof( buf ), current_color ); break; case '%': *p++ = *msg; break; default: // Maybe add a warning here? *p++ = '%'; *p++ = *msg; break; } if( strlen( buf ) + ( p - outmsg ) < sizeof( outmsg ) - 3 ) { Q_strncatz( outmsg, buf, sizeof( outmsg ) ); p += strlen( buf ); } } else if( *msg == '^' ) { *p++ = *msg++; *p++ = *msg; Q_strncpyz( current_color, p-2, sizeof( current_color ) ); } else { *p++ = *msg; } } *p = 0; G_ChatMsg( NULL, who, true, "%s", outmsg ); }