experimental - dungeon overview (trunk only)

Add Hojita Discordia's Dungeon Map overview as
conditional code for experimentation and testing.
Everything is guarded by
#ifdef DUNGEON_OVERVIEW
#endif

The notes that accompanied the original patch follow.

Dungeon Map Overview Patch for Nethack 3.4.3
Version 3
=============================================================================
Changelist:
    v3: Changed #level to #annotate to avoid #levelchange collision.  Fixed
        handling of elemental planes and astral plane (oops).  Changed
	formatting to be slightly closer to print_dungeon()'s.  Should be
	"final" version for 3.4.3.
    v2: Added tracking of trees.  Changed ctrl-m command to ctrl-o.  Portals
        displayed as "sealed" instead of "closed".
    v1: First release.
    (Note: all versions are mutually save compatible.)
=============================================================================
This patch creates a dungeon map overview that is recorded as the player
explores the dungeon.  I was tired of returning to a game a few days later
and having no idea what the dungeon looked like.  Trying to name pieces
of armor with shorthand didn't work so well as an intermediate solution
either, especially around nymphs.

It can be assumed that this map is in the mind of the hero and thus
can't be stolen, can be read when blind, or when buried, or when the hero
doesn't have any hands, or eyes, or hands free, or...etc. On the other hand,
this implies that the hero doesn't remember all of the details ("a fountain",
"some fountains", "many fountains") and that the map is subject to amnesia
when applicable.

This overview tracks fountains, altars, stores, temples, sinks, thrones,
trees, and dungeon branches.  It attempts to not spoil the player nor
reveal more information than the hero knows.  For this reason, it only
tracks dungeon features found in the guidebook and dungeon branches.

This patch breaks save file compatibility.  Sorry.

Added commands
=============================================================================
#overview (ctrl-o, if not in wizard mode) - displays overview
#annotate (ctrl-n, if using numpad) - names current level

Example Output From #overview
=============================================================================
The Dungeons of Doom: levels 1 to level 15
   Level 1:
      A fountain
   Level 3: (My stash.)
      An altar, some fountains
      Stairs down to The Gnomish Mines
   Level 7:
      Many fountains
   Level 8:
      Stairs up to Sokoban, level 7
   Level 15:
      A general store
      Sealed portal to The Quest
The Gnomish Mines: levels 4 to level 7
   Level 7: <- You are here
      Many stores, some fountains, a temple

More Details
=============================================================================
The overview shows only levels that have anything interesting to display and
doesn't show branches that don't have any interesting levels.

To avoid the map revealing more information than the hero knows, the overview
only displays things that the hero has seen or touched.  (If the hero
blinds herself, levitates above a known fountain, and obliterates it with a
wand of digging, the overview will still say that there is a fountain.)

This is done, sadly, by adding 6 bits to the rm struct to track the last
known dungeon type.  On the other hand, this change could potentially allow
a window port to do something like drawing an item and a fountain on the same
square.

Things That Could Be Better And Maybe Some Feedback Would Help
=============================================================================
"<- You Are Here" is pretty goofy
    -...but an indicator of some sort is nice.
=============================================================================
Many thanks to all the kind folks on r.g.r.n. who had very good feedback
about this patch, in particular L (for the trees), <Someone> Papaganou (for the
#annotate suggestion and some formatting feedback), and <Someone> (for the suggestion
of just overriding ctrl-o instead of using the very broken ctrl-m.)
=============================================================================
20060311. Hojita Discordia. (My usenet email is bogus. Sorry.)
This commit is contained in:
nethack.allison
2006-04-20 00:57:45 +00:00
parent 08d4a0c0fa
commit 660d3589c5
13 changed files with 800 additions and 3 deletions

View File

@@ -372,7 +372,8 @@ typedef unsigned char uchar;
#if !defined(MAC)
# define CLIPPING /* allow smaller screens -- ERS */
#endif
#define BARGETHROUGH /* allow some monster to move others out of their way */
#define AUTOPICKUP_EXCEPTIONS /* exceptions to autopickup */
#define BARGETHROUGH /* allow some monsters to move others out of their way */
#ifdef REDO
# define DOAGAIN '\001' /* ^A, the "redo" key used in cmd.c and getline.c */
@@ -390,10 +391,9 @@ typedef unsigned char uchar;
*/
/*#define GOLDOBJ */ /* Gold is kept on obj chains - Helge Hafting */
#define AUTOPICKUP_EXCEPTIONS /* exceptions to autopickup */
#define STATUS_VIA_WINDOWPORT /* re-work of the status line updating process */
#define STATUS_HILITES /* support hilites of status fields */
#define DUNGEON_OVERVIEW /* dungeon overview by Hojita Discordia */
/* End of Section 5 */
#include "global.h" /* Define everything else according to choices above */

