make rank() static again
[NetHack.git] / src / wizcmds.c
blob6dd7568805b255ef495587a47169cb316870b4a3
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. */
5 #include "hack.h"
6 #include "func_tab.h"
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 *,
15 long *);
16 staticfn void mon_invent_chain(winid, const char *, struct monst *, long *,
17 long *);
18 staticfn void mon_chain(winid, const char *, struct monst *, boolean, long *,
19 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 */
31 int
32 wiz_wish(void) /* Unlimited wishes for debug mode by Paul Polderman */
34 if (wizard) {
35 boolean save_verbose = flags.verbose;
37 flags.verbose = FALSE;
38 makewish();
39 flags.verbose = save_verbose;
40 (void) encumber_msg();
41 } else
42 pline(unavailcmd, ecname_from_fn(wiz_wish));
43 return ECMD_OK;
46 DISABLE_WARNING_FORMAT_NONLITERAL
48 /* #wizidentify command - reveal and optionally identify hero's inventory */
49 int
50 wiz_identify(void)
52 if (wizard) {
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;
63 } else
64 pline(unavailcmd, ecname_from_fn(wiz_identify));
65 return ECMD_OK;
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 */
72 staticfn void
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 */
87 if (mtmp->isgd) {
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)) {
92 setpaid(mtmp);
94 if (migratory) {
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);
101 mtmp->nmon = fmon;
102 fmon = mtmp;
104 mongone(mtmp);
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 */
109 void
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))
121 continue;
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; ) {
134 if (mtmp->mextra
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)))) {
138 *mprev = mtmp->nmon;
139 makemap_unmakemon(mtmp, TRUE);
140 } else {
141 mprev = &mtmp->nmon;
144 /* release dead and 'unmade' monsters */
145 dmonsfree();
146 if (fmon) {
147 impossible("makemap_remove_mons: 'fmon' did not get emptied?");
149 return;
152 DISABLE_WARNING_FORMAT_NONLITERAL
154 /* #wizmakemap - discard current dungeon level and replace with a new one */
156 wiz_makemap(void)
158 if (wizard) {
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 */
165 mklev();
166 makemap_prepost(FALSE, was_in_W_tower);
167 } else {
168 pline(unavailcmd, ecname_from_fn(wiz_makemap));
170 return ECMD_OK;
173 /* the #wizmap command - reveal the level map
174 and any traps or engravings on it */
176 wiz_map(void)
178 if (wizard) {
179 struct trap *t;
180 struct engr *ep;
181 long save_Hconf = HConfusion, save_Hhallu = HHallucination;
183 notice_mon_off();
184 HConfusion = HHallucination = 0L;
185 for (t = gf.ftrap; t != 0; t = t->ntrap) {
186 t->tseen = 1;
187 map_trap(t, TRUE);
189 for (ep = head_engr; ep != 0; ep = ep->nxt_engr) {
190 map_engraving(ep, TRUE);
192 do_mapping();
193 notice_mon_on();
194 HConfusion = save_Hconf;
195 HHallucination = save_Hhallu;
196 } else
197 pline(unavailcmd, ecname_from_fn(wiz_map));
198 return ECMD_OK;
201 /* #wizgenesis - generate monster(s); a count prefix will be honored */
203 wiz_genesis(void)
205 if (wizard) {
206 boolean mongen_saved = iflags.debug_mongen;
208 iflags.debug_mongen = FALSE;
209 (void) create_particular();
210 iflags.debug_mongen = mongen_saved;
211 } else
212 pline(unavailcmd, ecname_from_fn(wiz_genesis));
213 return ECMD_OK;
216 /* #wizwhere command - display dungeon layout */
218 wiz_where(void)
220 if (wizard)
221 (void) print_dungeon(FALSE, (schar *) 0, (xint16 *) 0);
222 else
223 pline(unavailcmd, ecname_from_fn(wiz_where));
224 return ECMD_OK;
227 /* the #wizdetect command - detect secret doors, traps, hidden monsters */
229 wiz_detect(void)
231 if (wizard)
232 (void) findit();
233 else
234 pline(unavailcmd, ecname_from_fn(wiz_detect));
235 return ECMD_OK;
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 */
243 wiz_kill(void)
245 struct monst *mtmp;
246 coord cc;
247 int ans;
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;
255 for (;;) {
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)
265 break;
267 mtmp = 0;
268 if (u_at(cc.x, cc.y)) {
269 if (u.usteed) {
270 Sprintf(qbuf, "Kill %.110s?", mon_nam(u.usteed));
271 if ((c = ynq(qbuf)) == 'q')
272 break;
273 if (c == 'y')
274 mtmp = u.usteed;
276 if (!mtmp) {
277 Sprintf(qbuf, "%s?", Role_if(PM_SAMURAI) ? "Perform seppuku"
278 : "Commit suicide");
279 if (paranoid_query(TRUE, qbuf)) {
280 Sprintf(svk.killer.name, "%s own player", uhis());
281 svk.killer.format = KILLED_BY;
282 done(DIED);
284 break;
286 } else if (u.uswallow) {
287 mtmp = next2u(cc.x, cc.y) ? u.ustuck : 0;
288 } else {
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;
294 let hero know too */
295 (void) unmap_invisible(cc.x, cc.y);
297 if (mtmp) {
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
306 : 0)),
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-
330 changing trap */
331 if (u.utotype || !on_level(&u.uz, &uarehere))
332 break;
333 } else {
334 There("is no monster there.");
335 break;
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 */
344 dmonsfree();
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 */
353 wiz_load_lua(void)
355 if (wizard) {
356 char buf[BUFSZ];
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};
362 buf[0] = '\0';
363 getlin("Load which lua file?", buf);
364 if (buf[0] == '\033' || buf[0] == '\0')
365 return ECMD_CANCEL;
366 if (!strchr(buf, '.'))
367 strcat(buf, ".lua");
368 (void) load_lua(buf, &sbi);
369 } else
370 pline(unavailcmd, ecname_from_fn(wiz_load_lua));
371 return ECMD_OK;
374 /* the #wizloaddes command - load a special level lua file */
376 wiz_load_splua(void)
378 if (wizard) {
379 char buf[BUFSZ];
381 buf[0] = '\0';
382 getlin("Load which des lua file?", buf);
383 if (buf[0] == '\033' || buf[0] == '\0')
384 return ECMD_CANCEL;
385 if (!strchr(buf, '.'))
386 strcat(buf, ".lua");
388 lspo_reset_level(NULL);
389 (void) load_special(buf);
390 lspo_finalize_level(NULL);
392 } else
393 pline(unavailcmd, ecname_from_fn(wiz_load_splua));
394 return ECMD_OK;
397 /* the #wizlevelport command - level teleport */
399 wiz_level_tele(void)
401 if (wizard)
402 level_tele();
403 else
404 pline(unavailcmd, ecname_from_fn(wiz_level_tele));
405 return ECMD_OK;
408 RESTORE_WARNING_FORMAT_NONLITERAL
410 /* #wizfliplevel - transpose the current level */
412 wiz_flip_level(void)
414 static const char choices[] = "0123",
415 prmpt[] = "Flip 0=randomly, 1=vertically, 2=horizontally, 3=both:";
418 * Does not handle
419 * levregions,
420 * monster mtrack,
421 * migrating monsters aimed at returning to specific coordinates
422 * on this level
423 * as flipping is normally done only during level creation.
425 if (wizard) {
426 char c = yn_function(prmpt, choices, '\0', TRUE);
428 if (c && strchr(choices, c)) {
429 c -= '0';
431 if (!c)
432 flip_level_rnd(3, TRUE);
433 else
434 flip_level((int) c, TRUE);
436 docrt();
437 } else {
438 pline("%s", Never_mind);
441 return ECMD_OK;
444 /* #levelchange command - adjust hero's experience level */
446 wiz_level_change(void)
448 char buf[BUFSZ], dummy = '\0';
449 int newlevel = 0;
450 int ret;
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')
456 ret = 0;
457 else
458 ret = sscanf(buf, "%d%c", &newlevel, &dummy);
460 if (ret != 1) {
461 pline1(Never_mind);
462 return ECMD_OK;
464 if (newlevel == u.ulevel) {
465 You("are already that experienced.");
466 } else if (newlevel < u.ulevel) {
467 if (u.ulevel == 1) {
468 You("are already as inexperienced as you can get.");
469 return ECMD_OK;
471 if (newlevel < 1)
472 newlevel = 1;
473 while (u.ulevel > newlevel)
474 losexp("#levelchange");
475 } else {
476 if (u.ulevel >= MAXULEV) {
477 You("are already as experienced as you can get.");
478 return ECMD_OK;
480 if (newlevel > MAXULEV)
481 newlevel = MAXULEV;
482 while (u.ulevel < newlevel)
483 pluslvl(FALSE);
485 /* blessed full healing or restore ability won't fix any lost levels */
486 u.ulevelmax = u.ulevel;
487 return ECMD_OK;
490 DISABLE_WARNING_CONDEXPR_IS_CONSTANT
492 /* #wiztelekinesis */
494 wiz_telekinesis(void)
496 int ans = 0;
497 coord cc;
498 struct monst *mtmp = (struct monst *) 0;
500 cc.x = u.ux;
501 cc.y = u.uy;
503 pline("Pick a monster to hurtle.");
504 do {
505 ans = getpos(&cc, TRUE, "a monster");
506 if (ans < 0 || cc.x < 1)
507 return ECMD_CANCEL;
509 if ((((mtmp = m_at(cc.x, cc.y)) != 0) && canspotmon(mtmp))
510 || u_at(cc.x, cc.y)) {
511 if (!getdir("which direction?"))
512 return ECMD_CANCEL;
514 if (mtmp) {
515 mhurtle(mtmp, u.dx, u.dy, 6);
516 if (!DEADMONSTER(mtmp) && canspotmon(mtmp)) {
517 cc.x = mtmp->mx;
518 cc.y = mtmp->my;
520 } else {
521 hurtle(u.dx, u.dy, 6, FALSE);
522 cc.x = u.ux, cc.y = u.uy;
526 } while (u.utotype == UTOTYPE_NONE);
527 return ECMD_OK;
530 RESTORE_WARNING_CONDEXPR_IS_CONSTANT
532 /* #panic command - test program's panic handling */
534 wiz_panic(void)
536 if (iflags.debug_fuzzer) {
537 u.uhp = u.uhpmax = 1000;
538 u.uen = u.uenmax = 1000;
539 return ECMD_OK;
541 if (paranoid_query(TRUE,
542 "Do you want to call panic() and end your game?"))
543 panic("Crash test (#panic).");
544 return ECMD_OK;
547 /* #debugfuzzer command - fuzztest the program */
549 wiz_fuzzer(void)
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;
559 } else {
560 iflags.debug_fuzzer = fuzzer_impossible_panic;
563 return ECMD_OK;
566 /* #polyself command - change hero's form */
568 wiz_polyself(void)
570 polyself(POLY_CONTROLLED);
571 return ECMD_OK;
574 /* #seenv command */
576 wiz_show_seenv(void)
578 winid win;
579 coordxy x, y, startx, stopx, curx;
580 int v;
581 char row[COLNO + 1];
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)
592 startx++;
594 for (y = 0; y < ROWNO; y++) {
595 for (x = startx, curx = 0; x < stopx; x++, curx += 2) {
596 if (u_at(x, y)) {
597 row[curx] = row[curx + 1] = '@';
598 } else {
599 v = levl[x][y].seenv & 0xff;
600 if (v == 0)
601 row[curx] = row[curx + 1] = ' ';
602 else
603 Sprintf(&row[curx], "%02x", v);
606 /* remove trailing spaces */
607 for (x = curx - 1; x >= 0; x--)
608 if (row[x] != ' ')
609 break;
610 row[x + 1] = '\0';
612 putstr(win, 0, row);
614 display_nhwindow(win, TRUE);
615 destroy_nhwindow(win);
616 return ECMD_OK;
619 /* #vision command */
621 wiz_show_vision(void)
623 winid win;
624 coordxy x, y;
625 int v;
626 char row[COLNO + 1];
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);
631 putstr(win, 0, row);
632 putstr(win, 0, "");
633 for (y = 0; y < ROWNO; y++) {
634 for (x = 1; x < COLNO; x++) {
635 if (u_at(x, y)) {
636 row[x] = '@';
637 } else {
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--)
644 if (row[x] != ' ')
645 break;
646 row[x + 1] = '\0';
648 putstr(win, 0, &row[1]);
650 display_nhwindow(win, TRUE);
651 destroy_nhwindow(win);
652 return ECMD_OK;
655 /* #wmode command */
657 wiz_show_wmodes(void)
659 winid win;
660 coordxy x, y;
661 char row[COLNO + 1];
662 struct rm *lev;
663 boolean istty = WINDOWPORT(tty);
665 win = create_nhwindow(NHW_TEXT);
666 if (istty)
667 putstr(win, 0, ""); /* tty only: blank top line */
668 for (y = 0; y < ROWNO; y++) {
669 for (x = 0; x < COLNO; x++) {
670 lev = &levl[x][y];
671 if (u_at(x, y))
672 row[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)
676 row[x] = '#';
677 else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ))
678 row[x] = '.';
679 else
680 row[x] = 'x';
682 row[COLNO] = '\0';
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);
688 return ECMD_OK;
691 /* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */
692 void
693 wiz_map_levltyp(void)
695 winid win;
696 coordxy x, y;
697 int terrain;
698 char row[COLNO + 1];
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 */
703 if (istty)
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))
712 ? '*'
713 : (terrain < 10)
714 ? '0' + terrain
715 : (terrain < 36)
716 ? 'a' + terrain - 10
717 : 'A' + terrain - 36);
719 x--;
720 if (levl[0][y].typ != STONE || may_dig(0, y))
721 row[x++] = '!';
722 row[x] = '\0';
723 putstr(win, 0, row);
727 char dsc[COLBUFSZ];
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 */
733 if (slev) {
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 */
747 /* level features */
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)
763 Strcat(dsc, " 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");
772 /* level flags */
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");
791 if (Sokoban)
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 */
800 if (u.uz.dnum == 0)
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");
816 else {
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)
821 brname = "unknown";
822 if (!strncmpi(brname, "the ", 4))
823 brname += 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 */
829 putstr(win, 0, dsc);
832 display_nhwindow(win, TRUE);
833 destroy_nhwindow(win);
834 return;
837 DISABLE_WARNING_FORMAT_NONLITERAL
839 /* explanation of base-36 output from wiz_map_levltyp() */
840 void
841 wiz_levltyp_legend(void)
843 winid win;
844 int i, j, last, c;
845 const char *dsc, *fmt;
846 char buf[BUFSZ];
848 win = create_nhwindow(NHW_TEXT);
849 putstr(win, 0, "#terrain encodings:");
850 putstr(win, 0, "");
851 fmt = " %c - %-28s"; /* TODO: include tab-separated variant for win32 */
852 *buf = '\0';
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) {
861 dsc = levltyp[j];
862 c = !*dsc ? ' '
863 : !strncmp(dsc, "unreachable", 11) ? '*'
864 /* same int-to-char conversion as wiz_map_levltyp() */
865 : (j < 10) ? '0' + j
866 : (j < 36) ? 'a' + j - 10
867 : 'A' + j - 36;
868 Sprintf(eos(buf), fmt, c, dsc);
869 if (j > i) {
870 putstr(win, 0, buf);
871 *buf = '\0';
874 display_nhwindow(win, TRUE);
875 destroy_nhwindow(win);
876 return;
879 RESTORE_WARNING_FORMAT_NONLITERAL
881 DISABLE_WARNING_CONDEXPR_IS_CONSTANT
883 /* #wizsmell command - test usmellmon(). */
885 wiz_smell(void)
887 struct monst *mtmp; /* monster being smelled */
888 struct permonst *mptr;
889 int ans, glyph;
890 coord cc; /* screen pos to sniff */
891 boolean is_you;
893 cc.x = u.ux;
894 cc.y = u.uy;
895 if (!olfaction(gy.youmonst.data)) {
896 You("are incapable of detecting odors in your present form.");
897 return ECMD_OK;
900 You("can move the cursor to a monster that you want to smell.");
901 do {
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 */
907 is_you = FALSE;
908 if (u_at(cc.x, cc.y)) {
909 if (u.usteed) {
910 mptr = u.usteed->data;
911 } else {
912 mptr = gy.youmonst.data;
913 is_you = TRUE;
915 } else if ((mtmp = m_at(cc.x, cc.y)) != (struct monst *) 0) {
916 mptr = mtmp->data;
917 } else {
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? */
924 if (mptr) {
925 if (is_you)
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);
932 } else {
933 You("don't smell any monster there.");
934 if (glyph_is_invisible(glyph))
935 unmap_invisible(cc.x, cc.y);
937 } while (TRUE);
938 return ECMD_OK;
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 */
949 wiz_intrinsic(void)
951 if (wizard) {
952 static const char wizintrinsic[] = "#wizintrinsic";
953 static const char fmt[] = "You are%s %s.";
954 winid win;
955 anything any;
956 char buf[BUFSZ];
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;
961 int clr = NO_COLOR;
963 any = cg.zeroany;
964 win = create_nhwindow(NHW_MENU);
965 start_menu(win, MENU_BEHAVE_STANDARD);
966 if (iflags.cmdassist) {
967 /* start menu with a subtitle */
968 Sprintf(buf,
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... */
979 continue;
981 if (p == FIRE_RES) {
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;
989 if (oldtimeout)
990 Sprintf(buf, "%-27s [%li]", propname, oldtimeout);
991 else
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 */
1007 continue;
1008 newtimeout = oldtimeout + (long) amt;
1010 switch (p) {
1011 case SICK:
1012 case SLIMED:
1013 case STONED:
1014 if (oldtimeout > 0L && newtimeout > oldtimeout)
1015 newtimeout = oldtimeout;
1016 break;
1019 switch (p) {
1020 case BLINDED:
1021 make_blinded(newtimeout, TRUE);
1022 break;
1023 #if 0 /* make_confused() only gives feedback when confusion is
1024 * ending so use the 'default' case for it instead */
1025 case CONFUSION:
1026 make_confused(newtimeout, TRUE);
1027 break;
1028 #endif /*0*/
1029 case DEAF:
1030 make_deaf(newtimeout, TRUE);
1031 break;
1032 case HALLUC:
1033 make_hallucinated(newtimeout, TRUE, 0L);
1034 break;
1035 case SICK:
1036 typ = !rn2(2) ? SICK_VOMITABLE : SICK_NONVOMITABLE;
1037 make_sick(newtimeout, wizintrinsic, TRUE, typ);
1038 break;
1039 case SLIMED:
1040 Sprintf(buf, fmt,
1041 !Slimed ? "" : " still", "turning into slime");
1042 make_slimed(newtimeout, buf);
1043 break;
1044 case STONED:
1045 Sprintf(buf, fmt,
1046 !Stoned ? "" : " still", "turning into stone");
1047 make_stoned(newtimeout, buf, KILLED_BY, wizintrinsic);
1048 break;
1049 case STUNNED:
1050 make_stunned(newtimeout, TRUE);
1051 break;
1052 case VOMITING:
1053 Sprintf(buf, fmt, !Vomiting ? "" : " still", "vomiting");
1054 make_vomiting(newtimeout, FALSE);
1055 pline1(buf);
1056 break;
1057 case WARN_OF_MON:
1058 if (!Warn_of_mon) {
1059 svc.context.warntype.speciesidx = PM_GRID_BUG;
1060 svc.context.warntype.species
1061 = &mons[svc.context.warntype.speciesidx];
1063 goto def_feedback;
1064 case GLIB:
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);
1069 FALLTHROUGH;
1070 /*FALLTHRU*/
1071 default:
1072 def_feedback:
1073 if (p != GLIB)
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);
1078 break;
1080 /* this has to be after incr_itimeout() */
1081 if (p == LEVITATION || p == FLYING)
1082 float_vs_flight();
1083 else if (p == PROT_FROM_SHAPE_CHANGERS)
1084 rescham();
1085 if (p == WWALKING || p == LEVITATION || p == FLYING) {
1086 if (u.uinwater)
1087 (void) pooleffects(FALSE);
1090 if (n >= 1)
1091 free((genericptr_t) pick_list);
1092 docrt();
1093 } else
1094 pline(unavailcmd, ecname_from_fn(wiz_intrinsic));
1095 return ECMD_OK;
1098 RESTORE_WARNING_FORMAT_NONLITERAL
1100 /* #wizrumorcheck command - verify each rumor access */
1102 wiz_rumor_check(void)
1104 rumor_check();
1105 return ECMD_OK;
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[] = "--------------------------- ----- -------";
1116 staticfn int
1117 size_obj(struct obj *otmp)
1119 int sz = (int) sizeof (struct obj);
1121 if (otmp->oextra) {
1122 sz += (int) sizeof (struct oextra);
1123 if (ONAME(otmp))
1124 sz += (int) strlen(ONAME(otmp)) + 1;
1125 if (OMONST(otmp))
1126 sz += size_monst(OMONST(otmp), FALSE);
1127 if (OMAILCMD(otmp))
1128 sz += (int) strlen(OMAILCMD(otmp)) + 1;
1129 /* sz += (int) sizeof (unsigned); -- now part of oextra itself */
1131 return sz;
1134 staticfn void
1135 count_obj(struct obj *chain, long *total_count, long *total_size,
1136 boolean top, boolean recurse)
1138 long count, size;
1139 struct obj *obj;
1141 for (count = size = 0, obj = chain; obj; obj = obj->nobj) {
1142 if (top) {
1143 count++;
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 */
1155 staticfn void
1156 obj_chain(
1157 winid win,
1158 const char *src,
1159 struct obj *chain,
1160 boolean force,
1161 long *total_count, long *total_size)
1163 char buf[BUFSZ];
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);
1176 staticfn void
1177 mon_invent_chain(
1178 winid win,
1179 const char *src,
1180 struct monst *chain,
1181 long *total_count, long *total_size)
1183 char buf[BUFSZ];
1184 long count = 0, size = 0;
1185 struct monst *mon;
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);
1198 staticfn void
1199 contained_stats(
1200 winid win,
1201 const char *src,
1202 long *total_count, long *total_size)
1204 char buf[BUFSZ];
1205 long count = 0, size = 0;
1206 struct monst *mon;
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
1213 * inventory */
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);
1227 staticfn int
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);
1235 if (mtmp->mextra) {
1236 sz += (int) sizeof (struct mextra);
1237 if (MGIVENNAME(mtmp))
1238 sz += (int) strlen(MGIVENNAME(mtmp)) + 1;
1239 if (EGD(mtmp))
1240 sz += (int) sizeof (struct egd);
1241 if (EPRI(mtmp))
1242 sz += (int) sizeof (struct epri);
1243 if (ESHK(mtmp))
1244 sz += (int) sizeof (struct eshk);
1245 if (EMIN(mtmp))
1246 sz += (int) sizeof (struct emin);
1247 if (EDOG(mtmp))
1248 sz += (int) sizeof (struct edog);
1249 if (EBONES(mtmp))
1250 sz += (int) sizeof (struct ebones);
1251 /* mextra->mcorpsenm doesn't point to more memory */
1253 return sz;
1256 staticfn void
1257 mon_chain(
1258 winid win,
1259 const char *src,
1260 struct monst *chain,
1261 boolean force,
1262 long *total_count, long *total_size)
1264 char buf[BUFSZ];
1265 long count, size;
1266 struct monst *mon;
1267 /* mon->wormno means something different for migrating_mons and mydogs */
1268 boolean incl_wsegs = !strcmpi(src, "fmon");
1270 count = size = 0L;
1271 for (mon = chain; mon; mon = mon->nmon) {
1272 count++;
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);
1283 staticfn void
1284 misc_stats(
1285 winid win,
1286 long *total_count, long *total_size)
1288 char buf[BUFSZ], hdrbuf[QBUFSZ];
1289 long count, size;
1290 int idx;
1291 struct trap *tt;
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
1299 count = size = 0L;
1300 for (tt = gf.ftrap; tt; tt = tt->ntrap) {
1301 ++count;
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);
1310 count = size = 0L;
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);
1317 count = size = 0L;
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);
1326 count = size = 0L;
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);
1335 count = size = 0L;
1336 for (sd = svl.level.damagelist; sd; sd = sd->next) {
1337 ++count;
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);
1349 count = size = 0L;
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);
1358 count = size = 0L;
1359 for (k = svk.killer.next; k; k = k->next) {
1360 ++count;
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);
1372 count = size = 0L;
1373 for (bi = svl.level.bonesinfo; bi; bi = bi->next) {
1374 ++count;
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);
1386 count = size = 0L;
1387 for (idx = 0; idx < NUM_OBJECTS; ++idx)
1388 if (objects[idx].oc_uname) {
1389 ++count;
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);
1401 staticfn void
1402 you_sanity_check(void)
1404 struct monst *mtmp;
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 */
1411 u.uswallow = 0;
1412 u.uswldtim = 0;
1413 docrt();
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)",
1424 u.uhp, u.uhpmax);
1425 u.uhp = u.uhpmax;
1427 if (Upolyd && u.mh > u.mhmax) {
1428 impossible(
1429 "current hero health as monster (%d) better than maximum? (%d)",
1430 u.mh, u.mhmax);
1431 u.mh = u.mhmax;
1433 if (u.uen > u.uenmax) {
1434 impossible("current hero energy (%d) better than maximum? (%d)",
1435 u.uen, u.uenmax);
1436 u.uen = u.uenmax;
1439 check_wornmask_slots();
1440 (void) check_invent_gold("invent");
1443 staticfn void
1444 levl_sanity_check(void)
1446 coordxy x, y;
1448 if (Underwater)
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);
1459 void
1460 sanity_check(void)
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;
1468 return;
1470 program_state.in_sanity_check++;
1471 you_sanity_check();
1472 obj_sanity_check();
1473 timer_sanity_check();
1474 mon_sanity_check();
1475 light_sources_sanity_check();
1476 bc_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 */
1493 if (d1 != d2)
1494 return d1 - d2;
1495 /* within same branch, sort by level number */
1496 if (l1 != l2)
1497 return l1 - l2;
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 */
1505 staticfn void
1506 list_migrating_mons(
1507 d_level *nextlevl) /* default destination for wiz_migrate_mons() */
1509 winid win = WIN_ERR;
1510 boolean showit = FALSE;
1511 unsigned n;
1512 int xyloc;
1513 coordxy x, y;
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)
1520 ++here;
1521 else if (mtmp->mux == nextlevl->dnum && mtmp->muy == nextlevl->dlevel)
1522 ++nxtlv;
1523 else
1524 ++other;
1526 if (here + nxtlv + other == 0) {
1527 pline("No monsters currently migrating.");
1528 } else {
1529 pline(
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");
1537 if (*xtra)
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
1544 : 0;
1545 if (n > 0) {
1546 win = create_nhwindow(NHW_TEXT);
1547 switch (c) {
1548 case 'c':
1549 case 'n':
1550 case 'o':
1551 Sprintf(buf, "Monster%s migrating to %s:", plur(n),
1552 (c == 'c') ? "current level"
1553 : (c == 'n') ? "next level"
1554 : "'other' levels");
1555 break;
1556 default:
1557 Strcpy(buf, "All migrating monsters:");
1558 break;
1560 putstr(win, 0, buf);
1561 putstr(win, 0, "");
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);
1567 n = 0;
1568 for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) {
1569 if (c == 'a')
1570 showit = TRUE;
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');
1576 else
1577 showit = (c == 'o');
1579 if (showit)
1580 marray[n++] = mtmp;
1582 marray[n] = (struct monst *) 0; /* mark end for traversal loop */
1583 if (n > 1)
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') {
1606 pline("None.");
1612 /* the #stats command
1613 * Display memory usage of all monsters and objects on the level.
1616 wiz_show_stats(void)
1618 char buf[BUFSZ];
1619 winid win;
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;
1650 putstr(win, 0, "");
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;
1666 putstr(win, 0, "");
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;
1674 putstr(win, 0, "");
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);
1681 putstr(win, 0, "");
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);
1692 #endif
1694 display_nhwindow(win, FALSE);
1695 destroy_nhwindow(win);
1696 return ECMD_OK;
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:";
1708 char buf[BUFSZ];
1709 winid win;
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) {
1720 if (!trouble++)
1721 putstr(win, 0, display_issues);
1722 Sprintf(buf, "glyph_is_cmap() / glyph_to_cmap(glyph=%d)"
1723 " sync failure, returned NO_GLYPH (%d)",
1724 glyph, test);
1725 putstr(win, 0, buf);
1727 if (glyph_is_cmap_zap(glyph)
1728 && !(test >= S_vbeam && test <= S_rslant)) {
1729 if (!trouble++)
1730 putstr(win, 0, display_issues);
1731 Sprintf(buf,
1732 "glyph_is_cmap_zap(glyph=%d) returned non-zap cmap %d",
1733 glyph, test);
1734 putstr(win, 0, buf);
1736 /* check against defsyms array subscripts */
1737 if (!IndexOk(test, defsyms)) {
1738 if (!trouble++)
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) {
1751 if (!trouble++)
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) {
1764 if (!trouble++)
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);
1773 if (!trouble)
1774 putstr(win, 0, "No display macro issues detected.");
1775 display_nhwindow(win, FALSE);
1776 destroy_nhwindow(win);
1777 return ECMD_OK;
1779 #endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */
1781 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG)
1782 /* the #wizmondiff command */
1784 wiz_mon_diff(void)
1786 static const char window_title[] = "Review of monster difficulty ratings"
1787 " [index:level]:";
1788 char buf[BUFSZ];
1789 winid win;
1790 int mhardcoded = 0, mcalculated = 0, trouble = 0, cnt = 0, mdiff = 0;
1791 int mlev;
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;
1804 if (mdiff) {
1805 if (!trouble++)
1806 putstr(win, 0, window_title);
1807 mlev = (int) ptr->mlevel;
1808 if (mlev > 50) /* hack for named demons */
1809 mlev = 50;
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);
1817 if (!trouble)
1818 putstr(win, 0, "No monster difficulty discrepancies were detected.");
1819 display_nhwindow(win, FALSE);
1820 destroy_nhwindow(win);
1821 return ECMD_OK;
1823 #endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */
1825 /* #migratemons command */
1827 wiz_migrate_mons(void)
1829 #ifdef DEBUG_MIGRATING_MONS
1830 int mcount;
1831 char inbuf[BUFSZ];
1832 struct permonst *ptr;
1833 struct monst *mtmp;
1834 boolean use_random_mon = TRUE;
1835 #endif
1836 d_level tolevel;
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);
1842 else
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]",
1851 inbuf);
1852 else
1853 pline("Can't get there from here.");
1854 if (*inbuf == '\033' || *inbuf == '\0')
1855 return ECMD_OK;
1857 mcount = atoi(inbuf);
1858 if (mcount < 0) {
1859 use_random_mon = FALSE;
1860 mcount *= -1;
1862 if (mcount < 1)
1863 mcount = 0;
1864 else if (mcount > ((COLNO - 1) * ROWNO))
1865 mcount = (COLNO - 1) * ROWNO;
1867 while (mcount > 0) {
1868 if (use_random_mon) {
1869 ptr = rndmonst();
1870 mtmp = makemon(ptr, 0, 0, MM_NOMSG);
1871 } else {
1872 mtmp = fmon;
1874 if (mtmp)
1875 migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM,
1876 (coord *) 0);
1877 mcount--;
1879 #endif /* DEBUG_MIGRATING_MONS */
1880 return ECMD_OK;
1883 /* #wizcustom command to see glyphmap customizations */
1885 wiz_custom(void)
1887 extern const char *const known_handling[]; /* symbols.c */
1889 if (wizard) {
1890 static const char wizcustom[] = "#wizcustom";
1891 winid win;
1892 char buf[BUFSZ], bufa[BUFSZ];
1893 int n;
1894 #if 0
1895 int j, glyph;
1896 #endif
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
1910 : "default");
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);
1922 #if 0
1923 for (j = 0; j < n; ++j) {
1924 glyph = pick_list[j].item.a_int - 1; /* -1: reverse +1 above */
1926 #endif
1927 if (n >= 1)
1928 free((genericptr_t) pick_list);
1929 if (glyphid_cache_status())
1930 free_glyphid_cache();
1931 docrt();
1932 } else
1933 pline(unavailcmd, ecname_from_fn(wiz_custom));
1934 return ECMD_OK;
1937 void
1938 wizcustom_callback(winid win, int glyphnum, char *id)
1940 extern glyph_map glyphmap[MAX_GLYPH];
1941 glyph_map *cgm;
1942 int clr = NO_COLOR;
1943 char buf[BUFSZ], bufa[BUFSZ], bufb[BUFSZ], bufc[BUFSZ], bufd[BUFSZ],
1944 bufu[BUFSZ];
1945 anything any;
1946 uint8 *cp;
1948 if (win && id) {
1949 cgm = &glyphmap[glyphnum];
1950 if (
1951 #ifdef ENHANCED_SYMBOLS
1952 cgm->u ||
1953 #endif
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);
1959 bufu[0] = '\0';
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;
1964 while (*cp) {
1965 Sprintf(bufd, " <%d>", (int) *cp);
1966 Strcat(bufu, bufd);
1967 cp++;
1970 #endif
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);
1977 return;
1980 /*wizcmds.c*/