OSDN Git Service

finalize changelog
[jnethack/source.git] / src / dungeon.c
index 45bbf30..ee21ae1 100644 (file)
@@ -1,5 +1,6 @@
-/* NetHack 3.6 dungeon.c       $NHDT-Date: 1448862377 2015/11/30 05:46:17 $  $NHDT-Branch: master $:$NHDT-Revision: 1.69 $ */
+/* NetHack 3.6 dungeon.c       $NHDT-Date: 1559476918 2019/06/02 12:01:58 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.95 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/*-Copyright (c) Robert Patrick Rankin, 2012. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
@@ -57,18 +58,19 @@ STATIC_DCL boolean FDECL(unreachable_level, (d_level *, BOOLEAN_P));
 STATIC_DCL void FDECL(tport_menu, (winid, char *, struct lchoice *, d_level *,
                                    BOOLEAN_P));
 STATIC_DCL const char *FDECL(br_string, (int));
+STATIC_DCL char FDECL(chr_u_on_lvl, (d_level *));
 STATIC_DCL void FDECL(print_branch, (winid, int, int, int, BOOLEAN_P,
                                      struct lchoice *));
 STATIC_DCL mapseen *FDECL(load_mapseen, (int));
 STATIC_DCL void FDECL(save_mapseen, (int, mapseen *));
 STATIC_DCL mapseen *FDECL(find_mapseen, (d_level *));
+STATIC_DCL mapseen *FDECL(find_mapseen_by_str, (const char *));
 STATIC_DCL void FDECL(print_mapseen, (winid, mapseen *, int, int, BOOLEAN_P));
 STATIC_DCL boolean FDECL(interest_mapseen, (mapseen *));
 STATIC_DCL void FDECL(traverse_mapseenchn, (BOOLEAN_P, winid,
                                             int, int, int *));
 STATIC_DCL const char *FDECL(seen_string, (XCHAR_P, const char *));
 STATIC_DCL const char *FDECL(br_string2, (branch *));
-STATIC_DCL const char *FDECL(endgamelevelname, (char *, int));
 STATIC_DCL const char *FDECL(shop_string, (int));
 STATIC_DCL char *FDECL(tunesuffix, (mapseen *, char *));
 
@@ -179,6 +181,8 @@ boolean perform_write, free_data;
             next_ms = curr_ms->next;
             if (curr_ms->custom)
                 free((genericptr_t) curr_ms->custom);
+            if (curr_ms->final_resting_place)
+                savecemetery(fd, FREE_SAVE, &curr_ms->final_resting_place);
             free((genericptr_t) curr_ms);
         }
         mapseenchn = 0;
@@ -246,7 +250,7 @@ dlb *stream;
         panic(
   "Premature EOF on dungeon description file!\r\nExpected %d bytes - got %d.",
               (size * nitems), (size * cnt));
-        terminate(EXIT_FAILURE);
+        nh_terminate(EXIT_FAILURE);
     }
 }
 
@@ -490,6 +494,7 @@ struct proto_dungeon *pd;
 
     branch_num = find_branch(dungeons[dgn].dname, pd);
     new_branch = (branch *) alloc(sizeof(branch));
+    (void) memset((genericptr_t)new_branch, 0, sizeof(branch));
     new_branch->next = (branch *) 0;
     new_branch->id = branch_id++;
     new_branch->type = correct_branch_type(&pd->tmpbranch[branch_num]);
@@ -545,6 +550,7 @@ struct proto_dungeon *pd;
 
     pd->final_lev[proto_index] = new_level =
         (s_level *) alloc(sizeof(s_level));
+    (void) memset((genericptr_t)new_level, 0, sizeof(s_level));
     /* load new level with data */
     Strcpy(new_level->proto, tlevel->name);
     new_level->boneid = tlevel->boneschar;
@@ -852,7 +858,7 @@ init_dungeons()
              * its branch.  First, the depth of the entry point:
              *
              *  depth of branch from "parent" dungeon
-             *  + -1 or 1 depending on a up or down stair or
+             *  + -1 or 1 depending on an up or down stair or
              *    0 if portal
              *
              * Followed by the depth of the top of the dungeon:
@@ -974,8 +980,8 @@ init_dungeons()
            instead of 0, so adjust the start point to shift endgame up */
         if (dunlevs_in_dungeon(&x->dlevel) > 1 - dungeons[i].depth_start)
             dungeons[i].depth_start -= 1;