View File

@@ -167,4 +167,70 @@ struct linfo {
#endif /* MFLOPPY */
};
#ifdef DUNGEON_OVERVIEW
/* types and structures for dungeon map recording
*
* It is designed to eliminate the need for an external notes file for some of
* the more mundane dungeon elements. "Where was the last altar I passed?" etc...
* Presumably the character can remember this sort of thing even if, months
* later in real time picking up an old save game, I can't.
*
* To be consistent, one can assume that this map is in the player's mind and
* has no physical correspondence (eliminating illiteracy/blind/hands/hands free
* concerns.) Therefore, this map is not exaustive nor detailed ("some fountains").
* This makes it also subject to player conditions (amnesia).
*/
/* Because clearly Nethack needs more ways to specify alignment */
#define Amask2msa(x) ((x) == 4 ? 3 : (x) & AM_MASK)
#define Msa2amask(x) ((x) == 3 ? 4 : (x))
#define MSA_NONE 0 /* unaligned or multiple alignments */
#define MSA_LAWFUL 1
#define MSA_NEUTRAL 2
#define MSA_CHAOTIC 3
typedef struct mapseen_feat {
/* feature knowledge that must be calculated from levl array */
Bitfield(nfount, 2);
Bitfield(nsink, 2);
Bitfield(naltar, 2);
Bitfield(msalign, 2); /* corresponds to MSA_* above */
Bitfield(nthrone, 2);
Bitfield(ntree, 2);
/* water, lava, ice are too verbose so commented out for now */
/*
Bitfield(water, 1);
Bitfield(lava, 1);
Bitfield(ice, 1);
*/
/* calculated from rooms array */
Bitfield(nshop, 2);
Bitfield(ntemple, 2);
Bitfield(shoptype, 5);
Bitfield(forgot, 1); /* player has forgotten about this level? */
} mapseen_feat;
/* for mapseen->rooms */
#define MSR_SEEN 1
/* what the player knows about a single dungeon level */
/* initialized in mklev() */
typedef struct mapseen {
struct mapseen *next; /* next map in the chain */
branch *br; /* knows about branch via taking it in goto_level */
d_level lev; /* corresponding dungeon level */
mapseen_feat feat;
/* custom naming */
char *custom;
unsigned custom_lth;
/* maybe this should just be in struct mkroom? */
schar rooms[(MAXNROFROOMS+1)*2];
} mapseen;
#endif /* DUNGEON_OVERVIEW */
#endif /* DUNGEON_H */

View File

@@ -549,6 +549,15 @@ E schar FDECL(lev_by_name, (const char *));
#ifdef WIZARD
E schar FDECL(print_dungeon, (BOOLEAN_P,schar *,xchar *));
#endif
#ifdef DUNGEON_OVERVIEW
E int NDECL(donamelevel);
E int NDECL(dooverview);
E void FDECL(forget_mapseen, (int));
E void FDECL(init_mapseen, (d_level *));
E void NDECL(recalc_mapseen);
E void FDECL(recbranch_mapseen, (d_level *, d_level *));
E void FDECL(remdun_mapseen, (int));
#endif /* DUNGEON_OVERVIEW */
/* ### eat.c ### */

View File

