/* NetHack 3.7	display.h	$NHDT-Date: 1698741423 2023/10/31 08:37:03 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.95 $ */
/* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */
/* and Dave Cohrs, 1990.                                          */
/* NetHack may be freely redistributed.  See license for details. */

#ifndef DISPLAY_H
#define DISPLAY_H

#ifndef VISION_H
#include "vision.h"
#endif
#ifndef MONDATA_H
#include "mondata.h" /* for mindless() */
#endif

/*
 * vobj_at()
 *
 * Returns the head of the list of objects that the player can see
 * at location (x,y).  [Vestige of unimplemented invisible objects.]
 */
#define vobj_at(x, y) (gl.level.objects[x][y])

/*
 * sensemon()
 *
 * Returns true if the hero can sense the given monster.  This includes
 * monsters that are hiding or mimicing other monsters.
 *
 * [3.7] Note: the map doesn't display any monsters when hero is swallowed
 * (or display non-adjacent, non-submerged ones when hero is underwater),
 * so treat those situations as blocking telepathy, detection, and warning
 * even though conceptually they shouldn't do so.
 *
 * [3.7 also] The macros whose name begins with an underscore have been
 * converted to functions in order to have compilers generate smaller code.
 * The retained underscore versions are still used in display.c but should
 * only be used in other situations if the function calls actually produce
 * noticeably slower processing.
 */
#define _tp_sensemon(mon) \
    (/* The hero can always sense a monster IF:        */  \
     /* 1. the monster has a brain to sense            */  \
     (!mindless(mon->data))                                \
     /* AND     2a. hero is blind and telepathic       */  \
      && ((Blind && Blind_telepat)                         \
          /* OR 2b. hero is using a telepathy inducing */  \
          /*        object and in range                */  \
          || (Unblind_telepat                              \
              && (mdistu(mon) <= (BOLT_LIM * BOLT_LIM)))))

/* organized to perform cheaper tests first;
   is_pool() vs is_pool_or_lava(): hero who is underwater can see adjacent
   lava, but presumably any monster there is on top so not sensed */
#define _sensemon(mon) \
    (   (!u.uswallow || (mon) == u.ustuck)                                   \
     && (!Underwater || (mdistu(mon) <= 2 && is_pool((mon)->mx, (mon)->my))) \
     && (Detect_monsters || tp_sensemon(mon) || MATCH_WARN_OF_MON(mon))   )

/*
 * mon_warning() is used to warn of any dangerous monsters in your
 * vicinity, and a glyph representing the warning level is displayed.
 */
#define _mon_warning(mon) \
    (Warning && !(mon)->mpeaceful && (mdistu(mon) < 100)     \
     && (((int) ((mon)->m_lev / 4)) >= gc.context.warnlevel))

/*
 * mon_visible()
 *
 * Returns true if the hero can see the monster.  It is assumed that the
 * hero can physically see the location of the monster.  The function
 * vobj_at() returns a pointer to an object that the hero can see there.
 * Infravision is not taken into account.
 *
 * Note:  not reliable for concealed mimics.  They don't have
 * 'mon->mundetected' set even when mimicking objects or furniture.
 * [Fixing this with a pair of mon->m_ap_type checks here (via either
 * 'typ!=object && typ!=furniture' or 'typ==nothing || typ==monster')
 * will require reviewing every instance of mon_visible(), canseemon(),
 * canspotmon(), is_safemon() and perhaps others.  Fixing it by setting
 * mon->mundetected when concealed would be better but also require
 * reviewing all those instances and also existing mundetected instances.]
 */
#if 0
#define _mon_visible(mon) \
    (/* The hero can see the monster IF the monster                     */ \
     (!mon->minvis || See_invisible)  /*     1. is not invisible        */ \
     && !mon->mundetected             /* AND 2. not an undetected hider */ \
     && !(mon->mburied || u.uburied)) /* AND 3. neither you nor it is buried */
#else   /* without 'mburied' and 'uburied' */
#define _mon_visible(mon) \
    (/* The hero can see the monster IF the monster                     */ \
     (!mon->minvis || See_invisible)  /*     1. is not invisible        */ \
     && !mon->mundetected)            /* AND 2. not an undetected hider */
#endif

/*
 * see_with_infrared()
 *
 * This function is true if the player can see a monster using infravision.
 * The caller must check for invisibility (invisible monsters are also
 * invisible to infravision), because this is usually called from within
 * canseemon() or canspotmon() which already check that.
 */
#define _see_with_infrared(mon) \
    (!Blind && Infravision && mon && infravisible(mon->data) \
     && couldsee(mon->mx, mon->my))

/*
 * canseemon()
 *
 * This is the globally used canseemon().  It is not called within the display
 * routines.  Like mon_visible(), but it checks to see if the hero sees the
 * location instead of assuming it.  (And also considers worms.)
 */
#define _canseemon(mon) \
    ((mon->wormno ? worm_known(mon)                                       \
                  : (cansee(mon->mx, mon->my) || see_with_infrared(mon))) \
     && mon_visible(mon))

/*
 * canspotmon(mon)
 *
 * This function checks whether you can either see a monster or sense it by
 * telepathy, and is what you usually call for monsters about which nothing is
 * known.
 */
#define canspotmon(mon) (canseemon(mon) || sensemon(mon))

/* knowninvisible(mon)
 * This one checks to see if you know a monster is both there and invisible.
 * 1) If you can see the monster and have see invisible, it is assumed the
 * monster is transparent, but visible in some manner.  (Earlier versions of
 * Nethack were really inconsistent on this.)
 * 2) If you can't see the monster, but can see its location and you have
 * telepathy that works when you can see, you can tell that there is a
 * creature in an apparently empty spot.
 * Infravision is not relevant; we assume that invisible monsters are also
 * invisible to infravision.
 * [3.7: the macro definition erroneously started with 'mtmp->minvis' for
 * over 20 years.  The one place it's used called it as knowninvisible(mtmp)
 * so worked by coincidence when there was no argument expansion involved
 * on the first line.]
 */
#define _knowninvisible(mon) \
    ((mon)->minvis                                                      \
     && ((cansee((mon)->mx, (mon)->my)                                  \
          && (See_invisible || Detect_monsters))                        \
         || (!Blind && (HTelepat & ~INTRINSIC)                          \
             && mdistu(mon) <= (BOLT_LIM * BOLT_LIM))))

/*
 * is_safemon(mon)
 *
 * A special case check used in attack() and domove().  Placing the
 * definition here is convenient.  No longer limited to pets.
 */
#define _is_safemon(mon) \
    (flags.safe_dog && (mon) && (mon)->mpeaceful && canspotmon(mon)     \
     && !Confusion && !Hallucination && !Stunned)

/*
 * canseeself()
 * senseself()
 * canspotself()
 *
 * This returns true if the hero can see her/himself.
 *
 * Sensing yourself by touch is treated as seeing yourself, even if
 * unable to see.  So when blind, being invisible won't affect your
 * self-perception, and when swallowed, the enclosing monster touches.
 */
#define canseeself() (Blind || u.uswallow || (!Invisible && !u.uundetected))
#define senseself() (Unblind_telepat || Detect_monsters)
#define canspotself() (canseeself() || senseself())

