Logo Search packages:      
Sourcecode: falconseye version File versions  Download package

display.c

/*    SCCS Id: @(#)display.c  3.3   2000/07/27  */
/* 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.doc 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 (telepathy)
 *          memory
 *
 *
 *
 * 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
 * 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 a 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"
#include "region.h"

STATIC_DCL void FDECL(display_monster,(XCHAR_P,XCHAR_P,struct monst *,int,XCHAR_P));
STATIC_DCL int FDECL(swallow_to_glyph, (int, int));
STATIC_DCL void FDECL(display_warning,(struct monst *));

STATIC_DCL int FDECL(check_pos, (int, int, int));
#ifdef WA_VERBOSE
STATIC_DCL boolean FDECL(more_than_one, (int, int, int, int, int));
#endif
STATIC_DCL int FDECL(set_twall, (int,int, int,int, int,int, int,int));
STATIC_DCL int FDECL(set_wall, (int, int, int));
STATIC_DCL int FDECL(set_corn, (int,int, int,int, int,int, int,int));
STATIC_DCL int FDECL(set_crosswall, (int, int));
STATIC_DCL void FDECL(set_seenv, (struct rm *, int, int, int, int));
STATIC_DCL void FDECL(t_warn, (struct rm *));
STATIC_DCL int FDECL(wall_angle, (struct rm *));

#ifdef INVISIBLE_OBJECTS
/*
 * vobj_at()
 *
 * Returns a pointer to an object if the hero can see an object at the
 * given location.  This takes care of invisible objects.  NOTE, this
 * assumes that the hero is not blind and on top of the object pile.
 * It does NOT take into account that the location is out of sight, or,
 * say, one can see blessed, etc.
 */
struct obj *
vobj_at(x,y)
    xchar x,y;
{
    register struct obj *obj = level.objects[x][y];

    while (obj) {
      if (!obj->oinvis || See_invisible) return obj;
      obj = obj->nexthere;
    }
    return ((struct obj *) 0);
}
#endif      /* else vobj_at() is defined in display.h */

/*
 * 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(x, y, show)
    xchar x,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 = cmap_to_glyph(S_stone);
      else if (lev->typ == CORR && glyph == cmap_to_glyph(S_litcorr))
          glyph = cmap_to_glyph(S_corr);
    }
    if (level.flags.hero_memory)
      lev->glyph = glyph;
    if (show) show_glyph(x,y, glyph);
}

/*
 * 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.
 */
/* FIXME: some of these use xchars for x and y, and some use ints.  Make
 * this consistent.
 */

/*
 * 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(x, y, show)
    register xchar x,y;
    register int  show;
{
    register int glyph = back_to_glyph(x,y);

    if (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(trap, show)
    register struct trap *trap;
    register int   show;
{
    register int x = trap->tx, y = trap->ty;
    register int glyph = trap_to_glyph(trap);

    if (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.
 */