@@ -338,9 +338,15 @@ struct rm {
Bitfield(horizontal,1); /* wall/door/etc is horiz. (more typ info) */
Bitfield(lit,1); /* speed hack for lit rooms */
Bitfield(waslit,1); /* remember if a location was lit */
Bitfield(roomno,6); /* room # for special rooms */
Bitfield(edge,1); /* marks boundaries for special rooms*/
Bitfield(candig,1); /* Exception to Can_dig_down; was a trapdoor */
#ifdef DUNGEON_OVERVIEW
Bitfield(styp, 6); /* last seen/touched dungeon typ */
/* 2 free bits */
#endif /* DUNGEON_OVERVIEW */
};
/*

View File

@@ -111,6 +111,9 @@ STATIC_PTR int NDECL(doprev_message);
STATIC_PTR int NDECL(timed_occupation);
STATIC_PTR int NDECL(doextcmd);
STATIC_PTR int NDECL(domonability);
#ifdef DUNGEON_OVERVIEW
STATIC_PTR int NDECL(dooverview_or_wiz_where);
#endif /* DUNGEON_OVERVIEW */
STATIC_PTR int NDECL(dotravel);
# ifdef WIZARD
STATIC_PTR int NDECL(wiz_wish);
@@ -497,6 +500,19 @@ enter_explore_mode()
return 0;
}
#ifdef DUNGEON_OVERVIEW
STATIC_PTR int
dooverview_or_wiz_where()
{
#ifdef WIZARD
if (wizard) return wiz_where();
else
#endif
dooverview();
return 0;
}
#endif /* DUNGEON_OVERVIEW */
#ifdef WIZARD
/* ^W command - wish for something */
@@ -1561,9 +1577,14 @@ static const struct func_tab cmdlist[] = {
{C('i'), TRUE, wiz_identify},
#endif
{C('l'), TRUE, doredraw}, /* if number_pad is set */
#ifndef DUNGEON_OVERVIEW
#ifdef WIZARD
{C('o'), TRUE, wiz_where},
#endif
#else
{C('n'), TRUE, donamelevel}, /* if number_pad is set */
{C('o'), TRUE, dooverview_or_wiz_where}, /* depending on wizard status */
#endif /* DUNGEON_OVERVIEW */
{C('p'), TRUE, doprev_message},
{C('r'), TRUE, doredraw},
{C('t'), TRUE, dotele},
@@ -1673,6 +1694,9 @@ static const struct func_tab cmdlist[] = {
struct ext_func_tab extcmdlist[] = {
{"adjust", "adjust inventory letters", doorganize, TRUE},
#ifdef DUNGEON_OVERVIEW
{"annotate", "name current level", donamelevel, TRUE},
#endif /* DUNGEON_OVERVIEW */
{"chat", "talk to someone", dotalk, TRUE}, /* converse? */
{"conduct", "list voluntary challenges you have maintained",
doconduct, TRUE},
@@ -1686,6 +1710,9 @@ struct ext_func_tab extcmdlist[] = {
{"monster", "use a monster's special ability", domonability, TRUE},
{"name", "name an item or type of object", ddocall, TRUE},
{"offer", "offer a sacrifice to the gods", dosacrifice, FALSE},
#ifdef DUNGEON_OVERVIEW
{"overview", "show an overview of the dungeon", dooverview, TRUE},
#endif /* DUNGEON_OVERVIEW */
{"pray", "pray to the gods for help", dopray, TRUE},
{"quit", "exit without saving current game", done2, TRUE},
#ifdef STEED

View File

@@ -185,6 +185,9 @@ magic_map_background(x, y, show)
if (level.flags.hero_memory)
lev->glyph = glyph;
if (show) show_glyph(x,y, glyph);
#ifdef DUNGEON_OVERVIEW
lev->styp = lev->typ;
#endif /* DUNGEON_OVERVIEW */
}
/*

View File

@@ -1070,6 +1070,9 @@ boolean at_stairs, falling, portal;
keepdogs(FALSE);
if (u.uswallow) /* idem */
u.uswldtim = u.uswallow = 0;
#ifdef DUNGEON_OVERVIEW
recalc_mapseen(); /* recalculate map overview before we leave the level */
#endif /* DUNGEON_OVERVIEW */
/*
* We no longer see anything on the level. Make sure that this
* follows u.uswallow set to null since uswallow overrides all
@@ -1105,6 +1108,13 @@ boolean at_stairs, falling, portal;
#ifdef USE_TILES
substitute_tiles(newlevel);
#endif
#ifdef DUNGEON_OVERVIEW
/* record this level transition as a potential seen branch unless using
* some non-standard means of transportation (level teleport).
*/
if ((at_stairs || falling || portal) && (u.uz.dnum != newlevel->dnum))
recbranch_mapseen(&u.uz, newlevel);
#endif /* DUNGEON_OVERVIEW */
assign_level(&u.uz0, &u.uz);
assign_level(&u.uz, newlevel);
assign_level(&u.utolev, newlevel);

View File

@@ -5,6 +5,9 @@
#include "hack.h"
#include "dgn_file.h"
#include "dlb.h"
#ifdef DUNGEON_OVERVIEW
#include "display.h"
#endif /* DUNGEON_OVERVIEW */
#define DUNGEON_FILE "dungeon"
@@ -57,6 +60,18 @@ STATIC_DCL const char *FDECL(br_string, (int));
STATIC_DCL void FDECL(print_branch, (winid, int, int, int, BOOLEAN_P, struct lchoice *));
#endif
#ifdef DUNGEON_OVERVIEW
mapseen *mapseenchn = (struct mapseen *)0;
STATIC_DCL void FDECL(free_mapseen, (mapseen *));
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 void FDECL(print_mapseen, (winid,mapseen *,boolean));
STATIC_DCL boolean FDECL(interest_mapseen, (mapseen *));
STATIC_DCL char *FDECL(seen_string, (xchar x, const char *));
STATIC_DCL const char *FDECL(br_string2, (branch *));
#endif /* DUNGEON_OVERVIEW */
#ifdef DEBUG
#define DD dungeons[i]
STATIC_DCL void NDECL(dumpit);
@@ -118,6 +133,9 @@ save_dungeon(fd, perform_write, free_data)
boolean perform_write, free_data;
{
branch *curr, *next;
#ifdef DUNGEON_OVERVIEW
mapseen *curr_ms, *next_ms;
#endif
int count;
if (perform_write) {
@@ -138,6 +156,15 @@ save_dungeon(fd, perform_write, free_data)
bwrite(fd, (genericptr_t) level_info,
(unsigned)count * sizeof (struct linfo));
bwrite(fd, (genericptr_t) &inv_pos, sizeof inv_pos);
#ifdef DUNGEON_OVERVIEW
for (count = 0, curr_ms = mapseenchn; curr_ms; curr_ms = curr_ms->next)
count++;
bwrite(fd, (genericptr_t) &count, sizeof(count));
for (curr_ms = mapseenchn; curr_ms; curr_ms = curr_ms->next)
save_mapseen(fd, curr_ms);
#endif /* DUNGEON_OVERVIEW */
}
if (free_data) {
@@ -146,6 +173,15 @@ save_dungeon(fd, perform_write, free_data)
free((genericptr_t) curr);
}
branches = 0;
#ifdef DUNGEON_OVERVIEW
for (curr_ms = mapseenchn; curr_ms; curr_ms = next_ms) {
next_ms = curr_ms->next;
if (curr_ms->custom)
free((genericptr_t)curr_ms->custom);
free((genericptr_t) curr_ms);
}
mapseenchn = 0;
#endif /* DUNGEON_OVERVIEW */
}
}
@@ -156,6 +192,9 @@ restore_dungeon(fd)
{
branch *curr, *last;
int count, i;
#ifdef DUNGEON_OVERVIEW
mapseen *curr_ms, *last_ms;
#endif
mread(fd, (genericptr_t) &n_dgns, sizeof(n_dgns));
mread(fd, (genericptr_t) dungeons, sizeof(dungeon) * (unsigned)n_dgns);
@@ -181,6 +220,20 @@ restore_dungeon(fd)
panic("level information count larger (%d) than allocated size", count);
mread(fd, (genericptr_t) level_info, (unsigned)count*sizeof(struct linfo));
mread(fd, (genericptr_t) &inv_pos, sizeof inv_pos);
#ifdef DUNGEON_OVERVIEW
mread(fd, (genericptr_t) &count, sizeof(count));
last_ms = (mapseen *) 0;
for (i = 0; i < count; i++) {
curr_ms = load_mapseen(fd);
curr_ms->next = (mapseen *) 0;
if (last_ms)
last_ms->next = curr_ms;
else
mapseenchn = curr_ms;
last_ms = curr_ms;
}
#endif /* DUNGEON_OVERVIEW */
}
static void
@@ -1762,4 +1815,608 @@ xchar *rdgn;
}
#endif /* WIZARD */
#ifdef DUNGEON_OVERVIEW
/* Record that the player knows about a branch from a level. This function
* will determine whether or not it was a "real" branch that was taken.
* This function should not be called for a transition done via level
* teleport or via the Eye.
*/
void
recbranch_mapseen(source, dest)
d_level *source;
d_level *dest;
{
mapseen *mptr;
branch* br;
/* not a branch */
if (source->dnum == dest->dnum) return;
/* we only care about forward branches */
for (br = branches; br; br = br->next) {
if (on_level(source, &br->end1) && on_level(dest, &br->end2)) break;
if (on_level(source, &br->end2) && on_level(dest, &br->end1)) return;
}
/* branch not found, so not a real branch. */
if (!br) return;
if (mptr = find_mapseen(source)) {
if (mptr->br && br != mptr->br)
impossible("Two branches on the same level?");
mptr->br = br;
} else {
impossible("Can't note branch for unseen level (%d, %d)",
source->dnum, source->dlevel);
}
}
/* 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 */
int len;
if (!(mptr = find_mapseen(&u.uz))) return 0;
Sprintf(qbuf,"What do you want to call this dungeon level? ");
getlin(qbuf, nbuf);
if (index(nbuf, '\033')) return 0;
len = strlen(nbuf) + 1;
if (mptr->custom) {
free((genericptr_t)mptr->custom);
mptr->custom = (char *)0;
mptr->custom_lth = 0;
}
if (*nbuf) {
mptr->custom = (char *) alloc(sizeof(char) * len);
mptr->custom_lth = len;
strcpy(mptr->custom, nbuf);
}
return 0;
}
/* find the particular mapseen object in the chain */
/* may return 0 */
STATIC_OVL mapseen *
find_mapseen(lev)
d_level *lev;
{
mapseen *mptr;
for (mptr = mapseenchn; mptr; mptr = mptr->next)
if (on_level(&(mptr->lev), lev)) break;
return mptr;
}
void
forget_mapseen(ledger_no)
int ledger_no;
{
mapseen *mptr;
for (mptr = mapseenchn; mptr; mptr = mptr->next)
if (dungeons[mptr->lev.dnum].ledger_start +
mptr->lev.dlevel == ledger_no) break;
/* if not found, then nothing to forget */
if (mptr) {
mptr->feat.forgot = 1;
mptr->br = (branch *)0;
/* custom names are erased, not forgotten until revisted */
if (mptr->custom) {
mptr->custom_lth = 0;
free((genericptr_t)mptr->custom);
mptr->custom = (char *)0;
}
memset((genericptr_t) mptr->rooms, 0, sizeof(mptr->rooms));
}
}
STATIC_OVL void
save_mapseen(fd, mptr)
int fd;
mapseen *mptr;
{
branch *curr;
int count;
count = 0;
for (curr = branches; curr; curr = curr->next) {
if (curr == mptr->br) break;
count++;
}
bwrite(fd, (genericptr_t) &count, sizeof(int));
bwrite(fd, (genericptr_t) &mptr->lev, sizeof(d_level));
bwrite(fd, (genericptr_t) &mptr->feat, sizeof(mapseen_feat));
bwrite(fd, (genericptr_t) &mptr->custom_lth, sizeof(unsigned));
if (mptr->custom_lth)
bwrite(fd, (genericptr_t) mptr->custom,
sizeof(char) * mptr->custom_lth);
bwrite(fd, (genericptr_t) &mptr->rooms, sizeof(mptr->rooms));
}
STATIC_OVL mapseen *
load_mapseen(fd)
int fd;
{
int branchnum, count;
mapseen *load;
branch *curr;
load = (mapseen *) alloc(sizeof(mapseen));
mread(fd, (genericptr_t) &branchnum, sizeof(int));
count = 0;
for (curr = branches; curr; curr = curr->next) {
if (count == branchnum) break;
count++;
}
load->br = curr;
mread(fd, (genericptr_t) &load->lev, sizeof(d_level));
mread(fd, (genericptr_t) &load->feat, sizeof(mapseen_feat));
mread(fd, (genericptr_t) &load->custom_lth, sizeof(unsigned));
if (load->custom_lth > 0) {
load->custom = (char *) alloc(sizeof(char) * load->custom_lth);
mread(fd, (genericptr_t) load->custom,
sizeof(char) * load->custom_lth);
} else load->custom = (char *) 0;
mread(fd, (genericptr_t) &load->rooms, sizeof(load->rooms));
return load;
}
/* Remove all mapseen objects for a particular dnum.
* Useful during quest expulsion to remove quest levels.
*/
void
remdun_mapseen(dnum)
int dnum;
{
mapseen *mptr, *prev;
prev = mapseenchn;
if (!prev) return;
mptr = prev->next;
for (; mptr; prev = mptr, mptr = mptr->next) {
if (mptr->lev.dnum == dnum) {
prev->next = mptr->next;
free((genericptr_t) mptr);
mptr = prev;
}
}
}
void
init_mapseen(lev)
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;
mapseen *init;
mapseen *old;
init = (mapseen *) alloc(sizeof(mapseen));
(void) memset((genericptr_t)init, 0, sizeof(mapseen));
init->lev.dnum = lev->dnum;
init->lev.dlevel = lev->dlevel;
if (!mapseenchn) {
mapseenchn = init;
return;
}
/* 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) \
((feat).nfount) || \
((feat).nsink) || \
((feat).nthrone) || \
((feat).naltar) || \
((feat).nshop) || \
((feat).ntemple) || \
((feat).ntree)
/*
|| ((feat).water) || \
((feat).ice) || \
((feat).lava)
*/
/* returns true if this level has something interesting to print out */
STATIC_OVL boolean
interest_mapseen(mptr)
mapseen *mptr;
{
return (on_level(&u.uz, &mptr->lev) || (!mptr->feat.forgot) && (
INTEREST(mptr->feat) ||
(mptr->custom) ||
(mptr->br)
));
}
/* recalculate mapseen for the current level */
void
recalc_mapseen()
{
mapseen *mptr;
struct monst *shkp;
int x, y, ridx;
/* 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
*/
if (!(mptr = find_mapseen(&u.uz))) return;
/* reset all features */
memset((genericptr_t) &mptr->feat, 0, sizeof(mapseen_feat));
/* track rooms the hero is in */
for (x = 0; x < sizeof(u.urooms); x++) {
if (!u.urooms[x]) continue;
ridx = u.urooms[x] - ROOMOFFSET;
if (rooms[ridx].rtype < SHOPBASE ||
((shkp = shop_keeper(u.urooms[x])) && inhishop(shkp)))
mptr->rooms[ridx] |= MSR_SEEN;
else
/* shops without shopkeepers are no shops at all */
mptr->rooms[ridx] &= ~MSR_SEEN;
}
/* recalculate room knowledge: for now, just shops and temples
* this could be extended to an array of 0..SHOPBASE
*/
for (x = 0; x < sizeof(mptr->rooms); x++) {
if (mptr->rooms[x] & MSR_SEEN) {
if (rooms[x].rtype >= SHOPBASE) {
if (!mptr->feat.nshop)
mptr->feat.shoptype = rooms[x].rtype;
else if (mptr->feat.shoptype != (unsigned)rooms[x].rtype)
mptr->feat.shoptype = 0;
mptr->feat.nshop = min(mptr->feat.nshop + 1, 3);
} else if (rooms[x].rtype == TEMPLE)
/* altar and temple alignment handled below */
mptr->feat.ntemple = min(mptr->feat.ntemple + 1, 3);
}
}
/* Update styp with typ if and only if it is in sight or the hero can
* feel it on their current location (i.e. not levitating). This *should*
* give the "last known typ" for each dungeon location. (At the very least,
* it's a better assumption than determining what the player knows from
* the glyph and the typ (which is isn't quite enough information in some
* cases).
*
* It was reluctantly added to struct rm to track. Alternatively
* we could track "features" and then update them all here, and keep
* track of when new features are created or destroyed, but this
* seemed the most elegant, despite adding more data to struct rm.
*
* Although no current windowing systems (can) do this, this would add the
* ability to have non-dungeon glyphs float above the last known dungeon
* glyph (i.e. items on fountains).
*
* (vision-related styp update done in loop below)
*/
if (!Levitation)
levl[u.ux][u.uy].styp = levl[u.ux][u.uy].typ;
for (x = 0; x < COLNO; x++) {
for (y = 0; y < ROWNO; y++) {
/* update styp from viz_array */
if (viz_array[y][x] & IN_SIGHT)
levl[x][y].styp = levl[x][y].typ;
switch (levl[x][y].styp) {
/*
case ICE:
mptr->feat.ice = 1;
break;
case POOL:
case MOAT:
case WATER:
mptr->feat.water = 1;
break;
case LAVAPOOL:
mptr->feat.lava = 1;
break;
*/
case TREE:
mptr->feat.ntree = min(mptr->feat.ntree + 1, 3);
break;
case FOUNTAIN:
mptr->feat.nfount = min(mptr->feat.nfount + 1, 3);
break;
case THRONE:
mptr->feat.nthrone = min(mptr->feat.nthrone + 1, 3);
break;
case SINK:
mptr->feat.nsink = min(mptr->feat.nsink + 1, 3);
break;
case ALTAR:
if (!mptr->feat.naltar)
mptr->feat.msalign = Amask2msa(levl[x][y].altarmask);
else if (mptr->feat.msalign != Amask2msa(levl[x][y].altarmask))
mptr->feat.msalign = MSA_NONE;
mptr->feat.naltar = min(mptr->feat.naltar + 1, 3);
break;
}
}
}
}
int
dooverview()
{
winid win;
mapseen *mptr;
boolean first;
boolean printdun;
int lastdun;
first = TRUE;
/* 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 (interest_mapseen(mptr)) {
printdun = (first || lastdun != mptr->lev.dnum);
/* if (!first) putstr(win, 0, ""); */
print_mapseen(win, mptr, printdun);
if (printdun) {
first = FALSE;
lastdun = mptr->lev.dnum;
}
}
}
display_nhwindow(win, TRUE);
destroy_nhwindow(win);
return 0;
}
STATIC_OVL char *
seen_string(x, obj)
xchar x;
const char *obj;
{
/* players are computer scientists: 0, 1, 2, n */
switch(x) {
case 0: return "no";
/* an() returns too much. index is ok in this case */
case 1: return index(vowels, *obj) ? "an" : "a";
case 2: return "some";
case 3: return "many";
}
return "(unknown)";
}
/* better br_string */
STATIC_OVL const char *
br_string2(br)
branch *br;
{
/* Special case: quest portal says closed if kicked from quest */
boolean closed_portal =
(br->end2.dnum == quest_dnum && u.uevent.qexpelled);
switch(br->type)
{
case BR_PORTAL: return closed_portal ? "Sealed portal" : "Portal";
case BR_NO_END1: return "Connection";
case BR_NO_END2: return (br->end1_up) ? "One way stairs up" :
"One way stairs down";
case BR_STAIR: return (br->end1_up) ? "Stairs up" : "Stairs down";
}
return "(unknown)";
}
STATIC_OVL const char*
shop_string(rtype)
int rtype;
{
/* Yuck, redundancy...but shclass.name doesn't cut it as a noun */
switch(rtype) {
case SHOPBASE:
return "general store";
case ARMORSHOP:
return "armor shop";
case SCROLLSHOP:
return "scroll shop";
case POTIONSHOP:
return "potion shop";
case WEAPONSHOP:
return "weapon shop";
case FOODSHOP:
return "delicatessen";
case RINGSHOP:
return "jewelers";
case WANDSHOP:
return "wand shop";
case BOOKSHOP:
return "bookstore";
case CANDLESHOP:
return "lighting shop";
default:
/* In case another patch adds a shop type that doesn't exist,
* do something reasonable like "a shop".
*/
return "shop";
}
}
/* some utility macros for print_mapseen */
#define TAB " "
#define BULLET ""
#define PREFIX TAB TAB BULLET
#define COMMA (i++ > 0 ? ", " : PREFIX)
#define ADDNTOBUF(nam, var) { if (var) \
Sprintf(eos(buf), "%s%s " nam "%s", COMMA, seen_string((var), (nam)), \
((var) != 1 ? "s" : "")); }
#define ADDTOBUF(nam, var) { if (var) Sprintf(eos(buf), "%s " nam, COMMA); }
STATIC_OVL void
print_mapseen(win, mptr, printdun)
winid win;
mapseen *mptr;
boolean printdun;
{
char buf[BUFSZ];
int i, depthstart;
/* Damnable special cases */
/* The quest and knox should appear to be level 1 to match
* other text.
*/
if (mptr->lev.dnum == quest_dnum || mptr->lev.dnum == knox_level.dnum)
depthstart = 1;
else
depthstart = dungeons[mptr->lev.dnum].depth_start;
if (printdun) {
/* Sokoban lies about dunlev_ureached and we should
* suppress the negative numbers in the endgame.
*/
if (dungeons[mptr->lev.dnum].dunlev_ureached == 1 ||
mptr->lev.dnum == sokoban_dnum || In_endgame(&mptr->lev))
Sprintf(buf, "%s:", dungeons[mptr->lev.dnum].dname);
else
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);
}
/* calculate level number */
i = depthstart + mptr->lev.dlevel - 1;
if (Is_astralevel(&mptr->lev))
Sprintf(buf, TAB "Astral Plane:");
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 bizarre reason.
*/
Sprintf(buf, TAB "Plane %i:", -i);
else
Sprintf(buf, TAB "Level %d:", i);
#ifdef WIZARD
/* wizmode prints out proto dungeon names for clarity */
if (wizard) {
s_level *slev;
if (slev = Is_special(&mptr->lev))
Sprintf(eos(buf), " [%s]", slev->proto);
}
#endif
if (mptr->custom)
Sprintf(eos(buf), " (%s)", mptr->custom);
/* print out glyph or something more interesting? */
Sprintf(eos(buf), "%s", on_level(&u.uz, &mptr->lev) ?
" <- You are here" : "");
putstr(win, ATR_BOLD, buf);
if (mptr->feat.forgot) return;
if (INTEREST(mptr->feat)) {
buf[0] = 0;
i = 0; /* interest counter */
/* List interests in an order vaguely corresponding to
* how important they are.
*/
if (mptr->feat.nshop > 1)
ADDNTOBUF("shop", mptr->feat.nshop)
else if (mptr->feat.nshop == 1)
Sprintf(eos(buf), "%s%s", COMMA,
an(shop_string(mptr->feat.shoptype)));
/* Temples + non-temple altars get munged into just "altars" */
if (!mptr->feat.ntemple || mptr->feat.ntemple != mptr->feat.naltar)
ADDNTOBUF("altar", mptr->feat.naltar)
else
ADDNTOBUF("temple", mptr->feat.ntemple)
/* only print out altar's god if they are all to your god */
if (Amask2align(Msa2amask(mptr->feat.msalign)) == u.ualign.type)
Sprintf(eos(buf), " to %s", align_gname(u.ualign.type));
ADDNTOBUF("fountain", mptr->feat.nfount)
ADDNTOBUF("sink", mptr->feat.nsink)
ADDNTOBUF("throne", mptr->feat.nthrone)
ADDNTOBUF("tree", mptr->feat.ntree);
/*
ADDTOBUF("water", mptr->feat.water)
ADDTOBUF("lava", mptr->feat.lava)
ADDTOBUF("ice", mptr->feat.ice)
*/
/* capitalize afterwards */
i = strlen(PREFIX);
buf[i] = toupper(buf[i]);
putstr(win, 0, buf);
}
/* print out branches */
if (mptr->br) {
Sprintf(buf, PREFIX "%s to %s", br_string2(mptr->br),
dungeons[mptr->br->end2.dnum].dname);
/* since mapseen objects are printed out in increasing order
* of dlevel, clarify which level this branch is going to
* if the branch goes upwards. Unless it's the end game
*/
if (mptr->br->end1_up && !In_endgame(&(mptr->br->end2)))
Sprintf(eos(buf), ", level %d", depth(&(mptr->br->end2)));
putstr(win, 0, buf);
}
}
#endif /* DUNGEON_OVERVIEW */
/*dungeon.c*/

