-/* 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: 1523308357 2018/04/09 21:12:37 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.87 $ */
/* 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"
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,
panic(
"Premature EOF on dungeon description file!\r\nExpected %d bytes - got %d.",
(size * nitems), (size * cnt));
- terminate(EXIT_FAILURE);
+ nh_terminate(EXIT_FAILURE);
}
}
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]);
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;
* 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:
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,
+ /* TODO: strip "dummy" out all the way here,
so that it's hidden from <ctrl/O> feedback. */
}
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);
}
/* place you on the special staircase */
* 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;
}
const char *nam;
{
schar lev = 0;
- s_level *slev;
+ s_level *slev = (s_level *)0;
d_level dlev;
const char *p;
int idx, idxtoo;
char buf[BUFSZ];
+ 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 */
+
+ /* 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";
+ }
+
+ 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 */
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);
case BR_PORTAL:
return "Portal";
case BR_NO_END1:
+/*JP
return "Connection";
+*/
+ return "\90Ú\91±\95\94";
case BR_NO_END2:
return "One way stair";
case BR_STAIR:
return " (unknown)";
}
+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. */
STATIC_OVL void
print_branch(win, dnum, lower_bound, upper_bound, bymenu, 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),
+ 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));
if (bymenu)
tport_menu(win, buf, lchoices_p, &br->end1,
branch *br;
anything any;
struct lchoice lchoices;
-
winid win = create_nhwindow(NHW_MENU);
+
if (bymenu) {
start_menu(win);
lchoices.idx = 0;
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)
Sprintf(buf, "Invocation position @ (%d,%d), hero @ (%d,%d)",
inv_pos.x, inv_pos.y, u.ux, u.uy);
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 */
+ 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))
+ 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);
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*/
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 */
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);
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;
}
}
+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;
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
/* 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,
* 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;
*/
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";
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);
*/
}
/* [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*/
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);
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*/
Sprintf(buf, "%sHome%s.", PREFIX,
}
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 (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");