mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2024-11-10 07:21:58 +00:00
07bfab72db
Don't require the player to be dead for the 'autoload' console function to work. Reset autoload info on 'map' and 'changelevel'. Add future ideas for autosave criteria.
2372 lines
48 KiB
C
2372 lines
48 KiB
C
/*
|
|
Copyright (C) 1996-2001 Id Software, Inc.
|
|
Copyright (C) 2002-2009 John Fitzgibbons and others
|
|
Copyright (C) 2007-2008 Kristian Duske
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
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.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
#include "q_stdinc.h"
|
|
#include "arch_def.h"
|
|
#include "net_sys.h" /* for net_defs.h */
|
|
#include <dirent.h>
|
|
#include "quakedef.h"
|
|
#include "net_defs.h" /* for struct qsocket_s details */
|
|
|
|
extern cvar_t pausable;
|
|
|
|
extern int com_nummissionpacks; //johnfitz
|
|
|
|
int current_skill;
|
|
|
|
char lastsavename[MAX_QPATH]; // autosave
|
|
char lastsavemapname[MAX_QPATH]; // autosave
|
|
|
|
void Mod_Print (void);
|
|
|
|
static qboolean _autoloadIfPossible();
|
|
static void _resetAutoload();
|
|
|
|
/*
|
|
==================
|
|
Host_Quit_f
|
|
==================
|
|
*/
|
|
|
|
void Host_Quit_f (void)
|
|
{
|
|
if (key_dest != key_console && cls.state != ca_dedicated)
|
|
{
|
|
M_Menu_Quit_f ();
|
|
return;
|
|
}
|
|
CL_Disconnect ();
|
|
Host_ShutdownServer(false);
|
|
|
|
Sys_Quit ();
|
|
}
|
|
|
|
//==============================================================================
|
|
//johnfitz -- dynamic gamedir stuff
|
|
//==============================================================================
|
|
|
|
// Declarations shared with common.c:
|
|
typedef struct
|
|
{
|
|
char name[MAX_QPATH];
|
|
int filepos, filelen;
|
|
} packfile_t;
|
|
|
|
typedef struct pack_s
|
|
{
|
|
char filename[MAX_OSPATH];
|
|
int handle;
|
|
int numfiles;
|
|
packfile_t *files;
|
|
} pack_t;
|
|
|
|
typedef struct searchpath_s
|
|
{
|
|
char filename[MAX_OSPATH];
|
|
pack_t *pack; // only one of filename / pack will be used
|
|
struct searchpath_s *next;
|
|
} searchpath_t;
|
|
|
|
extern qboolean com_modified;
|
|
extern searchpath_t *com_searchpaths;
|
|
pack_t *COM_LoadPackFile (const char *packfile);
|
|
|
|
// Kill all the search packs until the game path is found. Kill it, then return
|
|
// the next path to it.
|
|
void KillGameDir(searchpath_t *search)
|
|
{
|
|
searchpath_t *search_killer;
|
|
while (search)
|
|
{
|
|
if (*search->filename)
|
|
{
|
|
com_searchpaths = search->next;
|
|
Z_Free(search);
|
|
return; //once you hit the dir, youve already freed the paks
|
|
}
|
|
Sys_FileClose (search->pack->handle); //johnfitz
|
|
search_killer = search->next;
|
|
Z_Free(search->pack->files);
|
|
Z_Free(search->pack);
|
|
Z_Free(search);
|
|
search = search_killer;
|
|
}
|
|
}
|
|
|
|
// Return the number of games in memory
|
|
int NumGames(searchpath_t *search)
|
|
{
|
|
int found = 0;
|
|
while (search)
|
|
{
|
|
if (*search->filename)
|
|
found++;
|
|
search = search->next;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
void ExtraMaps_NewGame (void);
|
|
|
|
/*
|
|
==================
|
|
Host_Game_f
|
|
==================
|
|
*/
|
|
void Host_Game_f (void)
|
|
{
|
|
int i;
|
|
searchpath_t *search = com_searchpaths;
|
|
pack_t *pak;
|
|
char pakfile[MAX_OSPATH]; //FIXME: it's confusing to use this string for two different things
|
|
|
|
if (Cmd_Argc() > 1)
|
|
{
|
|
|
|
if (!registered.value) //disable command for shareware quake
|
|
{
|
|
Con_Printf("You must have the registered version to use modified games\n");
|
|
return;
|
|
}
|
|
|
|
if (strstr(Cmd_Argv(1), ".."))
|
|
{
|
|
Con_Printf ("Relative pathnames are not allowed.\n");
|
|
return;
|
|
}
|
|
|
|
strcpy (pakfile, va("%s/%s", host_parms.basedir, Cmd_Argv(1)));
|
|
if (!Q_strcasecmp(pakfile, com_gamedir)) //no change
|
|
{
|
|
Con_Printf("\"game\" is already \"%s\"\n", COM_SkipPath(com_gamedir));
|
|
return;
|
|
}
|
|
|
|
com_modified = true;
|
|
|
|
//Kill the server
|
|
CL_Disconnect ();
|
|
Host_ShutdownServer(true);
|
|
|
|
//Write config file
|
|
Host_WriteConfiguration ();
|
|
|
|
//Kill the extra game if it is loaded
|
|
if (NumGames(com_searchpaths) > 1 + com_nummissionpacks)
|
|
KillGameDir(com_searchpaths);
|
|
|
|
strcpy (com_gamedir, pakfile);
|
|
|
|
if (Q_strcasecmp(Cmd_Argv(1), GAMENAME)) //game is not id1
|
|
{
|
|
search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t));
|
|
strcpy (search->filename, pakfile);
|
|
search->next = com_searchpaths;
|
|
com_searchpaths = search;
|
|
|
|
//Load the paks if any are found:
|
|
for (i = 0; ; i++)
|
|
{
|
|
sprintf (pakfile, "%s/pak%i.pak", com_gamedir, i);
|
|
pak = COM_LoadPackFile (pakfile);
|
|
if (!pak)
|
|
break;
|
|
search = (searchpath_t *) Z_Malloc(sizeof(searchpath_t));
|
|
search->pack = pak;
|
|
search->next = com_searchpaths;
|
|
com_searchpaths = search;
|
|
}
|
|
}
|
|
|
|
//clear out and reload appropriate data
|
|
Cache_Flush ();
|
|
if (!isDedicated)
|
|
{
|
|
TexMgr_NewGame ();
|
|
Draw_NewGame ();
|
|
R_NewGame ();
|
|
}
|
|
ExtraMaps_NewGame ();
|
|
//Cbuf_InsertText ("exec quake.rc\n");
|
|
|
|
Con_Printf("\"game\" changed to \"%s\"\n", COM_SkipPath(com_gamedir));
|
|
}
|
|
else //Diplay the current gamedir
|
|
Con_Printf("\"game\" is \"%s\"\n", COM_SkipPath(com_gamedir));
|
|
}
|
|
|
|
//==============================================================================
|
|
//johnfitz -- extramaps management
|
|
//==============================================================================
|
|
|
|
typedef struct extralevel_s
|
|
{
|
|
char name[32];
|
|
struct extralevel_s *next;
|
|
} extralevel_t;
|
|
|
|
extralevel_t *extralevels;
|
|
|
|
void ExtraMaps_Add (const char *name)
|
|
{
|
|
extralevel_t *level,*cursor,*prev;
|
|
|
|
// ignore duplicate
|
|
for (level = extralevels; level; level = level->next)
|
|
{
|
|
if (!Q_strcmp (name, level->name))
|
|
return;
|
|
}
|
|
|
|
level = (extralevel_t *) Z_Malloc(sizeof(extralevel_t));
|
|
strcpy (level->name, name);
|
|
|
|
// insert each entry in alphabetical order
|
|
if (extralevels == NULL ||
|
|
Q_strcasecmp(level->name, extralevels->name) < 0) //insert at front
|
|
{
|
|
level->next = extralevels;
|
|
extralevels = level;
|
|
}
|
|
else //insert later
|
|
{
|
|
prev = extralevels;
|
|
cursor = extralevels->next;
|
|
while (cursor && (Q_strcasecmp(level->name, cursor->name) > 0))
|
|
{
|
|
prev = cursor;
|
|
cursor = cursor->next;
|
|
}
|
|
level->next = prev->next;
|
|
prev->next = level;
|
|
}
|
|
}
|
|
|
|
void ExtraMaps_Init (void)
|
|
{
|
|
DIR *dir_p;
|
|
struct dirent *dir_t;
|
|
char filestring[MAX_OSPATH];
|
|
char mapname[32];
|
|
char ignorepakdir[32];
|
|
searchpath_t *search;
|
|
pack_t *pak;
|
|
int i;
|
|
|
|
// we don't want to list the maps in id1 pakfiles,
|
|
// because these are not "add-on" levels
|
|
sprintf (ignorepakdir, "/%s/", GAMENAME);
|
|
|
|
for (search = com_searchpaths; search; search = search->next)
|
|
{
|
|
if (*search->filename) //directory
|
|
{
|
|
sprintf (filestring,"%s/maps/",search->filename);
|
|
dir_p = opendir(filestring);
|
|
if (dir_p == NULL)
|
|
continue;
|
|
while ((dir_t = readdir(dir_p)) != NULL)
|
|
{
|
|
if (!strstr(dir_t->d_name, ".bsp") && !strstr(dir_t->d_name, ".BSP"))
|
|
continue;
|
|
COM_StripExtension(dir_t->d_name, mapname);
|
|
ExtraMaps_Add (mapname);
|
|
}
|
|
closedir(dir_p);
|
|
}
|
|
else //pakfile
|
|
{
|
|
if (!strstr(search->pack->filename, ignorepakdir))
|
|
{ //don't list standard id maps
|
|
for (i = 0, pak = search->pack; i < pak->numfiles; i++)
|
|
{
|
|
if (strstr(pak->files[i].name, ".bsp"))
|
|
{
|
|
if (pak->files[i].filelen > 32*1024)
|
|
{ // don't list files under 32k (ammo boxes etc)
|
|
COM_StripExtension(pak->files[i].name + 5, mapname);
|
|
ExtraMaps_Add (mapname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExtraMaps_Clear (void)
|
|
{
|
|
extralevel_t *blah;
|
|
|
|
while (extralevels)
|
|
{
|
|
blah = extralevels->next;
|
|
Z_Free(extralevels);
|
|
extralevels = blah;
|
|
}
|
|
}
|
|
|
|
void ExtraMaps_NewGame (void)
|
|
{
|
|
ExtraMaps_Clear ();
|
|
ExtraMaps_Init ();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Maps_f
|
|
==================
|
|
*/
|
|
void Host_Maps_f (void)
|
|
{
|
|
int i;
|
|
extralevel_t *level;
|
|
|
|
for (level = extralevels, i = 0; level; level = level->next, i++)
|
|
Con_SafePrintf (" %s\n", level->name);
|
|
|
|
if (i)
|
|
Con_SafePrintf ("%i map(s)\n", i);
|
|
else
|
|
Con_SafePrintf ("no maps found\n");
|
|
}
|
|
|
|
//==============================================================================
|
|
//johnfitz -- modlist management
|
|
//==============================================================================
|
|
|
|
typedef struct mod_s
|
|
{
|
|
char name[MAX_OSPATH];
|
|
struct mod_s *next;
|
|
} mod_t;
|
|
|
|
mod_t *modlist;
|
|
|
|
void Modlist_Add (const char *name)
|
|
{
|
|
mod_t *mod,*cursor,*prev;
|
|
|
|
//ingore duplicate
|
|
for (mod = modlist; mod; mod = mod->next)
|
|
{
|
|
if (!Q_strcmp (name, mod->name))
|
|
return;
|
|
}
|
|
|
|
mod = (mod_t *) Z_Malloc(sizeof(mod_t));
|
|
strcpy (mod->name, name);
|
|
|
|
//insert each entry in alphabetical order
|
|
if (modlist == NULL ||
|
|
Q_strcasecmp(mod->name, modlist->name) < 0) //insert at front
|
|
{
|
|
mod->next = modlist;
|
|
modlist = mod;
|
|
}
|
|
else //insert later
|
|
{
|
|
prev = modlist;
|
|
cursor = modlist->next;
|
|
while (cursor && (Q_strcasecmp(mod->name, cursor->name) > 0))
|
|
{
|
|
prev = cursor;
|
|
cursor = cursor->next;
|
|
}
|
|
mod->next = prev->next;
|
|
prev->next = mod;
|
|
}
|
|
}
|
|
|
|
void Modlist_Init (void)
|
|
{
|
|
DIR *dir_p, *mod_dir_p;
|
|
struct dirent *dir_t, *mod_dir_t;
|
|
qboolean progs_found, pak_found;
|
|
char dir_string[MAX_OSPATH], mod_dir_string[MAX_OSPATH];
|
|
int i;
|
|
|
|
i = COM_CheckParm ("-basedir");
|
|
if (i && i < com_argc-1)
|
|
sprintf (dir_string, "%s/", com_argv[i+1]);
|
|
else
|
|
sprintf (dir_string, "%s/", host_parms.basedir);
|
|
|
|
dir_p = opendir(dir_string);
|
|
if (dir_p == NULL)
|
|
return;
|
|
|
|
while ((dir_t = readdir(dir_p)) != NULL)
|
|
{
|
|
if ((strcmp(dir_t->d_name, ".") == 0) || (strcmp(dir_t->d_name, "..") == 0))
|
|
continue;
|
|
sprintf(mod_dir_string, "%s%s/", dir_string, dir_t->d_name);
|
|
mod_dir_p = opendir(mod_dir_string);
|
|
if (mod_dir_p == NULL)
|
|
continue;
|
|
progs_found = false;
|
|
pak_found = false;
|
|
// find progs.dat and pak file(s)
|
|
while ((mod_dir_t = readdir(mod_dir_p)) != NULL)
|
|
{
|
|
if ((strcmp(mod_dir_t->d_name, ".") == 0) || (strcmp(mod_dir_t->d_name, "..") == 0))
|
|
continue;
|
|
if (Q_strcasecmp(mod_dir_t->d_name, "progs.dat") == 0)
|
|
progs_found = true;
|
|
if (strstr(mod_dir_t->d_name, ".pak") || strstr(mod_dir_t->d_name, ".PAK"))
|
|
pak_found = true;
|
|
if (progs_found || pak_found)
|
|
break;
|
|
}
|
|
closedir(mod_dir_p);
|
|
if (!progs_found && !pak_found)
|
|
continue;
|
|
Modlist_Add(dir_t->d_name);
|
|
}
|
|
|
|
closedir(dir_p);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Mods_f -- johnfitz
|
|
|
|
list all potential mod directories (contain either a pak file or a progs.dat)
|
|
==================
|
|
*/
|
|
void Host_Mods_f (void)
|
|
{
|
|
int i;
|
|
mod_t *mod;
|
|
|
|
for (mod = modlist, i=0; mod; mod = mod->next, i++)
|
|
Con_SafePrintf (" %s\n", mod->name);
|
|
|
|
if (i)
|
|
Con_SafePrintf ("%i mod(s)\n", i);
|
|
else
|
|
Con_SafePrintf ("no mods found\n");
|
|
}
|
|
|
|
//==============================================================================
|
|
|
|
/*
|
|
=============
|
|
Host_Mapname_f -- johnfitz
|
|
=============
|
|
*/
|
|
void Host_Mapname_f (void)
|
|
{
|
|
if (sv.active)
|
|
{
|
|
Con_Printf ("\"mapname\" is \"%s\"\n", sv.name);
|
|
return;
|
|
}
|
|
|
|
if (cls.state == ca_connected)
|
|
{
|
|
Con_Printf ("\"mapname\" is \"%s\"\n", cl.mapname);
|
|
return;
|
|
}
|
|
|
|
Con_Printf ("no map loaded\n");
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Status_f
|
|
==================
|
|
*/
|
|
void Host_Status_f (void)
|
|
{
|
|
client_t *client;
|
|
int seconds;
|
|
int minutes;
|
|
int hours = 0;
|
|
int j;
|
|
void (*print_fn) (const char *fmt, ...) __fp_attribute__((__format__(__printf__,1,2)));
|
|
|
|
if (cmd_source == src_command)
|
|
{
|
|
if (!sv.active)
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
print_fn = Con_Printf;
|
|
}
|
|
else
|
|
print_fn = SV_ClientPrintf;
|
|
|
|
print_fn ("host: %s\n", Cvar_VariableString ("hostname"));
|
|
print_fn ("version: %4.2f\n", VERSION);
|
|
if (tcpipAvailable)
|
|
print_fn ("tcp/ip: %s\n", my_tcpip_address);
|
|
if (ipxAvailable)
|
|
print_fn ("ipx: %s\n", my_ipx_address);
|
|
print_fn ("map: %s\n", sv.name);
|
|
print_fn ("players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients);
|
|
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
|
|
{
|
|
if (!client->active)
|
|
continue;
|
|
seconds = (int)(net_time - client->netconnection->connecttime);
|
|
minutes = seconds / 60;
|
|
if (minutes)
|
|
{
|
|
seconds -= (minutes * 60);
|
|
hours = minutes / 60;
|
|
if (hours)
|
|
minutes -= (hours * 60);
|
|
}
|
|
else
|
|
hours = 0;
|
|
print_fn ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->v.frags, hours, minutes, seconds);
|
|
print_fn (" %s\n", client->netconnection->address);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_God_f
|
|
|
|
Sets client to godmode
|
|
==================
|
|
*/
|
|
void Host_God_f (void)
|
|
{
|
|
if (cmd_source == src_command)
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
|
|
if (pr_global_struct->deathmatch && !host_client->privileged)
|
|
return;
|
|
|
|
//johnfitz -- allow user to explicitly set god mode to on or off
|
|
switch (Cmd_Argc())
|
|
{
|
|
case 1:
|
|
sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE;
|
|
if (!((int)sv_player->v.flags & FL_GODMODE) )
|
|
SV_ClientPrintf ("godmode OFF\n");
|
|
else
|
|
SV_ClientPrintf ("godmode ON\n");
|
|
break;
|
|
case 2:
|
|
if (Q_atof(Cmd_Argv(1)))
|
|
{
|
|
sv_player->v.flags = (int)sv_player->v.flags | FL_GODMODE;
|
|
SV_ClientPrintf ("godmode ON\n");
|
|
}
|
|
else
|
|
{
|
|
sv_player->v.flags = (int)sv_player->v.flags & ~FL_GODMODE;
|
|
SV_ClientPrintf ("godmode OFF\n");
|
|
}
|
|
break;
|
|
default:
|
|
Con_Printf("god [value] : toggle god mode. values: 0 = off, 1 = on\n");
|
|
break;
|
|
}
|
|
//johnfitz
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Notarget_f
|
|
==================
|
|
*/
|
|
void Host_Notarget_f (void)
|
|
{
|
|
if (cmd_source == src_command)
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
|
|
if (pr_global_struct->deathmatch && !host_client->privileged)
|
|
return;
|
|
|
|
//johnfitz -- allow user to explicitly set notarget to on or off
|
|
switch (Cmd_Argc())
|
|
{
|
|
case 1:
|
|
sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET;
|
|
if (!((int)sv_player->v.flags & FL_NOTARGET) )
|
|
SV_ClientPrintf ("notarget OFF\n");
|
|
else
|
|
SV_ClientPrintf ("notarget ON\n");
|
|
break;
|
|
case 2:
|
|
if (Q_atof(Cmd_Argv(1)))
|
|
{
|
|
sv_player->v.flags = (int)sv_player->v.flags | FL_NOTARGET;
|
|
SV_ClientPrintf ("notarget ON\n");
|
|
}
|
|
else
|
|
{
|
|
sv_player->v.flags = (int)sv_player->v.flags & ~FL_NOTARGET;
|
|
SV_ClientPrintf ("notarget OFF\n");
|
|
}
|
|
break;
|
|
default:
|
|
Con_Printf("notarget [value] : toggle notarget mode. values: 0 = off, 1 = on\n");
|
|
break;
|
|
}
|
|
//johnfitz
|
|
}
|
|
|
|
qboolean noclip_anglehack;
|
|
|
|
/*
|
|
==================
|
|
Host_Noclip_f
|
|
==================
|
|
*/
|
|
void Host_Noclip_f (void)
|
|
{
|
|
if (cmd_source == src_command)
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
|
|
if (pr_global_struct->deathmatch && !host_client->privileged)
|
|
return;
|
|
|
|
//johnfitz -- allow user to explicitly set noclip to on or off
|
|
switch (Cmd_Argc())
|
|
{
|
|
case 1:
|
|
if (sv_player->v.movetype != MOVETYPE_NOCLIP)
|
|
{
|
|
noclip_anglehack = true;
|
|
sv_player->v.movetype = MOVETYPE_NOCLIP;
|
|
SV_ClientPrintf ("noclip ON\n");
|
|
}
|
|
else
|
|
{
|
|
noclip_anglehack = false;
|
|
sv_player->v.movetype = MOVETYPE_WALK;
|
|
SV_ClientPrintf ("noclip OFF\n");
|
|
}
|
|
break;
|
|
case 2:
|
|
if (Q_atof(Cmd_Argv(1)))
|
|
{
|
|
noclip_anglehack = true;
|
|
sv_player->v.movetype = MOVETYPE_NOCLIP;
|
|
SV_ClientPrintf ("noclip ON\n");
|
|
}
|
|
else
|
|
{
|
|
noclip_anglehack = false;
|
|
sv_player->v.movetype = MOVETYPE_WALK;
|
|
SV_ClientPrintf ("noclip OFF\n");
|
|
}
|
|
break;
|
|
default:
|
|
Con_Printf("noclip [value] : toggle noclip mode. values: 0 = off, 1 = on\n");
|
|
break;
|
|
}
|
|
//johnfitz
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Fly_f
|
|
|
|
Sets client to flymode
|
|
==================
|
|
*/
|
|
void Host_Fly_f (void)
|
|
{
|
|
if (cmd_source == src_command)
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
|
|
if (pr_global_struct->deathmatch && !host_client->privileged)
|
|
return;
|
|
|
|
//johnfitz -- allow user to explicitly set noclip to on or off
|
|
switch (Cmd_Argc())
|
|
{
|
|
case 1:
|
|
if (sv_player->v.movetype != MOVETYPE_FLY)
|
|
{
|
|
sv_player->v.movetype = MOVETYPE_FLY;
|
|
SV_ClientPrintf ("flymode ON\n");
|
|
}
|
|
else
|
|
{
|
|
sv_player->v.movetype = MOVETYPE_WALK;
|
|
SV_ClientPrintf ("flymode OFF\n");
|
|
}
|
|
break;
|
|
case 2:
|
|
if (Q_atof(Cmd_Argv(1)))
|
|
{
|
|
sv_player->v.movetype = MOVETYPE_FLY;
|
|
SV_ClientPrintf ("flymode ON\n");
|
|
}
|
|
else
|
|
{
|
|
sv_player->v.movetype = MOVETYPE_WALK;
|
|
SV_ClientPrintf ("flymode OFF\n");
|
|
}
|
|
break;
|
|
default:
|
|
Con_Printf("fly [value] : toggle fly mode. values: 0 = off, 1 = on\n");
|
|
break;
|
|
}
|
|
//johnfitz
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
Host_Ping_f
|
|
|
|
==================
|
|
*/
|
|
void Host_Ping_f (void)
|
|
{
|
|
int i, j;
|
|
float total;
|
|
client_t *client;
|
|
|
|
if (cmd_source == src_command)
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
|
|
SV_ClientPrintf ("Client ping times:\n");
|
|
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++)
|
|
{
|
|
if (!client->active)
|
|
continue;
|
|
total = 0;
|
|
for (j = 0; j < NUM_PING_TIMES; j++)
|
|
total+=client->ping_times[j];
|
|
total /= NUM_PING_TIMES;
|
|
SV_ClientPrintf ("%4i %s\n", (int)(total*1000), client->name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
SERVER TRANSITIONS
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
======================
|
|
Host_Map_f
|
|
|
|
handle a
|
|
map <servername>
|
|
command from the console. Active clients are kicked off.
|
|
======================
|
|
*/
|
|
void Host_Map_f (void)
|
|
{
|
|
int i;
|
|
char name[MAX_QPATH], *p;
|
|
|
|
if (Cmd_Argc() < 2) //no map name given
|
|
{
|
|
if (cls.state == ca_dedicated)
|
|
{
|
|
if (sv.active)
|
|
Con_Printf ("Current map: %s\n", sv.name);
|
|
else
|
|
Con_Printf ("Server not active\n");
|
|
}
|
|
else if (cls.state == ca_connected)
|
|
{
|
|
Con_Printf ("Current map: %s ( %s )\n", cl.levelname, cl.mapname);
|
|
}
|
|
else
|
|
{
|
|
Con_Printf ("map <levelname>: start a new server\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (cmd_source != src_command)
|
|
return;
|
|
|
|
cls.demonum = -1; // stop demo loop in case this fails
|
|
|
|
CL_Disconnect ();
|
|
Host_ShutdownServer(false);
|
|
|
|
if (cls.state != ca_dedicated)
|
|
IN_Activate();
|
|
key_dest = key_game; // remove console or menu
|
|
SCR_BeginLoadingPlaque ();
|
|
|
|
_resetAutoload(); // autosave
|
|
|
|
svs.serverflags = 0; // haven't completed an episode yet
|
|
strcpy (name, Cmd_Argv(1));
|
|
// remove (any) trailing ".bsp" from mapname S.A.
|
|
p = strstr(name, ".bsp");
|
|
if (p && p[4] == '\0')
|
|
*p = '\0';
|
|
SV_SpawnServer (name);
|
|
if (!sv.active)
|
|
return;
|
|
|
|
if (cls.state != ca_dedicated)
|
|
{
|
|
strcpy (cls.spawnparms, "");
|
|
|
|
for (i = 2; i < Cmd_Argc(); i++)
|
|
{
|
|
strcat (cls.spawnparms, Cmd_Argv(i));
|
|
strcat (cls.spawnparms, " ");
|
|
}
|
|
|
|
Cmd_ExecuteString ("connect local", src_command);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Changelevel_f
|
|
|
|
Goes to a new map, taking all clients along
|
|
==================
|
|
*/
|
|
void Host_Changelevel_f (void)
|
|
{
|
|
char level[MAX_QPATH];
|
|
int i; //johnfitz
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_Printf ("changelevel <levelname> : continue game on a new level\n");
|
|
return;
|
|
}
|
|
if (!sv.active || cls.demoplayback)
|
|
{
|
|
Con_Printf ("Only the server may changelevel\n");
|
|
return;
|
|
}
|
|
|
|
//johnfitz -- check for client having map before anything else
|
|
sprintf (level, "maps/%s.bsp", Cmd_Argv(1));
|
|
if (COM_OpenFile (level, &i) == -1)
|
|
Host_Error ("cannot find map %s", level);
|
|
//johnfitz
|
|
|
|
if (cls.state != ca_dedicated)
|
|
IN_Activate(); // -- S.A.
|
|
key_dest = key_game; // remove console or menu
|
|
SV_SaveSpawnparms ();
|
|
strcpy (level, Cmd_Argv(1));
|
|
fprintf(stderr, "Host_Changelevel_f '%s'\n", level);
|
|
_resetAutoload(); // autosave
|
|
SV_SpawnServer (level);
|
|
}
|
|
|
|
static qboolean _autoloadIfPossible()
|
|
{
|
|
if (!deathmatch.value &&
|
|
!coop.value &&
|
|
sv.active &&
|
|
!cl.intermission &&
|
|
svs.maxclients == 1)
|
|
{
|
|
if (0 == strcmp(sv.name, lastsavemapname))
|
|
{
|
|
char command[MAX_QPATH + 5];
|
|
sprintf(command, "load %s", lastsavename);
|
|
|
|
Cmd_ExecuteString (command, src_command);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void _resetAutoload()
|
|
{
|
|
lastsavemapname[0] = '\0'; // autosave - we are explicitly restarting the level, so don't autoload
|
|
lastsavename[0] = '\0';
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Autoload_f
|
|
|
|
Automatically loads either the
|
|
==================
|
|
*/
|
|
void Host_Autoload_f (void)
|
|
{
|
|
if (!_autoloadIfPossible())
|
|
{
|
|
Host_Restart_f();
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Restart_f
|
|
|
|
Restarts the current server for a dead player
|
|
==================
|
|
*/
|
|
void Host_Restart_f (void)
|
|
{
|
|
char mapname[MAX_QPATH];
|
|
|
|
if (cls.demoplayback || !sv.active)
|
|
return;
|
|
|
|
if (cmd_source != src_command)
|
|
return;
|
|
strcpy (mapname, sv.name); // must copy out, because it gets cleared
|
|
// in sv_spawnserver
|
|
|
|
if (sv_player->v.deadflag != DEAD_NO) // autosave
|
|
{
|
|
// If we are dead, attempt to autoload
|
|
if (_autoloadIfPossible())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
_resetAutoload();
|
|
|
|
SV_SpawnServer (mapname);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Reconnect_f
|
|
|
|
This command causes the client to wait for the signon messages again.
|
|
This is sent just before a server changes levels
|
|
==================
|
|
*/
|
|
void Host_Reconnect_f (void)
|
|
{
|
|
SCR_BeginLoadingPlaque ();
|
|
cls.signon = 0; // need new connection messages
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
Host_Connect_f
|
|
|
|
User command to connect to server
|
|
=====================
|
|
*/
|
|
void Host_Connect_f (void)
|
|
{
|
|
char name[MAX_QPATH];
|
|
|
|
cls.demonum = -1; // stop demo loop in case this fails
|
|
if (cls.demoplayback)
|
|
{
|
|
CL_StopPlayback ();
|
|
CL_Disconnect ();
|
|
}
|
|
strcpy (name, Cmd_Argv(1));
|
|
CL_EstablishConnection (name);
|
|
Host_Reconnect_f ();
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
LOAD / SAVE GAME
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
#define SAVEGAME_VERSION 5
|
|
|
|
/*
|
|
===============
|
|
Host_SavegameComment
|
|
|
|
Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current
|
|
===============
|
|
*/
|
|
void Host_SavegameComment (char *text)
|
|
{
|
|
int i;
|
|
char kills[20];
|
|
|
|
for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++)
|
|
text[i] = ' ';
|
|
memcpy (text, cl.levelname, min(strlen(cl.levelname),22)); //johnfitz -- only copy 22 chars.
|
|
sprintf (kills,"kills:%3i/%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
|
|
memcpy (text+22, kills, strlen(kills));
|
|
// convert space to _ to make stdio happy
|
|
for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++)
|
|
{
|
|
if (text[i] == ' ')
|
|
text[i] = '_';
|
|
}
|
|
text[SAVEGAME_COMMENT_LENGTH] = '\0';
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
Host_Savegame_f
|
|
===============
|
|
*/
|
|
void Host_Savegame_f (void)
|
|
{
|
|
char name[256];
|
|
FILE *f;
|
|
int i;
|
|
char comment[SAVEGAME_COMMENT_LENGTH+1];
|
|
|
|
if (cmd_source != src_command)
|
|
return;
|
|
|
|
if (!sv.active)
|
|
{
|
|
Con_Printf ("Not playing a local game.\n");
|
|
return;
|
|
}
|
|
|
|
if (cl.intermission)
|
|
{
|
|
Con_Printf ("Can't save in intermission.\n");
|
|
return;
|
|
}
|
|
|
|
if (svs.maxclients != 1)
|
|
{
|
|
Con_Printf ("Can't save multiplayer games.\n");
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_Printf ("save <savename> : save a game\n");
|
|
return;
|
|
}
|
|
|
|
if (strstr(Cmd_Argv(1), ".."))
|
|
{
|
|
Con_Printf ("Relative pathnames are not allowed.\n");
|
|
return;
|
|
}
|
|
|
|
for (i=0 ; i<svs.maxclients ; i++)
|
|
{
|
|
if (svs.clients[i].active && (svs.clients[i].edict->v.health <= 0) )
|
|
{
|
|
Con_Printf ("Can't savegame with a dead player\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
strcpy(lastsavename, Cmd_Argv(1)); // autosave
|
|
strcpy(lastsavemapname, sv.name); // autosave
|
|
|
|
sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
|
|
COM_DefaultExtension (name, ".sav");
|
|
|
|
Con_Printf ("Saving game to %s...\n", name);
|
|
f = fopen (name, "w");
|
|
if (!f)
|
|
{
|
|
Con_Printf ("ERROR: couldn't open.\n");
|
|
return;
|
|
}
|
|
|
|
fprintf (f, "%i\n", SAVEGAME_VERSION);
|
|
Host_SavegameComment (comment);
|
|
fprintf (f, "%s\n", comment);
|
|
for (i = 0; i < NUM_SPAWN_PARMS; i++)
|
|
fprintf (f, "%f\n", svs.clients->spawn_parms[i]);
|
|
fprintf (f, "%d\n", current_skill);
|
|
fprintf (f, "%s\n", sv.name);
|
|
fprintf (f, "%f\n",sv.time);
|
|
|
|
// write the light styles
|
|
|
|
for (i = 0; i < MAX_LIGHTSTYLES; i++)
|
|
{
|
|
if (sv.lightstyles[i])
|
|
fprintf (f, "%s\n", sv.lightstyles[i]);
|
|
else
|
|
fprintf (f,"m\n");
|
|
}
|
|
|
|
|
|
ED_WriteGlobals (f);
|
|
for (i = 0; i < sv.num_edicts; i++)
|
|
{
|
|
ED_Write (f, EDICT_NUM(i));
|
|
fflush (f);
|
|
}
|
|
fclose (f);
|
|
Con_Printf ("done.\n");
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
Host_Loadgame_f
|
|
===============
|
|
*/
|
|
void Host_Loadgame_f (void)
|
|
{
|
|
char name[MAX_OSPATH];
|
|
FILE *f;
|
|
char mapname[MAX_QPATH];
|
|
float time, tfloat;
|
|
char str[32768];
|
|
const char *start;
|
|
int i, r;
|
|
edict_t *ent;
|
|
int entnum;
|
|
int version;
|
|
float spawn_parms[NUM_SPAWN_PARMS];
|
|
|
|
if (cmd_source != src_command)
|
|
return;
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_Printf ("load <savename> : load a game\n");
|
|
return;
|
|
}
|
|
|
|
cls.demonum = -1; // stop demo loop in case this fails
|
|
|
|
sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
|
|
COM_DefaultExtension (name, ".sav");
|
|
|
|
// we can't call SCR_BeginLoadingPlaque, because too much stack space has
|
|
// been used. The menu calls it before stuffing loadgame command
|
|
// SCR_BeginLoadingPlaque ();
|
|
|
|
Con_Printf ("Loading game from %s...\n", name);
|
|
f = fopen (name, "r");
|
|
if (!f)
|
|
{
|
|
Con_Printf ("ERROR: couldn't open.\n");
|
|
return;
|
|
}
|
|
|
|
fscanf (f, "%i\n", &version);
|
|
if (version != SAVEGAME_VERSION)
|
|
{
|
|
fclose (f);
|
|
Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
|
|
return;
|
|
}
|
|
fscanf (f, "%s\n", str);
|
|
for (i = 0; i < NUM_SPAWN_PARMS; i++)
|
|
fscanf (f, "%f\n", &spawn_parms[i]);
|
|
// this silliness is so we can load 1.06 save files, which have float skill values
|
|
fscanf (f, "%f\n", &tfloat);
|
|
current_skill = (int)(tfloat + 0.1);
|
|
Cvar_SetValue ("skill", (float)current_skill);
|
|
|
|
fscanf (f, "%s\n",mapname);
|
|
fscanf (f, "%f\n",&time);
|
|
|
|
CL_Disconnect_f ();
|
|
|
|
SV_SpawnServer (mapname);
|
|
|
|
if (!sv.active)
|
|
{
|
|
Con_Printf ("Couldn't load map\n");
|
|
return;
|
|
}
|
|
sv.paused = true; // pause until all clients connect
|
|
sv.loadgame = true;
|
|
|
|
// load the light styles
|
|
|
|
for (i = 0; i < MAX_LIGHTSTYLES; i++)
|
|
{
|
|
fscanf (f, "%s\n", str);
|
|
sv.lightstyles[i] = (const char *)Hunk_Strdup (str, "lightstyles");
|
|
}
|
|
|
|
// load the edicts out of the savegame file
|
|
entnum = -1; // -1 is the globals
|
|
while (!feof(f))
|
|
{
|
|
for (i = 0; i < sizeof(str) - 1; i++)
|
|
{
|
|
r = fgetc (f);
|
|
if (r == EOF || !r)
|
|
break;
|
|
str[i] = r;
|
|
if (r == '}')
|
|
{
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
if (i == sizeof(str) - 1)
|
|
Sys_Error ("Loadgame buffer overflow");
|
|
str[i] = 0;
|
|
start = str;
|
|
start = COM_Parse(str);
|
|
if (!com_token[0])
|
|
break; // end of file
|
|
if (strcmp(com_token,"{"))
|
|
Sys_Error ("First token isn't a brace");
|
|
|
|
if (entnum == -1)
|
|
{ // parse the global vars
|
|
ED_ParseGlobals (start);
|
|
}
|
|
else
|
|
{ // parse an edict
|
|
|
|
ent = EDICT_NUM(entnum);
|
|
memset (&ent->v, 0, progs->entityfields * 4);
|
|
ent->free = false;
|
|
ED_ParseEdict (start, ent);
|
|
|
|
// link it into the bsp tree
|
|
if (!ent->free)
|
|
SV_LinkEdict (ent, false);
|
|
}
|
|
|
|
entnum++;
|
|
}
|
|
|
|
sv.num_edicts = entnum;
|
|
sv.time = time;
|
|
|
|
fclose (f);
|
|
|
|
for (i = 0; i < NUM_SPAWN_PARMS; i++)
|
|
svs.clients->spawn_parms[i] = spawn_parms[i];
|
|
|
|
if (cls.state != ca_dedicated)
|
|
{
|
|
CL_EstablishConnection ("local");
|
|
Host_Reconnect_f ();
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
======================
|
|
Host_Name_f
|
|
======================
|
|
*/
|
|
void Host_Name_f (void)
|
|
{
|
|
char newName[32];
|
|
|
|
if (Cmd_Argc () == 1)
|
|
{
|
|
Con_Printf ("\"name\" is \"%s\"\n", cl_name.string);
|
|
return;
|
|
}
|
|
if (Cmd_Argc () == 2)
|
|
Q_strncpy(newName, Cmd_Argv(1), sizeof(newName)-1);
|
|
else
|
|
Q_strncpy(newName, Cmd_Args(), sizeof(newName)-1);
|
|
newName[15] = 0; // client_t structure actually says name[32].
|
|
|
|
if (cmd_source == src_command)
|
|
{
|
|
if (Q_strcmp(cl_name.string, newName) == 0)
|
|
return;
|
|
Cvar_Set ("_cl_name", newName);
|
|
if (cls.state == ca_connected)
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
|
|
if (host_client->name[0] && strcmp(host_client->name, "unconnected") )
|
|
{
|
|
if (Q_strcmp(host_client->name, newName) != 0)
|
|
Con_Printf ("%s renamed to %s\n", host_client->name, newName);
|
|
}
|
|
Q_strcpy (host_client->name, newName);
|
|
host_client->edict->v.netname = PR_SetEngineString(host_client->name);
|
|
|
|
// send notification to all clients
|
|
|
|
MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
|
|
MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
|
|
MSG_WriteString (&sv.reliable_datagram, host_client->name);
|
|
}
|
|
|
|
|
|
void Host_Version_f (void)
|
|
{
|
|
Con_Printf ("Quake Version %1.2f\n", VERSION); //johnfitz
|
|
Con_Printf ("QuakeSpasm Version %1.2f.%d\n", FITZQUAKE_VERSION, QUAKESPASM_VER_PATCH); //johnfitz
|
|
Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
|
|
}
|
|
|
|
void Host_Say(qboolean teamonly)
|
|
{
|
|
int j;
|
|
client_t *client;
|
|
client_t *save;
|
|
const char *p;
|
|
char text[MAXCMDLINE], *p2;
|
|
qboolean quoted;
|
|
qboolean fromServer = false;
|
|
|
|
if (cmd_source == src_command)
|
|
{
|
|
if (cls.state == ca_dedicated)
|
|
{
|
|
fromServer = true;
|
|
teamonly = false;
|
|
}
|
|
else
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (Cmd_Argc () < 2)
|
|
return;
|
|
|
|
save = host_client;
|
|
|
|
p = Cmd_Args();
|
|
// remove quotes if present
|
|
quoted = false;
|
|
if (*p == '\"')
|
|
{
|
|
p++;
|
|
quoted = true;
|
|
}
|
|
// turn on color set 1
|
|
if (!fromServer)
|
|
q_snprintf (text, sizeof(text), "\001%s: %s", save->name, p);
|
|
else
|
|
q_snprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p);
|
|
|
|
// check length & truncate if necessary
|
|
j = strlen(text);
|
|
if (j >= sizeof(text) - 1)
|
|
{
|
|
text[sizeof(text) - 2] = '\n';
|
|
text[sizeof(text) - 1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
p2 = text + j;
|
|
while ((const char *)p2 > (const char *)text &&
|
|
(p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)) )
|
|
{
|
|
if (p2[-1] == '\"' && quoted)
|
|
quoted = false;
|
|
p2[-1] = '\0';
|
|
p2--;
|
|
}
|
|
p2[0] = '\n';
|
|
p2[1] = '\0';
|
|
}
|
|
|
|
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
|
|
{
|
|
if (!client || !client->active || !client->spawned)
|
|
continue;
|
|
if (teamplay.value && teamonly && client->edict->v.team != save->edict->v.team)
|
|
continue;
|
|
host_client = client;
|
|
SV_ClientPrintf("%s", text);
|
|
}
|
|
host_client = save;
|
|
|
|
if (cls.state == ca_dedicated)
|
|
Sys_Printf("%s", &text[1]);
|
|
}
|
|
|
|
|
|
void Host_Say_f(void)
|
|
{
|
|
Host_Say(false);
|
|
}
|
|
|
|
|
|
void Host_Say_Team_f(void)
|
|
{
|
|
Host_Say(true);
|
|
}
|
|
|
|
|
|
void Host_Tell_f(void)
|
|
{
|
|
int j;
|
|
client_t *client;
|
|
client_t *save;
|
|
const char *p;
|
|
char text[MAXCMDLINE], *p2;
|
|
qboolean quoted;
|
|
|
|
if (cmd_source == src_command)
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc () < 3)
|
|
return;
|
|
|
|
p = Cmd_Args();
|
|
// remove quotes if present
|
|
quoted = false;
|
|
if (*p == '\"')
|
|
{
|
|
p++;
|
|
quoted = true;
|
|
}
|
|
q_snprintf (text, sizeof(text), "%s: %s", host_client->name, p);
|
|
|
|
// check length & truncate if necessary
|
|
j = strlen(text);
|
|
if (j >= sizeof(text) - 1)
|
|
{
|
|
text[sizeof(text) - 2] = '\n';
|
|
text[sizeof(text) - 1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
p2 = text + j;
|
|
while ((const char *)p2 > (const char *)text &&
|
|
(p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)) )
|
|
{
|
|
if (p2[-1] == '\"' && quoted)
|
|
quoted = false;
|
|
p2[-1] = '\0';
|
|
p2--;
|
|
}
|
|
p2[0] = '\n';
|
|
p2[1] = '\0';
|
|
}
|
|
|
|
save = host_client;
|
|
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
|
|
{
|
|
if (!client->active || !client->spawned)
|
|
continue;
|
|
if (Q_strcasecmp(client->name, Cmd_Argv(1)))
|
|
continue;
|
|
host_client = client;
|
|
SV_ClientPrintf("%s", text);
|
|
break;
|
|
}
|
|
host_client = save;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
Host_Color_f
|
|
==================
|
|
*/
|
|
void Host_Color_f(void)
|
|
{
|
|
int top, bottom;
|
|
int playercolor;
|
|
|
|
if (Cmd_Argc() == 1)
|
|
{
|
|
Con_Printf ("\"color\" is \"%i %i\"\n", ((int)cl_color.value) >> 4, ((int)cl_color.value) & 0x0f);
|
|
Con_Printf ("color <0-13> [0-13]\n");
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc() == 2)
|
|
top = bottom = atoi(Cmd_Argv(1));
|
|
else
|
|
{
|
|
top = atoi(Cmd_Argv(1));
|
|
bottom = atoi(Cmd_Argv(2));
|
|
}
|
|
|
|
top &= 15;
|
|
if (top > 13)
|
|
top = 13;
|
|
bottom &= 15;
|
|
if (bottom > 13)
|
|
bottom = 13;
|
|
|
|
playercolor = top*16 + bottom;
|
|
|
|
if (cmd_source == src_command)
|
|
{
|
|
Cvar_SetValue ("_cl_color", playercolor);
|
|
if (cls.state == ca_connected)
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
|
|
host_client->colors = playercolor;
|
|
host_client->edict->v.team = bottom + 1;
|
|
|
|
// send notification to all clients
|
|
MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
|
|
MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
|
|
MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Kill_f
|
|
==================
|
|
*/
|
|
void Host_Kill_f (void)
|
|
{
|
|
if (cmd_source == src_command)
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
|
|
if (sv_player->v.health <= 0)
|
|
{
|
|
SV_ClientPrintf ("Can't suicide -- allready dead!\n");
|
|
return;
|
|
}
|
|
|
|
pr_global_struct->time = sv.time;
|
|
pr_global_struct->self = EDICT_TO_PROG(sv_player);
|
|
PR_ExecuteProgram (pr_global_struct->ClientKill);
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
Host_Pause_f
|
|
==================
|
|
*/
|
|
void Host_Pause_f (void)
|
|
{
|
|
|
|
if (cmd_source == src_command)
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
if (!pausable.value)
|
|
SV_ClientPrintf ("Pause not allowed.\n");
|
|
else
|
|
{
|
|
sv.paused ^= 1;
|
|
|
|
if (sv.paused)
|
|
{
|
|
SV_BroadcastPrintf ("%s paused the game\n", PR_GetString(sv_player->v.netname));
|
|
}
|
|
else
|
|
{
|
|
SV_BroadcastPrintf ("%s unpaused the game\n",PR_GetString(sv_player->v.netname));
|
|
}
|
|
|
|
// send notification to all clients
|
|
MSG_WriteByte (&sv.reliable_datagram, svc_setpause);
|
|
MSG_WriteByte (&sv.reliable_datagram, sv.paused);
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
|
|
/*
|
|
==================
|
|
Host_PreSpawn_f
|
|
==================
|
|
*/
|
|
void Host_PreSpawn_f (void)
|
|
{
|
|
if (cmd_source == src_command)
|
|
{
|
|
Con_Printf ("prespawn is not valid from the console\n");
|
|
return;
|
|
}
|
|
|
|
if (host_client->spawned)
|
|
{
|
|
Con_Printf ("prespawn not valid -- allready spawned\n");
|
|
return;
|
|
}
|
|
|
|
SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize);
|
|
MSG_WriteByte (&host_client->message, svc_signonnum);
|
|
MSG_WriteByte (&host_client->message, 2);
|
|
host_client->sendsignon = true;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Spawn_f
|
|
==================
|
|
*/
|
|
void Host_Spawn_f (void)
|
|
{
|
|
int i;
|
|
client_t *client;
|
|
edict_t *ent;
|
|
|
|
if (cmd_source == src_command)
|
|
{
|
|
Con_Printf ("spawn is not valid from the console\n");
|
|
return;
|
|
}
|
|
|
|
if (host_client->spawned)
|
|
{
|
|
Con_Printf ("Spawn not valid -- allready spawned\n");
|
|
return;
|
|
}
|
|
|
|
// run the entrance script
|
|
if (sv.loadgame)
|
|
{ // loaded games are fully inited allready
|
|
// if this is the last client to be connected, unpause
|
|
sv.paused = false;
|
|
}
|
|
else
|
|
{
|
|
// set up the edict
|
|
ent = host_client->edict;
|
|
|
|
memset (&ent->v, 0, progs->entityfields * 4);
|
|
ent->v.colormap = NUM_FOR_EDICT(ent);
|
|
ent->v.team = (host_client->colors & 15) + 1;
|
|
ent->v.netname = PR_SetEngineString(host_client->name);
|
|
|
|
// copy spawn parms out of the client_t
|
|
|
|
for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
|
|
(&pr_global_struct->parm1)[i] = host_client->spawn_parms[i];
|
|
|
|
// call the spawn function
|
|
|
|
pr_global_struct->time = sv.time;
|
|
pr_global_struct->self = EDICT_TO_PROG(sv_player);
|
|
PR_ExecuteProgram (pr_global_struct->ClientConnect);
|
|
|
|
if ((Sys_FloatTime() - host_client->netconnection->connecttime) <= sv.time)
|
|
Sys_Printf ("%s entered the game\n", host_client->name);
|
|
|
|
PR_ExecuteProgram (pr_global_struct->PutClientInServer);
|
|
}
|
|
|
|
|
|
// send all current names, colors, and frag counts
|
|
SZ_Clear (&host_client->message);
|
|
|
|
// send time of update
|
|
MSG_WriteByte (&host_client->message, svc_time);
|
|
MSG_WriteFloat (&host_client->message, sv.time);
|
|
|
|
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++)
|
|
{
|
|
MSG_WriteByte (&host_client->message, svc_updatename);
|
|
MSG_WriteByte (&host_client->message, i);
|
|
MSG_WriteString (&host_client->message, client->name);
|
|
MSG_WriteByte (&host_client->message, svc_updatefrags);
|
|
MSG_WriteByte (&host_client->message, i);
|
|
MSG_WriteShort (&host_client->message, client->old_frags);
|
|
MSG_WriteByte (&host_client->message, svc_updatecolors);
|
|
MSG_WriteByte (&host_client->message, i);
|
|
MSG_WriteByte (&host_client->message, client->colors);
|
|
}
|
|
|
|
// send all current light styles
|
|
for (i = 0; i < MAX_LIGHTSTYLES; i++)
|
|
{
|
|
MSG_WriteByte (&host_client->message, svc_lightstyle);
|
|
MSG_WriteByte (&host_client->message, (char)i);
|
|
MSG_WriteString (&host_client->message, sv.lightstyles[i]);
|
|
}
|
|
|
|
//
|
|
// send some stats
|
|
//
|
|
MSG_WriteByte (&host_client->message, svc_updatestat);
|
|
MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS);
|
|
MSG_WriteLong (&host_client->message, pr_global_struct->total_secrets);
|
|
|
|
MSG_WriteByte (&host_client->message, svc_updatestat);
|
|
MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS);
|
|
MSG_WriteLong (&host_client->message, pr_global_struct->total_monsters);
|
|
|
|
MSG_WriteByte (&host_client->message, svc_updatestat);
|
|
MSG_WriteByte (&host_client->message, STAT_SECRETS);
|
|
MSG_WriteLong (&host_client->message, pr_global_struct->found_secrets);
|
|
|
|
MSG_WriteByte (&host_client->message, svc_updatestat);
|
|
MSG_WriteByte (&host_client->message, STAT_MONSTERS);
|
|
MSG_WriteLong (&host_client->message, pr_global_struct->killed_monsters);
|
|
|
|
|
|
//
|
|
// send a fixangle
|
|
// Never send a roll angle, because savegames can catch the server
|
|
// in a state where it is expecting the client to correct the angle
|
|
// and it won't happen if the game was just loaded, so you wind up
|
|
// with a permanent head tilt
|
|
ent = EDICT_NUM( 1 + (host_client - svs.clients) );
|
|
MSG_WriteByte (&host_client->message, svc_setangle);
|
|
for (i = 0; i < 2; i++)
|
|
MSG_WriteAngle (&host_client->message, ent->v.angles[i] );
|
|
MSG_WriteAngle (&host_client->message, 0 );
|
|
|
|
SV_WriteClientdataToMessage (sv_player, &host_client->message);
|
|
|
|
MSG_WriteByte (&host_client->message, svc_signonnum);
|
|
MSG_WriteByte (&host_client->message, 3);
|
|
host_client->sendsignon = true;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Begin_f
|
|
==================
|
|
*/
|
|
void Host_Begin_f (void)
|
|
{
|
|
if (cmd_source == src_command)
|
|
{
|
|
Con_Printf ("begin is not valid from the console\n");
|
|
return;
|
|
}
|
|
|
|
host_client->spawned = true;
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
|
|
/*
|
|
==================
|
|
Host_Kick_f
|
|
|
|
Kicks a user off of the server
|
|
==================
|
|
*/
|
|
void Host_Kick_f (void)
|
|
{
|
|
const char *who;
|
|
const char *message = NULL;
|
|
client_t *save;
|
|
int i;
|
|
qboolean byNumber = false;
|
|
|
|
if (cmd_source == src_command)
|
|
{
|
|
if (!sv.active)
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
}
|
|
else if (pr_global_struct->deathmatch && !host_client->privileged)
|
|
return;
|
|
|
|
save = host_client;
|
|
|
|
if (Cmd_Argc() > 2 && Q_strcmp(Cmd_Argv(1), "#") == 0)
|
|
{
|
|
i = Q_atof(Cmd_Argv(2)) - 1;
|
|
if (i < 0 || i >= svs.maxclients)
|
|
return;
|
|
if (!svs.clients[i].active)
|
|
return;
|
|
host_client = &svs.clients[i];
|
|
byNumber = true;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
|
|
{
|
|
if (!host_client->active)
|
|
continue;
|
|
if (Q_strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < svs.maxclients)
|
|
{
|
|
if (cmd_source == src_command)
|
|
if (cls.state == ca_dedicated)
|
|
who = "Console";
|
|
else
|
|
who = cl_name.string;
|
|
else
|
|
who = save->name;
|
|
|
|
// can't kick yourself!
|
|
if (host_client == save)
|
|
return;
|
|
|
|
if (Cmd_Argc() > 2)
|
|
{
|
|
message = COM_Parse(Cmd_Args());
|
|
if (byNumber)
|
|
{
|
|
message++; // skip the #
|
|
while (*message == ' ') // skip white space
|
|
message++;
|
|
message += Q_strlen(Cmd_Argv(2)); // skip the number
|
|
}
|
|
while (*message && *message == ' ')
|
|
message++;
|
|
}
|
|
if (message)
|
|
SV_ClientPrintf ("Kicked by %s: %s\n", who, message);
|
|
else
|
|
SV_ClientPrintf ("Kicked by %s\n", who);
|
|
SV_DropClient (false);
|
|
}
|
|
|
|
host_client = save;
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
DEBUGGING TOOLS
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
==================
|
|
Host_Give_f
|
|
==================
|
|
*/
|
|
void Host_Give_f (void)
|
|
{
|
|
const char *t;
|
|
int v;
|
|
eval_t *val;
|
|
|
|
if (cmd_source == src_command)
|
|
{
|
|
Cmd_ForwardToServer ();
|
|
return;
|
|
}
|
|
|
|
if (pr_global_struct->deathmatch && !host_client->privileged)
|
|
return;
|
|
|
|
t = Cmd_Argv(1);
|
|
v = atoi (Cmd_Argv(2));
|
|
|
|
switch (t[0])
|
|
{
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
// MED 01/04/97 added hipnotic give stuff
|
|
if (hipnotic)
|
|
{
|
|
if (t[0] == '6')
|
|
{
|
|
if (t[1] == 'a')
|
|
sv_player->v.items = (int)sv_player->v.items | HIT_PROXIMITY_GUN;
|
|
else
|
|
sv_player->v.items = (int)sv_player->v.items | IT_GRENADE_LAUNCHER;
|
|
}
|
|
else if (t[0] == '9')
|
|
sv_player->v.items = (int)sv_player->v.items | HIT_LASER_CANNON;
|
|
else if (t[0] == '0')
|
|
sv_player->v.items = (int)sv_player->v.items | HIT_MJOLNIR;
|
|
else if (t[0] >= '2')
|
|
sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2'));
|
|
}
|
|
else
|
|
{
|
|
if (t[0] >= '2')
|
|
sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2'));
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
if (rogue)
|
|
{
|
|
val = GetEdictFieldValue(sv_player, "ammo_shells1");
|
|
if (val)
|
|
val->_float = v;
|
|
}
|
|
sv_player->v.ammo_shells = v;
|
|
break;
|
|
|
|
case 'n':
|
|
if (rogue)
|
|
{
|
|
val = GetEdictFieldValue(sv_player, "ammo_nails1");
|
|
if (val)
|
|
{
|
|
val->_float = v;
|
|
if (sv_player->v.weapon <= IT_LIGHTNING)
|
|
sv_player->v.ammo_nails = v;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sv_player->v.ammo_nails = v;
|
|
}
|
|
break;
|
|
|
|
case 'l':
|
|
if (rogue)
|
|
{
|
|
val = GetEdictFieldValue(sv_player, "ammo_lava_nails");
|
|
if (val)
|
|
{
|
|
val->_float = v;
|
|
if (sv_player->v.weapon > IT_LIGHTNING)
|
|
sv_player->v.ammo_nails = v;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'r':
|
|
if (rogue)
|
|
{
|
|
val = GetEdictFieldValue(sv_player, "ammo_rockets1");
|
|
if (val)
|
|
{
|
|
val->_float = v;
|
|
if (sv_player->v.weapon <= IT_LIGHTNING)
|
|
sv_player->v.ammo_rockets = v;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sv_player->v.ammo_rockets = v;
|
|
}
|
|
break;
|
|
|
|
case 'm':
|
|
if (rogue)
|
|
{
|
|
val = GetEdictFieldValue(sv_player, "ammo_multi_rockets");
|
|
if (val)
|
|
{
|
|
val->_float = v;
|
|
if (sv_player->v.weapon > IT_LIGHTNING)
|
|
sv_player->v.ammo_rockets = v;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'h':
|
|
sv_player->v.health = v;
|
|
break;
|
|
|
|
case 'c':
|
|
if (rogue)
|
|
{
|
|
val = GetEdictFieldValue(sv_player, "ammo_cells1");
|
|
if (val)
|
|
{
|
|
val->_float = v;
|
|
if (sv_player->v.weapon <= IT_LIGHTNING)
|
|
sv_player->v.ammo_cells = v;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sv_player->v.ammo_cells = v;
|
|
}
|
|
break;
|
|
|
|
case 'p':
|
|
if (rogue)
|
|
{
|
|
val = GetEdictFieldValue(sv_player, "ammo_plasma");
|
|
if (val)
|
|
{
|
|
val->_float = v;
|
|
if (sv_player->v.weapon > IT_LIGHTNING)
|
|
sv_player->v.ammo_cells = v;
|
|
}
|
|
}
|
|
break;
|
|
|
|
//johnfitz -- give armour
|
|
case 'a':
|
|
if (v > 150)
|
|
{
|
|
sv_player->v.armortype = 0.8;
|
|
sv_player->v.armorvalue = v;
|
|
sv_player->v.items = sv_player->v.items -
|
|
((int)(sv_player->v.items) & (int)(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) +
|
|
IT_ARMOR3;
|
|
}
|
|
else if (v > 100)
|
|
{
|
|
sv_player->v.armortype = 0.6;
|
|
sv_player->v.armorvalue = v;
|
|
sv_player->v.items = sv_player->v.items -
|
|
((int)(sv_player->v.items) & (int)(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) +
|
|
IT_ARMOR2;
|
|
}
|
|
else if (v >= 0)
|
|
{
|
|
sv_player->v.armortype = 0.3;
|
|
sv_player->v.armorvalue = v;
|
|
sv_player->v.items = sv_player->v.items -
|
|
((int)(sv_player->v.items) & (int)(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) +
|
|
IT_ARMOR1;
|
|
}
|
|
break;
|
|
//johnfitz
|
|
}
|
|
|
|
//johnfitz -- update currentammo to match new ammo (so statusbar updates correctly)
|
|
switch ((int)(sv_player->v.weapon))
|
|
{
|
|
case IT_SHOTGUN:
|
|
case IT_SUPER_SHOTGUN:
|
|
sv_player->v.currentammo = sv_player->v.ammo_shells;
|
|
break;
|
|
case IT_NAILGUN:
|
|
case IT_SUPER_NAILGUN:
|
|
case RIT_LAVA_SUPER_NAILGUN:
|
|
sv_player->v.currentammo = sv_player->v.ammo_nails;
|
|
break;
|
|
case IT_GRENADE_LAUNCHER:
|
|
case IT_ROCKET_LAUNCHER:
|
|
case RIT_MULTI_GRENADE:
|
|
case RIT_MULTI_ROCKET:
|
|
sv_player->v.currentammo = sv_player->v.ammo_rockets;
|
|
break;
|
|
case IT_LIGHTNING:
|
|
case HIT_LASER_CANNON:
|
|
case HIT_MJOLNIR:
|
|
sv_player->v.currentammo = sv_player->v.ammo_cells;
|
|
break;
|
|
case RIT_LAVA_NAILGUN: //same as IT_AXE
|
|
if (rogue)
|
|
sv_player->v.currentammo = sv_player->v.ammo_nails;
|
|
break;
|
|
case RIT_PLASMA_GUN: //same as HIT_PROXIMITY_GUN
|
|
if (rogue)
|
|
sv_player->v.currentammo = sv_player->v.ammo_cells;
|
|
if (hipnotic)
|
|
sv_player->v.currentammo = sv_player->v.ammo_rockets;
|
|
break;
|
|
}
|
|
//johnfitz
|
|
}
|
|
|
|
edict_t *FindViewthing (void)
|
|
{
|
|
int i;
|
|
edict_t *e;
|
|
|
|
for (i=0 ; i<sv.num_edicts ; i++)
|
|
{
|
|
e = EDICT_NUM(i);
|
|
if ( !strcmp (PR_GetString(e->v.classname), "viewthing") )
|
|
return e;
|
|
}
|
|
Con_Printf ("No viewthing on map\n");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Viewmodel_f
|
|
==================
|
|
*/
|
|
void Host_Viewmodel_f (void)
|
|
{
|
|
edict_t *e;
|
|
model_t *m;
|
|
|
|
e = FindViewthing ();
|
|
if (!e)
|
|
return;
|
|
|
|
m = Mod_ForName (Cmd_Argv(1), false);
|
|
if (!m)
|
|
{
|
|
Con_Printf ("Can't load %s\n", Cmd_Argv(1));
|
|
return;
|
|
}
|
|
|
|
e->v.frame = 0;
|
|
cl.model_precache[(int)e->v.modelindex] = m;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Viewframe_f
|
|
==================
|
|
*/
|
|
void Host_Viewframe_f (void)
|
|
{
|
|
edict_t *e;
|
|
int f;
|
|
model_t *m;
|
|
|
|
e = FindViewthing ();
|
|
if (!e)
|
|
return;
|
|
m = cl.model_precache[(int)e->v.modelindex];
|
|
|
|
f = atoi(Cmd_Argv(1));
|
|
if (f >= m->numframes)
|
|
f = m->numframes - 1;
|
|
|
|
e->v.frame = f;
|
|
}
|
|
|
|
|
|
void PrintFrameName (model_t *m, int frame)
|
|
{
|
|
aliashdr_t *hdr;
|
|
maliasframedesc_t *pframedesc;
|
|
|
|
hdr = (aliashdr_t *)Mod_Extradata (m);
|
|
if (!hdr)
|
|
return;
|
|
pframedesc = &hdr->frames[frame];
|
|
|
|
Con_Printf ("frame %i: %s\n", frame, pframedesc->name);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Viewnext_f
|
|
==================
|
|
*/
|
|
void Host_Viewnext_f (void)
|
|
{
|
|
edict_t *e;
|
|
model_t *m;
|
|
|
|
e = FindViewthing ();
|
|
if (!e)
|
|
return;
|
|
m = cl.model_precache[(int)e->v.modelindex];
|
|
|
|
e->v.frame = e->v.frame + 1;
|
|
if (e->v.frame >= m->numframes)
|
|
e->v.frame = m->numframes - 1;
|
|
|
|
PrintFrameName (m, e->v.frame);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Viewprev_f
|
|
==================
|
|
*/
|
|
void Host_Viewprev_f (void)
|
|
{
|
|
edict_t *e;
|
|
model_t *m;
|
|
|
|
e = FindViewthing ();
|
|
if (!e)
|
|
return;
|
|
|
|
m = cl.model_precache[(int)e->v.modelindex];
|
|
|
|
e->v.frame = e->v.frame - 1;
|
|
if (e->v.frame < 0)
|
|
e->v.frame = 0;
|
|
|
|
PrintFrameName (m, e->v.frame);
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
DEMO LOOP CONTROL
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
==================
|
|
Host_Startdemos_f
|
|
==================
|
|
*/
|
|
void Host_Startdemos_f (void)
|
|
{
|
|
int i, c;
|
|
|
|
if (cls.state == ca_dedicated)
|
|
return;
|
|
|
|
c = Cmd_Argc() - 1;
|
|
if (c > MAX_DEMOS)
|
|
{
|
|
Con_Printf ("Max %i demos in demoloop\n", MAX_DEMOS);
|
|
c = MAX_DEMOS;
|
|
}
|
|
Con_Printf ("%i demo(s) in loop\n", c);
|
|
|
|
for (i = 1; i < c + 1; i++)
|
|
strncpy (cls.demos[i-1], Cmd_Argv(i), sizeof(cls.demos[0])-1);
|
|
|
|
if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
|
|
{
|
|
cls.demonum = 0;
|
|
if (!fitzmode)
|
|
{ /* QuakeSpasm customization: */
|
|
/* go straight to menu, no CL_NextDemo */
|
|
Cbuf_InsertText("menu_main\n");
|
|
return;
|
|
}
|
|
CL_NextDemo ();
|
|
}
|
|
else
|
|
{
|
|
cls.demonum = -1;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
Host_Demos_f
|
|
|
|
Return to looping demos
|
|
==================
|
|
*/
|
|
void Host_Demos_f (void)
|
|
{
|
|
if (cls.state == ca_dedicated)
|
|
return;
|
|
if (cls.demonum == -1)
|
|
cls.demonum = 1;
|
|
CL_Disconnect_f ();
|
|
CL_NextDemo ();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_Stopdemo_f
|
|
|
|
Return to looping demos
|
|
==================
|
|
*/
|
|
void Host_Stopdemo_f (void)
|
|
{
|
|
if (cls.state == ca_dedicated)
|
|
return;
|
|
if (!cls.demoplayback)
|
|
return;
|
|
CL_StopPlayback ();
|
|
CL_Disconnect ();
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
==================
|
|
Host_InitCommands
|
|
==================
|
|
*/
|
|
void Host_InitCommands (void)
|
|
{
|
|
Cmd_AddCommand ("maps", Host_Maps_f); //johnfitz
|
|
Cmd_AddCommand ("game", Host_Game_f); //johnfitz
|
|
Cmd_AddCommand ("mods", Host_Mods_f); //johnfitz
|
|
Cmd_AddCommand ("games", Host_Mods_f); // as an alias to "mods" -- S.A. / QuakeSpasm
|
|
Cmd_AddCommand ("mapname", Host_Mapname_f); //johnfitz
|
|
|
|
Cmd_AddCommand ("status", Host_Status_f);
|
|
Cmd_AddCommand ("quit", Host_Quit_f);
|
|
Cmd_AddCommand ("god", Host_God_f);
|
|
Cmd_AddCommand ("notarget", Host_Notarget_f);
|
|
Cmd_AddCommand ("fly", Host_Fly_f);
|
|
Cmd_AddCommand ("map", Host_Map_f);
|
|
Cmd_AddCommand ("restart", Host_Restart_f);
|
|
Cmd_AddCommand ("changelevel", Host_Changelevel_f);
|
|
Cmd_AddCommand ("connect", Host_Connect_f);
|
|
Cmd_AddCommand ("reconnect", Host_Reconnect_f);
|
|
Cmd_AddCommand ("name", Host_Name_f);
|
|
Cmd_AddCommand ("noclip", Host_Noclip_f);
|
|
Cmd_AddCommand ("version", Host_Version_f);
|
|
|
|
Cmd_AddCommand ("say", Host_Say_f);
|
|
Cmd_AddCommand ("say_team", Host_Say_Team_f);
|
|
Cmd_AddCommand ("tell", Host_Tell_f);
|
|
Cmd_AddCommand ("color", Host_Color_f);
|
|
Cmd_AddCommand ("kill", Host_Kill_f);
|
|
Cmd_AddCommand ("pause", Host_Pause_f);
|
|
Cmd_AddCommand ("spawn", Host_Spawn_f);
|
|
Cmd_AddCommand ("begin", Host_Begin_f);
|
|
Cmd_AddCommand ("prespawn", Host_PreSpawn_f);
|
|
Cmd_AddCommand ("kick", Host_Kick_f);
|
|
Cmd_AddCommand ("ping", Host_Ping_f);
|
|
Cmd_AddCommand ("load", Host_Loadgame_f);
|
|
Cmd_AddCommand ("save", Host_Savegame_f);
|
|
Cmd_AddCommand ("give", Host_Give_f);
|
|
Cmd_AddCommand ("autoload", Host_Autoload_f); //autosave
|
|
|
|
Cmd_AddCommand ("startdemos", Host_Startdemos_f);
|
|
Cmd_AddCommand ("demos", Host_Demos_f);
|
|
Cmd_AddCommand ("stopdemo", Host_Stopdemo_f);
|
|
|
|
Cmd_AddCommand ("viewmodel", Host_Viewmodel_f);
|
|
Cmd_AddCommand ("viewframe", Host_Viewframe_f);
|
|
Cmd_AddCommand ("viewnext", Host_Viewnext_f);
|
|
Cmd_AddCommand ("viewprev", Host_Viewprev_f);
|
|
|
|
Cmd_AddCommand ("mcache", Mod_Print);
|
|
}
|
|
|