void
map_object(obj, show)
    register struct obj *obj;
    register int  show;
{
    register int x = obj->ox, y = obj->oy;
    register int glyph = obj_to_glyph(obj);

    if (level.flags.hero_memory)
      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(x, y)
register xchar x, y;
{
    if (level.flags.hero_memory)
      levl[x][y].glyph = GLYPH_INVISIBLE;
    show_glyph(x, y, GLYPH_INVISIBLE);
}

/*
 * unmap_object()
 *
 * Remove something from the map when the hero realizes it's not there any
 * more.  Replace it with background or known trap, but not with any other
 * 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(x, y)
    register int x, y;
{
    register struct trap *trap;

    if (!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];

      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.
 */
#ifdef JTP_GRAPHICS
#define _map_location(x,y,show)                                   \
{                                                     \
    register struct obj   *obj;                                   \
    register struct trap  *trap;                            \
                                                      \
    map_background(x,y,show);                         \
    if ((trap = t_at(x,y)) && trap->tseen && !covers_traps(x,y))  \
      map_trap(trap,show);                                  \
    if ((obj = vobj_at(x,y)) && !covers_objects(x,y))             \
      map_object(obj,show);                                 \
}
#else
#define _map_location(x,y,show)                                   \
{                                                     \
    register struct obj   *obj;                                   \
    register struct trap  *trap;                            \
                                                      \
    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                                              \
      map_background(x,y,show);                             \
}
#endif

void map_location(x,y,show)
    int x, y, show;
{
    _map_location(x,y,show);
}


/*
 * 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.
 *
 */
STATIC_OVL void
display_monster(x, y, mon, in_sight, worm_tail)
    register xchar x, y;      /* display position */
    register struct monst *mon;     /* monster to display */
    int in_sight;       /* TRUE if the monster is physically seen */
    register xchar worm_tail; /* mon is actually a worm tail */
{
    register boolean mon_mimic = (mon->m_ap_type != M_AP_NOTHING);
    register int sensed = mon_mimic &&
      (Protection_from_shape_changers || sensemon(mon));
    /*
     * We must do the mimic check first.  If the mimic is mimicing 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 mimicing.
     */

    if (mon_mimic && in_sight) {
      switch (mon->m_ap_type) {
          default:
            impossible("display_monster:  bad m_ap_type value [ = %d ]",
                                          (int) mon->m_ap_type);
          case M_AP_NOTHING:
            show_glyph(x, y, mon_to_glyph(mon));
            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.
             */
            register int glyph = cmap_to_glyph(mon->mappearance);
            levl[x][y].glyph = glyph;
            if (!sensed) show_glyph(x,y, glyph);
            break;
          }

          case M_AP_OBJECT: {
            struct obj obj;   /* Make a fake object to send */
                        /* to map_object().           */
            obj.ox = x;
            obj.oy = y;
            obj.otyp = mon->mappearance;
            obj.corpsenm = PM_TENGU;      /* if mimicing a corpse */
            map_object(&obj,!sensed);
            break;
          }

          case M_AP_MONSTER:
            show_glyph(x,y, monnum_to_glyph(what_mon(mon->mappearance)));
            break;
      }
      
    }

    /* If the mimic is unsucessfully mimicing something, display the monster */
    if (!mon_mimic || sensed) {
      int num;

      if (Detect_monsters) {
          if (worm_tail)
            num = detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL));
          else
            num = detected_mon_to_glyph(mon);
      } else if (mon->mtame && !Hallucination) {
          if (worm_tail)
            num = petnum_to_glyph(PM_LONG_WORM_TAIL);
          else
            num = pet_to_glyph(mon);
      } else {
          if (worm_tail)
            num = monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL));
          else
            num = mon_to_glyph(mon);
      }
      show_glyph(x,y,num);
    }
}

/*
 * 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.
 */
STATIC_OVL void
display_warning(mon)
    register struct monst *mon;
{
    int x = mon->mx, y = mon->my;
    int wl = (int) (mon->m_lev / 4);
    int glyph;

    if (mon_warning(mon)) {
        if (wl > WARNCOUNT - 1) wl = WARNCOUNT - 1;
        glyph = warning_to_glyph(wl);
    } else if (MATCH_WARN_OF_MON(mon)) {
      glyph = mon_to_glyph(mon);
    } else {
      impossible("display_warning did not match warning type?");
        return;
    }
    show_glyph(x, y, glyph);
}