/*
 * random_monster()
 * random_object()
 *
 * Respectively return a random monster or object.
 * random_object() won't return STRANGE_OBJECT or the generic objects.
 * -/+ FIRST_OBJECT is used to skip it and them.
 */
#define random_monster(rng) ((*rng)(NUMMONS))
#define random_object(rng) ((*rng)(NUM_OBJECTS - FIRST_OBJECT) + FIRST_OBJECT)

/*
 * what_obj()
 * what_mon()
 *
 * If hallucinating, choose a random object/monster, otherwise, use the one
 * given. Use the given rng to handle hallucination.
 */
#define what_obj(obj, rng) (Hallucination ? random_object(rng) : obj)
#define what_mon(mon, rng) (Hallucination ? random_monster(rng) : mon)

/*
 * newsym_rn2
 *
 * An appropriate random number generator for use with newsym(), when
 * randomness is needed there. This is currently hardcoded as
 * rn2_on_display_rng, but is futureproofed for cases where we might
 * want to prevent display-random objects entering the character's
 * memory (this isn't important at present but may be if we need
 * reproducible gameplay for some reason).
 */
#define newsym_rn2 rn2_on_display_rng

/*
 * covers_objects()
 * covers_traps()
 *
 * These routines are true if what is really at the given location will
 * "cover" any objects or traps that might be there.
 */
#define covers_objects(xx, yy) \
    ((is_pool(xx, yy) && !Underwater) || (levl[xx][yy].typ == LAVAPOOL) \
      || (levl[xx][yy].typ == LAVAWALL))

#define covers_traps(xx, yy) covers_objects(xx, yy)

/*
 * tmp_at() control calls.
 */
#define DISP_BEAM    (-1) /* Keep all glyphs showing & clean up at end. */
#define DISP_ALL     (-2) /* Like beam, but still displayed if not visible. */
#define DISP_TETHER  (-3) /* Like beam, but tether glyph differs from final */
#define DISP_FLASH   (-4) /* Clean up each glyph before displaying new one. */
#define DISP_ALWAYS  (-5) /* Like flash, but still displayed if not visible. */
#define DISP_CHANGE  (-6) /* Change glyph. */
#define DISP_END     (-7) /* Clean up. */
#define DISP_FREEMEM (-8) /* Free all memory during exit only. */

/* Total number of cmap indices in the shield_static[] array. */
#define SHIELD_COUNT 21
#define BACKTRACK (-1)    /* flag for DISP_END to display each prior location */

/*
 * display_self()
 *
 * Display the hero.  It is assumed that all checks necessary to determine
 * _if_ the hero can be seen have already been done.
 */
#define maybe_display_usteed(otherwise_self)                 \
    ((u.usteed && mon_visible(u.usteed))                     \
         ? ridden_mon_to_glyph(u.usteed, rn2_on_display_rng) \
         : (otherwise_self))

#define display_self() \
    show_glyph(u.ux, u.uy, maybe_display_usteed(                        \
        ((int) U_AP_TYPE == M_AP_NOTHING)                               \
        ? hero_glyph                                                    \
        : ((int) U_AP_TYPE == M_AP_FURNITURE)                           \
          ? cmap_to_glyph((int) gy.youmonst.mappearance)                \
          : ((int) U_AP_TYPE == M_AP_OBJECT)                            \
            ? objnum_to_glyph((int) gy.youmonst.mappearance)            \
            /* else U_AP_TYPE == M_AP_MONSTER */                        \
            : monnum_to_glyph((int) gy.youmonst.mappearance, Ugender)))

/*
 * NetHack glyphs
 *
 * A glyph is an abstraction that represents a _unique_ monster, object,
 * dungeon part, or effect.  The uniqueness is important.  For example,
 * It is not enough to have four (one for each "direction") zap beam glyphs,
 * we need a set of four for each beam type.  Why go to so much trouble?
 * Because it is possible that any given window dependent display driver
 * [print_glyph()] can produce something different for each type of glyph.
 * That is, a beam of cold and a beam of fire would not only be different
 * colors, but would also be represented by different symbols.
 */


#include "color.h"
/* 3.6.3: poison gas zap used to be yellow and acid zap was green,
   which conflicted with the corresponding dragon colors */
enum zap_colors {
    zap_color_missile    = HI_ZAP,
    zap_color_fire       = CLR_ORANGE,
    zap_color_frost      = CLR_WHITE,
    zap_color_sleep      = HI_ZAP,
    zap_color_death      = CLR_BLACK,
    zap_color_lightning  = CLR_WHITE,
    zap_color_poison_gas = CLR_GREEN,
    zap_color_acid       = CLR_YELLOW
};

enum altar_colors {
    altar_color_unaligned = CLR_RED,
#if defined(USE_GENERAL_ALTAR_COLORS)
        /* On OSX with TERM=xterm-color256 these render as
         *  white -> tty: gray, curses: ok
         *  gray  -> both tty and curses: black
         *  black -> both tty and curses: blue
         *  red   -> both tty and curses: ok.
         * Since the colors have specific associations (with the
         * unicorns matched with each alignment), we shouldn't use
         * scrambled colors and we don't have sufficient information
         * to handle platform-specific color variations.
         */
    altar_color_chaotic = CLR_BLACK,
    altar_color_neutral = CLR_GRAY,
    altar_color_lawful  = CLR_WHITE,
#else
    altar_color_chaotic = CLR_GRAY,
    altar_color_neutral = CLR_GRAY,
    altar_color_lawful  = CLR_GRAY,
#endif
    altar_color_other = CLR_BRIGHT_MAGENTA,
};

/* types of explosions */
enum explosion_types {
    EXPL_DARK = 0,
    EXPL_NOXIOUS = 1,
    EXPL_MUDDY = 2,
    EXPL_WET = 3,
    EXPL_MAGICAL = 4,
    EXPL_FIERY = 5,
    EXPL_FROSTY = 6,
    EXPL_MAX = 7
};

/* above plus this redundant? */
enum expl_types {
    expl_dark,
    expl_noxious,
    expl_muddy,
    expl_wet,
    expl_magical,
    expl_fiery,
    expl_frosty,
};

enum explode_colors {
    explode_color_dark = CLR_BLACK,
    explode_color_noxious = CLR_GREEN,
    explode_color_muddy = CLR_BROWN,
    explode_color_wet = CLR_BLUE,
    explode_color_magical = CLR_MAGENTA,
    explode_color_fiery = CLR_ORANGE,
    explode_color_frosty = CLR_WHITE
};
enum altar_types {
    altar_unaligned,
    altar_chaotic,
    altar_neutral,
    altar_lawful,
    altar_other
};
enum level_walls  { main_walls, mines_walls, gehennom_walls,
                    knox_walls, sokoban_walls };
enum { GM_FLAGS, GM_TTYCHAR, GM_COLOR, NUM_GLYPHMOD }; /* glyphmod entries */
enum glyphmap_change_triggers { gm_nochange, gm_newgame, gm_levelchange,
                                gm_optionchange, gm_symchange,
                                gm_accessibility_change };
