og-odamex/common/m_cheat.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

723 lines
14 KiB
C++
Raw Permalink Normal View History

// Emacs style mode select -*- C++ -*-
2007-01-16 04:43:42 +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.
//
//-----------------------------------------------------------------------------
#include "odamex.h"
2007-01-16 04:43:42 +00:00
#include <math.h>
#include "g_gametype.h"
2007-01-16 04:43:42 +00:00
#include "d_player.h"
#include "m_cheat.h"
#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"
#include "c_effect.h"
#include "c_dispatch.h"
2007-01-16 04:43:42 +00:00
extern bool simulated_connection;
EXTERN_CVAR(sv_allowcheats)
#ifdef CLIENT_APP
2021-06-06 20:10:27 +02:00
#include "am_map.h"
#include "cl_main.h"
extern bool automapactive;
#endif
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
//
#ifdef CLIENT_APP
2021-06-06 20:10:27 +02:00
namespace cheat
{
//-------------
// THESE ARE MAINLY FOR THE CLIENT
// Smashing Pumpkins Into Small Piles Of Putrid Debris.
bool AutoMap(cheatseq_t* cheat)
{
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;
return true;
}
return false;
}
bool ChangeLevel(cheatseq_t* cheat)
{
std::string buf;
// What were you trying to achieve?
if (multiplayer)
return false;
// [ML] Chex mode: always set the episode number to 1.
// FIXME: This is probably a horrible hack, it sure looks like one at least
// And why is there only a newline for non-chex?
if (gamemode == retail_chex)
2025-08-17 20:38:50 -05:00
buf = fmt::format("map 1{:c}", cheat->Args[1]);
else
2025-08-17 20:35:41 -05:00
buf = fmt::format("map {:c}{:c}\n", cheat->Args[0], cheat->Args[1]);
AddCommandString(buf);
return true;
}
bool IdMyPos(cheatseq_t* cheat)
{
C_DoCommand("toggle idmypos", 0);
return true;
}
bool BeholdMenu(cheatseq_t* cheat)
{
PrintFmt(PRINT_HIGH, "{}\n", GStrings(STSTR_BEHOLD));
return false;
}
bool ChangeMusic(cheatseq_t* cheat)
{
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
//
bool SetGeneric(cheatseq_t* cheat)
{
if (!AreCheatsEnabled())
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;
}
DoCheat(consoleplayer(), (ECheatFlags)cheat->Args[0]);
CL_SendCheat((ECheatFlags)cheat->Args[0]);
return true;
}
// [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.
bool AddKey(cheatseq_t* cheat, unsigned char key, bool* eat)
{
if (cheat->Pos == nullptr)
2007-01-16 04:43:42 +00: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
{
cheat->Pos = cheat->Sequence;
cheat->CurrentArg = 0;
2007-01-16 04:43:42 +00:00
}
if (*cheat->Pos == 0xff)
2007-01-16 04:43:42 +00:00
{
cheat->Pos = cheat->Sequence;
cheat->CurrentArg = 0;
return true;
2007-01-16 04:43:42 +00:00
}
return false;
2007-01-16 04:43:42 +00:00
}
} // namespace cheat
BEGIN_COMMAND(tntem)
{
if (!cheat::AreCheatsEnabled())
return;
2021-06-10 16:42:34 +02:00
if (multiplayer && !G_IsCoopGame())
return;
cheat::DoCheat(consoleplayer(), CHT_MASSACRE);
CL_SendCheat(CHT_MASSACRE);
}
END_COMMAND(tntem)
2025-01-16 22:13:59 -05:00
BEGIN_COMMAND(summon)
2025-01-16 01:41:27 -05: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));
if (!cheat::ValidSummonActor(mobname))
2025-01-16 01:41:27 -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;
}
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)
{
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));
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;
}
cheat::Summon(consoleplayer(), mobname.c_str(), true);
2025-01-29 22:25:36 -05:00
CL_SendSummonFriendCheat(mobname.c_str());
}
END_COMMAND(summonfriend)
BEGIN_COMMAND(mdk)
{
if (!cheat::AreCheatsEnabled())
return;
2021-06-10 16:42:34 +02:00
if (multiplayer && !G_IsCoopGame())
return;
cheat::DoCheat(consoleplayer(), CHT_MDK);
CL_SendCheat(CHT_MDK);
}
END_COMMAND(mdk)
#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
void A_PainDie(AActor*);
namespace cheat
{
// Checks if all the conditions to enable cheats are met.
bool AreCheatsEnabled()
{
// [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;
// [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)
{
if (!sv_allowcheats)
{
PrintFmt(PRINT_WARNING,
"You must 'set sv_allowcheats 1' in the console to enable "
"this command on this difficulty.\n");
return false;
}
}
if ((multiplayer || !G_IsCoopGame()) && !sv_allowcheats)
{
PrintFmt(PRINT_WARNING, "You must run the server with '+set sv_allowcheats 1' to "
"enable this command.\n");
return false;
}
return true;
}
2007-01-16 04:43:42 +00:00
void DoCheat(player_t& player, int cheat, bool silentmsg)
2007-01-16 04:43:42 +00:00
{
std::string msg;
2007-01-16 04:43:42 +00:00
if (player.health <= 0)
return;
2025-01-16 01:41:27 -05:00
switch (cheat)
{
case CHT_IDDQD:
if (player.spectator)
2025-01-16 01:41:27 -05:00
return;
2007-01-16 04:43:42 +00:00
if (!(player.cheats & CF_GODMODE))
2025-01-16 01:41:27 -05:00
{
if (player.mo)
player.mo->health = deh.GodHealth;
player.health = deh.GodHealth;
2025-01-16 01:41:27 -05:00
}
[[fallthrough]];
case CHT_GOD:
if (player.spectator)
2025-01-16 01:41:27 -05:00
return;
2007-01-16 04:43:42 +00: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;
2025-01-16 01:41:27 -05:00
case CHT_NOCLIP:
if (player.spectator)
2025-01-16 01:41:27 -05:00
silentmsg = true;
2007-01-16 04:43:42 +00: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:
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;
2025-01-16 01:41:27 -05:00
case CHT_NOTARGET:
if (player.spectator)
2025-01-16 01:41:27 -05:00
return;
2007-01-16 04:43:42 +00:00
player.cheats ^= CF_NOTARGET;
msg = (player.cheats & CF_NOTARGET) ? "notarget ON" : "notarget OFF";
2025-01-16 01:41:27 -05:00
break;
2025-01-16 01:41:27 -05:00
case CHT_CHASECAM:
if (player.spectator)
2025-01-16 01:41:27 -05:00
return;
2007-01-16 04:43:42 +00:00
player.cheats ^= CF_CHASECAM;
msg = (player.cheats & CF_CHASECAM) ? "chasecam ON" : "chasecam OFF";
2025-01-16 01:41:27 -05:00
break;
2025-01-16 01:41:27 -05:00
case CHT_CHAINSAW:
if (player.spectator)
2025-01-16 01:41:27 -05:00
return;
2007-01-16 04:43:42 +00:00
player.weaponowned[wp_chainsaw] = true;
player.powers[pw_invulnerability] = true;
2025-01-16 01:41:27 -05:00
msg = GStrings(STSTR_CHOPPERS);
break;
2025-01-16 01:41:27 -05:00
case CHT_IDKFA:
if (player.spectator)
2025-01-16 01:41:27 -05:00
return;
2007-01-16 04:43:42 +00: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;
2025-01-16 01:41:27 -05:00
case CHT_IDFA:
if (player.spectator)
2025-01-16 01:41:27 -05:00
return;
2007-01-16 04:43:42 +00: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: {
if (player.spectator)
2025-01-16 01:41:27 -05:00
return;
2025-01-16 01:41:27 -05:00
int i = cheat - CHT_BEHOLDV;
2007-01-16 04:43:42 +00:00
if (!player.powers[i])
2025-01-16 01:41:27 -05:00
P_GivePower(player, i);
else if (i != pw_strength)
player.powers[i] = 1;
2025-01-16 01:41:27 -05:00
else
player.powers[i] = 0;
2025-01-16 01:41:27 -05:00
}
msg = GStrings(STSTR_BEHOLDX);
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
if (multiplayer && !player.client.allow_rcon)
2025-01-16 01:41:27 -05:00
return;
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++;
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
{
::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
}
}
}
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
msg = fmt::format("{} Monster{} Killed", killcount, killcount == 1 ? "" : "s");
2025-01-16 01:41:27 -05:00
}
break;
case CHT_MDK: {
if (multiplayer && !player.client.allow_rcon)
2025-01-16 01:41:27 -05:00
return;
if (player.spectator)
2025-01-16 01:41:27 -05:00
return;
// Never enable that in PvP, are you crazy?
if (!G_IsCoopGame())
return;
2025-01-16 01:41:27 -05:00
if (serverside)
2021-06-02 00:41:02 +02: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: {
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)
{
if (&player == &consoleplayer())
{
PrintFmt("{}\n", msg);
}
2021-06-01 19:17:21 +02:00
#ifdef SERVER_APP
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
}
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;
}
AActor* Summon(player_t& player, const std::string& sum, bool friendly)
2025-01-16 01:41:27 -05:00
{
AActor* entity = AActor::AActorPtr();
AActor* source = player.mo;
2025-01-16 01:41:27 -05: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",
player.userinfo.netname);
2025-01-16 01:41:27 -05:00
return entity;
}
fixed_t x = source->x + FixedMul(mobjinfo[mobjtype].radius * 2 + source->radius,
2025-01-16 01:41:27 -05:00
finecosine[source->angle >> ANGLETOFINESHIFT]);
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;
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";
P_GiveFriendlyOwnerInfo(entity, player.mo);
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",
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
}
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
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;
if (0 < (h = ParseNum<int>(name + 6).value_or(0)))
2025-01-16 01:41:27 -05:00
{
if (player.mo)
2025-01-16 01:41:27 -05:00
{
player.mo->health += h;
player.health = player.mo->health;
2025-01-16 01:41:27 -05:00
}
else
{
player.health += h;
2007-01-16 04:43:42 +00:00
}
2025-01-16 01:41:27 -05:00
}
else
{
if (player.mo)
player.mo->health = deh.GodHealth;
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)
{
if (!player.backpack)
2025-01-16 01:41:27 -05:00
{
for (int i = 0; i < NUMAMMO; i++)
player.maxammo[i] *= 2;
player.backpack = true;
2007-01-16 04:43:42 +00: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)
{
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);
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)
{
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)
{
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)
{
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;
gitem_t* it = FindItem(name);
2025-01-16 01:41:27 -05:00
if (!it)
{
it = FindItemByClassname(name);
if (!it)
{
if (&player == &consoleplayer())
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
}
}
// Heretic cheat code (unused!)
#if 0
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;
}
#endif
} // namespace cheat
2025-01-16 01:41:27 -05:00
VERSION_CONTROL(m_cheat_cpp, "$Id$")