/*
 * 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(x, y)
    xchar x, y;
{
    struct rm *lev = &(levl[x][y]);
    struct obj *boulder;
    register struct monst *mon;

    /* If the 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(levl[x][y].glyph) && m_at(x,y)) return;

    /* The hero can't feel non pool locations while under water. */
    if (Underwater && !Is_waterlevel(&u.uz) && ! is_pool(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 (Levitation && !Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz)) {
      /*
       * 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_ROCK(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)) {
          /*
           * 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 occations 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.
           *
           * 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).
           */
          if (lev->glyph == objnum_to_glyph(BOULDER)) {
            if (lev->typ != ROOM && lev->seenv) {
                map_background(x, y, 1);
            } else {
                lev->glyph = 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 {
      _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.
           */
          if (uchain->ox == x && uchain->oy == y) {
            if (level.objects[x][y] == uchain)
                u.bc_felt |= BC_CHAIN;
            else
                u.bc_felt &= ~BC_CHAIN;   /* do not feel the chain */
          }
          if (!carried(uball) && uball->ox == x && uball->oy == y) {
            if (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)
          show_glyph(x,y, lev->glyph = cmap_to_glyph(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 ((x != u.ux || y != u.uy) && (mon = m_at(x,y)) && sensemon(mon))
      display_monster(x,y,mon,1,((x != mon->mx)  || (y != mon->my)));
}

/*
 * newsym()
 *
 * Possibly put a new glyph at the given location.
 */
void
newsym(x,y)
    register int x,y;
{
    register struct monst *mon;
    register struct rm *lev = &(levl[x][y]);
    register int see_it;
    register xchar worm_tail;

    if (in_mklev) return;

#ifdef JTP_GRAPHICS
    /* only permit updating the hero when swallowed */
    if (u.uswallow) {
      if (x == u.ux && y == u.uy) display_self();
      return;
    }
    if (Underwater && !Is_waterlevel(&u.uz)) {
      /* don't do anything unless (x,y) is an adjacent underwater position */
      int dx, dy;
      if (!is_pool(x,y)) return;
      dx = x - u.ux;    if (dx < 0) dx = -dx;
      dy = y - u.uy;    if (dy < 0) dy = -dy;
      if (dx > 1 || dy > 1) 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 */

      if (reg != NULL && ACCESSIBLE(lev->typ)) {
          show_region(reg,x,y);
          return;
      }
      if (x == u.ux && y == u.uy) {
          if (canseeself()) {
            _map_location(x,y,1);   /* map *under* self */
            display_self();
          } else
            /* we can see what is there */
            _map_location(x,y,1);
      }
      else {
          mon = m_at(x,y);
          worm_tail = mon && ((x != mon->mx)  || (y != mon->my));
          if (mon &&
             ((see_it = (worm_tail
                  ? (!mon->minvis || See_invisible)
                  : (mon_visible(mon)) || sensemon(mon))))) {
            _map_location(x,y,1);   /* map under the monster */
            /* also gets rid of any invisibility glyph */
            display_monster(x,y,mon,see_it,worm_tail);
          }
          else if (glyph_is_invisible(levl[x][y].glyph))
            map_invisible(x, y);
          else
            _map_location(x,y,1);   /* map the location */
      }
    }

    /* Can't see the location. */
    else {
      if (x == u.ux && y == u.uy) {
          feel_location(u.ux, u.uy);            /* forces an update */

          if (canseeself()) display_self();
      }
      else if ((mon = m_at(x,y))
            && (sensemon(mon)
                || (see_with_infrared(mon) && mon_visible(mon)))
            && !((x != mon->mx) || (y != mon->my))) {
          /* Monsters are printed every time. */
          /* This also gets rid of any invisibility glyph */
          display_monster(x,y,mon,0,0);
      }
      else if ((mon = m_at(x,y)) && mon_warning(mon) &&
             !((x != mon->mx) || (y != mon->my))) {
              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.
       *
       * 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 (!lev->waslit) {
          if (flags.lit_corridor && 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(S_stone));
          else
            goto show_mem;
      } else {
show_mem:
          show_glyph(x, y, lev->glyph);
      }
    }
#else
    /* only permit updating the hero when swallowed */
    if (u.uswallow) {
      if (x == u.ux && y == u.uy) display_self();
      return;
    }
    if (Underwater && !Is_waterlevel(&u.uz)) {
      /* don't do anything unless (x,y) is an adjacent underwater position */
      int dx, dy;
      if (!is_pool(x,y)) return;
      dx = x - u.ux;    if (dx < 0) dx = -dx;
      dy = y - u.uy;    if (dy < 0) dy = -dy;
      if (dx > 1 || dy > 1) 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 */

      if (reg != NULL && ACCESSIBLE(lev->typ)) {
          show_region(reg,x,y);
          return;
      }
      if (x == u.ux && y == u.uy) {
          if (canseeself()) {
            _map_location(x,y,0);   /* map *under* self */
            display_self();
          } else
            /* we can see what is there */
            _map_location(x,y,1);
      }
      else {
          mon = m_at(x,y);
          worm_tail = mon && ((x != mon->mx)  || (y != mon->my));
          if (mon &&
             ((see_it = (worm_tail
                  ? (!mon->minvis || See_invisible)
                  : (mon_visible(mon)) || sensemon(mon))))) {
            _map_location(x,y,0);   /* map under the monster */
            /* also gets rid of any invisibility glyph */
            display_monster(x,y,mon,see_it,worm_tail);
          }
          else if (glyph_is_invisible(levl[x][y].glyph))
            map_invisible(x, y);
          else
            _map_location(x,y,1);   /* map the location */
      }
    }

    /* Can't see the location. */
    else {
      if (x == u.ux && y == u.uy) {
          feel_location(u.ux, u.uy);            /* forces an update */

          if (canseeself()) display_self();
      }
      else if ((mon = m_at(x,y))
            && (sensemon(mon)
                || (see_with_infrared(mon) && mon_visible(mon)))
            && !((x != mon->mx) || (y != mon->my))) {
          /* Monsters are printed every time. */
          /* This also gets rid of any invisibility glyph */
          display_monster(x,y,mon,0,0);
      }
      else if ((mon = m_at(x,y)) && mon_warning(mon) &&
             !((x != mon->mx) || (y != mon->my))) {
              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.
       *
       * 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 (!lev->waslit) {
          if (flags.lit_corridor && 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(S_stone));
          else
            goto show_mem;
      } else {
show_mem:
          show_glyph(x, y, lev->glyph);
      }
    }
#endif    
}


/*
 * 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(x,y)
    xchar x,y;
{
    register int i;

    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 */
          delay_output();
      }
      newsym(x,y);            /* restore the old information */
    }
}


/*
 * tmp_at()
 *
 * Temporarily place glyphs on the screen.  Do not call 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 (second 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_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.
 */

static struct tmp_glyph {
    coord saved[COLNO]; /* 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(x, y)
    int x, y;
{
    static struct tmp_glyph *tglyph = (struct tmp_glyph *)0;
    struct tmp_glyph *tmp;

    switch (x) {
      case DISP_BEAM:
      case DISP_FLASH:
      case DISP_ALWAYS:
          if (!tglyph)
            tmp = &tgfirst;
          else    /* nested effect; we need dynamic memory */
            tmp = (struct tmp_glyph *)alloc(sizeof (struct tmp_glyph));
          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");

    switch (x) {
      case DISP_CHANGE:
          tglyph->glyph = y;
          break;

      case DISP_END:
          if (tglyph->style == DISP_BEAM) {
            register 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 {            /* 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 (tglyph->style == DISP_BEAM) {
            if (!cansee(x,y)) break;
            /* save pos for later erasing */
            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 case */
}


/*
 * 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(first)
    int first;
{
    static xchar lastx, lasty;      /* last swallowed position */
    int swallower, left_ok, rght_ok;

    if (first)
      cls();
    else {
      register int 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,cmap_to_glyph(S_stone));
    }

    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(mode)
    int mode;
{
    static xchar lastx, lasty;
    static boolean dela;
    register int 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,cmap_to_glyph(S_stone));
    }
    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(x,y)) {
            if (Blind && !(x == u.ux && y == u.uy))
                show_glyph(x,y,cmap_to_glyph(S_stone));
            else  
                newsym(x,y);
          }
    lastx = u.ux;
    lasty = u.uy;
}

/*
 *    under_ground()
 *
 *    Very restricted display.  You can only see yourself.
 */
void
under_ground(mode)
    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
 *    + 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()
{
    register struct monst *mon;
    for (mon = fmon; mon; mon = mon->nmon) {
      if (DEADMONSTER(mon)) continue;
      newsym(mon->mx,mon->my);
      if (mon->wormno) see_wsegs(mon);
    }
}

/*
 * Block/unblock light depending on what a mimic is mimicing and if it's
 * invisible or not.  Should be called only when the state of See_invisible
 * changes.
 */
void
set_mimic_blocking()
{
    register struct monst *mon;
    for (mon = fmon; mon; mon = mon->nmon)
      if(!DEADMONSTER(mon) && mon->minvis &&
         ((mon->m_ap_type == M_AP_FURNITURE &&
            (mon->mappearance == S_vcdoor || mon->mappearance == S_hcdoor))||
          (mon->m_ap_type == M_AP_OBJECT && mon->mappearance == BOULDER))) {
          if(See_invisible)
            block_point(mon->mx, mon->my);
          else
            unblock_point(mon->mx, mon->my);
      }
}

/*
 * Loop through all of the object *locations* and update them.  Called when
 *    + hallucinating.
 */
void
see_objects()
{
    register struct obj *obj;
    for(obj = fobj; obj; obj = obj->nobj)
      if (vobj_at(obj->ox,obj->oy) == obj) newsym(obj->ox, obj->oy);
}

/*
 * Update hallucinated traps.
 */
void
see_traps()
{
    struct trap *trap;
    int glyph;

    for (trap = ftrap; trap; trap = trap->ntrap) {
      glyph = glyph_at(trap->tx, trap->ty);
      if (glyph_is_trap(glyph))
          newsym(trap->tx, trap->ty);
    }
}

/*
 * Put the cursor on the hero.  Flush all accumulated glyphs before doing it.
 */
void
curs_on_u()
{
    flush_screen(1);    /* Flush waiting glyphs & put cursor on hero */
}

int
doredraw()
{
    docrt();
    return 0;
}

void
docrt()
{
    register int x,y;
    register struct rm *lev;

    if (!u.ux) return; /* display isn't ready yet */

    if (u.uswallow) {
      swallowed(1);
      return;
    }
    if (Underwater && !Is_waterlevel(&u.uz)) {
      under_water(1);
      return;
    }
    if (u.uburied) {
      under_ground(1);
      return;
    }

    /* 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
     */
    cls();

    /* display memory */
    for (x = 1; x < COLNO; x++) {
      lev = &levl[x][0];
      for (y = 0; y < ROWNO; y++, lev++)
          if (lev->glyph != cmap_to_glyph(S_stone))
            show_glyph(x,y,lev->glyph);
    }

    /* see what is to be seen */
    vision_recalc(0);

    /* overlay with monsters */
    see_monsters();

    flags.botlx = 1;    /* force a redraw of the bottom line */
}


/* ========================================================================= */
/* Glyph Buffering (3rd screen) ============================================ */

typedef struct {
    xchar new;          /* perhaps move this bit into the rm strucure. */
    int   glyph;
} gbuf_entry;

static gbuf_entry gbuf[ROWNO][COLNO];
static char gbuf_start[ROWNO];
static char gbuf_stop[ROWNO];

/*
 * Store the glyph in the 3rd screen for later flushing.
 */
void
show_glyph(x,y,glyph)
    int x, y, glyph;
{
    /*
     * Check for bad positions and glyphs.
     */
    if (!isok(x, y)) {
      const char *text;
      int  offset;

      /* 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 >= GLYPH_WARNING_OFF) {   /* a warning */
          text = "warning";         offset = glyph - GLYPH_WARNING_OFF;
      } else if (glyph >= GLYPH_SWALLOW_OFF) {  /* swallow border */
          text = "swallow border";  offset = glyph - GLYPH_SWALLOW_OFF;
      } else if (glyph >= GLYPH_ZAP_OFF) {            /* zap beam */
          text = "zap beam";        offset = glyph - GLYPH_ZAP_OFF;
      } else if (glyph >= GLYPH_CMAP_OFF) {           /* cmap */
          text = "cmap_index";      offset = glyph - GLYPH_CMAP_OFF;
      } else if (glyph >= GLYPH_OBJ_OFF) {            /* object */
          text = "object";          offset = glyph - GLYPH_OBJ_OFF;
      } else if (glyph >= GLYPH_RIDDEN_OFF) {         /* ridden mon */
          text = "ridden mon";      offset = glyph - GLYPH_RIDDEN_OFF;
      } else if (glyph >= GLYPH_BODY_OFF) {           /* a corpse */
          text = "corpse";          offset = glyph - GLYPH_BODY_OFF;
      } else if (glyph >= GLYPH_DETECT_OFF) {         /* detected mon */
          text = "detected mon";    offset = glyph - GLYPH_DETECT_OFF;
      } else if (glyph >= GLYPH_INVIS_OFF) {          /* invisible mon */
          text = "invisible mon";   offset = glyph - GLYPH_INVIS_OFF;
      } else if (glyph >= GLYPH_PET_OFF) {            /* a pet */
          text = "pet";       offset = glyph - GLYPH_PET_OFF;
      } else {                            /* a monster */
          text = "monster";         offset = glyph;
      }

      impossible("show_glyph:  bad pos %d %d with glyph %d [%s %d].",
                                    x, y, glyph, text, offset);
      return;
    }

    if (glyph >= MAX_GLYPH) {
      impossible("show_glyph:  bad glyph %d [max %d] at (%d,%d).",
                              glyph, MAX_GLYPH, x, y);
      return;
    }

#ifdef JTP_GRAPHICS
    print_glyph(WIN_MAP,x,y,gbuf[y][x].glyph);
#endif
    if (gbuf[y][x].glyph != glyph) {
      gbuf[y][x].glyph = glyph;
      gbuf[y][x].new   = 1;
      if (gbuf_start[y] > x) gbuf_start[y] = x;
      if (gbuf_stop[y]  < x) gbuf_stop[y]  = x;
    }
}


/*
 * 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++) {       \
          gbuf_start[i] = COLNO-1;        \
          gbuf_stop[i]  = 0;              \
      }                             \
    }


static gbuf_entry nul_gbuf = { 0, cmap_to_glyph(S_stone) };
/*
 * Turn the 3rd screen into stone.
 */
void
clear_glyph_buffer()
{
    register int x, y;
    register gbuf_entry *gptr;

    for (y = 0; y < ROWNO; y++) {
      gptr = &gbuf[y][0];
      for (x = COLNO; x; x--) {
          *gptr++ = nul_gbuf;
      }
    }
    reset_glyph_bbox();
}

/*
 * Assumes that the indicated positions are filled with S_stone glyphs.
 */
void
row_refresh(start,stop,y)
    int start,stop,y;
{
    register int x;

    for (x = start; x <= stop; x++)
      if (gbuf[y][x].glyph != cmap_to_glyph(S_stone))
          print_glyph(WIN_MAP,x,y,gbuf[y][x].glyph);
}

void
cls()
{
    display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
    flags.botlx = 1;          /* force update of botl window */
    clear_nhwindow(WIN_MAP);  /* clear physical screen */

    clear_glyph_buffer();     /* this is sort of an extra effort, but OK */
}

/*
 * Synch the third screen with the display.
 */
void
flush_screen(cursor_on_u)
    int cursor_on_u;
{
    /* Prevent infinite loops on errors:
     *          flush_screen->print_glyph->impossible->pline->flush_screen
     */
    static   boolean flushing = 0;
    register int x,y;

    if (flushing) return;     /* if already flushing then return */
    flushing = 1;

    for (y = 0; y < ROWNO; y++) {
      register gbuf_entry *gptr = &gbuf[y][x = gbuf_start[y]];
      for (; x <= gbuf_stop[y]; gptr++, x++)
          if (gptr->new) {
            print_glyph(WIN_MAP,x,y,gptr->glyph);
            gptr->new = 0;
          }
    }

    if (cursor_on_u) curs(WIN_MAP, u.ux,u.uy); /* move cursor to the hero */
    display_nhwindow(WIN_MAP, FALSE);
    reset_glyph_bbox();
    flushing = 0;
    if(flags.botl || flags.botlx) bot();
}

/* ========================================================================= */

/*
 * 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(x,y)
    xchar x,y;
{
    int idx;
    struct rm *ptr = &(levl[x][y]);

    switch (ptr->typ) {
      case SCORR:
      case STONE:
          idx = 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:
          idx = (ptr->ladder & LA_DOWN) ? S_dnstair : S_upstair;
          break;
      case LADDER:
          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;    break;
      case GRAVE:       idx = S_grave;    break;
      case THRONE:            idx = S_throne;   break;
      case LAVAPOOL:          idx = S_lava;       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 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.
 */
STATIC_OVL int
swallow_to_glyph(mnum, loc)
    int mnum;
    int loc;
{
    if (loc < S_sw_tl || S_sw_br < loc) {
      impossible("swallow_to_glyph: bad swallow location");
      loc = S_sw_br;
    }
    return ((int) (what_mon(mnum)<<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 rm.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(dx, dy, beam_type)
    register int dx, 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(x, y)
    xchar x,y;
{
    if(x < 0 || y < 0 || x >= COLNO || y >= ROWNO)
      return cmap_to_glyph(S_room);             /* XXX */
    return gbuf[y][x].glyph;
}


/* ------------------------------------------------------------------------- */
/* Wall Angle -------------------------------------------------------------- */

/*#define WA_VERBOSE*/  /* give (x,y) locations for all "bad" spots */

#ifdef WA_VERBOSE

static const char *FDECL(type_to_name, (int));
static void FDECL(error4, (int,int,int,int,int,int));

static int bad_count[MAX_TYPE]; /* count of positions flagged as bad */
static const char *type_names[MAX_TYPE] = {
      "STONE",    "VWALL",    "HWALL",    "TLCORNER",
      "TRCORNER", "BLCORNER", "BRCORNER", "CROSSWALL",
      "TUWALL",   "TDWALL",   "TLWALL",   "TRWALL",
      "DBWALL",   "SDOOR",    "SCORR",    "POOL",
      "MOAT",           "WATER",    "DRAWBRIDGE_UP","LAVAPOOL",
      "DOOR",           "CORR",           "ROOM",           "STAIRS",
      "LADDER",   "FOUNTAIN", "THRONE",   "SINK",
      "ALTAR",    "ICE",            "DRAWBRIDGE_DOWN","AIR",
      "CLOUD"
};


static const char *
type_to_name(type)
    int type;
{
    return (type < 0 || type > MAX_TYPE) ? "unknown" : type_names[type];
}

static void
error4(x, y, a, b, c, dd)
    int x, y, a, b, c, 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 unfinshed exterior.  Return
 * zero otherwise.  Unfinished implies outer area is rock or a corridor.
 *
 * Things that are ambigious: lava
 */
STATIC_OVL int
check_pos(x, y, which)
    int x, y, which;
{
    int type;
    if (!isok(x,y)) return which;
    type = levl[x][y].typ;
    if (IS_ROCK(type) || type == CORR || type == SCORR) return which;
    return 0;
}

/* Return TRUE if more than one is non-zero. */
/*ARGSUSED*/
#ifdef WA_VERBOSE
STATIC_OVL boolean
more_than_one(x, y, a, b, c)
    int x, y, a, b, 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. */
STATIC_OVL int
set_twall(x0,y0, x1,y1, x2,y2, x3,y3)
int x0,y0, x1,y1, x2,y2, x3,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. */
STATIC_OVL int
set_wall(x, y, horiz)
    int x, y, 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. */
STATIC_OVL int
set_corn(x1,y1, x2,y2, x3,y3, x4,y4)
      int x1, y1, x2, y2, x3, y3, x4, y4;
{
    int 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. */
STATIC_OVL int
set_crosswall(x, y)
    int x, y;
{
    int 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 from mklev.  Scan the level and set the wall modes. */
void
set_wall_state()
{
    int x, y;
    int wmode;
    struct rm *lev;

#ifdef WA_VERBOSE
    for (x = 0; x < MAX_TYPE; x++) bad_count[x] = 0;
#endif

    for (x = 0; x < COLNO; x++)
      for (lev = &levl[x][0], y = 0; y < ROWNO; y++, lev++) {
          switch (lev->typ) {
            case SDOOR:
                wmode = set_wall(x, y, (int) 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;
      }

#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 print 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. */
unsigned char seenv_matrix[3][3] = { {SV2,   SV1, SV0},
                             {SV3, SVALL, SV7},
                             {SV4,   SV5, SV6} };

#define sign(z) ((z) < 0 ? -1 : ((z) > 0 ? 1 : 0))

/* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */
STATIC_OVL void
set_seenv(lev, x0, y0, x, y)
    struct rm *lev;
    int x0, y0, x, y;   /* from, to */
{
    int dx = x-x0, dy = y0-y;
    lev->seenv |= seenv_matrix[sign(dy)+1][sign(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'ere 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. */
STATIC_OVL void
t_warn(lev)
    struct rm *lev;
{
    static const char *warn_str = "wall_angle: %s: case %d: seenv = 0x%x";
    const char *wname;

    if (lev->typ == TUWALL) wname = "tuwall";
    else if (lev->typ == TLWALL) wname = "tlwall";
    else if (lev->typ == TRWALL) wname = "trwall";
    else if (lev->typ == TDWALL) wname = "tdwall";
    else wname = "unknown";
    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).
 */
STATIC_OVL int
wall_angle(lev)
    struct rm *lev;
{
    register 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 0 /* older method, fixed */
                  if (only(seenv, SV4|SV5)) {
                      col = T_tlcorn;
                  } else if ((seenv & (SV0|SV1|SV2)) &&
                              only(seenv, SV0|SV1|SV2|SV6|SV7)) {
                      col = T_hwall;
                  } else if (seenv & SV3 ||
                      ((seenv & (SV0|SV1|SV2)) && (seenv & (SV4|SV5)))) {
                      col = T_tdwall;
                  } else {
                      if (seenv != SV6)
                        t_warn(lev);
                      col = T_stone;
                  }
#endif      /* 0 */
                  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 0 /* older method, fixed */
                  if (only(seenv, SV5|SV6)) {
                      col = T_trcorn;
                  } else if ((seenv & (SV0|SV1|SV2)) &&
                                  only(seenv, SV0|SV1|SV2|SV3|SV4)) {
                      col = T_hwall;
                  } else if (seenv & SV7 ||
                      ((seenv & (SV0|SV1|SV2)) && (seenv & (SV5|SV6)))) {
                      col = T_tdwall;
                  } else {
                      if (seenv != SV4)
                        t_warn(lev);
                      col = T_stone;
                  }
#endif      /* 0 */
                  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;
            /* fall through */
      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;
}

/*display.c*/

Generated by  Doxygen 1.6.0   Back to index