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