Various bits I had in progress before Michael's commit. Mainly forget engravings when bones are saved instead of leaving them flagged as seen for the next hero who gets the level.
3801 lines
126 KiB
C
3801 lines
126 KiB
C
/* NetHack 3.7 display.c $NHDT-Date: 1723834773 2024/08/16 18:59:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.244 $ */
|
|
/* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */
|
|
/* and Dave Cohrs, 1990. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/*
|
|
* THE NEW DISPLAY CODE
|
|
*
|
|
* The old display code has been broken up into three parts: vision, display,
|
|
* and drawing. Vision decides what locations can and cannot be physically
|
|
* seen by the hero. Display decides _what_ is displayed at a given location.
|
|
* Drawing decides _how_ to draw a monster, fountain, sword, etc.
|
|
*
|
|
* The display system uses information from the vision system to decide
|
|
* what to draw at a given location. The routines for the vision system
|
|
* can be found in vision.c and vision.h. The routines for display can
|
|
* be found in this file (display.c) and display.h. The drawing routines
|
|
* are part of the window port. See doc/window.txt for the drawing
|
|
* interface.
|
|
*
|
|
* The display system deals with an abstraction called a glyph. Anything
|
|
* that could possibly be displayed has a unique glyph identifier.
|
|
*
|
|
* What is seen on the screen is a combination of what the hero remembers
|
|
* and what the hero currently sees. Objects and dungeon features (walls
|
|
* doors, etc) are remembered when out of sight. Monsters and temporary
|
|
* effects are not remembered. Each location on the level has an
|
|
* associated glyph. This is the hero's _memory_ of what he or she has
|
|
* seen there before.
|
|
*
|
|
* Display rules:
|
|
*
|
|
* If the location is in sight, display in order:
|
|
* visible (or sensed) monsters
|
|
* visible objects
|
|
* known traps
|
|
* background
|
|
*
|
|
* If the location is out of sight, display in order:
|
|
* sensed monsters (via telepathy or persistent detection)
|
|
* warning (partly-sensed monster shown as an abstraction)
|
|
* memory
|
|
*
|
|
* "Remembered, unseen monster" is handled like an object rather
|
|
* than a monster, and stays displayed whether or not it is in sight.
|
|
* It is removed when a visible or sensed or warned-of monster gets
|
|
* shown at its location or when searching or fighting reveals that
|
|
* no monster is there.
|
|
*
|
|
*
|
|
* Here is a list of the major routines in this file to be used externally:
|
|
*
|
|
* newsym
|
|
*
|
|
* Possibly update the screen location (x,y). This is the workhorse routine.
|
|
* It is always correct --- where correct means following the in-sight/out-
|
|
* of-sight rules. **Most of the code should use this routine.** This
|
|
* routine updates the map and displays monsters.
|
|
*
|
|
*
|
|
* map_background
|
|
* map_object
|
|
* map_trap or map_engraving
|
|
* map_invisible
|
|
* unmap_object
|
|
*
|
|
* If you absolutely must override the in-sight/out-of-sight rules, there
|
|
* are two possibilities. First, you can mess with vision to force the
|
|
* location in sight then use newsym(), or you can use the map_* routines.
|
|
* The first has not been tried [no need] and the second is used in the
|
|
* detect routines --- detect object, magic mapping, etc. The map_*
|
|
* routines *change* what the hero remembers. All changes made by these
|
|
* routines will be sticky --- they will survive screen redraws. Do *not*
|
|
* use these for things that only temporarily change the screen. These
|
|
* routines are also used directly by newsym(). unmap_object is used to
|
|
* clear a remembered object when/if detection reveals it isn't there.
|
|
*
|
|
*
|
|
* show_glyph
|
|
*
|
|
* This is direct (no processing in between) buffered access to the screen.
|
|
* Temporary screen effects are run through this and its companion,
|
|
* flush_screen(). There is yet a lower level routine, print_glyph(),
|
|
* but this is unbuffered and graphic dependent (i.e. it must be surrounded
|
|
* by graphic set-up and tear-down routines). Do not use print_glyph().
|
|
*
|
|
*
|
|
* see_monsters
|
|
* see_objects
|
|
* see_traps
|
|
*
|
|
* These are only used when something affects all of the monsters or
|
|
* objects or traps. For objects and traps, the only thing is hallucination.
|
|
* For monsters, there are hallucination and changing from/to blindness, etc.
|
|
*
|
|
*
|
|
* tmp_at
|
|
*
|
|
* This is a useful interface for displaying temporary items on the screen.
|
|
* Its interface is different than previously, so look at it carefully.
|
|
*
|
|
*
|
|
*
|
|
* Parts of the rm structure that are used:
|
|
*
|
|
* typ - What is really there.
|
|
* glyph - What the hero remembers. This will never be a monster.
|
|
* Monsters "float" above this.
|
|
* lit - True if the position is lit. An optimization for
|
|
* lit/unlit rooms.
|
|
* waslit - True if the position was *remembered* as lit.
|
|
* seenv - A vector of bits representing the directions from which the
|
|
* hero has seen this position. The vector's primary use is
|
|
* determining how walls are seen. E.g. a wall sometimes looks
|
|
* like stone on one side, but is seen as wall from the other.
|
|
* Other uses are for unmapping detected objects and felt
|
|
* locations, where we need to know if the hero has ever
|
|
* seen the location.
|
|
* flags - Additional information for the typ field. Different for
|
|
* each typ.
|
|
* horizontal - Indicates whether the wall or door is horizontal or
|
|
* vertical.
|
|
*/
|
|
#include "hack.h"
|
|
|
|
staticfn void show_mon_or_warn(coordxy, coordxy, int);
|
|
staticfn void display_monster(coordxy, coordxy,
|
|
struct monst *, int, boolean) NONNULLPTRS;
|
|
staticfn int swallow_to_glyph(int, int);
|
|
staticfn void display_warning(struct monst *) NONNULLARG1;
|
|
staticfn boolean mon_overrides_region(struct monst *, coordxy, coordxy);
|
|
staticfn int check_pos(coordxy, coordxy, int);
|
|
staticfn void get_bkglyph_and_framecolor(coordxy x, coordxy y, int *,
|
|
uint32 *);
|
|
staticfn int tether_glyph(coordxy, coordxy);
|
|
staticfn void mimic_light_blocking(struct monst *) NONNULLARG1;
|
|
|
|
/*#define WA_VERBOSE*/ /* give (x,y) locations for all "bad" spots */
|
|
#ifdef WA_VERBOSE
|
|
staticfn boolean more_than_one(coordxy, coordxy, coordxy, coordxy, coordxy);
|
|
#endif
|
|
|
|
staticfn int set_twall(coordxy, coordxy, coordxy, coordxy,
|
|
coordxy, coordxy, coordxy, coordxy);
|
|
staticfn int set_wall(coordxy, coordxy, int);
|
|
staticfn int set_corn(coordxy, coordxy, coordxy, coordxy,
|
|
coordxy, coordxy, coordxy, coordxy);
|
|
staticfn int set_crosswall(coordxy, coordxy);
|
|
staticfn void set_seenv(struct rm *, coordxy, coordxy, coordxy, coordxy);
|
|
staticfn void t_warn(struct rm *);
|
|
staticfn int wall_angle(struct rm *);
|
|
|
|
#define _glyph_at(x, y) gg.gbuf[y][x].glyphinfo.glyph
|
|
|
|
/*
|
|
* See display.h for descriptions of tp_sensemon() through
|
|
* is_safemon(). Some of these were generating an awful lot of
|
|
* code "behind the curtain", particularly canspotmon() (which is
|
|
* still a macro but one that now expands to a pair of function
|
|
* calls rather than to a ton of special case checks). Return
|
|
* values are all int 0 or 1, not boolean.
|
|
*
|
|
* They're still implemented as macros within this file.
|
|
*/
|
|
int
|
|
tp_sensemon(struct monst *mon)
|
|
{
|
|
return _tp_sensemon(mon);
|
|
}
|
|
#define tp_sensemon(mon) _tp_sensemon(mon)
|
|
|
|
int
|
|
sensemon(struct monst *mon)
|
|
{
|
|
return _sensemon(mon);
|
|
}
|
|
#define sensemon(mon) _sensemon(mon)
|
|
|
|
int
|
|
mon_warning(struct monst *mon)
|
|
{
|
|
return _mon_warning(mon);
|
|
}
|
|
#define mon_warning(mon) _mon_warning(mon)
|
|
|
|
int
|
|
mon_visible(struct monst *mon)
|
|
{
|
|
return _mon_visible(mon);
|
|
}
|
|
#define mon_visible(mon) _mon_visible(mon)
|
|
|
|
int
|
|
see_with_infrared(struct monst *mon)
|
|
{
|
|
return _see_with_infrared(mon);
|
|
}
|
|
#define see_with_infrared(mon) _see_with_infrared(mon)
|
|
|
|
int
|
|
canseemon(struct monst *mon)
|
|
{
|
|
return _canseemon(mon);
|
|
}
|
|
#define canseemon(mon) _canseemon(mon)
|
|
|
|
int
|
|
knowninvisible(struct monst *mon)
|
|
{
|
|
return _knowninvisible(mon);
|
|
}
|
|
/* #define knowninvisible() isn't useful here */
|
|
|
|
int
|
|
is_safemon(struct monst *mon)
|
|
{
|
|
return _is_safemon(mon);
|
|
}
|
|
/* #define is_safemon() isn't useful here */
|
|
|
|
/*
|
|
* End of former macro-only vision related (mostly) routines
|
|
* converted to functions.
|
|
*/
|
|
|
|
/*
|
|
* magic_map_background()
|
|
*
|
|
* This function is similar to map_background (see below) except we pay
|
|
* attention to and correct unexplored, lit ROOM and CORR spots.
|
|
*/
|
|
void
|
|
magic_map_background(coordxy x, coordxy y, int show)
|
|
{
|
|
int glyph = back_to_glyph(x, y); /* assumes hero can see x,y */
|
|
struct rm *lev = &levl[x][y];
|
|
|
|
/*
|
|
* Correct for out of sight lit corridors and rooms that the hero
|
|
* doesn't remember as lit.
|
|
*/
|
|
if (!cansee(x, y) && !lev->waslit) {
|
|
/* Floor spaces are dark if unlit. Corridors are dark if unlit. */
|
|
if (lev->typ == ROOM && glyph == cmap_to_glyph(S_room))
|
|
glyph = (flags.dark_room && iflags.use_color)
|
|
? cmap_to_glyph(DARKROOMSYM)
|
|
: GLYPH_NOTHING;
|
|
else if (lev->typ == CORR && glyph == cmap_to_glyph(S_litcorr))
|
|
glyph = cmap_to_glyph(S_corr);
|
|
}
|
|
if (svl.level.flags.hero_memory
|
|
&& (glyph_is_unexplored(lev->glyph) || glyph_is_cmap(lev->glyph)))
|
|
lev->glyph = glyph;
|
|
if (show)
|
|
show_glyph(x, y, glyph);
|
|
|
|
update_lastseentyp(x, y);
|
|
}
|
|
|
|
/*
|
|
* The routines map_background(), map_object(), and map_trap() could just
|
|
* as easily be:
|
|
*
|
|
* map_glyph(x,y,glyph,show)
|
|
*
|
|
* Which is called with the xx_to_glyph() in the call. Then I can get
|
|
* rid of 3 routines that don't do very much anyway. And then stop
|
|
* having to create fake objects and traps. However, I am reluctant to
|
|
* make this change.
|
|
*/
|
|
|
|
/*
|
|
* map_background()
|
|
*
|
|
* Make the real background part of our map. This routine assumes that
|
|
* the hero can physically see the location. Update the screen if directed.
|
|
*/
|
|
void
|
|
map_background(coordxy x, coordxy y, int show)
|
|
{
|
|
int glyph = back_to_glyph(x, y);
|
|
|
|
if (svl.level.flags.hero_memory)
|
|
levl[x][y].glyph = glyph;
|
|
if (show)
|
|
show_glyph(x, y, glyph);
|
|
}
|
|
|
|
/*
|
|
* map_trap()
|
|
*
|
|
* Map the trap and print it out if directed. This routine assumes that the
|
|
* hero can physically see the location.
|
|
*/
|
|
void
|
|
map_trap(struct trap *trap, int show)
|
|
{
|
|
coordxy x = trap->tx, y = trap->ty;
|
|
int glyph = trap_to_glyph(trap);
|
|
|
|
if (svl.level.flags.hero_memory)
|
|
levl[x][y].glyph = glyph;
|
|
if (show)
|
|
show_glyph(x, y, glyph);
|
|
}
|
|
|
|
/*
|
|
* map_engraving()
|
|
*
|
|
* Map the engraving and print it out if directed.
|
|
*/
|
|
void
|
|
map_engraving(struct engr *ep, int show)
|
|
{
|
|
coordxy x = ep->engr_x, y = ep->engr_y;
|
|
int glyph = engraving_to_glyph(ep);
|
|
|
|
if (svl.level.flags.hero_memory)
|
|
levl[x][y].glyph = glyph;
|
|
if (show)
|
|
show_glyph(x, y, glyph);
|
|
}
|
|
|
|
/*
|
|
* map_object()
|
|
*
|
|
* Map the given object. This routine assumes that the hero can physically
|
|
* see the location of the object. Update the screen if directed.
|
|
* [Note: feel_location() -> map_location() -> map_object() contradicts
|
|
* the claim here that the hero can see obj's <ox,oy>.]
|
|
*/
|
|
void
|
|
map_object(struct obj *obj, int show)
|
|
{
|
|
coordxy x = obj->ox, y = obj->oy;
|
|
int glyph = obj_to_glyph(obj, newsym_rn2);
|
|
|
|
/* if this object is already displayed as a generic object, it might
|
|
become a specific one now */
|
|
if (glyph_is_generic_object(glyph) && cansee(x, y) && !Hallucination) {
|
|
/* these 'r' and 'neardist' calculations match distant_name(objnam.c)
|
|
and see_nearby_objects(below); we assume that this is a lone
|
|
object or a pile-top, not something below the top of a pile */
|
|
int r = (u.xray_range > 2) ? u.xray_range : 2,
|
|
/* neardist produces a small square with rounded corners */
|
|
neardist = (r * r) * 2 - r; /* same as r*r + r*(r-1) */
|
|
|
|
if (distu(x, y) <= neardist) {
|
|
obj->dknown = 1;
|
|
glyph = obj_to_glyph(obj, newsym_rn2);
|
|
}
|
|
}
|
|
|
|
if (svl.level.flags.hero_memory) {
|
|
/* MRKR: While hallucinating, statues are seen as random monsters */
|
|
/* but remembered as random objects. */
|
|
|
|
if (Hallucination && obj->otyp == STATUE) {
|
|
levl[x][y].glyph = random_obj_to_glyph(newsym_rn2);
|
|
} else {
|
|
levl[x][y].glyph = glyph;
|
|
}
|
|
}
|
|
if (show)
|
|
show_glyph(x, y, glyph);
|
|
}
|
|
|
|
/*
|
|
* map_invisible()
|
|
*
|
|
* Make the hero remember that a square contains an invisible monster.
|
|
* This is a special case in that the square will continue to be displayed
|
|
* this way even when the hero is close enough to see it. To get rid of
|
|
* this and display the square's actual contents, use unmap_object() followed
|
|
* by newsym() if necessary.
|
|
*/
|
|
void
|
|
map_invisible(coordxy x, coordxy y)
|
|
{
|
|
if (x != u.ux || y != u.uy) { /* don't display I at hero's location */
|
|
if (svl.level.flags.hero_memory)
|
|
levl[x][y].glyph = GLYPH_INVISIBLE;
|
|
show_glyph(x, y, GLYPH_INVISIBLE);
|
|
}
|
|
}
|
|
|
|
boolean
|
|
unmap_invisible(coordxy x, coordxy y)
|
|
{
|
|
if (isok(x,y) && glyph_is_invisible(levl[x][y].glyph)) {
|
|
unmap_object(x, y);
|
|
newsym(x, y);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* unmap_object()
|
|
*
|
|
* Remove something from the map when the hero realizes it's not there
|
|
* anymore. Replace it with background or known trap, but not with
|
|
* any other remembered object. If this is used for detection, a full
|
|
* screen update is imminent anyway; if this is used to get rid of an
|
|
* invisible monster notation, we might have to call newsym().
|
|
*/
|
|
void
|
|
unmap_object(coordxy x, coordxy y)
|
|
{
|
|
struct trap *trap;
|
|
struct engr *ep;
|
|
|
|
if (!svl.level.flags.hero_memory)
|
|
return;
|
|
|
|
if ((trap = t_at(x, y)) != 0 && trap->tseen && !covers_traps(x, y)) {
|
|
map_trap(trap, 0);
|
|
} else if (levl[x][y].seenv) {
|
|
struct rm *lev = &levl[x][y];
|
|
|
|
if (spot_shows_engravings(x, y)
|
|
&& (ep = engr_at(x, y)) != 0 && !covers_traps(x, y)) {
|
|
if (cansee(x, y))
|
|
ep->erevealed = 1;
|
|
map_engraving(ep, 0);
|
|
} else {
|
|
map_background(x, y, 0);
|
|
}
|
|
|
|
/* turn remembered dark room squares dark */
|
|
if (!lev->waslit && lev->glyph == cmap_to_glyph(S_room)
|
|
&& lev->typ == ROOM)
|
|
lev->glyph = cmap_to_glyph(S_stone);
|
|
} else {
|
|
levl[x][y].glyph = cmap_to_glyph(S_stone); /* default val */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* map_location()
|
|
*
|
|
* Make whatever at this location show up. This is only for non-living
|
|
* things. This will not handle feeling invisible objects correctly.
|
|
*
|
|
* Internal to display.c, this is a #define for speed.
|
|
*/
|
|
#define _map_location(x, y, show) \
|
|
do { \
|
|
struct obj *obj; \
|
|
struct trap *trap; \
|
|
struct engr *ml_ep; \
|
|
NhRegion *_ml_reg; \
|
|
\
|
|
if ((obj = vobj_at(x, y)) && !covers_objects(x, y)) { \
|
|
map_object(obj, show); \
|
|
} else if ((trap = t_at(x, y)) \
|
|
&& trap->tseen && !covers_traps(x, y)) { \
|
|
map_trap(trap, show); \
|
|
} else if (spot_shows_engravings(x, y) \
|
|
&& (ml_ep = engr_at(x, y)) != 0 \
|
|
&& ml_ep->erevealed \
|
|
&& !covers_traps(x, y)) { \
|
|
map_engraving(ml_ep, show); \
|
|
} else { \
|
|
map_background(x, y, show); \
|
|
} \
|
|
\
|
|
update_lastseentyp(x, y); \
|
|
if (show && !Blind && (_ml_reg = visible_region_at(x, y)) != 0) \
|
|
show_region(_ml_reg, x, y); \
|
|
} while (0)
|
|
|
|
void
|
|
map_location(coordxy x, coordxy y, int show)
|
|
{
|
|
_map_location(x, y, show);
|
|
}
|
|
|
|
/* display something on monster layer; may need to fixup object layer */
|
|
staticfn void
|
|
show_mon_or_warn(coordxy x, coordxy y, int monglyph)
|
|
{
|
|
struct obj *o;
|
|
|
|
/* "remembered, unseen monster" is tracked by object layer so if we're
|
|
putting something on monster layer at same spot, stop remembering
|
|
that; if an object is in view there, start remembering it instead */
|
|
if (glyph_is_invisible(levl[x][y].glyph)) {
|
|
unmap_object(x, y);
|
|
if (cansee(x, y) && (o = vobj_at(x, y)) != 0)
|
|
map_object(o, FALSE);
|
|
}
|
|
|
|
show_glyph(x, y, monglyph);
|
|
}
|
|
|
|
#define DETECTED 2
|
|
#define PHYSICALLY_SEEN 1
|
|
#define is_worm_tail(mon) ((mon) && ((x != (mon)->mx) || (y != (mon)->my)))
|
|
|
|
/*
|
|
* display_monster()
|
|
*
|
|
* Note that this is *not* a map_XXXX() function! Monsters sort of float
|
|
* above everything.
|
|
*
|
|
* Yuck. Display body parts by recognizing that the display position is
|
|
* not the same as the monster position. Currently the only body part is
|
|
* a worm tail.
|
|
*
|
|
*/
|
|
staticfn void
|
|
display_monster(
|
|
coordxy x, coordxy y, /* display position */
|
|
struct monst *mon, /* monster to display */
|
|
int sightflags, /* 1 if the monster is physically seen;
|
|
* 2 if detected using Detect_monsters */
|
|
boolean worm_tail) /* mon is actually a worm tail */
|
|
{
|
|
boolean mon_mimic = (M_AP_TYPE(mon) != M_AP_NOTHING);
|
|
int sensed = (mon_mimic && (Protection_from_shape_changers
|
|
|| sensemon(mon))),
|
|
mgendercode = mon->female ? FEMALE : MALE;
|
|
|
|
/*
|
|
* We must do the mimic check first. If the mimic is mimicking something,
|
|
* and the location is in sight, we have to change the hero's memory
|
|
* so that when the position is out of sight, the hero remembers what
|
|
* the mimic was mimicking.
|
|
*/
|
|
if (mon_mimic && (sightflags == PHYSICALLY_SEEN)) {
|
|
switch (M_AP_TYPE(mon)) {
|
|
default:
|
|
impossible("display_monster: bad m_ap_type value [ = %d ]",
|
|
(int) mon->m_ap_type);
|
|
FALLTHROUGH;
|
|
/*FALLTHRU*/
|
|
case M_AP_NOTHING:
|
|
show_glyph(x, y, mon_to_glyph(mon, newsym_rn2));
|
|
break;
|
|
|
|
case M_AP_FURNITURE: {
|
|
/*
|
|
* This is a poor man's version of map_background(). I can't
|
|
* use map_background() because we are overriding what is in
|
|
* the 'typ' field. Maybe have map_background()'s parameters
|
|
* be (x,y,glyph) instead of just (x,y).
|
|
*
|
|
* mappearance is currently set to an S_ index value in
|
|
* makemon.c.
|
|
*/
|
|
int sym = mon->mappearance, glyph = cmap_to_glyph(sym);
|
|
|
|
levl[x][y].glyph = glyph;
|
|
if (!sensed) {
|
|
show_glyph(x, y, glyph);
|
|
/* override real topology with mimic's fake one */
|
|
svl.lastseentyp[x][y] = cmap_to_type(sym);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case M_AP_OBJECT: {
|
|
/* Make a fake object to send to map_object(). */
|
|
struct obj obj;
|
|
|
|
obj = cg.zeroobj;
|
|
obj.ox = x;
|
|
obj.oy = y;
|
|
obj.otyp = mon->mappearance;
|
|
/* might be mimicking a corpse or statue */
|
|
obj.corpsenm = has_mcorpsenm(mon) ? MCORPSENM(mon) : PM_TENGU;
|
|
map_object(&obj, !sensed);
|
|
break;
|
|
}
|
|
|
|
case M_AP_MONSTER: {
|
|
int mndx = what_mon((int) mon->mappearance, rn2_on_display_rng);
|
|
|
|
show_glyph(x, y, monnum_to_glyph(mndx, mgendercode));
|
|
break;
|
|
} /* case M_AP_MONSTER */
|
|
} /* switch M_AP_TYPE() */
|
|
}
|
|
|
|
/* If mimic is unsuccessfully mimicking something, display the monster. */
|
|
if (!mon_mimic || sensed) {
|
|
int num;
|
|
|
|
/* [ALI] Only use detected glyphs when monster wouldn't be
|
|
* visible by any other means.
|
|
*
|
|
* There are no glyphs for "detected pets" so we have to
|
|
* decide whether to display such things as detected or as tame.
|
|
* If both are being highlighted in the same way, it doesn't
|
|
* matter, but if not, showing them as pets is preferrable.
|
|
*/
|
|
if (mon->mtame && !Hallucination) {
|
|
if (worm_tail)
|
|
num = petnum_to_glyph(PM_LONG_WORM_TAIL, mgendercode);
|
|
else
|
|
num = pet_to_glyph(mon, rn2_on_display_rng);
|
|
} else if (sightflags == DETECTED) {
|
|
if (worm_tail)
|
|
num = detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL,
|
|
rn2_on_display_rng),
|
|
mgendercode);
|
|
else
|
|
num = detected_mon_to_glyph(mon, rn2_on_display_rng);
|
|
} else {
|
|
if (worm_tail)
|
|
num = monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL,
|
|
rn2_on_display_rng),
|
|
mgendercode);
|
|
else
|
|
num = mon_to_glyph(mon, rn2_on_display_rng);
|
|
}
|
|
show_mon_or_warn(x, y, num);
|
|
mon->meverseen = 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* display_warning()
|
|
*
|
|
* This is also *not* a map_XXXX() function! Monster warnings float
|
|
* above everything just like monsters do, but only if the monster
|
|
* is not showing.
|
|
*
|
|
* Do not call for worm tails.
|
|
*/
|
|
staticfn void
|
|
display_warning(struct monst *mon)
|
|
{
|
|
coordxy x = mon->mx, y = mon->my;
|
|
int glyph;
|
|
|
|
if (mon_warning(mon)) {
|
|
int wl = Hallucination ? rn2_on_display_rng(WARNCOUNT - 1) + 1
|
|
: warning_of(mon);
|
|
|
|
glyph = warning_to_glyph(wl);
|
|
} else if (MATCH_WARN_OF_MON(mon)) {
|
|
glyph = mon_to_glyph(mon, rn2_on_display_rng);
|
|
} else {
|
|
impossible("display_warning did not match warning type?");
|
|
return;
|
|
}
|
|
show_mon_or_warn(x, y, glyph);
|
|
}
|
|
|
|
int
|
|
warning_of(struct monst *mon)
|
|
{
|
|
int wl = 0, tmp = 0;
|
|
|
|
if (mon_warning(mon)) {
|
|
tmp = (int) (mon->m_lev / 4); /* match display.h */
|
|
wl = (tmp > WARNCOUNT - 1) ? WARNCOUNT - 1 : tmp;
|
|
}
|
|
return wl;
|
|
}
|
|
|
|
/* used by newsym() to decide whether to show a monster or a visible gas
|
|
cloud region when both are at the same spot; caller deals with region */
|
|
staticfn boolean
|
|
mon_overrides_region(
|
|
struct monst *mon, /* might be Null */
|
|
coordxy mx, coordxy my) /* won't match mon->mx,my if long worm's tail */
|
|
{
|
|
int r;
|
|
|
|
/* this is redundant because newsym() doesn't call us when swallowed */
|
|
if (u.uswallow && (!mon || mon != u.ustuck))
|
|
return FALSE;
|
|
|
|
if (mon) {
|
|
/* when not a worm tail, show mon if sensed rather than seen */
|
|
if (mx == mon->mx && my == mon->my
|
|
&& (sensemon(mon) || mon_warning(mon)))
|
|
return TRUE;
|
|
|
|
/* even if worm tail;
|
|
check whether the spot is adjacent and 'mon' would be visible
|
|
there if the gas cloud wasn't interfering with normal vision;
|
|
_mon_visible() handles mon->mundetected; don't need to check
|
|
infravision when monster is adjacent */
|
|
r = (u.xray_range > 1) ? u.xray_range : 1;
|
|
if (!Blind && _mon_visible(mon)
|
|
&& M_AP_TYPE(mon) != M_AP_FURNITURE
|
|
&& M_AP_TYPE(mon) != M_AP_OBJECT
|
|
&& distu(mx, my) <= r * (r + 1))
|
|
return TRUE;
|
|
}
|
|
|
|
/* if not overriding region for current mon, propagate "remembered,
|
|
unseen monster" */
|
|
return glyph_is_invisible(levl[mx][my].glyph) ? TRUE : FALSE;
|
|
}
|
|
|
|
#ifdef HANGUPHANDLING
|
|
#define _suppress_map_output() \
|
|
(gi.in_mklev || program_state.saving || program_state.restoring \
|
|
|| program_state.done_hup)
|
|
#else
|
|
#define _suppress_map_output() \
|
|
(gi.in_mklev || program_state.saving || program_state.restoring)
|
|
#endif
|
|
|
|
/* map or status window might not be ready for output during level creation
|
|
or game restoration (something like u.usteed which affects display of
|
|
the hero and also a status condition might not be set up yet) */
|
|
boolean
|
|
suppress_map_output(void)
|
|
{
|
|
return _suppress_map_output();
|
|
}
|
|
|
|
/*
|
|
* feel_newsym()
|
|
*
|
|
* When hero knows what happened to location, even when blind.
|
|
*/
|
|
void
|
|
feel_newsym(coordxy x, coordxy y)
|
|
{
|
|
if (Blind)
|
|
feel_location(x, y);
|
|
else
|
|
newsym(x, y);
|
|
}
|
|
|
|
|
|
/*
|
|
* feel_location()
|
|
*
|
|
* Feel the given location. This assumes that the hero is blind and that
|
|
* the given position is either the hero's or one of the eight squares
|
|
* adjacent to the hero (except for a boulder push).
|
|
* If an invisible monster has gone away, that will be discovered. If an
|
|
* invisible monster has appeared, this will _not_ be discovered since
|
|
* searching only finds one monster per turn so we must check that separately.
|
|
*/
|
|
void
|
|
feel_location(coordxy x, coordxy y)
|
|
{
|
|
struct rm *lev;
|
|
struct obj *boulder;
|
|
struct monst *mon;
|
|
struct engr *ep;
|
|
|
|
/* replicate safeguards used by newsym(); might not be required here */
|
|
if (_suppress_map_output())
|
|
return;
|
|
|
|
if (!isok(x, y))
|
|
return;
|
|
lev = &(levl[x][y]);
|
|
/* If hero's memory of an invisible monster is accurate, we want to keep
|
|
* him from detecting the same monster over and over again on each turn.
|
|
* We must return (so we don't erase the monster). (We must also, in the
|
|
* search function, be sure to skip over previously detected 'I's.)
|
|
*/
|
|
if (glyph_is_invisible(lev->glyph) && m_at(x, y))
|
|
return;
|
|
|
|
/* The hero can't feel non pool locations while under water
|
|
except for lava and ice. */
|
|
if (Underwater && !Is_waterlevel(&u.uz)
|
|
&& !is_pool_or_lava(x, y) && !is_ice(x, y))
|
|
return;
|
|
|
|
/* Set the seen vector as if the hero had seen it.
|
|
It doesn't matter if the hero is levitating or not. */
|
|
set_seenv(lev, u.ux, u.uy, x, y);
|
|
|
|
if (!can_reach_floor(FALSE)) {
|
|
/*
|
|
* Levitation Rules. It is assumed that the hero can feel the state
|
|
* of the walls around herself and can tell if she is in a corridor,
|
|
* room, or doorway. Boulders are felt because they are large enough.
|
|
* Anything else is unknown because the hero can't reach the ground.
|
|
* This makes things difficult.
|
|
*
|
|
* Check (and display) in order:
|
|
*
|
|
* + Stone, walls, and closed doors.
|
|
* + Boulders. [see a boulder before a doorway]
|
|
* + doors.
|
|
* + Room/water positions
|
|
* + Everything else (hallways!)
|
|
*/
|
|
if (IS_OBSTRUCTED(lev->typ)
|
|
|| (IS_DOOR(lev->typ)
|
|
&& (lev->doormask & (D_LOCKED | D_CLOSED)))) {
|
|
map_background(x, y, 1);
|
|
} else if ((boulder = sobj_at(BOULDER, x, y)) != 0) {
|
|
map_object(boulder, 1);
|
|
} else if (IS_DOOR(lev->typ)) {
|
|
map_background(x, y, 1);
|
|
} else if (IS_ROOM(lev->typ) || IS_POOL(lev->typ)) {
|
|
boolean do_room_glyph;
|
|
|
|
/*
|
|
* An open room or water location. Normally we wouldn't touch
|
|
* this, but we have to get rid of remembered boulder symbols.
|
|
* This will only occur in rare occasions when the hero goes
|
|
* blind and doesn't find a boulder where expected (something
|
|
* came along and picked it up). We know that there is not a
|
|
* boulder at this location. Show fountains, pools, etc.
|
|
* underneath if already seen. Otherwise, show the appropriate
|
|
* floor symbol.
|
|
*
|
|
* Similarly, if the hero digs a hole in a wall or feels a
|
|
* location that used to contain an unseen monster. In these
|
|
* cases, there's no reason to assume anything was underneath,
|
|
* so just show the appropriate floor symbol. If something was
|
|
* embedded in the wall, the glyph will probably already
|
|
* reflect that. Don't change the symbol in this case.
|
|
*
|
|
* This isn't quite correct. If the boulder was on top of some
|
|
* other objects they should be seen once the boulder is removed.
|
|
* However, we have no way of knowing that what is there now
|
|
* was there then. So we let the hero have a lapse of memory.
|
|
* We could also just display what is currently on the top of the
|
|
* object stack (if anything).
|
|
*/
|
|
do_room_glyph = FALSE;
|
|
if (lev->glyph == objnum_to_glyph(BOULDER)
|
|
|| glyph_is_invisible(lev->glyph)) {
|
|
if (lev->typ != ROOM && lev->seenv)
|
|
map_background(x, y, 1);
|
|
else
|
|
do_room_glyph = TRUE;
|
|
} else if (lev->glyph >= cmap_to_glyph(S_stone)
|
|
&& lev->glyph < cmap_to_glyph(S_darkroom)) {
|
|
do_room_glyph = TRUE;
|
|
}
|
|
if (do_room_glyph) {
|
|
lev->glyph = (flags.dark_room && iflags.use_color
|
|
&& !Is_rogue_level(&u.uz))
|
|
? cmap_to_glyph(S_darkroom)
|
|
: (lev->waslit ? cmap_to_glyph(S_room)
|
|
: cmap_to_glyph(S_stone));
|
|
show_glyph(x, y, lev->glyph);
|
|
}
|
|
} else {
|
|
/* We feel it (I think hallways are the only things left). */
|
|
map_background(x, y, 1);
|
|
/* Corridors are never felt as lit (unless remembered that way) */
|
|
/* (lit_corridor only). */
|
|
if (lev->typ == CORR && lev->glyph == cmap_to_glyph(S_litcorr)
|
|
&& !lev->waslit)
|
|
show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
|
|
else if (lev->typ == ROOM && flags.dark_room && iflags.use_color
|
|
&& lev->glyph == cmap_to_glyph(S_room))
|
|
show_glyph(x, y, lev->glyph = cmap_to_glyph(S_darkroom));
|
|
}
|
|
} else {
|
|
if ((ep = engr_at(x, y)) != 0 && engr_can_be_felt(ep))
|
|
ep->erevealed = 1;
|
|
|
|
_map_location(x, y, 1);
|
|
|
|
if (Punished) {
|
|
/*
|
|
* A ball or chain is only felt if it is first on the object
|
|
* location list. Otherwise, we need to clear the felt bit ---
|
|
* something has been dropped on the ball/chain. If the bit is
|
|
* not cleared, then when the ball/chain is moved it will drop
|
|
* the wrong glyph.
|
|
*
|
|
* Note: during unpunish() we can be called by delobj() when
|
|
* destroying uchain while uball hasn't been cleared yet (so
|
|
* Punished will still yield True but uchain might not be part
|
|
* of the floor list anymore).
|
|
*/
|
|
if (uchain && uchain->where == OBJ_FLOOR
|
|
&& uchain->ox == x && uchain->oy == y
|
|
&& svl.level.objects[x][y] == uchain)
|
|
u.bc_felt |= BC_CHAIN;
|
|
else
|
|
u.bc_felt &= ~BC_CHAIN; /* do not feel the chain */
|
|
|
|
if (uball && uball->where == OBJ_FLOOR
|
|
&& uball->ox == x && uball->oy == y
|
|
&& svl.level.objects[x][y] == uball)
|
|
u.bc_felt |= BC_BALL;
|
|
else
|
|
u.bc_felt &= ~BC_BALL; /* do not feel the ball */
|
|
}
|
|
|
|
/* Floor spaces are dark if unlit. Corridors are dark if unlit. */
|
|
if (lev->typ == ROOM && lev->glyph == cmap_to_glyph(S_room)
|
|
&& (!lev->waslit || (flags.dark_room && iflags.use_color)))
|
|
show_glyph(x, y, lev->glyph = cmap_to_glyph(
|
|
flags.dark_room ? S_darkroom : S_stone));
|
|
else if (lev->typ == CORR && lev->glyph == cmap_to_glyph(S_litcorr)
|
|
&& !lev->waslit)
|
|
show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
|
|
}
|
|
/* draw monster on top if we can sense it */
|
|
if (!u_at(x, y) && (mon = m_at(x, y)) != 0 && sensemon(mon))
|
|
display_monster(x, y, mon,
|
|
(tp_sensemon(mon) || MATCH_WARN_OF_MON(mon))
|
|
? PHYSICALLY_SEEN
|
|
: DETECTED,
|
|
is_worm_tail(mon));
|
|
}
|
|
|
|
/*
|
|
* newsym()
|
|
*
|
|
* Possibly put a new glyph at the given location.
|
|
*/
|
|
void
|
|
newsym(coordxy x, coordxy y)
|
|
{
|
|
struct monst *mon;
|
|
int see_it;
|
|
boolean worm_tail;
|
|
struct rm *lev = &(levl[x][y]);
|
|
struct engr *ep;
|
|
|
|
/* don't try to produce map output when level is in a state of flux */
|
|
if (_suppress_map_output())
|
|
return;
|
|
|
|
/* only permit updating the hero when swallowed */
|
|
if (u.uswallow) {
|
|
if (u_at(x, y))
|
|
display_self();
|
|
return;
|
|
}
|
|
if (Underwater && !Is_waterlevel(&u.uz)) {
|
|
/* when underwater, don't do anything unless <x,y> is an
|
|
adjacent water or lava or ice position */
|
|
if (!(is_pool_or_lava(x, y) || is_ice(x, y)) || !next2u(x, y))
|
|
return;
|
|
}
|
|
|
|
/* Can physically see the location. */
|
|
if (cansee(x, y)) {
|
|
NhRegion *reg = visible_region_at(x, y);
|
|
/*
|
|
* Don't use templit here: E.g.
|
|
*
|
|
* lev->waslit = !!(lev->lit || templit(x,y));
|
|
*
|
|
* Otherwise we have the "light pool" problem, where non-permanently
|
|
* lit areas just out of sight stay remembered as lit. They should
|
|
* re-darken.
|
|
*
|
|
* Perhaps ALL areas should revert to their "unlit" look when
|
|
* out of sight.
|
|
*/
|
|
lev->waslit = (lev->lit != 0); /* remember lit condition */
|
|
|
|
mon = m_at(x, y);
|
|
worm_tail = is_worm_tail(mon);
|
|
|
|
if ((ep = engr_at(x, y)) != 0)
|
|
ep->erevealed = 1; /* even when covered by objects or a monster */
|
|
/*
|
|
* Normal region shown only on accessible positions, but
|
|
* poison clouds and steam clouds also shown above lava,
|
|
* pools and moats.
|
|
* However, sensed monsters (via detection or telepathy or
|
|
* warning) take precedence over all regions.
|
|
* Adjacent monsters also take precedence if they would be
|
|
* seen when there's no gas region.
|
|
*
|
|
* FIXME:
|
|
* The adjacency checking [in mon_overrides_region()] works
|
|
* when the hero is outside the region and the monster is
|
|
* inside, and when they're both inside, but not when the
|
|
* hero is inside and monster outside (because 'reg' will be
|
|
* Null for mon's <x,y>). Checking whether hero is inside
|
|
* a region for every newsym() seems excessive. The hero is
|
|
* usually blind when in a gas cloud so the problem is less
|
|
* noticeable then it might otherwise be.
|
|
*/
|
|
if (reg && (ACCESSIBLE(lev->typ)
|
|
|| (reg->visible && is_pool_or_lava(x, y)))) {
|
|
if (!mon_overrides_region(mon, x, y)) {
|
|
show_region(reg, x, y);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (u_at(x, y)) {
|
|
int see_self = canspotself();
|
|
|
|
/* update map information for <u.ux,u.uy> (remembered topology
|
|
and object/known trap/terrain glyph) but only display it if
|
|
hero can't see him/herself, then show self if appropriate */
|
|
_map_location(x, y, !see_self);
|
|
if (see_self)
|
|
display_self();
|
|
} else {
|
|
boolean show = FALSE;
|
|
|
|
see_it = mon && (mon_visible(mon)
|
|
|| (!worm_tail && (tp_sensemon(mon)
|
|
|| MATCH_WARN_OF_MON(mon))));
|
|
if (mon && (see_it || (!worm_tail && Detect_monsters))) {
|
|
if (mon->mtrapped) {
|
|
struct trap *trap = t_at(x, y);
|
|
int tt = trap ? trap->ttyp : NO_TRAP;
|
|
|
|
/* if monster is in a physical trap, you see trap too */
|
|
if (tt == BEAR_TRAP || is_pit(tt) || tt == WEB)
|
|
trap->tseen = 1;
|
|
}
|
|
_map_location(x, y, show); /* map under the monster */
|
|
/* also gets rid of any invisibility glyph */
|
|
display_monster(x, y, mon,
|
|
see_it ? PHYSICALLY_SEEN : DETECTED,
|
|
worm_tail);
|
|
} else if (mon && mon_warning(mon) && !worm_tail) {
|
|
display_warning(mon);
|
|
} else if (glyph_is_invisible(lev->glyph)) {
|
|
map_invisible(x, y);
|
|
} else {
|
|
_map_location(x, y, 1); /* map the location */
|
|
}
|
|
}
|
|
|
|
/* Can't see the location. */
|
|
} else {
|
|
if (u_at(x, y)) {
|
|
feel_location(u.ux, u.uy); /* forces an update */
|
|
|
|
if (canspotself())
|
|
display_self();
|
|
} else if ((mon = m_at(x, y)) != 0
|
|
&& ((see_it = (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)
|
|
|| (see_with_infrared(mon)
|
|
&& mon_visible(mon)))) != 0
|
|
|| (Detect_monsters && !is_worm_tail(mon)))) {
|
|
/* Seen or sensed monsters are printed every time.
|
|
This also gets rid of any invisibility glyph. */
|
|
display_monster(x, y, mon, see_it ? 0 : DETECTED,
|
|
is_worm_tail(mon) ? TRUE : FALSE);
|
|
} else if (mon && mon_warning(mon) && !is_worm_tail(mon)) {
|
|
display_warning(mon);
|
|
|
|
/*
|
|
* If the location is remembered as being both dark (waslit is false)
|
|
* and lit (glyph is a lit room or lit corridor) then it was either:
|
|
*
|
|
* (1) A dark location that the hero could see through night
|
|
* vision.
|
|
* (2) Darkened while out of the hero's sight. This can happen
|
|
* when cursed scroll of light is read.
|
|
*
|
|
* In either case, we have to manually correct the hero's memory to
|
|
* match waslit. Deciding when to change waslit is non-trivial.
|
|
*
|
|
* Note: If flags.lit_corridor is set, then corridors act like room
|
|
* squares. That is, they light up if in night vision range.
|
|
* If flags.lit_corridor is not set, then corridors will
|
|
* remain dark unless lit by a light spell and may darken
|
|
* again, as discussed above.
|
|
*
|
|
* These checks and changes must be here and not in back_to_glyph().
|
|
* They are dependent on the position being out of sight.
|
|
*/
|
|
} else if (Is_rogue_level(&u.uz)) {
|
|
if (lev->glyph == cmap_to_glyph(S_litcorr) && lev->typ == CORR)
|
|
show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
|
|
else if (lev->glyph == cmap_to_glyph(S_room) && lev->typ == ROOM
|
|
&& !lev->waslit)
|
|
show_glyph(x, y, lev->glyph = cmap_to_glyph(S_stone));
|
|
else
|
|
goto show_mem;
|
|
} else if (!lev->waslit || (flags.dark_room && iflags.use_color)) {
|
|
if (lev->glyph == cmap_to_glyph(S_litcorr) && lev->typ == CORR)
|
|
show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
|
|
else if (lev->glyph == cmap_to_glyph(S_room) && lev->typ == ROOM)
|
|
show_glyph(x, y, lev->glyph = cmap_to_glyph(DARKROOMSYM));
|
|
else
|
|
goto show_mem;
|
|
} else {
|
|
show_mem:
|
|
show_glyph(x, y, lev->glyph);
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef is_worm_tail
|
|
|
|
/*
|
|
* shieldeff()
|
|
*
|
|
* Put magic shield pyrotechnics at the given location. This *could* be
|
|
* pulled into a platform dependent routine for fancier graphics if desired.
|
|
*/
|
|
void
|
|
shieldeff(coordxy x, coordxy y)
|
|
{
|
|
int i;
|
|
|
|
if (!flags.sparkle)
|
|
return;
|
|
if (cansee(x, y)) { /* Don't see anything if can't see the location */
|
|
for (i = 0; i < SHIELD_COUNT; i++) {
|
|
show_glyph(x, y, cmap_to_glyph(shield_static[i]));
|
|
flush_screen(1); /* make sure the glyph shows up */
|
|
nh_delay_output();
|
|
}
|
|
newsym(x, y); /* restore the old information */
|
|
}
|
|
}
|
|
|
|
staticfn int
|
|
tether_glyph(coordxy x, coordxy y)
|
|
{
|
|
int tdx, tdy;
|
|
tdx = u.ux - x;
|
|
tdy = u.uy - y;
|
|
return zapdir_to_glyph(sgn(tdx),sgn(tdy), 2);
|
|
}
|
|
|
|
/*
|
|
* tmp_at()
|
|
*
|
|
* Temporarily place glyphs on the screen. Do not call nh_delay_output(). It
|
|
* is up to the caller to decide if it wants to wait [presently, everyone
|
|
* but explode() wants to delay].
|
|
*
|
|
* Call:
|
|
* (DISP_BEAM, glyph) open, initialize glyph
|
|
* (DISP_FLASH, glyph) open, initialize glyph
|
|
* (DISP_ALWAYS, glyph) open, initialize glyph
|
|
* (DISP_CHANGE, glyph) change glyph
|
|
* (DISP_END, 0) close & clean up (2nd argument doesn't matter)
|
|
* (DISP_FREEMEM, 0) only used to prevent memory leak during exit)
|
|
* (x, y) display the glyph at the location
|
|
*
|
|
* DISP_BEAM - Display the given glyph at each location, but do not erase
|
|
* any until the close call.
|
|
* DISP_ALL - Same as DISP_BEAM except glyph is shown at the specified
|
|
* spot even when that spot can't be seen.
|
|
* DISP_TETHER - Display a tether glyph at each location, and the tethered
|
|
* object at the farthest location, but do not erase any
|
|
* until the return trip or close.
|
|
* DISP_FLASH - Display the given glyph at each location, but erase the
|
|
* previous location's glyph.
|
|
* DISP_ALWAYS - Like DISP_FLASH, but vision is not taken into account.
|
|
*/
|
|
|
|
#define TMP_AT_MAX_GLYPHS (COLNO * 2)
|
|
|
|
static struct tmp_glyph {
|
|
coord saved[TMP_AT_MAX_GLYPHS]; /* previously updated positions */
|
|
int sidx; /* index of next unused slot in saved[] */
|
|
int style; /* either DISP_BEAM or DISP_FLASH or DISP_ALWAYS */
|
|
int glyph; /* glyph to use when printing */
|
|
struct tmp_glyph *prev;
|
|
} tgfirst;
|
|
|
|
void
|
|
tmp_at(coordxy x, coordxy y)
|
|
{
|
|
static struct tmp_glyph *tglyph = (struct tmp_glyph *) 0;
|
|
struct tmp_glyph *tmp;
|
|
|
|
switch (x) {
|
|
case DISP_BEAM:
|
|
case DISP_ALL:
|
|
case DISP_TETHER:
|
|
case DISP_FLASH:
|
|
case DISP_ALWAYS:
|
|
if (!tglyph)
|
|
tmp = &tgfirst;
|
|
else /* nested effect; we need dynamic memory */
|
|
tmp = (struct tmp_glyph *) alloc(sizeof *tmp);
|
|
tmp->prev = tglyph;
|
|
tglyph = tmp;
|
|
tglyph->sidx = 0;
|
|
tglyph->style = x;
|
|
tglyph->glyph = y;
|
|
flush_screen(0); /* flush buffered glyphs */
|
|
return;
|
|
|
|
case DISP_FREEMEM: /* in case game ends with tmp_at() in progress */
|
|
while (tglyph) {
|
|
tmp = tglyph->prev;
|
|
if (tglyph != &tgfirst)
|
|
free((genericptr_t) tglyph);
|
|
tglyph = tmp;
|
|
}
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!tglyph) {
|
|
panic("tmp_at: tglyph not initialized");
|
|
} else {
|
|
switch (x) {
|
|
case DISP_CHANGE:
|
|
tglyph->glyph = y;
|
|
break;
|
|
|
|
case DISP_END:
|
|
if (tglyph->style == DISP_BEAM || tglyph->style == DISP_ALL) {
|
|
int i;
|
|
|
|
/* Erase (reset) from source to end */
|
|
for (i = 0; i < tglyph->sidx; i++)
|
|
newsym(tglyph->saved[i].x, tglyph->saved[i].y);
|
|
} else if (tglyph->style == DISP_TETHER) {
|
|
int i;
|
|
|
|
if (y == BACKTRACK && tglyph->sidx > 1) {
|
|
/* backtrack */
|
|
for (i = tglyph->sidx - 1; i > 0; i--) {
|
|
newsym(tglyph->saved[i].x, tglyph->saved[i].y);
|
|
show_glyph(tglyph->saved[i - 1].x,
|
|
tglyph->saved[i - 1].y, tglyph->glyph);
|
|
flush_screen(0); /* make sure it shows up */
|
|
nh_delay_output();
|
|
}
|
|
tglyph->sidx = 1;
|
|
}
|
|
for (i = 0; i < tglyph->sidx; i++)
|
|
newsym(tglyph->saved[i].x, tglyph->saved[i].y);
|
|
} else { /* DISP_FLASH or DISP_ALWAYS */
|
|
if (tglyph->sidx) /* been called at least once */
|
|
newsym(tglyph->saved[0].x, tglyph->saved[0].y);
|
|
}
|
|
/* tglyph->sidx = 0; -- about to be freed, so not necessary */
|
|
tmp = tglyph->prev;
|
|
if (tglyph != &tgfirst)
|
|
free((genericptr_t) tglyph);
|
|
tglyph = tmp;
|
|
break;
|
|
|
|
default: /* do it */
|
|
if (!isok(x, y))
|
|
break;
|
|
if (tglyph->style == DISP_BEAM || tglyph->style == DISP_ALL) {
|
|
if (tglyph->style != DISP_ALL && !cansee(x, y))
|
|
break;
|
|
if (tglyph->sidx >= TMP_AT_MAX_GLYPHS)
|
|
break; /* too many locations */
|
|
/* save pos for later erasing */
|
|
tglyph->saved[tglyph->sidx].x = x;
|
|
tglyph->saved[tglyph->sidx].y = y;
|
|
tglyph->sidx += 1;
|
|
} else if (tglyph->style == DISP_TETHER) {
|
|
if (tglyph->sidx >= TMP_AT_MAX_GLYPHS)
|
|
break; /* too many locations */
|
|
if (tglyph->sidx) {
|
|
int px, py;
|
|
|
|
px = tglyph->saved[tglyph->sidx - 1].x;
|
|
py = tglyph->saved[tglyph->sidx - 1].y;
|
|
show_glyph(px, py, tether_glyph(px, py));
|
|
}
|
|
/* save pos for later use or erasure */
|
|
tglyph->saved[tglyph->sidx].x = x;
|
|
tglyph->saved[tglyph->sidx].y = y;
|
|
tglyph->sidx += 1;
|
|
} else { /* DISP_FLASH/ALWAYS */
|
|
if (tglyph
|
|
->sidx) { /* not first call, so reset previous pos */
|
|
newsym(tglyph->saved[0].x, tglyph->saved[0].y);
|
|
tglyph->sidx = 0; /* display is presently up to date */
|
|
}
|
|
if (!cansee(x, y) && tglyph->style != DISP_ALWAYS)
|
|
break;
|
|
tglyph->saved[0].x = x;
|
|
tglyph->saved[0].y = y;
|
|
tglyph->sidx = 1;
|
|
}
|
|
|
|
show_glyph(x, y, tglyph->glyph); /* show it */
|
|
flush_screen(0); /* make sure it shows up */
|
|
break;
|
|
} /* end switch */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* flash_glyph_at(x, y, glyph, repeatcount)
|
|
*
|
|
* Briefly flash between the passed glyph and the glyph that's
|
|
* meant to be at the location.
|
|
*/
|
|
void
|
|
flash_glyph_at(coordxy x, coordxy y, int tg, int rpt)
|
|
{
|
|
int i, glyph[2];
|
|
|
|
rpt *= 2; /* two loop iterations per 'count' */
|
|
glyph[0] = tg;
|
|
glyph[1] = (svl.level.flags.hero_memory) ? levl[x][y].glyph
|
|
: back_to_glyph(x, y);
|
|
/* even iteration count (guaranteed) ends with glyph[1] showing;
|
|
caller might want to override that, but no newsym() calls here
|
|
in case caller has tinkered with location visibility */
|
|
for (i = 0; i < rpt; i++) {
|
|
show_glyph(x, y, glyph[i % 2]);
|
|
flush_screen(1);
|
|
nh_delay_output();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* swallowed()
|
|
*
|
|
* The hero is swallowed. Show a special graphics sequence for this. This
|
|
* bypasses all of the display routines and messes with buffered screen
|
|
* directly. This method works because both vision and display check for
|
|
* being swallowed.
|
|
*/
|
|
void
|
|
swallowed(int first)
|
|
{
|
|
static coordxy lastx, lasty; /* last swallowed position */
|
|
int swallower, left_ok, rght_ok;
|
|
|
|
if (first) {
|
|
cls();
|
|
bot();
|
|
} else {
|
|
coordxy x, y;
|
|
|
|
/* Clear old location */
|
|
for (y = lasty - 1; y <= lasty + 1; y++)
|
|
for (x = lastx - 1; x <= lastx + 1; x++)
|
|
if (isok(x, y))
|
|
show_glyph(x, y, GLYPH_UNEXPLORED);
|
|
}
|
|
|
|
swallower = monsndx(u.ustuck->data);
|
|
/* assume isok(u.ux,u.uy) */
|
|
left_ok = isok(u.ux - 1, u.uy);
|
|
rght_ok = isok(u.ux + 1, u.uy);
|
|
/*
|
|
* Display the hero surrounded by the monster's stomach.
|
|
*/
|
|
if (isok(u.ux, u.uy - 1)) {
|
|
if (left_ok)
|
|
show_glyph(u.ux - 1, u.uy - 1,
|
|
swallow_to_glyph(swallower, S_sw_tl));
|
|
show_glyph(u.ux, u.uy - 1, swallow_to_glyph(swallower, S_sw_tc));
|
|
if (rght_ok)
|
|
show_glyph(u.ux + 1, u.uy - 1,
|
|
swallow_to_glyph(swallower, S_sw_tr));
|
|
}
|
|
|
|
if (left_ok)
|
|
show_glyph(u.ux - 1, u.uy, swallow_to_glyph(swallower, S_sw_ml));
|
|
display_self();
|
|
if (rght_ok)
|
|
show_glyph(u.ux + 1, u.uy, swallow_to_glyph(swallower, S_sw_mr));
|
|
|
|
if (isok(u.ux, u.uy + 1)) {
|
|
if (left_ok)
|
|
show_glyph(u.ux - 1, u.uy + 1,
|
|
swallow_to_glyph(swallower, S_sw_bl));
|
|
show_glyph(u.ux, u.uy + 1, swallow_to_glyph(swallower, S_sw_bc));
|
|
if (rght_ok)
|
|
show_glyph(u.ux + 1, u.uy + 1,
|
|
swallow_to_glyph(swallower, S_sw_br));
|
|
}
|
|
|
|
/* Update the swallowed position. */
|
|
lastx = u.ux;
|
|
lasty = u.uy;
|
|
}
|
|
|
|
/*
|
|
* under_water()
|
|
*
|
|
* Similar to swallowed() in operation. Shows hero when underwater
|
|
* except when in water level. Special routines exist for that.
|
|
*/
|
|
void
|
|
under_water(int mode)
|
|
{
|
|
static coordxy lastx, lasty;
|
|
static boolean dela;
|
|
coordxy x, y;
|
|
|
|
/* swallowing has a higher precedence than under water */
|
|
if (Is_waterlevel(&u.uz) || u.uswallow)
|
|
return;
|
|
|
|
/* full update */
|
|
if (mode == 1 || dela) {
|
|
cls();
|
|
dela = FALSE;
|
|
|
|
/* delayed full update */
|
|
} else if (mode == 2) {
|
|
dela = TRUE;
|
|
return;
|
|
|
|
/* limited update */
|
|
} else {
|
|
for (y = lasty - 1; y <= lasty + 1; y++)
|
|
for (x = lastx - 1; x <= lastx + 1; x++)
|
|
if (isok(x, y))
|
|
show_glyph(x, y, GLYPH_UNEXPLORED);
|
|
}
|
|
|
|
/*
|
|
* TODO? Should this honor Xray radius rather than force radius 1?
|
|
*/
|
|
|
|
for (x = u.ux - 1; x <= u.ux + 1; x++)
|
|
for (y = u.uy - 1; y <= u.uy + 1; y++)
|
|
if (isok(x, y) && (is_pool_or_lava(x, y) || is_ice(x, y))) {
|
|
if (Blind && !u_at(x, y))
|
|
show_glyph(x, y, GLYPH_UNEXPLORED);
|
|
else
|
|
newsym(x, y);
|
|
}
|
|
lastx = u.ux;
|
|
lasty = u.uy;
|
|
}
|
|
|
|
/*
|
|
* under_ground()
|
|
*
|
|
* Very restricted display. You can only see yourself.
|
|
*/
|
|
void
|
|
under_ground(int mode)
|
|
{
|
|
static boolean dela;
|
|
|
|
/* swallowing has a higher precedence than under ground */
|
|
if (u.uswallow)
|
|
return;
|
|
|
|
/* full update */
|
|
if (mode == 1 || dela) {
|
|
cls();
|
|
dela = FALSE;
|
|
|
|
/* delayed full update */
|
|
} else if (mode == 2) {
|
|
dela = TRUE;
|
|
return;
|
|
|
|
/* limited update */
|
|
} else {
|
|
newsym(u.ux, u.uy);
|
|
}
|
|
}
|
|
|
|
/* ======================================================================== */
|
|
|
|
/*
|
|
* Loop through all of the monsters and update them. Called when:
|
|
* + going blind & telepathic
|
|
* + regaining sight & telepathic
|
|
* + getting and losing infravision
|
|
* + hallucinating
|
|
* + doing a full screen redraw
|
|
* + see invisible times out or a ring of see invisible is taken off
|
|
* or intrinsic see invisible is stolen by a gremlin
|
|
* + when a potion of see invisible is quaffed or a ring of see
|
|
* invisible is put on
|
|
* + gaining telepathy when blind [givit() in eat.c, pleased() in pray.c]
|
|
* + losing telepathy while blind [xkilled() in mon.c, attrcurse() in
|
|
* sit.c]
|
|
*/
|
|
void
|
|
see_monsters(void)
|
|
{
|
|
struct monst *mon;
|
|
int new_warn_obj_cnt = 0;
|
|
|
|
if (gd.defer_see_monsters)
|
|
return;
|
|
|
|
/* steed and unseen engulfer/holder/holdee are recognized via touch
|
|
even if they aren't going to be rendered; other monsters
|
|
may get flagged as having been seen by display_monster() if it's
|
|
called by newsym() */
|
|
if (u.usteed)
|
|
u.usteed->meverseen = 1;
|
|
if (u.ustuck)
|
|
u.ustuck->meverseen = 1;
|
|
|
|
/* loop through level.monsters (aka fmon) */
|
|
for (mon = fmon; mon; mon = mon->nmon) {
|
|
if (DEADMONSTER(mon))
|
|
continue;
|
|
newsym(mon->mx, mon->my);
|
|
if (mon->wormno)
|
|
see_wsegs(mon);
|
|
if (Warn_of_mon
|
|
&& (svc.context.warntype.obj & mon->data->mflags2) != 0L)
|
|
new_warn_obj_cnt++;
|
|
}
|
|
|
|
/*
|
|
* Make Sting glow blue or stop glowing if required.
|
|
*/
|
|
if (new_warn_obj_cnt != gw.warn_obj_cnt) {
|
|
Sting_effects(new_warn_obj_cnt);
|
|
gw.warn_obj_cnt = new_warn_obj_cnt;
|
|
}
|
|
|
|
/* when mounted, hero's location gets caught by monster loop */
|
|
if (!u.usteed)
|
|
newsym(u.ux, u.uy);
|
|
}
|
|
|
|
staticfn void
|
|
mimic_light_blocking(struct monst *mtmp)
|
|
{
|
|
if (mtmp->minvis && is_lightblocker_mappear(mtmp)) {
|
|
if (See_invisible)
|
|
block_point(mtmp->mx, mtmp->my);
|
|
else
|
|
unblock_point(mtmp->mx, mtmp->my);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Block/unblock light depending on what a mimic is mimicking and if it's
|
|
* invisible or not. Should be called only when the state of See_invisible
|
|
* changes.
|
|
*/
|
|
void
|
|
set_mimic_blocking(void)
|
|
{
|
|
iter_mons(mimic_light_blocking);
|
|
}
|
|
|
|
/*
|
|
* Loop through all of the object *locations* and update them. Called when
|
|
* + hallucinating.
|
|
*/
|
|
void
|
|
see_objects(void)
|
|
{
|
|
struct obj *obj;
|
|
|
|
for (obj = fobj; obj; obj = obj->nobj)
|
|
if (vobj_at(obj->ox, obj->oy) == obj)
|
|
newsym(obj->ox, obj->oy);
|
|
|
|
/* Qt's "paper doll" subset of persistent inventory shows map tiles
|
|
for objects which aren't on the floor so not handled by above loop;
|
|
inventory which includes glyphs should also be affected, so do this
|
|
for all interfaces in case any feature that for persistent inventory */
|
|
update_inventory();
|
|
}
|
|
|
|
/* mark the top object of nearby stacks as having been seen, and if
|
|
that object was being displayed as generic, redisplay it as specific */
|
|
void
|
|
see_nearby_objects(void)
|
|
{
|
|
struct obj *obj;
|
|
int glyph;
|
|
coordxy ix, iy, x = u.ux, y = u.uy;
|
|
/* these 'r' and 'neardist' calculations match distant_name(objnam.c) */
|
|
int r = (u.xray_range > 2) ? u.xray_range : 2,
|
|
/* neardist produces a small square with rounded corners */
|
|
neardist = (r * r) * 2 - r; /* same as r*r + r*(r-1) */
|
|
|
|
/* [note: this could be optimized to avoid the distu() calculations] */
|
|
for (iy = y - r; iy <= y + r; ++iy)
|
|
for (ix = x - r; ix <= x + r; ++ix) {
|
|
if (!isok(ix, iy))
|
|
continue;
|
|
/* skip if no object or the object has already been marked as
|
|
having been seen up close */
|
|
if ((obj = vobj_at(ix, iy)) == 0 || obj->dknown)
|
|
continue;
|
|
/* skip if the spot can't be seen or is too far (diagonal) */
|
|
if (!cansee(ix, iy) || distu(ix, iy) > neardist)
|
|
continue;
|
|
|
|
obj->dknown = 1; /* near enough to see it */
|
|
/* operate on remembered glyph rather than current one */
|
|
glyph = levl[ix][iy].glyph;
|
|
if (glyph_is_generic_object(glyph))
|
|
newsym_force(ix, iy);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update hallucinated traps.
|
|
*/
|
|
void
|
|
see_traps(void)
|
|
{
|
|
struct trap *trap;
|
|
int glyph;
|
|
|
|
for (trap = gf.ftrap; trap; trap = trap->ntrap) {
|
|
glyph = _glyph_at(trap->tx, trap->ty);
|
|
if (glyph_is_trap(glyph))
|
|
newsym(trap->tx, trap->ty);
|
|
}
|
|
}
|
|
|
|
/* glyph, ttychar, framecolor,
|
|
{ glyphflags, { NO_COLOR, sym.symidx },
|
|
customcolor, color256idx, tileidx, u } */
|
|
static glyph_info no_ginfo = {
|
|
NO_GLYPH, ' ', NO_COLOR,
|
|
{ MG_BADXY, { NO_COLOR, 0 },
|
|
0,
|
|
0U, 0U
|
|
#ifdef ENHANCED_SYMBOLS
|
|
, 0
|
|
#endif
|
|
}
|
|
};
|
|
|
|
#ifndef UNBUFFERED_GLYPHINFO
|
|
/* Note that the 'glyph' argument is not used in the expansion
|
|
* of this !UNBUFFERED_GLYPHINFO (default) variation, but is
|
|
* a requirement for the UNBUFFERED_GLYPHINFO variation */
|
|
#define Glyphinfo_at(x, y, glyph) \
|
|
(((x) < 0 || (y) < 0 || (x) >= COLNO || (y) >= ROWNO) ? &no_ginfo \
|
|
: &gg.gbuf[(y)][(x)].glyphinfo)
|
|
#else
|
|
static glyph_info ginfo;
|
|
staticfn glyph_info *glyphinfo_at(coordxy, coordxy, int);
|
|
#define Glyphinfo_at(x, y, glyph) glyphinfo_at((x), (y), (glyph))
|
|
#endif
|
|
|
|
#ifdef TILES_IN_GLYPHMAP
|
|
extern const glyph_info nul_glyphinfo; /* tile.c */
|
|
#else
|
|
/* glyph, ttychar, framecolor, { glyphflags, { sym.symidx }, nhcolor,
|
|
tileidx, 0} */
|
|
const glyph_info nul_glyphinfo = {
|
|
NO_GLYPH, ' ', NO_COLOR,
|
|
{ /* glyph_map */
|
|
MG_UNEXPL,
|
|
{ NO_COLOR, SYM_UNEXPLORED + SYM_OFF_X },
|
|
0,
|
|
0U, 0U
|
|
#ifdef ENHANCED_SYMBOLS
|
|
, 0
|
|
#endif
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#ifdef TILES_IN_GLYPHMAP
|
|
extern glyph_map glyphmap[MAX_GLYPH]; /* from tile.c */
|
|
#else
|
|
glyph_map glyphmap[MAX_GLYPH] = {
|
|
{ 0U, { NO_COLOR, 0 },
|
|
0,
|
|
0U, 0U
|
|
#ifdef ENHANCED_SYMBOLS
|
|
, 0
|
|
#endif
|
|
}
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
* Put the cursor on the hero. Flush all accumulated glyphs before doing it.
|
|
*/
|
|
void
|
|
curs_on_u(void)
|
|
{
|
|
flush_screen(1); /* Flush waiting glyphs & put cursor on hero */
|
|
}
|
|
|
|
/* the #redraw command */
|
|
int
|
|
doredraw(void)
|
|
{
|
|
docrt();
|
|
return ECMD_OK;
|
|
}
|
|
|
|
/* the main refresh-the-screen routine */
|
|
void
|
|
docrt(void)
|
|
{
|
|
docrt_flags(docrtRecalc);
|
|
}
|
|
|
|
/* docrt() with finer control */
|
|
void
|
|
docrt_flags(int refresh_flags)
|
|
{
|
|
coordxy x, y;
|
|
struct rm *lev;
|
|
boolean maponly = (refresh_flags & docrtMapOnly) != 0,
|
|
redrawonly = (refresh_flags & docrtRefresh) != 0,
|
|
nocls = (refresh_flags & docrtNocls) != 0;
|
|
|
|
if (!u.ux || program_state.in_docrt)
|
|
return; /* display isn't ready yet */
|
|
|
|
program_state.in_docrt = TRUE;
|
|
|
|
if (redrawonly) {
|
|
redraw_map(FALSE);
|
|
goto post_map;
|
|
}
|
|
if (u.uswallow) {
|
|
swallowed(1);
|
|
goto post_map;
|
|
}
|
|
if (Underwater && !Is_waterlevel(&u.uz)) {
|
|
under_water(1);
|
|
goto post_map;
|
|
}
|
|
if (u.uburied) { /* [not implemented] */
|
|
under_ground(1);
|
|
goto post_map;
|
|
}
|
|
|
|
/* shut down vision */
|
|
vision_recalc(2);
|
|
|
|
/*
|
|
* This routine assumes that cls() does the following:
|
|
* + fills the physical screen with the symbol for rock
|
|
* + clears the glyph buffer
|
|
*/
|
|
if (!nocls)
|
|
cls();
|
|
|
|
/* display memory */
|
|
for (x = 1; x < COLNO; x++) {
|
|
lev = &levl[x][0];
|
|
for (y = 0; y < ROWNO; y++, lev++)
|
|
show_glyph(x, y, lev->glyph);
|
|
}
|
|
|
|
/* see what is to be seen */
|
|
vision_recalc(0);
|
|
|
|
/* overlay with monsters */
|
|
see_monsters();
|
|
|
|
post_map:
|
|
|
|
if (!maponly) {
|
|
/* perm_invent */
|
|
update_inventory();
|
|
/* status */
|
|
disp.botlx = TRUE; /* force a redraw of the bottom lines */
|
|
/* note: caller needs to call bot() to actually redraw status */
|
|
}
|
|
program_state.in_docrt = FALSE;
|
|
}
|
|
|
|
/* for panning beyond a clipped region; resend the current map data to
|
|
the interface rather than use docrt()'s regeneration of that data */
|
|
void
|
|
redraw_map(boolean cursor_on_u)
|
|
{
|
|
coordxy x, y;
|
|
int glyph;
|
|
glyph_info bkglyphinfo = nul_glyphinfo;
|
|
|
|
/*
|
|
* Not sure whether this is actually necessary; save and restore did
|
|
* used to get much too involved with each dungeon level as it was
|
|
* read and written.
|
|
*
|
|
* !u.ux: display isn't ready yet; (restoring || !on_level()): was part
|
|
* of cliparound() but interface shouldn't access this much internals
|
|
*/
|
|
if (!u.ux || suppress_map_output() || !on_level(&u.uz0, &u.uz))
|
|
return;
|
|
|
|
/*
|
|
* This yields sensible clipping when #terrain+getpos is in
|
|
* progress and the screen displays something other than what
|
|
* the map would currently be showing.
|
|
*/
|
|
for (y = 0; y < ROWNO; ++y)
|
|
for (x = 1; x < COLNO; ++x) {
|
|
glyph = _glyph_at(x, y); /* not levl[x][y].glyph */
|
|
get_bkglyph_and_framecolor(x, y, &bkglyphinfo.glyph,
|
|
&bkglyphinfo.framecolor);
|
|
print_glyph(WIN_MAP, x, y,
|
|
Glyphinfo_at(x, y, glyph), &bkglyphinfo);
|
|
}
|
|
flush_screen(cursor_on_u);
|
|
#ifndef UNBUFFERED_GLYPHINFO
|
|
nhUse(glyph);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* =======================================================
|
|
*/
|
|
void
|
|
reglyph_darkroom(void)
|
|
{
|
|
coordxy x, y;
|
|
|
|
for (x = 1; x < COLNO; x++)
|
|
for (y = 0; y < ROWNO; y++) {
|
|
struct rm *lev = &levl[x][y];
|
|
|
|
if (!flags.dark_room) {
|
|
if (lev->glyph == cmap_to_glyph(S_corr)
|
|
&& lev->waslit)
|
|
lev->glyph = cmap_to_glyph(S_litcorr);
|
|
} else {
|
|
if (lev->glyph == cmap_to_glyph(S_litcorr)
|
|
&& !cansee(x, y))
|
|
lev->glyph = cmap_to_glyph(S_corr);
|
|
}
|
|
|
|
if (!flags.dark_room || !iflags.use_color
|
|
|| Is_rogue_level(&u.uz)) {
|
|
if (lev->glyph == cmap_to_glyph(S_darkroom))
|
|
lev->glyph = lev->waslit ? cmap_to_glyph(S_room)
|
|
: GLYPH_NOTHING;
|
|
} else {
|
|
if (lev->glyph == cmap_to_glyph(S_room) && lev->seenv
|
|
&& lev->waslit && !cansee(x, y))
|
|
lev->glyph = cmap_to_glyph(S_darkroom);
|
|
else if (lev->glyph == GLYPH_NOTHING
|
|
&& lev->typ == ROOM && lev->seenv && !cansee(x, y))
|
|
lev->glyph = cmap_to_glyph(S_darkroom);
|
|
}
|
|
}
|
|
if (flags.dark_room && iflags.use_color)
|
|
gs.showsyms[S_darkroom] = gs.showsyms[S_room];
|
|
else
|
|
gs.showsyms[S_darkroom] = gs.showsyms[SYM_NOTHING + SYM_OFF_X];
|
|
}
|
|
|
|
/* ======================================================================== */
|
|
/* Glyph Buffering (3rd screen) =========================================== */
|
|
|
|
/* FIXME: This is a dirty hack, because newsym() doesn't distinguish
|
|
* between object piles and single objects, it doesn't mark the location
|
|
* for update. */
|
|
void
|
|
newsym_force(coordxy x, coordxy y)
|
|
{
|
|
newsym(x, y);
|
|
gg.gbuf[y][x].gnew = 1;
|
|
if (gg.gbuf_start[y] > x)
|
|
gg.gbuf_start[y] = x;
|
|
if (gg.gbuf_stop[y] < x)
|
|
gg.gbuf_stop[y] = x;
|
|
}
|
|
|
|
/*
|
|
* Store the glyph in the 3rd screen for later flushing.
|
|
*/
|
|
void
|
|
show_glyph(coordxy x, coordxy y, int glyph)
|
|
{
|
|
#ifndef UNBUFFERED_GLYPHINFO
|
|
glyph_info glyphinfo;
|
|
#endif
|
|
boolean show_glyph_change = FALSE;
|
|
int oldglyph;
|
|
|
|
/* don't process map glyphs when saving, restoring, or in_mklev */
|
|
if (_suppress_map_output())
|
|
return;
|
|
|
|
//if (glyph == 3972 || glyph == 3988)
|
|
// __debugbreak();
|
|
/*
|
|
* Check for bad positions and glyphs.
|
|
*/
|
|
if (!isok(x, y)) {
|
|
const char *text = "";
|
|
int offset = -1;
|
|
|
|
/* column 0 is invalid, but it's often used as a flag, so ignore it */
|
|
if (x == 0)
|
|
return;
|
|
|
|
/*
|
|
* This assumes an ordering of the offsets. See display.h for
|
|
* the definition.
|
|
*/
|
|
if (glyph < 0 || glyph >= MAX_GLYPH) {
|
|
/* invalid location and also invalid glyph */
|
|
text = "invalid";
|
|
} else if ((offset = (glyph - GLYPH_NOTHING_OFF)) >= 0) {
|
|
text = "nothing";
|
|
} else if ((offset = (glyph - GLYPH_UNEXPLORED_OFF)) >= 0) {
|
|
text = "unexplored";
|
|
} else if ((offset = (glyph - GLYPH_STATUE_FEM_PILETOP_OFF)) >= 0) {
|
|
text = "statue of a female monster at top of a pile";
|
|
} else if ((offset = (glyph - GLYPH_STATUE_MALE_PILETOP_OFF)) >= 0) {
|
|
text = "statue of a male monster at top of a pile";
|
|
} else if ((offset = (glyph - GLYPH_BODY_PILETOP_OFF)) >= 0) {
|
|
text = "body at top of a pile";
|
|
} else if ((offset = (glyph - GLYPH_OBJ_PILETOP_OFF)) >= 0) {
|
|
text = (glyph_is_piletop_generic_obj(glyph)
|
|
? "generic object at top of a pile"
|
|
: "object at top of a pile");
|
|
} else if ((offset = (glyph - GLYPH_STATUE_FEM_OFF)) >= 0) {
|
|
text = "statue of female monster";
|
|
} else if ((offset = (glyph - GLYPH_STATUE_MALE_OFF)) >= 0) {
|
|
text = "statue of male monster";
|
|
} else if ((offset = (glyph - GLYPH_WARNING_OFF)) >= 0) {
|
|
/* warn flash */
|
|
text = "warning explosion";
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_FROSTY_OFF)) >= 0) {
|
|
text = "frosty explosion";
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_FIERY_OFF)) >= 0) {
|
|
text = "fiery explosion";
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_MAGICAL_OFF)) >= 0) {
|
|
text = "magical explosion";
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_WET_OFF)) >= 0) {
|
|
text = "wet explosion";
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_MUDDY_OFF)) >= 0) {
|
|
text = "muddy explosion";
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_NOXIOUS_OFF)) >= 0) {
|
|
text = "noxious explosion";
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_DARK_OFF)) >= 0) {
|
|
text = "dark explosion";
|
|
} else if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) {
|
|
text = "swallow";
|
|
} else if ((offset = (glyph - GLYPH_CMAP_C_OFF)) >= 0) {
|
|
text = "cmap C";
|
|
} else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) {
|
|
text = "zap";
|
|
} else if ((offset = (glyph - GLYPH_CMAP_B_OFF)) >= 0) {
|
|
text = "cmap B";
|
|
} else if ((offset = (glyph - GLYPH_ALTAR_OFF)) >= 0) {
|
|
text = "altar";
|
|
} else if ((offset = (glyph - GLYPH_CMAP_A_OFF)) >= 0) {
|
|
text = "cmap A";
|
|
} else if ((offset = (glyph - GLYPH_CMAP_SOKO_OFF)) >= 0) {
|
|
text = "sokoban dungeon walls";
|
|
} else if ((offset = (glyph - GLYPH_CMAP_KNOX_OFF)) >= 0) {
|
|
text = "knox dungeon walls";
|
|
} else if ((offset = (glyph - GLYPH_CMAP_GEH_OFF)) >= 0) {
|
|
text = "gehennom dungeon walls";
|
|
} else if ((offset = (glyph - GLYPH_CMAP_MINES_OFF)) >= 0) {
|
|
text = "gnomish mines dungeon walls";
|
|
} else if ((offset = (glyph - GLYPH_CMAP_MAIN_OFF)) >= 0) {
|
|
text = "main dungeon walls";
|
|
} else if ((offset = (glyph - GLYPH_CMAP_STONE_OFF)) >= 0) {
|
|
text = "stone";
|
|
} else if ((offset = (glyph - GLYPH_OBJ_OFF)) >= 0) {
|
|
text = (glyph_is_normal_generic_obj(glyph)
|
|
? "generic object"
|
|
: "object");
|
|
} else if ((offset = (glyph - GLYPH_RIDDEN_FEM_OFF)) >= 0) {
|
|
text = "ridden female monster";
|
|
} else if ((offset = (glyph - GLYPH_RIDDEN_MALE_OFF)) >= 0) {
|
|
text = "ridden male monster";
|
|
} else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) {
|
|
text = "body";
|
|
} else if ((offset = (glyph - GLYPH_DETECT_FEM_OFF)) >= 0) {
|
|
text = "detected female monster";
|
|
} else if ((offset = (glyph - GLYPH_DETECT_MALE_OFF)) >= 0) {
|
|
text = "detected male monster";
|
|
} else if ((offset = (glyph - GLYPH_INVIS_OFF)) >= 0) {
|
|
text = "invisible monster";
|
|
} else if ((offset = (glyph - GLYPH_PET_FEM_OFF)) >= 0) {
|
|
text = "female pet";
|
|
} else if ((offset = (glyph - GLYPH_PET_MALE_OFF)) >= 0) {
|
|
text = "male pet";
|
|
} else if ((offset = (glyph - GLYPH_MON_FEM_OFF)) >= 0) {
|
|
text = "female monster";
|
|
} else if ((offset = (glyph - GLYPH_MON_MALE_OFF)) >= 0) {
|
|
text = "male monster";
|
|
}
|
|
impossible("show_glyph: bad pos <%d,%d> with glyph %d [%s %d].",
|
|
x, y, glyph, text, offset);
|
|
return;
|
|
} else if (glyph < 0 || glyph >= MAX_GLYPH) {
|
|
/* valid location but invalid glyph */
|
|
impossible("show_glyph: bad glyph %d [max %d] at <%d,%d>.",
|
|
glyph, MAX_GLYPH, x, y);
|
|
return;
|
|
}
|
|
#ifndef UNBUFFERED_GLYPHINFO
|
|
/* without UNBUFFERED_GLYPHINFO defined the glyphinfo values are buffered
|
|
alongside the glyphs themselves for better performance, increased
|
|
buffer size */
|
|
map_glyphinfo(x, y, glyph, 0, &glyphinfo);
|
|
#endif
|
|
|
|
oldglyph = gg.gbuf[y][x].glyphinfo.glyph;
|
|
|
|
if (a11y.glyph_updates && !a11y.mon_notices_blocked
|
|
&& !program_state.in_docrt
|
|
&& !program_state.in_getlev
|
|
&& (oldglyph != glyph || gg.gbuf[y][x].gnew)) {
|
|
int c = glyph_to_cmap(glyph);
|
|
|
|
if ((glyph_is_nothing(oldglyph) || glyph_is_unexplored(oldglyph)
|
|
|| is_cmap_furniture(c))
|
|
&& !is_cmap_wall(c) && !is_cmap_room(c)) {
|
|
if ((a11y.mon_notices && glyph_is_monster(glyph))
|
|
|| (glyph_is_monster(oldglyph))
|
|
|| u_at(x, y)) {
|
|
; /* nothing */
|
|
} else {
|
|
show_glyph_change = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gg.gbuf[y][x].glyphinfo.glyph != glyph
|
|
#ifndef UNBUFFERED_GLYPHINFO
|
|
/* flags might change (single object vs pile, monster tamed or pet
|
|
gone feral), color might change (altar's alignment converted by
|
|
invisible hero), but ttychar normally won't change unless the
|
|
glyph does too (changing boulder symbol would be an exception,
|
|
but that triggers full redraw so doesn't matter here); still,
|
|
be thorough and check everything */
|
|
|| gg.gbuf[y][x].glyphinfo.ttychar != glyphinfo.ttychar
|
|
|| gg.gbuf[y][x].glyphinfo.gm.customcolor != glyphinfo.gm.customcolor
|
|
|| gg.gbuf[y][x].glyphinfo.gm.glyphflags != glyphinfo.gm.glyphflags
|
|
|| gg.gbuf[y][x].glyphinfo.gm.sym.color != glyphinfo.gm.sym.color
|
|
|| gg.gbuf[y][x].glyphinfo.gm.tileidx != glyphinfo.gm.tileidx
|
|
#endif
|
|
|| iflags.use_background_glyph) {
|
|
gg.gbuf[y][x].glyphinfo.glyph = glyph;
|
|
gg.gbuf[y][x].gnew = 1;
|
|
#ifndef UNBUFFERED_GLYPHINFO
|
|
gg.gbuf[y][x].glyphinfo.glyph = glyphinfo.glyph;
|
|
gg.gbuf[y][x].glyphinfo.ttychar = glyphinfo.ttychar;
|
|
gg.gbuf[y][x].glyphinfo.gm = glyphinfo.gm;
|
|
#endif
|
|
if (gg.gbuf_start[y] > x)
|
|
gg.gbuf_start[y] = x;
|
|
if (gg.gbuf_stop[y] < x)
|
|
gg.gbuf_stop[y] = x;
|
|
}
|
|
|
|
if (show_glyph_change) {
|
|
char buf[BUFSZ];
|
|
coord cc;
|
|
int sym = 0;
|
|
const char *firstmatch = 0;
|
|
boolean tmp_accessiblemsg = a11y.accessiblemsg;
|
|
|
|
a11y.accessiblemsg = TRUE;
|
|
cc.x = x, cc.y = y;
|
|
do_screen_description(cc, TRUE, sym, buf, &firstmatch, NULL);
|
|
pline_xy(x, y, "%s.", firstmatch);
|
|
a11y.accessiblemsg = tmp_accessiblemsg;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reset the changed glyph borders so that none of the 3rd screen has
|
|
* changed.
|
|
*/
|
|
#define reset_glyph_bbox() \
|
|
{ \
|
|
int i; \
|
|
\
|
|
for (i = 0; i < ROWNO; i++) { \
|
|
gg.gbuf_start[i] = COLNO - 1; \
|
|
gg.gbuf_stop[i] = 0; \
|
|
} \
|
|
}
|
|
|
|
static gbuf_entry nul_gbuf = {
|
|
0, /* gnew */
|
|
{ GLYPH_UNEXPLORED, (unsigned) ' ', NO_COLOR,
|
|
/* glyphinfo.glyph, glyphinfo.ttychar */
|
|
/* glyphinfo.gm */
|
|
{ MG_UNEXPL, { NO_COLOR, 0 },
|
|
0,
|
|
0U, 0U
|
|
#ifdef ENHANCED_SYMBOLS
|
|
, 0
|
|
#endif
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Turn the 3rd screen into UNEXPLORED that needs to be refreshed.
|
|
*/
|
|
void
|
|
clear_glyph_buffer(void)
|
|
{
|
|
coordxy x, y;
|
|
gbuf_entry *gptr = &gg.gbuf[0][0];
|
|
glyph_info *giptr =
|
|
#ifndef UNBUFFERED_GLYPHINFO
|
|
&gptr->glyphinfo;
|
|
#else
|
|
&ginfo;
|
|
|
|
map_glyphinfo(0, 0, GLYPH_UNEXPLORED, 0, giptr);
|
|
#endif
|
|
#ifndef UNBUFFERED_GLYPHINFO
|
|
nul_gbuf.gnew = (giptr->ttychar != nul_gbuf.glyphinfo.ttychar
|
|
|| giptr->gm.sym.color != nul_gbuf.glyphinfo.gm.sym.color
|
|
|| giptr->gm.glyphflags
|
|
!= nul_gbuf.glyphinfo.gm.glyphflags
|
|
|| giptr->gm.customcolor
|
|
!= nul_gbuf.glyphinfo.gm.customcolor
|
|
|| giptr->gm.tileidx != nul_gbuf.glyphinfo.gm.tileidx)
|
|
#else
|
|
nul_gbuf.gnew = (giptr->ttychar != ' '
|
|
|| giptr->gm.sym.color != NO_COLOR
|
|
|| giptr->gm.customcolor != 0
|
|
|| (giptr->gm.glyphflags & ~MG_UNEXPL) != 0)
|
|
#endif
|
|
? 1 : 0;
|
|
for (y = 0; y < ROWNO; y++) {
|
|
gptr = &gg.gbuf[y][0];
|
|
for (x = COLNO; x; x--) {
|
|
*gptr++ = nul_gbuf;
|
|
}
|
|
gg.gbuf_start[y] = 1;
|
|
gg.gbuf_stop[y] = COLNO - 1;
|
|
}
|
|
}
|
|
|
|
/* used by tty after menu or text popup has temporarily overwritten the map
|
|
and it has been erased so shows spaces, not necessarily S_unexplored */
|
|
void
|
|
row_refresh(coordxy start, coordxy stop, coordxy y)
|
|
{
|
|
coordxy x;
|
|
int glyph;
|
|
boolean force;
|
|
gbuf_entry *gptr = &gg.gbuf[0][0];
|
|
glyph_info bkglyphinfo = nul_glyphinfo;
|
|
glyph_info *giptr =
|
|
#ifndef UNBUFFERED_GLYPHINFO
|
|
&gptr->glyphinfo;
|
|
#else
|
|
&ginfo;
|
|
|
|
map_glyphinfo(0, 0, GLYPH_UNEXPLORED, 0U, giptr);
|
|
#endif
|
|
#ifndef UNBUFFERED_GLYPHINFO
|
|
force = (giptr->ttychar != nul_gbuf.glyphinfo.ttychar
|
|
|| giptr->gm.sym.color != nul_gbuf.glyphinfo.gm.sym.color
|
|
|| giptr->gm.glyphflags != nul_gbuf.glyphinfo.gm.glyphflags
|
|
|| giptr->gm.customcolor != nul_gbuf.glyphinfo.gm.customcolor
|
|
|| giptr->gm.tileidx != nul_gbuf.glyphinfo.gm.tileidx)
|
|
#else
|
|
force = (giptr->ttychar != ' '
|
|
|| giptr->gm.sym.color != NO_COLOR
|
|
|| giptr->gm.gm.customcolor != 0
|
|
|| (giptr->gm.glyphflags & ~MG_UNEXPL) != 0)
|
|
#endif
|
|
? 1 : 0;
|
|
for (x = start; x <= stop; x++) {
|
|
gptr = &gg.gbuf[y][x];
|
|
glyph = gptr->glyphinfo.glyph;
|
|
get_bkglyph_and_framecolor(x, y, &bkglyphinfo.glyph,
|
|
&bkglyphinfo.framecolor);
|
|
if (force || glyph != GLYPH_UNEXPLORED
|
|
|| bkglyphinfo.framecolor != NO_COLOR) {
|
|
print_glyph(WIN_MAP, x, y,
|
|
Glyphinfo_at(x, y, glyph), &bkglyphinfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
cls(void)
|
|
{
|
|
static boolean in_cls = 0;
|
|
|
|
if (in_cls)
|
|
return;
|
|
in_cls = TRUE;
|
|
display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
|
|
disp.botlx = TRUE; /* force update of botl window */
|
|
clear_nhwindow(WIN_MAP); /* clear physical screen */
|
|
|
|
clear_glyph_buffer(); /* force gbuf[][].glyph to unexplored */
|
|
in_cls = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Synch the third screen with the display.
|
|
*/
|
|
void
|
|
flush_screen(int cursor_on_u)
|
|
{
|
|
/* Prevent infinite loops on errors:
|
|
* flush_screen->print_glyph->impossible->pline->flush_screen
|
|
*/
|
|
static int flushing = 0;
|
|
static int delay_flushing = 0;
|
|
coordxy x, y;
|
|
glyph_info bkglyphinfo = nul_glyphinfo;
|
|
int bkglyph;
|
|
|
|
/* 3.7: don't update map, status, or perm_invent during save/restore */
|
|
if (_suppress_map_output())
|
|
return;
|
|
|
|
if (cursor_on_u == -1)
|
|
delay_flushing = !delay_flushing;
|
|
if (delay_flushing)
|
|
return;
|
|
if (flushing)
|
|
return; /* if already flushing then return */
|
|
flushing = 1;
|
|
#ifdef HANGUPHANDLING
|
|
if (program_state.done_hup)
|
|
return;
|
|
#endif
|
|
|
|
/* get this done now, before we place the cursor on the hero */
|
|
if (disp.botl || disp.botlx)
|
|
bot();
|
|
else if (disp.time_botl)
|
|
timebot();
|
|
|
|
for (y = 0; y < ROWNO; y++) {
|
|
gbuf_entry *gptr = &gg.gbuf[y][x = gg.gbuf_start[y]];
|
|
|
|
for (; x <= gg.gbuf_stop[y]; gptr++, x++) {
|
|
get_bkglyph_and_framecolor(x, y, &bkglyph,
|
|
&bkglyphinfo.framecolor);
|
|
if (gptr->gnew
|
|
|| (gw.wsettings.map_frame_color != NO_COLOR
|
|
&& bkglyphinfo.framecolor != NO_COLOR)) {
|
|
/* map_glyphinfo() won't touch framecolor */
|
|
map_glyphinfo(x, y, bkglyph, 0, &bkglyphinfo);
|
|
print_glyph(WIN_MAP, x, y,
|
|
Glyphinfo_at(x, y, gptr->glyphinfo.glyph),
|
|
&bkglyphinfo);
|
|
gptr->gnew = 0;
|
|
}
|
|
}
|
|
}
|
|
reset_glyph_bbox();
|
|
|
|
/* after map update, before display_nhwindow(WIN_MAP) */
|
|
if (cursor_on_u)
|
|
curs(WIN_MAP, u.ux, u.uy); /* move cursor to the hero */
|
|
|
|
display_nhwindow(WIN_MAP, FALSE);
|
|
flushing = 0;
|
|
}
|
|
|
|
/* ======================================================================== */
|
|
|
|
/*
|
|
* back_to_glyph()
|
|
*
|
|
* Use the information in the rm structure at the given position to create
|
|
* a glyph of a background.
|
|
*
|
|
* I had to add a field in the rm structure (horizontal) so that we knew
|
|
* if open doors and secret doors were horizontal or vertical. Previously,
|
|
* the screen symbol had the horizontal/vertical information set at
|
|
* level generation time.
|
|
*
|
|
* I used the 'ladder' field (really doormask) for deciding if stairwells
|
|
* were up or down. I didn't want to check the upstairs and dnstairs
|
|
* variables.
|
|
*/
|
|
int
|
|
back_to_glyph(coordxy x, coordxy y)
|
|
{
|
|
int idx, bypass_glyph = NO_GLYPH;
|
|
struct rm *ptr = &(levl[x][y]);
|
|
struct stairway *sway;
|
|
|
|
switch (ptr->typ) {
|
|
case SCORR:
|
|
case STONE:
|
|
idx = svl.level.flags.arboreal ? S_tree : S_stone;
|
|
break;
|
|
case ROOM:
|
|
idx = S_room;
|
|
break;
|
|
case CORR:
|
|
idx = (ptr->waslit || flags.lit_corridor) ? S_litcorr : S_corr;
|
|
break;
|
|
case HWALL:
|
|
case VWALL:
|
|
case TLCORNER:
|
|
case TRCORNER:
|
|
case BLCORNER:
|
|
case BRCORNER:
|
|
case CROSSWALL:
|
|
case TUWALL:
|
|
case TDWALL:
|
|
case TLWALL:
|
|
case TRWALL:
|
|
case SDOOR:
|
|
idx = ptr->seenv ? wall_angle(ptr) : S_stone;
|
|
break;
|
|
case DOOR:
|
|
if (ptr->doormask) {
|
|
if (ptr->doormask & D_BROKEN)
|
|
idx = S_ndoor;
|
|
else if (ptr->doormask & D_ISOPEN)
|
|
idx = (ptr->horizontal) ? S_hodoor : S_vodoor;
|
|
else /* else is closed */
|
|
idx = (ptr->horizontal) ? S_hcdoor : S_vcdoor;
|
|
} else
|
|
idx = S_ndoor;
|
|
break;
|
|
case IRONBARS:
|
|
idx = S_bars;
|
|
break;
|
|
case TREE:
|
|
idx = S_tree;
|
|
break;
|
|
case POOL:
|
|
case MOAT:
|
|
idx = S_pool;
|
|
break;
|
|
case STAIRS:
|
|
sway = stairway_at(x, y);
|
|
if (known_branch_stairs(sway))
|
|
idx = (ptr->ladder & LA_DOWN) ? S_brdnstair : S_brupstair;
|
|
else
|
|
idx = (ptr->ladder & LA_DOWN) ? S_dnstair : S_upstair;
|
|
break;
|
|
case LADDER:
|
|
sway = stairway_at(x, y);
|
|
if (known_branch_stairs(sway))
|
|
idx = (ptr->ladder & LA_DOWN) ? S_brdnladder : S_brupladder;
|
|
else
|
|
idx = (ptr->ladder & LA_DOWN) ? S_dnladder : S_upladder;
|
|
break;
|
|
case FOUNTAIN:
|
|
idx = S_fountain;
|
|
break;
|
|
case SINK:
|
|
idx = S_sink;
|
|
break;
|
|
case ALTAR:
|
|
idx = S_altar; /* not really used */
|
|
bypass_glyph = altar_to_glyph(ptr->altarmask);
|
|
break;
|
|
case GRAVE:
|
|
idx = S_grave;
|
|
break;
|
|
case THRONE:
|
|
idx = S_throne;
|
|
break;
|
|
case LAVAPOOL:
|
|
idx = S_lava;
|
|
break;
|
|
case LAVAWALL:
|
|
idx = S_lavawall;
|
|
break;
|
|
case ICE:
|
|
idx = S_ice;
|
|
break;
|
|
case AIR:
|
|
idx = S_air;
|
|
break;
|
|
case CLOUD:
|
|
idx = S_cloud;
|
|
break;
|
|
case WATER:
|
|
idx = S_water;
|
|
break;
|
|
case DBWALL:
|
|
idx = (ptr->horizontal) ? S_hcdbridge : S_vcdbridge;
|
|
break;
|
|
case DRAWBRIDGE_UP:
|
|
switch (ptr->drawbridgemask & DB_UNDER) {
|
|
case DB_MOAT:
|
|
idx = S_pool;
|
|
break;
|
|
case DB_LAVA:
|
|
idx = S_lava;
|
|
break;
|
|
case DB_ICE:
|
|
idx = S_ice;
|
|
break;
|
|
case DB_FLOOR:
|
|
idx = S_room;
|
|
break;
|
|
default:
|
|
impossible("Strange db-under: %d",
|
|
ptr->drawbridgemask & DB_UNDER);
|
|
idx = S_room; /* something is better than nothing */
|
|
break;
|
|
}
|
|
break;
|
|
case DRAWBRIDGE_DOWN:
|
|
idx = (ptr->horizontal) ? S_hodbridge : S_vodbridge;
|
|
break;
|
|
default:
|
|
impossible("back_to_glyph: unknown level type [ = %d ]", ptr->typ);
|
|
idx = S_room;
|
|
break;
|
|
}
|
|
|
|
return (bypass_glyph != NO_GLYPH) ? bypass_glyph : cmap_to_glyph(idx);
|
|
}
|
|
|
|
/*
|
|
* swallow_to_glyph()
|
|
*
|
|
* Convert a monster number and a swallow location into the correct glyph.
|
|
* If you don't want a patchwork monster while hallucinating, decide on
|
|
* a random monster in swallowed() and don't use what_mon() here.
|
|
*/
|
|
staticfn int
|
|
swallow_to_glyph(int mnum, int loc)
|
|
{
|
|
int m_3 = what_mon(mnum, rn2_on_display_rng) << 3;
|
|
|
|
if (loc < S_sw_tl || S_sw_br < loc) {
|
|
impossible("swallow_to_glyph: bad swallow location");
|
|
loc = S_sw_br;
|
|
}
|
|
return (m_3 | (loc - S_sw_tl)) + GLYPH_SWALLOW_OFF;
|
|
}
|
|
|
|
/*
|
|
* zapdir_to_glyph()
|
|
*
|
|
* Change the given zap direction and beam type into a glyph. Each beam
|
|
* type has four glyphs, one for each of the symbols below. The order of
|
|
* the zap symbols [0-3] as defined in defsym.h are:
|
|
*
|
|
* | S_vbeam ( 0, 1) or ( 0,-1)
|
|
* - S_hbeam ( 1, 0) or (-1, 0)
|
|
* \ S_lslant ( 1, 1) or (-1,-1)
|
|
* / S_rslant (-1, 1) or ( 1,-1)
|
|
*/
|
|
int
|
|
zapdir_to_glyph(int dx, int dy, int beam_type)
|
|
{
|
|
if (beam_type >= NUM_ZAP) {
|
|
impossible("zapdir_to_glyph: illegal beam type");
|
|
beam_type = 0;
|
|
}
|
|
dx = (dx == dy) ? 2 : (dx && dy) ? 3 : dx ? 1 : 0;
|
|
|
|
return ((int) ((beam_type << 2) | dx)) + GLYPH_ZAP_OFF;
|
|
}
|
|
|
|
/*
|
|
* Utility routine for dowhatis() used to find out the glyph displayed at
|
|
* the location. This isn't necessarily the same as the glyph in the levl
|
|
* structure, so we must check the "third screen".
|
|
*/
|
|
int
|
|
glyph_at(coordxy x, coordxy y)
|
|
{
|
|
if (x < 0 || y < 0 || x >= COLNO || y >= ROWNO)
|
|
return cmap_to_glyph(S_room); /* XXX */
|
|
return gg.gbuf[y][x].glyphinfo.glyph; /* _glyph_at(x,y) */
|
|
}
|
|
|
|
#ifdef UNBUFFERED_GLYPHINFO
|
|
glyph_info *
|
|
glyphinfo_at(coordxy x, coordxy y, int glyph)
|
|
{
|
|
map_glyphinfo(x, y, glyph, 0, &ginfo); /* ginfo declared at file scope */
|
|
return &ginfo;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Get the glyph for the background so that it can potentially
|
|
* be merged into graphical window ports to improve the appearance
|
|
* of stuff on dark room squares and the plane of air etc.
|
|
* [This should be using background as recorded for #overview rather
|
|
* than current data from the map.]
|
|
*
|
|
* Also get the frame color to use for highlighting the map location
|
|
* for some purpose.
|
|
*
|
|
*/
|
|
|
|
staticfn void
|
|
get_bkglyph_and_framecolor(
|
|
coordxy x, coordxy y,
|
|
int *bkglyph,
|
|
uint32 *framecolor)
|
|
{
|
|
int idx, tmp_bkglyph = GLYPH_UNEXPLORED;
|
|
struct rm *lev = &levl[x][y];
|
|
|
|
if (iflags.use_background_glyph && lev->seenv != 0
|
|
&& (gg.gbuf[y][x].glyphinfo.glyph != GLYPH_UNEXPLORED)) {
|
|
switch (lev->typ) {
|
|
case SCORR:
|
|
case STONE:
|
|
idx = svl.level.flags.arboreal ? S_tree : S_stone;
|
|
break;
|
|
case ROOM:
|
|
idx = S_room;
|
|
break;
|
|
case CORR:
|
|
idx = (lev->waslit || flags.lit_corridor) ? S_litcorr : S_corr;
|
|
break;
|
|
case ICE:
|
|
idx = S_ice;
|
|
break;
|
|
case AIR:
|
|
idx = S_air;
|
|
break;
|
|
case CLOUD:
|
|
idx = S_cloud;
|
|
break;
|
|
case POOL:
|
|
case MOAT:
|
|
idx = S_pool;
|
|
break;
|
|
case WATER:
|
|
idx = S_water;
|
|
break;
|
|
case LAVAPOOL:
|
|
idx = S_lava;
|
|
break;
|
|
case LAVAWALL:
|
|
idx = S_lavawall;
|
|
break;
|
|
default:
|
|
idx = S_room;
|
|
break;
|
|
}
|
|
|
|
if (!cansee(x, y) && (!lev->waslit || flags.dark_room)) {
|
|
/* Floor spaces and corridors are dark if unlit. */
|
|
if (lev->typ == CORR && idx == S_litcorr)
|
|
idx = S_corr;
|
|
else if (idx == S_room)
|
|
idx = (flags.dark_room && iflags.use_color)
|
|
? DARKROOMSYM : S_stone;
|
|
}
|
|
if (idx != S_room)
|
|
tmp_bkglyph = cmap_to_glyph(idx);
|
|
}
|
|
*bkglyph = tmp_bkglyph;
|
|
#if 0
|
|
/* this guard should be unnecessary */
|
|
if (!framecolor) {
|
|
impossible("null framecolor passed to get_bkglyph_and_framecolor");
|
|
return;
|
|
}
|
|
#endif
|
|
if (iflags.bgcolors
|
|
&& gw.wsettings.map_frame_color != NO_COLOR && mapxy_valid(x, y))
|
|
*framecolor = gw.wsettings.map_frame_color;
|
|
else
|
|
*framecolor = NO_COLOR;
|
|
}
|
|
|
|
#if defined(TILES_IN_GLYPHMAP) && defined(MSDOS)
|
|
#define HAS_ROGUE_IBM_GRAPHICS \
|
|
(gc.currentgraphics == ROGUESET && SYMHANDLING(H_IBM) && !iflags.grmode)
|
|
#else
|
|
#define HAS_ROGUE_IBM_GRAPHICS \
|
|
(gc.currentgraphics == ROGUESET && SYMHANDLING(H_IBM))
|
|
#endif
|
|
|
|
/* masks for per-level variances kept in gg.glyphmap_perlevel_flags */
|
|
#define GMAP_SET 0x0001
|
|
#define GMAP_ROGUELEVEL 0x0002
|
|
|
|
void
|
|
map_glyphinfo(
|
|
coordxy x, coordxy y,
|
|
int glyph,
|
|
unsigned mgflags,
|
|
glyph_info *glyphinfo)
|
|
{
|
|
int offset;
|
|
boolean is_you = (u_at(x, y)
|
|
/* verify hero or steed (not something underneath
|
|
when hero is invisible without see invisible,
|
|
or a temporary display effect like an explosion);
|
|
this is only approximate, because hero might be
|
|
mimicking an object (after eating mimic corpse or
|
|
if polyd into mimic) or furniture (only if polyd) */
|
|
&& glyph_is_monster(glyph));
|
|
const glyph_map *gmap = &glyphmap[glyph];
|
|
|
|
glyphinfo->gm = *gmap; /* glyphflags, sym.symidx, sym.color, tileidx */
|
|
/*
|
|
* Only hero tinkering permitted on-the-fly (who).
|
|
* Unique glyphs in glyphmap[] determine everything else (what).
|
|
*
|
|
* (Note: if hero is invisible without see invisible, he/she usually
|
|
* can't see himself/herself. This applies to accessibility hacks
|
|
* as well as to regular display.)
|
|
*/
|
|
if (is_you) {
|
|
if (!iflags.use_color || Upolyd || glyph != hero_glyph) {
|
|
; /* color tweak not needed (!use_color) or not wanted (poly'd
|
|
or riding--which uses steed's color, not hero's) */
|
|
} else if (HAS_ROGUE_IBM_GRAPHICS
|
|
&& gs.symset[gc.currentgraphics].nocolor == 0) {
|
|
/* actually player should be yellow-on-gray if in corridor */
|
|
glyphinfo->gm.sym.color = CLR_YELLOW;
|
|
} else if (flags.showrace) {
|
|
/* for showrace, non-human hero is displayed by the symbol of
|
|
corresponding type of monster rather than by '@' (handled
|
|
by newsym()); we change the color to same as human hero */
|
|
glyphinfo->gm.sym.color = HI_DOMESTIC;
|
|
}
|
|
/* accessibility
|
|
This unchanging display character for hero was requested by
|
|
a blind player to enhance screen reader use.
|
|
Turn on override symbol if caller hasn't specified NOOVERRIDE. */
|
|
if (sysopt.accessibility == 1 && !(mgflags & MG_FLAG_NOOVERRIDE)) {
|
|
offset = SYM_HERO_OVERRIDE + SYM_OFF_X;
|
|
if ((gg.glyphmap_perlevel_flags & GMAP_ROGUELEVEL)
|
|
? go.ov_rogue_syms[offset]
|
|
: go.ov_primary_syms[offset])
|
|
glyphinfo->gm.sym.symidx = offset;
|
|
}
|
|
glyphinfo->gm.glyphflags |= MG_HERO;
|
|
}
|
|
if (sysopt.accessibility == 1
|
|
&& (mgflags & MG_FLAG_NOOVERRIDE) && glyph_is_pet(glyph)) {
|
|
/* one more accessibility kludge;
|
|
turn off override symbol if caller has specified NOOVERRIDE */
|
|
glyphinfo->gm.sym.symidx = mons[glyph_to_mon(glyph)].mlet + SYM_OFF_M;
|
|
}
|
|
glyphinfo->ttychar = gs.showsyms[glyphinfo->gm.sym.symidx];
|
|
glyphinfo->glyph = glyph;
|
|
}
|
|
|
|
/*
|
|
* This must be the same order as used for buzz() in zap.c.
|
|
* The zap_color_ and altar_color_ enums are in decl.h.
|
|
*/
|
|
const int zapcolors[NUM_ZAP] = {
|
|
zap_color_missile, zap_color_fire, zap_color_frost,
|
|
zap_color_sleep, zap_color_death, zap_color_lightning,
|
|
zap_color_poison_gas, zap_color_acid,
|
|
};
|
|
const int altarcolors[] = {
|
|
altar_color_unaligned, altar_color_chaotic, altar_color_neutral,
|
|
altar_color_lawful, altar_color_other
|
|
};
|
|
const int explodecolors[7] = {
|
|
explode_color_dark, explode_color_noxious, explode_color_muddy,
|
|
explode_color_wet, explode_color_magical, explode_color_fiery,
|
|
explode_color_frosty,
|
|
};
|
|
|
|
/* main_walls, mines_walls, gehennom_walls, knox_walls, sokoban_walls */
|
|
int wallcolors[sokoban_walls + 1] = {
|
|
/* default init value is to match defsym[S_vwall + n].color (CLR_GRAY) */
|
|
CLR_GRAY, CLR_GRAY, CLR_GRAY, CLR_GRAY, CLR_GRAY,
|
|
/* CLR_GRAY, CLR_BROWN, CLR_RED, CLR_GRAY, CLR_BRIGHT_BLUE, */
|
|
};
|
|
|
|
#define zap_color(n) color = iflags.use_color ? zapcolors[n] : NO_COLOR
|
|
#define cmap_color(n) color = iflags.use_color ? defsyms[n].color : NO_COLOR
|
|
#define obj_color(n) color = iflags.use_color ? objects[n].oc_color : NO_COLOR
|
|
#define mon_color(n) color = iflags.use_color ? mons[n].mcolor : NO_COLOR
|
|
#define invis_color(n) color = NO_COLOR
|
|
#define pet_color(n) color = iflags.use_color ? mons[n].mcolor : NO_COLOR
|
|
#define warn_color(n) \
|
|
color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR
|
|
#define explode_color(n) \
|
|
color = iflags.use_color ? explodecolors[n] : NO_COLOR
|
|
#define wall_color(n) color = iflags.use_color ? wallcolors[n] : NO_COLOR
|
|
#define altar_color(n) color = iflags.use_color ? altarcolors[n] : NO_COLOR
|
|
|
|
staticfn int cmap_to_roguecolor(int);
|
|
|
|
staticfn int
|
|
cmap_to_roguecolor(int cmap)
|
|
{
|
|
int color = NO_COLOR;
|
|
|
|
if (gs.symset[gc.currentgraphics].nocolor)
|
|
return NO_COLOR;
|
|
|
|
if (cmap >= S_vwall && cmap <= S_hcdoor)
|
|
color = CLR_BROWN;
|
|
else if (cmap >= S_arrow_trap && cmap <= S_polymorph_trap)
|
|
color = CLR_MAGENTA;
|
|
else if (cmap == S_corr || cmap == S_litcorr)
|
|
color = CLR_GRAY;
|
|
else if (cmap >= S_room && cmap <= S_water
|
|
&& cmap != S_darkroom)
|
|
color = CLR_GREEN;
|
|
else
|
|
color = NO_COLOR;
|
|
|
|
return color;
|
|
}
|
|
|
|
/*
|
|
reset_glyphmap(trigger)
|
|
|
|
The glyphmap likely needs to be re-calculated for the following triggers:
|
|
|
|
gm_levelchange called when the player has gone to a new level.
|
|
|
|
gm_symchange called if someone has interactively altered the symbols
|
|
for the game, most likely at the options menu.
|
|
|
|
gm_optionchange The game settings have been toggled and some of them
|
|
are configured to trigger a reset_glyphmap()
|
|
|
|
gm_accessibility_change One of the accessibility flags has changed.
|
|
|
|
*/
|
|
|
|
void
|
|
reset_glyphmap(enum glyphmap_change_triggers trigger)
|
|
{
|
|
int glyph;
|
|
int offset;
|
|
int color = NO_COLOR;
|
|
|
|
/* condense multiple tests in macro version down to single */
|
|
boolean has_rogue_ibm_graphics = HAS_ROGUE_IBM_GRAPHICS,
|
|
has_rogue_color = (has_rogue_ibm_graphics
|
|
&& gs.symset[gc.currentgraphics].nocolor == 0);
|
|
if (trigger == gm_levelchange)
|
|
gg.glyphmap_perlevel_flags = 0;
|
|
|
|
if (!gg.glyphmap_perlevel_flags) {
|
|
/*
|
|
* GMAP_SET 0x00000001
|
|
* GMAP_ROGUELEVEL 0x00000002
|
|
*/
|
|
gg.glyphmap_perlevel_flags |= GMAP_SET;
|
|
|
|
if (Is_rogue_level(&u.uz)) {
|
|
gg.glyphmap_perlevel_flags |= GMAP_ROGUELEVEL;
|
|
}
|
|
}
|
|
|
|
for (glyph = 0; glyph < MAX_GLYPH; ++glyph) {
|
|
glyph_map *gmap = &glyphmap[glyph];
|
|
|
|
gmap->glyphflags = 0U;
|
|
/*
|
|
* Map the glyph to a character and color.
|
|
*
|
|
* Warning: For speed, this makes an assumption on the order of
|
|
* offsets. The order is set in display.h.
|
|
*/
|
|
if ((offset = (glyph - GLYPH_NOTHING_OFF)) >= 0) {
|
|
gmap->sym.symidx = SYM_NOTHING + SYM_OFF_X;
|
|
color = NO_COLOR;
|
|
gmap->glyphflags |= MG_NOTHING;
|
|
} else if ((offset = (glyph - GLYPH_UNEXPLORED_OFF)) >= 0) {
|
|
gmap->sym.symidx = SYM_UNEXPLORED + SYM_OFF_X;
|
|
color = NO_COLOR;
|
|
gmap->glyphflags |= MG_UNEXPL;
|
|
} else if ((offset = (glyph - GLYPH_STATUE_FEM_PILETOP_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color)
|
|
color = CLR_RED;
|
|
else
|
|
obj_color(STATUE);
|
|
gmap->glyphflags |= (MG_STATUE | MG_FEMALE | MG_OBJPILE);
|
|
} else if ((offset = (glyph - GLYPH_STATUE_MALE_PILETOP_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color)
|
|
color = CLR_RED;
|
|
else
|
|
obj_color(STATUE);
|
|
gmap->glyphflags |= (MG_STATUE | MG_MALE | MG_OBJPILE);
|
|
} else if ((offset = (glyph - GLYPH_BODY_PILETOP_OFF)) >= 0) {
|
|
gmap->sym.symidx = objects[CORPSE].oc_class + SYM_OFF_O;
|
|
if (has_rogue_color)
|
|
color = CLR_RED;
|
|
else
|
|
mon_color(offset);
|
|
gmap->glyphflags |= (MG_CORPSE | MG_OBJPILE);
|
|
} else if ((offset = (glyph - GLYPH_OBJ_PILETOP_OFF)) >= 0) {
|
|
gmap->sym.symidx = objects[offset].oc_class + SYM_OFF_O;
|
|
if (offset == BOULDER)
|
|
gmap->sym.symidx = SYM_BOULDER + SYM_OFF_X;
|
|
if (has_rogue_color) {
|
|
switch (objects[offset].oc_class) {
|
|
case COIN_CLASS:
|
|
color = CLR_YELLOW;
|
|
break;
|
|
case FOOD_CLASS:
|
|
color = CLR_RED;
|
|
break;
|
|
default:
|
|
color = CLR_BRIGHT_BLUE;
|
|
break;
|
|
}
|
|
} else
|
|
obj_color(offset);
|
|
gmap->glyphflags |= MG_OBJPILE;
|
|
} else if ((offset = (glyph - GLYPH_STATUE_FEM_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color)
|
|
color = CLR_RED;
|
|
else
|
|
obj_color(STATUE);
|
|
gmap->glyphflags |= (MG_STATUE | MG_FEMALE);
|
|
} else if ((offset = (glyph - GLYPH_STATUE_MALE_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color)
|
|
color = CLR_RED;
|
|
else
|
|
obj_color(STATUE);
|
|
gmap->glyphflags |= (MG_STATUE | MG_MALE);
|
|
} else if ((offset = (glyph - GLYPH_WARNING_OFF))
|
|
>= 0) { /* warn flash */
|
|
gmap->sym.symidx = offset + SYM_OFF_W;
|
|
if (has_rogue_color)
|
|
color = NO_COLOR;
|
|
else
|
|
warn_color(offset);
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_FROSTY_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P;
|
|
explode_color(expl_frosty);
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_FIERY_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P;
|
|
explode_color(expl_fiery);
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_MAGICAL_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P;
|
|
explode_color(expl_magical);
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_WET_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P;
|
|
explode_color(expl_wet);
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_MUDDY_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P;
|
|
explode_color(expl_muddy);
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_NOXIOUS_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P;
|
|
explode_color(expl_noxious);
|
|
} else if ((offset = (glyph - GLYPH_EXPLODE_DARK_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P;
|
|
explode_color(expl_dark);
|
|
} else if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) {
|
|
/* see swallow_to_glyph() in display.c */
|
|
gmap->sym.symidx = (S_sw_tl + (offset & 0x7)) + SYM_OFF_P;
|
|
if (has_rogue_color)
|
|
color = NO_COLOR;
|
|
else
|
|
mon_color(offset >> 3);
|
|
} else if ((offset = (glyph - GLYPH_CMAP_C_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_digbeam + offset + SYM_OFF_P;
|
|
if (has_rogue_color)
|
|
color = cmap_to_roguecolor(S_digbeam + offset);
|
|
else
|
|
cmap_color(S_digbeam + offset);
|
|
} else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) {
|
|
/* see zapdir_to_glyph() in display.c */
|
|
gmap->sym.symidx = (S_vbeam + (offset & 0x3)) + SYM_OFF_P;
|
|
if (has_rogue_color)
|
|
color = NO_COLOR;
|
|
else
|
|
zap_color((offset >> 2));
|
|
} else if ((offset = (glyph - GLYPH_CMAP_B_OFF)) >= 0) {
|
|
int cmap = S_grave + offset;
|
|
int sym = gs.showsyms[cmap + SYM_OFF_P];
|
|
|
|
gmap->sym.symidx = cmap + SYM_OFF_P;
|
|
cmap_color(cmap);
|
|
if (!iflags.use_color) {
|
|
unsigned spec_cmap = 0;
|
|
|
|
/* try to provide a visible difference between water and lava
|
|
if they use the same symbol and color is disabled;
|
|
similar for floor and ice, for fountain vs sink, and for
|
|
corridor engravings (CMAP_A below) */
|
|
switch (cmap) {
|
|
case S_lava:
|
|
case S_lavawall:
|
|
if (sym == gs.showsyms[S_pool + SYM_OFF_P]
|
|
|| sym == gs.showsyms[S_water + SYM_OFF_P])
|
|
spec_cmap = MG_BW_LAVA;
|
|
break;
|
|
case S_ice:
|
|
if (sym == gs.showsyms[S_room + SYM_OFF_P]
|
|
|| sym == gs.showsyms[S_darkroom + SYM_OFF_P])
|
|
spec_cmap = MG_BW_ICE;
|
|
break;
|
|
case S_sink:
|
|
if (sym == gs.showsyms[S_fountain + SYM_OFF_P])
|
|
spec_cmap = MG_BW_SINK;
|
|
break;
|
|
}
|
|
gmap->glyphflags |= spec_cmap;
|
|
} else if (has_rogue_color) {
|
|
color = cmap_to_roguecolor(cmap);
|
|
}
|
|
} else if ((offset = (glyph - GLYPH_ALTAR_OFF)) >= 0) {
|
|
/* unaligned, chaotic, neutral, lawful, other altar */
|
|
gmap->sym.symidx = S_altar + SYM_OFF_P;
|
|
if (has_rogue_color)
|
|
color = cmap_to_roguecolor(S_altar);
|
|
else
|
|
altar_color(offset);
|
|
} else if ((offset = (glyph - GLYPH_CMAP_A_OFF)) >= 0) {
|
|
int sym, cmap = S_ndoor + offset;
|
|
|
|
gmap->sym.symidx = cmap + SYM_OFF_P;
|
|
cmap_color(cmap);
|
|
sym = gs.showsyms[gmap->sym.symidx];
|
|
/*
|
|
* Some specialty color mappings not hardcoded in data init
|
|
*/
|
|
if (has_rogue_color) {
|
|
color = cmap_to_roguecolor(cmap);
|
|
/* provide a visible difference if normal and lit corridor
|
|
use the same symbol */
|
|
} else if (cmap == S_litcorr
|
|
&& sym == gs.showsyms[S_corr + SYM_OFF_P]) {
|
|
color = CLR_WHITE;
|
|
/* likewise for corridor and engraving-in-corridor */
|
|
} else if (cmap == S_engrcorr
|
|
&& (sym == gs.showsyms[S_corr + SYM_OFF_P]
|
|
|| sym == gs.showsyms[S_litcorr + SYM_OFF_P])) {
|
|
gmap->glyphflags |= MG_BW_ENGR;
|
|
}
|
|
} else if ((offset = (glyph - GLYPH_CMAP_SOKO_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_vwall + offset + SYM_OFF_P;
|
|
wall_color(sokoban_walls);
|
|
} else if ((offset = (glyph - GLYPH_CMAP_KNOX_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_vwall + offset + SYM_OFF_P;
|
|
wall_color(knox_walls);
|
|
} else if ((offset = (glyph - GLYPH_CMAP_GEH_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_vwall + offset + SYM_OFF_P;
|
|
wall_color(gehennom_walls);
|
|
} else if ((offset = (glyph - GLYPH_CMAP_MINES_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_vwall + offset + SYM_OFF_P;
|
|
wall_color(mines_walls);
|
|
} else if ((offset = (glyph - GLYPH_CMAP_MAIN_OFF)) >= 0) {
|
|
gmap->sym.symidx = S_vwall + offset + SYM_OFF_P;
|
|
if (has_rogue_color)
|
|
color = cmap_to_roguecolor(S_vwall + offset);
|
|
else
|
|
wall_color(main_walls);
|
|
} else if ((offset = (glyph - GLYPH_CMAP_STONE_OFF)) >= 0) {
|
|
gmap->sym.symidx = SYM_OFF_P;
|
|
cmap_color(S_stone);
|
|
} else if ((offset = (glyph - GLYPH_OBJ_OFF)) >= 0) {
|
|
gmap->sym.symidx = objects[offset].oc_class + SYM_OFF_O;
|
|
if (offset == BOULDER)
|
|
gmap->sym.symidx = SYM_BOULDER + SYM_OFF_X;
|
|
if (has_rogue_color) {
|
|
switch (objects[offset].oc_class) {
|
|
case COIN_CLASS:
|
|
color = CLR_YELLOW;
|
|
break;
|
|
case FOOD_CLASS:
|
|
color = CLR_RED;
|
|
break;
|
|
default:
|
|
color = CLR_BRIGHT_BLUE;
|
|
break;
|
|
}
|
|
} else
|
|
obj_color(offset);
|
|
} else if ((offset = (glyph - GLYPH_RIDDEN_FEM_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color)
|
|
/* This currently implies that the hero is here -- monsters */
|
|
/* don't ride (yet...). Should we set it to yellow like in */
|
|
/* the monster case below? There is no equivalent in rogue.
|
|
*/
|
|
color = NO_COLOR;
|
|
else
|
|
mon_color(offset);
|
|
gmap->glyphflags |= (MG_RIDDEN | MG_FEMALE);
|
|
} else if ((offset = (glyph - GLYPH_RIDDEN_MALE_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color)
|
|
color = NO_COLOR;
|
|
else
|
|
mon_color(offset);
|
|
gmap->glyphflags |= (MG_RIDDEN | MG_MALE);
|
|
} else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) {
|
|
gmap->sym.symidx = objects[CORPSE].oc_class + SYM_OFF_O;
|
|
if (has_rogue_color)
|
|
color = CLR_RED;
|
|
else
|
|
mon_color(offset);
|
|
gmap->glyphflags |= MG_CORPSE;
|
|
} else if ((offset = (glyph - GLYPH_DETECT_FEM_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color)
|
|
color = NO_COLOR;
|
|
else
|
|
mon_color(offset);
|
|
/* Disabled for now; anyone want to get reverse video to work? */
|
|
/* is_reverse = TRUE; */
|
|
gmap->glyphflags |= (MG_DETECT | MG_FEMALE);
|
|
} else if ((offset = (glyph - GLYPH_DETECT_MALE_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color)
|
|
color = NO_COLOR;
|
|
else
|
|
mon_color(offset);
|
|
/* Disabled for now; anyone want to get reverse video to work? */
|
|
/* is_reverse = TRUE; */
|
|
gmap->glyphflags |= (MG_DETECT | MG_MALE);
|
|
} else if ((offset = (glyph - GLYPH_INVIS_OFF)) >= 0) {
|
|
gmap->sym.symidx = SYM_INVISIBLE + SYM_OFF_X;
|
|
if (has_rogue_color)
|
|
color = NO_COLOR;
|
|
else
|
|
invis_color(offset);
|
|
gmap->glyphflags |= MG_INVIS;
|
|
} else if ((offset = (glyph - GLYPH_PET_FEM_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color)
|
|
color = NO_COLOR;
|
|
else
|
|
pet_color(offset);
|
|
gmap->glyphflags |= (MG_PET | MG_FEMALE);
|
|
} else if ((offset = (glyph - GLYPH_PET_MALE_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color)
|
|
color = NO_COLOR;
|
|
else
|
|
pet_color(offset);
|
|
gmap->glyphflags |= (MG_PET | MG_MALE);
|
|
} else if ((offset = (glyph - GLYPH_MON_FEM_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color) {
|
|
color = NO_COLOR;
|
|
} else {
|
|
mon_color(offset);
|
|
}
|
|
gmap->glyphflags |= MG_FEMALE;
|
|
} else if ((offset = (glyph - GLYPH_MON_MALE_OFF)) >= 0) {
|
|
gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M;
|
|
if (has_rogue_color) {
|
|
color = CLR_YELLOW;
|
|
} else {
|
|
mon_color(offset);
|
|
}
|
|
gmap->glyphflags |= MG_MALE;
|
|
}
|
|
/* This was requested by a blind player to enhance screen reader use
|
|
*/
|
|
if (sysopt.accessibility == 1 && (gmap->glyphflags & MG_PET) != 0) {
|
|
int pet_override = ((gg.glyphmap_perlevel_flags & GMAP_ROGUELEVEL)
|
|
? go.ov_rogue_syms[SYM_PET_OVERRIDE + SYM_OFF_X]
|
|
: go.ov_primary_syms[SYM_PET_OVERRIDE + SYM_OFF_X]);
|
|
|
|
if (gs.showsyms[pet_override] != ' ')
|
|
gmap->sym.symidx = SYM_PET_OVERRIDE + SYM_OFF_X;
|
|
}
|
|
/* Turn off color if no color defined, or rogue level w/o PC graphics.
|
|
*/
|
|
if ((!has_color(color)
|
|
|| ((gg.glyphmap_perlevel_flags & GMAP_ROGUELEVEL)
|
|
&& !has_rogue_color)) || !iflags.use_color)
|
|
color = NO_COLOR;
|
|
gmap->sym.color = color;
|
|
}
|
|
gg.glyph_reset_timestamp = svm.moves;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/* Wall Angle ------------------------------------------------------------- */
|
|
|
|
#ifdef WA_VERBOSE
|
|
|
|
staticfn const char *type_to_name(int);
|
|
staticfn void error4(coordxy, coordxy, int, int, int, int);
|
|
|
|
static int bad_count[MAX_TYPE]; /* count of positions flagged as bad */
|
|
static const char *const type_names[MAX_TYPE] = {
|
|
"STONE", "VWALL", "HWALL", "TLCORNER", "TRCORNER", "BLCORNER", "BRCORNER",
|
|
"CROSSWALL", "TUWALL", "TDWALL", "TLWALL", "TRWALL", "DBWALL", "TREE",
|
|
"SDOOR", "SCORR", "POOL", "MOAT", "WATER", "DRAWBRIDGE_UP", "LAVAPOOL",
|
|
"LAVAWALL",
|
|
"IRON_BARS", "DOOR", "CORR", "ROOM", "STAIRS", "LADDER", "FOUNTAIN",
|
|
"THRONE", "SINK", "GRAVE", "ALTAR", "ICE", "DRAWBRIDGE_DOWN", "AIR",
|
|
"CLOUD"
|
|
};
|
|
|
|
staticfn const char *
|
|
type_to_name(int type)
|
|
{
|
|
return (type < 0 || type >= MAX_TYPE) ? "unknown" : type_names[type];
|
|
}
|
|
|
|
staticfn void
|
|
error4(coordxy x, coordxy y, int a, int b, int c, int dd)
|
|
{
|
|
pline("set_wall_state: %s @ (%d,%d) %s%s%s%s",
|
|
type_to_name(levl[x][y].typ), x, y,
|
|
a ? "1" : "", b ? "2" : "", c ? "3" : "", dd ? "4" : "");
|
|
bad_count[levl[x][y].typ]++;
|
|
}
|
|
#endif /* WA_VERBOSE */
|
|
|
|
/*
|
|
* Return 'which' if position is implies an unfinished exterior. Return
|
|
* zero otherwise. Unfinished implies outer area is rock or a corridor.
|
|
*
|
|
* Things that are ambiguous: lava
|
|
*/
|
|
staticfn int
|
|
check_pos(coordxy x, coordxy y, int which)
|
|
{
|
|
int type;
|
|
|
|
if (!isok(x, y))
|
|
return which;
|
|
type = levl[x][y].typ;
|
|
/* Everything below POOL, excluding TREE */
|
|
if (IS_STWALL(type) || type == CORR || type == SCORR || IS_SDOOR(type))
|
|
return which;
|
|
return 0;
|
|
}
|
|
|
|
/* Return TRUE if more than one is non-zero. */
|
|
/*ARGSUSED*/
|
|
#ifdef WA_VERBOSE
|
|
staticfn boolean
|
|
more_than_one(coordxy x, coordxy y, coordxy a, coordxy b, coordxy c)
|
|
{
|
|
if ((a && (b | c)) || (b && (a | c)) || (c && (a | b))) {
|
|
error4(x, y, a, b, c, 0);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
#else
|
|
#define more_than_one(x, y, a, b, c) \
|
|
(((a) && ((b) | (c))) || ((b) && ((a) | (c))) || ((c) && ((a) | (b))))
|
|
#endif
|
|
|
|
/* Return the wall mode for a T wall. */
|
|
staticfn int
|
|
set_twall(
|
|
#ifdef WA_VERBOSE
|
|
coordxy x0, coordxy y0, /* used #if WA_VERBOSE */
|
|
#else
|
|
coordxy x0 UNUSED, coordxy y0 UNUSED,
|
|
#endif
|
|
coordxy x1, coordxy y1,
|
|
coordxy x2, coordxy y2,
|
|
coordxy x3, coordxy y3)
|
|
{
|
|
int wmode, is_1, is_2, is_3;
|
|
|
|
is_1 = check_pos(x1, y1, WM_T_LONG);
|
|
is_2 = check_pos(x2, y2, WM_T_BL);
|
|
is_3 = check_pos(x3, y3, WM_T_BR);
|
|
if (more_than_one(x0, y0, is_1, is_2, is_3)) {
|
|
wmode = 0;
|
|
} else {
|
|
wmode = is_1 + is_2 + is_3;
|
|
}
|
|
return wmode;
|
|
}
|
|
|
|
/* Return wall mode for a horizontal or vertical wall. */
|
|
staticfn int
|
|
set_wall(coordxy x, coordxy y, int horiz)
|
|
{
|
|
int wmode, is_1, is_2;
|
|
|
|
if (horiz) {
|
|
is_1 = check_pos(x, y - 1, WM_W_TOP);
|
|
is_2 = check_pos(x, y + 1, WM_W_BOTTOM);
|
|
} else {
|
|
is_1 = check_pos(x - 1, y, WM_W_LEFT);
|
|
is_2 = check_pos(x + 1, y, WM_W_RIGHT);
|
|
}
|
|
if (more_than_one(x, y, is_1, is_2, 0)) {
|
|
wmode = 0;
|
|
} else {
|
|
wmode = is_1 + is_2;
|
|
}
|
|
return wmode;
|
|
}
|
|
|
|
/* Return a wall mode for a corner wall. (x4,y4) is the "inner" position. */
|
|
staticfn int
|
|
set_corn(
|
|
coordxy x1, coordxy y1,
|
|
coordxy x2, coordxy y2,
|
|
coordxy x3, coordxy y3,
|
|
coordxy x4, coordxy y4)
|
|
{
|
|
coordxy wmode, is_1, is_2, is_3, is_4;
|
|
|
|
is_1 = check_pos(x1, y1, 1);
|
|
is_2 = check_pos(x2, y2, 1);
|
|
is_3 = check_pos(x3, y3, 1);
|
|
is_4 = check_pos(x4, y4, 1); /* inner location */
|
|
|
|
/*
|
|
* All 4 should not be true. So if the inner location is rock,
|
|
* use it. If all of the outer 3 are true, use outer. We currently
|
|
* can't cover the case where only part of the outer is rock, so
|
|
* we just say that all the walls are finished (if not overridden
|
|
* by the inner section).
|
|
*/
|
|
if (is_4) {
|
|
wmode = WM_C_INNER;
|
|
} else if (is_1 && is_2 && is_3)
|
|
wmode = WM_C_OUTER;
|
|
else
|
|
wmode = 0; /* finished walls on all sides */
|
|
|
|
return wmode;
|
|
}
|
|
|
|
/* Return mode for a crosswall. */
|
|
staticfn int
|
|
set_crosswall(coordxy x, coordxy y)
|
|
{
|
|
coordxy wmode, is_1, is_2, is_3, is_4;
|
|
|
|
is_1 = check_pos(x - 1, y - 1, 1);
|
|
is_2 = check_pos(x + 1, y - 1, 1);
|
|
is_3 = check_pos(x + 1, y + 1, 1);
|
|
is_4 = check_pos(x - 1, y + 1, 1);
|
|
|
|
wmode = is_1 + is_2 + is_3 + is_4;
|
|
if (wmode > 1) {
|
|
if (is_1 && is_3 && (is_2 + is_4 == 0)) {
|
|
wmode = WM_X_TLBR;
|
|
} else if (is_2 && is_4 && (is_1 + is_3 == 0)) {
|
|
wmode = WM_X_BLTR;
|
|
} else {
|
|
#ifdef WA_VERBOSE
|
|
error4(x, y, is_1, is_2, is_3, is_4);
|
|
#endif
|
|
wmode = 0;
|
|
}
|
|
} else if (is_1)
|
|
wmode = WM_X_TL;
|
|
else if (is_2)
|
|
wmode = WM_X_TR;
|
|
else if (is_3)
|
|
wmode = WM_X_BR;
|
|
else if (is_4)
|
|
wmode = WM_X_BL;
|
|
|
|
return wmode;
|
|
}
|
|
|
|
/* called for every <x,y> by set_wall_state() and for specific <x,y> during
|
|
vault wall repair */
|
|
void
|
|
xy_set_wall_state(coordxy x, coordxy y)
|
|
{
|
|
coordxy wmode;
|
|
struct rm *lev = &levl[x][y];
|
|
|
|
switch (lev->typ) {
|
|
case SDOOR:
|
|
wmode = set_wall(x, y, (coordxy) lev->horizontal);
|
|
break;
|
|
case VWALL:
|
|
wmode = set_wall(x, y, 0);
|
|
break;
|
|
case HWALL:
|
|
wmode = set_wall(x, y, 1);
|
|
break;
|
|
case TDWALL:
|
|
wmode = set_twall(x, y, x, y - 1, x - 1, y + 1, x + 1, y + 1);
|
|
break;
|
|
case TUWALL:
|
|
wmode = set_twall(x, y, x, y + 1, x + 1, y - 1, x - 1, y - 1);
|
|
break;
|
|
case TLWALL:
|
|
wmode = set_twall(x, y, x + 1, y, x - 1, y - 1, x - 1, y + 1);
|
|
break;
|
|
case TRWALL:
|
|
wmode = set_twall(x, y, x - 1, y, x + 1, y + 1, x + 1, y - 1);
|
|
break;
|
|
case TLCORNER:
|
|
wmode = set_corn(x - 1, y - 1, x, y - 1, x - 1, y, x + 1, y + 1);
|
|
break;
|
|
case TRCORNER:
|
|
wmode = set_corn(x, y - 1, x + 1, y - 1, x + 1, y, x - 1, y + 1);
|
|
break;
|
|
case BLCORNER:
|
|
wmode = set_corn(x, y + 1, x - 1, y + 1, x - 1, y, x + 1, y - 1);
|
|
break;
|
|
case BRCORNER:
|
|
wmode = set_corn(x + 1, y, x + 1, y + 1, x, y + 1, x - 1, y - 1);
|
|
break;
|
|
case CROSSWALL:
|
|
wmode = set_crosswall(x, y);
|
|
break;
|
|
|
|
default:
|
|
wmode = -1; /* don't set wall info */
|
|
break;
|
|
}
|
|
|
|
if (wmode >= 0)
|
|
lev->wall_info = (lev->wall_info & ~WM_MASK) | wmode;
|
|
}
|
|
|
|
/* Called from mklev. Scan the level and set the wall modes. */
|
|
void
|
|
set_wall_state(void)
|
|
{
|
|
coordxy x, y;
|
|
|
|
#ifdef WA_VERBOSE
|
|
for (x = 0; x < MAX_TYPE; x++)
|
|
bad_count[x] = 0;
|
|
#endif
|
|
|
|
for (x = 0; x < COLNO; x++)
|
|
for (y = 0; y < ROWNO; y++)
|
|
xy_set_wall_state(x, y);
|
|
|
|
#ifdef WA_VERBOSE
|
|
/* check if any bad positions found */
|
|
for (x = y = 0; x < MAX_TYPE; x++)
|
|
if (bad_count[x]) {
|
|
if (y == 0) {
|
|
y = 1; /* only prcoordxy once */
|
|
pline("set_wall_type: wall mode problems with: ");
|
|
}
|
|
pline("%s %d;", type_names[x], bad_count[x]);
|
|
}
|
|
#endif /* WA_VERBOSE */
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
/* This matrix is used here and in vision.c. */
|
|
const seenV seenv_matrix[3][3] = {
|
|
{ SV2, SV1, SV0 },
|
|
{ SV3, SVALL, SV7 },
|
|
{ SV4, SV5, SV6 }
|
|
};
|
|
|
|
/* negative: -1, zero: 0, positive +1; same as sgn() function (hacklib.c) */
|
|
#define sign(z) ((z) < 0 ? -1 : ((z) != 0))
|
|
|
|
/* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */
|
|
staticfn void
|
|
set_seenv(
|
|
struct rm *lev,
|
|
coordxy x0, coordxy y0, /* from */
|
|
coordxy x, coordxy y) /* to */
|
|
{
|
|
coordxy dx = x - x0, dy = y0 - y;
|
|
|
|
lev->seenv |= seenv_matrix[sign(dy) + 1][sign(dx) + 1];
|
|
}
|
|
|
|
#undef sign
|
|
|
|
/* Called by blackout(vault.c) when vault guard removes temporary corridor,
|
|
turning spot <x0,y0> back into stone; <x1,y1> is an adjacent spot. */
|
|
void
|
|
unset_seenv(
|
|
struct rm *lev, /* &levl[x1][y1] */
|
|
coordxy x0, coordxy y0, /* from */
|
|
coordxy x1, coordxy y1) /* to; abs(x1-x0)==1 && abs(y0-y1)==1 */
|
|
|
|
{
|
|
coordxy dx = x1 - x0, dy = y0 - y1;
|
|
|
|
lev->seenv &= ~seenv_matrix[dy + 1][dx + 1];
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
/* T wall types, one for each row in wall_matrix[][]. */
|
|
#define T_d 0
|
|
#define T_l 1
|
|
#define T_u 2
|
|
#define T_r 3
|
|
|
|
/*
|
|
* These are the column names of wall_matrix[][]. They are the "results"
|
|
* of a tdwall pattern match. All T walls are rotated so they become
|
|
* a tdwall. Then we do a single pattern match, but return the
|
|
* correct result for the original wall by using different rows for
|
|
* each of the wall types.
|
|
*/
|
|
#define T_stone 0
|
|
#define T_tlcorn 1
|
|
#define T_trcorn 2
|
|
#define T_hwall 3
|
|
#define T_tdwall 4
|
|
|
|
static const int wall_matrix[4][5] = {
|
|
{ S_stone, S_tlcorn, S_trcorn, S_hwall, S_tdwall }, /* tdwall */
|
|
{ S_stone, S_trcorn, S_brcorn, S_vwall, S_tlwall }, /* tlwall */
|
|
{ S_stone, S_brcorn, S_blcorn, S_hwall, S_tuwall }, /* tuwall */
|
|
{ S_stone, S_blcorn, S_tlcorn, S_vwall, S_trwall }, /* trwall */
|
|
};
|
|
|
|
/* Cross wall types, one for each "solid" quarter. Rows of cross_matrix[][].
|
|
*/
|
|
#define C_bl 0
|
|
#define C_tl 1
|
|
#define C_tr 2
|
|
#define C_br 3
|
|
|
|
/*
|
|
* These are the column names for cross_matrix[][]. They express results
|
|
* in C_br (bottom right) terms. All crosswalls with a single solid
|
|
* quarter are rotated so the solid section is at the bottom right.
|
|
* We pattern match on that, but return the correct result depending
|
|
* on which row we're looking at.
|
|
*/
|
|
#define C_trcorn 0
|
|
#define C_brcorn 1
|
|
#define C_blcorn 2
|
|
#define C_tlwall 3
|
|
#define C_tuwall 4
|
|
#define C_crwall 5
|
|
|
|
static const int cross_matrix[4][6] = {
|
|
{ S_brcorn, S_blcorn, S_tlcorn, S_tuwall, S_trwall, S_crwall },
|
|
{ S_blcorn, S_tlcorn, S_trcorn, S_trwall, S_tdwall, S_crwall },
|
|
{ S_tlcorn, S_trcorn, S_brcorn, S_tdwall, S_tlwall, S_crwall },
|
|
{ S_trcorn, S_brcorn, S_blcorn, S_tlwall, S_tuwall, S_crwall },
|
|
};
|
|
|
|
/* Print out a T wall warning and all interesting info. */
|
|
staticfn void
|
|
t_warn(struct rm *lev)
|
|
{
|
|
static const char warn_str[] = "wall_angle: %s: case %d: seenv = 0x%x";
|
|
const char *wname;
|
|
|
|
/* 3.7: non-T_wall cases added after shop repair (via breaching a wall,
|
|
using locking magic to put a door there, then unlocking the door;
|
|
D_CLOSED carried over to the wall) triggered warning for "unknown" */
|
|
switch (lev->typ) {
|
|
case TUWALL:
|
|
wname = "tuwall";
|
|
break;
|
|
case TLWALL:
|
|
wname = "tlwall";
|
|
break;
|
|
case TRWALL:
|
|
wname = "trwall";
|
|
break;
|
|
case TDWALL:
|
|
wname = "tdwall";
|
|
break;
|
|
case VWALL:
|
|
wname = "vwall";
|
|
break;
|
|
case HWALL:
|
|
wname = "hwall";
|
|
break;
|
|
case TLCORNER:
|
|
wname = "tlcorner";
|
|
break;
|
|
case TRCORNER:
|
|
wname = "trcorner";
|
|
break;
|
|
case BLCORNER:
|
|
wname = "blcorner";
|
|
break;
|
|
case BRCORNER:
|
|
wname = "brcorner";
|
|
break;
|
|
default:
|
|
wname = "unknown";
|
|
break;
|
|
}
|
|
impossible(warn_str, wname, lev->wall_info & WM_MASK,
|
|
(unsigned int) lev->seenv);
|
|
}
|
|
|
|
/*
|
|
* Return the correct graphics character index using wall type, wall mode,
|
|
* and the seen vector. It is expected that seenv is non zero.
|
|
*
|
|
* All T-wall vectors are rotated to be TDWALL. All single crosswall
|
|
* blocks are rotated to bottom right. All double crosswall are rotated
|
|
* to W_X_BLTR. All results are converted back.
|
|
*
|
|
* The only way to understand this is to take out pen and paper and
|
|
* draw diagrams. See rm.h for more details on the wall modes and
|
|
* seen vector (SV).
|
|
*/
|
|
staticfn int
|
|
wall_angle(struct rm *lev)
|
|
{
|
|
unsigned int seenv = lev->seenv & 0xff;
|
|
const int *row;
|
|
int col, idx;
|
|
|
|
#define only(sv, bits) (((sv) & (bits)) && !((sv) & ~(bits)))
|
|
switch (lev->typ) {
|
|
case TUWALL:
|
|
row = wall_matrix[T_u];
|
|
seenv = (seenv >> 4 | seenv << 4) & 0xff; /* rotate to tdwall */
|
|
goto do_twall;
|
|
case TLWALL:
|
|
row = wall_matrix[T_l];
|
|
seenv = (seenv >> 2 | seenv << 6) & 0xff; /* rotate to tdwall */
|
|
goto do_twall;
|
|
case TRWALL:
|
|
row = wall_matrix[T_r];
|
|
seenv = (seenv >> 6 | seenv << 2) & 0xff; /* rotate to tdwall */
|
|
goto do_twall;
|
|
case TDWALL:
|
|
row = wall_matrix[T_d];
|
|
do_twall:
|
|
switch (lev->wall_info & WM_MASK) {
|
|
case 0:
|
|
if (seenv == SV4) {
|
|
col = T_tlcorn;
|
|
} else if (seenv == SV6) {
|
|
col = T_trcorn;
|
|
} else if (seenv & (SV3 | SV5 | SV7)
|
|
|| ((seenv & SV4) && (seenv & SV6))) {
|
|
col = T_tdwall;
|
|
} else if (seenv & (SV0 | SV1 | SV2)) {
|
|
col = (seenv & (SV4 | SV6) ? T_tdwall : T_hwall);
|
|
} else {
|
|
t_warn(lev);
|
|
col = T_stone;
|
|
}
|
|
break;
|
|
case WM_T_LONG:
|
|
if (seenv & (SV3 | SV4) && !(seenv & (SV5 | SV6 | SV7))) {
|
|
col = T_tlcorn;
|
|
} else if (seenv & (SV6 | SV7) && !(seenv & (SV3 | SV4 | SV5))) {
|
|
col = T_trcorn;
|
|
} else if ((seenv & SV5)
|
|
|| ((seenv & (SV3 | SV4)) && (seenv & (SV6 | SV7)))) {
|
|
col = T_tdwall;
|
|
} else {
|
|
/* only SV0|SV1|SV2 */
|
|
if (!only(seenv, SV0 | SV1 | SV2))
|
|
t_warn(lev);
|
|
col = T_stone;
|
|
}
|
|
break;
|
|
case WM_T_BL:
|
|
if (only(seenv, SV4 | SV5))
|
|
col = T_tlcorn;
|
|
else if ((seenv & (SV0 | SV1 | SV2 | SV7))
|
|
&& !(seenv & (SV3 | SV4 | SV5)))
|
|
col = T_hwall;
|
|
else if (only(seenv, SV6))
|
|
col = T_stone;
|
|
else
|
|
col = T_tdwall;
|
|
break;
|
|
case WM_T_BR:
|
|
if (only(seenv, SV5 | SV6))
|
|
col = T_trcorn;
|
|
else if ((seenv & (SV0 | SV1 | SV2 | SV3))
|
|
&& !(seenv & (SV5 | SV6 | SV7)))
|
|
col = T_hwall;
|
|
else if (only(seenv, SV4))
|
|
col = T_stone;
|
|
else
|
|
col = T_tdwall;
|
|
|
|
break;
|
|
default:
|
|
impossible("wall_angle: unknown T wall mode %d",
|
|
lev->wall_info & WM_MASK);
|
|
col = T_stone;
|
|
break;
|
|
}
|
|
idx = row[col];
|
|
break;
|
|
|
|
case SDOOR:
|
|
if (lev->horizontal)
|
|
goto horiz;
|
|
FALLTHROUGH;
|
|
/*FALLTHRU*/
|
|
case VWALL:
|
|
switch (lev->wall_info & WM_MASK) {
|
|
case 0:
|
|
idx = seenv ? S_vwall : S_stone;
|
|
break;
|
|
case 1:
|
|
idx = seenv & (SV1 | SV2 | SV3 | SV4 | SV5) ? S_vwall : S_stone;
|
|
break;
|
|
case 2:
|
|
idx = seenv & (SV0 | SV1 | SV5 | SV6 | SV7) ? S_vwall : S_stone;
|
|
break;
|
|
default:
|
|
impossible("wall_angle: unknown vwall mode %d",
|
|
lev->wall_info & WM_MASK);
|
|
idx = S_stone;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case HWALL:
|
|
horiz:
|
|
switch (lev->wall_info & WM_MASK) {
|
|
case 0:
|
|
idx = seenv ? S_hwall : S_stone;
|
|
break;
|
|
case 1:
|
|
idx = seenv & (SV3 | SV4 | SV5 | SV6 | SV7) ? S_hwall : S_stone;
|
|
break;
|
|
case 2:
|
|
idx = seenv & (SV0 | SV1 | SV2 | SV3 | SV7) ? S_hwall : S_stone;
|
|
break;
|
|
default:
|
|
impossible("wall_angle: unknown hwall mode %d",
|
|
lev->wall_info & WM_MASK);
|
|
idx = S_stone;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
#define set_corner(idx, lev, which, outer, inner, name) \
|
|
switch ((lev)->wall_info & WM_MASK) { \
|
|
case 0: \
|
|
idx = which; \
|
|
break; \
|
|
case WM_C_OUTER: \
|
|
idx = seenv & (outer) ? which : S_stone; \
|
|
break; \
|
|
case WM_C_INNER: \
|
|
idx = seenv & ~(inner) ? which : S_stone; \
|
|
break; \
|
|
default: \
|
|
impossible("wall_angle: unknown %s mode %d", name, \
|
|
(lev)->wall_info &WM_MASK); \
|
|
idx = S_stone; \
|
|
break; \
|
|
}
|
|
|
|
case TLCORNER:
|
|
set_corner(idx, lev, S_tlcorn, (SV3 | SV4 | SV5), SV4, "tlcorn");
|
|
break;
|
|
case TRCORNER:
|
|
set_corner(idx, lev, S_trcorn, (SV5 | SV6 | SV7), SV6, "trcorn");
|
|
break;
|
|
case BLCORNER:
|
|
set_corner(idx, lev, S_blcorn, (SV1 | SV2 | SV3), SV2, "blcorn");
|
|
break;
|
|
case BRCORNER:
|
|
set_corner(idx, lev, S_brcorn, (SV7 | SV0 | SV1), SV0, "brcorn");
|
|
break;
|
|
|
|
case CROSSWALL:
|
|
switch (lev->wall_info & WM_MASK) {
|
|
case 0:
|
|
if (seenv == SV0)
|
|
idx = S_brcorn;
|
|
else if (seenv == SV2)
|
|
idx = S_blcorn;
|
|
else if (seenv == SV4)
|
|
idx = S_tlcorn;
|
|
else if (seenv == SV6)
|
|
idx = S_trcorn;
|
|
else if (!(seenv & ~(SV0 | SV1 | SV2))
|
|
&& (seenv & SV1 || seenv == (SV0 | SV2)))
|
|
idx = S_tuwall;
|
|
else if (!(seenv & ~(SV2 | SV3 | SV4))
|
|
&& (seenv & SV3 || seenv == (SV2 | SV4)))
|
|
idx = S_trwall;
|
|
else if (!(seenv & ~(SV4 | SV5 | SV6))
|
|
&& (seenv & SV5 || seenv == (SV4 | SV6)))
|
|
idx = S_tdwall;
|
|
else if (!(seenv & ~(SV0 | SV6 | SV7))
|
|
&& (seenv & SV7 || seenv == (SV0 | SV6)))
|
|
idx = S_tlwall;
|
|
else
|
|
idx = S_crwall;
|
|
break;
|
|
|
|
case WM_X_TL:
|
|
row = cross_matrix[C_tl];
|
|
seenv = (seenv >> 4 | seenv << 4) & 0xff;
|
|
goto do_crwall;
|
|
case WM_X_TR:
|
|
row = cross_matrix[C_tr];
|
|
seenv = (seenv >> 6 | seenv << 2) & 0xff;
|
|
goto do_crwall;
|
|
case WM_X_BL:
|
|
row = cross_matrix[C_bl];
|
|
seenv = (seenv >> 2 | seenv << 6) & 0xff;
|
|
goto do_crwall;
|
|
case WM_X_BR:
|
|
row = cross_matrix[C_br];
|
|
do_crwall:
|
|
if (seenv == SV4) {
|
|
idx = S_stone;
|
|
} else {
|
|
seenv = seenv & ~SV4; /* strip SV4 */
|
|
if (seenv == SV0) {
|
|
col = C_brcorn;
|
|
} else if (seenv & (SV2 | SV3)) {
|
|
if (seenv & (SV5 | SV6 | SV7))
|
|
col = C_crwall;
|
|
else if (seenv & (SV0 | SV1))
|
|
col = C_tuwall;
|
|
else
|
|
col = C_blcorn;
|
|
} else if (seenv & (SV5 | SV6)) {
|
|
if (seenv & (SV1 | SV2 | SV3))
|
|
col = C_crwall;
|
|
else if (seenv & (SV0 | SV7))
|
|
col = C_tlwall;
|
|
else
|
|
col = C_trcorn;
|
|
} else if (seenv & SV1) {
|
|
col = seenv & SV7 ? C_crwall : C_tuwall;
|
|
} else if (seenv & SV7) {
|
|
col = seenv & SV1 ? C_crwall : C_tlwall;
|
|
} else {
|
|
impossible("wall_angle: bottom of crwall check");
|
|
col = C_crwall;
|
|
}
|
|
|
|
idx = row[col];
|
|
}
|
|
break;
|
|
|
|
case WM_X_TLBR:
|
|
if (only(seenv, SV1 | SV2 | SV3))
|
|
idx = S_blcorn;
|
|
else if (only(seenv, SV5 | SV6 | SV7))
|
|
idx = S_trcorn;
|
|
else if (only(seenv, SV0 | SV4))
|
|
idx = S_stone;
|
|
else
|
|
idx = S_crwall;
|
|
break;
|
|
|
|
case WM_X_BLTR:
|
|
if (only(seenv, SV0 | SV1 | SV7))
|
|
idx = S_brcorn;
|
|
else if (only(seenv, SV3 | SV4 | SV5))
|
|
idx = S_tlcorn;
|
|
else if (only(seenv, SV2 | SV6))
|
|
idx = S_stone;
|
|
else
|
|
idx = S_crwall;
|
|
break;
|
|
|
|
default:
|
|
impossible("wall_angle: unknown crosswall mode");
|
|
idx = S_stone;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
impossible("wall_angle: unexpected wall type %d", lev->typ);
|
|
idx = S_stone;
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
/*
|
|
* c++ 20 has problems with some of the display.h macros because
|
|
* comparisons and bit-fiddling and math between different enums
|
|
* is deprecated.
|
|
* Create function versions of some of the macros used in some
|
|
* NetHack c++ source files (Qt) for use there.
|
|
*/
|
|
int
|
|
fn_cmap_to_glyph(int cmap)
|
|
{
|
|
return cmap_to_glyph(cmap);
|
|
}
|
|
|
|
/* for 'onefile' processing where end of this file isn't necessarily the
|
|
end of the source code seen by the compiler (there are lots of other
|
|
macros defined above...) */
|
|
#undef _glyph_at
|
|
#undef DETECTED
|
|
#undef PHYSICALLY_SEEN
|
|
#undef is_worm_tail
|
|
#undef _suppress_map_output
|
|
#undef TMP_AT_MAX_GLYPHS
|
|
#undef Glyphinfo_at
|
|
#undef reset_glyph_bbox
|
|
#undef HAS_ROGUE_IBM_GRAPHICS
|
|
#undef GMAP_SET
|
|
#undef GMAP_ROGUELEVEL
|
|
#ifndef WA_VERBOSE
|
|
#undef more_than_one
|
|
#endif
|
|
#undef only
|
|
#undef set_corner
|
|
|
|
/*display.c*/
|