1 /* NetHack 3.7 wizcmds.c $NHDT-Date: 1736530208 2025/01/10 09:30:08 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.21 $ */
2 /*-Copyright (c) Robert Patrick Rankin, 2024. */
3 /* NetHack may be freely redistributed. See license for details. */
8 extern const char unavailcmd
[]; /* cmd.c [27] */
9 extern const char *levltyp
[MAX_TYPE
+ 2]; /* cmd.c */
11 staticfn
int size_monst(struct monst
*, boolean
);
12 staticfn
int size_obj(struct obj
*);
13 staticfn
void count_obj(struct obj
*, long *, long *, boolean
, boolean
);
14 staticfn
void obj_chain(winid
, const char *, struct obj
*, boolean
, long *,
16 staticfn
void mon_invent_chain(winid
, const char *, struct monst
*, long *,
18 staticfn
void mon_chain(winid
, const char *, struct monst
*, boolean
, long *,
20 staticfn
void contained_stats(winid
, const char *, long *, long *);
21 staticfn
void misc_stats(winid
, long *, long *);
22 staticfn
void you_sanity_check(void);
23 staticfn
void levl_sanity_check(void);
24 staticfn
void makemap_unmakemon(struct monst
*, boolean
);
25 staticfn
int QSORTCALLBACK
migrsort_cmp(const genericptr
, const genericptr
);
26 staticfn
void list_migrating_mons(d_level
*);
28 DISABLE_WARNING_FORMAT_NONLITERAL
30 /* #wizwish command - wish for something */
32 wiz_wish(void) /* Unlimited wishes for debug mode by Paul Polderman */
35 boolean save_verbose
= flags
.verbose
;
37 flags
.verbose
= FALSE
;
39 flags
.verbose
= save_verbose
;
40 (void) encumber_msg();
42 pline(unavailcmd
, ecname_from_fn(wiz_wish
));
46 DISABLE_WARNING_FORMAT_NONLITERAL
48 /* #wizidentify command - reveal and optionally identify hero's inventory */
53 iflags
.override_ID
= (int) cmd_from_func(wiz_identify
);
54 /* command remapping might leave #wizidentify as the only way
55 to invoke us, in which case cmd_from_func() will yield NUL;
56 it won't matter to display_inventory()/display_pickinv()
57 if ^I invokes some other command--what matters is that
58 display_pickinv() and xname() see override_ID as nonzero */
59 if (!iflags
.override_ID
)
60 iflags
.override_ID
= C('I');
61 (void) display_inventory((char *) 0, FALSE
);
62 iflags
.override_ID
= 0;
64 pline(unavailcmd
, ecname_from_fn(wiz_identify
));
68 RESTORE_WARNING_FORMAT_NONLITERAL
70 /* used when wiz_makemap() gets rid of monsters for the old incarnation of
71 a level before creating a new incarnation of it */
73 makemap_unmakemon(struct monst
*mtmp
, boolean migratory
)
75 int ndx
= monsndx(mtmp
->data
);
77 /* uncreate any unique monster so that it is eligible to be remade
78 on the new incarnation of the level; ignores DEADMONSTER() [why?] */
79 if (mtmp
->data
->geno
& G_UNIQ
)
80 svm
.mvitals
[ndx
].mvflags
&= ~G_EXTINCT
;
81 if (svm
.mvitals
[ndx
].born
)
82 svm
.mvitals
[ndx
].born
--;
84 /* vault is going away; get rid of guard who might be in play or
85 be parked at <0,0>; for the latter, might already be flagged as
86 dead but is being kept around because of the 'isgd' flag */
88 mtmp
->isgd
= 0; /* after this, fall through to mongone() */
89 } else if (DEADMONSTER(mtmp
)) {
90 return; /* already set to be discarded */
91 } else if (mtmp
->isshk
&& on_level(&u
.uz
, &ESHK(mtmp
)->shoplevel
)) {
95 /* caller has removed 'mtmp' from migrating_mons; put it onto fmon
96 so that dmonsfree() bookkeeping for number of dead or removed
97 monsters won't get out of sync; it is not on the map but
98 mongone() -> m_detach() -> mon_leaving_level() copes with that */
99 mtmp
->mstate
|= MON_OFFMAP
;
100 mtmp
->mstate
&= ~(MON_MIGRATING
| MON_LIMBO
| MON_ENDGAME_MIGR
);
107 /* get rid of the all the monsters on--or intimately involved with--current
108 level; used when #wizmakemap destroys the level before replacing it */
110 makemap_remove_mons(void)
112 struct monst
*mtmp
, **mprev
;
114 /* keep steed and other adjacent pets after releasing them
115 from traps, stopping eating, &c as if hero were ascending */
116 keepdogs(TRUE
); /* (pets-only; normally we'd be using 'FALSE') */
117 /* get rid of all the monsters that didn't make it to 'mydogs' */
118 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
119 /* if already dead, dmonsfree(below) will get rid of it */
120 if (DEADMONSTER(mtmp
))
122 makemap_unmakemon(mtmp
, FALSE
);
124 /* some monsters retain details of this level in mon->mextra; that
125 data becomes invalid when the level is replaced by a new one;
126 get rid of them now if migrating or already arrived elsewhere;
127 [when on their 'home' level, the previous loop got rid of them;
128 if they aren't actually migrating but have been placed on some
129 'away' level, such monsters are treated like the Wizard: kept
130 on migrating monsters list, scheduled to migrate back to their
131 present location instead of being saved with whatever level they
132 happen to be on; see keepdogs() and keep_mon_accessible(dog.c)] */
133 for (mprev
= &gm
.migrating_mons
; (mtmp
= *mprev
) != 0; ) {
135 && ((mtmp
->isshk
&& on_level(&u
.uz
, &ESHK(mtmp
)->shoplevel
))
136 || (mtmp
->ispriest
&& on_level(&u
.uz
, &EPRI(mtmp
)->shrlevel
))
137 || (mtmp
->isgd
&& on_level(&u
.uz
, &EGD(mtmp
)->gdlevel
)))) {
139 makemap_unmakemon(mtmp
, TRUE
);
144 /* release dead and 'unmade' monsters */
147 impossible("makemap_remove_mons: 'fmon' did not get emptied?");
152 DISABLE_WARNING_FORMAT_NONLITERAL
154 /* #wizmakemap - discard current dungeon level and replace with a new one */
159 boolean was_in_W_tower
= In_W_tower(u
.ux
, u
.uy
, &u
.uz
);
161 makemap_prepost(TRUE
, was_in_W_tower
);
162 /* create a new level; various things like bestowing a guardian
163 angel on Astral or setting off alarm on Ft.Ludios are handled
164 by goto_level(do.c) so won't occur for replacement levels */
166 makemap_prepost(FALSE
, was_in_W_tower
);
168 pline(unavailcmd
, ecname_from_fn(wiz_makemap
));
173 /* the #wizmap command - reveal the level map
174 and any traps or engravings on it */
181 long save_Hconf
= HConfusion
, save_Hhallu
= HHallucination
;
184 HConfusion
= HHallucination
= 0L;
185 for (t
= gf
.ftrap
; t
!= 0; t
= t
->ntrap
) {
189 for (ep
= head_engr
; ep
!= 0; ep
= ep
->nxt_engr
) {
190 map_engraving(ep
, TRUE
);
194 HConfusion
= save_Hconf
;
195 HHallucination
= save_Hhallu
;
197 pline(unavailcmd
, ecname_from_fn(wiz_map
));
201 /* #wizgenesis - generate monster(s); a count prefix will be honored */
206 boolean mongen_saved
= iflags
.debug_mongen
;
208 iflags
.debug_mongen
= FALSE
;
209 (void) create_particular();
210 iflags
.debug_mongen
= mongen_saved
;
212 pline(unavailcmd
, ecname_from_fn(wiz_genesis
));
216 /* #wizwhere command - display dungeon layout */
221 (void) print_dungeon(FALSE
, (schar
*) 0, (xint16
*) 0);
223 pline(unavailcmd
, ecname_from_fn(wiz_where
));
227 /* the #wizdetect command - detect secret doors, traps, hidden monsters */
234 pline(unavailcmd
, ecname_from_fn(wiz_detect
));
238 RESTORE_WARNING_FORMAT_NONLITERAL
240 /* the #wizkill command - pick targets and reduce them to 0HP;
241 by default, the hero is credited/blamed; use 'm' prefix to avoid that */
248 char c
, qbuf
[QBUFSZ
];
249 const char *prompt
= "Pick first monster to slay";
250 boolean save_verbose
= flags
.verbose
,
251 save_autodescribe
= iflags
.autodescribe
;
252 d_level uarehere
= u
.uz
;
254 cc
.x
= u
.ux
, cc
.y
= u
.uy
;
256 pline("%s:", prompt
);
257 prompt
= "Next monster";
259 flags
.verbose
= FALSE
;
260 iflags
.autodescribe
= TRUE
;
261 ans
= getpos(&cc
, TRUE
, "a monster");
262 flags
.verbose
= save_verbose
;
263 iflags
.autodescribe
= save_autodescribe
;
264 if (ans
< 0 || cc
.x
< 1)
268 if (u_at(cc
.x
, cc
.y
)) {
270 Sprintf(qbuf
, "Kill %.110s?", mon_nam(u
.usteed
));
271 if ((c
= ynq(qbuf
)) == 'q')
277 Sprintf(qbuf
, "%s?", Role_if(PM_SAMURAI
) ? "Perform seppuku"
279 if (paranoid_query(TRUE
, qbuf
)) {
280 Sprintf(svk
.killer
.name
, "%s own player", uhis());
281 svk
.killer
.format
= KILLED_BY
;
286 } else if (u
.uswallow
) {
287 mtmp
= next2u(cc
.x
, cc
.y
) ? u
.ustuck
: 0;
289 mtmp
= m_at(cc
.x
, cc
.y
);
292 /* whether there's an unseen monster here or not, player will know
293 that there's no monster here after the kill or failed attempt;
295 (void) unmap_invisible(cc
.x
, cc
.y
);
298 /* we don't require that the monster be seen or sensed so
299 we issue our own message in order to name it in case it
300 isn't; note that if it triggers other kills, those might
301 be referred to as "it" */
302 int tame
= !!mtmp
->mtame
,
303 seen
= (canspotmon(mtmp
) || (u
.uswallow
&& mtmp
== u
.ustuck
)),
304 flgs
= (SUPPRESS_IT
| SUPPRESS_HALLUCINATION
305 | ((tame
&& has_mgivenname(mtmp
)) ? SUPPRESS_SADDLE
307 articl
= tame
? ARTICLE_YOUR
: seen
? ARTICLE_THE
: ARTICLE_A
;
308 const char *adjs
= tame
? (!seen
? "poor, unseen" : "poor")
309 : (!seen
? "unseen" : (const char *) 0);
310 char *Mn
= x_monnam(mtmp
, articl
, adjs
, flgs
, FALSE
);
312 if (!iflags
.menu_requested
) {
313 /* normal case: hero is credited/blamed */
314 You("%s %s!", nonliving(mtmp
->data
) ? "destroy" : "kill", Mn
);
315 xkilled(mtmp
, XKILL_NOMSG
);
316 } else { /* 'm'-prefix */
317 /* we know that monsters aren't moving because player has
318 just issued this #wizkill command, but if 'mtmp' is a
319 gas spore whose explosion kills any other monsters we
320 need to have the mon_moving flag be True in order to
321 avoid blaming or crediting hero for their deaths */
322 svc
.context
.mon_moving
= TRUE
;
323 pline("%s is %s.", upstart(Mn
),
324 nonliving(mtmp
->data
) ? "destroyed" : "killed");
325 /* Null second arg suppresses the usual message */
326 monkilled(mtmp
, (char *) 0, AD_PHYS
);
327 svc
.context
.mon_moving
= FALSE
;
329 /* end targetting loop if an engulfer dropped hero onto a level-
331 if (u
.utotype
|| !on_level(&u
.uz
, &uarehere
))
334 There("is no monster there.");
338 /* since #wizkill takes no game time, it is possible to kill something
339 in the main dungeon and immediately level teleport into the endgame
340 which will delete the main dungeon's level files; avoid triggering
341 impossible "dmonsfree: 0 removed doesn't match N pending" by forcing
342 dead monster cleanup; we don't track whether anything was actually
343 killed above--if nothing was, this will be benign */
345 /* distinction between ECMD_CANCEL and ECMD_OK is unimportant here */
346 return ECMD_OK
; /* no time elapses */
349 DISABLE_WARNING_FORMAT_NONLITERAL
351 /* the #wizloadlua command - load an arbitrary lua file */
357 /* Large but not unlimited memory and CPU so random bits of
358 * code can be tested by wizards. */
359 nhl_sandbox_info sbi
= {NHL_SB_SAFE
| NHL_SB_DEBUGGING
,
360 16*1024*1024, 0, 16*1024*1024};
363 getlin("Load which lua file?", buf
);
364 if (buf
[0] == '\033' || buf
[0] == '\0')
366 if (!strchr(buf
, '.'))
368 (void) load_lua(buf
, &sbi
);
370 pline(unavailcmd
, ecname_from_fn(wiz_load_lua
));
374 /* the #wizloaddes command - load a special level lua file */
382 getlin("Load which des lua file?", buf
);
383 if (buf
[0] == '\033' || buf
[0] == '\0')
385 if (!strchr(buf
, '.'))
388 lspo_reset_level(NULL
);
389 (void) load_special(buf
);
390 lspo_finalize_level(NULL
);
393 pline(unavailcmd
, ecname_from_fn(wiz_load_splua
));
397 /* the #wizlevelport command - level teleport */
404 pline(unavailcmd
, ecname_from_fn(wiz_level_tele
));
408 RESTORE_WARNING_FORMAT_NONLITERAL
410 /* #wizfliplevel - transpose the current level */
414 static const char choices
[] = "0123",
415 prmpt
[] = "Flip 0=randomly, 1=vertically, 2=horizontally, 3=both:";
421 * migrating monsters aimed at returning to specific coordinates
423 * as flipping is normally done only during level creation.
426 char c
= yn_function(prmpt
, choices
, '\0', TRUE
);
428 if (c
&& strchr(choices
, c
)) {
432 flip_level_rnd(3, TRUE
);
434 flip_level((int) c
, TRUE
);
438 pline("%s", Never_mind
);
444 /* #levelchange command - adjust hero's experience level */
446 wiz_level_change(void)
448 char buf
[BUFSZ
], dummy
= '\0';
452 buf
[0] = '\0'; /* in case EDIT_GETLIN is enabled */
453 getlin("To what experience level do you want to be set?", buf
);
454 (void) mungspaces(buf
);
455 if (buf
[0] == '\033' || buf
[0] == '\0')
458 ret
= sscanf(buf
, "%d%c", &newlevel
, &dummy
);
464 if (newlevel
== u
.ulevel
) {
465 You("are already that experienced.");
466 } else if (newlevel
< u
.ulevel
) {
468 You("are already as inexperienced as you can get.");
473 while (u
.ulevel
> newlevel
)
474 losexp("#levelchange");
476 if (u
.ulevel
>= MAXULEV
) {
477 You("are already as experienced as you can get.");
480 if (newlevel
> MAXULEV
)
482 while (u
.ulevel
< newlevel
)
485 /* blessed full healing or restore ability won't fix any lost levels */
486 u
.ulevelmax
= u
.ulevel
;
490 DISABLE_WARNING_CONDEXPR_IS_CONSTANT
492 /* #wiztelekinesis */
494 wiz_telekinesis(void)
498 struct monst
*mtmp
= (struct monst
*) 0;
503 pline("Pick a monster to hurtle.");
505 ans
= getpos(&cc
, TRUE
, "a monster");
506 if (ans
< 0 || cc
.x
< 1)
509 if ((((mtmp
= m_at(cc
.x
, cc
.y
)) != 0) && canspotmon(mtmp
))
510 || u_at(cc
.x
, cc
.y
)) {
511 if (!getdir("which direction?"))
515 mhurtle(mtmp
, u
.dx
, u
.dy
, 6);
516 if (!DEADMONSTER(mtmp
) && canspotmon(mtmp
)) {
521 hurtle(u
.dx
, u
.dy
, 6, FALSE
);
522 cc
.x
= u
.ux
, cc
.y
= u
.uy
;
526 } while (u
.utotype
== UTOTYPE_NONE
);
530 RESTORE_WARNING_CONDEXPR_IS_CONSTANT
532 /* #panic command - test program's panic handling */
536 if (iflags
.debug_fuzzer
) {
537 u
.uhp
= u
.uhpmax
= 1000;
538 u
.uen
= u
.uenmax
= 1000;
541 if (paranoid_query(TRUE
,
542 "Do you want to call panic() and end your game?"))
543 panic("Crash test (#panic).");
547 /* #debugfuzzer command - fuzztest the program */
551 if (flags
.suppress_alert
< FEATURE_NOTICE_VER(3,7,0)) {
552 pline("The fuzz tester will make NetHack execute random keypresses.");
553 There("is no conventional way out of this mode.");
555 if (paranoid_query(TRUE
, "Do you want to start fuzz testing?")) {
556 /* Thoth, take the reins */
557 if (y_n("Do you want to call panic() after impossible()?") == 'n') {
558 iflags
.debug_fuzzer
= fuzzer_impossible_continue
;
560 iflags
.debug_fuzzer
= fuzzer_impossible_panic
;
566 /* #polyself command - change hero's form */
570 polyself(POLY_CONTROLLED
);
579 coordxy x
, y
, startx
, stopx
, curx
;
583 win
= create_nhwindow(NHW_TEXT
);
585 * Each seenv description takes up 2 characters, so center
586 * the seenv display around the hero.
588 startx
= max(1, u
.ux
- (COLNO
/ 4));
589 stopx
= min(startx
+ (COLNO
/ 2), COLNO
);
590 /* can't have a line exactly 80 chars long */
591 if (stopx
- startx
== COLNO
/ 2)
594 for (y
= 0; y
< ROWNO
; y
++) {
595 for (x
= startx
, curx
= 0; x
< stopx
; x
++, curx
+= 2) {
597 row
[curx
] = row
[curx
+ 1] = '@';
599 v
= levl
[x
][y
].seenv
& 0xff;
601 row
[curx
] = row
[curx
+ 1] = ' ';
603 Sprintf(&row
[curx
], "%02x", v
);
606 /* remove trailing spaces */
607 for (x
= curx
- 1; x
>= 0; x
--)
614 display_nhwindow(win
, TRUE
);
615 destroy_nhwindow(win
);
619 /* #vision command */
621 wiz_show_vision(void)
628 win
= create_nhwindow(NHW_TEXT
);
629 Sprintf(row
, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit",
630 COULD_SEE
, IN_SIGHT
, TEMP_LIT
);
633 for (y
= 0; y
< ROWNO
; y
++) {
634 for (x
= 1; x
< COLNO
; x
++) {
638 v
= gv
.viz_array
[y
][x
]; /* data access should be hidden */
639 row
[x
] = (v
== 0) ? ' ' : ('0' + v
);
642 /* remove trailing spaces */
643 for (x
= COLNO
- 1; x
>= 1; x
--)
648 putstr(win
, 0, &row
[1]);
650 display_nhwindow(win
, TRUE
);
651 destroy_nhwindow(win
);
657 wiz_show_wmodes(void)
663 boolean istty
= WINDOWPORT(tty
);
665 win
= create_nhwindow(NHW_TEXT
);
667 putstr(win
, 0, ""); /* tty only: blank top line */
668 for (y
= 0; y
< ROWNO
; y
++) {
669 for (x
= 0; x
< COLNO
; x
++) {
673 else if (IS_WALL(lev
->typ
) || lev
->typ
== SDOOR
)
674 row
[x
] = '0' + (lev
->wall_info
& WM_MASK
);
675 else if (lev
->typ
== CORR
)
677 else if (IS_ROOM(lev
->typ
) || IS_DOOR(lev
->typ
))
683 /* map column 0, levl[0][], is off the left edge of the screen */
684 putstr(win
, 0, &row
[1]);
686 display_nhwindow(win
, TRUE
);
687 destroy_nhwindow(win
);
691 /* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */
693 wiz_map_levltyp(void)
699 boolean istty
= !strcmp(windowprocs
.name
, "tty");
701 win
= create_nhwindow(NHW_TEXT
);
702 /* map row 0, levl[][0], is drawn on the second line of tty screen */
704 putstr(win
, 0, ""); /* tty only: blank top line */
705 for (y
= 0; y
< ROWNO
; y
++) {
706 /* map column 0, levl[0][], is off the left edge of the screen;
707 it should always have terrain type "undiggable stone" */
708 for (x
= 1; x
< COLNO
; x
++) {
709 terrain
= levl
[x
][y
].typ
;
710 /* assumes there aren't more than 10+26+26 terrain types */
711 row
[x
- 1] = (char) ((terrain
== STONE
&& !may_dig(x
, y
))
717 : 'A' + terrain
- 36);
720 if (levl
[0][y
].typ
!= STONE
|| may_dig(0, y
))
728 s_level
*slev
= Is_special(&u
.uz
);
730 Sprintf(dsc
, "D:%d,L:%d", u
.uz
.dnum
, u
.uz
.dlevel
);
731 /* [dungeon branch features currently omitted] */
732 /* special level features */
734 Sprintf(eos(dsc
), " \"%s\"", slev
->proto
);
735 /* special level flags (note: dungeon.def doesn't set `maze'
736 or `hell' for any specific levels so those never show up) */
737 if (slev
->flags
.maze_like
)
738 Strcat(dsc
, " mazelike");
739 if (slev
->flags
.hellish
)
740 Strcat(dsc
, " hellish");
741 if (slev
->flags
.town
)
742 Strcat(dsc
, " town");
743 if (slev
->flags
.rogue_like
)
744 Strcat(dsc
, " roguelike");
745 /* alignment currently omitted to save space */
748 if (svl
.level
.flags
.nfountains
)
749 Sprintf(eos(dsc
), " %c:%d", defsyms
[S_fountain
].sym
,
750 (int) svl
.level
.flags
.nfountains
);
751 if (svl
.level
.flags
.nsinks
)
752 Sprintf(eos(dsc
), " %c:%d", defsyms
[S_sink
].sym
,
753 (int) svl
.level
.flags
.nsinks
);
754 if (svl
.level
.flags
.has_vault
)
755 Strcat(dsc
, " vault");
756 if (svl
.level
.flags
.has_shop
)
757 Strcat(dsc
, " shop");
758 if (svl
.level
.flags
.has_temple
)
759 Strcat(dsc
, " temple");
760 if (svl
.level
.flags
.has_court
)
761 Strcat(dsc
, " throne");
762 if (svl
.level
.flags
.has_zoo
)
764 if (svl
.level
.flags
.has_morgue
)
765 Strcat(dsc
, " morgue");
766 if (svl
.level
.flags
.has_barracks
)
767 Strcat(dsc
, " barracks");
768 if (svl
.level
.flags
.has_beehive
)
769 Strcat(dsc
, " hive");
770 if (svl
.level
.flags
.has_swamp
)
771 Strcat(dsc
, " swamp");
773 if (svl
.level
.flags
.noteleport
)
774 Strcat(dsc
, " noTport");
775 if (svl
.level
.flags
.hardfloor
)
776 Strcat(dsc
, " noDig");
777 if (svl
.level
.flags
.nommap
)
778 Strcat(dsc
, " noMMap");
779 if (!svl
.level
.flags
.hero_memory
)
780 Strcat(dsc
, " noMem");
781 if (svl
.level
.flags
.shortsighted
)
782 Strcat(dsc
, " shortsight");
783 if (svl
.level
.flags
.graveyard
)
784 Strcat(dsc
, " graveyard");
785 if (svl
.level
.flags
.is_maze_lev
)
786 Strcat(dsc
, " maze");
787 if (svl
.level
.flags
.is_cavernous_lev
)
788 Strcat(dsc
, " cave");
789 if (svl
.level
.flags
.arboreal
)
790 Strcat(dsc
, " tree");
792 Strcat(dsc
, " sokoban-rules");
793 /* non-flag info; probably should include dungeon branching
794 checks (extra stairs and magic portals) here */
795 if (Invocation_lev(&u
.uz
))
796 Strcat(dsc
, " invoke");
797 if (On_W_tower_level(&u
.uz
))
798 Strcat(dsc
, " tower");
799 /* append a branch identifier for completeness' sake */
801 Strcat(dsc
, " dungeon");
802 else if (u
.uz
.dnum
== mines_dnum
)
803 Strcat(dsc
, " mines");
804 else if (In_sokoban(&u
.uz
))
805 Strcat(dsc
, " sokoban");
806 else if (u
.uz
.dnum
== quest_dnum
)
807 Strcat(dsc
, " quest");
808 else if (Is_knox(&u
.uz
))
809 Strcat(dsc
, " ludios");
810 else if (u
.uz
.dnum
== 1)
811 Strcat(dsc
, " gehennom");
812 else if (u
.uz
.dnum
== tower_dnum
)
813 Strcat(dsc
, " vlad");
814 else if (In_endgame(&u
.uz
))
815 Strcat(dsc
, " endgame");
817 /* somebody's added a dungeon branch we're not expecting */
818 const char *brname
= svd
.dungeons
[u
.uz
.dnum
].dname
;
820 if (!brname
|| !*brname
)
822 if (!strncmpi(brname
, "the ", 4))
824 Sprintf(eos(dsc
), " %s", brname
);
826 /* limit the line length to map width */
827 if (strlen(dsc
) >= COLNO
)
828 dsc
[COLNO
- 1] = '\0'; /* truncate */
832 display_nhwindow(win
, TRUE
);
833 destroy_nhwindow(win
);
837 DISABLE_WARNING_FORMAT_NONLITERAL
839 /* explanation of base-36 output from wiz_map_levltyp() */
841 wiz_levltyp_legend(void)
845 const char *dsc
, *fmt
;
848 win
= create_nhwindow(NHW_TEXT
);
849 putstr(win
, 0, "#terrain encodings:");
851 fmt
= " %c - %-28s"; /* TODO: include tab-separated variant for win32 */
853 /* output in pairs, left hand column holds [0],[1],...,[N/2-1]
854 and right hand column holds [N/2],[N/2+1],...,[N-1];
855 N ('last') will always be even, and may or may not include
856 the empty string entry to pad out the final pair, depending
857 upon how many other entries are present in levltyp[] */
858 last
= SIZE(levltyp
) & ~1;
859 for (i
= 0; i
< last
/ 2; ++i
)
860 for (j
= i
; j
< last
; j
+= last
/ 2) {
863 : !strncmp(dsc
, "unreachable", 11) ? '*'
864 /* same int-to-char conversion as wiz_map_levltyp() */
866 : (j
< 36) ? 'a' + j
- 10
868 Sprintf(eos(buf
), fmt
, c
, dsc
);
874 display_nhwindow(win
, TRUE
);
875 destroy_nhwindow(win
);
879 RESTORE_WARNING_FORMAT_NONLITERAL
881 DISABLE_WARNING_CONDEXPR_IS_CONSTANT
883 /* #wizsmell command - test usmellmon(). */
887 struct monst
*mtmp
; /* monster being smelled */
888 struct permonst
*mptr
;
890 coord cc
; /* screen pos to sniff */
895 if (!olfaction(gy
.youmonst
.data
)) {
896 You("are incapable of detecting odors in your present form.");
900 You("can move the cursor to a monster that you want to smell.");
902 pline("Pick a monster to smell.");
903 ans
= getpos(&cc
, TRUE
, "a monster");
904 if (ans
< 0 || cc
.x
< 0) {
905 return ECMD_CANCEL
; /* done */
908 if (u_at(cc
.x
, cc
.y
)) {
910 mptr
= u
.usteed
->data
;
912 mptr
= gy
.youmonst
.data
;
915 } else if ((mtmp
= m_at(cc
.x
, cc
.y
)) != (struct monst
*) 0) {
918 mptr
= (struct permonst
*) 0;
920 /* Buglet: mapping or unmapping "remembered, unseen monster" should
921 cause time to elapse; since we're in wizmode, don't bother */
922 glyph
= glyph_at(cc
.x
, cc
.y
);
923 /* Is it a monster? */
926 You("surreptitiously sniff under your %s.", body_part(ARM
));
927 if (!usmellmon(mptr
))
928 pline("%s to not give off any smell.",
929 is_you
? "You seem" : "That monster seems");
930 if (!glyph_is_monster(glyph
))
931 map_invisible(cc
.x
, cc
.y
);
933 You("don't smell any monster there.");
934 if (glyph_is_invisible(glyph
))
935 unmap_invisible(cc
.x
, cc
.y
);
941 RESTORE_WARNING_CONDEXPR_IS_CONSTANT
943 DISABLE_WARNING_FORMAT_NONLITERAL
945 #define DEFAULT_TIMEOUT_INCR 30
947 /* #wizinstrinsic command to set some intrinsics for testing */
952 static const char wizintrinsic
[] = "#wizintrinsic";
953 static const char fmt
[] = "You are%s %s.";
957 int i
, j
, n
, amt
, typ
, p
= 0;
958 long oldtimeout
, newtimeout
;
959 const char *propname
;
960 menu_item
*pick_list
= (menu_item
*) 0;
964 win
= create_nhwindow(NHW_MENU
);
965 start_menu(win
, MENU_BEHAVE_STANDARD
);
966 if (iflags
.cmdassist
) {
967 /* start menu with a subtitle */
969 "[Precede any selection with a count to increment by other than %d.]",
970 DEFAULT_TIMEOUT_INCR
);
971 add_menu_str(win
, buf
);
973 for (i
= 0; (propname
= property_by_index(i
, &p
)) != 0; ++i
) {
974 if (p
== HALLUC_RES
) {
975 /* Grayswandir vs hallucination; ought to be redone to
976 use u.uprops[HALLUC].blocked instead of being treated
977 as a separate property; letting in be manually toggled
978 even only in wizard mode would be asking for trouble... */
982 /* FIRE_RES and properties beyond it (in the propertynames[]
983 ordering, not their numerical PROP values), can only be
984 set to timed values here so show a separator */
985 add_menu_str(win
, "--");
987 any
.a_int
= i
+ 1; /* +1: avoid 0 */
988 oldtimeout
= u
.uprops
[p
].intrinsic
& TIMEOUT
;
990 Sprintf(buf
, "%-27s [%li]", propname
, oldtimeout
);
992 Sprintf(buf
, "%s", propname
);
993 add_menu(win
, &nul_glyphinfo
, &any
, 0, 0, ATR_NONE
, clr
, buf
,
994 MENU_ITEMFLAGS_NONE
);
996 end_menu(win
, "Which intrinsics?");
997 n
= select_menu(win
, PICK_ANY
, &pick_list
);
998 destroy_nhwindow(win
);
1000 for (j
= 0; j
< n
; ++j
) {
1001 i
= pick_list
[j
].item
.a_int
- 1; /* -1: reverse +1 above */
1002 propname
= property_by_index(i
, &p
);
1003 oldtimeout
= u
.uprops
[p
].intrinsic
& TIMEOUT
;
1004 amt
= (pick_list
[j
].count
== -1L) ? DEFAULT_TIMEOUT_INCR
1005 : (int) pick_list
[j
].count
;
1006 if (amt
<= 0) /* paranoia */
1008 newtimeout
= oldtimeout
+ (long) amt
;
1014 if (oldtimeout
> 0L && newtimeout
> oldtimeout
)
1015 newtimeout
= oldtimeout
;
1021 make_blinded(newtimeout
, TRUE
);
1023 #if 0 /* make_confused() only gives feedback when confusion is
1024 * ending so use the 'default' case for it instead */
1026 make_confused(newtimeout
, TRUE
);
1030 make_deaf(newtimeout
, TRUE
);
1033 make_hallucinated(newtimeout
, TRUE
, 0L);
1036 typ
= !rn2(2) ? SICK_VOMITABLE
: SICK_NONVOMITABLE
;
1037 make_sick(newtimeout
, wizintrinsic
, TRUE
, typ
);
1041 !Slimed
? "" : " still", "turning into slime");
1042 make_slimed(newtimeout
, buf
);
1046 !Stoned
? "" : " still", "turning into stone");
1047 make_stoned(newtimeout
, buf
, KILLED_BY
, wizintrinsic
);
1050 make_stunned(newtimeout
, TRUE
);
1053 Sprintf(buf
, fmt
, !Vomiting
? "" : " still", "vomiting");
1054 make_vomiting(newtimeout
, FALSE
);
1059 svc
.context
.warntype
.speciesidx
= PM_GRID_BUG
;
1060 svc
.context
.warntype
.species
1061 = &mons
[svc
.context
.warntype
.speciesidx
];
1065 /* slippery fingers might need a persistent inventory update
1066 so needs more than simple incr_itimeout() but we want
1067 the pline() issued with that */
1068 make_glib((int) newtimeout
);
1074 incr_itimeout(&u
.uprops
[p
].intrinsic
, amt
);
1075 disp
.botl
= TRUE
; /* have pline() do a status update */
1076 pline("Timeout for %s %s %d.", propname
,
1077 oldtimeout
? "increased by" : "set to", amt
);
1080 /* this has to be after incr_itimeout() */
1081 if (p
== LEVITATION
|| p
== FLYING
)
1083 else if (p
== PROT_FROM_SHAPE_CHANGERS
)
1085 if (p
== WWALKING
|| p
== LEVITATION
|| p
== FLYING
) {
1087 (void) pooleffects(FALSE
);
1091 free((genericptr_t
) pick_list
);
1094 pline(unavailcmd
, ecname_from_fn(wiz_intrinsic
));
1098 RESTORE_WARNING_FORMAT_NONLITERAL
1100 /* #wizrumorcheck command - verify each rumor access */
1102 wiz_rumor_check(void)
1109 * wizard mode sanity_check code
1112 static const char template[] = "%-27s %4ld %6ld";
1113 static const char stats_hdr
[] = " count bytes";
1114 static const char stats_sep
[] = "--------------------------- ----- -------";
1117 size_obj(struct obj
*otmp
)
1119 int sz
= (int) sizeof (struct obj
);
1122 sz
+= (int) sizeof (struct oextra
);
1124 sz
+= (int) strlen(ONAME(otmp
)) + 1;
1126 sz
+= size_monst(OMONST(otmp
), FALSE
);
1128 sz
+= (int) strlen(OMAILCMD(otmp
)) + 1;
1129 /* sz += (int) sizeof (unsigned); -- now part of oextra itself */
1135 count_obj(struct obj
*chain
, long *total_count
, long *total_size
,
1136 boolean top
, boolean recurse
)
1141 for (count
= size
= 0, obj
= chain
; obj
; obj
= obj
->nobj
) {
1144 size
+= size_obj(obj
);
1146 if (recurse
&& obj
->cobj
)
1147 count_obj(obj
->cobj
, total_count
, total_size
, TRUE
, TRUE
);
1149 *total_count
+= count
;
1150 *total_size
+= size
;
1153 DISABLE_WARNING_FORMAT_NONLITERAL
/* RESTORE_WARNING follows wiz_show_stats */
1161 long *total_count
, long *total_size
)
1164 long count
= 0L, size
= 0L;
1166 count_obj(chain
, &count
, &size
, TRUE
, FALSE
);
1168 if (count
|| size
|| force
) {
1169 *total_count
+= count
;
1170 *total_size
+= size
;
1171 Sprintf(buf
, template, src
, count
, size
);
1172 putstr(win
, 0, buf
);
1180 struct monst
*chain
,
1181 long *total_count
, long *total_size
)
1184 long count
= 0, size
= 0;
1187 for (mon
= chain
; mon
; mon
= mon
->nmon
)
1188 count_obj(mon
->minvent
, &count
, &size
, TRUE
, FALSE
);
1190 if (count
|| size
) {
1191 *total_count
+= count
;
1192 *total_size
+= size
;
1193 Sprintf(buf
, template, src
, count
, size
);
1194 putstr(win
, 0, buf
);
1202 long *total_count
, long *total_size
)
1205 long count
= 0, size
= 0;
1208 count_obj(gi
.invent
, &count
, &size
, FALSE
, TRUE
);
1209 count_obj(fobj
, &count
, &size
, FALSE
, TRUE
);
1210 count_obj(svl
.level
.buriedobjlist
, &count
, &size
, FALSE
, TRUE
);
1211 count_obj(gm
.migrating_objs
, &count
, &size
, FALSE
, TRUE
);
1212 /* DEADMONSTER check not required in this loop since they have no
1214 for (mon
= fmon
; mon
; mon
= mon
->nmon
)
1215 count_obj(mon
->minvent
, &count
, &size
, FALSE
, TRUE
);
1216 for (mon
= gm
.migrating_mons
; mon
; mon
= mon
->nmon
)
1217 count_obj(mon
->minvent
, &count
, &size
, FALSE
, TRUE
);
1219 if (count
|| size
) {
1220 *total_count
+= count
;
1221 *total_size
+= size
;
1222 Sprintf(buf
, template, src
, count
, size
);
1223 putstr(win
, 0, buf
);
1228 size_monst(struct monst
*mtmp
, boolean incl_wsegs
)
1230 int sz
= (int) sizeof (struct monst
);
1232 if (mtmp
->wormno
&& incl_wsegs
)
1233 sz
+= size_wseg(mtmp
);
1236 sz
+= (int) sizeof (struct mextra
);
1237 if (MGIVENNAME(mtmp
))
1238 sz
+= (int) strlen(MGIVENNAME(mtmp
)) + 1;
1240 sz
+= (int) sizeof (struct egd
);
1242 sz
+= (int) sizeof (struct epri
);
1244 sz
+= (int) sizeof (struct eshk
);
1246 sz
+= (int) sizeof (struct emin
);
1248 sz
+= (int) sizeof (struct edog
);
1250 sz
+= (int) sizeof (struct ebones
);
1251 /* mextra->mcorpsenm doesn't point to more memory */
1260 struct monst
*chain
,
1262 long *total_count
, long *total_size
)
1267 /* mon->wormno means something different for migrating_mons and mydogs */
1268 boolean incl_wsegs
= !strcmpi(src
, "fmon");
1271 for (mon
= chain
; mon
; mon
= mon
->nmon
) {
1273 size
+= size_monst(mon
, incl_wsegs
);
1275 if (count
|| size
|| force
) {
1276 *total_count
+= count
;
1277 *total_size
+= size
;
1278 Sprintf(buf
, template, src
, count
, size
);
1279 putstr(win
, 0, buf
);
1286 long *total_count
, long *total_size
)
1288 char buf
[BUFSZ
], hdrbuf
[QBUFSZ
];
1292 struct damage
*sd
; /* shop damage */
1293 struct kinfo
*k
; /* delayed killer */
1294 struct cemetery
*bi
; /* bones info */
1296 /* traps and engravings are output unconditionally;
1297 * others only if nonzero
1300 for (tt
= gf
.ftrap
; tt
; tt
= tt
->ntrap
) {
1302 size
+= (long) sizeof *tt
;
1304 *total_count
+= count
;
1305 *total_size
+= size
;
1306 Sprintf(hdrbuf
, "traps, size %ld", (long) sizeof (struct trap
));
1307 Sprintf(buf
, template, hdrbuf
, count
, size
);
1308 putstr(win
, 0, buf
);
1311 engr_stats("engravings, size %ld+text", hdrbuf
, &count
, &size
);
1312 *total_count
+= count
;
1313 *total_size
+= size
;
1314 Sprintf(buf
, template, hdrbuf
, count
, size
);
1315 putstr(win
, 0, buf
);
1318 light_stats("light sources, size %ld", hdrbuf
, &count
, &size
);
1319 if (count
|| size
) {
1320 *total_count
+= count
;
1321 *total_size
+= size
;
1322 Sprintf(buf
, template, hdrbuf
, count
, size
);
1323 putstr(win
, 0, buf
);
1327 timer_stats("timers, size %ld", hdrbuf
, &count
, &size
);
1328 if (count
|| size
) {
1329 *total_count
+= count
;
1330 *total_size
+= size
;
1331 Sprintf(buf
, template, hdrbuf
, count
, size
);
1332 putstr(win
, 0, buf
);
1336 for (sd
= svl
.level
.damagelist
; sd
; sd
= sd
->next
) {
1338 size
+= (long) sizeof *sd
;
1340 if (count
|| size
) {
1341 *total_count
+= count
;
1342 *total_size
+= size
;
1343 Sprintf(hdrbuf
, "shop damage, size %ld",
1344 (long) sizeof (struct damage
));
1345 Sprintf(buf
, template, hdrbuf
, count
, size
);
1346 putstr(win
, 0, buf
);
1350 region_stats("regions, size %ld+%ld*rect+N", hdrbuf
, &count
, &size
);
1351 if (count
|| size
) {
1352 *total_count
+= count
;
1353 *total_size
+= size
;
1354 Sprintf(buf
, template, hdrbuf
, count
, size
);
1355 putstr(win
, 0, buf
);
1359 for (k
= svk
.killer
.next
; k
; k
= k
->next
) {
1361 size
+= (long) sizeof *k
;
1363 if (count
|| size
) {
1364 *total_count
+= count
;
1365 *total_size
+= size
;
1366 Sprintf(hdrbuf
, "delayed killer%s, size %ld",
1367 plur(count
), (long) sizeof (struct kinfo
));
1368 Sprintf(buf
, template, hdrbuf
, count
, size
);
1369 putstr(win
, 0, buf
);
1373 for (bi
= svl
.level
.bonesinfo
; bi
; bi
= bi
->next
) {
1375 size
+= (long) sizeof *bi
;
1377 if (count
|| size
) {
1378 *total_count
+= count
;
1379 *total_size
+= size
;
1380 Sprintf(hdrbuf
, "bones history, size %ld",
1381 (long) sizeof (struct cemetery
));
1382 Sprintf(buf
, template, hdrbuf
, count
, size
);
1383 putstr(win
, 0, buf
);
1387 for (idx
= 0; idx
< NUM_OBJECTS
; ++idx
)
1388 if (objects
[idx
].oc_uname
) {
1390 size
+= (long) (strlen(objects
[idx
].oc_uname
) + 1);
1392 if (count
|| size
) {
1393 *total_count
+= count
;
1394 *total_size
+= size
;
1395 Strcpy(hdrbuf
, "object type names, text");
1396 Sprintf(buf
, template, hdrbuf
, count
, size
);
1397 putstr(win
, 0, buf
);
1402 you_sanity_check(void)
1406 if (u
.uswallow
&& !u
.ustuck
) {
1407 /* this probably ought to be panic() */
1408 impossible("sanity_check: swallowed by nothing?");
1409 display_nhwindow(WIN_MESSAGE
, TRUE
);
1410 /* try to recover from whatever the problem is */
1415 if ((mtmp
= m_at(u
.ux
, u
.uy
)) != 0) {
1416 /* u.usteed isn't on the map */
1417 if (u
.ustuck
!= mtmp
)
1418 impossible("sanity_check: you over monster");
1420 /* [should we also check for (u.uhp < 1), (Upolyd && u.mh < 1),
1421 and (u.uen < 0) here?] */
1422 if (u
.uhp
> u
.uhpmax
) {
1423 impossible("current hero health (%d) better than maximum? (%d)",
1427 if (Upolyd
&& u
.mh
> u
.mhmax
) {
1429 "current hero health as monster (%d) better than maximum? (%d)",
1433 if (u
.uen
> u
.uenmax
) {
1434 impossible("current hero energy (%d) better than maximum? (%d)",
1439 check_wornmask_slots();
1440 (void) check_invent_gold("invent");
1444 levl_sanity_check(void)
1449 return; /* Underwater uses different vision */
1451 for (y
= 0; y
< ROWNO
; y
++) {
1452 for (x
= 1; x
< COLNO
; x
++) {
1453 if ((does_block(x
, y
, &levl
[x
][y
]) ? 1 : 0) != get_viz_clear(x
, y
))
1454 impossible("levl[%i][%i] vision blocking", x
, y
);
1462 if (iflags
.sanity_no_check
) {
1463 /* in case a recurring sanity_check warning occurs, we mustn't
1464 re-trigger it when ^P is used, otherwise msg_window:Single
1465 and msg_window:Combination will always repeat the most recent
1466 instance, never able to go back to any earlier messages */
1467 iflags
.sanity_no_check
= FALSE
;
1470 program_state
.in_sanity_check
++;
1473 timer_sanity_check();
1475 light_sources_sanity_check();
1477 trap_sanity_check();
1478 engraving_sanity_check();
1479 levl_sanity_check();
1480 program_state
.in_sanity_check
--;
1483 /* qsort() comparison routine for use in list_migrating_mons() */
1484 staticfn
int QSORTCALLBACK
1485 migrsort_cmp(const genericptr vptr1
, const genericptr vptr2
)
1487 const struct monst
*m1
= *(const struct monst
**) vptr1
,
1488 *m2
= *(const struct monst
**) vptr2
;
1489 int d1
= (int) m1
->mux
, l1
= (int) m1
->muy
,
1490 d2
= (int) m2
->mux
, l2
= (int) m2
->muy
;
1492 /* if different branches, sort by dungeon number */
1495 /* within same branch, sort by level number */
1498 /* same destination level: use a tie-breaker to force stable sort;
1499 monst->m_id is unsigned so we need more than just simple subtraction */
1500 return (m1
->m_id
< m2
->m_id
) ? -1 : (m1
->m_id
> m2
->m_id
);
1503 /* called by #migratemons; displays count of migrating monsters, optionally
1504 displays them as well */
1506 list_migrating_mons(
1507 d_level
*nextlevl
) /* default destination for wiz_migrate_mons() */
1509 winid win
= WIN_ERR
;
1510 boolean showit
= FALSE
;
1514 char c
, prmpt
[10], xtra
[10], buf
[BUFSZ
];
1515 struct monst
*mtmp
, **marray
;
1516 int here
= 0, nxtlv
= 0, other
= 0;
1518 for (mtmp
= gm
.migrating_mons
; mtmp
; mtmp
= mtmp
->nmon
) {
1519 if (mtmp
->mux
== u
.uz
.dnum
&& mtmp
->muy
== u
.uz
.dlevel
)
1521 else if (mtmp
->mux
== nextlevl
->dnum
&& mtmp
->muy
== nextlevl
->dlevel
)
1526 if (here
+ nxtlv
+ other
== 0) {
1527 pline("No monsters currently migrating.");
1530 "%d mon%s pending for current level, %d for next level, %d for others.",
1531 here
, plur(here
), nxtlv
, other
);
1532 prmpt
[0] = xtra
[0] = '\0';
1533 (void) strkitten(here
? prmpt
: xtra
, 'c');
1534 (void) strkitten(nxtlv
? prmpt
: xtra
, 'n');
1535 (void) strkitten(other
? prmpt
: xtra
, 'o');
1536 Strcat(prmpt
, "a q");
1538 Sprintf(eos(prmpt
), "%c%s", '\033', xtra
);
1539 c
= yn_function("List which?", prmpt
, 'q', TRUE
);
1540 n
= (c
== 'c') ? here
1541 : (c
== 'n') ? nxtlv
1542 : (c
== 'o') ? other
1543 : (c
== 'a') ? here
+ nxtlv
+ other
1546 win
= create_nhwindow(NHW_TEXT
);
1551 Sprintf(buf
, "Monster%s migrating to %s:", plur(n
),
1552 (c
== 'c') ? "current level"
1553 : (c
== 'n') ? "next level"
1554 : "'other' levels");
1557 Strcpy(buf
, "All migrating monsters:");
1560 putstr(win
, 0, buf
);
1562 /* collect the migrating monsters into an array; for 'o' and 'a'
1563 where multiple destination levels might be present, sort by
1564 the destination; 'c' and 'n' don't need to be sorted but we
1565 do that anyway to get the same tie-breaker as 'o' and 'a' */
1566 marray
= (struct monst
**) alloc((n
+ 1) * sizeof *marray
);
1568 for (mtmp
= gm
.migrating_mons
; mtmp
; mtmp
= mtmp
->nmon
) {
1571 else if (mtmp
->mux
== u
.uz
.dnum
&& mtmp
->muy
== u
.uz
.dlevel
)
1572 showit
= (c
== 'c');
1573 else if (mtmp
->mux
== nextlevl
->dnum
1574 && mtmp
->muy
== nextlevl
->dlevel
)
1575 showit
= (c
== 'n');
1577 showit
= (c
== 'o');
1582 marray
[n
] = (struct monst
*) 0; /* mark end for traversal loop */
1584 qsort((genericptr_t
) marray
, (size_t) n
, sizeof *marray
,
1585 migrsort_cmp
); /* sort elements [0] through [n-1] */
1586 for (n
= 0; (mtmp
= marray
[n
]) != 0; ++n
) {
1587 Sprintf(buf
, " %s", minimal_monnam(mtmp
, FALSE
));
1588 /* minimal_monnam() appends map coordinates; strip that */
1589 (void) strsubst(buf
, " <0,0>", "");
1590 if (has_mgivenname(mtmp
)) /* if mtmp is named, include that */
1591 Sprintf(eos(buf
), " named %s", MGIVENNAME(mtmp
));
1592 if (c
== 'o' || c
== 'a')
1593 Sprintf(eos(buf
), " to %d:%d", mtmp
->mux
, mtmp
->muy
);
1594 xyloc
= mtmp
->mtrack
[0].x
; /* (for legibility) */
1595 if (xyloc
== MIGR_EXACT_XY
) {
1596 x
= mtmp
->mtrack
[1].x
;
1597 y
= mtmp
->mtrack
[1].y
;
1598 Sprintf(eos(buf
), " at <%d,%d>", (int) x
, (int) y
);
1600 putstr(win
, 0, buf
);
1602 free((genericptr_t
) marray
);
1603 display_nhwindow(win
, FALSE
);
1604 destroy_nhwindow(win
);
1605 } else if (c
!= 'q') {
1612 /* the #stats command
1613 * Display memory usage of all monsters and objects on the level.
1616 wiz_show_stats(void)
1620 long total_obj_size
, total_obj_count
,
1621 total_mon_size
, total_mon_count
,
1622 total_ovr_size
, total_ovr_count
,
1623 total_misc_size
, total_misc_count
;
1625 win
= create_nhwindow(NHW_TEXT
);
1626 putstr(win
, 0, "Current memory statistics:");
1628 total_obj_count
= total_obj_size
= 0L;
1629 putstr(win
, 0, stats_hdr
);
1630 Sprintf(buf
, " Objects, base size %ld", (long) sizeof (struct obj
));
1631 putstr(win
, 0, buf
);
1632 obj_chain(win
, "invent", gi
.invent
, TRUE
,
1633 &total_obj_count
, &total_obj_size
);
1634 obj_chain(win
, "fobj", fobj
, TRUE
, &total_obj_count
, &total_obj_size
);
1635 obj_chain(win
, "buried", svl
.level
.buriedobjlist
, FALSE
,
1636 &total_obj_count
, &total_obj_size
);
1637 obj_chain(win
, "migrating obj", gm
.migrating_objs
, FALSE
,
1638 &total_obj_count
, &total_obj_size
);
1639 obj_chain(win
, "billobjs", gb
.billobjs
, FALSE
,
1640 &total_obj_count
, &total_obj_size
);
1641 mon_invent_chain(win
, "minvent", fmon
, &total_obj_count
, &total_obj_size
);
1642 mon_invent_chain(win
, "migrating minvent", gm
.migrating_mons
,
1643 &total_obj_count
, &total_obj_size
);
1644 contained_stats(win
, "contained", &total_obj_count
, &total_obj_size
);
1645 putstr(win
, 0, stats_sep
);
1646 Sprintf(buf
, template, " Obj total", total_obj_count
, total_obj_size
);
1647 putstr(win
, 0, buf
);
1649 total_mon_count
= total_mon_size
= 0L;
1651 Sprintf(buf
, " Monsters, base size %ld", (long) sizeof (struct monst
));
1652 putstr(win
, 0, buf
);
1653 mon_chain(win
, "fmon", fmon
, TRUE
, &total_mon_count
, &total_mon_size
);
1654 mon_chain(win
, "migrating", gm
.migrating_mons
, FALSE
,
1655 &total_mon_count
, &total_mon_size
);
1656 /* 'gm.mydogs' is only valid during level change or end of game disclosure,
1657 but conceivably we've been called from within debugger at such time */
1658 if (gm
.mydogs
) /* monsters accompanying hero */
1659 mon_chain(win
, "mydogs", gm
.mydogs
, FALSE
,
1660 &total_mon_count
, &total_mon_size
);
1661 putstr(win
, 0, stats_sep
);
1662 Sprintf(buf
, template, " Mon total", total_mon_count
, total_mon_size
);
1663 putstr(win
, 0, buf
);
1665 total_ovr_count
= total_ovr_size
= 0L;
1667 putstr(win
, 0, " Overview");
1668 overview_stats(win
, template, &total_ovr_count
, &total_ovr_size
);
1669 putstr(win
, 0, stats_sep
);
1670 Sprintf(buf
, template, " Over total", total_ovr_count
, total_ovr_size
);
1671 putstr(win
, 0, buf
);
1673 total_misc_count
= total_misc_size
= 0L;
1675 putstr(win
, 0, " Miscellaneous");
1676 misc_stats(win
, &total_misc_count
, &total_misc_size
);
1677 putstr(win
, 0, stats_sep
);
1678 Sprintf(buf
, template, " Misc total", total_misc_count
, total_misc_size
);
1679 putstr(win
, 0, buf
);
1682 putstr(win
, 0, stats_sep
);
1683 Sprintf(buf
, template, " Grand total",
1684 (total_obj_count
+ total_mon_count
1685 + total_ovr_count
+ total_misc_count
),
1686 (total_obj_size
+ total_mon_size
1687 + total_ovr_size
+ total_misc_size
));
1688 putstr(win
, 0, buf
);
1690 #if defined(__BORLANDC__) && !defined(_WIN32)
1691 show_borlandc_stats(win
);
1694 display_nhwindow(win
, FALSE
);
1695 destroy_nhwindow(win
);
1699 RESTORE_WARNING_FORMAT_NONLITERAL
1701 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG)
1702 /* the #wizdispmacros command
1703 * Verify that some display macros are returning sane values */
1705 wiz_display_macros(void)
1707 static const char display_issues
[] = "Display macro issues:";
1710 int glyph
, test
, trouble
= 0, no_glyph
= NO_GLYPH
, max_glyph
= MAX_GLYPH
;
1712 win
= create_nhwindow(NHW_TEXT
);
1714 for (glyph
= 0; glyph
< MAX_GLYPH
; ++glyph
) {
1715 /* glyph_is_cmap / glyph_to_cmap() */
1716 if (glyph_is_cmap(glyph
)) {
1717 test
= glyph_to_cmap(glyph
);
1718 /* check for MAX_GLYPH return */
1719 if (test
== no_glyph
) {
1721 putstr(win
, 0, display_issues
);
1722 Sprintf(buf
, "glyph_is_cmap() / glyph_to_cmap(glyph=%d)"
1723 " sync failure, returned NO_GLYPH (%d)",
1725 putstr(win
, 0, buf
);
1727 if (glyph_is_cmap_zap(glyph
)
1728 && !(test
>= S_vbeam
&& test
<= S_rslant
)) {
1730 putstr(win
, 0, display_issues
);
1732 "glyph_is_cmap_zap(glyph=%d) returned non-zap cmap %d",
1734 putstr(win
, 0, buf
);
1736 /* check against defsyms array subscripts */
1737 if (!IndexOk(test
, defsyms
)) {
1739 putstr(win
, 0, display_issues
);
1740 Sprintf(buf
, "glyph_to_cmap(glyph=%d) returns %d"
1741 " exceeds defsyms[%d] bounds (MAX_GLYPH = %d)",
1742 glyph
, test
, SIZE(defsyms
), max_glyph
);
1743 putstr(win
, 0, buf
);
1746 /* glyph_is_monster / glyph_to_mon */
1747 if (glyph_is_monster(glyph
)) {
1748 test
= glyph_to_mon(glyph
);
1749 /* check against mons array subscripts */
1750 if (test
< 0 || test
>= NUMMONS
) {
1752 putstr(win
, 0, display_issues
);
1753 Sprintf(buf
, "glyph_to_mon(glyph=%d) returns %d"
1754 " exceeds mons[%d] bounds",
1755 glyph
, test
, NUMMONS
);
1756 putstr(win
, 0, buf
);
1759 /* glyph_is_object / glyph_to_obj */
1760 if (glyph_is_object(glyph
)) {
1761 test
= glyph_to_obj(glyph
);
1762 /* check against objects array subscripts */
1763 if (test
< 0 || test
> NUM_OBJECTS
) {
1765 putstr(win
, 0, display_issues
);
1766 Sprintf(buf
, "glyph_to_obj(glyph=%d) returns %d"
1767 " exceeds objects[%d] bounds",
1768 glyph
, test
, NUM_OBJECTS
);
1769 putstr(win
, 0, buf
);
1774 putstr(win
, 0, "No display macro issues detected.");
1775 display_nhwindow(win
, FALSE
);
1776 destroy_nhwindow(win
);
1779 #endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */
1781 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG)
1782 /* the #wizmondiff command */
1786 static const char window_title
[] = "Review of monster difficulty ratings"
1790 int mhardcoded
= 0, mcalculated
= 0, trouble
= 0, cnt
= 0, mdiff
= 0;
1792 struct permonst
*ptr
;
1795 * Possible extension: choose between showing discrepancies,
1796 * showing all monsters, or monsters within a particular class.
1799 win
= create_nhwindow(NHW_TEXT
);
1800 for (ptr
= &mons
[0]; ptr
->mlet
; ptr
++, cnt
++) {
1801 mcalculated
= mstrength(ptr
);
1802 mhardcoded
= (int) ptr
->difficulty
;
1803 mdiff
= mhardcoded
- mcalculated
;
1806 putstr(win
, 0, window_title
);
1807 mlev
= (int) ptr
->mlevel
;
1808 if (mlev
> 50) /* hack for named demons */
1810 Snprintf(buf
, sizeof buf
,
1811 "%-18s [%3d:%2d]: calculated: %2d, hardcoded: %2d (%+d)",
1812 ptr
->pmnames
[NEUTRAL
], cnt
, mlev
,
1813 mcalculated
, mhardcoded
, mdiff
);
1814 putstr(win
, 0, buf
);
1818 putstr(win
, 0, "No monster difficulty discrepancies were detected.");
1819 display_nhwindow(win
, FALSE
);
1820 destroy_nhwindow(win
);
1823 #endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */
1825 /* #migratemons command */
1827 wiz_migrate_mons(void)
1829 #ifdef DEBUG_MIGRATING_MONS
1832 struct permonst
*ptr
;
1834 boolean use_random_mon
= TRUE
;
1838 if (Is_stronghold(&u
.uz
))
1839 assign_level(&tolevel
, &valley_level
);
1840 else if (!Is_botlevel(&u
.uz
))
1841 get_level(&tolevel
, depth(&u
.uz
) + 1);
1843 tolevel
.dnum
= 0, tolevel
.dlevel
= 0;
1845 list_migrating_mons(&tolevel
);
1847 #ifdef DEBUG_MIGRATING_MONS
1848 inbuf
[0] = inbuf
[1] = '\0';
1849 if (tolevel
.dnum
|| tolevel
.dlevel
)
1850 getlin("How many random monsters to migrate to next level? [0]",
1853 pline("Can't get there from here.");
1854 if (*inbuf
== '\033' || *inbuf
== '\0')
1857 mcount
= atoi(inbuf
);
1859 use_random_mon
= FALSE
;
1864 else if (mcount
> ((COLNO
- 1) * ROWNO
))
1865 mcount
= (COLNO
- 1) * ROWNO
;
1867 while (mcount
> 0) {
1868 if (use_random_mon
) {
1870 mtmp
= makemon(ptr
, 0, 0, MM_NOMSG
);
1875 migrate_to_level(mtmp
, ledger_no(&tolevel
), MIGR_RANDOM
,
1879 #endif /* DEBUG_MIGRATING_MONS */
1883 /* #wizcustom command to see glyphmap customizations */
1887 extern const char *const known_handling
[]; /* symbols.c */
1890 static const char wizcustom
[] = "#wizcustom";
1892 char buf
[BUFSZ
], bufa
[BUFSZ
];
1897 menu_item
*pick_list
= (menu_item
*) 0;
1899 if (!glyphid_cache_status())
1900 fill_glyphid_cache();
1902 win
= create_nhwindow(NHW_MENU
);
1903 start_menu(win
, MENU_BEHAVE_STANDARD
);
1904 add_menu_heading(win
,
1905 " glyph glyph identifier "
1906 " sym clr customcolor unicode utf8");
1907 Sprintf(bufa
, "%s: colorcount=%ld %s", wizcustom
,
1908 (long) iflags
.colorcount
,
1909 gs
.symset
[PRIMARYSET
].name
? gs
.symset
[PRIMARYSET
].name
1911 if (gc
.currentgraphics
== PRIMARYSET
&& gs
.symset
[PRIMARYSET
].name
)
1912 Strcat(bufa
, ", active");
1913 if (gs
.symset
[PRIMARYSET
].handling
) {
1914 Sprintf(eos(bufa
), ", handler=%s",
1915 known_handling
[gs
.symset
[PRIMARYSET
].handling
]);
1917 Sprintf(buf
, "%s", bufa
);
1918 wizcustom_glyphids(win
);
1919 end_menu(win
, bufa
);
1920 n
= select_menu(win
, PICK_NONE
, &pick_list
);
1921 destroy_nhwindow(win
);
1923 for (j
= 0; j
< n
; ++j
) {
1924 glyph
= pick_list
[j
].item
.a_int
- 1; /* -1: reverse +1 above */
1928 free((genericptr_t
) pick_list
);
1929 if (glyphid_cache_status())
1930 free_glyphid_cache();
1933 pline(unavailcmd
, ecname_from_fn(wiz_custom
));
1938 wizcustom_callback(winid win
, int glyphnum
, char *id
)
1940 extern glyph_map glyphmap
[MAX_GLYPH
];
1943 char buf
[BUFSZ
], bufa
[BUFSZ
], bufb
[BUFSZ
], bufc
[BUFSZ
], bufd
[BUFSZ
],
1949 cgm
= &glyphmap
[glyphnum
];
1951 #ifdef ENHANCED_SYMBOLS
1954 cgm
->customcolor
!= 0) {
1955 Sprintf(bufa
, "[%04d] %-44s", glyphnum
, id
);
1956 Sprintf(bufb
, "'\\%03d' %02d",
1957 gs
.showsyms
[cgm
->sym
.symidx
], cgm
->sym
.color
);
1958 Sprintf(bufc
, "%011lx", (unsigned long) cgm
->customcolor
);
1960 #ifdef ENHANCED_SYMBOLS
1961 if (cgm
->u
&& cgm
->u
->utf8str
) {
1962 Sprintf(bufu
, "U+%04lx", (unsigned long) cgm
->u
->utf32ch
);
1963 cp
= cgm
->u
->utf8str
;
1965 Sprintf(bufd
, " <%d>", (int) *cp
);
1971 any
.a_int
= glyphnum
+ 1; /* avoid 0 */
1972 Snprintf(buf
, sizeof buf
, "%s %s %s %s", bufa
, bufb
, bufc
, bufu
);
1973 add_menu(win
, &nul_glyphinfo
, &any
, 0, 0, ATR_NONE
, clr
, buf
,
1974 MENU_ITEMFLAGS_NONE
);