-        /* TO DO: strip "dummy" out all the way here,
-           so that it's hidden from <ctrl/O> feedback. */
+        /* TODO: strip "dummy" out all the way here,
+           so that it's hidden from '#wizwhere' feedback. */
     }
 
 #ifdef DEBUG
@@ -1196,6 +1202,13 @@ void
 u_on_newpos(x, y)
 int x, y;
 {
+    if (!isok(x, y)) { /* validate location */
+        void VDECL((*func), (const char *, ...)) PRINTF_F(1, 2);
+
+        func = (x < 0 || y < 0 || x > COLNO - 1 || y > ROWNO - 1) ? panic
+               : impossible;
+        (*func)("u_on_newpos: trying to place hero off map <%d,%d>", x, y);
+    }
     u.ux = x;
     u.uy = y;
 #ifdef CLIPPING
@@ -1210,7 +1223,7 @@ int x, y;
         u.ux0 = u.ux, u.uy0 = u.uy;
 }
 
-/* place you on a random location */
+/* place you on a random location when arriving on a level */
 void
 u_on_rndspot(upflag)
 int upflag;
@@ -1228,16 +1241,19 @@ int upflag;
            destination instead of its enclosing region.
            Note: up vs down doesn't matter in this case
            because both specify the same exclusion area. */
-        place_lregion(dndest.nlx, dndest.nly, dndest.nhx, dndest.nhy, 0, 0, 0,
-                      0, LR_DOWNTELE, (d_level *) 0);
+        place_lregion(dndest.nlx, dndest.nly, dndest.nhx, dndest.nhy,
+                      0, 0, 0, 0, LR_DOWNTELE, (d_level *) 0);
     else if (up)
-        place_lregion(updest.lx, updest.ly, updest.hx, updest.hy, updest.nlx,
-                      updest.nly, updest.nhx, updest.nhy, LR_UPTELE,
-                      (d_level *) 0);
+        place_lregion(updest.lx, updest.ly, updest.hx, updest.hy,
+                      updest.nlx, updest.nly, updest.nhx, updest.nhy,
+                      LR_UPTELE, (d_level *) 0);
     else
-        place_lregion(dndest.lx, dndest.ly, dndest.hx, dndest.hy, dndest.nlx,
-                      dndest.nly, dndest.nhx, dndest.nhy, LR_DOWNTELE,
-                      (d_level *) 0);
+        place_lregion(dndest.lx, dndest.ly, dndest.hx, dndest.hy,
+                      dndest.nlx, dndest.nly, dndest.nhx, dndest.nhy,
+                      LR_DOWNTELE, (d_level *) 0);
+
+    /* might have just left solid rock and unblocked levitation */
+    switch_terrain();
 }
 
 /* place you on the special staircase */
@@ -1616,6 +1632,25 @@ level_difficulty()
              * The same applies to Vlad's Tower, although the increment
              * there is inconsequential compared to overall depth.
              */
+#if 0
+        /*
+         * The inside of the Wizard's Tower is also effectively a
+         * builds-up area, reached from a portal an arbitrary distance
+         * below rather than stairs 1 level beneath the entry level.
+         */
+        else if (On_W_tower_level(&u.uz) && In_W_tower(some_X, some_Y, &u.uz))
+            res += (fakewiz1.dlev - u.uz.dlev);
+            /*
+             * Handling this properly would need more information here:
+             * an inside/outside flag, or coordinates to calculate it.
+             * Unfortunately level difficulty may be wanted before
+             * coordinates have been chosen so simply extending this
+             * routine to take extra arguments is not sufficient to cope.
+             * The difference beyond naive depth-from-surface is small
+             * relative to the overall depth, so just ignore complications
+             * posed by W_tower.
+             */
+#endif /*0*/
     }
     return (xchar) res;
 }
