2025-01-13 03:34:38 -06:00
|
|
|
// Emacs style mode select -*- C++ -*-
|
2007-01-16 04:43:42 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//
|
2008-05-04 19:51:00 +00:00
|
|
|
// $Id$
|
2007-01-16 04:43:42 +00:00
|
|
|
//
|
|
|
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
2026-01-05 01:52:47 +00:00
|
|
|
// Copyright (C) 2006-2026 by The Odamex Team.
|
2007-01-16 04:43:42 +00:00
|
|
|
//
|
|
|
|
|
// 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.
|
|
|
|
|
//
|
|
|
|
|
// DESCRIPTION:
|
|
|
|
|
// Cheat code checking.
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2021-09-14 20:09:20 -04:00
|
|
|
#include "odamex.h"
|
|
|
|
|
|
2007-01-16 04:43:42 +00:00
|
|
|
#include <math.h>
|
|
|
|
|
|
2021-04-21 15:58:18 +02:00
|
|
|
#include "g_gametype.h"
|
2007-01-16 04:43:42 +00:00
|
|
|
#include "d_player.h"
|
2026-01-29 17:11:41 -06:00
|
|
|
#include "m_cheat.h"
|
2012-01-25 05:16:48 +00:00
|
|
|
#include "gstrings.h"
|
2007-01-16 04:43:42 +00:00
|
|
|
#include "p_inter.h"
|
|
|
|
|
#include "d_items.h"
|
2022-02-08 03:28:25 -08:00
|
|
|
#include "g_skill.h"
|
2007-01-16 04:43:42 +00:00
|
|
|
#include "p_local.h"
|
2025-01-16 01:41:27 -05:00
|
|
|
#include "infomap.h"
|
2025-02-07 00:52:07 -05:00
|
|
|
#include "c_effect.h"
|
2026-06-29 15:14:31 -05:00
|
|
|
#include "c_dispatch.h"
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2021-04-21 15:58:18 +02:00
|
|
|
extern bool simulated_connection;
|
|
|
|
|
EXTERN_CVAR(sv_allowcheats)
|
|
|
|
|
|
2021-04-28 03:59:45 +02:00
|
|
|
#ifdef CLIENT_APP
|
2021-06-06 20:10:27 +02:00
|
|
|
#include "am_map.h"
|
2021-04-28 03:59:45 +02:00
|
|
|
#include "cl_main.h"
|
|
|
|
|
extern bool automapactive;
|
|
|
|
|
#endif
|
|
|
|
|
|
2025-04-15 00:07:32 -05:00
|
|
|
void C_DoCommand(std::string_view cmd, uint32_t key = 0);
|
2021-06-01 22:45:00 +02:00
|
|
|
|
2007-01-16 04:43:42 +00:00
|
|
|
//
|
|
|
|
|
// CHEAT SEQUENCE PACKAGE
|
|
|
|
|
//
|
|
|
|
|
|
2021-04-28 18:55:19 +02:00
|
|
|
#ifdef CLIENT_APP
|
2021-06-06 20:10:27 +02:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
namespace cheat
|
|
|
|
|
{
|
|
|
|
|
|
2021-04-28 03:59:45 +02:00
|
|
|
//-------------
|
|
|
|
|
// THESE ARE MAINLY FOR THE CLIENT
|
|
|
|
|
// Smashing Pumpkins Into Small Piles Of Putrid Debris.
|
2026-01-29 17:11:41 -06:00
|
|
|
bool AutoMap(cheatseq_t* cheat)
|
2021-04-28 03:59:45 +02:00
|
|
|
{
|
|
|
|
|
if (automapactive)
|
|
|
|
|
{
|
2021-06-10 16:42:34 +02:00
|
|
|
if (!multiplayer || G_IsCoopGame())
|
2021-06-06 20:10:27 +02:00
|
|
|
am_cheating = (am_cheating + 1) % 3;
|
|
|
|
|
|
2021-04-28 03:59:45 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
bool ChangeLevel(cheatseq_t* cheat)
|
2021-04-28 03:59:45 +02:00
|
|
|
{
|
2025-02-08 15:07:18 -06:00
|
|
|
std::string buf;
|
2021-04-28 03:59:45 +02:00
|
|
|
|
2021-04-28 18:55:19 +02:00
|
|
|
// What were you trying to achieve?
|
|
|
|
|
if (multiplayer)
|
|
|
|
|
return false;
|
|
|
|
|
|
2021-04-28 03:59:45 +02:00
|
|
|
// [ML] Chex mode: always set the episode number to 1.
|
|
|
|
|
// FIXME: This is probably a horrible hack, it sure looks like one at least
|
2025-02-08 15:07:18 -06:00
|
|
|
// And why is there only a newline for non-chex?
|
2021-04-28 03:59:45 +02:00
|
|
|
if (gamemode == retail_chex)
|
2025-08-17 20:38:50 -05:00
|
|
|
buf = fmt::format("map 1{:c}", cheat->Args[1]);
|
2021-04-28 18:55:19 +02:00
|
|
|
else
|
2025-08-17 20:35:41 -05:00
|
|
|
buf = fmt::format("map {:c}{:c}\n", cheat->Args[0], cheat->Args[1]);
|
2025-01-13 03:34:38 -06:00
|
|
|
|
2021-04-28 03:59:45 +02:00
|
|
|
AddCommandString(buf);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
bool IdMyPos(cheatseq_t* cheat)
|
2021-04-28 03:59:45 +02:00
|
|
|
{
|
|
|
|
|
C_DoCommand("toggle idmypos", 0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
bool BeholdMenu(cheatseq_t* cheat)
|
2021-04-28 03:59:45 +02:00
|
|
|
{
|
2025-08-21 06:08:01 -05:00
|
|
|
PrintFmt(PRINT_HIGH, "{}\n", GStrings(STSTR_BEHOLD));
|
2021-04-28 03:59:45 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
bool ChangeMusic(cheatseq_t* cheat)
|
2021-04-28 03:59:45 +02:00
|
|
|
{
|
|
|
|
|
char buf[9] = "idmus xx";
|
|
|
|
|
|
|
|
|
|
buf[6] = cheat->Args[0];
|
|
|
|
|
buf[7] = cheat->Args[1];
|
|
|
|
|
C_DoCommand(buf, 0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Sets clientside the new cheat flag
|
|
|
|
|
// and also requests its new status serverside
|
|
|
|
|
//
|
2026-01-29 17:11:41 -06:00
|
|
|
bool SetGeneric(cheatseq_t* cheat)
|
2021-04-28 03:59:45 +02:00
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
if (!AreCheatsEnabled())
|
2021-04-28 03:59:45 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (cheat->Args[0] == CHT_NOCLIP)
|
|
|
|
|
{
|
|
|
|
|
if (cheat->Args[1] == 0 && gamemode != shareware && gamemode != registered &&
|
|
|
|
|
gamemode != retail && gamemode != retail_bfg)
|
|
|
|
|
return true;
|
|
|
|
|
else if (cheat->Args[1] == 1 && gamemode != commercial &&
|
|
|
|
|
gamemode != commercial_bfg)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
DoCheat(consoleplayer(), (ECheatFlags)cheat->Args[0]);
|
2021-04-28 03:59:45 +02:00
|
|
|
CL_SendCheat((ECheatFlags)cheat->Args[0]);
|
|
|
|
|
|
2021-04-21 15:58:18 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 03:59:45 +02:00
|
|
|
// [RH] Actually handle the cheat. The cheat code in st_stuff.c now just
|
|
|
|
|
// writes some bytes to the network data stream, and the network code
|
|
|
|
|
// later calls us.
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
bool AddKey(cheatseq_t* cheat, unsigned char key, bool* eat)
|
2021-04-28 03:59:45 +02:00
|
|
|
{
|
2026-06-29 15:15:52 -05:00
|
|
|
if (cheat->Pos == nullptr)
|
2007-01-16 04:43:42 +00:00
|
|
|
{
|
2021-04-28 03:59:45 +02:00
|
|
|
cheat->Pos = cheat->Sequence;
|
|
|
|
|
cheat->CurrentArg = 0;
|
|
|
|
|
}
|
|
|
|
|
if (*cheat->Pos == 0)
|
|
|
|
|
{
|
|
|
|
|
*eat = true;
|
|
|
|
|
cheat->Args[cheat->CurrentArg++] = key;
|
|
|
|
|
cheat->Pos++;
|
|
|
|
|
}
|
|
|
|
|
else if (key == *cheat->Pos)
|
|
|
|
|
{
|
|
|
|
|
cheat->Pos++;
|
2007-01-16 04:43:42 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-04-28 03:59:45 +02:00
|
|
|
cheat->Pos = cheat->Sequence;
|
|
|
|
|
cheat->CurrentArg = 0;
|
2007-01-16 04:43:42 +00:00
|
|
|
}
|
2021-04-28 03:59:45 +02:00
|
|
|
if (*cheat->Pos == 0xff)
|
2007-01-16 04:43:42 +00:00
|
|
|
{
|
2021-04-28 03:59:45 +02:00
|
|
|
cheat->Pos = cheat->Sequence;
|
|
|
|
|
cheat->CurrentArg = 0;
|
|
|
|
|
return true;
|
2007-01-16 04:43:42 +00:00
|
|
|
}
|
2021-04-28 03:59:45 +02:00
|
|
|
return false;
|
2007-01-16 04:43:42 +00:00
|
|
|
}
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
} // namespace cheat
|
|
|
|
|
|
2021-06-10 16:08:47 +02:00
|
|
|
BEGIN_COMMAND(tntem)
|
2021-04-29 01:22:55 +02:00
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
if (!cheat::AreCheatsEnabled())
|
2021-04-29 01:22:55 +02:00
|
|
|
return;
|
|
|
|
|
|
2021-06-10 16:42:34 +02:00
|
|
|
if (multiplayer && !G_IsCoopGame())
|
2021-04-29 01:22:55 +02:00
|
|
|
return;
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
cheat::DoCheat(consoleplayer(), CHT_MASSACRE);
|
2021-04-29 01:22:55 +02:00
|
|
|
CL_SendCheat(CHT_MASSACRE);
|
|
|
|
|
}
|
2021-06-10 16:08:47 +02:00
|
|
|
END_COMMAND(tntem)
|
|
|
|
|
|
2025-01-16 22:13:59 -05:00
|
|
|
BEGIN_COMMAND(summon)
|
2025-01-16 01:41:27 -05:00
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
if (!cheat::AreCheatsEnabled())
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (argc < 2)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const std::string mobname = C_ArgCombine(argc - 1, (const char**)(argv + 1));
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (!cheat::ValidSummonActor(mobname))
|
2025-01-16 01:41:27 -05:00
|
|
|
{
|
2025-08-21 06:08:01 -05:00
|
|
|
PrintFmt(PRINT_HIGH, "Invalid summon argument: {}. Please use `dumpactors` for a valid list of actor names.\n", mobname);
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
cheat::Summon(consoleplayer(), mobname, false);
|
2025-01-16 01:41:27 -05:00
|
|
|
CL_SendSummonCheat(mobname.c_str());
|
|
|
|
|
}
|
2025-01-16 22:13:59 -05:00
|
|
|
END_COMMAND(summon)
|
2025-01-16 01:41:27 -05:00
|
|
|
|
2025-01-29 22:25:36 -05:00
|
|
|
BEGIN_COMMAND(summonfriend)
|
|
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
if (!cheat::AreCheatsEnabled())
|
2025-01-29 22:25:36 -05:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (argc < 2)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const std::string mobname = C_ArgCombine(argc - 1, (const char**)(argv + 1));
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (!cheat::ValidSummonActor(mobname.c_str()))
|
2025-01-29 22:25:36 -05:00
|
|
|
{
|
2025-10-22 16:38:21 -05:00
|
|
|
PrintFmt(PRINT_HIGH,
|
|
|
|
|
"Invalid summon argument: {}. Please use `dumpactors` for a valid list of "
|
|
|
|
|
"actor names.\n",
|
|
|
|
|
mobname);
|
2025-01-29 22:25:36 -05:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
cheat::Summon(consoleplayer(), mobname.c_str(), true);
|
2025-01-29 22:25:36 -05:00
|
|
|
CL_SendSummonFriendCheat(mobname.c_str());
|
|
|
|
|
}
|
|
|
|
|
END_COMMAND(summonfriend)
|
|
|
|
|
|
2021-06-10 16:08:47 +02:00
|
|
|
BEGIN_COMMAND(mdk)
|
|
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
if (!cheat::AreCheatsEnabled())
|
2021-06-10 16:08:47 +02:00
|
|
|
return;
|
|
|
|
|
|
2021-06-10 16:42:34 +02:00
|
|
|
if (multiplayer && !G_IsCoopGame())
|
2021-06-10 16:08:47 +02:00
|
|
|
return;
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
cheat::DoCheat(consoleplayer(), CHT_MDK);
|
2021-06-10 16:08:47 +02:00
|
|
|
CL_SendCheat(CHT_MDK);
|
|
|
|
|
}
|
2021-04-29 01:22:55 +02:00
|
|
|
END_COMMAND(mdk)
|
|
|
|
|
|
2021-04-28 18:55:19 +02:00
|
|
|
#endif
|
|
|
|
|
|
2026-06-29 14:54:58 -05:00
|
|
|
#ifdef SERVER_APP
|
2026-06-29 14:38:50 -05:00
|
|
|
BEGIN_COMMAND(tntem)
|
2026-06-29 14:54:58 -05:00
|
|
|
{
|
2026-06-29 14:38:50 -05:00
|
|
|
const int kills = P_Massacre();
|
|
|
|
|
PrintFmt("Killed {} enemies.\n", kills);
|
2026-06-29 14:54:58 -05:00
|
|
|
}
|
2026-06-29 14:38:50 -05:00
|
|
|
END_COMMAND(tntem)
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
void A_PainDie(AActor*);
|
|
|
|
|
|
|
|
|
|
namespace cheat
|
|
|
|
|
{
|
|
|
|
|
|
2021-04-28 18:55:19 +02:00
|
|
|
// Checks if all the conditions to enable cheats are met.
|
2026-01-29 17:11:41 -06:00
|
|
|
bool AreCheatsEnabled()
|
2021-04-28 18:55:19 +02:00
|
|
|
{
|
|
|
|
|
// [SL] 2012-04-04 - Don't allow cheat codes to be entered while playing
|
|
|
|
|
// back a netdemo
|
|
|
|
|
if (simulated_connection)
|
|
|
|
|
return false;
|
|
|
|
|
|
2021-06-01 19:17:21 +02:00
|
|
|
// Disallow cheats within any state other than ingame.
|
|
|
|
|
if (gamestate != GS_LEVEL)
|
|
|
|
|
return false;
|
|
|
|
|
|
2021-04-28 18:55:19 +02:00
|
|
|
// [Russell] - Allow vanilla style "no message" in singleplayer when cheats
|
|
|
|
|
// are disabled
|
2022-02-17 00:31:51 -08:00
|
|
|
if (!multiplayer && G_GetCurrentSkill().disable_cheats)
|
2023-01-16 20:00:21 -05:00
|
|
|
{
|
|
|
|
|
if (!sv_allowcheats)
|
|
|
|
|
{
|
2025-08-21 06:08:01 -05:00
|
|
|
PrintFmt(PRINT_WARNING,
|
|
|
|
|
"You must 'set sv_allowcheats 1' in the console to enable "
|
|
|
|
|
"this command on this difficulty.\n");
|
2023-01-16 20:00:21 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-28 18:55:19 +02:00
|
|
|
|
|
|
|
|
if ((multiplayer || !G_IsCoopGame()) && !sv_allowcheats)
|
|
|
|
|
{
|
2025-08-21 06:08:01 -05:00
|
|
|
PrintFmt(PRINT_WARNING, "You must run the server with '+set sv_allowcheats 1' to "
|
|
|
|
|
"enable this command.\n");
|
2021-04-28 18:55:19 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
void DoCheat(player_t& player, int cheat, bool silentmsg)
|
2007-01-16 04:43:42 +00:00
|
|
|
{
|
2025-02-08 15:07:18 -06:00
|
|
|
std::string msg;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.health <= 0)
|
2021-04-29 01:22:55 +02:00
|
|
|
return;
|
|
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
switch (cheat)
|
|
|
|
|
{
|
|
|
|
|
case CHT_IDDQD:
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.spectator)
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (!(player.cheats & CF_GODMODE))
|
2025-01-16 01:41:27 -05:00
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.mo)
|
|
|
|
|
player.mo->health = deh.GodHealth;
|
2021-04-21 19:48:09 +02:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
player.health = deh.GodHealth;
|
2025-01-16 01:41:27 -05:00
|
|
|
}
|
|
|
|
|
[[fallthrough]];
|
|
|
|
|
case CHT_GOD:
|
2021-04-21 19:48:09 +02:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.spectator)
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
player.cheats ^= CF_GODMODE;
|
|
|
|
|
msg = (player.cheats & CF_GODMODE) ? GStrings(STSTR_DQDON)
|
2025-01-16 01:41:27 -05:00
|
|
|
: GStrings(STSTR_DQDOFF);
|
|
|
|
|
break;
|
2021-04-21 19:48:09 +02:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
case CHT_NOCLIP:
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.spectator)
|
2025-01-16 01:41:27 -05:00
|
|
|
silentmsg = true;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
player.cheats ^= CF_NOCLIP;
|
|
|
|
|
msg = (player.cheats & CF_NOCLIP) ? GStrings(STSTR_NCON) : GStrings(STSTR_NCOFF);
|
2025-01-16 01:41:27 -05:00
|
|
|
break;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
case CHT_FLY:
|
2026-01-29 17:11:41 -06:00
|
|
|
player.cheats ^= CF_FLY;
|
|
|
|
|
msg = (player.cheats & CF_FLY) ? "You feel lighter" : "Gravity weighs you down";
|
2025-01-16 01:41:27 -05:00
|
|
|
break;
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
case CHT_NOTARGET:
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.spectator)
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
player.cheats ^= CF_NOTARGET;
|
|
|
|
|
msg = (player.cheats & CF_NOTARGET) ? "notarget ON" : "notarget OFF";
|
2025-01-16 01:41:27 -05:00
|
|
|
break;
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
case CHT_CHASECAM:
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.spectator)
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
player.cheats ^= CF_CHASECAM;
|
|
|
|
|
msg = (player.cheats & CF_CHASECAM) ? "chasecam ON" : "chasecam OFF";
|
2025-01-16 01:41:27 -05:00
|
|
|
break;
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
case CHT_CHAINSAW:
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.spectator)
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
player.weaponowned[wp_chainsaw] = true;
|
|
|
|
|
player.powers[pw_invulnerability] = true;
|
2025-01-16 01:41:27 -05:00
|
|
|
msg = GStrings(STSTR_CHOPPERS);
|
|
|
|
|
break;
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
case CHT_IDKFA:
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.spectator)
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
GiveTo(player, "all");
|
|
|
|
|
player.armorpoints = deh.KFAArmor;
|
|
|
|
|
player.armortype = deh.KFAAC;
|
2025-01-16 01:41:27 -05:00
|
|
|
msg = GStrings(STSTR_KFAADDED);
|
|
|
|
|
break;
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
case CHT_IDFA:
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.spectator)
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
GiveTo(player, "backpack");
|
|
|
|
|
GiveTo(player, "weapons");
|
|
|
|
|
GiveTo(player, "ammo");
|
|
|
|
|
player.armorpoints = deh.FAArmor;
|
|
|
|
|
player.armortype = deh.FAAC;
|
2025-01-16 01:41:27 -05:00
|
|
|
msg = GStrings(STSTR_FAADDED);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CHT_BEHOLDV:
|
|
|
|
|
case CHT_BEHOLDS:
|
|
|
|
|
case CHT_BEHOLDI:
|
|
|
|
|
case CHT_BEHOLDR:
|
|
|
|
|
case CHT_BEHOLDA:
|
|
|
|
|
case CHT_BEHOLDL: {
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.spectator)
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
int i = cheat - CHT_BEHOLDV;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (!player.powers[i])
|
2025-01-16 01:41:27 -05:00
|
|
|
P_GivePower(player, i);
|
|
|
|
|
else if (i != pw_strength)
|
2026-01-29 17:11:41 -06:00
|
|
|
player.powers[i] = 1;
|
2025-01-16 01:41:27 -05:00
|
|
|
else
|
2026-01-29 17:11:41 -06:00
|
|
|
player.powers[i] = 0;
|
2025-01-16 01:41:27 -05:00
|
|
|
}
|
|
|
|
|
msg = GStrings(STSTR_BEHOLDX);
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
break;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
case CHT_MASSACRE: {
|
|
|
|
|
// jff 02/01/98 'em' cheat - kill all monsters
|
|
|
|
|
// partially taken from Chi's .46 port
|
|
|
|
|
//
|
|
|
|
|
// killough 2/7/98: cleaned up code and changed to use dprintf;
|
|
|
|
|
// fixed lost soul bug (LSs left behind when PEs are killed)
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
int killcount = 0;
|
|
|
|
|
AActor* actor;
|
|
|
|
|
TThinkerIterator<AActor> iterator;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (multiplayer && !player.client.allow_rcon)
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
2021-04-29 01:22:55 +02:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
while ((actor = iterator.Next()))
|
|
|
|
|
{
|
|
|
|
|
if (actor->flags & MF_COUNTKILL || actor->type == MT_SKULL)
|
|
|
|
|
{
|
|
|
|
|
// killough 3/6/98: kill even if PE is dead
|
|
|
|
|
if (actor->health > 0)
|
|
|
|
|
{
|
|
|
|
|
killcount++;
|
2026-06-29 15:15:52 -05:00
|
|
|
P_DamageMobj(actor, nullptr, nullptr, 10000, MOD_UNKNOWN);
|
2025-01-16 01:41:27 -05:00
|
|
|
}
|
|
|
|
|
if (actor->type == MT_PAIN)
|
2007-01-16 04:43:42 +00:00
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
::A_PainDie(actor); // killough 2/8/98
|
2025-01-16 01:41:27 -05:00
|
|
|
P_SetMobjState(actor, S_PAIN_DIE6);
|
2007-01-16 04:43:42 +00:00
|
|
|
}
|
|
|
|
|
}
|
2021-06-10 16:08:47 +02:00
|
|
|
}
|
2025-01-16 01:41:27 -05:00
|
|
|
// killough 3/22/98: make more intelligent about plural
|
|
|
|
|
// Ty 03/27/98 - string(s) *not* externalized
|
2025-02-08 15:07:18 -06:00
|
|
|
msg = fmt::format("{} Monster{} Killed", killcount, killcount == 1 ? "" : "s");
|
2025-01-16 01:41:27 -05:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CHT_MDK: {
|
2026-01-29 17:11:41 -06:00
|
|
|
if (multiplayer && !player.client.allow_rcon)
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.spectator)
|
2025-01-16 01:41:27 -05:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Never enable that in PvP, are you crazy?
|
|
|
|
|
if (!G_IsCoopGame())
|
|
|
|
|
return;
|
2021-06-10 16:08:47 +02:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
if (serverside)
|
2021-06-02 00:41:02 +02:00
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
P_LineAttack(player.mo, player.mo->angle, 8192 * FRACUNIT,
|
|
|
|
|
P_AimLineAttack(player.mo, player.mo->angle, 8192 * FRACUNIT),
|
2025-01-16 01:41:27 -05:00
|
|
|
10000);
|
|
|
|
|
|
|
|
|
|
if (multiplayer)
|
|
|
|
|
msg = "MDK";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case CHT_BUDDHA: {
|
2026-01-29 17:11:41 -06:00
|
|
|
player.cheats ^= CF_BUDDHA;
|
|
|
|
|
msg = (player.cheats & CF_BUDDHA) ? GStrings(TXT_BUDDHAON)
|
2025-01-16 01:41:27 -05:00
|
|
|
: GStrings(TXT_BUDDHAOFF);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2007-01-16 04:43:42 +00:00
|
|
|
}
|
2021-06-01 19:17:21 +02:00
|
|
|
|
|
|
|
|
if (!silentmsg)
|
|
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
if (&player == &consoleplayer())
|
2021-06-10 16:08:47 +02:00
|
|
|
{
|
2025-02-08 15:07:18 -06:00
|
|
|
PrintFmt("{}\n", msg);
|
2021-06-10 16:08:47 +02:00
|
|
|
}
|
2025-01-13 03:34:38 -06:00
|
|
|
|
2021-06-01 19:17:21 +02:00
|
|
|
#ifdef SERVER_APP
|
2026-01-29 17:11:41 -06:00
|
|
|
SV_BroadcastPrintFmtButPlayer(PRINT_HIGH, player.id, "{} is a cheater: {}\n",
|
|
|
|
|
player.userinfo.netname, msg);
|
2025-01-16 01:41:27 -05:00
|
|
|
#endif
|
2021-06-01 19:17:21 +02:00
|
|
|
}
|
2007-01-16 04:43:42 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
bool ValidSummonActor(const std::string& summon) {
|
2025-01-16 01:41:27 -05:00
|
|
|
std::string mobname = "";
|
|
|
|
|
|
2025-01-16 02:21:16 -05:00
|
|
|
mobjtype_t mobjtype = P_INameToMobj(summon);
|
2025-01-16 01:41:27 -05:00
|
|
|
|
|
|
|
|
if (mobjtype == MT_NULL)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
AActor* Summon(player_t& player, const std::string& sum, bool friendly)
|
2025-01-16 01:41:27 -05:00
|
|
|
{
|
|
|
|
|
AActor* entity = AActor::AActorPtr();
|
2026-01-29 17:11:41 -06:00
|
|
|
AActor* source = player.mo;
|
2025-01-16 01:41:27 -05:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.spectator || source == nullptr)
|
2025-01-16 01:41:27 -05:00
|
|
|
return entity;
|
|
|
|
|
|
|
|
|
|
if (serverside)
|
|
|
|
|
{
|
|
|
|
|
// First, find whatever the heck we summoned.
|
2025-01-16 02:21:16 -05:00
|
|
|
mobjtype_t mobjtype = P_INameToMobj(sum);
|
2025-01-16 01:41:27 -05:00
|
|
|
|
|
|
|
|
if (mobjtype == MT_NULL)
|
|
|
|
|
{
|
2025-01-16 22:13:59 -05:00
|
|
|
PrintFmt(PRINT_HIGH, "{} tried to cheat but can't even summon right\n",
|
2026-01-29 17:11:41 -06:00
|
|
|
player.userinfo.netname);
|
2025-01-16 01:41:27 -05:00
|
|
|
return entity;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-14 15:38:48 -05:00
|
|
|
fixed_t x = source->x + FixedMul(mobjinfo[mobjtype].radius * 2 + source->radius,
|
2025-01-16 01:41:27 -05:00
|
|
|
finecosine[source->angle >> ANGLETOFINESHIFT]);
|
2025-07-14 15:38:48 -05:00
|
|
|
fixed_t y = source->y + FixedMul(mobjinfo[mobjtype].radius * 2 + source->radius,
|
2025-01-16 01:41:27 -05:00
|
|
|
finesine[source->angle >> ANGLETOFINESHIFT]);
|
|
|
|
|
fixed_t z = source->z + 8 * FRACUNIT;
|
|
|
|
|
|
2025-07-14 15:38:48 -05:00
|
|
|
if (mobjinfo[mobjtype].flags & MF_MISSILE)
|
2025-01-16 01:55:33 -05:00
|
|
|
{
|
|
|
|
|
entity = P_SpawnPlayerMissile(source, mobjtype);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
entity = new AActor(x, y, z, mobjtype);
|
2025-01-16 01:41:27 -05:00
|
|
|
|
2025-01-16 01:55:33 -05:00
|
|
|
entity->angle = source->angle;
|
|
|
|
|
}
|
2025-01-16 01:41:27 -05:00
|
|
|
}
|
|
|
|
|
|
2025-01-29 22:25:36 -05:00
|
|
|
std::string cheatname = "summon";
|
|
|
|
|
|
|
|
|
|
if (friendly)
|
|
|
|
|
{
|
|
|
|
|
entity->flags |= MF_FRIEND;
|
|
|
|
|
cheatname = "summonfriend";
|
2026-01-29 17:11:41 -06:00
|
|
|
P_GiveFriendlyOwnerInfo(entity, player.mo);
|
2025-12-04 10:06:04 -06:00
|
|
|
P_FriendlyEffects(entity);
|
2025-01-29 22:25:36 -05:00
|
|
|
}
|
|
|
|
|
|
2025-01-16 22:13:59 -05:00
|
|
|
if (multiplayer)
|
2025-01-29 22:25:36 -05:00
|
|
|
PrintFmt(PRINT_HIGH, "{} is a cheater: {} {}\n",
|
2026-01-29 17:11:41 -06:00
|
|
|
player.userinfo.netname,
|
2025-01-29 22:25:36 -05:00
|
|
|
cheatname,
|
|
|
|
|
sum);
|
2025-01-16 22:13:59 -05:00
|
|
|
|
|
|
|
|
return entity;
|
2025-01-16 01:41:27 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
void GiveTo(player_t& player, const char* name)
|
2007-01-16 04:43:42 +00:00
|
|
|
{
|
2025-01-25 20:54:51 -06:00
|
|
|
bool giveall;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
if (&player != &consoleplayer())
|
|
|
|
|
PrintFmt(PRINT_HIGH, "{} is a cheater: give {}\n", player.userinfo.netname,
|
2025-01-16 01:41:27 -05:00
|
|
|
name);
|
2007-01-16 04:43:42 +00:00
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
if (stricmp(name, "all") == 0)
|
2007-01-16 04:43:42 +00:00
|
|
|
giveall = true;
|
|
|
|
|
else
|
|
|
|
|
giveall = false;
|
|
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
if (giveall || strnicmp(name, "health", 6) == 0)
|
|
|
|
|
{
|
2007-01-16 04:43:42 +00:00
|
|
|
int h;
|
|
|
|
|
|
2026-06-29 15:15:52 -05:00
|
|
|
if (0 < (h = ParseNum<int>(name + 6).value_or(0)))
|
2025-01-16 01:41:27 -05:00
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.mo)
|
2025-01-16 01:41:27 -05:00
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
player.mo->health += h;
|
|
|
|
|
player.health = player.mo->health;
|
2025-01-16 01:41:27 -05:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
player.health += h;
|
2007-01-16 04:43:42 +00:00
|
|
|
}
|
2025-01-16 01:41:27 -05:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
if (player.mo)
|
|
|
|
|
player.mo->health = deh.GodHealth;
|
2025-01-13 03:34:38 -06:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
player.health = deh.GodHealth;
|
2007-01-16 04:43:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!giveall)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
if (giveall || stricmp(name, "backpack") == 0)
|
|
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
if (!player.backpack)
|
2025-01-16 01:41:27 -05:00
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
for (int i = 0; i < NUMAMMO; i++)
|
|
|
|
|
player.maxammo[i] *= 2;
|
|
|
|
|
player.backpack = true;
|
2007-01-16 04:43:42 +00:00
|
|
|
}
|
2026-01-29 17:11:41 -06:00
|
|
|
for (int i = 0; i < NUMAMMO; i++)
|
2025-01-16 01:41:27 -05:00
|
|
|
P_GiveAmmo(player, (ammotype_t)i, 1);
|
2007-01-16 04:43:42 +00:00
|
|
|
|
|
|
|
|
if (!giveall)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
if (giveall || stricmp(name, "weapons") == 0)
|
|
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
weapontype_t pendweap = player.pendingweapon;
|
|
|
|
|
for (int i = 0; i < NUMWEAPONS; i++)
|
2025-01-16 01:41:27 -05:00
|
|
|
P_GiveWeapon(player, (weapontype_t)i, false);
|
2026-01-29 17:11:41 -06:00
|
|
|
player.pendingweapon = pendweap;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
|
|
|
|
if (!giveall)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
if (giveall || stricmp(name, "ammo") == 0)
|
|
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
for (int i = 0; i < NUMAMMO; i++)
|
|
|
|
|
player.ammo[i] = player.maxammo[i];
|
2007-01-16 04:43:42 +00:00
|
|
|
|
|
|
|
|
if (!giveall)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
if (giveall || stricmp(name, "armor") == 0)
|
|
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
player.armorpoints = 200;
|
|
|
|
|
player.armortype = 2;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
|
|
|
|
if (!giveall)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
if (giveall || stricmp(name, "keys") == 0)
|
|
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
for (int i = 0; i < NUMCARDS; i++)
|
|
|
|
|
player.cards[i] = true;
|
2007-01-16 04:43:42 +00:00
|
|
|
|
|
|
|
|
if (!giveall)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (giveall)
|
|
|
|
|
return;
|
|
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
gitem_t* it = FindItem(name);
|
2025-01-16 01:41:27 -05:00
|
|
|
if (!it)
|
|
|
|
|
{
|
|
|
|
|
it = FindItemByClassname(name);
|
|
|
|
|
if (!it)
|
|
|
|
|
{
|
2026-01-29 17:11:41 -06:00
|
|
|
if (&player == &consoleplayer())
|
2025-08-21 06:08:01 -05:00
|
|
|
PrintFmt(PRINT_HIGH, "Unknown item\n");
|
2007-01-16 04:43:42 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
if (it->flags & IT_AMMO)
|
|
|
|
|
{
|
2007-01-16 04:43:42 +00:00
|
|
|
int howmuch;
|
|
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
/* if (argc == 3)
|
|
|
|
|
howmuch = atoi (argv[2]);
|
|
|
|
|
else */
|
|
|
|
|
howmuch = it->quantity;
|
|
|
|
|
|
|
|
|
|
P_GiveAmmo(player, (ammotype_t)it->offset, howmuch);
|
|
|
|
|
}
|
|
|
|
|
else if (it->flags & IT_WEAPON)
|
|
|
|
|
{
|
|
|
|
|
P_GiveWeapon(player, (weapontype_t)it->offset, 0);
|
|
|
|
|
}
|
|
|
|
|
else if (it->flags & IT_KEY)
|
|
|
|
|
{
|
|
|
|
|
P_GiveCard(player, (card_t)it->offset);
|
|
|
|
|
}
|
|
|
|
|
else if (it->flags & IT_POWERUP)
|
|
|
|
|
{
|
|
|
|
|
P_GivePower(player, it->offset);
|
|
|
|
|
}
|
|
|
|
|
else if (it->flags & IT_ARMOR)
|
|
|
|
|
{
|
|
|
|
|
P_GiveArmor(player, it->offset);
|
2007-01-16 04:43:42 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-29 01:22:55 +02:00
|
|
|
// Heretic cheat code (unused!)
|
|
|
|
|
#if 0
|
2026-01-29 17:11:41 -06:00
|
|
|
void Suicide(player_t& plyr)
|
2007-01-16 04:43:42 +00:00
|
|
|
{
|
|
|
|
|
plyr->mo->flags |= MF_SHOOTABLE;
|
|
|
|
|
while (plyr->health > 0)
|
|
|
|
|
P_DamageMobj (plyr->mo, plyr->mo, plyr->mo, 10000, MOD_SUICIDE);
|
|
|
|
|
plyr->mo->flags &= ~MF_SHOOTABLE;
|
|
|
|
|
}
|
2021-04-29 01:22:55 +02:00
|
|
|
#endif
|
2007-01-16 19:13:59 +00:00
|
|
|
|
2026-01-29 17:11:41 -06:00
|
|
|
} // namespace cheat
|
|
|
|
|
|
2025-01-16 01:41:27 -05:00
|
|
|
VERSION_CONTROL(m_cheat_cpp, "$Id$")
|