#define NUM_ZAP 8 /* number of zap beam types */

/*
 * Glyphs are grouped for easy accessibility:
 *
 * male monsters    Represents all the wild (not tame) male monsters.
 *                  Count: NUMMONS.
 *
 * female monsters  Represents all the wild (not tame) female monsters.
 *                  Count: NUMMONS.
 *
 * male pets        Represents all of the male tame monsters.
 *                  Count: NUMMONS.
 *
 * female pets      Represents all of the female tame monsters.
 *                  Count: NUMMONS.
 *
 * invisible        Invisible monster placeholder.
 *                  Count: 1.
 *
 * detect (male)    Represents all detected male monsters.
 *                  Count: NUMMONS.
 *
 * detect (female)  Represents all detected female monsters.
 *                  Count: NUMMONS.
 *
 * corpse           One for each monster (male/female not differentiated).
 *                  Count: NUMMONS.
 *
 * ridden (male)    Represents all male monsters being ridden.
 *                  Count: NUMMONS
 *
 * ridden (female)  Represents all female monsters being ridden.
 *                  Count: NUMMONS
 *
 * object           One for each type of object.  The first entry is
 *                  'strange object', the next 1..MAXOCLASSES-1 entries
 *                  are generic (one for each class, including one for
 *                  strange object's 'illobj class'), the rest are
 *                  regular objects.  Some members of the generic
 *                  subset are used to prevent color of potions, gems,
 *                  and spellbooks from being revealed when obj->dknown
 *                  hasn't been set, avoiding a bug which had been
 *                  present since day one of color support.
 *                  Count: NUM_OBJECTS
 *
 * Stone            Stone
 *                  Count: 1
 *
 * main walls       level walls (main)
 *                  Count: (S_trwall - S_vwall) + 1 = 11
 *
 * mines walls      level walls (mines)
 *                  Count: (S_trwall - S_vwall) + 1 = 11
 *
 * gehennom walls   level walls (gehennom)
 *                  Count: (S_trwall - S_vwall) + 1 = 11
 *
 * knox walls       level walls (knox)
 *                  Count: (S_trwall - S_vwall) + 1 = 11
 *
 * sokoban walls    level walls (sokoban)
 *                  Count: (S_trwall - S_vwall) + 1 = 11
 *
 * cmap A           S_ndoor through S_brdnladder
 *                  Count: (S_brdnladder - S_ndoor) + 1 = 19
 *
 * Altars           Altar (unaligned, chaotic, neutral, lawful, other)
 *                  Count: 5
 *
 * cmap B           S_grave through S_arrow_trap + TRAPNUM - 1
 *                  Count: (S_arrow_trap + (TRAPNUM - 1) - S_grave) = 39
 *
 * zap beams        set of four (there are four directions) HI_ZAP.
 *                  Count: 4 * NUM_ZAP
 *
 * cmap C           S_digbeam through S_goodpos
 *                  Count: (S_goodpos - S_digbeam) + 1 = 10
 *
 * swallow          A set of eight for each monster.  The eight positions
 *                  represent those surrounding the hero.  The monster
 *                  number is shifted over 3 positions and the swallow
 *                  position is stored in the lower three bits.
 *                  Count: NUMMONS << 3
 *
 * dark explosions        A set of nine.
 *                        Count: MAXEXPCHAR
 *
 * noxious explosions     A set of nine.
 *                        Count: MAXEXPCHAR
 *
 * muddy explosions       A set of nine.
 *                        Count: MAXEXPCHAR
 *
 * wet explosions         A set of nine.
 *                        Count: MAXEXPCHAR
 *
 * magical explosions     A set of nine.
 *                        Count: MAXEXPCHAR
 *
 * fiery explosions       A set of nine.
 *                        Count: MAXEXPCHAR
 *
 * frosty explosions      A set of nine.
 *                        Count: MAXEXPCHAR
 *
 * warning                A set of six representing the different warning
 *                        levels.
 *                        Count: 6
 *
 * statues (male)         One for each male monster.
 *                        Count: NUMMONS
 *
 * statues (female)       One for each female mo nster.
 *                        Count: NUMMONS
 *
 * objects piletop        Represents the top of a pile as well as
 *                        the object.
 *                        Count: NUM_OBJECTS
 *
 * bodies piletop         Represents the top of a pile as well as
 *                        the object, corpse in this case.
 *                        Count: NUMMONS
 *
 * male statues piletop   Represents the top of a pile as well as
 *                        the statue of a male monster.
 *                        Count: NUMMONS
 *
 * female statues piletop Represents the top of a pile as well as
 *                        the statue of a female monster.
 *                        Count: NUMMONS
 *
 * unexplored             One for unexplored areas of the map
 * nothing                Nothing but background
 *
 * The following are offsets used to convert to and from a glyph.
 */