@@ -1628,30 +1663,44 @@ lev_by_name(nam)
 const char *nam;
 {
     schar lev = 0;
-    s_level *slev;
+    s_level *slev = (s_level *)0;
     d_level dlev;
     const char *p;
     int idx, idxtoo;
+#if 0 /*JP*/
     char buf[BUFSZ];
+#endif
+    mapseen *mseen;
 
-    /* allow strings like "the oracle level" to find "oracle" */
-    if (!strncmpi(nam, "the ", 4))
-        nam += 4;
-    if ((p = strstri(nam, " level")) != 0 && p == eos((char *) nam) - 6) {
-        nam = strcpy(buf, nam);
-        *(eos(buf) - 6) = '\0';
-    }
-    /* hell is the old name, and wouldn't match; gehennom would match its
-       branch, yielding the castle level instead of the valley of the dead */
-    if (!strcmpi(nam, "gehennom") || !strcmpi(nam, "hell")) {
-        if (In_V_tower(&u.uz))
-            nam = " to Vlad's tower"; /* branch to... */
-        else
-            nam = "valley";
+    /* look at the player's custom level annotations first */
+    if ((mseen = find_mapseen_by_str(nam)) != 0) {
+        dlev = mseen->lev;
+    } else {
+        /* no matching annotation, check whether they used a name we know */
+
+#if 0 /*JP*//*\93ú\96{\8cê\82Å\82Í\8f\88\97\9d\82µ\82È\82¢*/
+        /* allow strings like "the oracle level" to find "oracle" */
+        if (!strncmpi(nam, "the ", 4))
+            nam += 4;
+        if ((p = strstri(nam, " level")) != 0 && p == eos((char *) nam) - 6) {
+            nam = strcpy(buf, nam);
+            *(eos(buf) - 6) = '\0';
+        }
+        /* hell is the old name, and wouldn't match; gehennom would match its
+           branch, yielding the castle level instead of valley of the dead */
+        if (!strcmpi(nam, "gehennom") || !strcmpi(nam, "hell")) {
+            if (In_V_tower(&u.uz))
+                nam = " to Vlad's tower"; /* branch to... */
+            else
+                nam = "valley";
+        }
+#endif
+
+        if ((slev = find_level(nam)) != 0)
+            dlev = slev->dlevel;
     }
 
-    if ((slev = find_level(nam)) != 0) {
-        dlev = slev->dlevel;
+    if (mseen || slev) {
         idx = ledger_no(&dlev);
         if ((dlev.dnum == u.uz.dnum
              /* within same branch, or else main dungeon <-> gehennom */
@@ -1663,7 +1712,7 @@ const char *nam;
                 wizard
                 || (level_info[idx].flags & (FORGOTTEN | VISITED))
                        == VISITED)) {
-            lev = depth(&slev->dlevel);
+            lev = depth(&dlev);
         }
     } else { /* not a specific level; try branch names */
         idx = find_branch(nam, (struct proto_dungeon *) 0);
@@ -1763,15 +1812,37 @@ int type;
 {
     switch (type) {
     case BR_PORTAL:
+/*JP
         return "Portal";
+*/
+        return "\96\82\96@\82Ì\93ü\82è\8cû";
     case BR_NO_END1:
+/*JP
         return "Connection";
+*/
+        return "\90Ú\91±\95\94";
     case BR_NO_END2:
+/*JP
         return "One way stair";
+*/
+        return "\88ê\95û\92Ê\8ds\82Ì\8aK\92i";
     case BR_STAIR:
+/*JP
         return "Stair";
+*/
+        return "\8aK\92i";
     }
+/*JP
     return " (unknown)";
+*/
+    return " (\95s\96¾)";
+}
+
+STATIC_OVL char
+chr_u_on_lvl(dlev)
+d_level *dlev;
+{
+    return u.uz.dnum == dlev->dnum && u.uz.dlevel == dlev->dlevel ? '*' : ' ';
 }
 
 /* Print all child branches between the lower and upper bounds. */
@@ -1791,8 +1862,17 @@ struct lchoice *lchoices_p;
     for (br = branches; br; br = br->next) {
         if (br->end1.dnum == dnum && lower_bound < br->end1.dlevel
             && br->end1.dlevel <= upper_bound) {
-            Sprintf(buf, "   %s to %s: %d", br_string(br->type),
+#if 0 /*JP:T*/
+            Sprintf(buf, "%c %s to %s: %d",
+                    bymenu ? chr_u_on_lvl(&br->end1) : ' ',
+                    br_string(br->type),
                     dungeons[br->end2.dnum].dname, depth(&br->end1));
+#else
+            Sprintf(buf, "%c %s\82©\82ç%s: %d",
+                    bymenu ? chr_u_on_lvl(&br->end1) : ' ',
+                    br_string(br->type),
+                    dungeons[br->end2.dnum].dname, depth(&br->end1));
+#endif
             if (bymenu)
                 tport_menu(win, buf, lchoices_p, &br->end1,
                            unreachable_level(&br->end1, FALSE));
@@ -1818,8 +1898,8 @@ xchar *rdgn;
     branch *br;
     anything any;
     struct lchoice lchoices;
-
     winid win = create_nhwindow(NHW_MENU);
+
     if (bymenu) {
         start_menu(win);
         lchoices.idx = 0;
@@ -1830,21 +1910,37 @@ xchar *rdgn;
         if (bymenu && In_endgame(&u.uz) && i != astral_level.dnum)
             continue;
         unplaced = unplaced_floater(dptr);
+/*JP
         descr = unplaced ? "depth" : "level";
+*/
+        descr = unplaced ? "\92n\89º" : "\83\8c\83x\83\8b";
         nlev = dptr->num_dunlevs;
         if (nlev > 1)
+#if 0 /*JP:T*/
             Sprintf(buf, "%s: %s %d to %d", dptr->dname, makeplural(descr),
                     dptr->depth_start, dptr->depth_start + nlev - 1);
+#else
+            Sprintf(buf, "%s: %s%d\82©\82ç%d", dptr->dname, descr,
+                    dptr->depth_start, dptr->depth_start + nlev - 1);
+#endif
         else
             Sprintf(buf, "%s: %s %d", dptr->dname, descr, dptr->depth_start);
 
         /* Most entrances are uninteresting. */
         if (dptr->entry_lev != 1) {
             if (dptr->entry_lev == nlev)
+/*JP
                 Strcat(buf, ", entrance from below");
+*/
+                Strcat(buf, ", \89º\82©\82ç\82Ì\93ü\82è\8cû");
             else
+#if 0 /*JP:T*/
                 Sprintf(eos(buf), ", entrance on %d",
                         dptr->depth_start + dptr->entry_lev - 1);
+#else
+                Sprintf(eos(buf), ", %d\82Ì\93ü\82è\8cû",
+                        dptr->depth_start + dptr->entry_lev - 1);
+#endif
         }
         if (bymenu) {
             any = zeroany;
@@ -1865,7 +1961,9 @@ xchar *rdgn;
             print_branch(win, i, last_level, slev->dlevel.dlevel, bymenu,
                          &lchoices);
 
-            Sprintf(buf, "   %s: %d", slev->proto, depth(&slev->dlevel));
+            Sprintf(buf, "%c %s: %d",
+                    chr_u_on_lvl(&slev->dlevel),
+                    slev->proto, depth(&slev->dlevel));
             if (Is_stronghold(&slev->dlevel))
                 Sprintf(eos(buf), " (tune %s)", tune);
             if (bymenu)
@@ -1885,7 +1983,10 @@ xchar *rdgn;
         menu_item *selected;
         int idx;
 
+/*JP
         end_menu(win, "Level teleport to where:");
+*/
+        end_menu(win, "\82Ç\82±\82É\8fu\8aÔ\88Ú\93®\82·\82é\81F");
         n = select_menu(win, PICK_ONE, &selected);
         destroy_nhwindow(win);
         if (n > 0) {
@@ -1905,11 +2006,19 @@ xchar *rdgn;
         if (br->end1.dnum == n_dgns) {
             if (first) {
                 putstr(win, 0, "");
+/*JP
                 putstr(win, 0, "Floating branches");
+*/
+                putstr(win, 0, "\95\82\93®\95ª\8aò");
                 first = FALSE;
             }
+#if 0 /*JP:T*/
             Sprintf(buf, "   %s to %s", br_string(br->type),
                     dungeons[br->end2.dnum].dname);
+#else
+            Sprintf(buf, "   %s\82©\82ç%s", br_string(br->type),
+                    dungeons[br->end2.dnum].dname);
+#endif
             putstr(win, 0, buf);
         }
     }
@@ -1917,29 +2026,50 @@ xchar *rdgn;
     /* I hate searching for the invocation pos while debugging. -dean */
     if (Invocation_lev(&u.uz)) {
         putstr(win, 0, "");
+#if 0 /*JP:T*/
         Sprintf(buf, "Invocation position @ (%d,%d), hero @ (%d,%d)",
                 inv_pos.x, inv_pos.y, u.ux, u.uy);
+#else
+        Sprintf(buf, "\94­\93®\88Ê\92u @ (%d,%d), \83v\83\8c\83C\83\84\81[ @ (%d,%d)",
+                inv_pos.x, inv_pos.y, u.ux, u.uy);
+#endif
         putstr(win, 0, buf);
-    }
-    /*
-     * The following is based on the assumption that the inter-level portals
-     * created by the level compiler (not the dungeon compiler) only exist
-     * one per level (currently true, of course).
-     */
-    else if (Is_earthlevel(&u.uz) || Is_waterlevel(&u.uz)
-             || Is_firelevel(&u.uz) || Is_airlevel(&u.uz)) {
+    } else {
         struct trap *trap;
+
+        /* if current level has a magic portal, report its location;
+           this assumes that there is at most one magic portal on any
+           given level; quest and ft.ludios have pairs (one in main
+           dungeon matched with one in the corresponding branch), the
+           elemental planes have singletons (connection to next plane) */
+        *buf = '\0';
         for (trap = ftrap; trap; trap = trap->ntrap)
             if (trap->ttyp == MAGIC_PORTAL)
                 break;
 
-        putstr(win, 0, "");
         if (trap)
-            Sprintf(buf, "Portal @ (%d,%d), hero @ (%d,%d)", trap->tx,
-                    trap->ty, u.ux, u.uy);
-        else
-            Sprintf(buf, "No portal found.");
-        putstr(win, 0, buf);
+            Sprintf(buf, "Portal @ (%d,%d), hero @ (%d,%d)",
+                    trap->tx, trap->ty, u.ux, u.uy);
+
+        /* only report "no portal found" when actually expecting a portal */
+#if 0 /*JP:T*/
+        else if (Is_earthlevel(&u.uz) || Is_waterlevel(&u.uz)
+                 || Is_firelevel(&u.uz) || Is_airlevel(&u.uz)
+                 || Is_qstart(&u.uz) || at_dgn_entrance("The Quest")
+                 || Is_knox(&u.uz))
+#else
+        else if (Is_earthlevel(&u.uz) || Is_waterlevel(&u.uz)
+                 || Is_firelevel(&u.uz) || Is_airlevel(&u.uz)
+                 || Is_qstart(&u.uz) || at_dgn_entrance("\83N\83G\83X\83g")
+                 || Is_knox(&u.uz))
+#endif
+            Strcpy(buf, "No portal found.");
+
+        /* only give output if we found a portal or expected one and didn't */
+        if (*buf) {
+            putstr(win, 0, "");
+            putstr(win, 0, buf);
+        }
     }
 
     display_nhwindow(win, TRUE);
@@ -2007,24 +2137,36 @@ donamelevel()
     if (!(mptr = find_mapseen(&u.uz)))
         return 0;
 
+    nbuf[0] = '\0';
+#ifdef EDIT_GETLIN
+    if (mptr->custom) {
+        (void) strncpy(nbuf, mptr->custom, BUFSZ);
+        nbuf[BUFSZ - 1] = '\0';
+    }
+#else
     if (mptr->custom) {
         char tmpbuf[BUFSZ];
-#if 0 /*JP*/
+
+#if 0 /*JP:T*/
         Sprintf(tmpbuf, "Replace annotation \"%.30s%s\" with?", mptr->custom,
-                strlen(mptr->custom) > 30 ? "..." : "");
-        getlin(tmpbuf, nbuf);
+                (strlen(mptr->custom) > 30) ? "..." : "");
 #else
-        Sprintf(tmpbuf, "\8c»\8dÝ\82Ì\96¼\91O\81u%.30s%s\81v\82ð\89½\82É\8f\91\82«\8a·\82¦\82é\81H", mptr->custom,
+        Sprintf(tmpbuf, "\8c»\8dÝ\82Ì\83\81\83\82\81u%.30s%s\81v\82ð\89½\82É\8f\91\82«\8a·\82¦\82é\81H", mptr->custom,
                 strlen(mptr->custom) > 30 ? "..." : "");
-        getlin(tmpbuf, nbuf);
 #endif
+        getlin(tmpbuf, nbuf);
     } else
+#endif
 /*JP
         getlin("What do you want to call this dungeon level?", nbuf);
 */
         getlin("\82±\82Ì\8aK\82ð\89½\82Æ\8cÄ\82Ô\81H", nbuf);
-    if (index(nbuf, '\033'))
+
+    /* empty input or ESC means don't add or change annotation;
+       space-only means discard current annotation without adding new one */
+    if (!*nbuf || *nbuf == '\033')
         return 0;
+    /* strip leading and trailing spaces, compress out consecutive spaces */
     (void) mungspaces(nbuf);
 
     /* discard old annotation, if any */
@@ -2033,7 +2175,8 @@ donamelevel()
         mptr->custom = (char *) 0;
         mptr->custom_lth = 0;
     }
-    /* add new annotation, unless it's empty or a single space */
+    /* add new annotation, unless it's all spaces (which will be an
+       empty string after mungspaces() above) */
     if (*nbuf && strcmp(nbuf, " ")) {
         mptr->custom = dupstr(nbuf);
         mptr->custom_lth = strlen(mptr->custom);
@@ -2055,6 +2198,20 @@ d_level *lev;
     return mptr;
 }
 
+STATIC_OVL mapseen *
+find_mapseen_by_str(s)
+const char *s;
+{
+    mapseen *mptr;
+
+    for (mptr = mapseenchn; mptr; mptr = mptr->next)
+        if (mptr->custom && !strcmpi(s, mptr->custom))
+            break;
+
+    return mptr;
+}
+
+
 void
 forget_mapseen(ledger_num)
 int ledger_num;
@@ -2084,6 +2241,40 @@ int ledger_num;
     }
 }
 
+void
+rm_mapseen(ledger_num)
+int ledger_num;
+{
+    mapseen *mptr, *mprev = (mapseen *)0;
+    struct cemetery *bp, *bpnext;
+
+    for (mptr = mapseenchn; mptr; mprev = mptr, mptr = mptr->next)
+        if (dungeons[mptr->lev.dnum].ledger_start + mptr->lev.dlevel
+            == ledger_num)
+            break;
+
+    if (!mptr)
+        return;
+
+    if (mptr->custom)
+        free((genericptr_t) mptr->custom);
+
+    bp = mptr->final_resting_place;
+    while (bp) {
+        bpnext = bp->next;
+        free(bp);
+        bp = bpnext;
+    }
+
+    if (mprev) {
+        mprev->next = mptr->next;
+        free(mptr);
+    } else {
+        mapseenchn = mptr->next;
+        free(mptr);
+    }
+}
+
 STATIC_OVL void
 save_mapseen(fd, mptr)
 int fd;
@@ -2103,7 +2294,7 @@ mapseen *mptr;
     bwrite(fd, (genericptr_t) &mptr->custom_lth, sizeof mptr->custom_lth);
     if (mptr->custom_lth)
         bwrite(fd, (genericptr_t) mptr->custom, mptr->custom_lth);
-    bwrite(fd, (genericptr_t) &mptr->msrooms, sizeof mptr->msrooms);
+    bwrite(fd, (genericptr_t) mptr->msrooms, sizeof mptr->msrooms);
     savecemetery(fd, WRITE_SAVE, &mptr->final_resting_place);
 }
 
@@ -2134,12 +2325,56 @@ int fd;
         load->custom[load->custom_lth] = '\0';
     } else
         load->custom = 0;
-    mread(fd, (genericptr_t) &load->msrooms, sizeof load->msrooms);
+    mread(fd, (genericptr_t) load->msrooms, sizeof load->msrooms);
     restcemetery(fd, &load->final_resting_place);
 
     return load;
 }
 
+/* to support '#stats' wizard-mode command */
+void
+overview_stats(win, statsfmt, total_count, total_size)
+winid win;
+const char *statsfmt;
+long *total_count, *total_size;
+{
+    char buf[BUFSZ], hdrbuf[QBUFSZ];
+    long ocount, osize, bcount, bsize, acount, asize;
+    struct cemetery *ce;
+    mapseen *mptr = find_mapseen(&u.uz);
+
+    ocount = bcount = acount = osize = bsize = asize = 0L;
+    for (mptr = mapseenchn; mptr; mptr = mptr->next) {
+        ++ocount;
+        osize += (long) sizeof *mptr;
+        for (ce = mptr->final_resting_place; ce; ce = ce->next) {
+            ++bcount;
+            bsize += (long) sizeof *ce;
+        }
+        if (mptr->custom_lth) {
+            ++acount;
+            asize += (long) (mptr->custom_lth + 1);
+        }
+    }
+
+    Sprintf(hdrbuf, "general, size %ld", (long) sizeof (mapseen));
+    Sprintf(buf, statsfmt, hdrbuf, ocount, osize);
+    putstr(win, 0, buf);
+    if (bcount) {
+        Sprintf(hdrbuf, "cemetery, size %ld",
+                (long) sizeof (struct cemetery));
+        Sprintf(buf, statsfmt, hdrbuf, bcount, bsize);
+        putstr(win, 0, buf);
+    }
+    if (acount) {
+        Sprintf(hdrbuf, "annotations, text");
+        Sprintf(buf, statsfmt, hdrbuf, acount, asize);
+        putstr(win, 0, buf);
+    }
+    *total_count += ocount + bcount + acount;
+    *total_size += osize + bsize + asize;
+}
+
 /* Remove all mapseen objects for a particular dnum.
  * Useful during quest expulsion to remove quest levels.
  * [No longer deleted, just marked as unreachable.  #overview will
@@ -2379,7 +2614,7 @@ recalc_mapseen()
                 if (ltyp == DRAWBRIDGE_UP)
                     ltyp = db_under_typ(levl[x][y].drawbridgemask);
                 if ((mtmp = m_at(x, y)) != 0
-                    && mtmp->m_ap_type == M_AP_FURNITURE && canseemon(mtmp))
+                    && M_AP_TYPE(mtmp) == M_AP_FURNITURE && canseemon(mtmp))
                     ltyp = cmap_to_type(mtmp->mappearance);
                 lastseentyp[x][y] = ltyp;
             }
@@ -2445,6 +2680,9 @@ recalc_mapseen()
             /*  An automatic annotation is added to the Castle and
              *  to Fort Ludios once their structure's main entrance
              *  has been seen (in person or via magic mapping).
+             *  For the Fort, that entrance is just a secret door
+             *  which will be converted into a regular one when
+             *  located (or destroyed).
              * DOOR: possibly a lowered drawbridge's open portcullis;
              * DBWALL: a raised drawbridge's "closed door";
              * DRAWBRIDGE_DOWN: the span provided by lowered bridge,
@@ -2454,15 +2692,33 @@ recalc_mapseen()
              *  the adjacent DBWALL has been seen.
              */
             case DOOR:
+                if (Is_knox(&u.uz)) {
+                    int ty, tx = x - 4;
+
+                    /* Throne is four columns left, either directly in
+                     * line or one row higher or lower, and doesn't have
+                     * to have been seen yet.
+                     *   ......|}}}.
+                     *   ..\...S}...
+                     *   ..\...S}...
+                     *   ......|}}}.
+                     * For 3.6.0 and earlier, it was always in direct line:
+                     * both throne and door on the lower of the two rows.
+                     */
+                    for (ty = y - 1; ty <= y + 1; ++ty)
+                        if (isok(tx, ty) && IS_THRONE(levl[tx][ty].typ)) {
+                            mptr->flags.ludios = 1;
+                            break;
+                        }
+                    break;
+                }
                 if (is_drawbridge_wall(x, y) < 0)
                     break;
-            /* else FALLTHRU */
+                /*FALLTHRU*/
             case DBWALL:
             case DRAWBRIDGE_DOWN:
                 if (Is_stronghold(&u.uz))
                     mptr->flags.castle = 1, mptr->flags.castletune = 1;
-                else if (Is_knox(&u.uz))
-                    mptr->flags.ludios = 1;
                 break;
             default:
                 break;
@@ -2494,8 +2750,7 @@ recalc_mapseen()
 }
 
 /*ARGUSED*/
-/* valley and sanctum levels get automatic annotation once temple is entered
- */
+/* valley and sanctum levels get automatic annotation once temple is entered */
 void
 mapseen_temple(priest)
 struct monst *priest UNUSED; /* currently unused; might be useful someday */
@@ -2589,7 +2844,7 @@ const char *obj;
 /*JP
         return index(vowels, *obj) ? "an" : "a";
 */
-       return "";
+        return "";
     case 2:
 /*JP
         return "some";
@@ -2621,11 +2876,7 @@ branch *br;
 */
         return closed_portal ? "\95\95\88ó\82³\82ê\82½\96\82\96@\82Ì\93ü\8cû" : "\96\82\96@\82Ì\93ü\8cû";
     case BR_NO_END1:
-#if 0 /*JP*/
         return "Connection";
-#else
-        return "\90Ú\91±\95\94";
-#endif
     case BR_NO_END2:
 /*JP
         return br->end1_up ? "One way stairs up" : "One way stairs down";
@@ -2642,7 +2893,7 @@ branch *br;
 }
 
 /* get the name of an endgame level; topten.c does something similar */
-STATIC_OVL const char *
+const char *
 endgamelevelname(outbuf, indx)
 char *outbuf;
 int indx;
@@ -2696,7 +2947,7 @@ STATIC_OVL const char *
 shop_string(rtype)
 int rtype;
 {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
     const char *str = "shop"; /* catchall */
 #else
     const char *str = "\93X"; /* catchall */
@@ -2813,7 +3064,7 @@ char *outbuf;
 #endif
 #define COMMA (i++ > 0 ? ", " : PREFIX)
 /* "iterate" once; safe to use as ``if (cond) ADDTOBUF(); else whatever;'' */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
 #define ADDNTOBUF(nam, var)                                                  \
     do {                                                                     \
         if (var)                                                             \
@@ -2884,10 +3135,6 @@ boolean printdun;
     if (In_endgame(&mptr->lev))
         Sprintf(buf, "%s%s:", TAB, endgamelevelname(tmpbuf, i));
     else
-        /* FIXME: when this branch has only one level (Ft.Ludios),
-         * listing "Level 1:" for it might confuse inexperienced
-         * players into thinking there's more than one.
-         */
 /*JP
         Sprintf(buf, "%sLevel %d:", TAB, i);
 */
@@ -2902,14 +3149,18 @@ boolean printdun;
     }
     /* [perhaps print custom annotation on its own line when it's long] */
     if (mptr->custom)
-        Sprintf(eos(buf), " (%s)", mptr->custom);
+        Sprintf(eos(buf), " \"%s\"", mptr->custom);
     if (on_level(&u.uz, &mptr->lev))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Sprintf(eos(buf), " <- You %s here.",
-                (!final || (final == 1 && how == ASCENDED)) ? "are" : "were");
+                (!final || (final == 1 && how == ASCENDED)) ? "are"
+                  : (final == 1 && how == ESCAPED) ? "left from"
+                    : "were");
 #else
-        Sprintf(eos(buf), " <- \82±\82±\82É%s\81D",
-                (!final || (final == 1 && how == ASCENDED)) ? "\82¢\82é" : "\82¢\82½");
+        Sprintf(eos(buf), " <- \82±\82±%s\81D",
+                (!final || (final == 1 && how == ASCENDED)) ? "\82É\82¢\82é"
+                  : (final == 1 && how == ESCAPED) ? "\82©\82ç\94²\82¯\82½"
+                    : "\82É\82¢\82½");
 #endif
     putstr(win, !final ? ATR_BOLD : 0, buf);
 
@@ -2994,7 +3245,7 @@ boolean printdun;
 */
         Sprintf(buf, "%s\83f\83\8b\83t\83@\83C\82Ì\90_\93a\81D", PREFIX);
     } else if (In_sokoban(&mptr->lev)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Sprintf(buf, "%s%s.", PREFIX,
                 mptr->flags.sokosolved ? "Solved" : "Unsolved");
 #else
@@ -3011,13 +3262,8 @@ boolean printdun;
         Sprintf(buf, "%sA primitive area.", PREFIX);
 */
         Sprintf(buf, "%s\92P\8f\83\82È\95\94\89®\81D", PREFIX);
-    } else if (mptr->flags.quest_summons) {
-/*JP
-        Sprintf(buf, "%sSummoned by %s.", PREFIX, ldrname());
-*/
-        Sprintf(buf, "%s%s\82©\82ç\8cÄ\82Ñ\8fo\82³\82ê\82½\81D", PREFIX, ldrname());
     } else if (on_level(&mptr->lev, &qstart_level)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Sprintf(buf, "%sHome%s.", PREFIX,
                 mptr->flags.unreachable ? " (no way back...)" : "");
 #else
@@ -3060,15 +3306,23 @@ boolean printdun;
     }
     if (*buf)
         putstr(win, 0, buf);
+    /* quest entrance is not mutually-exclusive with bigroom or rogue level */
+    if (mptr->flags.quest_summons) {
+/*JP
+        Sprintf(buf, "%sSummoned by %s.", PREFIX, ldrname());
+*/
+        Sprintf(buf, "%s%s\82©\82ç\8cÄ\82Ñ\8fo\82³\82ê\82½\81D", PREFIX, ldrname());
+        putstr(win, 0, buf);
+    }
 
     /* print out branches */
     if (mptr->br) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Sprintf(buf, "%s%s to %s", PREFIX, br_string2(mptr->br),
                 dungeons[mptr->br->end2.dnum].dname);
 #else
         Sprintf(buf, "%s%s\82Ö\82Ì%s", PREFIX, dungeons[mptr->br->end2.dnum].dname,
-               br_string2(mptr->br));
+                br_string2(mptr->br));
 #endif
 
         /* Since mapseen objects are printed out in increasing order
@@ -3101,7 +3355,7 @@ boolean printdun;
             if (died_here) {
                 /* disclosure occurs before bones creation, so listing dead
                    hero here doesn't give away whether bones are produced */
-                formatkiller(tmpbuf, sizeof tmpbuf, how);
+                formatkiller(tmpbuf, sizeof tmpbuf, how, TRUE);
 #if 0 /*JP*/
                 /* rephrase a few death reasons to work with "you" */
                 (void) strsubst(tmpbuf, " himself", " yourself");