610 lines
12 KiB
C
610 lines
12 KiB
C
// Copyright (C) 2001-2002 Raven Software.
|
|
//
|
|
// cg_consolecmds.c -- text commands typed in at the local console, or
|
|
// executed by a key binding
|
|
|
|
#include "cg_local.h"
|
|
#include "../ui/ui_shared.h"
|
|
#include "../ui/ui_public.h"
|
|
|
|
/*
|
|
=============
|
|
CG_Viewpos_f
|
|
|
|
Debugging command to print the current position
|
|
=============
|
|
*/
|
|
static void CG_Viewpos_f (void)
|
|
{
|
|
Com_Printf ("(%i %i %i) : %i\n", (int)cg.refdef.vieworg[0],
|
|
(int)cg.refdef.vieworg[1], (int)cg.refdef.vieworg[2],
|
|
(int)cg.refdef.viewangles[YAW]);
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_ScoresDown_f
|
|
=============
|
|
*/
|
|
static void CG_ScoresDown_f( void )
|
|
{
|
|
if ( cg.scoresRequestTime + 2000 < cg.time )
|
|
{
|
|
// the scores are more than two seconds out of data,
|
|
// so request new ones
|
|
cg.scoresRequestTime = cg.time;
|
|
trap_SendClientCommand( "score" );
|
|
|
|
// leave the current scores up if they were already
|
|
// displayed, but if this is the first hit, clear them out
|
|
if ( !cg.showScores )
|
|
{
|
|
cg.showScores = qtrue;
|
|
cg.numScores = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// show the cached contents even if they just pressed if it
|
|
// is within two seconds
|
|
cg.showScores = qtrue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_ScoresUp_f
|
|
=============
|
|
*/
|
|
static void CG_ScoresUp_f( void )
|
|
{
|
|
if ( cg.showScores )
|
|
{
|
|
cg.showScores = qfalse;
|
|
cg.scoreFadeTime = cg.time;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_AutomapDown_f
|
|
=============
|
|
*/
|
|
static void CG_AutomapDown_f( void )
|
|
{
|
|
cg.showAutomap = qtrue;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_AutomapUp_f
|
|
=============
|
|
*/
|
|
static void CG_AutomapUp_f( void )
|
|
{
|
|
cg.showAutomap = qfalse;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_ReloadHud_f
|
|
=============
|
|
*/
|
|
static void CG_ReloadHud_f ( void )
|
|
{
|
|
// Reset the string table used for menus
|
|
String_Init();
|
|
|
|
// Clear all menus
|
|
Menu_Reset();
|
|
|
|
// Reload the menus
|
|
CG_LoadMenus ( "ui/hud.txt" );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_Radio_f
|
|
|
|
Bring up the radio menu if all the conditions are met
|
|
=============
|
|
*/
|
|
static void CG_Radio_f ( void )
|
|
{
|
|
// Only in team games
|
|
if ( !cgs.gametypeData->teams )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Not when ghosting or following
|
|
if ( cg.snap->ps.pm_flags & (PMF_FOLLOW|PMF_GHOST) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Not when a spectator
|
|
if ( cg.snap->ps.pm_type == PM_SPECTATOR )
|
|
{
|
|
return;
|
|
}
|
|
|
|
trap_UI_SetActiveMenu ( UIMENU_RADIO );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_Objectives_f
|
|
|
|
Bring up the objectives menu if all the conditions are met
|
|
=============
|
|
*/
|
|
static void CG_Objectives_f ( void )
|
|
{
|
|
// Dont bother popping up the objectives dialog if there is
|
|
// no objective text
|
|
if ( !cgs.gametypeData->description )
|
|
{
|
|
return;
|
|
}
|
|
|
|
trap_UI_SetActiveMenu ( UIMENU_OBJECTIVES );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_Outfitting_f
|
|
|
|
Bring up the outfitting menu if all the conditions are met
|
|
=============
|
|
*/
|
|
static void CG_Outfitting_f ( void )
|
|
{
|
|
// Only allow outfitting when pickups are disabled
|
|
if ( !cgs.pickupsDisabled )
|
|
{
|
|
return;
|
|
}
|
|
|
|
trap_UI_SetActiveMenu ( UIMENU_OUTFITTING );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_Team_f
|
|
|
|
bring up the team selection user interface
|
|
=============
|
|
*/
|
|
static void CG_Team_f ( void )
|
|
{
|
|
// No team menu in non-team games
|
|
if ( !cgs.gametypeData->teams )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Special case which only brings up the team menu if its the clients first
|
|
// time to the objectives dialog
|
|
if ( atoi ( CG_Argv(1) ) )
|
|
{
|
|
if ( ui_info_seenobjectives.integer )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
CG_UpdateTeamCountCvars ( );
|
|
|
|
trap_Cvar_Set ( "ui_info_seenobjectives", "1" );
|
|
trap_UI_SetActiveMenu ( UIMENU_TEAM );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_Drop_f
|
|
|
|
Drops the selected weapon
|
|
=============
|
|
*/
|
|
void CG_Drop_f ( void )
|
|
{
|
|
char cmd[128];
|
|
int exclude;
|
|
|
|
// Cant drop when following or a ghost
|
|
if ( cg.snap->ps.pm_flags & (PMF_FOLLOW|PMF_GHOST) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Either dead or spectating if not normal
|
|
if ( cg.predictedPlayerState.pm_type != PM_NORMAL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Can only drop a weapon when in ready state
|
|
if ( cg.predictedPlayerState.weaponstate != WEAPON_READY )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Cant drop the knife
|
|
if( cg.weaponSelect == WP_KNIFE )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Close the menu since a weapon is being dropped
|
|
cg.weaponMenuUp = qfalse;
|
|
|
|
// Build the server command
|
|
Com_sprintf( cmd, 128, "drop %i", cg.weaponSelect );
|
|
|
|
// Go to next weapon before the current drops
|
|
exclude = cg.weaponSelect;
|
|
cg.weaponSelect = WP_M84_GRENADE;
|
|
CG_PrevWeapon ( qfalse, exclude );
|
|
|
|
// Send server comand
|
|
trap_SendClientCommand( cmd );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_GetOutfittingGroupFromString
|
|
|
|
Converts the given string into an outfitting group id
|
|
=============
|
|
*/
|
|
static int CG_GetOutfittingGroupFromString ( const char* str )
|
|
{
|
|
if ( Q_stricmp ( str, "primary" ) == 0 )
|
|
{
|
|
return OUTFITTING_GROUP_PRIMARY;
|
|
}
|
|
else if ( Q_stricmp ( str, "secondary" ) == 0 )
|
|
{
|
|
return OUTFITTING_GROUP_SECONDARY;
|
|
}
|
|
else if ( Q_stricmp ( str, "pistol" ) == 0 )
|
|
{
|
|
return OUTFITTING_GROUP_PISTOL;
|
|
}
|
|
else if ( Q_stricmp ( str, "grenade" ) == 0 )
|
|
{
|
|
return OUTFITTING_GROUP_GRENADE;
|
|
}
|
|
else if ( Q_stricmp ( str, "knife" ) == 0 )
|
|
{
|
|
return OUTFITTING_GROUP_KNIFE;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_WeaponToggle_f
|
|
|
|
toggles between multiple weapons
|
|
=============
|
|
*/
|
|
static void CG_WeaponToggle_f ( void )
|
|
{
|
|
int group1;
|
|
int group2;
|
|
int weapon1;
|
|
int weapon2;
|
|
gitem_t* item;
|
|
int i;
|
|
|
|
if ( !cg.snap )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( cg.predictedPlayerState.stats[STAT_USEWEAPONDROP] )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( cg.snap->ps.pm_flags & PMF_FOLLOW )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( cg.predictedPlayerState.weaponstate == WEAPON_CHARGING ||
|
|
cg.predictedPlayerState.weaponstate == WEAPON_CHARGING_ALT )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Get the toggle groups
|
|
group1 = CG_GetOutfittingGroupFromString ( CG_Argv(1) );
|
|
group2 = CG_GetOutfittingGroupFromString ( CG_Argv(2) );
|
|
|
|
// Invalid toggle if either is -1
|
|
if ( group1 == -1 ) // || group2 == -1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Make sure they have something from both of the groups
|
|
weapon1 = WP_NONE;
|
|
weapon2 = WP_NONE;
|
|
for ( i = WP_KNIFE; i < WP_NUM_WEAPONS; i ++ )
|
|
{
|
|
// Make sure this weapon is selectable.
|
|
if ( !CG_WeaponSelectable ( i, qtrue ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
item = BG_FindWeaponItem ( i );
|
|
if ( item->outfittingGroup == group1 )
|
|
{
|
|
weapon1 = i;
|
|
}
|
|
else if ( item->outfittingGroup == group2 )
|
|
{
|
|
weapon2 = i;
|
|
}
|
|
}
|
|
|
|
// IF only one of the two weapons is available then go to it
|
|
if ( weapon1 == WP_NONE && weapon2 == WP_NONE )
|
|
{
|
|
return;
|
|
}
|
|
else if ( weapon1 == WP_NONE )
|
|
{
|
|
cg.weaponSelect = weapon2;
|
|
return;
|
|
}
|
|
else if ( weapon2 == WP_NONE )
|
|
{
|
|
cg.weaponSelect = weapon1;
|
|
return;
|
|
}
|
|
|
|
// They have both weapons, so figure out which to go to
|
|
item = BG_FindWeaponItem ( cg.weaponSelect );
|
|
|
|
if ( item->outfittingGroup == group1 )
|
|
{
|
|
cg.weaponSelect = weapon2;
|
|
}
|
|
else
|
|
{
|
|
cg.weaponSelect = weapon1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_NextWeapon_f
|
|
|
|
selects next weapon in inventory and allows empty weapons to
|
|
be selected
|
|
=============
|
|
*/
|
|
static void CG_NextWeapon_f ( void )
|
|
{
|
|
if ( cg_zoomWeaponChange.integer && (cg.predictedPlayerState.pm_flags & PMF_ZOOMED ) )
|
|
{
|
|
trap_SendConsoleCommand ( "+zoomin; wait; -zoomin;" );
|
|
return;
|
|
}
|
|
|
|
CG_NextWeapon ( qtrue, -1 );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_PrevWeapon_f
|
|
|
|
selects previous weapon in inventory and allows empty weapons
|
|
to be selectd
|
|
=============
|
|
*/
|
|
static void CG_PrevWeapon_f ( void )
|
|
{
|
|
if ( cg_zoomWeaponChange.integer && (cg.predictedPlayerState.pm_flags & PMF_ZOOMED) )
|
|
{
|
|
trap_SendConsoleCommand ( "+zoomout; wait; -zoomout;" );
|
|
return;
|
|
}
|
|
|
|
CG_PrevWeapon ( qtrue, -1 );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CG_LastWeapon_f
|
|
|
|
Selects the last weapon that was selected
|
|
=============
|
|
*/
|
|
static void CG_LastWeapon_f ( void )
|
|
{
|
|
if ( CG_WeaponSelectable ( cg.weaponLastSelect, qtrue ) )
|
|
{
|
|
int swap = cg.weaponSelect;
|
|
cg.weaponSelect = cg.weaponLastSelect;
|
|
cg.weaponLastSelect = swap;
|
|
}
|
|
}
|
|
|
|
|
|
static void CG_TellTarget_f( void ) {
|
|
int clientNum;
|
|
char command[128];
|
|
char message[128];
|
|
|
|
clientNum = CG_CrosshairPlayer();
|
|
if ( clientNum == -1 ) {
|
|
return;
|
|
}
|
|
|
|
trap_Args( message, 128 );
|
|
Com_sprintf( command, 128, "tell %i %s", clientNum, message );
|
|
trap_SendClientCommand( command );
|
|
}
|
|
|
|
static void CG_TellAttacker_f( void ) {
|
|
int clientNum;
|
|
char command[128];
|
|
char message[128];
|
|
|
|
clientNum = CG_LastAttacker();
|
|
if ( clientNum == -1 ) {
|
|
return;
|
|
}
|
|
|
|
trap_Args( message, 128 );
|
|
Com_sprintf( command, 128, "tell %i %s", clientNum, message );
|
|
trap_SendClientCommand( command );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_StartOrbit_f
|
|
==================
|
|
*/
|
|
|
|
static void CG_StartOrbit_f( void ) {
|
|
char var[MAX_TOKEN_CHARS];
|
|
|
|
trap_Cvar_VariableStringBuffer( "developer", var, sizeof( var ) );
|
|
if ( !atoi(var) ) {
|
|
return;
|
|
}
|
|
if (cg_cameraOrbit.value != 0) {
|
|
trap_Cvar_Set ("cg_cameraOrbit", "0");
|
|
trap_Cvar_Set("cg_thirdPerson", "0");
|
|
} else {
|
|
trap_Cvar_Set("cg_cameraOrbit", "5");
|
|
trap_Cvar_Set("cg_thirdPerson", "1");
|
|
trap_Cvar_Set("cg_thirdPersonAngle", "0");
|
|
trap_Cvar_Set("cg_thirdPersonRange", "100");
|
|
}
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
char *cmd;
|
|
void (*function)(void);
|
|
|
|
} consoleCommand_t;
|
|
|
|
static consoleCommand_t commands[] =
|
|
{
|
|
{ "testmodel", CG_TestModel_f },
|
|
{ "nextframe", CG_TestModelNextFrame_f },
|
|
{ "prevframe", CG_TestModelPrevFrame_f },
|
|
{ "nextskin", CG_TestModelNextSkin_f },
|
|
{ "prevskin", CG_TestModelPrevSkin_f },
|
|
{ "viewpos", CG_Viewpos_f },
|
|
{ "+scores", CG_ScoresDown_f },
|
|
{ "-scores", CG_ScoresUp_f },
|
|
{ "+automap", CG_AutomapDown_f },
|
|
{ "-automap", CG_AutomapUp_f },
|
|
{ "weapnext", CG_NextWeapon_f },
|
|
{ "weapprev", CG_PrevWeapon_f },
|
|
{ "weaplast", CG_LastWeapon_f },
|
|
{ "weapon", CG_Weapon_f },
|
|
{ "tell_target", CG_TellTarget_f },
|
|
{ "tell_attacker", CG_TellAttacker_f },
|
|
{ "reloadhud", CG_ReloadHud_f },
|
|
{ "startOrbit", CG_StartOrbit_f },
|
|
{ "loaddeferred", CG_LoadDeferredPlayers },
|
|
{ "drop", CG_Drop_f },
|
|
|
|
{ "weaptoggle", CG_WeaponToggle_f },
|
|
|
|
{ "ui_radio", CG_Radio_f },
|
|
{ "ui_objectives", CG_Objectives_f },
|
|
{ "ui_outfitting", CG_Outfitting_f },
|
|
{ "ui_team", CG_Team_f },
|
|
};
|
|
|
|
/*
|
|
=================
|
|
CG_ConsoleCommand
|
|
|
|
The string has been tokenized and can be retrieved with
|
|
Cmd_Argc() / Cmd_Argv()
|
|
=================
|
|
*/
|
|
qboolean CG_ConsoleCommand( void )
|
|
{
|
|
const char *cmd;
|
|
int i;
|
|
|
|
// No console commands when a map is changing
|
|
if ( cg.mMapChange )
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
cmd = CG_Argv(0);
|
|
|
|
for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ )
|
|
{
|
|
if ( !Q_stricmp( cmd, commands[i].cmd ) )
|
|
{
|
|
commands[i].function();
|
|
return qtrue;
|
|
}
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_InitConsoleCommands
|
|
|
|
Let the client system know about all of our commands
|
|
so it can perform tab completion
|
|
=================
|
|
*/
|
|
void CG_InitConsoleCommands( void )
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ )
|
|
{
|
|
trap_AddCommand( commands[i].cmd );
|
|
}
|
|
|
|
//
|
|
// the game server will interpret these commands, which will be automatically
|
|
// forwarded to the server after they are not recognized locally
|
|
//
|
|
trap_AddCommand ("kill");
|
|
trap_AddCommand ("say");
|
|
trap_AddCommand ("ignore");
|
|
trap_AddCommand ("say_team");
|
|
trap_AddCommand ("tell");
|
|
trap_AddCommand ("vsay_team");
|
|
trap_AddCommand ("give");
|
|
trap_AddCommand ("god");
|
|
trap_AddCommand ("notarget");
|
|
trap_AddCommand ("noclip");
|
|
trap_AddCommand ("team");
|
|
trap_AddCommand ("follow");
|
|
trap_AddCommand ("levelshot");
|
|
#ifdef _SOF2_BOTS
|
|
trap_AddCommand ("addbot");
|
|
#endif
|
|
trap_AddCommand ("setviewpos");
|
|
trap_AddCommand ("callvote");
|
|
trap_AddCommand ("vote");
|
|
trap_AddCommand ("stats");
|
|
trap_AddCommand ("teamtask");
|
|
trap_AddCommand ("loaddeferred");
|
|
trap_AddCommand ("gametype_restart");
|
|
}
|