enum glyph_offsets {
    GLYPH_MON_OFF = 0,
    GLYPH_MON_MALE_OFF = (GLYPH_MON_OFF),
    GLYPH_MON_FEM_OFF = (NUMMONS + GLYPH_MON_MALE_OFF),
    GLYPH_PET_OFF = (NUMMONS + GLYPH_MON_FEM_OFF),
    GLYPH_PET_MALE_OFF = (GLYPH_PET_OFF),
    GLYPH_PET_FEM_OFF = (NUMMONS + GLYPH_PET_MALE_OFF),
    GLYPH_INVIS_OFF = (NUMMONS + GLYPH_PET_FEM_OFF),
    GLYPH_DETECT_OFF = (1 + GLYPH_INVIS_OFF),
    GLYPH_DETECT_MALE_OFF = (GLYPH_DETECT_OFF),
    GLYPH_DETECT_FEM_OFF = (NUMMONS + GLYPH_DETECT_MALE_OFF),
    GLYPH_BODY_OFF = (NUMMONS + GLYPH_DETECT_FEM_OFF),
    GLYPH_RIDDEN_OFF = (NUMMONS + GLYPH_BODY_OFF),
    GLYPH_RIDDEN_MALE_OFF = (GLYPH_RIDDEN_OFF),
    GLYPH_RIDDEN_FEM_OFF = (NUMMONS + GLYPH_RIDDEN_MALE_OFF),
    GLYPH_OBJ_OFF = (NUMMONS + GLYPH_RIDDEN_FEM_OFF),
    GLYPH_CMAP_OFF = (NUM_OBJECTS + GLYPH_OBJ_OFF),
    GLYPH_CMAP_STONE_OFF = (GLYPH_CMAP_OFF),
    GLYPH_CMAP_MAIN_OFF = (1 + GLYPH_CMAP_STONE_OFF),
    GLYPH_CMAP_MINES_OFF = (((S_trwall - S_vwall) + 1) + GLYPH_CMAP_MAIN_OFF),
    GLYPH_CMAP_GEH_OFF = (((S_trwall - S_vwall) + 1) + GLYPH_CMAP_MINES_OFF),
    GLYPH_CMAP_KNOX_OFF = (((S_trwall - S_vwall) + 1) + GLYPH_CMAP_GEH_OFF),
    GLYPH_CMAP_SOKO_OFF = (((S_trwall - S_vwall) + 1) + GLYPH_CMAP_KNOX_OFF),
    GLYPH_CMAP_A_OFF = (((S_trwall - S_vwall) + 1) + GLYPH_CMAP_SOKO_OFF),
    GLYPH_ALTAR_OFF = (((S_brdnladder - S_ndoor) + 1) + GLYPH_CMAP_A_OFF),
    GLYPH_CMAP_B_OFF = (5 + GLYPH_ALTAR_OFF),
    GLYPH_ZAP_OFF = ((S_arrow_trap + MAXTCHARS - S_grave) + GLYPH_CMAP_B_OFF),
    GLYPH_CMAP_C_OFF = ((NUM_ZAP << 2) + GLYPH_ZAP_OFF),
    GLYPH_SWALLOW_OFF = (((S_goodpos - S_digbeam) + 1) + GLYPH_CMAP_C_OFF),
    GLYPH_EXPLODE_OFF = ((NUMMONS << 3) + GLYPH_SWALLOW_OFF),
    GLYPH_EXPLODE_DARK_OFF = (GLYPH_EXPLODE_OFF),
    GLYPH_EXPLODE_NOXIOUS_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_DARK_OFF),
    GLYPH_EXPLODE_MUDDY_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_NOXIOUS_OFF),
    GLYPH_EXPLODE_WET_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_MUDDY_OFF),
    GLYPH_EXPLODE_MAGICAL_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_WET_OFF),
    GLYPH_EXPLODE_FIERY_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_MAGICAL_OFF),
    GLYPH_EXPLODE_FROSTY_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_FIERY_OFF),
    GLYPH_WARNING_OFF = (MAXEXPCHARS + GLYPH_EXPLODE_FROSTY_OFF),
    GLYPH_STATUE_OFF = (WARNCOUNT + GLYPH_WARNING_OFF),
    GLYPH_STATUE_MALE_OFF = (GLYPH_STATUE_OFF),
    GLYPH_STATUE_FEM_OFF = (NUMMONS + GLYPH_STATUE_MALE_OFF),
    GLYPH_PILETOP_OFF = (NUMMONS + GLYPH_STATUE_FEM_OFF),
    GLYPH_OBJ_PILETOP_OFF = (GLYPH_PILETOP_OFF),
    GLYPH_BODY_PILETOP_OFF = (NUM_OBJECTS + GLYPH_OBJ_PILETOP_OFF),
    GLYPH_STATUE_MALE_PILETOP_OFF = (NUMMONS + GLYPH_BODY_PILETOP_OFF),
    GLYPH_STATUE_FEM_PILETOP_OFF = (NUMMONS + GLYPH_STATUE_MALE_PILETOP_OFF),
    GLYPH_UNEXPLORED_OFF = (NUMMONS + GLYPH_STATUE_FEM_PILETOP_OFF),
    GLYPH_NOTHING_OFF = (GLYPH_UNEXPLORED_OFF + 1),
    MAX_GLYPH
};

#define NO_GLYPH          MAX_GLYPH
#define GLYPH_INVISIBLE   GLYPH_INVIS_OFF
#define GLYPH_UNEXPLORED  GLYPH_UNEXPLORED_OFF
#define GLYPH_NOTHING     GLYPH_NOTHING_OFF

#define warning_to_glyph(mwarnlev) ((mwarnlev) + GLYPH_WARNING_OFF)
#define mon_to_glyph(mon, rng) \
    ((int) what_mon(monsndx((mon)->data), rng)                          \
     + (((mon)->female == 0) ?  GLYPH_MON_MALE_OFF : GLYPH_MON_FEM_OFF))
#define detected_mon_to_glyph(mon, rng) \
    ((int) what_mon(monsndx((mon)->data), rng)                          \
     + (((mon)->female == 0) ?  GLYPH_DETECT_MALE_OFF : GLYPH_DETECT_FEM_OFF))
#define ridden_mon_to_glyph(mon, rng) \
    ((int) what_mon(monsndx((mon)->data), rng)                          \
     + (((mon)->female == 0) ?  GLYPH_RIDDEN_MALE_OFF : GLYPH_RIDDEN_FEM_OFF))
#define pet_to_glyph(mon, rng) \
    ((int) what_mon(monsndx((mon)->data), rng)                          \
     + (((mon)->female == 0) ? GLYPH_PET_MALE_OFF : GLYPH_PET_FEM_OFF))

/* treat unaligned as the default instead of explicitly checking for it;
   altar alignment uses 3 bits with 4 defined values and 4 unused ones */
#define altar_to_glyph(amsk) \
    ((((amsk) & AM_SANCTUM) == AM_SANCTUM)                \
      ? (GLYPH_ALTAR_OFF + altar_other)                   \
      : (((amsk) & AM_MASK) == AM_LAWFUL)                 \
         ? (GLYPH_ALTAR_OFF + altar_lawful)               \
         : (((amsk) & AM_MASK) == AM_NEUTRAL)             \
            ? (GLYPH_ALTAR_OFF + altar_neutral)           \
            : (((amsk) & AM_MASK) == AM_CHAOTIC)          \
               ? (GLYPH_ALTAR_OFF + altar_chaotic)        \
               /* (((amsk) & AM_MASK) == AM_UNALIGNED) */ \
               : (GLYPH_ALTAR_OFF + altar_unaligned))

/* not used, nor is it correct
#define zap_to_glyph(zaptype, cmap_idx) \
    ((((cmap_idx) - S_vbeam) + 1) + GLYPH_ZAP_OFF)
*/

/* EXPL_FIERY is the default explosion type */
#define explosion_to_glyph(expltyp, idx) \
    ((idx) - S_expl_tl                                                  \
     + (((expltyp) == EXPL_FROSTY) ? GLYPH_EXPLODE_FROSTY_OFF           \
        : ((expltyp) == EXPL_MAGICAL) ? GLYPH_EXPLODE_MAGICAL_OFF       \
          : ((expltyp) == EXPL_WET) ? GLYPH_EXPLODE_WET_OFF             \
           : ((expltyp) == EXPL_MUDDY) ? GLYPH_EXPLODE_MUDDY_OFF        \
             : ((expltyp) == EXPL_NOXIOUS) ? GLYPH_EXPLODE_NOXIOUS_OFF  \
               : GLYPH_EXPLODE_FIERY_OFF))

/* cmap_walls_to_glyph(): return the glyph number for specified wall
   symbol; result varies by dungeon branch */
#define cmap_walls_to_glyph(cmap_idx) \
    ((cmap_idx) - S_vwall                               \
     + (In_mines(&u.uz) ? GLYPH_CMAP_MINES_OFF          \
        : In_hell(&u.uz) ? GLYPH_CMAP_GEH_OFF           \
          : Is_knox(&u.uz) ? GLYPH_CMAP_KNOX_OFF        \
            : In_sokoban(&u.uz) ? GLYPH_CMAP_SOKO_OFF   \
              : GLYPH_CMAP_MAIN_OFF))

/* cmap_D0walls_to_glyph(): simpler version of cmap_walls_to_glyph()
   which returns the glyph that would be used in the main dungeon,
   regardless of hero's current location */
