// Copyright (C) 1999-2000 Id Software, Inc. // /* * Portions Copyright (C) 2000-2001 Tim Angus * * This program is free software; you can redistribute it and/or modify it * under the terms of the OSML - Open Source Modification License v1.0 as * described in the file COPYING which is distributed with this source * code. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include "g_local.h" /* ================== ScoreboardMessage ================== */ void ScoreboardMessage( gentity_t *ent ) { char entry[ 1024 ]; char string[ 1400 ]; int stringlength; int i, j; gclient_t *cl; int numSorted; // send the latest information on all clients string[ 0 ] = 0; stringlength = 0; numSorted = level.numConnectedClients; for( i = 0; i < numSorted; i++ ) { int ping; cl = &level.clients[ level.sortedClients[ i ] ]; if( cl->pers.connected == CON_CONNECTING ) ping = -1; else ping = cl->ps.ping < 999 ? cl->ps.ping : 999; Com_sprintf( entry, sizeof( entry ), " %i %i %i %i", level.sortedClients[ i ], cl->ps.persistant[ PERS_SCORE ], ping, ( level.time - cl->pers.enterTime ) / 60000 ); j = strlen( entry ); if( stringlength + j > 1024 ) break; strcpy( string + stringlength, entry ); stringlength += j; } trap_SendServerCommand( ent-g_entities, va( "scores %i %i %i%s", i, level.alienKills, level.humanKills, string ) ); } /* ================== Cmd_Score_f Request current scoreboard information ================== */ void Cmd_Score_f( gentity_t *ent ) { ScoreboardMessage( ent ); } /* ================== CheatsOk ================== */ qboolean CheatsOk( gentity_t *ent ) { if( !g_cheats.integer ) { trap_SendServerCommand( ent-g_entities, va( "print \"Cheats are not enabled on this server.\n\"" ) ); return qfalse; } if( ent->health <= 0 ) { trap_SendServerCommand( ent-g_entities, va( "print \"You must be alive to use this command.\n\"" ) ); return qfalse; } return qtrue; } /* ================== ConcatArgs ================== */ char *ConcatArgs( int start ) { int i, c, tlen; static char line[ MAX_STRING_CHARS ]; int len; char arg[ MAX_STRING_CHARS ]; len = 0; c = trap_Argc( ); for( i = start; i < c; i++ ) { trap_Argv( i, arg, sizeof( arg ) ); tlen = strlen( arg ); if( len + tlen >= MAX_STRING_CHARS - 1 ) break; memcpy( line + len, arg, tlen ); len += tlen; if( i != c - 1 ) { line[ len ] = ' '; len++; } } line[ len ] = 0; return line; } /* ================== Cmd_Give_f Give items to a client ================== */ void Cmd_Give_f( gentity_t *ent ) { char *name; gitem_t *it; int i; qboolean give_all; gentity_t *it_ent; trace_t trace; if( !CheatsOk( ent ) ) return; name = ConcatArgs( 1 ); if( Q_stricmp( name, "all" ) == 0 ) give_all = qtrue; else give_all = qfalse; if( give_all || Q_stricmp( name, "health" ) == 0 ) { ent->health = ent->client->ps.stats[ STAT_MAX_HEALTH ]; if( !give_all ) return; } if( give_all || Q_stricmp( name, "weapons" ) == 0 ) { BG_packWeapon( ( 1 << WP_NUM_WEAPONS ) - 1 - ( 1 << WP_NONE ), ent->client->ps.stats ); if( !give_all ) return; } if( give_all || Q_stricmp( name, "ammo" ) == 0 ) { for( i = 0; i < MAX_WEAPONS; i++ ) BG_packAmmoArray( i, ent->client->ps.ammo, ent->client->ps.powerups, 999, 0, 0 ); if( !give_all ) return; } if( give_all || Q_stricmpn( name, "funds", 5 ) == 0 ) { int credits = atoi( name + 6 ); if( !credits ) ent->client->ps.persistant[ PERS_CREDIT ]++; else ent->client->ps.persistant[ PERS_CREDIT ] += credits; if( !give_all ) return; } } /* ================== Cmd_God_f Sets client to godmode argv(0) god ================== */ void Cmd_God_f( gentity_t *ent ) { char *msg; if( !CheatsOk( ent ) ) return; ent->flags ^= FL_GODMODE; if( !( ent->flags & FL_GODMODE ) ) msg = "godmode OFF\n"; else msg = "godmode ON\n"; trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) ); } /* ================== Cmd_Notarget_f Sets client to notarget argv(0) notarget ================== */ void Cmd_Notarget_f( gentity_t *ent ) { char *msg; if( !CheatsOk( ent ) ) return; ent->flags ^= FL_NOTARGET; if( !( ent->flags & FL_NOTARGET ) ) msg = "notarget OFF\n"; else msg = "notarget ON\n"; trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) ); } /* ================== Cmd_Noclip_f argv(0) noclip ================== */ void Cmd_Noclip_f( gentity_t *ent ) { char *msg; if( !CheatsOk( ent ) ) return; if( ent->client->noclip ) msg = "noclip OFF\n"; else msg = "noclip ON\n"; ent->client->noclip = !ent->client->noclip; trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) ); } /* ================== Cmd_LevelShot_f This is just to help generate the level pictures for the menus. It goes to the intermission immediately and sends over a command to the client to resize the view, hide the scoreboard, and take a special screenshot ================== */ void Cmd_LevelShot_f( gentity_t *ent ) { if( !CheatsOk( ent ) ) return; BeginIntermission( ); trap_SendServerCommand( ent - g_entities, "clientLevelShot" ); } /* ================= Cmd_Kill_f ================= */ void Cmd_Kill_f( gentity_t *ent ) { if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_NONE ) return; if( ent->client->ps.stats[ STAT_STATE ] & SS_INFESTING ) return; if( ent->client->ps.stats[ STAT_STATE ] & SS_HOVELING ) { trap_SendServerCommand( ent-g_entities, "print \"Leave the hovel first (use your destroy key)\n\"" ); return; } if (ent->health <= 0) return; ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; player_die (ent, ent, ent, 100000, MOD_SUICIDE); } /* ================= Cmd_Team_f ================= */ void Cmd_Team_f( gentity_t *ent ) { int oldTeam; char s[MAX_TOKEN_CHARS]; //TA: rip out the q3a team system :) oldTeam = ent->client->pers.pteam; trap_Argv( 1, s, sizeof( s ) ); if( !strlen( s ) ) { trap_SendServerCommand( ent-g_entities, va("print \"team: %i\n\"", ent->client->pers.pteam ) ); return; } if( !Q_stricmp( s, "0" ) || !Q_stricmp( s, "spectate" ) ) ent->client->pers.pteam = PTE_NONE; else if( !Q_stricmp( s, "1" ) || !Q_stricmp( s, "aliens" ) ) ent->client->pers.pteam = PTE_ALIENS; else if( !Q_stricmp( s, "2" ) || !Q_stricmp( s, "humans" ) ) ent->client->pers.pteam = PTE_HUMANS; if( oldTeam != ent->client->pers.pteam ) { level.bankCredits[ ent->client->ps.clientNum ] = 0; ent->client->ps.persistant[ PERS_CREDIT ] = 0; ent->client->pers.pclass = 0; ClientSpawn( ent, NULL, NULL, NULL ); } //update ClientInfo ClientUserinfoChanged( ent->client->ps.clientNum ); //FIXME: put some team change broadcast code here. } /* ================== G_Say ================== */ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message ) { if( !other ) return; if( !other->inuse ) return; if( !other->client ) return; if( other->client->pers.connected != CON_CONNECTED ) return; if( mode == SAY_TEAM && !OnSameTeam( ent, other ) ) return; trap_SendServerCommand( other-g_entities, va( "%s \"%s%c%c%s\"", mode == SAY_TEAM ? "tchat" : "chat", name, Q_COLOR_ESCAPE, color, message ) ); } #define EC "\x19" void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) { int j; gentity_t *other; int color; char name[ 64 ]; // don't let text be too long for malicious reasons char text[ MAX_SAY_TEXT ]; char location[ 64 ]; switch( mode ) { default: case SAY_ALL: G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText ); Com_sprintf( name, sizeof( name ), "%s%c%c"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_GREEN; break; case SAY_TEAM: G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText ); if( Team_GetLocationMsg( ent, location, sizeof( location ) ) ) Com_sprintf( name, sizeof( name ), EC"(%s%c%c"EC") (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location ); else Com_sprintf( name, sizeof( name ), EC"(%s%c%c"EC")"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_CYAN; break; case SAY_TELL: if( target && target->client->ps.stats[ STAT_PTEAM ] == ent->client->ps.stats[ STAT_PTEAM ] && Team_GetLocationMsg( ent, location, sizeof( location ) ) ) Com_sprintf( name, sizeof( name ), EC"[%s%c%c"EC"] (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location ); else Com_sprintf( name, sizeof( name ), EC"[%s%c%c"EC"]"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_MAGENTA; break; } Q_strncpyz( text, chatText, sizeof( text ) ); if( target ) { G_SayTo( ent, target, mode, color, name, text ); return; } // echo the text to the console if( g_dedicated.integer ) G_Printf( "%s%s\n", name, text); // send it to all the apropriate clients for( j = 0; j < level.maxclients; j++ ) { other = &g_entities[ j ]; G_SayTo( ent, other, mode, color, name, text ); } } /* ================== Cmd_Say_f ================== */ static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) { char *p; if( trap_Argc( ) < 2 && !arg0 ) return; if( arg0 ) p = ConcatArgs( 0 ); else p = ConcatArgs( 1 ); G_Say( ent, NULL, mode, p ); } /* ================== Cmd_Tell_f ================== */ static void Cmd_Tell_f( gentity_t *ent ) { int targetNum; gentity_t *target; char *p; char arg[MAX_TOKEN_CHARS]; if( trap_Argc( ) < 2 ) return; trap_Argv( 1, arg, sizeof( arg ) ); targetNum = atoi( arg ); if( targetNum < 0 || targetNum >= level.maxclients ) return; target = &g_entities[ targetNum ]; if( !target || !target->inuse || !target->client ) return; p = ConcatArgs( 2 ); G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p ); G_Say( ent, target, SAY_TELL, p ); // don't tell to the player self if it was already directed to this player // also don't send the chat back to a bot if( ent != target && !( ent->r.svFlags & SVF_BOT ) ) G_Say( ent, ent, SAY_TELL, p ); } /* ================== Cmd_Where_f ================== */ void Cmd_Where_f( gentity_t *ent ) { trap_SendServerCommand( ent-g_entities, va( "print \"%s\n\"", vtos( ent->s.origin ) ) ); } /* ================== Cmd_CallVote_f ================== */ void Cmd_CallVote_f( gentity_t *ent ) { int i; char arg1[ MAX_STRING_TOKENS ]; char arg2[ MAX_STRING_TOKENS ]; if( !g_allowVote.integer ) { trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" ); return; } if( level.voteTime ) { trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" ); return; } if( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) { trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of votes.\n\"" ); return; } if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_NONE ) { trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" ); return; } // make sure it is a valid command to vote on trap_Argv( 1, arg1, sizeof( arg1 ) ); trap_Argv( 2, arg2, sizeof( arg2 ) ); if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) { trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); return; } if( !Q_stricmp( arg1, "map_restart" ) ) { } else if( !Q_stricmp( arg1, "nextmap" ) ) { } else if( !Q_stricmp( arg1, "map" ) ) { } else if( !Q_stricmp( arg1, "kick" ) ) { } else if( !Q_stricmp( arg1, "clientkick" ) ) { } else if( !Q_stricmp( arg1, "timelimit" ) ) { } else { trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map , " "kick , clientkick , " "timelimit