more #overview changes

Change how overview data is handled if/when you get expelled from
the quest:  mark quest levels as unreachable rather than discarding their
overview data, so that it can be included in end of game disclosure (and
can be revived if you manage to return to your quest branch by invoking
the W quest artifact).

     Order of endgame levels was odd:  earth followed by astral, water,
fire, and air, because the code didn't know how to insert in front of the
first one visited.  Now it does.  Placement of endgame levels was sub-
optimal:  since that has the highest internal dungeon branch number, it
came out last.  Now it is forced to come out first, so that it appears
above the dungeon.  And use "Plane of Earth" for level name rather than
"Plane 1", and so on for the others, when in the endgame.

     Since I'm bumping EDITLEVEL due to adding mapseen.flags.unreachable,
I am also inserting u.uevent.uvibrated now so that it won't trigger another
EDITLEVEL increment.  At the moment it doesn't do anything except get set
when you receive the "you feel strange vibrations <under you>" message.
The level where that occurs will eventually have an automatic annotation
of some sort.
This commit is contained in:
nethack.rankin
2012-04-14 08:31:05 +00:00
parent 99a51ab3b2
commit 4c3939bb76
6 changed files with 143 additions and 67 deletions

View File

@@ -219,6 +219,7 @@ typedef struct mapseen {
Bitfield(shoptype, 5);
} feat;
struct mapseen_flags {
Bitfield(unreachable, 1); /* can't get back to this level */
Bitfield(forgot, 1); /* player has forgotten about this level */
Bitfield(knownbones, 1); /* player aware of bones */
Bitfield(oracle, 1);
@@ -226,8 +227,8 @@ typedef struct mapseen {
Bitfield(bigroom, 1);
Bitfield(castle, 1);
Bitfield(castletune, 1); /* add tune hint to castle annotation */
Bitfield(valley, 1);
Bitfield(valley, 1);
Bitfield(msanctum, 1);
Bitfield(ludios, 1);
# ifdef REINCARNATION

View File

@@ -13,7 +13,7 @@
* Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files.
*/
#define EDITLEVEL 54
#define EDITLEVEL 55
#define COPYRIGHT_BANNER_A \
"NetHack, Copyright 1985-2012"

View File

@@ -50,6 +50,7 @@ struct u_event {
Bitfield(uhand_of_elbereth,2); /* became Hand of Elbereth */
#endif
Bitfield(udemigod,1); /* killed the wiz */
Bitfield(uvibrated,1); /* stepped on "vibrating square" */
Bitfield(ascended,1); /* has offered the Amulet */
};

View File

@@ -20,6 +20,8 @@ STATIC_DCL int NDECL(currentlevel_rewrite);
STATIC_DCL void NDECL(final_level);
/* static boolean FDECL(badspot, (XCHAR_P,XCHAR_P)); */
extern int n_dgns; /* number of dungeons, from dungeon.c */
static NEARDATA const char drop_types[] =
{ ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, 0 };
@@ -1100,6 +1102,11 @@ boolean at_stairs, falling, portal;
/* discard unreachable levels; keep #0 */
for (l_idx = maxledgerno(); l_idx > 0; --l_idx)
delete_levelfile(l_idx);
#ifdef DUNGEON_OVERVIEW
/* mark #overview data for all dungeon branches as uninteresting */
for (l_idx = 0; l_idx < n_dgns; ++l_idx)
remdun_mapseen(l_idx);
#endif
}
#ifdef REINCARNATION

View File

@@ -24,8 +24,8 @@ struct proto_dungeon {
int n_brs; /* number of tmpbranch entries */
};
int n_dgns; /* number of dungeons (used here, */
/* and mklev.c) */
int n_dgns; /* number of dungeons (also used */
/* in mklev.c and do.c) */
static branch *branches = (branch *) 0; /* dungeon branch list */
struct lchoice {
@@ -65,8 +65,10 @@ STATIC_DCL void FDECL(save_mapseen, (int, mapseen *));
STATIC_DCL mapseen *FDECL(find_mapseen, (d_level *));
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 *));
#endif /* DUNGEON_OVERVIEW */
@@ -1872,18 +1874,16 @@ d_level *dest;
}
}
/* add a custom name to the current level */
/* #annotate command - add a custom name to the current level */
int
donamelevel()
{
mapseen *mptr;
char qbuf[QBUFSZ]; /* Buffer for query text */
char nbuf[BUFSZ]; /* Buffer for response */
if (!(mptr = find_mapseen(&u.uz))) return 0;
Sprintf(qbuf,"What do you want to call this dungeon level? ");
getlin(qbuf, nbuf);
getlin("What do you want to call this dungeon level?", nbuf);
if (index(nbuf, '\033')) return 0;
/* discard old annotation, if any */
@@ -1997,6 +1997,8 @@ int fd;
/* 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
* ignore such levels, end of game disclosure will include them.]
*/
void
remdun_mapseen(dnum)
@@ -2007,6 +2009,10 @@ int dnum;
mptraddr = &mapseenchn;
while ((mptr = *mptraddr) != 0) {
if (mptr->lev.dnum == dnum) {
#if 1 /* use this... */
mptr->flags.unreachable = 1;
}
#else /* old deletion code */
*mptraddr = mptr->next;
if (mptr->custom)
free((genericptr_t) mptr->custom);
@@ -2014,6 +2020,7 @@ int dnum;
savecemetery(-1, FREE_SAVE, &mptr->final_resting_place);
free((genericptr_t) mptr);
} else
#endif
mptraddr = &mptr->next;
}
}
@@ -2025,7 +2032,7 @@ d_level *lev;
/* Create a level and insert in "sorted" order. This is an insertion
* sort first by dungeon (in order of discovery) and then by level number.
*/
mapseen *mptr, *init, *old;
mapseen *mptr, *init, *prev;
init = (mapseen *) alloc(sizeof *init);
(void) memset((genericptr_t)init, 0, sizeof *init);
@@ -2040,25 +2047,20 @@ d_level *lev;
init->lev.dnum = lev->dnum;
init->lev.dlevel = lev->dlevel;
if (!mapseenchn) {
/* walk until we get to the place where we should insert init */
for (mptr = mapseenchn, prev = 0; mptr; prev = mptr, mptr = mptr->next)
if (mptr->lev.dnum > init->lev.dnum ||
(mptr->lev.dnum == init->lev.dnum &&
mptr->lev.dlevel > init->lev.dlevel))
break;
if (!prev) {
init->next = mapseenchn;
mapseenchn = init;
return;
} else {
mptr = prev->next;
prev->next = init;
init->next = mptr;
}
/* walk until we get to the place where we should
* insert init between mptr and mptr->next
*/
for (mptr = mapseenchn; mptr->next; mptr = mptr->next) {
if (mptr->next->lev.dnum == init->lev.dnum) break;
}
for (; mptr->next; mptr = mptr->next) {
if ((mptr->next->lev.dnum != init->lev.dnum) ||
(mptr->next->lev.dlevel > init->lev.dlevel)) break;
}
old = mptr->next;
mptr->next = init;
init->next = old;
}
#define INTEREST(feat) \
@@ -2082,8 +2084,8 @@ interest_mapseen(mptr)
mapseen *mptr;
{
if (on_level(&u.uz, &mptr->lev)) return TRUE;
if (mptr->flags.forgot) return FALSE;
if (In_endgame(&u.uz) && !In_endgame(&mptr->lev)) return FALSE;
if (mptr->flags.unreachable || mptr->flags.forgot) return FALSE;
/* level is of interest if it has an auto-generated annotation */
if (mptr->flags.oracle || mptr->flags.bigroom ||
# ifdef REINCARNATION
mptr->flags.roguelevel ||
@@ -2095,6 +2097,12 @@ mapseen *mptr;
be furthest one reached, unless level teleporting in wizard mode) */
if (In_sokoban(&mptr->lev) &&
(In_sokoban(&u.uz) || !mptr->flags.sokosolved)) return TRUE;
/* when in the endgame, list all endgame levels visited, whether they
have annotations or not, so that #overview doesn't become extremely
sparse once the rest of the dungeon has been flagged as unreachable */
if (In_endgame(&u.uz)) return In_endgame(&mptr->lev);
/* level is of interest if it has non-zero feature count or known bones
or user annotation or known connection to another dungeon brancth */
return (INTEREST(mptr->feat) ||
(mptr->final_resting_place &&
(mptr->flags.knownbones || wizard)) ||
@@ -2109,17 +2117,34 @@ recalc_mapseen()
struct monst *mtmp;
struct cemetery *bp, **bonesaddr;
unsigned i, ridx;
int x, y, count, atmp;
int x, y, ltyp, count, atmp;
/* Should not happen in general, but possible if in the process
* of being booted from the quest. The mapseen object gets
* removed during the expulsion but prior to leaving the level
* [Since quest explusion no longer deletes quest mapseen data,
* null return from find_mapseen() should now be impossible.]
*/
if (!(mptr = find_mapseen(&u.uz))) return;
/* reset all features; mptr->feat.* = 0; */
(void) memset((genericptr_t) &mptr->feat, 0, sizeof mptr->feat);
/* reset most flags; some level-specific ones are left as-is */
if (mptr->flags.unreachable) {
mptr->flags.unreachable = 0; /* reached it; Eye of the Aethiopica? */
if (In_quest(&u.uz)) {
mapseen *mptrtmp = mapseenchn;
/* when quest was unreachable due to ejection and portal removal,
getting back to it via arti-invoke should revive annotation
data for all quest levels, not just the one we're on now */
do {
if (mptrtmp->lev.dnum == mptr->lev.dnum)
mptrtmp->flags.unreachable = 0;
mptrtmp = mptrtmp->next;
} while (mptrtmp);
}
}
mptr->flags.knownbones = 0;
mptr->flags.sokosolved = In_sokoban(&u.uz) && !Sokoban;
/* mptr->flags.bigroom retains previous value when hero can't see */
@@ -2190,17 +2215,21 @@ recalc_mapseen()
* the ability to have non-dungeon glyphs float above the last known
* dungeon glyph (i.e. items on fountains).
*/
if (!Levitation)
lastseentyp[u.ux][u.uy] = levl[u.ux][u.uy].typ;
for (x = 1; x < COLNO; x++) {
for (y = 0; y < ROWNO; y++) {
if (cansee(x, y)) {
mtmp = m_at(x, y);
lastseentyp[x][y] =
(mtmp && mtmp->m_ap_type == M_AP_FURNITURE && canseemon(mtmp)) ?
cmap_to_type(mtmp->mappearance) :
levl[x][y].typ;
if (cansee(x, y) || (x == u.ux && y == u.uy && !Levitation)) {
ltyp = levl[x][y].typ;
if (ltyp == DRAWBRIDGE_UP)
switch (levl[x][y].drawbridgemask & DB_UNDER) {
case DB_ICE: ltyp = ICE; break;
case DB_LAVA: ltyp = LAVAPOOL; break;
case DB_MOAT: ltyp = MOAT; break;
default: ltyp = STONE; break;
}
if ((mtmp = m_at(x, y)) != 0 &&
mtmp->m_ap_type == M_AP_FURNITURE && canseemon(mtmp))
ltyp = cmap_to_type(mtmp->mappearance);
lastseentyp[x][y] = ltyp;
}
switch (lastseentyp[x][y]) {
@@ -2323,6 +2352,7 @@ int roomno;
mptr->msrooms[roomno].seen = 1;
}
/* #overview command */
int
dooverview()
{
@@ -2330,32 +2360,53 @@ dooverview()
return 0;
}
/* called for #overview or for end of game disclosure */
void
show_overview(why, reason)
int why; /* 0 => #overview command,
1 or 2 => end of game disclosure (1: alive, 2: dead) */
1 or 2 => final disclosure (1: hero lived, 2: hero died) */
int reason; /* how hero died; used when disclosing end-of-game level */
{
winid win;
mapseen *mptr;
int lastdun = -1;
/* lazy intialization */
(void) recalc_mapseen();
win = create_nhwindow(NHW_MENU);
for (mptr = mapseenchn; mptr; mptr = mptr->next) {
/* only print out info for a level or a dungeon if interest */
if (why > 0 || interest_mapseen(mptr)) {
print_mapseen(win, mptr, why, reason,
(boolean)(mptr->lev.dnum != lastdun));
lastdun = mptr->lev.dnum;
}
}
/* show the endgame levels before the rest of the dungeon,
so that the Planes (dnum 5-ish) come out above main dungeon (dnum 0) */
if (In_endgame(&u.uz))
traverse_mapseenchn(TRUE, win, why, reason, &lastdun);
/* if game is over or we're not in the endgame yet, show the dungeon */
if (why > 0 || !In_endgame(&u.uz))
traverse_mapseenchn(FALSE, win, why, reason, &lastdun);
display_nhwindow(win, TRUE);
destroy_nhwindow(win);
}
/* display endgame levels or non-endgame levels, not both */
STATIC_OVL void
traverse_mapseenchn(viewendgame, win, why, reason, lastdun_p)
boolean viewendgame;
winid win;
int why, reason, *lastdun_p;
{
mapseen *mptr;
boolean showheader;
for (mptr = mapseenchn; mptr; mptr = mptr->next) {
if (viewendgame ^ In_endgame(&mptr->lev)) continue;
/* only print out info for a level or a dungeon if interest */
if (why > 0 || interest_mapseen(mptr)) {
showheader = (boolean)(mptr->lev.dnum != *lastdun_p);
print_mapseen(win, mptr, why, reason, showheader);
*lastdun_p = mptr->lev.dnum;
}
}
}
STATIC_OVL const char *
seen_string(x, obj)
xchar x;
@@ -2393,6 +2444,29 @@ branch *br;
return "(unknown)";
}
/* get the name of an endgame level; topten.c does something similar */
STATIC_OVL const char *
endgamelevelname(outbuf, indx)
char *outbuf;
int indx;
{
const char *planename = 0;
*outbuf = '\0';
switch (indx) {
case -5: Strcpy(outbuf, "Astral Plane"); break;
case -4: planename = "Water"; break;
case -3: planename = "Fire"; break;
case -2: planename = "Air"; break;
case -1: planename = "Earth"; break;
}
if (planename)
Sprintf(outbuf, "Plane of %s", planename);
else if (!*outbuf)
Sprintf(outbuf, "unknown plane #%d", indx);
return outbuf;
}
STATIC_OVL const char *
shop_string(rtype)
int rtype;
@@ -2461,11 +2535,11 @@ char *outbuf;
} while (0)
STATIC_OVL void
print_mapseen(win, mptr, final, reason, printdun)
print_mapseen(win, mptr, final, how, printdun)
winid win;
mapseen *mptr;
int final; /* 0: not final; 1: game over, alive; 2: game over, dead */
int reason; /* cause of death; only used if final==2 and mptr->lev==u.uz */
int how; /* cause of death; only used if final==2 and mptr->lev==u.uz */
boolean printdun;
{
char buf[BUFSZ], tmpbuf[BUFSZ];
@@ -2492,24 +2566,13 @@ boolean printdun;
Sprintf(buf, "%s: levels %d to %d",
dungeons[mptr->lev.dnum].dname, depthstart,
depthstart + dungeons[mptr->lev.dnum].dunlev_ureached - 1);
putstr(win, ATR_INVERSE, buf);
putstr(win, !final ? ATR_INVERSE : 0, buf);
}
/* calculate level number */
i = depthstart + mptr->lev.dlevel - 1;
if (Is_astralevel(&mptr->lev))
Sprintf(buf, "%sAstral Plane:", TAB);
else if (In_endgame(&mptr->lev))
/* Negative numbers are mildly confusing, since they are never
* shown to the player, except in wizard mode. We could show
* "Level -1" for the earth plane, for example. Instead,
* show "Plane 1" for the earth plane to differentiate from
* level 1. There's not much to show, but maybe the player
* wants to #annotate them for some reason such as keeping
* track of encounters with the Wizard.
* [TODO: change this to be "Plane of <element-name>:"]
*/
Sprintf(buf, "%sPlane %i:", TAB, -i);
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
@@ -2530,7 +2593,8 @@ boolean printdun;
if (mptr->custom)
Sprintf(eos(buf), " (%s)", mptr->custom);
if (on_level(&u.uz, &mptr->lev))
Sprintf(eos(buf), " <- You %s here", !final ? "are" : "were");
Sprintf(eos(buf), " <- You %s here.",
(!final || (final == 1 && how == ASCENDED)) ? "are" : "were");
putstr(win, !final ? ATR_BOLD : 0, buf);
if (mptr->flags.forgot) return;
@@ -2539,7 +2603,6 @@ boolean printdun;
buf[0] = 0;
i = 0; /* interest counter */
/* List interests in an order vaguely corresponding to
* how important they are.
*/
@@ -2592,6 +2655,9 @@ boolean printdun;
} else if (mptr->flags.roguelevel) {
Sprintf(buf, "%sA primitive area.", PREFIX);
# endif
} else if (on_level(&mptr->lev, &qstart_level)) {
Sprintf(buf, "%sHome%s.", PREFIX,
mptr->flags.unreachable ? " (no way back...)" : "");
} else if (mptr->flags.ludios) {
/* presence of the ludios branch in #overview output indicates that
the player has made it onto the level; presence of this annotation
@@ -2634,7 +2700,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, reason);
formatkiller(tmpbuf, sizeof tmpbuf, how);
/* rephrase a few death reasons to work with "you" */
(void) strsubst(tmpbuf, " himself", " yourself");
(void) strsubst(tmpbuf, " herself", " yourself");

View File

@@ -1638,6 +1638,7 @@ invocation_message()
else Sprintf(buf, "under your %s", makeplural(body_part(FOOT)));
You_feel("a strange vibration %s.", buf);
u.uevent.uvibrated = 1;
if (otmp && otmp->spe == 7 && otmp->lamplit)
pline("%s %s!", The(xname(otmp)),
Blind ? "throbs palpably" : "glows with a strange light");