#define cmap_D0walls_to_glyph(cmap_idx) \
    ((cmap_idx) - S_vwall + GLYPH_CMAP_MAIN_OFF)

#define cmap_a_to_glyph(cmap_idx) \
    (((cmap_idx) - S_ndoor) + GLYPH_CMAP_A_OFF)

#define cmap_b_to_glyph(cmap_idx) \
    (((cmap_idx) - S_grave) + GLYPH_CMAP_B_OFF)

#define cmap_c_to_glyph(cmap_idx) \
    (((cmap_idx) - S_digbeam) + GLYPH_CMAP_C_OFF)

#define cmap_to_glyph(cmap_idx) \
    ( ((cmap_idx) == S_stone)   ? GLYPH_CMAP_STONE_OFF                      \
    : ((cmap_idx) <= S_trwall)  ? cmap_walls_to_glyph(cmap_idx)             \
    : ((cmap_idx) <  S_altar)   ? cmap_a_to_glyph(cmap_idx)                 \
    : ((cmap_idx) == S_altar)   ? altar_to_glyph(AM_NEUTRAL)                \
    : ((cmap_idx) <  S_arrow_trap + MAXTCHARS) ? cmap_b_to_glyph(cmap_idx)  \
    : ((cmap_idx) <= S_goodpos) ? cmap_c_to_glyph(cmap_idx)                 \
      : NO_GLYPH )

#define trap_to_glyph(trap)                                \
    cmap_to_glyph(trap_to_defsym(((int) (trap)->ttyp)))

#define engraving_to_glyph(ep)                             \
    cmap_to_glyph(engraving_to_defsym(ep))

/* Not affected by hallucination.  Gives a generic body for CORPSE */
/* MRKR: ...and the generic statue */
#define objnum_to_glyph(onum) ((int) (onum) + GLYPH_OBJ_OFF)
#define monnum_to_glyph(mnum,gnd) \
    ((int) (mnum) + (((gnd) == MALE) ? GLYPH_MON_MALE_OFF       \
                                     : GLYPH_MON_FEM_OFF))
#define detected_monnum_to_glyph(mnum,gnd) \
    ((int) (mnum) + (((gnd) == MALE) ? GLYPH_DETECT_MALE_OFF    \
                                     : GLYPH_DETECT_FEM_OFF))
#define ridden_monnum_to_glyph(mnum,gnd) \
    ((int) (mnum) + (((gnd) == MALE) ? GLYPH_RIDDEN_MALE_OFF    \
                                     : GLYPH_RIDDEN_FEM_OFF))
#define petnum_to_glyph(mnum,gnd) \
    ((int) (mnum) + (((gnd) == MALE) ? GLYPH_PET_MALE_OFF       \
                                     : GLYPH_PET_FEM_OFF))

/* The hero's glyph when seen as a monster.
 */
#define hero_glyph \
    monnum_to_glyph((Upolyd || !flags.showrace) ? u.umonnum : gu.urace.mnum, \
                    (Ugender))

/*
 * Change the given glyph into it's given type.  Note:
 *      1) Pets, detected, and ridden monsters are animals and are converted
 *         to the proper monster number.
 *      2) Bodies are all mapped into the generic CORPSE object
 *      3) If handed a glyph out of range for the type, these functions
 *         will return NO_GLYPH (see exception below)
 *      4) glyph_to_swallow() does not return a showsyms[] index, but an
 *         offset from the first swallow symbol.  If handed something
 *         out of range, it will return zero (for lack of anything better
 *         to return).
 */

#define glyph_to_trap(glyph) \
    (glyph_is_trap(glyph)                                                   \
        ? ((int) defsym_to_trap(((glyph) - GLYPH_TRAP_OFF) + S_arrow_trap)) \
        : NO_GLYPH)

#define glyph_is_cmap_main(glyph) \
    ((glyph) >= GLYPH_CMAP_MAIN_OFF                                     \
     && (glyph) < (((S_trwall - S_vwall) +1) + GLYPH_CMAP_MAIN_OFF))
#define glyph_is_cmap_mines(glyph) \
    ((glyph) >= GLYPH_CMAP_MINES_OFF                                    \
     && (glyph) < (((S_trwall - S_vwall) + 1) + GLYPH_CMAP_MINES_OFF))
#define glyph_is_cmap_gehennom(glyph) \
    ((glyph) >= GLYPH_CMAP_GEH_OFF                                      \
     && (glyph) < (((S_trwall - S_vwall) + 1) + GLYPH_CMAP_GEH_OFF))
#define glyph_is_cmap_knox(glyph) \
    ((glyph) >= GLYPH_CMAP_KNOX_OFF                                     \
     && (glyph) < (((S_trwall - S_vwall) + 1) + GLYPH_CMAP_KNOX_OFF))
#define glyph_is_cmap_sokoban(glyph) \
    ((glyph) >= GLYPH_CMAP_SOKO_OFF                                     \
     && (glyph) < (((S_trwall - S_vwall) + 1) + GLYPH_CMAP_SOKO_OFF))
#define glyph_is_cmap_a(glyph) \
    ((glyph) >= GLYPH_CMAP_A_OFF                                        \
     && (glyph) < (((S_brdnladder - S_ndoor) + 1) + GLYPH_CMAP_A_OFF))
#define glyph_is_cmap_altar(glyph) \
    ((glyph) >= GLYPH_ALTAR_OFF && (glyph) < (5 + GLYPH_ALTAR_OFF))
#define glyph_is_cmap_b(glyph) \
    ((glyph) >= GLYPH_CMAP_B_OFF                                        \
     && ((glyph) < ((S_arrow_trap + MAXTCHARS - S_grave) + GLYPH_CMAP_B_OFF)))
#define glyph_is_cmap_zap(glyph) \
    ((glyph) >= GLYPH_ZAP_OFF && (glyph) < ((NUM_ZAP << 2) + GLYPH_ZAP_OFF))
#define glyph_is_cmap_c(glyph) \
    ((glyph) >= GLYPH_CMAP_C_OFF                                        \
     && (glyph) < (((S_goodpos - S_digbeam) + 1) + GLYPH_CMAP_C_OFF))
#define glyph_is_swallow(glyph) \
    ((glyph) >= GLYPH_SWALLOW_OFF                                       \
     && (glyph) < (((NUMMONS << 3) + GLYPH_SWALLOW_OFF)))
#define glyph_is_explosion(glyph) \
    ((glyph) >= GLYPH_EXPLODE_OFF                                       \
     && (glyph) < (MAXEXPCHARS + GLYPH_EXPLODE_FROSTY_OFF))
#if 0   /* this is more precise but expands to a lot of unnecessary code */
#define glyph_is_cmap(glyph) \
    (((glyph) == GLYPH_CMAP_STONE_OFF) \
     || glyph_is_cmap_main(glyph)      \
     || glyph_is_cmap_mines(glyph)     \
     || glyph_is_cmap_gehennom(glyph)  \
     || glyph_is_cmap_knox(glyph)      \
     || glyph_is_cmap_sokoban(glyph)   \
     || glyph_is_cmap_a(glyph)         \
     || glyph_is_cmap_altar(glyph)     \
     || glyph_is_cmap_b(glyph)         \
     || glyph_is_cmap_c(glyph))