View File

@@ -922,6 +922,9 @@ mklev()
{
struct mkroom *croom;
#ifdef DUNGEON_OVERVIEW
init_mapseen(&u.uz);
#endif
if(getbones()) return;
in_mklev = TRUE;
makelevel();

View File

@@ -157,6 +157,9 @@ boolean seal;
if (seal) { /* remove the portal to the quest - sealing it off */
int reexpelled = u.uevent.qexpelled;
u.uevent.qexpelled = 1;
#ifdef DUNGEON_OVERVIEW
remdun_mapseen(quest_dnum);
#endif
/* Delete the near portal now; the far (main dungeon side)
portal will be deleted as part of arrival on that level.
If monster movement is in progress, any who haven't moved

View File

@@ -537,6 +537,9 @@ forget_map(howmuch)
levl[zx][zy].seenv = 0;
levl[zx][zy].waslit = 0;
levl[zx][zy].glyph = cmap_to_glyph(S_stone);
#ifdef DUNGEON_OVERVIEW
levl[zx][zy].styp = STONE;
#endif
}
}
@@ -597,6 +600,9 @@ forget_levels(percent)
count = ((count * percent) + 50) / 100;
for (i = 0; i < count; i++) {
level_info[indices[i]].flags |= FORGOTTEN;
#ifdef DUNGEON_OVERVIEW
forget_mapseen(indices[i]);
#endif
}
}

View File

@@ -808,6 +808,10 @@ skip:
/* Set the new min and max pointers. */
viz_rmin = next_rmin;
viz_rmax = next_rmax;
#ifdef DUNGEON_OVERVIEW
recalc_mapseen();
#endif
}

View File

@@ -677,6 +677,9 @@ static const char *build_opts[] = {
#ifdef WIZARD
"debug mode",
#endif
#ifdef DUNGEON_OVERVIEW
"dungeon map overview patch",
#endif
#ifdef ELBERETH
"Elbereth",
#endif