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