1 /** @file m_cheat.c  Cheat code sequences.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2005-2014 Daniel Swanson <danij@dengine.net>
5  * @authors Copyright © 1993-1996 by id Software, Inc.
6  *
7  * @par License
8  * GPL: http://www.gnu.org/licenses/gpl.html
9  *
10  * <small>This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by the
12  * Free Software Foundation; either version 2 of the License, or (at your
13  * option) any later version. This program is distributed in the hope that it
14  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16  * Public License for more details. You should have received a copy of the GNU
17  * General Public License along with this program; if not, write to the Free
18  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301 USA</small>
20  */
21 
22 #include "m_cheat.h"
23 
24 #include <de/Log>
25 #include <de/Range>
26 #include <de/String>
27 #include <de/Vector>
28 
29 #include "jdoom.h"
30 #include "d_net.h"
31 #include "d_netcl.h"
32 #include "d_netsv.h"
33 #include "dmu_lib.h"
34 #include "g_eventsequence.h"
35 #include "g_defs.h"
36 #include "gamesession.h"
37 #include "hu_msg.h"
38 #include "p_user.h"
39 #include "p_sound.h"
40 #include "player.h"
41 
42 using namespace de;
43 
44 typedef eventsequencehandler_t cheatfunc_t;
45 
46 /// Helper macro for forming cheat callback function names.
47 #define CHEAT(x) G_Cheat##x
48 
49 /// Helper macro for declaring cheat callback functions.
50 #define CHEAT_FUNC(x) int G_Cheat##x(int player, EventSequenceArg const *args, int numArgs)
51 
CHEAT_FUNC(Music)52 CHEAT_FUNC(Music)
53 {
54     DENG2_UNUSED(numArgs);
55 
56     if(player < 0 || player >= MAXPLAYERS)
57         return false;
58 
59     player_t *plr = &players[player];
60 
61     int const numEpisodes = PlayableEpisodeCount();
62     if(!numEpisodes) return false;
63 
64     // The number of episodes determines how to interpret the arguments.
65     /// @note Logic here aims to be somewhat vanilla compatible, yet offer
66     /// a limited degree of support for custom episodes. The "playmusic"
67     /// cmd is a far more flexible method of changing music.
68     String episodeId;
69     int warpNumber;
70     if(numEpisodes > 1)
71     {
72         episodeId  = String::number(args[0] - '0');
73         warpNumber = args[1] - '0';
74     }
75     else
76     {
77         episodeId  = FirstPlayableEpisodeId();
78         warpNumber = (args[0] - '0') * 10 + (args[1] - '0');
79     }
80 
81     // Lookup and try to enqueue the Music for the referenced episode and map.
82     const auto mapUri = TranslateMapWarpNumber(episodeId, warpNumber);
83     if (S_MapMusic(mapUri))
84     {
85         P_SetMessageWithFlags(plr, STSTR_MUS, LMF_NO_HIDE);
86         return true;
87     }
88 
89     P_SetMessageWithFlags(plr, STSTR_NOMUS, LMF_NO_HIDE);
90     return false;
91 }
92 
CHEAT_FUNC(Reveal)93 CHEAT_FUNC(Reveal)
94 {
95     DENG2_UNUSED2(args, numArgs);
96 
97     if(IS_NETGAME && gfw_Rule(deathmatch))
98         return false;
99 
100     if(player < 0 || player >= MAXPLAYERS)
101         return false;
102 
103     player_t *plr = &players[player];
104 
105     // Dead players can't cheat.
106     if(plr->health <= 0) return false;
107 
108     if(ST_AutomapIsOpen(player))
109     {
110         ST_CycleAutomapCheatLevel(player);
111     }
112     return true;
113 }
114 
CHEAT_FUNC(Powerup)115 CHEAT_FUNC(Powerup)
116 {
117     DENG2_UNUSED2(args, numArgs);
118     if(player < 0 || player >= MAXPLAYERS)
119         return false;
120 
121     P_SetMessageWithFlags(&players[player], STSTR_BEHOLD, LMF_NO_HIDE);
122     return true;
123 }
124 
CHEAT_FUNC(Powerup2)125 CHEAT_FUNC(Powerup2)
126 {
127     DENG2_UNUSED(numArgs);
128     if(player < 0 || player >= MAXPLAYERS)
129         return false;
130 
131     struct mnemonic_pair_s {
132         char vanilla;
133         char give;
134     } static const mnemonics[] =
135     {
136         /*PT_INVULNERABILITY*/  { 'v', 'i' },
137         /*PT_STRENGTH*/         { 's', 'b' },
138         /*PT_INVISIBILITY*/     { 'i', 'v' },
139         /*PT_IRONFEET*/         { 'r', 's' },
140         /*PT_ALLMAP*/           { 'a', 'm' },
141         /*PT_INFRARED*/         { 'l', 'g' }
142     };
143     static int const numMnemonics = int(sizeof(mnemonics) / sizeof(mnemonics[0]));
144 
145     for(int i = 0; i < numMnemonics; ++i)
146     {
147         if(args[0] == mnemonics[i].vanilla)
148         {
149             DD_Executef(true, "give %c %i", mnemonics[i].give, player);
150             return true;
151         }
152     }
153     return false;
154 }
155 
CHEAT_FUNC(MyPos)156 CHEAT_FUNC(MyPos)
157 {
158     DENG2_UNUSED2(args, numArgs);
159     if(player < 0 || player >= MAXPLAYERS)
160         return false;
161 
162     mobj_t const *mob = players[CONSOLEPLAYER].plr->mo;
163     String const text = String("angle:0x%1 position:%2")
164                             .arg(mob->angle, 0, 16)
165                             .arg(Vector3d(mob->origin).asText());
166     P_SetMessageWithFlags(&players[player], text.toUtf8().constData(), LMF_NO_HIDE);
167     return true;
168 }
169 
170 /**
171  * The multipurpose cheat ccmd.
172  */
D_CMD(Cheat)173 D_CMD(Cheat)
174 {
175     DENG2_UNUSED2(src, argc);
176 
177     // Give each of the characters in argument two to the ST event handler.
178     int const len = qstrlen(argv[1]);
179     for(int i = 0; i < len; ++i)
180     {
181         event_t ev; de::zap(ev);
182         ev.type  = EV_KEY;
183         ev.state = EVS_DOWN;
184         ev.data1 = argv[1][i];
185         ev.data2 = ev.data3 = 0;
186         G_EventSequenceResponder(&ev);
187     }
188     return true;
189 }
190 
D_CMD(CheatGod)191 D_CMD(CheatGod)
192 {
193     DENG2_UNUSED(src);
194 
195     if(G_GameState() == GS_MAP)
196     {
197         if(IS_CLIENT)
198         {
199             NetCl_CheatRequest("god");
200         }
201         else if((IS_NETGAME && !netSvAllowCheats) ||
202                 gfw_Rule(skill) == SM_NIGHTMARE)
203         {
204             return false;
205         }
206         else
207         {
208             int player = CONSOLEPLAYER;
209             if(argc == 2)
210             {
211                 player = String(argv[1]).toInt();
212                 if(player < 0 || player >= MAXPLAYERS) return false;
213             }
214 
215             player_t *plr = &players[player];
216             if(!plr->plr->inGame) return false;
217 
218             // Dead players can't cheat.
219             if(plr->health <= 0) return false;
220 
221             plr->cheats ^= CF_GODMODE;
222             plr->update |= PSF_STATE;
223 
224             if(P_GetPlayerCheats(plr) & CF_GODMODE)
225             {
226                 if(plr->plr->mo)
227                     plr->plr->mo->health = maxHealth;
228                 plr->health = godModeHealth;
229                 plr->update |= PSF_HEALTH;
230             }
231 
232             P_SetMessageWithFlags(plr, ((P_GetPlayerCheats(plr) & CF_GODMODE) ? STSTR_DQDON : STSTR_DQDOFF), LMF_NO_HIDE);
233         }
234     }
235     return true;
236 }
237 
D_CMD(CheatNoClip)238 D_CMD(CheatNoClip)
239 {
240     DENG2_UNUSED(src);
241 
242     if(G_GameState() == GS_MAP)
243     {
244         if(IS_CLIENT)
245         {
246             NetCl_CheatRequest("noclip");
247         }
248         else if((IS_NETGAME && !netSvAllowCheats) ||
249                 gfw_Rule(skill) == SM_NIGHTMARE)
250         {
251             return false;
252         }
253         else
254         {
255             int player = CONSOLEPLAYER;
256             if(argc == 2)
257             {
258                 player = String(argv[1]).toInt();
259                 if(player < 0 || player >= MAXPLAYERS) return false;
260             }
261 
262             player_t *plr = &players[player];
263             if(!plr->plr->inGame) return false;
264 
265             // Dead players can't cheat.
266             if(plr->health <= 0) return false;
267 
268             plr->cheats ^= CF_NOCLIP;
269             plr->update |= PSF_STATE;
270             P_SetMessageWithFlags(plr, ((P_GetPlayerCheats(plr) & CF_NOCLIP) ? STSTR_NCON : STSTR_NCOFF), LMF_NO_HIDE);
271         }
272     }
273     return true;
274 }
275 
suicideResponse(msgresponse_t response,int,void *)276 static int suicideResponse(msgresponse_t response, int /*userValue*/, void * /*context*/)
277 {
278     if(response == MSG_YES)
279     {
280         if(IS_NETGAME && IS_CLIENT)
281         {
282             NetCl_CheatRequest("suicide");
283         }
284         else
285         {
286             player_t *plr = &players[CONSOLEPLAYER];
287             P_DamageMobj(plr->plr->mo, nullptr, nullptr, 10000, false);
288         }
289     }
290     return true;
291 }
292 
D_CMD(CheatSuicide)293 D_CMD(CheatSuicide)
294 {
295     DENG2_UNUSED(src);
296 
297     if(G_GameState() == GS_MAP)
298     {
299         int player = CONSOLEPLAYER;
300         if(!IS_CLIENT || argc == 2)
301         {
302             player = String(argv[1]).toInt();
303             if(player < 0 || player >= MAXPLAYERS) return false;
304         }
305 
306         player_t *plr = &players[player];
307         if(!plr->plr->inGame) return false;
308         if(plr->playerState == PST_DEAD) return false;
309 
310         if(!IS_NETGAME || IS_CLIENT)
311         {
312             Hu_MsgStart(MSG_YESNO, SUICIDEASK, suicideResponse, 0, nullptr);
313         }
314         else
315         {
316             P_DamageMobj(plr->plr->mo, nullptr, nullptr, 10000, false);
317         }
318         return true;
319     }
320 
321     Hu_MsgStart(MSG_ANYKEY, SUICIDEOUTMAP, nullptr, 0, nullptr);
322     return true;
323 }
324 
D_CMD(CheatReveal)325 D_CMD(CheatReveal)
326 {
327     DENG2_UNUSED2(src, argc);
328     // Server operator can always reveal.
329     if(IS_NETGAME && !IS_NETWORK_SERVER)
330         return false;
331 
332     int const option = String(argv[1]).toInt();
333     if(option < 0 || option > 3) return false;
334 
335     for(int i = 0; i < MAXPLAYERS; ++i)
336     {
337         ST_SetAutomapCheatLevel(i, 0);
338         ST_RevealAutomap(i, false);
339         if(option == 1)
340         {
341             ST_RevealAutomap(i, true);
342         }
343         else if(option != 0)
344         {
345             ST_SetAutomapCheatLevel(i, option - 1);
346         }
347     }
348 
349     return true;
350 }
351 
giveWeapon(player_t * player,weapontype_t weaponType)352 static void giveWeapon(player_t *player, weapontype_t weaponType)
353 {
354     P_GiveWeapon(player, weaponType, false/*not dropped*/);
355     if(weaponType == WT_EIGHTH)
356     {
357         P_SetMessageWithFlags(player, STSTR_CHOPPERS, LMF_NO_HIDE);
358     }
359 }
360 
togglePower(player_t * player,powertype_t powerType)361 static void togglePower(player_t *player, powertype_t powerType)
362 {
363     P_TogglePower(player, powerType);
364     P_SetMessageWithFlags(player, STSTR_BEHOLDX, LMF_NO_HIDE);
365 }
366 
D_CMD(CheatGive)367 D_CMD(CheatGive)
368 {
369     DENG2_UNUSED(src);
370 
371     if(G_GameState() != GS_MAP)
372     {
373         LOG_SCR_ERROR("Can only \"give\" when in a game!");
374         return true;
375     }
376 
377     if(argc != 2 && argc != 3)
378     {
379         LOG_SCR_NOTE("Usage:\n  give (stuff)\n  give (stuff) (player number)");
380 
381 #define TABBED(A, B) "\n" _E(Ta) _E(b) "  " << A << " " _E(.) _E(Tb) << B
382         LOG_SCR_MSG("Where (stuff) is one or more type:id codes"
383                     " (if no id, give all of that type):")
384                 << TABBED("a", "Ammo")
385                 << TABBED("b", "Berserk")
386                 << TABBED("f", "Flight ability")
387                 << TABBED("g", "Light amplification visor")
388                 << TABBED("h", "Health")
389                 << TABBED("i", "Invulnerability")
390                 << TABBED("k", "Keys")
391                 << TABBED("m", "Computer area map")
392                 << TABBED("p", "Backpack full of ammo")
393                 << TABBED("r", "Armor")
394                 << TABBED("s", "Radiation shielding suit")
395                 << TABBED("v", "Invisibility")
396                 << TABBED("w", "Weapons");
397 #undef TABBED
398 
399         LOG_SCR_MSG(_E(D) "Examples:");
400         LOG_SCR_MSG("  " _E(>) "Enter " _E(b) "give arw"  _E(.) " for full ammo and armor " _E(l) "(equivalent to cheat IDFA)");
401         LOG_SCR_MSG("  " _E(>) "Enter " _E(b) "give w2k1" _E(.) " for weapon two and key one");
402         return true;
403     }
404 
405     int player = CONSOLEPLAYER;
406     if(argc == 3)
407     {
408         player = String(argv[2]).toInt();
409         if(player < 0 || player >= MAXPLAYERS) return false;
410     }
411 
412     if(IS_CLIENT)
413     {
414         if(argc < 2) return false;
415 
416         String const request = String("give ") + argv[1];
417         NetCl_CheatRequest(request.toUtf8().constData());
418         return true;
419     }
420 
421     if(IS_NETGAME && !netSvAllowCheats) return false;
422     if(gfw_Rule(skill) == SM_NIGHTMARE) return false;
423 
424     player_t *plr = &players[player];
425 
426     // Can't give to a player who's not in the game.
427     if(!plr->plr->inGame) return false;
428     // Can't give to a dead player.
429     if(plr->health <= 0) return false;
430 
431     String const stuff = String(argv[1]).toLower(); // Stuff is the 2nd arg.
432     for(int i = 0; i < stuff.length(); ++i)
433     {
434         QChar const mnemonic = stuff.at(i);
435         switch(mnemonic.toLatin1())
436         {
437         // Ammo:
438         case 'a': {
439             ammotype_t ammos = NUM_AMMO_TYPES; // All types.
440 
441             // Give one specific ammo type?
442             if((i + 1) < stuff.length() && stuff.at(i + 1).isDigit())
443             {
444                 int const arg = stuff.at( ++i ).digitValue();
445                 if(arg < AT_FIRST || arg >= NUM_AMMO_TYPES)
446                 {
447                     LOG_SCR_ERROR("Ammo #%d unknown. Valid range %s")
448                             << arg << Rangei(AT_FIRST, NUM_AMMO_TYPES).asText();
449                     break;
450                 }
451                 ammos = ammotype_t(arg);
452             }
453             P_GiveAmmo(plr, ammos, -1 /*max rounds*/);
454             break; }
455 
456         // Armor:
457         case 'r': {
458             int armor = 1;
459 
460             if((i + 1) < stuff.length() && stuff.at(i + 1).isDigit())
461             {
462                 int const arg = stuff.at( ++i ).digitValue();
463                 if(arg < 0 || arg >= 4)
464                 {
465                     LOG_SCR_ERROR("Armor #%d unknown. Valid range %s")
466                             << arg << Rangei(0, 4).asText();
467                     break;
468                 }
469                 armor = arg;
470             }
471             P_GiveArmor(plr, armorClass[armor], armorPoints[armor]);
472             break; }
473 
474         // Keys:
475         case 'k': {
476             keytype_t keys = NUM_KEY_TYPES; // All types.
477 
478             // Give one specific key type?
479             if((i + 1) < stuff.length() && stuff.at(i + 1).isDigit())
480             {
481                 int const arg = stuff.at( ++i ).digitValue();
482                 if(arg < KT_FIRST || arg >= NUM_KEY_TYPES)
483                 {
484                     LOG_SCR_ERROR("Key #%d unknown. Valid range %s")
485                             << arg << Rangei(KT_FIRST, NUM_KEY_TYPES).asText();
486                     break;
487                 }
488                 keys = keytype_t(arg);
489             }
490             P_GiveKey(plr, keys);
491             break; }
492 
493         // Misc:
494         case 'p': P_GiveBackpack(plr);                  break;
495         case 'h': P_GiveHealth(plr, healthLimit);       break;
496 
497         // Powers:
498         case 'm': togglePower(plr, PT_ALLMAP);          break;
499         case 'f': togglePower(plr, PT_FLIGHT);          break;
500         case 'g': togglePower(plr, PT_INFRARED);        break;
501         case 'v': togglePower(plr, PT_INVISIBILITY);    break;
502         case 'i': togglePower(plr, PT_INVULNERABILITY); break;
503         case 's': togglePower(plr, PT_IRONFEET);        break;
504         case 'b': togglePower(plr, PT_STRENGTH);        break;
505 
506         // Weapons:
507         case 'w': {
508             weapontype_t weapons = NUM_WEAPON_TYPES; // All types.
509 
510             // Give one specific weapon type?
511             if((i + 1) < stuff.length() && stuff.at(i + 1).isDigit())
512             {
513                 int const arg = stuff.at( ++i ).digitValue();
514                 if(arg < WT_FIRST || arg >= NUM_WEAPON_TYPES)
515                 {
516                     LOG_SCR_ERROR("Weapon #%d unknown. Valid range %s")
517                             << arg << Rangei(WT_FIRST, NUM_WEAPON_TYPES).asText();
518                     break;
519                 }
520                 weapons = weapontype_t(arg);
521             }
522             giveWeapon(plr, weapons);
523             break; }
524 
525         default: // Unrecognized.
526             LOG_SCR_ERROR("Mnemonic '%c' unknown, cannot give") << mnemonic.toLatin1();
527             break;
528         }
529     }
530 
531     // If the give expression matches that of a vanilla cheat code print the
532     // associated confirmation message to the player's log.
533     /// @todo fixme: Somewhat of kludge...
534     if(stuff == "war2")
535     {
536         P_SetMessageWithFlags(plr, STSTR_FAADDED, LMF_NO_HIDE);
537     }
538     else if(stuff == "wakr3")
539     {
540         P_SetMessageWithFlags(plr, STSTR_KFAADDED, LMF_NO_HIDE);
541     }
542 
543     return true;
544 }
545 
D_CMD(CheatMassacre)546 D_CMD(CheatMassacre)
547 {
548     DENG2_UNUSED3(src, argc, argv);
549 
550     if(G_GameState() == GS_MAP)
551     {
552         if(IS_CLIENT)
553         {
554             NetCl_CheatRequest("kill");
555         }
556         else if((IS_NETGAME && !netSvAllowCheats) ||
557                 gfw_Rule(skill) == SM_NIGHTMARE)
558         {
559             return false;
560         }
561         else
562         {
563             int const killCount = P_Massacre();
564             LOG_SCR_MSG("%i monsters killed") << killCount;
565         }
566     }
567     return true;
568 }
569 
D_CMD(CheatWhere)570 D_CMD(CheatWhere)
571 {
572     DENG2_UNUSED3(src, argc, argv);
573 
574     if(G_GameState() != GS_MAP)
575         return true;
576 
577     player_t *plr = &players[CONSOLEPLAYER];
578     mobj_t *plrMo = plr->plr->mo;
579     if(!plrMo) return true;
580 
581     String const text = String("Map:%1 position:%2")
582                             .arg(gfw_Session()->mapUri().path().asText())
583                             .arg(Vector3d(plrMo->origin).asText());
584     P_SetMessageWithFlags(plr, text.toUtf8().constData(), LMF_NO_HIDE);
585 
586     // Also print the some information to the console.
587     LOG_SCR_NOTE("%s") << text;
588 
589     Sector *sector = Mobj_Sector(plrMo);
590 
591     uri_s *matUri = Materials_ComposeUri(P_GetIntp(sector, DMU_FLOOR_MATERIAL));
592     LOG_SCR_MSG("FloorZ:%f Material:%s")
593             << P_GetDoublep(sector, DMU_FLOOR_HEIGHT)
594             << Str_Text(Uri_ToString(matUri));
595     Uri_Delete(matUri);
596 
597     matUri = Materials_ComposeUri(P_GetIntp(sector, DMU_CEILING_MATERIAL));
598     LOG_SCR_MSG("CeilingZ:%f Material:%s")
599             << P_GetDoublep(sector, DMU_CEILING_HEIGHT)
600             << Str_Text(Uri_ToString(matUri));
601     Uri_Delete(matUri);
602 
603     LOG_SCR_MSG("Player height:%f Player radius:%f")
604             << plrMo->height << plrMo->radius;
605 
606     return true;
607 }
608 
G_RegisterCheats()609 void G_RegisterCheats()
610 {
611 /// Helper macro for registering new cheat event sequence handlers.
612 #define ADDCHEAT(name, callback)       G_AddEventSequence((name), CHEAT(callback))
613 
614 /// Helper macro for registering new cheat event sequence command handlers.
615 #define ADDCHEATCMD(name, cmdTemplate) G_AddEventSequenceCommand((name), cmdTemplate)
616 
617     switch(gameMode)
618     {
619     case doom2_hacx:
620         ADDCHEATCMD("blast",            "give wakr3 %p");
621         ADDCHEATCMD("boots",            "give s %p");
622         ADDCHEATCMD("bright",           "give g %p");
623         ADDCHEATCMD("ghost",            "give v %p");
624         ADDCHEAT   ("seeit%1",          Powerup2);
625         ADDCHEAT   ("seeit",            Powerup);
626         ADDCHEAT   ("show",             Reveal);
627         ADDCHEATCMD("superman",         "give i %p");
628         ADDCHEAT   ("tunes%1%2",        Music);
629         ADDCHEATCMD("walk",             "noclip %p");
630         ADDCHEATCMD("warpme%1%2",       "warp %1%2");
631         ADDCHEATCMD("whacko",           "give b %p");
632         ADDCHEAT   ("wheream",          MyPos);
633         ADDCHEATCMD("wuss",             "god %p");
634         ADDCHEATCMD("zap",              "give w7 %p");
635         break;
636 
637     case doom_chex:
638         ADDCHEATCMD("allen",            "give s %p");
639         ADDCHEATCMD("andrewbenson",     "give i %p");
640         ADDCHEATCMD("charlesjacobi",    "noclip %p");
641         ADDCHEATCMD("davidbrus",        "god %p");
642         ADDCHEATCMD("deanhyers",        "give b %p");
643         ADDCHEATCMD("digitalcafe",      "give m %p");
644         ADDCHEAT   ("idmus%1%2",        Music);
645         ADDCHEATCMD("joelkoenigs",      "give w7 %p");
646         ADDCHEATCMD("joshuastorms",     "give g %p");
647         ADDCHEAT   ("kimhyers",         MyPos);
648         ADDCHEATCMD("leesnyder%1%2",    "warp %1 %2");
649         ADDCHEATCMD("marybregi",        "give v %p");
650         ADDCHEATCMD("mikekoenigs",      "give war2 %p");
651         ADDCHEATCMD("scottholman",      "give wakr3 %p");
652         ADDCHEAT   ("sherrill",         Reveal);
653         break;
654 
655     default: // Doom
656         ADDCHEAT   ("idbehold%1",       Powerup2);
657         ADDCHEAT   ("idbehold",         Powerup);
658 
659         // Note that in vanilla this cheat enables invulnerability until the
660         // end of the current tic.
661         ADDCHEATCMD("idchoppers",       "give w7 %p");
662 
663         ADDCHEATCMD("idclev%1%2",       ((gameModeBits & GM_ANY_DOOM)? "warp %1 %2" : "warp %1%2"));
664         ADDCHEATCMD("idclip",           "noclip %p");
665         ADDCHEATCMD("iddqd",            "god %p");
666         ADDCHEAT   ("iddt",             Reveal);
667         ADDCHEATCMD("idfa",             "give war2 %p");
668         ADDCHEATCMD("idkfa",            "give wakr3 %p");
669         ADDCHEAT   ("idmus%1%2",        Music);
670         ADDCHEAT   ("idmypos",          MyPos);
671         ADDCHEATCMD("idspispopd",       "noclip %p");
672         break;
673     }
674 
675 #undef ADDCHEATCMD
676 #undef ADDCHEAT
677 }
678