// Copyright (C) 1999-2000 Id Software, Inc. // #include "g_local.h" #include "bg_saga.h" #include "../../ui/menudef.h" // for the voice chats //rww - for getting bot commands... int AcceptBotCommand(char *cmd, gentity_t *pl); //end rww #include "../namespace_begin.h" void WP_SetSaber( int entNum, saberInfo_t *sabers, int saberNum, const char *saberName ); #include "../namespace_end.h" void Cmd_NPC_f( gentity_t *ent ); void SetTeamQuick(gentity_t *ent, int team, qboolean doBegin); /* ================== DeathmatchScoreboardMessage ================== */ void DeathmatchScoreboardMessage( gentity_t *ent ) { char entry[1024]; char string[1400]; int stringlength; int i, j; gclient_t *cl; int numSorted, scoreFlags, accuracy, perfect; // send the latest information on all clients string[0] = 0; stringlength = 0; scoreFlags = 0; numSorted = level.numConnectedClients; if (numSorted > MAX_CLIENT_SCORE_SEND) { numSorted = MAX_CLIENT_SCORE_SEND; } 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; } if( cl->accuracy_shots ) { accuracy = cl->accuracy_hits * 100 / cl->accuracy_shots; } else { accuracy = 0; } perfect = ( cl->ps.persistant[PERS_RANK] == 0 && cl->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0; Com_sprintf (entry, sizeof(entry), " %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.sortedClients[i], cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime)/60000, scoreFlags, g_entities[level.sortedClients[i]].s.powerups, accuracy, cl->ps.persistant[PERS_IMPRESSIVE_COUNT], cl->ps.persistant[PERS_EXCELLENT_COUNT], cl->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], cl->ps.persistant[PERS_DEFEND_COUNT], cl->ps.persistant[PERS_ASSIST_COUNT], perfect, cl->ps.persistant[PERS_CAPTURES]); j = strlen(entry); if (stringlength + j > 1022) break; strcpy (string + stringlength, entry); stringlength += j; } //still want to know the total # of clients i = level.numConnectedClients; trap_SendServerCommand( ent-g_entities, va("scores %i %i %i%s", i, level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE], string ) ); } /* ================== Cmd_Score_f Request current scoreboard information ================== */ void Cmd_Score_f( gentity_t *ent ) { DeathmatchScoreboardMessage( ent ); } /* ================== CheatsOk ================== */ qboolean CheatsOk( gentity_t *ent ) { if ( !g_cheats.integer ) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOCHEATS"))); return qfalse; } if ( ent->health <= 0 ) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "MUSTBEALIVE"))); 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; } /* ================== SanitizeString Remove case and control characters ================== */ void SanitizeString( char *in, char *out ) { while ( *in ) { if ( *in == 27 ) { in += 2; // skip color code continue; } if ( *in < 32 ) { in++; continue; } *out++ = tolower( (unsigned char) *in++ ); } *out = 0; } /* ================== ClientNumberFromString Returns a player number for either a number or name string Returns -1 if invalid ================== */ int 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 SanitizeString( s, s2 ); for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) { if ( cl->pers.connected != CON_CONNECTED ) { continue; } SanitizeString( 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; } /* ================== Cmd_Give_f Give items to a client ================== */ void Cmd_Give_f (gentity_t *cmdent, int baseArg) { char name[MAX_TOKEN_CHARS]; gentity_t *ent; gitem_t *it; int i; qboolean give_all; gentity_t *it_ent; trace_t trace; char arg[MAX_TOKEN_CHARS]; if ( !CheatsOk( cmdent ) ) { return; } if (baseArg) { char otherindex[MAX_TOKEN_CHARS]; trap_Argv( 1, otherindex, sizeof( otherindex ) ); if (!otherindex[0]) { Com_Printf("giveother requires that the second argument be a client index number.\n"); return; } i = atoi(otherindex); if (i < 0 || i >= MAX_CLIENTS) { Com_Printf("%i is not a client index\n", i); return; } ent = &g_entities[i]; if (!ent->inuse || !ent->client) { Com_Printf("%i is not an active client\n", i); return; } } else { ent = cmdent; } trap_Argv( 1+baseArg, name, sizeof( name ) ); if (Q_stricmp(name, "all") == 0) give_all = qtrue; else give_all = qfalse; if (give_all) { i = 0; while (i < HI_NUM_HOLDABLE) { ent->client->ps.stats[STAT_HOLDABLE_ITEMS] |= (1 << i); i++; } i = 0; } if (give_all || Q_stricmp( name, "health") == 0) { if (trap_Argc() == 3+baseArg) { trap_Argv( 2+baseArg, arg, sizeof( arg ) ); ent->health = atoi(arg); if (ent->health > ent->client->ps.stats[STAT_MAX_HEALTH]) { ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; } } else { ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; } if (!give_all) return; } if (give_all || Q_stricmp(name, "weapons") == 0) { ent->client->ps.stats[STAT_WEAPONS] = (1 << (LAST_USEABLE_WEAPON+1)) - ( 1 << WP_NONE ); if (!give_all) return; } if ( !give_all && Q_stricmp(name, "weaponnum") == 0 ) { trap_Argv( 2+baseArg, arg, sizeof( arg ) ); ent->client->ps.stats[STAT_WEAPONS] |= (1 << atoi(arg)); return; } if (give_all || Q_stricmp(name, "ammo") == 0) { int num = 999; if (trap_Argc() == 3+baseArg) { trap_Argv( 2+baseArg, arg, sizeof( arg ) ); num = atoi(arg); } for ( i = 0 ; i < MAX_WEAPONS ; i++ ) { ent->client->ps.ammo[i] = num; } if (!give_all) return; } if (give_all || Q_stricmp(name, "armor") == 0) { if (trap_Argc() == 3+baseArg) { trap_Argv( 2+baseArg, arg, sizeof( arg ) ); ent->client->ps.stats[STAT_ARMOR] = atoi(arg); } else { ent->client->ps.stats[STAT_ARMOR] = ent->client->ps.stats[STAT_MAX_HEALTH]; } if (!give_all) return; } if (Q_stricmp(name, "excellent") == 0) { ent->client->ps.persistant[PERS_EXCELLENT_COUNT]++; return; } if (Q_stricmp(name, "impressive") == 0) { ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; return; } if (Q_stricmp(name, "gauntletaward") == 0) { ent->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; return; } if (Q_stricmp(name, "defend") == 0) { ent->client->ps.persistant[PERS_DEFEND_COUNT]++; return; } if (Q_stricmp(name, "assist") == 0) { ent->client->ps.persistant[PERS_ASSIST_COUNT]++; return; } // spawn a specific item right on the player if ( !give_all ) { it = BG_FindItem (name); if (!it) { return; } it_ent = G_Spawn(); VectorCopy( ent->r.currentOrigin, it_ent->s.origin ); it_ent->classname = it->classname; G_SpawnItem (it_ent, it); FinishSpawningItem(it_ent ); memset( &trace, 0, sizeof( trace ) ); Touch_Item (it_ent, ent, &trace); if (it_ent->inuse) { G_FreeEntity( it_ent ); } } } /* ================== 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; } // doesn't work in single player if ( g_gametype.integer != 0 ) { trap_SendServerCommand( ent-g_entities, "print \"Must be in g_gametype 0 for levelshot\n\"" ); return; } BeginIntermission(); trap_SendServerCommand( ent-g_entities, "clientLevelShot" ); } /* ================== Cmd_TeamTask_f From TA. ================== */ void Cmd_TeamTask_f( gentity_t *ent ) { char userinfo[MAX_INFO_STRING]; char arg[MAX_TOKEN_CHARS]; int task; int client = ent->client - level.clients; if ( trap_Argc() != 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); task = atoi( arg ); trap_GetUserinfo(client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, "teamtask", va("%d", task)); trap_SetUserinfo(client, userinfo); ClientUserinfoChanged(client); } extern void AddIP( char *str ); extern vmCvar_t g_autoKickTKSpammers; extern vmCvar_t g_autoBanTKSpammers; void G_CheckTKAutoKickBan( gentity_t *ent ) { if ( !ent || !ent->client || ent->s.number >= MAX_CLIENTS ) { return; } if ( g_autoKickTKSpammers.integer > 0 || g_autoBanTKSpammers.integer > 0 ) { ent->client->sess.TKCount++; if ( g_autoBanTKSpammers.integer > 0 && ent->client->sess.TKCount >= g_autoBanTKSpammers.integer ) { if ( ent->client->sess.IPstring ) {//ban their IP AddIP( ent->client->sess.IPstring ); } trap_SendServerCommand( -1, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStringEdString("MP_SVGAME_ADMIN", "TKBAN")) ); //Com_sprintf ( level.voteString, sizeof(level.voteString ), "clientkick %d", ent->s.number ); //Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "kick %s", ent->client->pers.netname ); //trap_SendConsoleCommand( EXEC_INSERT, va( "banClient %d\n", ent->s.number ) ); trap_SendConsoleCommand( EXEC_INSERT, va( "clientkick %d\n", ent->s.number ) ); return; } if ( g_autoKickTKSpammers.integer > 0 && ent->client->sess.TKCount >= g_autoKickTKSpammers.integer ) { trap_SendServerCommand( -1, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStringEdString("MP_SVGAME_ADMIN", "TKKICK")) ); //Com_sprintf ( level.voteString, sizeof(level.voteString ), "clientkick %d", ent->s.number ); //Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "kick \"%s\"\n", ent->client->pers.netname ); trap_SendConsoleCommand( EXEC_INSERT, va( "clientkick %d\n", ent->s.number ) ); return; } //okay, not gone (yet), but warn them... if ( g_autoBanTKSpammers.integer > 0 && (g_autoKickTKSpammers.integer <= 0 || g_autoBanTKSpammers.integer < g_autoKickTKSpammers.integer) ) {//warn about ban trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME_ADMIN", "WARNINGTKBAN")) ); } else if ( g_autoKickTKSpammers.integer > 0 ) {//warn about kick trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME_ADMIN", "WARNINGTKKICK")) ); } } } /* ================= Cmd_Kill_f ================= */ extern vmCvar_t g_autoKickKillSpammers; extern vmCvar_t g_autoBanKillSpammers; void Cmd_Kill_f( gentity_t *ent ) { if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { return; } if (ent->health <= 0) { return; } if ((g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) && level.numPlayingClients > 1 && !level.warmupTime) { if (!g_allowDuelSuicide.integer) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "ATTEMPTDUELKILL")) ); return; } } if ( g_autoKickKillSpammers.integer > 0 || g_autoBanKillSpammers.integer > 0 ) { ent->client->sess.killCount++; if ( g_autoBanKillSpammers.integer > 0 && ent->client->sess.killCount >= g_autoBanKillSpammers.integer ) { if ( ent->client->sess.IPstring ) {//ban their IP AddIP( ent->client->sess.IPstring ); } trap_SendServerCommand( -1, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStringEdString("MP_SVGAME_ADMIN", "SUICIDEBAN")) ); //Com_sprintf ( level.voteString, sizeof(level.voteString ), "clientkick %d", ent->s.number ); //Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "kick %s", ent->client->pers.netname ); //trap_SendConsoleCommand( EXEC_INSERT, va( "banClient %d\n", ent->s.number ) ); trap_SendConsoleCommand( EXEC_INSERT, va( "clientkick %d\n", ent->s.number ) ); return; } if ( g_autoKickKillSpammers.integer > 0 && ent->client->sess.killCount >= g_autoKickKillSpammers.integer ) { trap_SendServerCommand( -1, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStringEdString("MP_SVGAME_ADMIN", "SUICIDEKICK")) ); //Com_sprintf ( level.voteString, sizeof(level.voteString ), "clientkick %d", ent->s.number ); //Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "kick %s", ent->client->pers.netname ); trap_SendConsoleCommand( EXEC_INSERT, va( "clientkick %d\n", ent->s.number ) ); return; } //okay, not gone (yet), but warn them... if ( g_autoBanKillSpammers.integer > 0 && (g_autoKickKillSpammers.integer <= 0 || g_autoBanKillSpammers.integer < g_autoKickKillSpammers.integer) ) {//warn about ban trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME_ADMIN", "WARNINGSUICIDEBAN")) ); } else if ( g_autoKickKillSpammers.integer > 0 ) {//warn about kick trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME_ADMIN", "WARNINGSUICIDEKICK")) ); } } ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = -999; player_die (ent, ent, ent, 100000, MOD_SUICIDE); } gentity_t *G_GetDuelWinner(gclient_t *client) { gclient_t *wCl; int i; for ( i = 0 ; i < level.maxclients ; i++ ) { wCl = &level.clients[i]; if (wCl && wCl != client && /*wCl->ps.clientNum != client->ps.clientNum &&*/ wCl->pers.connected == CON_CONNECTED && wCl->sess.sessionTeam != TEAM_SPECTATOR) { return &g_entities[wCl->ps.clientNum]; } } return NULL; } /* ================= BroadCastTeamChange Let everyone know about a team change ================= */ void BroadcastTeamChange( gclient_t *client, int oldTeam ) { client->ps.fd.forceDoInit = 1; //every time we change teams make sure our force powers are set right if (g_gametype.integer == GT_SIEGE) { //don't announce these things in siege return; } if ( client->sess.sessionTeam == TEAM_RED ) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"", client->pers.netname, G_GetStringEdString("MP_SVGAME", "JOINEDTHEREDTEAM")) ); } else if ( client->sess.sessionTeam == TEAM_BLUE ) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"", client->pers.netname, G_GetStringEdString("MP_SVGAME", "JOINEDTHEBLUETEAM"))); } else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"", client->pers.netname, G_GetStringEdString("MP_SVGAME", "JOINEDTHESPECTATORS"))); } else if ( client->sess.sessionTeam == TEAM_FREE ) { if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) { /* gentity_t *currentWinner = G_GetDuelWinner(client); if (currentWinner && currentWinner->client) { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s %s\n\"", currentWinner->client->pers.netname, G_GetStringEdString("MP_SVGAME", "VERSUS"), client->pers.netname)); } else { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"", client->pers.netname, G_GetStringEdString("MP_SVGAME", "JOINEDTHEBATTLE"))); } */ //NOTE: Just doing a vs. once it counts two players up } else { trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"", client->pers.netname, G_GetStringEdString("MP_SVGAME", "JOINEDTHEBATTLE"))); } } G_LogPrintf ( "setteam: %i %s %s\n", client - &level.clients[0], TeamName ( oldTeam ), TeamName ( client->sess.sessionTeam ) ); } qboolean G_PowerDuelCheckFail(gentity_t *ent) { int loners = 0; int doubles = 0; if (!ent->client || ent->client->sess.duelTeam == DUELTEAM_FREE) { return qtrue; } G_PowerDuelCount(&loners, &doubles, qfalse); if (ent->client->sess.duelTeam == DUELTEAM_LONE && loners >= 1) { return qtrue; } if (ent->client->sess.duelTeam == DUELTEAM_DOUBLE && doubles >= 2) { return qtrue; } return qfalse; } /* ================= SetTeam ================= */ qboolean g_dontPenalizeTeam = qfalse; qboolean g_preventTeamBegin = qfalse; void SetTeam( gentity_t *ent, char *s ) { int team, oldTeam; gclient_t *client; int clientNum; spectatorState_t specState; int specClient; int teamLeader; // // see what change is requested // client = ent->client; clientNum = client - level.clients; specClient = 0; specState = SPECTATOR_NOT; if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_SCOREBOARD; } else if ( !Q_stricmp( s, "follow1" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -1; } else if ( !Q_stricmp( s, "follow2" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -2; } else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FREE; } else if ( g_gametype.integer >= GT_TEAM ) { // if running a team game, assign player to one of the teams specState = SPECTATOR_NOT; if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) { team = TEAM_RED; } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) { team = TEAM_BLUE; } else { // pick the team with the least number of players //For now, don't do this. The legalize function will set powers properly now. /* if (g_forceBasedTeams.integer) { if (ent->client->ps.fd.forceSide == FORCE_LIGHTSIDE) { team = TEAM_BLUE; } else { team = TEAM_RED; } } else { */ team = PickTeam( clientNum ); //} } if ( g_teamForceBalance.integer && !g_trueJedi.integer ) { int counts[TEAM_NUM_TEAMS]; counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED ); // We allow a spread of two if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) { //For now, don't do this. The legalize function will set powers properly now. /* if (g_forceBasedTeams.integer && ent->client->ps.fd.forceSide == FORCE_DARKSIDE) { trap_SendServerCommand( ent->client->ps.clientNum, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TOOMANYRED_SWITCH")) ); } else */ { trap_SendServerCommand( ent->client->ps.clientNum, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TOOMANYRED")) ); } return; // ignore the request } if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) { //For now, don't do this. The legalize function will set powers properly now. /* if (g_forceBasedTeams.integer && ent->client->ps.fd.forceSide == FORCE_LIGHTSIDE) { trap_SendServerCommand( ent->client->ps.clientNum, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TOOMANYBLUE_SWITCH")) ); } else */ { trap_SendServerCommand( ent->client->ps.clientNum, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TOOMANYBLUE")) ); } return; // ignore the request } // It's ok, the team we are switching to has less or same number of players } //For now, don't do this. The legalize function will set powers properly now. /* if (g_forceBasedTeams.integer) { if (team == TEAM_BLUE && ent->client->ps.fd.forceSide != FORCE_LIGHTSIDE) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "MUSTBELIGHT")) ); return; } if (team == TEAM_RED && ent->client->ps.fd.forceSide != FORCE_DARKSIDE) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "MUSTBEDARK")) ); return; } } */ } else { // force them to spectators if there aren't any spots free team = TEAM_FREE; } if (g_gametype.integer == GT_SIEGE) { if (client->tempSpectate >= level.time && team == TEAM_SPECTATOR) { //sorry, can't do that. return; } client->sess.siegeDesiredTeam = team; //oh well, just let them go. /* if (team != TEAM_SPECTATOR) { //can't switch to anything in siege unless you want to switch to being a fulltime spectator //fill them in on their objectives for this team now trap_SendServerCommand(ent-g_entities, va("sb %i", client->sess.siegeDesiredTeam)); trap_SendServerCommand( ent-g_entities, va("print \"You will be on the selected team the next time the round begins.\n\"") ); return; } */ if (client->sess.sessionTeam != TEAM_SPECTATOR && team != TEAM_SPECTATOR) { //not a spectator now, and not switching to spec, so you have to wait til you die. //trap_SendServerCommand( ent-g_entities, va("print \"You will be on the selected team the next time you respawn.\n\"") ); qboolean doBegin; if (ent->client->tempSpectate >= level.time) { doBegin = qfalse; } else { doBegin = qtrue; } if (doBegin) { // Kill them so they automatically respawn in the team they wanted. if (ent->health > 0) { ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; player_die( ent, ent, ent, 100000, MOD_TEAM_CHANGE ); } } if (ent->client->sess.sessionTeam != ent->client->sess.siegeDesiredTeam) { SetTeamQuick(ent, ent->client->sess.siegeDesiredTeam, qfalse); } return; } } // override decision if limiting the players if ( (g_gametype.integer == GT_DUEL) && level.numNonSpectatorClients >= 2 ) { team = TEAM_SPECTATOR; } else if ( (g_gametype.integer == GT_POWERDUEL) && (level.numPlayingClients >= 3 || G_PowerDuelCheckFail(ent)) ) { team = TEAM_SPECTATOR; } else if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer ) { team = TEAM_SPECTATOR; } // // decide if we will allow the change // oldTeam = client->sess.sessionTeam; if ( team == oldTeam && team != TEAM_SPECTATOR ) { return; } // // execute the team change // //If it's siege then show the mission briefing for the team you just joined. // if (g_gametype.integer == GT_SIEGE && team != TEAM_SPECTATOR) // { // trap_SendServerCommand(clientNum, va("sb %i", team)); // } // if the player was dead leave the body if ( client->ps.stats[STAT_HEALTH] <= 0 && client->sess.sessionTeam != TEAM_SPECTATOR ) { MaintainBodyQueue(ent); } // he starts at 'base' client->pers.teamState.state = TEAM_BEGIN; if ( oldTeam != TEAM_SPECTATOR ) { // Kill him (makes sure he loses flags, etc) ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; g_dontPenalizeTeam = qtrue; player_die (ent, ent, ent, 100000, MOD_SUICIDE); g_dontPenalizeTeam = qfalse; } // they go to the end of the line for tournements if ( team == TEAM_SPECTATOR ) { if ( (g_gametype.integer != GT_DUEL) || (oldTeam != TEAM_SPECTATOR) ) {//so you don't get dropped to the bottom of the queue for changing skins, etc. client->sess.spectatorTime = level.time; } } client->sess.sessionTeam = team; client->sess.spectatorState = specState; client->sess.spectatorClient = specClient; client->sess.teamLeader = qfalse; if ( team == TEAM_RED || team == TEAM_BLUE ) { teamLeader = TeamLeader( team ); // if there is no team leader or the team leader is a bot and this client is not a bot if ( teamLeader == -1 || ( !(g_entities[clientNum].r.svFlags & SVF_BOT) && (g_entities[teamLeader].r.svFlags & SVF_BOT) ) ) { //SetLeader( team, clientNum ); } } // make sure there is a team leader on the team the player came from if ( oldTeam == TEAM_RED || oldTeam == TEAM_BLUE ) { CheckTeamLeader( oldTeam ); } BroadcastTeamChange( client, oldTeam ); //make a disappearing effect where they were before teleporting them to the appropriate spawn point, //if we were not on the spec team if (oldTeam != TEAM_SPECTATOR) { gentity_t *tent = G_TempEntity( client->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.clientNum = clientNum; } // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); if (!g_preventTeamBegin) { ClientBegin( clientNum, qfalse ); } } /* ================= StopFollowing If the client being followed leaves the game, or you just want to drop to free floating spectator mode ================= */ void 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; ent->client->ps.weapon = WP_NONE; ent->client->ps.m_iVehicleNum = 0; ent->client->ps.viewangles[ROLL] = 0.0f; ent->client->ps.forceHandExtend = HANDEXTEND_NONE; ent->client->ps.forceHandExtendTime = 0; ent->client->ps.zoomMode = 0; ent->client->ps.zoomLocked = 0; ent->client->ps.zoomLockTime = 0; ent->client->ps.legsAnim = 0; ent->client->ps.legsTimer = 0; ent->client->ps.torsoAnim = 0; ent->client->ps.torsoTimer = 0; } /* ================= Cmd_Team_f ================= */ void Cmd_Team_f( gentity_t *ent ) { int oldTeam; char s[MAX_TOKEN_CHARS]; if ( trap_Argc() != 2 ) { oldTeam = ent->client->sess.sessionTeam; switch ( oldTeam ) { case TEAM_BLUE: trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PRINTBLUETEAM")) ); break; case TEAM_RED: trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PRINTREDTEAM")) ); break; case TEAM_FREE: trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PRINTFREETEAM")) ); break; case TEAM_SPECTATOR: trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PRINTSPECTEAM")) ); break; } return; } if ( ent->client->switchTeamTime > level.time ) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOSWITCH")) ); return; } if (gEscaping) { return; } // if they are playing a tournement game, count as a loss if ( g_gametype.integer == GT_DUEL && ent->client->sess.sessionTeam == TEAM_FREE ) {//in a tournament game //disallow changing teams trap_SendServerCommand( ent-g_entities, "print \"Cannot switch teams in Duel\n\"" ); return; //FIXME: why should this be a loss??? //ent->client->sess.losses++; } if (g_gametype.integer == GT_POWERDUEL) { //don't let clients change teams manually at all in powerduel, it will be taken care of through automated stuff trap_SendServerCommand( ent-g_entities, "print \"Cannot switch teams in Power Duel\n\"" ); return; } trap_Argv( 1, s, sizeof( s ) ); SetTeam( ent, s ); ent->client->switchTeamTime = level.time + 5000; } /* ================= Cmd_DuelTeam_f ================= */ void Cmd_DuelTeam_f(gentity_t *ent) { int oldTeam; char s[MAX_TOKEN_CHARS]; if (g_gametype.integer != GT_POWERDUEL) { //don't bother doing anything if this is not power duel return; } /* if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { trap_SendServerCommand( ent-g_entities, va("print \"You cannot change your duel team unless you are a spectator.\n\"")); return; } */ if ( trap_Argc() != 2 ) { //No arg so tell what team we're currently on. oldTeam = ent->client->sess.duelTeam; switch ( oldTeam ) { case DUELTEAM_FREE: trap_SendServerCommand( ent-g_entities, va("print \"None\n\"") ); break; case DUELTEAM_LONE: trap_SendServerCommand( ent-g_entities, va("print \"Single\n\"") ); break; case DUELTEAM_DOUBLE: trap_SendServerCommand( ent-g_entities, va("print \"Double\n\"") ); break; default: break; } return; } if ( ent->client->switchDuelTeamTime > level.time ) { //debounce for changing trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOSWITCH")) ); return; } trap_Argv( 1, s, sizeof( s ) ); oldTeam = ent->client->sess.duelTeam; if (!Q_stricmp(s, "free")) { ent->client->sess.duelTeam = DUELTEAM_FREE; } else if (!Q_stricmp(s, "single")) { ent->client->sess.duelTeam = DUELTEAM_LONE; } else if (!Q_stricmp(s, "double")) { ent->client->sess.duelTeam = DUELTEAM_DOUBLE; } else { trap_SendServerCommand( ent-g_entities, va("print \"'%s' not a valid duel team.\n\"", s) ); } if (oldTeam == ent->client->sess.duelTeam) { //didn't actually change, so don't care. return; } if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { //ok..die int curTeam = ent->client->sess.duelTeam; ent->client->sess.duelTeam = oldTeam; G_Damage(ent, ent, ent, NULL, ent->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_SUICIDE); ent->client->sess.duelTeam = curTeam; } //reset wins and losses ent->client->sess.wins = 0; ent->client->sess.losses = 0; //get and distribute relevent paramters ClientUserinfoChanged( ent->s.number ); ent->client->switchDuelTeamTime = level.time + 5000; } int G_TeamForSiegeClass(const char *clName) { int i = 0; int team = SIEGETEAM_TEAM1; siegeTeam_t *stm = BG_SiegeFindThemeForTeam(team); siegeClass_t *scl; if (!stm) { return 0; } while (team <= SIEGETEAM_TEAM2) { scl = stm->classes[i]; if (scl && scl->name[0]) { if (!Q_stricmp(clName, scl->name)) { return team; } } i++; if (i >= MAX_SIEGE_CLASSES || i >= stm->numClasses) { if (team == SIEGETEAM_TEAM2) { break; } team = SIEGETEAM_TEAM2; stm = BG_SiegeFindThemeForTeam(team); i = 0; } } return 0; } /* ================= Cmd_SiegeClass_f ================= */ void Cmd_SiegeClass_f( gentity_t *ent ) { char className[64]; int team = 0; int preScore; qboolean startedAsSpec = qfalse; if (g_gametype.integer != GT_SIEGE) { //classes are only valid for this gametype return; } if (!ent->client) { return; } if (trap_Argc() < 1) { return; } if ( ent->client->switchClassTime > level.time ) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOCLASSSWITCH")) ); return; } if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { startedAsSpec = qtrue; } trap_Argv( 1, className, sizeof( className ) ); team = G_TeamForSiegeClass(className); if (!team) { //not a valid class name return; } if (ent->client->sess.sessionTeam != team) { //try changing it then g_preventTeamBegin = qtrue; if (team == TEAM_RED) { SetTeam(ent, "red"); } else if (team == TEAM_BLUE) { SetTeam(ent, "blue"); } g_preventTeamBegin = qfalse; if (ent->client->sess.sessionTeam != team) { //failed, oh well if (ent->client->sess.sessionTeam != TEAM_SPECTATOR || ent->client->sess.siegeDesiredTeam != team) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOCLASSTEAM")) ); return; } } } //preserve 'is score preScore = ent->client->ps.persistant[PERS_SCORE]; //Make sure the class is valid for the team BG_SiegeCheckClassLegality(team, className); //Set the session data strcpy(ent->client->sess.siegeClass, className); // get and distribute relevent paramters ClientUserinfoChanged( ent->s.number ); if (ent->client->tempSpectate < level.time) { // Kill him (makes sure he loses flags, etc) if (ent->health > 0 && !startedAsSpec) { ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; player_die (ent, ent, ent, 100000, MOD_SUICIDE); } if (ent->client->sess.sessionTeam == TEAM_SPECTATOR || startedAsSpec) { //respawn them instantly. ClientBegin( ent->s.number, qfalse ); } } //set it back after we do all the stuff ent->client->ps.persistant[PERS_SCORE] = preScore; ent->client->switchClassTime = level.time + 5000; } /* ================= Cmd_ForceChanged_f ================= */ void Cmd_ForceChanged_f( gentity_t *ent ) { char fpChStr[1024]; const char *buf; // Cmd_Kill_f(ent); if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { //if it's a spec, just make the changes now //trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "FORCEAPPLIED")) ); //No longer print it, as the UI calls this a lot. WP_InitForcePowers( ent ); goto argCheck; } buf = G_GetStringEdString("MP_SVGAME", "FORCEPOWERCHANGED"); strcpy(fpChStr, buf); trap_SendServerCommand( ent-g_entities, va("print \"%s%s\n\n\"", S_COLOR_GREEN, fpChStr) ); ent->client->ps.fd.forceDoInit = 1; argCheck: if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) { //If this is duel, don't even bother changing team in relation to this. return; } if (trap_Argc() > 1) { char arg[MAX_TOKEN_CHARS]; trap_Argv( 1, arg, sizeof( arg ) ); if (arg && arg[0]) { //if there's an arg, assume it's a combo team command from the UI. Cmd_Team_f(ent); } } } extern qboolean WP_SaberStyleValidForSaber( saberInfo_t *saber1, saberInfo_t *saber2, int saberHolstered, int saberAnimLevel ); extern qboolean WP_UseFirstValidSaberStyle( saberInfo_t *saber1, saberInfo_t *saber2, int saberHolstered, int *saberAnimLevel ); qboolean G_SetSaber(gentity_t *ent, int saberNum, char *saberName, qboolean siegeOverride) { char truncSaberName[64]; int i = 0; if (!siegeOverride && g_gametype.integer == GT_SIEGE && ent->client->siegeClass != -1 && ( bgSiegeClasses[ent->client->siegeClass].saberStance || bgSiegeClasses[ent->client->siegeClass].saber1[0] || bgSiegeClasses[ent->client->siegeClass].saber2[0] )) { //don't let it be changed if the siege class has forced any saber-related things return qfalse; } while (saberName[i] && i < 64-1) { truncSaberName[i] = saberName[i]; i++; } truncSaberName[i] = 0; if ( saberNum == 0 && (Q_stricmp( "none", truncSaberName ) == 0 || Q_stricmp( "remove", truncSaberName ) == 0) ) { //can't remove saber 0 like this strcpy(truncSaberName, "Kyle"); } //Set the saber with the arg given. If the arg is //not a valid sabername defaults will be used. WP_SetSaber(ent->s.number, ent->client->saber, saberNum, truncSaberName); if (!ent->client->saber[0].model[0]) { assert(0); //should never happen! strcpy(ent->client->sess.saberType, "none"); } else { strcpy(ent->client->sess.saberType, ent->client->saber[0].name); } if (!ent->client->saber[1].model[0]) { strcpy(ent->client->sess.saber2Type, "none"); } else { strcpy(ent->client->sess.saber2Type, ent->client->saber[1].name); } if ( !WP_SaberStyleValidForSaber( &ent->client->saber[0], &ent->client->saber[1], ent->client->ps.saberHolstered, ent->client->ps.fd.saberAnimLevel ) ) { WP_UseFirstValidSaberStyle( &ent->client->saber[0], &ent->client->saber[1], ent->client->ps.saberHolstered, &ent->client->ps.fd.saberAnimLevel ); ent->client->ps.fd.saberAnimLevelBase = ent->client->saberCycleQueue = ent->client->ps.fd.saberAnimLevel; } return qtrue; } /* ================= 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 ) { StopFollowing( ent ); } return; } trap_Argv( 1, arg, sizeof( arg ) ); i = 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; } // if they are playing a tournement game, count as a loss if ( (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) && ent->client->sess.sessionTeam == TEAM_FREE ) { //WTF??? ent->client->sess.losses++; } // first set them to spectator if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { SetTeam( ent, "spectator" ); } 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; // if they are playing a tournement game, count as a loss if ( (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) && ent->client->sess.sessionTeam == TEAM_FREE ) {\ //WTF??? ent->client->sess.losses++; } // first set them to spectator if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) { SetTeam( ent, "spectator" ); } 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 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 } /* ================== G_Say ================== */ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message, char *locMsg ) { 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; } /* // no chatting to players in tournements if ( (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) && other->client->sess.sessionTeam == TEAM_FREE && ent->client->sess.sessionTeam != TEAM_FREE ) { //Hmm, maybe some option to do so if allowed? Or at least in developer mode... return; } */ //They've requested I take this out. if (g_gametype.integer == GT_SIEGE && ent->client && (ent->client->tempSpectate >= level.time || ent->client->sess.sessionTeam == TEAM_SPECTATOR) && other->client->sess.sessionTeam != TEAM_SPECTATOR && other->client->tempSpectate < level.time) { //siege temp spectators should not communicate to ingame players return; } if (locMsg) { trap_SendServerCommand( other-g_entities, va("%s \"%s\" \"%s\" \"%c\" \"%s\"", mode == SAY_TEAM ? "ltchat" : "lchat", name, locMsg, color, message)); } else { 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]; char *locMsg = NULL; if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) { mode = SAY_ALL; } 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")"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); locMsg = 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 && g_gametype.integer >= GT_TEAM && target->client->sess.sessionTeam == ent->client->sess.sessionTeam && Team_GetLocationMsg(ent, location, sizeof(location))) { Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"]"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); locMsg = 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, locMsg ); 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, locMsg ); } } /* ================== 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 ); } } //siege voice command static void Cmd_VoiceCommand_f(gentity_t *ent) { gentity_t *te; char arg[MAX_TOKEN_CHARS]; char *s; int i = 0; if (g_gametype.integer < GT_TEAM) { return; } if (trap_Argc() < 2) { return; } if (ent->client->sess.sessionTeam == TEAM_SPECTATOR || ent->client->tempSpectate >= level.time) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOVOICECHATASSPEC")) ); return; } trap_Argv(1, arg, sizeof(arg)); if (arg[0] == '*') { //hmm.. don't expect a * to be prepended already. maybe someone is trying to be sneaky. return; } s = va("*%s", arg); //now, make sure it's a valid sound to be playing like this.. so people can't go around //screaming out death sounds or whatever. while (i < MAX_CUSTOM_SIEGE_SOUNDS) { if (!bg_customSiegeSoundNames[i]) { break; } if (!Q_stricmp(bg_customSiegeSoundNames[i], s)) { //it matches this one, so it's ok break; } i++; } if (i == MAX_CUSTOM_SIEGE_SOUNDS || !bg_customSiegeSoundNames[i]) { //didn't find it in the list return; } te = G_TempEntity(vec3_origin, EV_VOICECMD_SOUND); te->s.groundEntityNum = ent->s.number; te->s.eventParm = G_SoundIndex((char *)bg_customSiegeSoundNames[i]); te->r.svFlags |= SVF_BROADCAST; } static char *gc_orders[] = { "hold your position", "hold this position", "come here", "cover me", "guard location", "search and destroy", "report" }; void Cmd_GameCommand_f( gentity_t *ent ) { int player; int order; char str[MAX_TOKEN_CHARS]; trap_Argv( 1, str, sizeof( str ) ); player = atoi( str ); trap_Argv( 2, str, sizeof( str ) ); order = atoi( str ); if ( player < 0 || player >= MAX_CLIENTS ) { return; } if ( order < 0 || order > sizeof(gc_orders)/sizeof(char *) ) { return; } G_Say( ent, &g_entities[player], SAY_TELL, gc_orders[order] ); G_Say( ent, ent, SAY_TELL, gc_orders[order] ); } /* ================== Cmd_Where_f ================== */ void Cmd_Where_f( gentity_t *ent ) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) ); } static const char *gameNames[] = { "Free For All", "Holocron FFA", "Jedi Master", "Duel", "Power Duel", "Single Player", "Team FFA", "Siege", "Capture the Flag", "Capture the Ysalamiri" }; /* ================== G_ClientNumberFromName Finds the client number of the client with the given name ================== */ int G_ClientNumberFromName ( const char* name ) { char s2[MAX_STRING_CHARS]; char n2[MAX_STRING_CHARS]; int i; gclient_t* cl; // check for a name match SanitizeString( (char*)name, s2 ); for ( i=0, cl=level.clients ; i < level.numConnectedClients ; i++, cl++ ) { SanitizeString( cl->pers.netname, n2 ); if ( !strcmp( n2, s2 ) ) { return i; } } return -1; } /* ================== SanitizeString2 Rich's revised version of SanitizeString ================== */ void SanitizeString2( char *in, char *out ) { int i = 0; int r = 0; while (in[i]) { if (i >= MAX_NAME_LENGTH-1) { //the ui truncates the name here.. break; } if (in[i] == '^') { if (in[i+1] >= 48 && //'0' in[i+1] <= 57) //'9' { //only skip it if there's a number after it for the color i += 2; continue; } else { //just skip the ^ i++; continue; } } if (in[i] < 32) { i++; continue; } out[r] = in[i]; r++; i++; } out[r] = 0; } /* ================== G_ClientNumberFromStrippedName Same as above, but strips special characters out of the names before comparing. ================== */ int G_ClientNumberFromStrippedName ( const char* name ) { char s2[MAX_STRING_CHARS]; char n2[MAX_STRING_CHARS]; int i; gclient_t* cl; // check for a name match SanitizeString2( (char*)name, s2 ); for ( i=0, cl=level.clients ; i < level.numConnectedClients ; i++, cl++ ) { SanitizeString2( cl->pers.netname, n2 ); if ( !strcmp( n2, s2 ) ) { return i; } } return -1; } /* ================== Cmd_CallVote_f ================== */ extern void SiegeClearSwitchData(void); //g_saga.c const char *G_GetArenaInfoByMap( const char *map ); void Cmd_CallVote_f( gentity_t *ent ) { char* c; int i; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; // int n = 0; // char* type = NULL; char* mapName = 0; const char* arenaInfo; if ( !g_allowVote.integer ) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOVOTE")) ); return; } if ( level.voteTime || level.voteExecuteTime >= level.time ) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "VOTEINPROGRESS")) ); return; } if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "MAXVOTES")) ); return; } if (g_gametype.integer != GT_DUEL && g_gametype.integer != GT_POWERDUEL) { if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOSPECVOTE")) ); 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; } // check for command separators in arg2 for( c = arg2; *c; ++c) { switch(*c) { case '\n': case '\r': case ';': trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); return; break; } } if ( !Q_stricmp( arg1, "map_restart" ) ) { } else if ( !Q_stricmp( arg1, "nextmap" ) ) { } else if ( !Q_stricmp( arg1, "map" ) ) { } else if ( !Q_stricmp( arg1, "g_gametype" ) ) { } else if ( !Q_stricmp( arg1, "kick" ) ) { } else if ( !Q_stricmp( arg1, "clientkick" ) ) { } else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) { } else if ( !Q_stricmp( arg1, "timelimit" ) ) { } else if ( !Q_stricmp( arg1, "fraglimit" ) ) { } else { trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map , g_gametype , kick , clientkick , g_doWarmup, timelimit