1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 // Cheat sequence checking.
21 //
22 //-----------------------------------------------------------------------------
23
24
25 #include <stdlib.h>
26 #include <math.h>
27
28 #include "m_cheat.h"
29 #include "d_player.h"
30 #include "doomstat.h"
31 #include "gstrings.h"
32 #include "p_local.h"
33 #include "a_strifeglobal.h"
34 #include "gi.h"
35 #include "p_enemy.h"
36 #include "sbar.h"
37 #include "c_dispatch.h"
38 #include "v_video.h"
39 #include "w_wad.h"
40 #include "a_keys.h"
41 #include "templates.h"
42 #include "c_console.h"
43 #include "r_data/r_translate.h"
44 #include "g_level.h"
45 #include "d_net.h"
46 #include "d_dehacked.h"
47 #include "gi.h"
48 #include "farchive.h"
49
50 // [RH] Actually handle the cheat. The cheat code in st_stuff.c now just
51 // writes some bytes to the network data stream, and the network code
52 // later calls us.
53
cht_DoCheat(player_t * player,int cheat)54 void cht_DoCheat (player_t *player, int cheat)
55 {
56 static const char * BeholdPowers[9] =
57 {
58 "PowerInvulnerable",
59 "PowerStrength",
60 "PowerInvisibility",
61 "PowerIronFeet",
62 "MapRevealer",
63 "PowerLightAmp",
64 "PowerShadow",
65 "PowerMask",
66 "PowerTargeter",
67 };
68 const PClass *type;
69 AInventory *item;
70 const char *msg = "";
71 char msgbuild[32];
72 int i;
73
74 switch (cheat)
75 {
76 case CHT_IDDQD:
77 if (!(player->cheats & CF_GODMODE) && player->playerstate == PST_LIVE)
78 {
79 if (player->mo)
80 player->mo->health = deh.GodHealth;
81
82 player->health = deh.GodHealth;
83 }
84 // fall through to CHT_GOD
85 case CHT_GOD:
86 player->cheats ^= CF_GODMODE;
87 if (player->cheats & CF_GODMODE)
88 msg = GStrings("STSTR_DQDON");
89 else
90 msg = GStrings("STSTR_DQDOFF");
91 ST_SetNeedRefresh();
92 break;
93
94 case CHT_BUDDHA:
95 player->cheats ^= CF_BUDDHA;
96 if (player->cheats & CF_BUDDHA)
97 msg = GStrings("TXT_BUDDHAON");
98 else
99 msg = GStrings("TXT_BUDDHAOFF");
100 break;
101
102 case CHT_GOD2:
103 player->cheats ^= CF_GODMODE2;
104 if (player->cheats & CF_GODMODE2)
105 msg = GStrings("STSTR_DQD2ON");
106 else
107 msg = GStrings("STSTR_DQD2OFF");
108 ST_SetNeedRefresh();
109 break;
110
111 case CHT_BUDDHA2:
112 player->cheats ^= CF_BUDDHA2;
113 if (player->cheats & CF_BUDDHA2)
114 msg = GStrings("TXT_BUDDHA2ON");
115 else
116 msg = GStrings("TXT_BUDDHA2OFF");
117 break;
118
119 case CHT_NOCLIP:
120 player->cheats ^= CF_NOCLIP;
121 if (player->cheats & CF_NOCLIP)
122 msg = GStrings("STSTR_NCON");
123 else
124 msg = GStrings("STSTR_NCOFF");
125 break;
126
127 case CHT_NOCLIP2:
128 player->cheats ^= CF_NOCLIP2;
129 if (player->cheats & CF_NOCLIP2)
130 {
131 player->cheats |= CF_NOCLIP;
132 msg = GStrings("STSTR_NC2ON");
133 }
134 else
135 {
136 player->cheats &= ~CF_NOCLIP;
137 msg = GStrings("STSTR_NCOFF");
138 }
139 if (player->mo->velx == 0) player->mo->velx = 1; // force some lateral movement so that internal variables are up to date
140 break;
141
142 case CHT_NOVELOCITY:
143 player->cheats ^= CF_NOVELOCITY;
144 if (player->cheats & CF_NOVELOCITY)
145 msg = GStrings("TXT_LEADBOOTSON");
146 else
147 msg = GStrings("TXT_LEADBOOTSOFF");
148 break;
149
150 case CHT_FLY:
151 if (player->mo != NULL)
152 {
153 player->mo->flags7 ^= MF7_FLYCHEAT;
154 if (player->mo->flags7 & MF7_FLYCHEAT)
155 {
156 player->mo->flags |= MF_NOGRAVITY;
157 player->mo->flags2 |= MF2_FLY;
158 msg = GStrings("TXT_LIGHTER");
159 }
160 else
161 {
162 player->mo->flags &= ~MF_NOGRAVITY;
163 player->mo->flags2 &= ~MF2_FLY;
164 msg = GStrings("TXT_GRAVITY");
165 }
166 }
167 break;
168
169 case CHT_MORPH:
170 msg = cht_Morph (player, PClass::FindClass (gameinfo.gametype == GAME_Heretic ? NAME_ChickenPlayer : NAME_PigPlayer), true);
171 break;
172
173 case CHT_NOTARGET:
174 player->cheats ^= CF_NOTARGET;
175 if (player->cheats & CF_NOTARGET)
176 msg = "notarget ON";
177 else
178 msg = "notarget OFF";
179 break;
180
181 case CHT_ANUBIS:
182 player->cheats ^= CF_FRIGHTENING;
183 if (player->cheats & CF_FRIGHTENING)
184 msg = "\"Quake with fear!\"";
185 else
186 msg = "No more ogre armor";
187 break;
188
189 case CHT_CHASECAM:
190 player->cheats ^= CF_CHASECAM;
191 if (player->cheats & CF_CHASECAM)
192 msg = "chasecam ON";
193 else
194 msg = "chasecam OFF";
195 R_ResetViewInterpolation ();
196 break;
197
198 case CHT_CHAINSAW:
199 if (player->mo != NULL && player->health >= 0)
200 {
201 type = PClass::FindClass ("Chainsaw");
202 if (player->mo->FindInventory (type) == NULL)
203 {
204 player->mo->GiveInventoryType (type);
205 }
206 msg = GStrings("STSTR_CHOPPERS");
207 }
208 // [RH] The original cheat also set powers[pw_invulnerability] to true.
209 // Since this is a timer and not a boolean, it effectively turned off
210 // the invulnerability powerup, although it looks like it was meant to
211 // turn it on.
212 break;
213
214 case CHT_POWER:
215 if (player->mo != NULL && player->health >= 0)
216 {
217 item = player->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true);
218 if (item != NULL)
219 {
220 item->Destroy ();
221 msg = GStrings("TXT_CHEATPOWEROFF");
222 }
223 else
224 {
225 player->mo->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2));
226 msg = GStrings("TXT_CHEATPOWERON");
227 }
228 }
229 break;
230
231 case CHT_IDKFA:
232 cht_Give (player, "backpack");
233 cht_Give (player, "weapons");
234 cht_Give (player, "ammo");
235 cht_Give (player, "keys");
236 cht_Give (player, "armor");
237 msg = GStrings("STSTR_KFAADDED");
238 break;
239
240 case CHT_IDFA:
241 cht_Give (player, "backpack");
242 cht_Give (player, "weapons");
243 cht_Give (player, "ammo");
244 cht_Give (player, "armor");
245 msg = GStrings("STSTR_FAADDED");
246 break;
247
248 case CHT_BEHOLDV:
249 case CHT_BEHOLDS:
250 case CHT_BEHOLDI:
251 case CHT_BEHOLDR:
252 case CHT_BEHOLDA:
253 case CHT_BEHOLDL:
254 case CHT_PUMPUPI:
255 case CHT_PUMPUPM:
256 case CHT_PUMPUPT:
257 i = cheat - CHT_BEHOLDV;
258
259 if (i == 4)
260 {
261 level.flags2 ^= LEVEL2_ALLMAP;
262 }
263 else if (player->mo != NULL && player->health >= 0)
264 {
265 item = player->mo->FindInventory (BeholdPowers[i]);
266 if (item == NULL)
267 {
268 if (i != 0)
269 {
270 cht_Give(player, BeholdPowers[i]);
271 if (cheat == CHT_BEHOLDS)
272 {
273 P_GiveBody (player->mo, -100);
274 }
275 }
276 else
277 {
278 // Let's give the item here so that the power doesn't need colormap information.
279 cht_Give(player, "InvulnerabilitySphere");
280 }
281 }
282 else
283 {
284 item->Destroy ();
285 }
286 }
287 msg = GStrings("STSTR_BEHOLDX");
288 break;
289
290 case CHT_MASSACRE:
291 {
292 int killcount = P_Massacre ();
293 // killough 3/22/98: make more intelligent about plural
294 // Ty 03/27/98 - string(s) *not* externalized
295 mysnprintf (msgbuild, countof(msgbuild), "%d Monster%s Killed", killcount, killcount==1 ? "" : "s");
296 msg = msgbuild;
297 }
298 break;
299
300 case CHT_HEALTH:
301 if (player->mo != NULL && player->playerstate == PST_LIVE)
302 {
303 player->health = player->mo->health = player->mo->GetDefault()->health;
304 msg = GStrings("TXT_CHEATHEALTH");
305 }
306 break;
307
308 case CHT_KEYS:
309 cht_Give (player, "keys");
310 msg = GStrings("TXT_CHEATKEYS");
311 break;
312
313 // [GRB]
314 case CHT_RESSURECT:
315 if (player->playerstate != PST_LIVE && player->mo != NULL)
316 {
317 if (player->mo->IsKindOf(RUNTIME_CLASS(APlayerChunk)))
318 {
319 Printf("Unable to resurrect. Player is no longer connected to its body.\n");
320 }
321 else
322 {
323 player->playerstate = PST_LIVE;
324 player->health = player->mo->health = player->mo->GetDefault()->health;
325 player->viewheight = ((APlayerPawn *)player->mo->GetDefault())->ViewHeight;
326 player->mo->flags = player->mo->GetDefault()->flags;
327 player->mo->flags2 = player->mo->GetDefault()->flags2;
328 player->mo->flags3 = player->mo->GetDefault()->flags3;
329 player->mo->flags4 = player->mo->GetDefault()->flags4;
330 player->mo->flags5 = player->mo->GetDefault()->flags5;
331 player->mo->flags6 = player->mo->GetDefault()->flags6;
332 player->mo->flags7 = player->mo->GetDefault()->flags7;
333 player->mo->renderflags &= ~RF_INVISIBLE;
334 player->mo->height = player->mo->GetDefault()->height;
335 player->mo->radius = player->mo->GetDefault()->radius;
336 player->mo->special1 = 0; // required for the Hexen fighter's fist attack.
337 // This gets set by AActor::Die as flag for the wimpy death and must be reset here.
338 player->mo->SetState (player->mo->SpawnState);
339 if (!(player->mo->flags2 & MF2_DONTTRANSLATE))
340 {
341 player->mo->Translation = TRANSLATION(TRANSLATION_Players, BYTE(player-players));
342 }
343 player->mo->DamageType = NAME_None;
344 if (player->ReadyWeapon != NULL)
345 {
346 P_SetPsprite(player, ps_weapon, player->ReadyWeapon->GetUpState());
347 }
348
349 if (player->morphTics > 0)
350 {
351 P_UndoPlayerMorph(player, player);
352 }
353
354 }
355 }
356 break;
357
358 case CHT_GIMMIEA:
359 cht_Give (player, "ArtiInvulnerability");
360 msg = "Valador's Ring of Invunerability";
361 break;
362
363 case CHT_GIMMIEB:
364 cht_Give (player, "ArtiInvisibility");
365 msg = "Shadowsphere";
366 break;
367
368 case CHT_GIMMIEC:
369 cht_Give (player, "ArtiHealth");
370 msg = "Quartz Flask";
371 break;
372
373 case CHT_GIMMIED:
374 cht_Give (player, "ArtiSuperHealth");
375 msg = "Mystic Urn";
376 break;
377
378 case CHT_GIMMIEE:
379 cht_Give (player, "ArtiTomeOfPower");
380 msg = "Tyketto's Tome of Power";
381 break;
382
383 case CHT_GIMMIEF:
384 cht_Give (player, "ArtiTorch");
385 msg = "Torch";
386 break;
387
388 case CHT_GIMMIEG:
389 cht_Give (player, "ArtiTimeBomb");
390 msg = "Delmintalintar's Time Bomb of the Ancients";
391 break;
392
393 case CHT_GIMMIEH:
394 cht_Give (player, "ArtiEgg");
395 msg = "Torpol's Morph Ovum";
396 break;
397
398 case CHT_GIMMIEI:
399 cht_Give (player, "ArtiFly");
400 msg = "Inhilicon's Wings of Wrath";
401 break;
402
403 case CHT_GIMMIEJ:
404 cht_Give (player, "ArtiTeleport");
405 msg = "Darchala's Chaos Device";
406 break;
407
408 case CHT_GIMMIEZ:
409 for (int i=0;i<16;i++)
410 {
411 cht_Give (player, "artifacts");
412 }
413 msg = "All artifacts!";
414 break;
415
416 case CHT_TAKEWEAPS:
417 if (player->morphTics || player->mo == NULL || player->mo->health <= 0)
418 {
419 return;
420 }
421 {
422 // Take away all weapons that are either non-wimpy or use ammo.
423 AInventory **invp = &player->mo->Inventory, **lastinvp;
424 for (item = *invp; item != NULL; item = *invp)
425 {
426 lastinvp = invp;
427 invp = &(*invp)->Inventory;
428 if (item->IsKindOf (RUNTIME_CLASS(AWeapon)))
429 {
430 AWeapon *weap = static_cast<AWeapon *> (item);
431 if (!(weap->WeaponFlags & WIF_WIMPY_WEAPON) ||
432 weap->AmmoType1 != NULL)
433 {
434 item->Destroy ();
435 invp = lastinvp;
436 }
437 }
438 }
439 }
440 msg = GStrings("TXT_CHEATIDKFA");
441 break;
442
443 case CHT_NOWUDIE:
444 cht_Suicide (player);
445 msg = GStrings("TXT_CHEATIDDQD");
446 break;
447
448 case CHT_ALLARTI:
449 for (int i=0;i<25;i++)
450 {
451 cht_Give (player, "artifacts");
452 }
453 msg = GStrings("TXT_CHEATARTIFACTS3");
454 break;
455
456 case CHT_PUZZLE:
457 cht_Give (player, "puzzlepieces");
458 msg = GStrings("TXT_CHEATARTIFACTS3");
459 break;
460
461 case CHT_MDK:
462 if (player->mo == NULL)
463 {
464 Printf ("What do you want to kill outside of a game?\n");
465 }
466 else if (!deathmatch)
467 {
468 // Don't allow this in deathmatch even with cheats enabled, because it's
469 // a very very cheap kill.
470 P_LineAttack (player->mo, player->mo->angle, PLAYERMISSILERANGE,
471 P_AimLineAttack (player->mo, player->mo->angle, PLAYERMISSILERANGE), TELEFRAG_DAMAGE,
472 NAME_MDK, NAME_BulletPuff);
473 }
474 break;
475
476 case CHT_DONNYTRUMP:
477 cht_Give (player, "HealthTraining");
478 msg = GStrings("TXT_MIDASTOUCH");
479 break;
480
481 case CHT_LEGO:
482 if (player->mo != NULL && player->health >= 0)
483 {
484 int oldpieces = ASigil::GiveSigilPiece (player->mo);
485 item = player->mo->FindInventory (RUNTIME_CLASS(ASigil));
486
487 if (item != NULL)
488 {
489 if (oldpieces == 5)
490 {
491 item->Destroy ();
492 }
493 else
494 {
495 player->PendingWeapon = static_cast<AWeapon *> (item);
496 }
497 }
498 }
499 break;
500
501 case CHT_PUMPUPH:
502 cht_Give (player, "MedPatch");
503 cht_Give (player, "MedicalKit");
504 cht_Give (player, "SurgeryKit");
505 msg = GStrings("TXT_GOTSTUFF");
506 break;
507
508 case CHT_PUMPUPP:
509 cht_Give (player, "AmmoSatchel");
510 msg = GStrings("TXT_GOTSTUFF");
511 break;
512
513 case CHT_PUMPUPS:
514 cht_Give (player, "UpgradeStamina", 10);
515 cht_Give (player, "UpgradeAccuracy");
516 msg = GStrings("TXT_GOTSTUFF");
517 break;
518
519 case CHT_CLEARFROZENPROPS:
520 player->cheats &= ~(CF_FROZEN|CF_TOTALLYFROZEN);
521 msg = "Frozen player properties turned off";
522 break;
523
524 case CHT_FREEZE:
525 bglobal.changefreeze ^= 1;
526 if (bglobal.freeze ^ bglobal.changefreeze)
527 {
528 msg = GStrings("TXT_FREEZEON");
529 }
530 else
531 {
532 msg = GStrings("TXT_FREEZEOFF");
533 }
534 break;
535 }
536
537 if (!*msg) // [SO] Don't print blank lines!
538 return;
539
540 if (player == &players[consoleplayer])
541 Printf ("%s\n", msg);
542 else if (cheat != CHT_CHASECAM)
543 Printf ("%s cheats: %s\n", player->userinfo.GetName(), msg);
544 }
545
cht_Morph(player_t * player,const PClass * morphclass,bool quickundo)546 const char *cht_Morph (player_t *player, const PClass *morphclass, bool quickundo)
547 {
548 if (player->mo == NULL)
549 {
550 return "";
551 }
552 PClass *oldclass = player->mo->GetClass();
553
554 // Set the standard morph style for the current game
555 int style = MORPH_UNDOBYTOMEOFPOWER;
556 if (gameinfo.gametype == GAME_Hexen) style |= MORPH_UNDOBYCHAOSDEVICE;
557
558 if (player->morphTics)
559 {
560 if (P_UndoPlayerMorph (player, player))
561 {
562 if (!quickundo && oldclass != morphclass && P_MorphPlayer (player, player, morphclass, 0, style))
563 {
564 return GStrings("TXT_STRANGER");
565 }
566 return GStrings("TXT_NOTSTRANGE");
567 }
568 }
569 else if (P_MorphPlayer (player, player, morphclass, 0, style))
570 {
571 return GStrings("TXT_STRANGE");
572 }
573 return "";
574 }
575
GiveSpawner(player_t * player,const PClass * type,int amount)576 void GiveSpawner (player_t *player, const PClass *type, int amount)
577 {
578 if (player->mo == NULL || player->health <= 0)
579 {
580 return;
581 }
582
583 AInventory *item = static_cast<AInventory *>
584 (Spawn (type, player->mo->X(), player->mo->Y(), player->mo->Z(), NO_REPLACE));
585 if (item != NULL)
586 {
587 if (amount > 0)
588 {
589 if (type->IsDescendantOf (RUNTIME_CLASS(ABasicArmorPickup)))
590 {
591 if (static_cast<ABasicArmorPickup*>(item)->SaveAmount != 0)
592 {
593 static_cast<ABasicArmorPickup*>(item)->SaveAmount *= amount;
594 }
595 else
596 {
597 static_cast<ABasicArmorPickup*>(item)->SaveAmount *= amount;
598 }
599 }
600 else if (type->IsDescendantOf (RUNTIME_CLASS(ABasicArmorBonus)))
601 {
602 static_cast<ABasicArmorBonus*>(item)->SaveAmount *= amount;
603 }
604 else
605 {
606 item->Amount = MIN (amount, item->MaxAmount);
607 }
608 }
609 item->ClearCounters();
610 if (!item->CallTryPickup (player->mo))
611 {
612 item->Destroy ();
613 }
614 }
615 }
616
cht_Give(player_t * player,const char * name,int amount)617 void cht_Give (player_t *player, const char *name, int amount)
618 {
619 enum { ALL_NO, ALL_YES, ALL_YESYES } giveall;
620 int i;
621 const PClass *type;
622
623 if (player != &players[consoleplayer])
624 Printf ("%s is a cheater: give %s\n", player->userinfo.GetName(), name);
625
626 if (player->mo == NULL || player->health <= 0)
627 {
628 return;
629 }
630
631 giveall = ALL_NO;
632 if (stricmp (name, "all") == 0)
633 {
634 giveall = ALL_YES;
635 }
636 else if (stricmp (name, "everything") == 0)
637 {
638 giveall = ALL_YESYES;
639 }
640
641 if (stricmp (name, "health") == 0)
642 {
643 if (amount > 0)
644 {
645 if (player->mo)
646 {
647 player->mo->health += amount;
648 player->health = player->mo->health;
649 }
650 else
651 {
652 player->health += amount;
653 }
654 }
655 else
656 {
657 if (player->mo != NULL)
658 {
659 player->health = player->mo->health = player->mo->GetMaxHealth();
660 }
661 else
662 {
663 player->health = deh.GodHealth;
664 }
665 }
666 }
667
668 if (giveall || stricmp (name, "backpack") == 0)
669 {
670 // Select the correct type of backpack based on the game
671 type = PClass::FindClass(gameinfo.backpacktype);
672 if (type != NULL)
673 {
674 GiveSpawner (player, type, 1);
675 }
676
677 if (!giveall)
678 return;
679 }
680
681 if (giveall || stricmp (name, "ammo") == 0)
682 {
683 // Find every unique type of ammo. Give it to the player if
684 // he doesn't have it already, and set each to its maximum.
685 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
686 {
687 const PClass *type = PClass::m_Types[i];
688
689 if (type->ParentClass == RUNTIME_CLASS(AAmmo))
690 {
691 AInventory *ammo = player->mo->FindInventory (type);
692 if (ammo == NULL)
693 {
694 ammo = static_cast<AInventory *>(Spawn (type, 0, 0, 0, NO_REPLACE));
695 ammo->AttachToOwner (player->mo);
696 ammo->Amount = ammo->MaxAmount;
697 }
698 else if (ammo->Amount < ammo->MaxAmount)
699 {
700 ammo->Amount = ammo->MaxAmount;
701 }
702 }
703 }
704
705 if (!giveall)
706 return;
707 }
708
709 if (giveall || stricmp (name, "armor") == 0)
710 {
711 if (gameinfo.gametype != GAME_Hexen)
712 {
713 ABasicArmorPickup *armor = Spawn<ABasicArmorPickup> (0,0,0, NO_REPLACE);
714 armor->SaveAmount = 100*deh.BlueAC;
715 armor->SavePercent = gameinfo.Armor2Percent > 0? gameinfo.Armor2Percent : FRACUNIT/2;
716 if (!armor->CallTryPickup (player->mo))
717 {
718 armor->Destroy ();
719 }
720 }
721 else
722 {
723 for (i = 0; i < 4; ++i)
724 {
725 AHexenArmor *armor = Spawn<AHexenArmor> (0,0,0, NO_REPLACE);
726 armor->health = i;
727 armor->Amount = 0;
728 if (!armor->CallTryPickup (player->mo))
729 {
730 armor->Destroy ();
731 }
732 }
733 }
734
735 if (!giveall)
736 return;
737 }
738
739 if (giveall || stricmp (name, "keys") == 0)
740 {
741 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
742 {
743 if (PClass::m_Types[i]->IsDescendantOf (RUNTIME_CLASS(AKey)))
744 {
745 AKey *key = (AKey *)GetDefaultByType (PClass::m_Types[i]);
746 if (key->KeyNumber != 0)
747 {
748 key = static_cast<AKey *>(Spawn (PClass::m_Types[i], 0,0,0, NO_REPLACE));
749 if (!key->CallTryPickup (player->mo))
750 {
751 key->Destroy ();
752 }
753 }
754 }
755 }
756 if (!giveall)
757 return;
758 }
759
760 if (giveall || stricmp (name, "weapons") == 0)
761 {
762 AWeapon *savedpending = player->PendingWeapon;
763 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
764 {
765 type = PClass::m_Types[i];
766 // Don't give replaced weapons unless the replacement was done by Dehacked.
767 if (type != RUNTIME_CLASS(AWeapon) &&
768 type->IsDescendantOf (RUNTIME_CLASS(AWeapon)) &&
769 (type->GetReplacement() == type ||
770 type->GetReplacement()->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup))))
771
772 {
773 // Give the weapon only if it is in a weapon slot.
774 if (player->weapons.LocateWeapon(type, NULL, NULL))
775 {
776 AWeapon *def = (AWeapon*)GetDefaultByType (type);
777 if (giveall == ALL_YESYES || !(def->WeaponFlags & WIF_CHEATNOTWEAPON))
778 {
779 GiveSpawner (player, type, 1);
780 }
781 }
782 }
783 }
784 player->PendingWeapon = savedpending;
785
786 if (!giveall)
787 return;
788 }
789
790 if (giveall || stricmp (name, "artifacts") == 0)
791 {
792 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
793 {
794 type = PClass::m_Types[i];
795 if (type->IsDescendantOf (RUNTIME_CLASS(AInventory)))
796 {
797 AInventory *def = (AInventory*)GetDefaultByType (type);
798 if (def->Icon.isValid() && def->MaxAmount > 1 &&
799 !type->IsDescendantOf (RUNTIME_CLASS(APuzzleItem)) &&
800 !type->IsDescendantOf (RUNTIME_CLASS(APowerup)) &&
801 !type->IsDescendantOf (RUNTIME_CLASS(AArmor)))
802 {
803 // Do not give replaced items unless using "give everything"
804 if (giveall == ALL_YESYES || type->GetReplacement() == type)
805 {
806 GiveSpawner (player, type, amount <= 0 ? def->MaxAmount : amount);
807 }
808 }
809 }
810 }
811 if (!giveall)
812 return;
813 }
814
815 if (giveall || stricmp (name, "puzzlepieces") == 0)
816 {
817 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
818 {
819 type = PClass::m_Types[i];
820 if (type->IsDescendantOf (RUNTIME_CLASS(APuzzleItem)))
821 {
822 AInventory *def = (AInventory*)GetDefaultByType (type);
823 if (def->Icon.isValid())
824 {
825 // Do not give replaced items unless using "give everything"
826 if (giveall == ALL_YESYES || type->GetReplacement() == type)
827 {
828 GiveSpawner (player, type, amount <= 0 ? def->MaxAmount : amount);
829 }
830 }
831 }
832 }
833 if (!giveall)
834 return;
835 }
836
837 if (giveall)
838 return;
839
840 type = PClass::FindClass (name);
841 if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS(AInventory)))
842 {
843 if (player == &players[consoleplayer])
844 Printf ("Unknown item \"%s\"\n", name);
845 }
846 else
847 {
848 GiveSpawner (player, type, amount);
849 }
850 return;
851 }
852
cht_Take(player_t * player,const char * name,int amount)853 void cht_Take (player_t *player, const char *name, int amount)
854 {
855 bool takeall;
856 const PClass *type;
857
858 if (player->mo == NULL || player->health <= 0)
859 {
860 return;
861 }
862
863 takeall = (stricmp (name, "all") == 0);
864
865 if (!takeall && stricmp (name, "health") == 0)
866 {
867 if (player->mo->health - amount <= 0
868 || player->health - amount <= 0
869 || amount == 0)
870 {
871
872 cht_Suicide (player);
873
874 if (player == &players[consoleplayer])
875 C_HideConsole ();
876
877 return;
878 }
879
880 if (amount > 0)
881 {
882 if (player->mo)
883 {
884 player->mo->health -= amount;
885 player->health = player->mo->health;
886 }
887 else
888 {
889 player->health -= amount;
890 }
891 }
892
893 if (!takeall)
894 return;
895 }
896
897 if (takeall || stricmp (name, "backpack") == 0)
898 {
899 // Take away all types of backpacks the player might own.
900 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
901 {
902 const PClass *type = PClass::m_Types[i];
903
904 if (type->IsDescendantOf(RUNTIME_CLASS (ABackpackItem)))
905 {
906 AInventory *pack = player->mo->FindInventory (type);
907
908 if (pack) pack->Destroy();
909 }
910 }
911
912 if (!takeall)
913 return;
914 }
915
916 if (takeall || stricmp (name, "ammo") == 0)
917 {
918 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
919 {
920 const PClass *type = PClass::m_Types[i];
921
922 if (type->ParentClass == RUNTIME_CLASS (AAmmo))
923 {
924 AInventory *ammo = player->mo->FindInventory (type);
925
926 if (ammo)
927 ammo->Amount = 0;
928 }
929 }
930
931 if (!takeall)
932 return;
933 }
934
935 if (takeall || stricmp (name, "armor") == 0)
936 {
937 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
938 {
939 type = PClass::m_Types[i];
940
941 if (type->IsDescendantOf (RUNTIME_CLASS (AArmor)))
942 {
943 AActor *armor = player->mo->FindInventory (type);
944
945 if (armor)
946 armor->Destroy ();
947 }
948 }
949
950 if (!takeall)
951 return;
952 }
953
954 if (takeall || stricmp (name, "keys") == 0)
955 {
956 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
957 {
958 type = PClass::m_Types[i];
959
960 if (type->IsDescendantOf (RUNTIME_CLASS (AKey)))
961 {
962 AActor *key = player->mo->FindInventory (type);
963
964 if (key)
965 key->Destroy ();
966 }
967 }
968
969 if (!takeall)
970 return;
971 }
972
973 if (takeall || stricmp (name, "weapons") == 0)
974 {
975 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
976 {
977 type = PClass::m_Types[i];
978
979 if (type != RUNTIME_CLASS(AWeapon) &&
980 type->IsDescendantOf (RUNTIME_CLASS (AWeapon)))
981 {
982 AActor *weapon = player->mo->FindInventory (type);
983
984 if (weapon)
985 weapon->Destroy ();
986
987 player->ReadyWeapon = NULL;
988 player->PendingWeapon = WP_NOCHANGE;
989 player->psprites[ps_weapon].state = NULL;
990 player->psprites[ps_flash].state = NULL;
991 }
992 }
993
994 if (!takeall)
995 return;
996 }
997
998 if (takeall || stricmp (name, "artifacts") == 0)
999 {
1000 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
1001 {
1002 type = PClass::m_Types[i];
1003
1004 if (type->IsDescendantOf (RUNTIME_CLASS (AInventory)))
1005 {
1006 if (!type->IsDescendantOf (RUNTIME_CLASS (APuzzleItem)) &&
1007 !type->IsDescendantOf (RUNTIME_CLASS (APowerup)) &&
1008 !type->IsDescendantOf (RUNTIME_CLASS (AArmor)) &&
1009 !type->IsDescendantOf (RUNTIME_CLASS (AWeapon)) &&
1010 !type->IsDescendantOf (RUNTIME_CLASS (AKey)))
1011 {
1012 AActor *artifact = player->mo->FindInventory (type);
1013
1014 if (artifact)
1015 artifact->Destroy ();
1016 }
1017 }
1018 }
1019
1020 if (!takeall)
1021 return;
1022 }
1023
1024 if (takeall || stricmp (name, "puzzlepieces") == 0)
1025 {
1026 for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i)
1027 {
1028 type = PClass::m_Types[i];
1029
1030 if (type->IsDescendantOf (RUNTIME_CLASS (APuzzleItem)))
1031 {
1032 AActor *puzzlepiece = player->mo->FindInventory (type);
1033
1034 if (puzzlepiece)
1035 puzzlepiece->Destroy ();
1036 }
1037 }
1038
1039 if (!takeall)
1040 return;
1041 }
1042
1043 if (takeall)
1044 return;
1045
1046 type = PClass::FindClass (name);
1047 if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS (AInventory)))
1048 {
1049 if (player == &players[consoleplayer])
1050 Printf ("Unknown item \"%s\"\n", name);
1051 }
1052 else
1053 {
1054 player->mo->TakeInventory(type, amount ? amount : 1);
1055 }
1056 return;
1057 }
1058
1059 class DSuicider : public DThinker
1060 {
1061 DECLARE_CLASS(DSuicider, DThinker)
1062 HAS_OBJECT_POINTERS;
1063 public:
1064 TObjPtr<APlayerPawn> Pawn;
1065
Tick()1066 void Tick()
1067 {
1068 Pawn->flags |= MF_SHOOTABLE;
1069 Pawn->flags2 &= ~MF2_INVULNERABLE;
1070 // Store the player's current damage factor, to restore it later.
1071 fixed_t plyrdmgfact = Pawn->DamageFactor;
1072 Pawn->DamageFactor = 65536;
1073 P_DamageMobj (Pawn, Pawn, Pawn, TELEFRAG_DAMAGE, NAME_Suicide);
1074 Pawn->DamageFactor = plyrdmgfact;
1075 if (Pawn->health <= 0)
1076 {
1077 Pawn->flags &= ~MF_SHOOTABLE;
1078 }
1079 Destroy();
1080 }
1081 // You'll probably never be able to catch this in a save game, but
1082 // just in case, add a proper serializer.
Serialize(FArchive & arc)1083 void Serialize(FArchive &arc)
1084 {
1085 Super::Serialize(arc);
1086 arc << Pawn;
1087 }
1088 };
1089
1090 IMPLEMENT_POINTY_CLASS(DSuicider)
DECLARE_POINTER(Pawn)1091 DECLARE_POINTER(Pawn)
1092 END_POINTERS
1093
1094 void cht_Suicide (player_t *plyr)
1095 {
1096 // If this cheat was initiated by the suicide ccmd, and this is a single
1097 // player game, the CHT_SUICIDE will be processed before the tic is run,
1098 // so the console has not gone up yet. Use a temporary thinker to delay
1099 // the suicide until the game ticks so that death noises can be heard on
1100 // the initial tick.
1101 if (plyr->mo != NULL)
1102 {
1103 DSuicider *suicide = new DSuicider;
1104 suicide->Pawn = plyr->mo;
1105 GC::WriteBarrier(suicide, suicide->Pawn);
1106 }
1107 }
1108
1109
CCMD(mdk)1110 CCMD (mdk)
1111 {
1112 if (CheckCheatmode ())
1113 return;
1114
1115 Net_WriteByte (DEM_GENERICCHEAT);
1116 Net_WriteByte (CHT_MDK);
1117 }
1118