#endif
#define glyph_is_cmap(glyph) \
    ((glyph) >= GLYPH_CMAP_STONE_OFF \
     && (glyph) < (GLYPH_CMAP_C_OFF + ((S_goodpos - S_digbeam) + 1)))

/* final MAXPCHARS is legal array index because of trailing fencepost entry */
#define glyph_to_cmap(glyph) \
    (((glyph) == GLYPH_CMAP_STONE_OFF)                                  \
      ? S_stone                                                         \
      : glyph_is_cmap_main(glyph)                                       \
        ? (((glyph) - GLYPH_CMAP_MAIN_OFF) + S_vwall)                   \
        : glyph_is_cmap_mines(glyph)                                    \
          ? (((glyph) - GLYPH_CMAP_MINES_OFF) + S_vwall)                \
          : glyph_is_cmap_gehennom(glyph)                               \
            ? (((glyph) - GLYPH_CMAP_GEH_OFF) + S_vwall)                \
            : glyph_is_cmap_knox(glyph)                                 \
              ? (((glyph) - GLYPH_CMAP_KNOX_OFF) + S_vwall)             \
              : glyph_is_cmap_sokoban(glyph)                            \
                ? (((glyph) - GLYPH_CMAP_SOKO_OFF) + S_vwall)           \
                : glyph_is_cmap_a(glyph)                                \
                  ? (((glyph) - GLYPH_CMAP_A_OFF) + S_ndoor)            \
                  : glyph_is_cmap_altar(glyph)                          \
                    ? (S_altar)                                         \
                    : glyph_is_cmap_b(glyph)                            \
                      ? (((glyph) - GLYPH_CMAP_B_OFF) + S_grave)        \
                      : glyph_is_cmap_c(glyph)                          \
                        ? (((glyph) - GLYPH_CMAP_C_OFF) + S_digbeam)    \
                        : glyph_is_cmap_zap(glyph)                      \
                          ? ((((glyph) - GLYPH_ZAP_OFF) % 4) + S_vbeam) \
                          : MAXPCHARS)

#define glyph_to_swallow(glyph) \
    (glyph_is_swallow(glyph) ? (((glyph) - GLYPH_SWALLOW_OFF) & 0x7) : 0)
#define glyph_to_warning(glyph) \
    (glyph_is_warning(glyph) ? ((glyph) - GLYPH_WARNING_OFF) : NO_GLYPH)

/*
 * Return true if the given glyph is what we want.  Note that bodies are
 * considered objects.
 */
#define glyph_is_normal_male_monster(glyph) \
    ((glyph) >= GLYPH_MON_MALE_OFF && (glyph) < (GLYPH_MON_MALE_OFF + NUMMONS))
#define glyph_is_normal_female_monster(glyph) \
    ((glyph) >= GLYPH_MON_FEM_OFF && (glyph) < (GLYPH_MON_FEM_OFF + NUMMONS))
#define glyph_is_normal_monster(glyph) \
    (glyph_is_normal_male_monster(glyph)                \
     || glyph_is_normal_female_monster(glyph))
#define glyph_is_female_pet(glyph) \
    ((glyph) >= GLYPH_PET_FEM_OFF && (glyph) < (GLYPH_PET_FEM_OFF + NUMMONS))
#define glyph_is_male_pet(glyph) \
    ((glyph) >= GLYPH_PET_MALE_OFF && (glyph) < (GLYPH_PET_MALE_OFF + NUMMONS))
#define glyph_is_pet(glyph) \
    (glyph_is_male_pet(glyph) || glyph_is_female_pet(glyph))
#define glyph_is_ridden_female_monster(glyph) \
    ((glyph) >= GLYPH_RIDDEN_FEM_OFF                    \
     && (glyph) < (GLYPH_RIDDEN_FEM_OFF + NUMMONS))
#define glyph_is_ridden_male_monster(glyph) \
    ((glyph) >= GLYPH_RIDDEN_MALE_OFF                   \
     && (glyph) < (GLYPH_RIDDEN_MALE_OFF + NUMMONS))
#define glyph_is_ridden_monster(glyph) \
    (glyph_is_ridden_male_monster(glyph) \
        || glyph_is_ridden_female_monster(glyph))
#define glyph_is_detected_female_monster(glyph) \
    ((glyph) >= GLYPH_DETECT_FEM_OFF                    \
     && (glyph) < (GLYPH_DETECT_FEM_OFF + NUMMONS))
#define glyph_is_detected_male_monster(glyph) \
    ((glyph) >= GLYPH_DETECT_MALE_OFF                   \
     && (glyph) < (GLYPH_DETECT_MALE_OFF + NUMMONS))
#define glyph_is_detected_monster(glyph) \
    (glyph_is_detected_male_monster(glyph) \
        || glyph_is_detected_female_monster(glyph))
#define glyph_is_monster(glyph)                            \
    (glyph_is_normal_monster(glyph) || glyph_is_pet(glyph) \
     || glyph_is_ridden_monster(glyph) || glyph_is_detected_monster(glyph))
#define glyph_is_invisible(glyph) ((glyph) == GLYPH_INVISIBLE)

/* final NUMMONS is legal array index because of trailing fencepost entry */
#define glyph_to_mon(glyph) \
       (glyph_is_normal_female_monster(glyph)                  \
         ? ((glyph) - GLYPH_MON_FEM_OFF)                       \
         : glyph_is_normal_male_monster(glyph)                 \
           ? ((glyph) - GLYPH_MON_MALE_OFF)                    \
           : glyph_is_female_pet(glyph)                        \
             ? ((glyph) - GLYPH_PET_FEM_OFF)                   \
             : glyph_is_male_pet(glyph)                        \
               ? ((glyph) - GLYPH_PET_MALE_OFF)                \
               : glyph_is_detected_female_monster(glyph)       \
                 ? ((glyph) - GLYPH_DETECT_FEM_OFF)            \
                 : glyph_is_detected_male_monster(glyph)       \
                   ? ((glyph) - GLYPH_DETECT_MALE_OFF)         \
                   : glyph_is_ridden_female_monster(glyph)     \
                     ? ((glyph) - GLYPH_RIDDEN_FEM_OFF)        \
                     : glyph_is_ridden_male_monster(glyph)     \
                       ? ((glyph) - GLYPH_RIDDEN_MALE_OFF)     \
                       : NUMMONS)

/* boulder hides pile except when on top of another boulder;
   the otg_otmp assignment might occur multiple times in the same
   expression but there will always be sequence points in between */
#define obj_is_piletop(obj) \
    ((obj)->where == OBJ_FLOOR                                  \
     && (go.otg_otmp = gl.level.objects[(obj)->ox][(obj)->oy]->nexthere) != 0 \
     && ((obj)->otyp != BOULDER || go.otg_otmp->otyp == BOULDER))
/* used to hide info such as potion and gem color when not seen yet;
   stones and rock are excluded for gem class; LAST_SPELL includes blank
   spellbook but excludes novel and the Book of the Dead */
