- added version check for Windows 8. I also would have liked to add 8.1 but due to some incredibly stupid changes in the version API it's no longer possible to reliably retrieve the correct Windows version for later builds.
1156 lines
24 KiB
C++
1156 lines
24 KiB
C++
/*
|
|
** c_cmds.cpp
|
|
** Miscellaneous console commands.
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 1998-2006 Randy Heit
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
** It might be a good idea to move these into files that they are more
|
|
** closely related to, but right now, I am too lazy to do that.
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <direct.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "version.h"
|
|
#include "c_console.h"
|
|
#include "c_dispatch.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "doomerrors.h"
|
|
#include "doomstat.h"
|
|
#include "gstrings.h"
|
|
#include "s_sound.h"
|
|
#include "g_game.h"
|
|
#include "g_level.h"
|
|
#include "w_wad.h"
|
|
#include "g_level.h"
|
|
#include "gi.h"
|
|
#include "r_defs.h"
|
|
#include "d_player.h"
|
|
#include "templates.h"
|
|
#include "p_local.h"
|
|
#include "r_sky.h"
|
|
#include "p_setup.h"
|
|
#include "cmdlib.h"
|
|
#include "d_net.h"
|
|
#include "v_text.h"
|
|
#include "p_lnspec.h"
|
|
#include "v_video.h"
|
|
|
|
extern FILE *Logfile;
|
|
extern bool insave;
|
|
|
|
CVAR (Bool, sv_cheats, false, CVAR_SERVERINFO | CVAR_LATCH)
|
|
CVAR (Bool, sv_unlimited_pickup, false, CVAR_SERVERINFO)
|
|
|
|
CCMD (toggleconsole)
|
|
{
|
|
C_ToggleConsole();
|
|
}
|
|
|
|
bool CheckCheatmode (bool printmsg)
|
|
{
|
|
if ((G_SkillProperty(SKILLP_DisableCheats) || netgame || deathmatch) && (!sv_cheats))
|
|
{
|
|
if (printmsg) Printf ("sv_cheats must be true to enable this command.\n");
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
CCMD (quit)
|
|
{
|
|
if (!insave) exit (0);
|
|
}
|
|
|
|
CCMD (exit)
|
|
{
|
|
if (!insave) exit (0);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Cmd_God
|
|
|
|
Sets client to godmode
|
|
|
|
argv(0) god
|
|
==================
|
|
*/
|
|
CCMD (god)
|
|
{
|
|
if (CheckCheatmode ())
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_GOD);
|
|
}
|
|
|
|
CCMD (iddqd)
|
|
{
|
|
if (CheckCheatmode ())
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_IDDQD);
|
|
}
|
|
|
|
CCMD (buddha)
|
|
{
|
|
if (CheckCheatmode())
|
|
return;
|
|
|
|
Net_WriteByte(DEM_GENERICCHEAT);
|
|
Net_WriteByte(CHT_BUDDHA);
|
|
}
|
|
|
|
CCMD (notarget)
|
|
{
|
|
if (CheckCheatmode ())
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_NOTARGET);
|
|
}
|
|
|
|
CCMD (fly)
|
|
{
|
|
if (CheckCheatmode ())
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_FLY);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Cmd_Noclip
|
|
|
|
argv(0) noclip
|
|
==================
|
|
*/
|
|
CCMD (noclip)
|
|
{
|
|
if (CheckCheatmode ())
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_NOCLIP);
|
|
}
|
|
|
|
CCMD (noclip2)
|
|
{
|
|
if (CheckCheatmode())
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_NOCLIP2);
|
|
}
|
|
|
|
CCMD (powerup)
|
|
{
|
|
if (CheckCheatmode ())
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_POWER);
|
|
}
|
|
|
|
CCMD (morphme)
|
|
{
|
|
if (CheckCheatmode ())
|
|
return;
|
|
|
|
if (argv.argc() == 1)
|
|
{
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_MORPH);
|
|
}
|
|
else
|
|
{
|
|
Net_WriteByte (DEM_MORPHEX);
|
|
Net_WriteString (argv[1]);
|
|
}
|
|
}
|
|
|
|
CCMD (anubis)
|
|
{
|
|
if (CheckCheatmode ())
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_ANUBIS);
|
|
}
|
|
|
|
// [GRB]
|
|
CCMD (resurrect)
|
|
{
|
|
if (CheckCheatmode ())
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_RESSURECT);
|
|
}
|
|
|
|
EXTERN_CVAR (Bool, chasedemo)
|
|
|
|
CCMD (chase)
|
|
{
|
|
if (demoplayback)
|
|
{
|
|
int i;
|
|
|
|
if (chasedemo)
|
|
{
|
|
chasedemo = false;
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
players[i].cheats &= ~CF_CHASECAM;
|
|
}
|
|
else
|
|
{
|
|
chasedemo = true;
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
players[i].cheats |= CF_CHASECAM;
|
|
}
|
|
R_ResetViewInterpolation ();
|
|
}
|
|
else
|
|
{
|
|
// Check if we're allowed to use chasecam.
|
|
if (gamestate != GS_LEVEL || (!(dmflags2 & DF2_CHASECAM) && deathmatch && CheckCheatmode ()))
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_CHASECAM);
|
|
}
|
|
}
|
|
|
|
CCMD (idclev)
|
|
{
|
|
if (netgame)
|
|
return;
|
|
|
|
if ((argv.argc() > 1) && (*(argv[1] + 2) == 0) && *(argv[1] + 1) && *argv[1])
|
|
{
|
|
int epsd, map;
|
|
char buf[2];
|
|
FString mapname;
|
|
|
|
buf[0] = argv[1][0] - '0';
|
|
buf[1] = argv[1][1] - '0';
|
|
|
|
if (gameinfo.flags & GI_MAPxx)
|
|
{
|
|
epsd = 1;
|
|
map = buf[0]*10 + buf[1];
|
|
}
|
|
else
|
|
{
|
|
epsd = buf[0];
|
|
map = buf[1];
|
|
}
|
|
|
|
// Catch invalid maps.
|
|
mapname = CalcMapName (epsd, map);
|
|
|
|
if (!P_CheckMapData(mapname))
|
|
return;
|
|
|
|
// So be it.
|
|
Printf ("%s\n", GStrings("STSTR_CLEV"));
|
|
G_DeferedInitNew (mapname);
|
|
//players[0].health = 0; // Force reset
|
|
}
|
|
}
|
|
|
|
CCMD (hxvisit)
|
|
{
|
|
if (netgame)
|
|
return;
|
|
|
|
if ((argv.argc() > 1) && (*(argv[1] + 2) == 0) && *(argv[1] + 1) && *argv[1])
|
|
{
|
|
FString mapname("&wt@");
|
|
|
|
mapname << argv[1][0] << argv[1][1];
|
|
|
|
if (CheckWarpTransMap (mapname, false))
|
|
{
|
|
// Just because it's in MAPINFO doesn't mean it's in the wad.
|
|
|
|
if (P_CheckMapData(mapname))
|
|
{
|
|
// So be it.
|
|
Printf ("%s\n", GStrings("STSTR_CLEV"));
|
|
G_DeferedInitNew (mapname);
|
|
return;
|
|
}
|
|
}
|
|
Printf ("No such map found\n");
|
|
}
|
|
}
|
|
|
|
CCMD (changemap)
|
|
{
|
|
if (who == NULL || !usergame)
|
|
{
|
|
Printf ("Use the map command when not in a game.\n");
|
|
return;
|
|
}
|
|
|
|
if (!players[who->player - players].settings_controller && netgame)
|
|
{
|
|
Printf ("Only setting controllers can change the map.\n");
|
|
return;
|
|
}
|
|
|
|
if (argv.argc() > 1)
|
|
{
|
|
try
|
|
{
|
|
if (!P_CheckMapData(argv[1]))
|
|
{
|
|
Printf ("No map %s\n", argv[1]);
|
|
}
|
|
else
|
|
{
|
|
if (argv.argc() > 2)
|
|
{
|
|
Net_WriteByte (DEM_CHANGEMAP2);
|
|
Net_WriteByte (atoi(argv[2]));
|
|
}
|
|
else
|
|
{
|
|
Net_WriteByte (DEM_CHANGEMAP);
|
|
}
|
|
Net_WriteString (argv[1]);
|
|
}
|
|
}
|
|
catch(CRecoverableError &error)
|
|
{
|
|
if (error.GetMessage())
|
|
Printf("%s", error.GetMessage());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Printf ("Usage: changemap <map name> [position]\n");
|
|
}
|
|
}
|
|
|
|
CCMD (give)
|
|
{
|
|
if (CheckCheatmode () || argv.argc() < 2)
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GIVECHEAT);
|
|
Net_WriteString (argv[1]);
|
|
if (argv.argc() > 2)
|
|
Net_WriteWord (clamp (atoi (argv[2]), 1, 32767));
|
|
else
|
|
Net_WriteWord (0);
|
|
}
|
|
|
|
CCMD (take)
|
|
{
|
|
if (CheckCheatmode () || argv.argc() < 2)
|
|
return;
|
|
|
|
Net_WriteByte (DEM_TAKECHEAT);
|
|
Net_WriteString (argv[1]);
|
|
if (argv.argc() > 2)
|
|
Net_WriteWord (clamp (atoi (argv[2]), 1, 32767));
|
|
else
|
|
Net_WriteWord (0);
|
|
}
|
|
|
|
CCMD (gameversion)
|
|
{
|
|
Printf ("%s @ %s\nCommit %s", GetVersionString(), GetGitTime(), GetGitHash());
|
|
}
|
|
|
|
CCMD (print)
|
|
{
|
|
if (argv.argc() != 2)
|
|
{
|
|
Printf ("print <name>: Print a string from the string table\n");
|
|
return;
|
|
}
|
|
const char *str = GStrings[argv[1]];
|
|
if (str == NULL)
|
|
{
|
|
Printf ("%s unknown\n", argv[1]);
|
|
}
|
|
else
|
|
{
|
|
Printf ("%s\n", str);
|
|
}
|
|
}
|
|
|
|
CCMD (exec)
|
|
{
|
|
if (argv.argc() < 2)
|
|
return;
|
|
|
|
for (int i = 1; i < argv.argc(); ++i)
|
|
{
|
|
switch (C_ExecFile (argv[i], gamestate == GS_STARTUP))
|
|
{
|
|
case 1: Printf ("Could not open \"%s\"\n", argv[1]); break;
|
|
case 2: Printf ("Error parsing \"%s\"\n", argv[1]); break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void execLogfile(const char *fn)
|
|
{
|
|
if ((Logfile = fopen(fn, "w")))
|
|
{
|
|
const char *timestr = myasctime();
|
|
Printf("Log started: %s\n", timestr);
|
|
}
|
|
else
|
|
{
|
|
Printf("Could not start log\n");
|
|
}
|
|
}
|
|
|
|
CCMD (logfile)
|
|
{
|
|
|
|
if (Logfile)
|
|
{
|
|
const char *timestr = myasctime();
|
|
Printf("Log stopped: %s\n", timestr);
|
|
fclose (Logfile);
|
|
Logfile = NULL;
|
|
}
|
|
|
|
if (argv.argc() >= 2)
|
|
{
|
|
execLogfile(argv[1]);
|
|
}
|
|
}
|
|
|
|
CCMD (puke)
|
|
{
|
|
int argc = argv.argc();
|
|
|
|
if (argc < 2 || argc > 6)
|
|
{
|
|
Printf ("Usage: puke <script> [arg1] [arg2] [arg3] [arg4]\n");
|
|
}
|
|
else
|
|
{
|
|
int script = atoi (argv[1]);
|
|
|
|
if (script == 0)
|
|
{ // Script 0 is reserved for Strife support. It is not pukable.
|
|
return;
|
|
}
|
|
int arg[4] = { 0, 0, 0, 0 };
|
|
int argn = MIN<int>(argc - 2, countof(arg)), i;
|
|
|
|
for (i = 0; i < argn; ++i)
|
|
{
|
|
arg[i] = atoi (argv[2+i]);
|
|
}
|
|
|
|
if (script > 0)
|
|
{
|
|
Net_WriteByte (DEM_RUNSCRIPT);
|
|
Net_WriteWord (script);
|
|
}
|
|
else
|
|
{
|
|
Net_WriteByte (DEM_RUNSCRIPT2);
|
|
Net_WriteWord (-script);
|
|
}
|
|
Net_WriteByte (argn);
|
|
for (i = 0; i < argn; ++i)
|
|
{
|
|
Net_WriteLong (arg[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
CCMD (pukename)
|
|
{
|
|
int argc = argv.argc();
|
|
|
|
if (argc < 2 || argc > 7)
|
|
{
|
|
Printf ("Usage: pukename \"<script>\" [\"always\"] [arg1] [arg2] [arg3] [arg4]\n");
|
|
}
|
|
else
|
|
{
|
|
bool always = false;
|
|
int argstart = 2;
|
|
int arg[4] = { 0, 0, 0, 0 };
|
|
int argn = 0, i;
|
|
|
|
if (argc > 2)
|
|
{
|
|
if (stricmp(argv[2], "always") == 0)
|
|
{
|
|
always = true;
|
|
argstart = 3;
|
|
}
|
|
argn = MIN<int>(argc - argstart, countof(arg));
|
|
for (i = 0; i < argn; ++i)
|
|
{
|
|
arg[i] = atoi(argv[argstart + i]);
|
|
}
|
|
}
|
|
Net_WriteByte(DEM_RUNNAMEDSCRIPT);
|
|
Net_WriteString(argv[1]);
|
|
Net_WriteByte(argn | (always << 7));
|
|
for (i = 0; i < argn; ++i)
|
|
{
|
|
Net_WriteLong(arg[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
CCMD (special)
|
|
{
|
|
int argc = argv.argc();
|
|
|
|
if (argc < 2 || argc > 7)
|
|
{
|
|
Printf("Usage: special <special-name> [arg1] [arg2] [arg3] [arg4] [arg5]\n");
|
|
}
|
|
else
|
|
{
|
|
int specnum;
|
|
|
|
if (argv[1][0] >= '0' && argv[1][0] <= '9')
|
|
{
|
|
specnum = atoi(argv[1]);
|
|
if (specnum < 0 || specnum > 255)
|
|
{
|
|
Printf("Bad special number\n");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int min_args;
|
|
specnum = P_FindLineSpecial(argv[1], &min_args);
|
|
if (specnum == 0 || min_args < 0)
|
|
{
|
|
Printf("Unknown special\n");
|
|
return;
|
|
}
|
|
if (argc < 2 + min_args)
|
|
{
|
|
Printf("%s needs at least %d argument%s\n", argv[1], min_args, min_args == 1 ? "" : "s");
|
|
return;
|
|
}
|
|
}
|
|
Net_WriteByte(DEM_RUNSPECIAL);
|
|
Net_WriteByte(specnum);
|
|
Net_WriteByte(argc - 2);
|
|
for (int i = 2; i < argc; ++i)
|
|
{
|
|
Net_WriteLong(atoi(argv[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
CCMD (error)
|
|
{
|
|
if (argv.argc() > 1)
|
|
{
|
|
char *textcopy = copystring (argv[1]);
|
|
I_Error ("%s", textcopy);
|
|
}
|
|
else
|
|
{
|
|
Printf ("Usage: error <error text>\n");
|
|
}
|
|
}
|
|
|
|
CCMD (error_fatal)
|
|
{
|
|
if (argv.argc() > 1)
|
|
{
|
|
char *textcopy = copystring (argv[1]);
|
|
I_FatalError ("%s", textcopy);
|
|
}
|
|
else
|
|
{
|
|
Printf ("Usage: error_fatal <error text>\n");
|
|
}
|
|
}
|
|
|
|
CCMD (dir)
|
|
{
|
|
FString dir, path;
|
|
char curdir[256];
|
|
const char *match;
|
|
findstate_t c_file;
|
|
void *file;
|
|
|
|
if (!getcwd (curdir, countof(curdir)))
|
|
{
|
|
Printf ("Current path too long\n");
|
|
return;
|
|
}
|
|
|
|
if (argv.argc() > 1)
|
|
{
|
|
path = NicePath(argv[1]);
|
|
if (chdir(path))
|
|
{
|
|
match = path;
|
|
dir = ExtractFilePath(path);
|
|
if (dir[0] != '\0')
|
|
{
|
|
match += dir.Len();
|
|
}
|
|
else
|
|
{
|
|
dir = "./";
|
|
}
|
|
if (match[0] == '\0')
|
|
{
|
|
match = "*";
|
|
}
|
|
if (chdir (dir))
|
|
{
|
|
Printf ("%s not found\n", dir.GetChars());
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
match = "*";
|
|
dir = path;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
match = "*";
|
|
dir = curdir;
|
|
}
|
|
if (dir[dir.Len()-1] != '/')
|
|
{
|
|
dir += '/';
|
|
}
|
|
|
|
if ( (file = I_FindFirst (match, &c_file)) == ((void *)(-1)))
|
|
Printf ("Nothing matching %s%s\n", dir.GetChars(), match);
|
|
else
|
|
{
|
|
Printf ("Listing of %s%s:\n", dir.GetChars(), match);
|
|
do
|
|
{
|
|
if (I_FindAttr (&c_file) & FA_DIREC)
|
|
Printf (PRINT_BOLD, "%s <dir>\n", I_FindName (&c_file));
|
|
else
|
|
Printf ("%s\n", I_FindName (&c_file));
|
|
} while (I_FindNext (file, &c_file) == 0);
|
|
I_FindClose (file);
|
|
}
|
|
|
|
chdir (curdir);
|
|
}
|
|
|
|
CCMD (fov)
|
|
{
|
|
player_t *player = who ? who->player : &players[consoleplayer];
|
|
|
|
if (argv.argc() != 2)
|
|
{
|
|
Printf ("fov is %g\n", player->DesiredFOV);
|
|
return;
|
|
}
|
|
else if (dmflags & DF_NO_FOV)
|
|
{
|
|
if (consoleplayer == Net_Arbitrator)
|
|
{
|
|
Net_WriteByte (DEM_FOV);
|
|
}
|
|
else
|
|
{
|
|
Printf ("A setting controller has disabled FOV changes.\n");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Net_WriteByte (DEM_MYFOV);
|
|
}
|
|
Net_WriteByte (clamp (atoi (argv[1]), 5, 179));
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD warp
|
|
//
|
|
// Warps to a specific location on a map
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (warp)
|
|
{
|
|
if (CheckCheatmode ())
|
|
{
|
|
return;
|
|
}
|
|
if (gamestate != GS_LEVEL)
|
|
{
|
|
Printf ("You can only warp inside a level.\n");
|
|
return;
|
|
}
|
|
if (argv.argc() != 3)
|
|
{
|
|
Printf ("Usage: warp <x> <y>\n");
|
|
}
|
|
else
|
|
{
|
|
Net_WriteByte (DEM_WARPCHEAT);
|
|
Net_WriteWord (atoi (argv[1]));
|
|
Net_WriteWord (atoi (argv[2]));
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD load
|
|
//
|
|
// Load a saved game.
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (load)
|
|
{
|
|
if (argv.argc() != 2)
|
|
{
|
|
Printf ("usage: load <filename>\n");
|
|
return;
|
|
}
|
|
if (netgame)
|
|
{
|
|
Printf ("cannot load during a network game\n");
|
|
return;
|
|
}
|
|
FString fname = argv[1];
|
|
DefaultExtension (fname, ".zds");
|
|
G_LoadGame (fname);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD save
|
|
//
|
|
// Save the current game.
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (save)
|
|
{
|
|
if (argv.argc() < 2 || argv.argc() > 3)
|
|
{
|
|
Printf ("usage: save <filename> [description]\n");
|
|
return;
|
|
}
|
|
if (!usergame)
|
|
{
|
|
Printf ("not in a saveable game\n");
|
|
return;
|
|
}
|
|
if (gamestate != GS_LEVEL)
|
|
{
|
|
Printf ("not in a level\n");
|
|
return;
|
|
}
|
|
if(players[consoleplayer].health <= 0 && !multiplayer)
|
|
{
|
|
Printf ("player is dead in a single-player game\n");
|
|
return;
|
|
}
|
|
FString fname = argv[1];
|
|
DefaultExtension (fname, ".zds");
|
|
G_SaveGame (fname, argv.argc() > 2 ? argv[2] : argv[1]);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// CCMD wdir
|
|
//
|
|
// Lists the contents of a loaded wad file.
|
|
//
|
|
//==========================================================================
|
|
|
|
CCMD (wdir)
|
|
{
|
|
if (argv.argc() != 2)
|
|
{
|
|
Printf ("usage: wdir <wadfile>\n");
|
|
return;
|
|
}
|
|
int wadnum = Wads.CheckIfWadLoaded (argv[1]);
|
|
if (wadnum < 0)
|
|
{
|
|
Printf ("%s must be loaded to view its directory.\n", argv[1]);
|
|
return;
|
|
}
|
|
for (int i = 0; i < Wads.GetNumLumps(); ++i)
|
|
{
|
|
if (Wads.GetLumpFile(i) == wadnum)
|
|
{
|
|
Printf ("%s\n", Wads.GetLumpFullName(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CCMD(linetarget)
|
|
{
|
|
AActor *linetarget;
|
|
|
|
if (CheckCheatmode () || players[consoleplayer].mo == NULL) return;
|
|
P_AimLineAttack(players[consoleplayer].mo,players[consoleplayer].mo->angle,MISSILERANGE, &linetarget, 0);
|
|
if (linetarget)
|
|
{
|
|
Printf("Target=%s, Health=%d, Spawnhealth=%d\n",
|
|
linetarget->GetClass()->TypeName.GetChars(),
|
|
linetarget->health,
|
|
linetarget->SpawnHealth());
|
|
}
|
|
else Printf("No target found\n");
|
|
}
|
|
|
|
// As linetarget, but also give info about non-shootable actors
|
|
CCMD(info)
|
|
{
|
|
AActor *linetarget;
|
|
|
|
if (CheckCheatmode () || players[consoleplayer].mo == NULL) return;
|
|
P_AimLineAttack(players[consoleplayer].mo,players[consoleplayer].mo->angle,MISSILERANGE,
|
|
&linetarget, 0, ALF_CHECKNONSHOOTABLE|ALF_FORCENOSMART);
|
|
if (linetarget)
|
|
{
|
|
Printf("Target=%s, Health=%d, Spawnhealth=%d\n",
|
|
linetarget->GetClass()->TypeName.GetChars(),
|
|
linetarget->health,
|
|
linetarget->SpawnHealth());
|
|
PrintMiscActorInfo(linetarget);
|
|
}
|
|
else Printf("No target found. Info cannot find actors that have\
|
|
the NOBLOCKMAP flag or have height/radius of 0.\n");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CCMD(monster)
|
|
{
|
|
AActor * mo;
|
|
|
|
if (CheckCheatmode ()) return;
|
|
TThinkerIterator<AActor> it;
|
|
|
|
while ( (mo = it.Next()) )
|
|
{
|
|
if (mo->flags3&MF3_ISMONSTER && !(mo->flags&MF_CORPSE) && !(mo->flags&MF_FRIENDLY))
|
|
{
|
|
Printf ("%s at (%d,%d,%d)\n",
|
|
mo->GetClass()->TypeName.GetChars(),
|
|
mo->x >> FRACBITS, mo->y >> FRACBITS, mo->z >> FRACBITS);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CCMD(items)
|
|
{
|
|
AActor * mo;
|
|
|
|
if (CheckCheatmode ()) return;
|
|
TThinkerIterator<AActor> it;
|
|
|
|
while ( (mo = it.Next()) )
|
|
{
|
|
if (mo->IsKindOf(RUNTIME_CLASS(AInventory)) && mo->flags&MF_SPECIAL)
|
|
{
|
|
Printf ("%s at (%d,%d,%d)\n",
|
|
mo->GetClass()->TypeName.GetChars(),
|
|
mo->x >> FRACBITS, mo->y >> FRACBITS, mo->z >> FRACBITS);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CCMD(changesky)
|
|
{
|
|
const char *sky1name;
|
|
|
|
if (netgame || argv.argc()<2) return;
|
|
|
|
sky1name = argv[1];
|
|
if (sky1name[0] != 0)
|
|
{
|
|
strncpy (level.skypic1, sky1name, 8);
|
|
sky1texture = TexMan.GetTexture (sky1name, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable);
|
|
}
|
|
R_InitSkyMap ();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CCMD(thaw)
|
|
{
|
|
if (CheckCheatmode())
|
|
return;
|
|
|
|
Net_WriteByte (DEM_GENERICCHEAT);
|
|
Net_WriteByte (CHT_CLEARFROZENPROPS);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CCMD(nextmap)
|
|
{
|
|
if (netgame)
|
|
{
|
|
Printf ("Use " TEXTCOLOR_BOLD "changemap" TEXTCOLOR_NORMAL " instead. " TEXTCOLOR_BOLD "Nextmap"
|
|
TEXTCOLOR_NORMAL " is for single-player only.\n");
|
|
return;
|
|
}
|
|
char *next = NULL;
|
|
|
|
if (*level.nextmap)
|
|
next = level.nextmap;
|
|
|
|
if (next != NULL && strncmp(next, "enDSeQ", 6))
|
|
{
|
|
G_DeferedInitNew(next);
|
|
}
|
|
else
|
|
{
|
|
Printf("no next map!\n");
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CCMD(nextsecret)
|
|
{
|
|
if (netgame)
|
|
{
|
|
Printf ("Use " TEXTCOLOR_BOLD "changemap" TEXTCOLOR_NORMAL " instead. " TEXTCOLOR_BOLD "Nextsecret"
|
|
TEXTCOLOR_NORMAL " is for single-player only.\n");
|
|
return;
|
|
}
|
|
char *next = NULL;
|
|
|
|
if (*level.secretmap)
|
|
next = level.secretmap;
|
|
|
|
if (next != NULL && strncmp(next, "enDSeQ", 6))
|
|
{
|
|
G_DeferedInitNew(next);
|
|
}
|
|
else
|
|
{
|
|
Printf("no next secret map!\n");
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CCMD(currentpos)
|
|
{
|
|
AActor *mo = players[consoleplayer].mo;
|
|
Printf("Current player position: (%1.3f,%1.3f,%1.3f), angle: %1.3f, floorheight: %1.3f, sector:%d, lightlevel: %d\n",
|
|
FIXED2FLOAT(mo->x), FIXED2FLOAT(mo->y), FIXED2FLOAT(mo->z), mo->angle/float(ANGLE_1), FIXED2FLOAT(mo->floorz), mo->Sector->sectornum, mo->Sector->lightlevel);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Print secret info (submitted by Karl Murks)
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void PrintSecretString(const char *string, bool thislevel)
|
|
{
|
|
const char *colstr = thislevel? TEXTCOLOR_YELLOW : TEXTCOLOR_CYAN;
|
|
if (string != NULL)
|
|
{
|
|
if (*string == '$')
|
|
{
|
|
if (string[1] == 'S' || string[1] == 's')
|
|
{
|
|
long secnum = strtol(string+2, (char**)&string, 10);
|
|
if (*string == ';') string++;
|
|
if (thislevel && secnum >= 0 && secnum < numsectors)
|
|
{
|
|
if (sectors[secnum].secretsector)
|
|
{
|
|
if ((sectors[secnum].special & SECRET_MASK)) colstr = TEXTCOLOR_RED;
|
|
else colstr = TEXTCOLOR_GREEN;
|
|
}
|
|
else colstr = TEXTCOLOR_ORANGE;
|
|
}
|
|
}
|
|
else if (string[1] == 'T' || string[1] == 't')
|
|
{
|
|
long tid = strtol(string+2, (char**)&string, 10);
|
|
if (*string == ';') string++;
|
|
FActorIterator it(tid);
|
|
AActor *actor;
|
|
bool foundone = false;
|
|
if (thislevel)
|
|
{
|
|
while ((actor = it.Next()))
|
|
{
|
|
if (!actor->IsKindOf(PClass::FindClass("SecretTrigger"))) continue;
|
|
foundone = true;
|
|
break;
|
|
}
|
|
}
|
|
if (foundone) colstr = TEXTCOLOR_RED;
|
|
else colstr = TEXTCOLOR_GREEN;
|
|
}
|
|
}
|
|
FBrokenLines *brok = V_BreakLines(ConFont, screen->GetWidth()*95/100, string);
|
|
|
|
for (int k = 0; brok[k].Width >= 0; k++)
|
|
{
|
|
Printf("%s%s\n", colstr, brok[k].Text.GetChars());
|
|
}
|
|
V_FreeBrokenLines(brok);
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// Print secret hints
|
|
//
|
|
//============================================================================
|
|
|
|
CCMD(secret)
|
|
{
|
|
const char *mapname = argv.argc() < 2? level.mapname : argv[1];
|
|
bool thislevel = !stricmp(mapname, level.mapname);
|
|
bool foundsome = false;
|
|
|
|
int lumpno=Wads.CheckNumForName("SECRETS");
|
|
if (lumpno < 0) return;
|
|
|
|
FWadLump lump = Wads.OpenLumpNum(lumpno);
|
|
FString maphdr;
|
|
maphdr.Format("[%s]", mapname);
|
|
|
|
FString linebuild;
|
|
char readbuffer[1024];
|
|
bool inlevel = false;
|
|
|
|
while (lump.Gets(readbuffer, 1024))
|
|
{
|
|
if (!inlevel)
|
|
{
|
|
if (readbuffer[0] == '[')
|
|
{
|
|
inlevel = !strnicmp(readbuffer, maphdr, maphdr.Len());
|
|
if (!foundsome)
|
|
{
|
|
FString levelname;
|
|
level_info_t *info = FindLevelInfo(mapname);
|
|
const char *ln = !(info->flags & LEVEL_LOOKUPLEVELNAME)? info->LevelName.GetChars() : GStrings[info->LevelName.GetChars()];
|
|
levelname.Format("%s - %s\n", mapname, ln);
|
|
size_t llen = levelname.Len() - 1;
|
|
for(size_t ii=0; ii<llen; ii++) levelname += '-';
|
|
Printf(TEXTCOLOR_YELLOW"%s\n", levelname.GetChars());
|
|
foundsome = true;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (readbuffer[0] != '[')
|
|
{
|
|
linebuild += readbuffer;
|
|
if (linebuild.Len() < 1023 || linebuild[1022] == '\n')
|
|
{
|
|
// line complete so print it.
|
|
linebuild.Substitute("\r", "");
|
|
linebuild.StripRight(" \t\n");
|
|
PrintSecretString(linebuild, thislevel);
|
|
linebuild = "";
|
|
}
|
|
}
|
|
else inlevel = false;
|
|
}
|
|
}
|
|
}
|