1 /*
2 ** c_cmds.cpp
3 ** Miscellaneous console commands.
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 ** It might be a good idea to move these into files that they are more
34 ** closely related to, but right now, I am too lazy to do that.
35 */
36 
37 #include <math.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <time.h>
41 
42 #ifdef _WIN32
43 #include <direct.h>
44 #else
45 #include <unistd.h>
46 #endif
47 
48 #include "version.h"
49 #include "c_console.h"
50 #include "c_dispatch.h"
51 
52 #include "i_system.h"
53 
54 #include "doomerrors.h"
55 #include "doomstat.h"
56 #include "gstrings.h"
57 #include "s_sound.h"
58 #include "g_game.h"
59 #include "g_level.h"
60 #include "w_wad.h"
61 #include "g_level.h"
62 #include "gi.h"
63 #include "r_defs.h"
64 #include "d_player.h"
65 #include "templates.h"
66 #include "p_local.h"
67 #include "r_sky.h"
68 #include "p_setup.h"
69 #include "cmdlib.h"
70 #include "d_net.h"
71 #include "v_text.h"
72 #include "p_lnspec.h"
73 #include "v_video.h"
74 
75 extern FILE *Logfile;
76 extern bool insave;
77 
78 CVAR (Bool, sv_cheats, false, CVAR_SERVERINFO | CVAR_LATCH)
CVAR(Bool,sv_unlimited_pickup,false,CVAR_SERVERINFO)79 CVAR (Bool, sv_unlimited_pickup, false, CVAR_SERVERINFO)
80 
81 CCMD (toggleconsole)
82 {
83 	C_ToggleConsole();
84 }
85 
CheckCheatmode(bool printmsg)86 bool CheckCheatmode (bool printmsg)
87 {
88 	if ((G_SkillProperty(SKILLP_DisableCheats) || netgame || deathmatch) && (!sv_cheats))
89 	{
90 		if (printmsg) Printf ("sv_cheats must be true to enable this command.\n");
91 		return true;
92 	}
93 	else
94 	{
95 		return false;
96 	}
97 }
98 
CCMD(quit)99 CCMD (quit)
100 {
101 	if (!insave) exit (0);
102 }
103 
CCMD(exit)104 CCMD (exit)
105 {
106 	if (!insave) exit (0);
107 }
108 
109 /*
110 ==================
111 Cmd_God
112 
113 Sets client to godmode
114 
115 argv(0) god
116 ==================
117 */
CCMD(god)118 CCMD (god)
119 {
120 	if (CheckCheatmode ())
121 		return;
122 
123 	Net_WriteByte (DEM_GENERICCHEAT);
124 	Net_WriteByte (CHT_GOD);
125 }
126 
CCMD(god2)127 CCMD(god2)
128 {
129 	if (CheckCheatmode())
130 		return;
131 
132 	Net_WriteByte(DEM_GENERICCHEAT);
133 	Net_WriteByte(CHT_GOD2);
134 }
135 
CCMD(iddqd)136 CCMD (iddqd)
137 {
138 	if (CheckCheatmode ())
139 		return;
140 
141 	Net_WriteByte (DEM_GENERICCHEAT);
142 	Net_WriteByte (CHT_IDDQD);
143 }
144 
CCMD(buddha)145 CCMD (buddha)
146 {
147 	if (CheckCheatmode())
148 		return;
149 
150 	Net_WriteByte(DEM_GENERICCHEAT);
151 	Net_WriteByte(CHT_BUDDHA);
152 }
153 
CCMD(buddha2)154 CCMD(buddha2)
155 {
156 	if (CheckCheatmode())
157 		return;
158 
159 	Net_WriteByte(DEM_GENERICCHEAT);
160 	Net_WriteByte(CHT_BUDDHA2);
161 }
162 
CCMD(notarget)163 CCMD (notarget)
164 {
165 	if (CheckCheatmode ())
166 		return;
167 
168 	Net_WriteByte (DEM_GENERICCHEAT);
169 	Net_WriteByte (CHT_NOTARGET);
170 }
171 
CCMD(fly)172 CCMD (fly)
173 {
174 	if (CheckCheatmode ())
175 		return;
176 
177 	Net_WriteByte (DEM_GENERICCHEAT);
178 	Net_WriteByte (CHT_FLY);
179 }
180 
181 /*
182 ==================
183 Cmd_Noclip
184 
185 argv(0) noclip
186 ==================
187 */
CCMD(noclip)188 CCMD (noclip)
189 {
190 	if (CheckCheatmode ())
191 		return;
192 
193 	Net_WriteByte (DEM_GENERICCHEAT);
194 	Net_WriteByte (CHT_NOCLIP);
195 }
196 
CCMD(noclip2)197 CCMD (noclip2)
198 {
199 	if (CheckCheatmode())
200 		return;
201 
202 	Net_WriteByte (DEM_GENERICCHEAT);
203 	Net_WriteByte (CHT_NOCLIP2);
204 }
205 
CCMD(powerup)206 CCMD (powerup)
207 {
208 	if (CheckCheatmode ())
209 		return;
210 
211 	Net_WriteByte (DEM_GENERICCHEAT);
212 	Net_WriteByte (CHT_POWER);
213 }
214 
CCMD(morphme)215 CCMD (morphme)
216 {
217 	if (CheckCheatmode ())
218 		return;
219 
220 	if (argv.argc() == 1)
221 	{
222 		Net_WriteByte (DEM_GENERICCHEAT);
223 		Net_WriteByte (CHT_MORPH);
224 	}
225 	else
226 	{
227 		Net_WriteByte (DEM_MORPHEX);
228 		Net_WriteString (argv[1]);
229 	}
230 }
231 
CCMD(anubis)232 CCMD (anubis)
233 {
234 	if (CheckCheatmode ())
235 		return;
236 
237 	Net_WriteByte (DEM_GENERICCHEAT);
238 	Net_WriteByte (CHT_ANUBIS);
239 }
240 
241 // [GRB]
CCMD(resurrect)242 CCMD (resurrect)
243 {
244 	if (CheckCheatmode ())
245 		return;
246 
247 	Net_WriteByte (DEM_GENERICCHEAT);
248 	Net_WriteByte (CHT_RESSURECT);
249 }
250 
EXTERN_CVAR(Bool,chasedemo)251 EXTERN_CVAR (Bool, chasedemo)
252 
253 CCMD (chase)
254 {
255 	if (demoplayback)
256 	{
257 		int i;
258 
259 		if (chasedemo)
260 		{
261 			chasedemo = false;
262 			for (i = 0; i < MAXPLAYERS; i++)
263 				players[i].cheats &= ~CF_CHASECAM;
264 		}
265 		else
266 		{
267 			chasedemo = true;
268 			for (i = 0; i < MAXPLAYERS; i++)
269 				players[i].cheats |= CF_CHASECAM;
270 		}
271 		R_ResetViewInterpolation ();
272 	}
273 	else
274 	{
275 		// Check if we're allowed to use chasecam.
276 		if (gamestate != GS_LEVEL || (!(dmflags2 & DF2_CHASECAM) && deathmatch && CheckCheatmode ()))
277 			return;
278 
279 		Net_WriteByte (DEM_GENERICCHEAT);
280 		Net_WriteByte (CHT_CHASECAM);
281 	}
282 }
283 
CCMD(idclev)284 CCMD (idclev)
285 {
286 	if (netgame)
287 		return;
288 
289 	if ((argv.argc() > 1) && (*(argv[1] + 2) == 0) && *(argv[1] + 1) && *argv[1])
290 	{
291 		int epsd, map;
292 		char buf[2];
293 		FString mapname;
294 
295 		buf[0] = argv[1][0] - '0';
296 		buf[1] = argv[1][1] - '0';
297 
298 		if (gameinfo.flags & GI_MAPxx)
299 		{
300 			epsd = 1;
301 			map = buf[0]*10 + buf[1];
302 		}
303 		else
304 		{
305 			epsd = buf[0];
306 			map = buf[1];
307 		}
308 
309 		// Catch invalid maps.
310 		mapname = CalcMapName (epsd, map);
311 
312 		if (!P_CheckMapData(mapname))
313 			return;
314 
315 		// So be it.
316 		Printf ("%s\n", GStrings("STSTR_CLEV"));
317       	G_DeferedInitNew (mapname);
318 		//players[0].health = 0;		// Force reset
319 	}
320 }
321 
CCMD(hxvisit)322 CCMD (hxvisit)
323 {
324 	if (netgame)
325 		return;
326 
327 	if ((argv.argc() > 1) && (*(argv[1] + 2) == 0) && *(argv[1] + 1) && *argv[1])
328 	{
329 		FString mapname("&wt@");
330 
331 		mapname << argv[1][0] << argv[1][1];
332 
333 		if (CheckWarpTransMap (mapname, false))
334 		{
335 			// Just because it's in MAPINFO doesn't mean it's in the wad.
336 
337 			if (P_CheckMapData(mapname))
338 			{
339 				// So be it.
340 				Printf ("%s\n", GStrings("STSTR_CLEV"));
341       			G_DeferedInitNew (mapname);
342 				return;
343 			}
344 		}
345 		Printf ("No such map found\n");
346 	}
347 }
348 
CCMD(changemap)349 CCMD (changemap)
350 {
351 	if (who == NULL || !usergame)
352 	{
353 		Printf ("Use the map command when not in a game.\n");
354 		return;
355 	}
356 
357 	if (!players[who->player - players].settings_controller && netgame)
358 	{
359 		Printf ("Only setting controllers can change the map.\n");
360 		return;
361 	}
362 
363 	if (argv.argc() > 1)
364 	{
365 		try
366 		{
367 			if (!P_CheckMapData(argv[1]))
368 			{
369 				Printf ("No map %s\n", argv[1]);
370 			}
371 			else
372 			{
373 				if (argv.argc() > 2)
374 				{
375 					Net_WriteByte (DEM_CHANGEMAP2);
376 					Net_WriteByte (atoi(argv[2]));
377 				}
378 				else
379 				{
380 					Net_WriteByte (DEM_CHANGEMAP);
381 				}
382 				Net_WriteString (argv[1]);
383 			}
384 		}
385 		catch(CRecoverableError &error)
386 		{
387 			if (error.GetMessage())
388 				Printf("%s", error.GetMessage());
389 		}
390 	}
391 	else
392 	{
393 		Printf ("Usage: changemap <map name> [position]\n");
394 	}
395 }
396 
CCMD(give)397 CCMD (give)
398 {
399 	if (CheckCheatmode () || argv.argc() < 2)
400 		return;
401 
402 	Net_WriteByte (DEM_GIVECHEAT);
403 	Net_WriteString (argv[1]);
404 	if (argv.argc() > 2)
405 		Net_WriteWord (clamp (atoi (argv[2]), 1, 32767));
406 	else
407 		Net_WriteWord (0);
408 }
409 
CCMD(take)410 CCMD (take)
411 {
412 	if (CheckCheatmode () || argv.argc() < 2)
413 		return;
414 
415 	Net_WriteByte (DEM_TAKECHEAT);
416 	Net_WriteString (argv[1]);
417 	if (argv.argc() > 2)
418 		Net_WriteWord (clamp (atoi (argv[2]), 1, 32767));
419 	else
420 		Net_WriteWord (0);
421 }
422 
CCMD(gameversion)423 CCMD (gameversion)
424 {
425 	Printf ("%s @ %s\nCommit %s\n", GetVersionString(), GetGitTime(), GetGitHash());
426 }
427 
CCMD(print)428 CCMD (print)
429 {
430 	if (argv.argc() != 2)
431 	{
432 		Printf ("print <name>: Print a string from the string table\n");
433 		return;
434 	}
435 	const char *str = GStrings[argv[1]];
436 	if (str == NULL)
437 	{
438 		Printf ("%s unknown\n", argv[1]);
439 	}
440 	else
441 	{
442 		Printf ("%s\n", str);
443 	}
444 }
445 
CCMD(exec)446 CCMD (exec)
447 {
448 	if (argv.argc() < 2)
449 		return;
450 
451 	for (int i = 1; i < argv.argc(); ++i)
452 	{
453 		if (!C_ExecFile(argv[i]))
454 		{
455 			Printf ("Could not exec \"%s\"\n", argv[i]);
456 			break;
457 		}
458 	}
459 }
460 
execLogfile(const char * fn)461 void execLogfile(const char *fn)
462 {
463 	if ((Logfile = fopen(fn, "w")))
464 	{
465 		const char *timestr = myasctime();
466 		Printf("Log started: %s\n", timestr);
467 	}
468 	else
469 	{
470 		Printf("Could not start log\n");
471 	}
472 }
473 
CCMD(logfile)474 CCMD (logfile)
475 {
476 
477 	if (Logfile)
478 	{
479 		const char *timestr = myasctime();
480 		Printf("Log stopped: %s\n", timestr);
481 		fclose (Logfile);
482 		Logfile = NULL;
483 	}
484 
485 	if (argv.argc() >= 2)
486 	{
487 		execLogfile(argv[1]);
488 	}
489 }
490 
CCMD(puke)491 CCMD (puke)
492 {
493 	int argc = argv.argc();
494 
495 	if (argc < 2 || argc > 6)
496 	{
497 		Printf ("Usage: puke <script> [arg1] [arg2] [arg3] [arg4]\n");
498 	}
499 	else
500 	{
501 		int script = atoi (argv[1]);
502 
503 		if (script == 0)
504 		{ // Script 0 is reserved for Strife support. It is not pukable.
505 			return;
506 		}
507 		int arg[4] = { 0, 0, 0, 0 };
508 		int argn = MIN<int>(argc - 2, countof(arg)), i;
509 
510 		for (i = 0; i < argn; ++i)
511 		{
512 			arg[i] = atoi (argv[2+i]);
513 		}
514 
515 		if (script > 0)
516 		{
517 			Net_WriteByte (DEM_RUNSCRIPT);
518 			Net_WriteWord (script);
519 		}
520 		else
521 		{
522 			Net_WriteByte (DEM_RUNSCRIPT2);
523 			Net_WriteWord (-script);
524 		}
525 		Net_WriteByte (argn);
526 		for (i = 0; i < argn; ++i)
527 		{
528 			Net_WriteLong (arg[i]);
529 		}
530 	}
531 }
532 
CCMD(pukename)533 CCMD (pukename)
534 {
535 	int argc = argv.argc();
536 
537 	if (argc < 2 || argc > 7)
538 	{
539 		Printf ("Usage: pukename \"<script>\" [\"always\"] [arg1] [arg2] [arg3] [arg4]\n");
540 	}
541 	else
542 	{
543 		bool always = false;
544 		int argstart = 2;
545 		int arg[4] = { 0, 0, 0, 0 };
546 		int argn = 0, i;
547 
548 		if (argc > 2)
549 		{
550 			if (stricmp(argv[2], "always") == 0)
551 			{
552 				always = true;
553 				argstart = 3;
554 			}
555 			argn = MIN<int>(argc - argstart, countof(arg));
556 			for (i = 0; i < argn; ++i)
557 			{
558 				arg[i] = atoi(argv[argstart + i]);
559 			}
560 		}
561 		Net_WriteByte(DEM_RUNNAMEDSCRIPT);
562 		Net_WriteString(argv[1]);
563 		Net_WriteByte(argn | (always << 7));
564 		for (i = 0; i < argn; ++i)
565 		{
566 			Net_WriteLong(arg[i]);
567 		}
568 	}
569 }
570 
CCMD(special)571 CCMD (special)
572 {
573 	int argc = argv.argc();
574 
575 	if (argc < 2 || argc > 7)
576 	{
577 		Printf("Usage: special <special-name> [arg1] [arg2] [arg3] [arg4] [arg5]\n");
578 	}
579 	else
580 	{
581 		int specnum;
582 
583 		if (argv[1][0] >= '0' && argv[1][0] <= '9')
584 		{
585 			specnum = atoi(argv[1]);
586 			if (specnum < 0 || specnum > 255)
587 			{
588 				Printf("Bad special number\n");
589 				return;
590 			}
591 		}
592 		else
593 		{
594 			int min_args;
595 			specnum = P_FindLineSpecial(argv[1], &min_args);
596 			if (specnum == 0 || min_args < 0)
597 			{
598 				Printf("Unknown special\n");
599 				return;
600 			}
601 			if (argc < 2 + min_args)
602 			{
603 				Printf("%s needs at least %d argument%s\n", argv[1], min_args, min_args == 1 ? "" : "s");
604 				return;
605 			}
606 		}
607 		Net_WriteByte(DEM_RUNSPECIAL);
608 		Net_WriteByte(specnum);
609 		Net_WriteByte(argc - 2);
610 		for (int i = 2; i < argc; ++i)
611 		{
612 			Net_WriteLong(atoi(argv[i]));
613 		}
614 	}
615 }
616 
CCMD(error)617 CCMD (error)
618 {
619 	if (argv.argc() > 1)
620 	{
621 		char *textcopy = copystring (argv[1]);
622 		I_Error ("%s", textcopy);
623 	}
624 	else
625 	{
626 		Printf ("Usage: error <error text>\n");
627 	}
628 }
629 
CCMD(error_fatal)630 CCMD (error_fatal)
631 {
632 	if (argv.argc() > 1)
633 	{
634 		char *textcopy = copystring (argv[1]);
635 		I_FatalError ("%s", textcopy);
636 	}
637 	else
638 	{
639 		Printf ("Usage: error_fatal <error text>\n");
640 	}
641 }
642 
643 //==========================================================================
644 //
645 // CCMD crashout
646 //
647 // Debugging routine for testing the crash logger.
648 // Useless in a win32 debug build, because that doesn't enable the crash logger.
649 //
650 //==========================================================================
651 
652 #if !defined(_WIN32) || !defined(_DEBUG)
CCMD(crashout)653 CCMD (crashout)
654 {
655 	*(volatile int *)0 = 0;
656 }
657 #endif
658 
659 
CCMD(dir)660 CCMD (dir)
661 {
662 	FString dir, path;
663 	char curdir[256];
664 	const char *match;
665 	findstate_t c_file;
666 	void *file;
667 
668 	if (!getcwd (curdir, countof(curdir)))
669 	{
670 		Printf ("Current path too long\n");
671 		return;
672 	}
673 
674 	if (argv.argc() > 1)
675 	{
676 		path = NicePath(argv[1]);
677 		if (chdir(path))
678 		{
679 			match = path;
680 			dir = ExtractFilePath(path);
681 			if (dir[0] != '\0')
682 			{
683 				match += dir.Len();
684 			}
685 			else
686 			{
687 				dir = "./";
688 			}
689 			if (match[0] == '\0')
690 			{
691 				match = "*";
692 			}
693 			if (chdir (dir))
694 			{
695 				Printf ("%s not found\n", dir.GetChars());
696 				return;
697 			}
698 		}
699 		else
700 		{
701 			match = "*";
702 			dir = path;
703 		}
704 	}
705 	else
706 	{
707 		match = "*";
708 		dir = curdir;
709 	}
710 	if (dir[dir.Len()-1] != '/')
711 	{
712 		dir += '/';
713 	}
714 
715 	if ( (file = I_FindFirst (match, &c_file)) == ((void *)(-1)))
716 		Printf ("Nothing matching %s%s\n", dir.GetChars(), match);
717 	else
718 	{
719 		Printf ("Listing of %s%s:\n", dir.GetChars(), match);
720 		do
721 		{
722 			if (I_FindAttr (&c_file) & FA_DIREC)
723 				Printf (PRINT_BOLD, "%s <dir>\n", I_FindName (&c_file));
724 			else
725 				Printf ("%s\n", I_FindName (&c_file));
726 		} while (I_FindNext (file, &c_file) == 0);
727 		I_FindClose (file);
728 	}
729 
730 	chdir (curdir);
731 }
732 
CCMD(fov)733 CCMD (fov)
734 {
735 	player_t *player = who ? who->player : &players[consoleplayer];
736 
737 	if (argv.argc() != 2)
738 	{
739 		Printf ("fov is %g\n", player->DesiredFOV);
740 		return;
741 	}
742 	else if (dmflags & DF_NO_FOV)
743 	{
744 		if (consoleplayer == Net_Arbitrator)
745 		{
746 			Net_WriteByte (DEM_FOV);
747 		}
748 		else
749 		{
750 			Printf ("A setting controller has disabled FOV changes.\n");
751 			return;
752 		}
753 	}
754 	else
755 	{
756 		Net_WriteByte (DEM_MYFOV);
757 	}
758 	Net_WriteByte (clamp (atoi (argv[1]), 5, 179));
759 }
760 
761 //==========================================================================
762 //
763 // CCMD warp
764 //
765 // Warps to a specific location on a map
766 //
767 //==========================================================================
768 
CCMD(warp)769 CCMD (warp)
770 {
771 	if (CheckCheatmode ())
772 	{
773 		return;
774 	}
775 	if (gamestate != GS_LEVEL)
776 	{
777 		Printf ("You can only warp inside a level.\n");
778 		return;
779 	}
780 	if (argv.argc() != 3)
781 	{
782 		Printf ("Usage: warp <x> <y>\n");
783 	}
784 	else
785 	{
786 		Net_WriteByte (DEM_WARPCHEAT);
787 		Net_WriteWord (atoi (argv[1]));
788 		Net_WriteWord (atoi (argv[2]));
789 	}
790 }
791 
792 //==========================================================================
793 //
794 // CCMD load
795 //
796 // Load a saved game.
797 //
798 //==========================================================================
799 
CCMD(load)800 CCMD (load)
801 {
802     if (argv.argc() != 2)
803 	{
804         Printf ("usage: load <filename>\n");
805         return;
806     }
807 	if (netgame)
808 	{
809 		Printf ("cannot load during a network game\n");
810 		return;
811 	}
812 	FString fname = argv[1];
813 	DefaultExtension (fname, ".zds");
814     G_LoadGame (fname);
815 }
816 
817 //==========================================================================
818 //
819 // CCMD save
820 //
821 // Save the current game.
822 //
823 //==========================================================================
824 
CCMD(save)825 CCMD (save)
826 {
827     if (argv.argc() < 2 || argv.argc() > 3)
828 	{
829         Printf ("usage: save <filename> [description]\n");
830         return;
831     }
832     FString fname = argv[1];
833 	DefaultExtension (fname, ".zds");
834 	G_SaveGame (fname, argv.argc() > 2 ? argv[2] : argv[1]);
835 }
836 
837 //==========================================================================
838 //
839 // CCMD wdir
840 //
841 // Lists the contents of a loaded wad file.
842 //
843 //==========================================================================
844 
CCMD(wdir)845 CCMD (wdir)
846 {
847 	if (argv.argc() != 2)
848 	{
849 		Printf ("usage: wdir <wadfile>\n");
850 		return;
851 	}
852 	int wadnum = Wads.CheckIfWadLoaded (argv[1]);
853 	if (wadnum < 0)
854 	{
855 		Printf ("%s must be loaded to view its directory.\n", argv[1]);
856 		return;
857 	}
858 	for (int i = 0; i < Wads.GetNumLumps(); ++i)
859 	{
860 		if (Wads.GetLumpFile(i) == wadnum)
861 		{
862 			Printf ("%s\n", Wads.GetLumpFullName(i));
863 		}
864 	}
865 }
866 
867 //-----------------------------------------------------------------------------
868 //
869 //
870 //
871 //-----------------------------------------------------------------------------
CCMD(linetarget)872 CCMD(linetarget)
873 {
874 	AActor *linetarget;
875 
876 	if (CheckCheatmode () || players[consoleplayer].mo == NULL) return;
877 	P_AimLineAttack(players[consoleplayer].mo,players[consoleplayer].mo->angle,MISSILERANGE, &linetarget, 0);
878 	if (linetarget)
879 	{
880 		Printf("Target=%s, Health=%d, Spawnhealth=%d\n",
881 			linetarget->GetClass()->TypeName.GetChars(),
882 			linetarget->health,
883 			linetarget->SpawnHealth());
884 	}
885 	else Printf("No target found\n");
886 }
887 
888 // As linetarget, but also give info about non-shootable actors
CCMD(info)889 CCMD(info)
890 {
891 	AActor *linetarget;
892 
893 	if (CheckCheatmode () || players[consoleplayer].mo == NULL) return;
894 	P_AimLineAttack(players[consoleplayer].mo,players[consoleplayer].mo->angle,MISSILERANGE,
895 		&linetarget, 0,	ALF_CHECKNONSHOOTABLE|ALF_FORCENOSMART);
896 	if (linetarget)
897 	{
898 		Printf("Target=%s, Health=%d, Spawnhealth=%d\n",
899 			linetarget->GetClass()->TypeName.GetChars(),
900 			linetarget->health,
901 			linetarget->SpawnHealth());
902 		PrintMiscActorInfo(linetarget);
903 	}
904 	else Printf("No target found. Info cannot find actors that have "
905 				"the NOBLOCKMAP flag or have height/radius of 0.\n");
906 }
907 
908 typedef bool (*ActorTypeChecker) (AActor *);
909 
IsActorAMonster(AActor * mo)910 static bool IsActorAMonster(AActor *mo)
911 {
912 	return mo->flags3&MF3_ISMONSTER && !(mo->flags&MF_CORPSE) && !(mo->flags&MF_FRIENDLY);
913 }
914 
IsActorAnItem(AActor * mo)915 static bool IsActorAnItem(AActor *mo)
916 {
917 	return mo->IsKindOf(RUNTIME_CLASS(AInventory)) && mo->flags&MF_SPECIAL;
918 }
919 
IsActorACountItem(AActor * mo)920 static bool IsActorACountItem(AActor *mo)
921 {
922 	return mo->IsKindOf(RUNTIME_CLASS(AInventory)) && mo->flags&MF_SPECIAL && mo->flags&MF_COUNTITEM;
923 }
924 
PrintFilteredActorList(const ActorTypeChecker IsActorType,const char * FilterName)925 static void PrintFilteredActorList(const ActorTypeChecker IsActorType, const char *FilterName)
926 {
927 	AActor *mo;
928 	const PClass *FilterClass = NULL;
929 
930 	if (FilterName != NULL)
931 	{
932 		FilterClass = PClass::FindClass(FilterName);
933 		if (FilterClass == NULL || FilterClass->ActorInfo == NULL)
934 		{
935 			Printf("%s is not an actor class.\n", FilterName);
936 			return;
937 		}
938 	}
939 	TThinkerIterator<AActor> it;
940 
941 	while ( (mo = it.Next()) )
942 	{
943 		if ((FilterClass == NULL || mo->IsA(FilterClass)) && IsActorType(mo))
944 		{
945 			Printf ("%s at (%d,%d,%d)\n",
946 				mo->GetClass()->TypeName.GetChars(),
947 				mo->X() >> FRACBITS, mo->Y() >> FRACBITS, mo->Z() >> FRACBITS);
948 		}
949 	}
950 }
951 
952 //-----------------------------------------------------------------------------
953 //
954 //
955 //
956 //-----------------------------------------------------------------------------
CCMD(monster)957 CCMD(monster)
958 {
959 	if (CheckCheatmode ()) return;
960 
961 	PrintFilteredActorList(IsActorAMonster, argv.argc() > 1 ? argv[1] : NULL);
962 }
963 
964 //-----------------------------------------------------------------------------
965 //
966 //
967 //
968 //-----------------------------------------------------------------------------
CCMD(items)969 CCMD(items)
970 {
971 	if (CheckCheatmode ()) return;
972 
973 	PrintFilteredActorList(IsActorAnItem, argv.argc() > 1 ? argv[1] : NULL);
974 }
975 
976 //-----------------------------------------------------------------------------
977 //
978 //
979 //
980 //-----------------------------------------------------------------------------
CCMD(countitems)981 CCMD(countitems)
982 {
983 	if (CheckCheatmode ()) return;
984 
985 	PrintFilteredActorList(IsActorACountItem, argv.argc() > 1 ? argv[1] : NULL);
986 }
987 
988 //-----------------------------------------------------------------------------
989 //
990 //
991 //
992 //-----------------------------------------------------------------------------
CCMD(changesky)993 CCMD(changesky)
994 {
995 	const char *sky1name;
996 
997 	if (netgame || argv.argc()<2) return;
998 
999 	sky1name = argv[1];
1000 	if (sky1name[0] != 0)
1001 	{
1002 		FTextureID newsky = TexMan.GetTexture(sky1name, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_ReturnFirst);
1003 		if (newsky.Exists())
1004 		{
1005 			sky1texture = level.skytexture1 = newsky;
1006 		}
1007 		else
1008 		{
1009 			Printf("changesky: Texture '%s' not found\n", sky1name);
1010 		}
1011 	}
1012 	R_InitSkyMap ();
1013 }
1014 
1015 //-----------------------------------------------------------------------------
1016 //
1017 //
1018 //
1019 //-----------------------------------------------------------------------------
CCMD(thaw)1020 CCMD(thaw)
1021 {
1022 	if (CheckCheatmode())
1023 		return;
1024 
1025 	Net_WriteByte (DEM_GENERICCHEAT);
1026 	Net_WriteByte (CHT_CLEARFROZENPROPS);
1027 }
1028 
1029 //-----------------------------------------------------------------------------
1030 //
1031 //
1032 //
1033 //-----------------------------------------------------------------------------
CCMD(nextmap)1034 CCMD(nextmap)
1035 {
1036 	if (netgame)
1037 	{
1038 		Printf ("Use " TEXTCOLOR_BOLD "changemap" TEXTCOLOR_NORMAL " instead. " TEXTCOLOR_BOLD "Nextmap"
1039 				TEXTCOLOR_NORMAL " is for single-player only.\n");
1040 		return;
1041 	}
1042 
1043 	if (level.NextMap.Len() > 0 && level.NextMap.Compare("enDSeQ", 6))
1044 	{
1045 		G_DeferedInitNew(level.NextMap);
1046 	}
1047 	else
1048 	{
1049 		Printf("no next map!\n");
1050 	}
1051 }
1052 
1053 //-----------------------------------------------------------------------------
1054 //
1055 //
1056 //
1057 //-----------------------------------------------------------------------------
CCMD(nextsecret)1058 CCMD(nextsecret)
1059 {
1060 	if (netgame)
1061 	{
1062 		Printf ("Use " TEXTCOLOR_BOLD "changemap" TEXTCOLOR_NORMAL " instead. " TEXTCOLOR_BOLD "Nextsecret"
1063 				TEXTCOLOR_NORMAL " is for single-player only.\n");
1064 		return;
1065 	}
1066 
1067 	if (level.NextSecretMap.Len() > 0 && level.NextSecretMap.Compare("enDSeQ", 6))
1068 	{
1069 		G_DeferedInitNew(level.NextSecretMap);
1070 	}
1071 	else
1072 	{
1073 		Printf("no next secret map!\n");
1074 	}
1075 }
1076 
1077 //-----------------------------------------------------------------------------
1078 //
1079 //
1080 //
1081 //-----------------------------------------------------------------------------
1082 
CCMD(currentpos)1083 CCMD(currentpos)
1084 {
1085 	AActor *mo = players[consoleplayer].mo;
1086 	if(mo)
1087 	{
1088 		Printf("Current player position: (%1.3f,%1.3f,%1.3f), angle: %1.3f, floorheight: %1.3f, sector:%d, lightlevel: %d\n",
1089 			FIXED2FLOAT(mo->X()), FIXED2FLOAT(mo->Y()), FIXED2FLOAT(mo->Z()), mo->angle/float(ANGLE_1), FIXED2FLOAT(mo->floorz), mo->Sector->sectornum, mo->Sector->lightlevel);
1090 	}
1091 	else
1092 	{
1093 		Printf("You are not in game!");
1094 	}
1095 }
1096 
1097 //-----------------------------------------------------------------------------
1098 //
1099 // Print secret info (submitted by Karl Murks)
1100 //
1101 //-----------------------------------------------------------------------------
1102 
PrintSecretString(const char * string,bool thislevel)1103 static void PrintSecretString(const char *string, bool thislevel)
1104 {
1105 	const char *colstr = thislevel? TEXTCOLOR_YELLOW : TEXTCOLOR_CYAN;
1106 	if (string != NULL)
1107 	{
1108 		if (*string == '$')
1109 		{
1110 			if (string[1] == 'S' || string[1] == 's')
1111 			{
1112 				long secnum = strtol(string+2, (char**)&string, 10);
1113 				if (*string == ';') string++;
1114 				if (thislevel && secnum >= 0 && secnum < numsectors)
1115 				{
1116 					if (sectors[secnum].isSecret()) colstr = TEXTCOLOR_RED;
1117 					else if (sectors[secnum].wasSecret()) colstr = TEXTCOLOR_GREEN;
1118 					else colstr = TEXTCOLOR_ORANGE;
1119 				}
1120 			}
1121 			else if (string[1] == 'T' || string[1] == 't')
1122 			{
1123 				long tid = strtol(string+2, (char**)&string, 10);
1124 				if (*string == ';') string++;
1125 				FActorIterator it(tid);
1126 				AActor *actor;
1127 				bool foundone = false;
1128 				if (thislevel)
1129 				{
1130 					while ((actor = it.Next()))
1131 					{
1132 						if (!actor->IsKindOf(PClass::FindClass("SecretTrigger"))) continue;
1133 						foundone = true;
1134 						break;
1135 					}
1136 				}
1137 				if (foundone) colstr = TEXTCOLOR_RED;
1138 				else colstr = TEXTCOLOR_GREEN;
1139 			}
1140 		}
1141 		FBrokenLines *brok = V_BreakLines(ConFont, screen->GetWidth()*95/100, string);
1142 
1143 		for (int k = 0; brok[k].Width >= 0; k++)
1144 		{
1145 			Printf("%s%s\n", colstr, brok[k].Text.GetChars());
1146 		}
1147 		V_FreeBrokenLines(brok);
1148 	}
1149 }
1150 
1151 //============================================================================
1152 //
1153 // Print secret hints
1154 //
1155 //============================================================================
1156 
CCMD(secret)1157 CCMD(secret)
1158 {
1159 	const char *mapname = argv.argc() < 2? level.MapName.GetChars() : argv[1];
1160 	bool thislevel = !stricmp(mapname, level.MapName);
1161 	bool foundsome = false;
1162 
1163 	int lumpno=Wads.CheckNumForName("SECRETS");
1164 	if (lumpno < 0) return;
1165 
1166 	FWadLump lump = Wads.OpenLumpNum(lumpno);
1167 	FString maphdr;
1168 	maphdr.Format("[%s]", mapname);
1169 
1170 	FString linebuild;
1171 	char readbuffer[1024];
1172 	bool inlevel = false;
1173 
1174 	while (lump.Gets(readbuffer, 1024))
1175 	{
1176 		if (!inlevel)
1177 		{
1178 			if (readbuffer[0] == '[')
1179 			{
1180 				inlevel = !strnicmp(readbuffer, maphdr, maphdr.Len());
1181 				if (!foundsome)
1182 				{
1183 					FString levelname;
1184 					level_info_t *info = FindLevelInfo(mapname);
1185 					const char *ln = !(info->flags & LEVEL_LOOKUPLEVELNAME)? info->LevelName.GetChars() : GStrings[info->LevelName.GetChars()];
1186 					levelname.Format("%s - %s\n", mapname, ln);
1187 					size_t llen = levelname.Len() - 1;
1188 					for(size_t ii=0; ii<llen; ii++) levelname += '-';
1189 					Printf(TEXTCOLOR_YELLOW"%s\n", levelname.GetChars());
1190 					foundsome = true;
1191 				}
1192 			}
1193 			continue;
1194 		}
1195 		else
1196 		{
1197 			if (readbuffer[0] != '[')
1198 			{
1199 				linebuild += readbuffer;
1200 				if (linebuild.Len() < 1023 || linebuild[1022] == '\n')
1201 				{
1202 					// line complete so print it.
1203 					linebuild.Substitute("\r", "");
1204 					linebuild.StripRight(" \t\n");
1205 					PrintSecretString(linebuild, thislevel);
1206 					linebuild = "";
1207 				}
1208 			}
1209 			else inlevel = false;
1210 		}
1211 	}
1212 }
1213