#define obj_is_generic(obj) \
    (!(obj)->dknown                                             \
     && ((obj)->oclass == POTION_CLASS                          \
         || ((obj)->otyp >= FIRST_REAL_GEM                      \
             && ((obj)->otyp <= LAST_GLASS_GEM))                \
         || ((obj)->otyp >= FIRST_SPELL                         \
             && ((obj)->otyp <= LAST_SPELL))))

#define glyph_is_body_piletop(glyph) \
    (((glyph) >= GLYPH_BODY_PILETOP_OFF)                        \
     && ((glyph) < (GLYPH_BODY_PILETOP_OFF + NUMMONS)))
#define glyph_is_body(glyph) \
    ((((glyph) >= GLYPH_BODY_OFF) && ((glyph) < (GLYPH_BODY_OFF + NUMMONS))) \
     || glyph_is_body_piletop(glyph))

#define glyph_is_fem_statue_piletop(glyph) \
    (((glyph) >= GLYPH_STATUE_FEM_PILETOP_OFF)                  \
      && ((glyph) < (GLYPH_STATUE_FEM_PILETOP_OFF + NUMMONS)))
#define glyph_is_male_statue_piletop(glyph) \
    (((glyph) >= GLYPH_STATUE_MALE_PILETOP_OFF)                 \
         && ((glyph) < (GLYPH_STATUE_MALE_PILETOP_OFF + NUMMONS)))
#define glyph_is_fem_statue(glyph) \
    ((((glyph) >= GLYPH_STATUE_FEM_OFF)                         \
      && ((glyph) < (GLYPH_STATUE_FEM_OFF + NUMMONS)))          \
     || glyph_is_fem_statue_piletop(glyph))
#define glyph_is_male_statue(glyph) \
    ((((glyph) >= GLYPH_STATUE_MALE_OFF)                        \
      && ((glyph) < (GLYPH_STATUE_MALE_OFF + NUMMONS)))         \
     || glyph_is_male_statue_piletop(glyph))
#define glyph_is_statue(glyph) \
    (glyph_is_male_statue(glyph) || glyph_is_fem_statue(glyph))
/* generic objects are after strange object (GLYPH_OBJ_OFF) and before
   other objects (GLYPH_OBJ_OFF + FIRST_OBJECT) */
#define glyph_is_normal_generic_obj(glyph) \
    ((glyph) > GLYPH_OBJ_OFF && (glyph) < GLYPH_OBJ_OFF + FIRST_OBJECT)
#define glyph_is_piletop_generic_obj(glyph) \
    ((glyph) > GLYPH_OBJ_PILETOP_OFF                            \
     && (glyph) < GLYPH_OBJ_PILETOP_OFF + FIRST_OBJECT)
#define glyph_is_generic_object(glyph) \
    (glyph_is_normal_generic_obj(glyph)                         \
     || glyph_is_piletop_generic_obj(glyph))
#define glyph_is_normal_piletop_obj(glyph) \
    ((glyph) == GLYPH_OBJ_PILETOP_OFF                           \
     || ((glyph) > GLYPH_OBJ_PILETOP_OFF + FIRST_OBJECT         \
         && (glyph) < (GLYPH_OBJ_PILETOP_OFF + NUM_OBJECTS)))
#define glyph_is_normal_object(glyph) \
    ((glyph) == GLYPH_OBJ_OFF                                   \
     || ((glyph) >= GLYPH_OBJ_OFF + FIRST_OBJECT                \
         && (glyph) < (GLYPH_OBJ_OFF + NUM_OBJECTS))            \
     || glyph_is_normal_piletop_obj(glyph))

#if 0   /* [note: out of date] */
#define glyph_is_object(glyph) \
  (   (((glyph) >= GLYPH_OBJ_OFF)                                       \
       && ((glyph) < (GLYPH_OBJ_OFF + NUM_OBJECTS)))                    \
   || (((glyph) >= GLYPH_OBJ_PILETOP_OFF)                               \
       && ((glyph) < (GLYPH_OBJ_PILETOP_OFF + NUM_OBJECTS)))            \
   || (((glyph) >= GLYPH_STATUE_MALE_OFF)                               \
       && ((glyph) < (GLYPH_STATUE_MALE_OFF + NUMMONS)))                \
   || (((glyph) >= GLYPH_STATUE_MALE_PILETOP_OFF)                       \
       && ((glyph) < (GLYPH_STATUE_MALE_PILETOP_OFF + NUMMONS)))        \
   || (((glyph) >= GLYPH_STATUE_FEM_OFF)                                \
       && ((glyph) < (GLYPH_STATUE_FEM_OFF + NUMMONS)))                 \
   || (((glyph) >= GLYPH_STATUE_FEM_PILETOP_OFF)                        \
       && ((glyph) < (GLYPH_STATUE_FEM_PILETOP_OFF + NUMMONS)))         \
   || (((glyph) >= GLYPH_BODY_OFF)                                      \
       && ((glyph) < (GLYPH_BODY_OFF + NUMMONS)))                       \
   || (((glyph) >= GLYPH_BODY_PILETOP_OFF)                              \
       && ((glyph) < (GLYPH_BODY_PILETOP_OFF + NUMMONS)))               \
  )
#endif
#define glyph_is_object(glyph) \
    (glyph_is_normal_object(glyph) || glyph_is_generic_object(glyph)    \
     || glyph_is_statue(glyph) || glyph_is_body(glyph))

/* briefly used for Qt's "paper doll" inventory which shows map tiles for
   equipped objects; those vary like floor items during hallucination now
   so this isn't used anywhere [note: out of date since generic objs added] */
#define obj_to_true_glyph(obj)                                  \
    (((obj)->otyp == STATUE)                                    \
     ? ((int) (obj)->corpsenm                                   \
        + (((obj)->spe & CORPSTAT_GENDER) == CORPSTAT_FEMALE)   \
             ? (obj_is_piletop(obj)                             \
                ? (GLYPH_STATUE_FEM_PILETOP_OFF)                \
                : (GLYPH_STATUE_FEM_OFF))                       \
             : (obj_is_piletop(obj)                             \
                ? (GLYPH_STATUE_MALE_PILETOP_OFF)               \
                : (GLYPH_STATUE_MALE_OFF))                      \
     : (((obj)->otyp == CORPSE)                                 \
         ? ((int) (obj)->corpsenm                               \
            + (obj_is_piletop(obj)                              \
                ? (GLYPH_BODY_PILETOP_OFF)                      \
                ? (GLYPH_BODY_OFF)))                            \
         : ((int) (obj)->otyp + GLYPH_OBJ_OFF))))

/* final NUM_OBJECTS is legal array idx because of trailing fencepost entry */
#define glyph_to_obj(glyph) \
    (glyph_is_body(glyph) ? CORPSE                              \
     : glyph_is_statue(glyph) ? STATUE                          \
       : glyph_is_generic_object(glyph)                         \
         ? ((glyph) - (glyph_is_piletop_generic_obj(glyph)      \
                       ? GLYPH_OBJ_PILETOP_OFF                  \
                       : GLYPH_OBJ_OFF))                        \
         : glyph_is_normal_object(glyph)                        \
           ? ((glyph) - (glyph_is_normal_piletop_obj(glyph)     \
                         ? GLYPH_OBJ_PILETOP_OFF                \
                         : GLYPH_OBJ_OFF))                      \
           : NUM_OBJECTS)

#define glyph_to_body_corpsenm(glyph) \
    (glyph_is_body_piletop(glyph)                       \
       ? ((glyph) - GLYPH_BODY_PILETOP_OFF)             \
       : ((glyph) - GLYPH_BODY_OFF))

#define glyph_to_statue_corpsenm(glyph) \
    (glyph_is_fem_statue_piletop(glyph)                 \
       ? ((glyph) - GLYPH_STATUE_FEM_PILETOP_OFF)       \
       : glyph_is_male_statue_piletop(glyph)            \
           ? ((glyph) - GLYPH_STATUE_MALE_PILETOP_OFF)  \
           : glyph_is_fem_statue(glyph)                 \
               ? ((glyph) - GLYPH_STATUE_FEM_OFF)       \
               : glyph_is_male_statue(glyph)            \
                   ? ((glyph) - GLYPH_STATUE_MALE_OFF)  \
                   : NO_GLYPH)

/* This has the unfortunate side effect of needing a global variable
   to store a result. 'otg_temp' is defined and declared in decl.{ch}.  */
#define random_obj_to_glyph(rng) \
    (((go.otg_temp = random_object(rng)) == CORPSE)     \
         ? (random_monster(rng) + GLYPH_BODY_OFF)       \
         : (go.otg_temp + GLYPH_OBJ_OFF))
#define corpse_to_glyph(obj) \
    ((int) ((obj)->corpsenm                             \
          + (obj_is_piletop(obj) ? GLYPH_BODY_PILETOP_OFF : GLYPH_BODY_OFF)))
#define generic_obj_to_glyph(obj) \
    ((int) ((obj)->oclass                               \
            + (obj_is_piletop(obj) ? GLYPH_OBJ_PILETOP_OFF : GLYPH_OBJ_OFF)))
#define normal_obj_to_glyph(obj) \
    ((int) ((obj)->otyp                                 \
            + (obj_is_piletop(obj) ? GLYPH_OBJ_PILETOP_OFF : GLYPH_OBJ_OFF)))

/* MRKR: Statues now have glyphs corresponding to the monster they    */
/*       represent and look like monsters when you are hallucinating. */

#define statue_to_glyph(obj, rng) \
    ((Hallucination)                                                \
     ? ((random_monster(rng))                                       \
        + ((!(rng)(2)) ? GLYPH_MON_MALE_OFF : GLYPH_MON_FEM_OFF))   \
     : ((int) (obj)->corpsenm                                       \
        + ((((obj)->spe & CORPSTAT_GENDER) == CORPSTAT_FEMALE)      \
           ? (obj_is_piletop(obj)                                   \
              ? GLYPH_STATUE_FEM_PILETOP_OFF                        \
              : GLYPH_STATUE_FEM_OFF)                               \
           : (obj_is_piletop(obj)                                   \
              ? GLYPH_STATUE_MALE_PILETOP_OFF                       \
              : GLYPH_STATUE_MALE_OFF))))

#define obj_to_glyph(obj, rng) \
    (((obj)->otyp == STATUE) ? statue_to_glyph(obj, rng)            \
     : (Hallucination) ? random_obj_to_glyph(rng)                   \
       : ((obj)->otyp == CORPSE) ? corpse_to_glyph(obj)             \
         : obj_is_generic(obj) ? generic_obj_to_glyph(obj)          \
           : normal_obj_to_glyph(obj))

#define GLYPH_TRAP_OFF \
    (GLYPH_CMAP_B_OFF + (S_arrow_trap - S_grave))
#define glyph_is_trap(glyph) \
    ((glyph) >= (GLYPH_TRAP_OFF)                        \
     && (glyph) < ((GLYPH_TRAP_OFF) + MAXTCHARS))
#define glyph_is_warning(glyph) \
    ((glyph) >= GLYPH_WARNING_OFF                       \
     && (glyph) < (GLYPH_WARNING_OFF + WARNCOUNT))
#define glyph_is_unexplored(glyph) ((glyph) == GLYPH_UNEXPLORED)
#define glyph_is_nothing(glyph) ((glyph) == GLYPH_NOTHING)

#if 0
#define glyph_is_piletop(glyph) \
    (glyph_is_body_piletop(glyph)           \
     || glyph_is_statue_piletop(glyph)      \
     || glyph_is_piletop_generic_obj(glyph) \
     || glyph_is_obj_piletop(glyph))
#endif

/* mgflags for altering map_glyphinfo() internal behavior */
#define MG_FLAG_NORMAL     0x00
#define MG_FLAG_NOOVERRIDE 0x01 /* disregard accessibility override values */

/* Special mapped glyphflags encoded by reset_glyphmap() + map_glyphinfo() */
#define MG_HERO    0x00001  /* represents the hero */
#define MG_CORPSE  0x00002  /* represents a body */
#define MG_INVIS   0x00004  /* represents invisible monster */
#define MG_DETECT  0x00008  /* represents a detected monster */
#define MG_PET     0x00010  /* represents a pet */
#define MG_RIDDEN  0x00020  /* represents a ridden monster */
#define MG_STATUE  0x00040  /* represents a statue */
#define MG_OBJPILE 0x00080  /* more than one stack of objects */
#define MG_BW_LAVA 0x00100  /* 'black & white lava': highlight lava if it
                             * can't be distinguished from water by color */
#define MG_BW_ICE  0x00200  /* similar for ice vs floor */
#define MG_BW_SINK 0x00200  /* identical for sink vs fountain [note: someday
                             * this may become a distinct flag */
#define MG_BW_ENGR 0x00200  /* likewise for corridor engravings */
#define MG_NOTHING 0x00400  /* char represents GLYPH_NOTHING */
#define MG_UNEXPL  0x00800  /* char represents GLYPH_UNEXPLORED */
#define MG_MALE    0x01000  /* represents a male mon or statue of one */
#define MG_FEMALE  0x02000  /* represents a female mon or statue of one */
#define MG_BADXY   0x04000  /* bad coordinates were passed */

/* docrt(): re-draw whole screen; docrt_flags(): docrt() with more control */
enum docrt_flags_bits {
    docrtRecalc  = 0, /* full docrt(), recalculate what the map should show */
    docrtRefresh = 1, /* redraw_map(), draw what we think the map shows */
    docrtMapOnly = 2, /* ORed with Recalc or Refresh; draw the map but not
                       * status or perminv */
};

typedef struct {
    xint8 gnew; /* perhaps move this bit into the rm structure. */
    glyph_info glyphinfo;
} gbuf_entry;

#ifdef TEXTCOLOR
extern const int altarcolors[];
extern const int zapcolors[];
extern const int explodecolors[];
extern int wallcolors[];
#endif

/* If TILES_IN_GLYPHMAP is defined during build, this is defined
 * in the generated tile.c, complete with appropriate tile references in
 * the initialization.  Otherwise, it gets defined in display.c.
 */
extern const glyph_info nul_glyphinfo;

#endif /* DISPLAY_H */
