Source:NetHack 3.6.1/src/botl.c

From NetHackWiki
(Redirected from Source:Ref/bot)
Jump to navigation Jump to search

Below is the full text to botl.c from the source code of NetHack 3.6.1. To link to a particular line, write [[Source:NetHack 3.6.1/src/botl.c#line123]], for example.

Top of file

 /* NetHack 3.6	botl.c	$NHDT-Date: 1506903619 2017/10/02 00:20:19 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.81 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /*-Copyright (c) Michael Allison, 2006. */
 /* NetHack may be freely redistributed.  See license for details. */

The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

 
 #include "hack.h"
 #include <limits.h>
 
 extern const char *hu_stat[]; /* defined in eat.c */
 
 const char *const enc_stat[] = { "",         "Burdened",  "Stressed",
                                  "Strained", "Overtaxed", "Overloaded" };
 
 STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */
 STATIC_DCL const char *NDECL(rank);
 #ifdef STATUS_HILITES
 STATIC_DCL void NDECL(bot_via_windowport);
 #endif
 

get_strength_str

 static char *
 get_strength_str()
 {
     static char buf[32];
     int st = ACURR(A_STR);
 
     if (st > 18) {
         if (st > STR18(100))
             Sprintf(buf, "%2d", st - 100);
         else if (st < STR18(100))
             Sprintf(buf, "18/%02d", st - 18);
         else
             Sprintf(buf, "18/**");
     } else
         Sprintf(buf, "%-1d", st);
 
     return buf;
 }
 

do_statusline1

 char *
 do_statusline1()
 {
     static char newbot1[BUFSZ];
     register char *nb;
     register int i, j;
 
     Strcpy(newbot1, plname);
     if ('a' <= newbot1[0] && newbot1[0] <= 'z')
         newbot1[0] += 'A' - 'a';
     newbot1[10] = 0;
     Sprintf(nb = eos(newbot1), " the ");
 
     if (Upolyd) {
         char mbot[BUFSZ];
         int k = 0;
 
         Strcpy(mbot, mons[u.umonnum].mname);
         while (mbot[k] != 0) {
             if ((k == 0 || (k > 0 && mbot[k - 1] == ' ')) && 'a' <= mbot[k]
                 && mbot[k] <= 'z')
                 mbot[k] += 'A' - 'a';
             k++;
         }
         Strcpy(nb = eos(nb), mbot);
     } else
         Strcpy(nb = eos(nb), rank());
 
     Sprintf(nb = eos(nb), "  ");
     i = mrank_sz + 15;
     j = (int) ((nb + 2) - newbot1); /* strlen(newbot1) but less computation */
     if ((i - j) > 0)
         Sprintf(nb = eos(nb), "%*s", i - j, " "); /* pad with spaces */
 
     Sprintf(nb = eos(nb), "St:%s Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d",
             get_strength_str(),
             ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS),
             ACURR(A_CHA));
     Sprintf(nb = eos(nb),
             (u.ualign.type == A_CHAOTIC)
                 ? "  Chaotic"
                 : (u.ualign.type == A_NEUTRAL) ? "  Neutral" : "  Lawful");
 #ifdef SCORE_ON_BOTL
     if (flags.showscore)
         Sprintf(nb = eos(nb), " S:%ld", botl_score());
 #endif
     return newbot1;
 }
 

check_gold_symbol

 void
 check_gold_symbol()
 {
     int goldch, goldoc;
     unsigned int goldos;
     int goldglyph = objnum_to_glyph(GOLD_PIECE);
     (void) mapglyph(goldglyph, &goldch, &goldoc, &goldos, 0, 0);
     iflags.invis_goldsym = ((char)goldch <= ' ');
 }
 

do_statusline2

 char *
 do_statusline2()
 {
     static char newbot2[BUFSZ], /* MAXCO: botl.h */
          /* dungeon location (and gold), hero health (HP, PW, AC),
             experience (HD if poly'd, else Exp level and maybe Exp points),
             time (in moves), varying number of status conditions */
          dloc[QBUFSZ], hlth[QBUFSZ], expr[QBUFSZ], tmmv[QBUFSZ], cond[QBUFSZ];
     register char *nb;
     unsigned dln, dx, hln, xln, tln, cln;
     int hp, hpmax, cap;
     long money;
 
     /*
      * Various min(x,9999)'s are to avoid having excessive values
      * violate the field width assumptions in botl.h and should not
      * impact normal play.  Particularly 64-bit long for gold which
      * could require many more digits if someone figures out a way
      * to get and carry a really large (or negative) amount of it.
      * Turn counter is also long, but we'll risk that.
      */
 
     /* dungeon location plus gold */
     (void) describe_level(dloc); /* includes at least one trailing space */
     if ((money = money_cnt(invent)) < 0L)
         money = 0L; /* ought to issue impossible() and then discard gold */
     Sprintf(eos(dloc), "%s:%-2ld", /* strongest hero can lift ~300000 gold */
             (iflags.in_dumplog || iflags.invis_goldsym) ? "$"
             : encglyph(objnum_to_glyph(GOLD_PIECE)),
             min(money, 999999L));
     dln = strlen(dloc);
     /* '$' encoded as \GXXXXNNNN is 9 chars longer than display will need */
     dx = strstri(dloc, "\\G") ? 9 : 0;
 
     /* health and armor class (has trailing space for AC 0..9) */
     hp = Upolyd ? u.mh : u.uhp;
     hpmax = Upolyd ? u.mhmax : u.uhpmax;
     if (hp < 0)
         hp = 0;
     Sprintf(hlth, "HP:%d(%d) Pw:%d(%d) AC:%-2d",
             min(hp, 9999), min(hpmax, 9999),
             min(u.uen, 9999), min(u.uenmax, 9999), u.uac);
     hln = strlen(hlth);
 
     /* experience */
     if (Upolyd)
         Sprintf(expr, "HD:%d", mons[u.umonnum].mlevel);
     else if (flags.showexp)
         Sprintf(expr, "Xp:%u/%-1ld", u.ulevel, u.uexp);
     else
         Sprintf(expr, "Exp:%u", u.ulevel);
     xln = strlen(expr);
 
     /* time/move counter */
     if (flags.time)
         Sprintf(tmmv, "T:%ld", moves);
     else
         tmmv[0] = '\0';
     tln = strlen(tmmv);
 
     /* status conditions; worst ones first */
     cond[0] = '\0'; /* once non-empty, cond will have a leading space */
     nb = cond;
     /*
      * Stoned, Slimed, Strangled, and both types of Sick are all fatal
      * unless remedied before timeout expires.  Should we order them by
      * shortest time left?  [Probably not worth the effort, since it's
      * unusual for more than one of them to apply at a time.]
      */
     if (Stoned)
         Strcpy(nb = eos(nb), " Stone");
     if (Slimed)
         Strcpy(nb = eos(nb), " Slime");
     if (Strangled)
         Strcpy(nb = eos(nb), " Strngl");
     if (Sick) {
         if (u.usick_type & SICK_VOMITABLE)
             Strcpy(nb = eos(nb), " FoodPois");
         if (u.usick_type & SICK_NONVOMITABLE)
             Strcpy(nb = eos(nb), " TermIll");
     }
     if (u.uhs != NOT_HUNGRY)
         Sprintf(nb = eos(nb), " %s", hu_stat[u.uhs]);
     if ((cap = near_capacity()) > UNENCUMBERED)
         Sprintf(nb = eos(nb), " %s", enc_stat[cap]);
     if (Blind)
         Strcpy(nb = eos(nb), " Blind");
     if (Deaf)
         Strcpy(nb = eos(nb), " Deaf");
     if (Stunned)
         Strcpy(nb = eos(nb), " Stun");
     if (Confusion)
         Strcpy(nb = eos(nb), " Conf");
     if (Hallucination)
         Strcpy(nb = eos(nb), " Hallu");
     /* levitation and flying are mutually exclusive; riding is not */
     if (Levitation)
         Strcpy(nb = eos(nb), " Lev");
     if (Flying)
         Strcpy(nb = eos(nb), " Fly");
     if (u.usteed)
         Strcpy(nb = eos(nb), " Ride");
     cln = strlen(cond);
 
     /*
      * Put the pieces together.  If they all fit, keep the traditional
      * sequence.  Otherwise, move least important parts to the end in
      * case the interface side of things has to truncate.  Note that
      * dloc[] contains '$' encoded in ten character sequence \GXXXXNNNN
      * so we want to test its display length rather than buffer length.
      *
      * We don't have an actual display limit here, so have to go by the
      * width of the map.  Since we're reordering rather than truncating,
      * wider displays can still show wider status than the map if the
      * interface supports that.
      */
     if ((dln - dx) + 1 + hln + 1 + xln + 1 + tln + 1 + cln <= COLNO) {
         Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, expr, tmmv, cond);
     } else {
         if (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1 > MAXCO) {
             panic("bot2: second status line exceeds MAXCO (%u > %d)",
                   (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1), MAXCO);
         } else if ((dln - dx) + 1 + hln + 1 + xln + 1 + cln <= COLNO) {
             Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, expr, cond, tmmv);
         } else if ((dln - dx) + 1 + hln + 1 + cln <= COLNO) {
             Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, cond, expr, tmmv);
         } else {
             Sprintf(newbot2, "%s %s %s %s %s", hlth, cond, dloc, expr, tmmv);
         }
         /* only two or three consecutive spaces available to squeeze out */
         mungspaces(newbot2);
     }
     return newbot2;
 }
 

bot

 void
 bot()
 {
     if (youmonst.data && iflags.status_updates) {
 #ifdef STATUS_HILITES
         bot_via_windowport();
 #else
         curs(WIN_STATUS, 1, 0);
         putstr(WIN_STATUS, 0, do_statusline1());
         curs(WIN_STATUS, 1, 1);
         putmixed(WIN_STATUS, 0, do_statusline2());
 #endif
     }
     context.botl = context.botlx = 0;
 }
 

xlev_to_rank

 /* convert experience level (1..30) to rank index (0..8) */
 int
 xlev_to_rank(xlev)
 int xlev;
 {
     return (xlev <= 2) ? 0 : (xlev <= 30) ? ((xlev + 2) / 4) : 8;
 }
 

rank_to_xlev

 #if 0 /* not currently needed */
 /* convert rank index (0..8) to experience level (1..30) */
 int
 rank_to_xlev(rank)
 int rank;
 {
     return (rank <= 0) ? 1 : (rank <= 8) ? ((rank * 4) - 2) : 30;
 }
 #endif
 

rank_of

 const char *
 rank_of(lev, monnum, female)
 int lev;
 short monnum;
 boolean female;
 {
     register const struct Role *role;
     register int i;
 
     /* Find the role */
     for (role = roles; role->name.m; role++)
         if (monnum == role->malenum || monnum == role->femalenum)
             break;
     if (!role->name.m)
         role = &urole;
 
     /* Find the rank */
     for (i = xlev_to_rank((int) lev); i >= 0; i--) {
         if (female && role->rank[i].f)
             return role->rank[i].f;
         if (role->rank[i].m)
             return role->rank[i].m;
     }
 
     /* Try the role name, instead */
     if (female && role->name.f)
         return role->name.f;
     else if (role->name.m)
         return role->name.m;
     return "Player";
 }
 

rank

 STATIC_OVL const char *
 rank()
 {
     return rank_of(u.ulevel, Role_switch, flags.female);
 }
 

title_to_mon

 int
 title_to_mon(str, rank_indx, title_length)
 const char *str;
 int *rank_indx, *title_length;
 {
     register int i, j;
 
     /* Loop through each of the roles */
     for (i = 0; roles[i].name.m; i++)
         for (j = 0; j < 9; j++) {
             if (roles[i].rank[j].m
                 && !strncmpi(str, roles[i].rank[j].m,
                              strlen(roles[i].rank[j].m))) {
                 if (rank_indx)
                     *rank_indx = j;
                 if (title_length)
                     *title_length = strlen(roles[i].rank[j].m);
                 return roles[i].malenum;
             }
             if (roles[i].rank[j].f
                 && !strncmpi(str, roles[i].rank[j].f,
                              strlen(roles[i].rank[j].f))) {
                 if (rank_indx)
                     *rank_indx = j;
                 if (title_length)
                     *title_length = strlen(roles[i].rank[j].f);
                 return (roles[i].femalenum != NON_PM) ? roles[i].femalenum
                                                       : roles[i].malenum;
             }
         }
     return NON_PM;
 }
 

max_rank_sz

 void
 max_rank_sz()
 {
     register int i, r, maxr = 0;
     for (i = 0; i < 9; i++) {
         if (urole.rank[i].m && (r = strlen(urole.rank[i].m)) > maxr)
             maxr = r;
         if (urole.rank[i].f && (r = strlen(urole.rank[i].f)) > maxr)
             maxr = r;
     }
     mrank_sz = maxr;
     return;
 }
 

botl_score

 #ifdef SCORE_ON_BOTL
 long
 botl_score()
 {
     long deepest = deepest_lev_reached(FALSE);
     long utotal;
 
     utotal = money_cnt(invent) + hidden_gold();
     if ((utotal -= u.umoney0) < 0L)
         utotal = 0L;
     utotal += u.urexp + (50 * (deepest - 1))
           + (deepest > 30 ? 10000 : deepest > 20 ? 1000 * (deepest - 20) : 0);
     if (utotal < u.urexp)
         utotal = LONG_MAX; /* wrap around */
     return utotal;
 }
 #endif /* SCORE_ON_BOTL */
 

describe_level

 /* provide the name of the current level for display by various ports */
 int
 describe_level(buf)
 char *buf;
 {
     int ret = 1;
 
     /* TODO:    Add in dungeon name */
     if (Is_knox(&u.uz))
         Sprintf(buf, "%s ", dungeons[u.uz.dnum].dname);
     else if (In_quest(&u.uz))
         Sprintf(buf, "Home %d ", dunlev(&u.uz));
     else if (In_endgame(&u.uz))
         Sprintf(buf, Is_astralevel(&u.uz) ? "Astral Plane " : "End Game ");
     else {
         /* ports with more room may expand this one */
         Sprintf(buf, "Dlvl:%-2d ", depth(&u.uz));
         ret = 0;
     }
     return ret;
 }
 
 /* =======================================================================*/
 /*  statusnew routines                                                    */
 /* =======================================================================*/
 
 /* structure that tracks the status details in the core */
 
 #ifdef STATUS_HILITES
 struct hilite_s {
     enum statusfields fld;
     boolean set;
     unsigned anytype;
     anything value;
     int behavior;
     char textmatch[QBUFSZ];
     enum relationships rel;
     int coloridx;
     struct hilite_s *next;
 };
 
 struct condmap {
     const char *id;
     unsigned long bitmask;
 };
 #endif /* STATUS_HILITES */
 
 struct istat_s {
     const char *fldname;
     const char *fldfmt;
     long time;  /* moves when this field hilite times out */
     boolean chg; /* need to recalc time? */
     unsigned anytype;
     anything a;
     char *val;
     int valwidth;
     enum statusfields idxmax;
     enum statusfields fld;
 #ifdef STATUS_HILITES
     struct hilite_s *thresholds;
 #endif
 };
 
 
 STATIC_DCL void NDECL(init_blstats);
 STATIC_DCL char *FDECL(anything_to_s, (char *, anything *, int));
 STATIC_OVL int FDECL(percentage, (struct istat_s *, struct istat_s *));
 STATIC_OVL int FDECL(compare_blstats, (struct istat_s *, struct istat_s *));
 STATIC_DCL boolean FDECL(evaluate_and_notify_windowport_field,
                          (int, boolean *, int, int));
 STATIC_DCL void FDECL(evaluate_and_notify_windowport, (boolean *, int, int));
 
 #ifdef STATUS_HILITES
 STATIC_DCL boolean FDECL(hilite_reset_needed, (struct istat_s *, long));
 STATIC_DCL void FDECL(s_to_anything, (anything *, char *, int));
 STATIC_DCL boolean FDECL(is_ltgt_percentnumber, (const char *));
 STATIC_DCL boolean FDECL(has_ltgt_percentnumber, (const char *));
 STATIC_DCL boolean FDECL(parse_status_hl2, (char (*)[QBUFSZ],BOOLEAN_P));
 STATIC_DCL boolean FDECL(parse_condition, (char (*)[QBUFSZ], int));
 STATIC_DCL void FDECL(merge_bestcolor, (int *, int));
 STATIC_DCL void FDECL(get_hilite_color, (int, int, genericptr_t, int,
                                                 int, int *));
 STATIC_DCL unsigned long FDECL(match_str2conditionbitmask, (const char *));
 STATIC_DCL unsigned long FDECL(str2conditionbitmask, (char *));
 STATIC_DCL void FDECL(split_clridx, (int, int *, int *));
 STATIC_DCL char *FDECL(hlattr2attrname, (int, char *, int));
 STATIC_DCL void FDECL(status_hilite_linestr_add, (int, struct hilite_s *,
                                             unsigned long, const char *));
 
 STATIC_DCL void NDECL(status_hilite_linestr_done);
 STATIC_DCL int FDECL(status_hilite_linestr_countfield, (int));
 STATIC_DCL void NDECL(status_hilite_linestr_gather_conditions);
 STATIC_DCL void NDECL(status_hilite_linestr_gather);
 STATIC_DCL char *FDECL(status_hilite2str, (struct hilite_s *));
 STATIC_DCL boolean FDECL(status_hilite_menu_add, (int));
 #define has_hilite(i) (blstats[0][(i)].thresholds)
 #endif
 
 #define INIT_BLSTAT(name, fmtstr, anytyp, wid, fld)                     \
     { name, fmtstr, 0L, FALSE, anytyp,  { (genericptr_t) 0 }, (char *) 0, \
       wid,  -1, fld }
 #define INIT_BLSTATP(name, fmtstr, anytyp, wid, maxfld, fld)            \
     { name, fmtstr, 0L, FALSE, anytyp,  { (genericptr_t) 0 }, (char *) 0, \
       wid,  maxfld, fld }
 
 /* If entries are added to this, botl.h will require updating too */
 STATIC_DCL struct istat_s initblstats[MAXBLSTATS] = {
     INIT_BLSTAT("title", "%s", ANY_STR, 80, BL_TITLE),
     INIT_BLSTAT("strength", " St:%s", ANY_INT, 10, BL_STR),
     INIT_BLSTAT("dexterity", " Dx:%s", ANY_INT,  10, BL_DX),
     INIT_BLSTAT("constitution", " Co:%s", ANY_INT, 10, BL_CO),
     INIT_BLSTAT("intelligence", " In:%s", ANY_INT, 10, BL_IN),
     INIT_BLSTAT("wisdom", " Wi:%s", ANY_INT, 10, BL_WI),
     INIT_BLSTAT("charisma", " Ch:%s", ANY_INT, 10, BL_CH),
     INIT_BLSTAT("alignment", " %s", ANY_STR, 40, BL_ALIGN),
     INIT_BLSTAT("score", " S:%s", ANY_LONG, 20, BL_SCORE),
     INIT_BLSTAT("carrying-capacity", " %s", ANY_LONG, 20, BL_CAP),
     INIT_BLSTAT("gold", " %s", ANY_LONG, 30, BL_GOLD),
     INIT_BLSTATP("power", " Pw:%s", ANY_INT, 10, BL_ENEMAX, BL_ENE),
     INIT_BLSTAT("power-max", "(%s)", ANY_INT, 10, BL_ENEMAX),
     INIT_BLSTAT("experience-level", " Xp:%s", ANY_LONG, 10, BL_XP),
     INIT_BLSTAT("armor-class", " AC:%s", ANY_INT, 10, BL_AC),
     INIT_BLSTAT("HD", " HD:%s", ANY_INT, 10, BL_HD),
     INIT_BLSTAT("time", " T:%s", ANY_INT, 20, BL_TIME),
     INIT_BLSTAT("hunger", " %s", ANY_UINT, 40, BL_HUNGER),
     INIT_BLSTATP("hitpoints", " HP:%s", ANY_INT, 10, BL_HPMAX, BL_HP),
     INIT_BLSTAT("hitpoints-max", "(%s)", ANY_INT, 10, BL_HPMAX),
     INIT_BLSTAT("dungeon-level", "%s", ANY_STR, 80, BL_LEVELDESC),
     INIT_BLSTAT("experience", "/%s", ANY_LONG, 20, BL_EXP),
     INIT_BLSTAT("condition", "%s", ANY_MASK32, 0, BL_CONDITION)
 };
 
 #undef INIT_BLSTATP
 #undef INIT_BLSTAT
 
 struct istat_s blstats[2][MAXBLSTATS];
 static boolean blinit = FALSE, update_all = FALSE;
 static boolean valset[MAXBLSTATS];
 unsigned long blcolormasks[CLR_MAX];
 static long bl_hilite_moves = 0L;
 
 /* we don't put this next declaration in #ifdef STATUS_HILITES.
  * In the absence of STATUS_HILITES, each array
  * element will be 0 however, and quite meaningless,
  * but we need to pass the first array element as
  * the final argument of status_update, with or
  * without STATUS_HILITES.
  */
 unsigned long cond_hilites[BL_ATTCLR_MAX];
 

bot_via_windowport

 void
 bot_via_windowport()
 {
     char buf[BUFSZ];
     register char *nb;
     static int i, idx = 0, idx_p, cap;
     long money;
 
     if (!blinit)
         panic("bot before init.");
 
     idx_p = idx;
     idx = 1 - idx; /* 0 -> 1, 1 -> 0 */
 
     /* clear the "value set" indicators */
     (void) memset((genericptr_t) valset, 0, MAXBLSTATS * sizeof (boolean));
 
     /*
      * Note: min(x,9999) - we enforce the same maximum on hp, maxhp,
      * pw, maxpw, and gold as basic status formatting so that the two
      * modes of status display don't produce different information.
      */
 
     /*
      *  Player name and title.
      */
     Strcpy(nb = buf, plname);
     nb[0] = highc(nb[0]);
     nb[10] = '\0';
     Sprintf(nb = eos(nb), " the ");
     if (Upolyd) {
         for (i = 0, nb = strcpy(eos(nb), mons[u.umonnum].mname); nb[i]; i++)
             if (i == 0 || nb[i - 1] == ' ')
                 nb[i] = highc(nb[i]);
     } else
         Strcpy(nb = eos(nb), rank());
     Sprintf(blstats[idx][BL_TITLE].val, "%-29s", buf);
     valset[BL_TITLE] = TRUE; /* indicate val already set */
 
     /* Strength */
     blstats[idx][BL_STR].a.a_int = ACURR(A_STR);
     Strcpy(blstats[idx][BL_STR].val, get_strength_str());
     valset[BL_STR] = TRUE; /* indicate val already set */
 
     /*  Dexterity, constitution, intelligence, wisdom, charisma. */
     blstats[idx][BL_DX].a.a_int = ACURR(A_DEX);
     blstats[idx][BL_CO].a.a_int = ACURR(A_CON);
     blstats[idx][BL_IN].a.a_int = ACURR(A_INT);
     blstats[idx][BL_WI].a.a_int = ACURR(A_WIS);
     blstats[idx][BL_CH].a.a_int = ACURR(A_CHA);
 
     /* Alignment */
     Strcpy(blstats[idx][BL_ALIGN].val, (u.ualign.type == A_CHAOTIC)
                                           ? "Chaotic"
                                           : (u.ualign.type == A_NEUTRAL)
                                                ? "Neutral"
                                                : "Lawful");
 
     /* Score */
     blstats[idx][BL_SCORE].a.a_long =
 #ifdef SCORE_ON_BOTL
         flags.showscore ? botl_score() :
 #endif
         0L;
 
     /*  Hit points  */
     i = Upolyd ? u.mh : u.uhp;
     if (i < 0)
         i = 0;
     blstats[idx][BL_HP].a.a_int = min(i, 9999);
     i = Upolyd ? u.mhmax : u.uhpmax;
     blstats[idx][BL_HPMAX].a.a_int = min(i, 9999);
 
     /*  Dungeon level. */
     (void) describe_level(blstats[idx][BL_LEVELDESC].val);
     valset[BL_LEVELDESC] = TRUE; /* indicate val already set */
 
     /* Gold */
     if ((money = money_cnt(invent)) < 0L)
         money = 0L; /* ought to issue impossible() and then discard gold */
     blstats[idx][BL_GOLD].a.a_long = min(money, 999999L);
     /*
      * The tty port needs to display the current symbol for gold
      * as a field header, so to accommodate that we pass gold with
      * that already included. If a window port needs to use the text
      * gold amount without the leading "$:" the port will have to
      * skip past ':' to the value pointer it was passed in status_update()
      * for the BL_GOLD case.
      *
      * Another quirk of BL_GOLD is that the field display may have
      * changed if a new symbol set was loaded, or we entered or left
      * the rogue level.
      *
      * The currency prefix is encoded as ten character \GXXXXNNNN
      * sequence.
      */
     Sprintf(blstats[idx][BL_GOLD].val, "%s:%ld",
             encglyph(objnum_to_glyph(GOLD_PIECE)),
             blstats[idx][BL_GOLD].a.a_long);
     valset[BL_GOLD] = TRUE; /* indicate val already set */
 
     /* Power (magical energy) */
     blstats[idx][BL_ENE].a.a_int = min(u.uen, 9999);
     blstats[idx][BL_ENEMAX].a.a_int = min(u.uenmax, 9999);
 
     /* Armor class */
     blstats[idx][BL_AC].a.a_int = u.uac;
 
     /* Monster level (if Upolyd) */
     blstats[idx][BL_HD].a.a_int = Upolyd ? (int) mons[u.umonnum].mlevel : 0;
 
     /* Experience */
     blstats[idx][BL_XP].a.a_int = u.ulevel;
     blstats[idx][BL_EXP].a.a_int = u.uexp;
 
     /* Time (moves) */
     blstats[idx][BL_TIME].a.a_long = moves;
 
     /* Hunger */
     blstats[idx][BL_HUNGER].a.a_uint = u.uhs;
     Strcpy(blstats[idx][BL_HUNGER].val,
            (u.uhs != NOT_HUNGRY) ? hu_stat[u.uhs] : "");
     valset[BL_HUNGER] = TRUE;
 
     /* Carrying capacity */
     cap = near_capacity();
     blstats[idx][BL_CAP].a.a_int = cap;
     Strcpy(blstats[idx][BL_CAP].val,
            (cap > UNENCUMBERED) ? enc_stat[cap] : "");
     valset[BL_CAP] = TRUE;
 
     /* Conditions */
     blstats[idx][BL_CONDITION].a.a_ulong = 0L;
     if (Stoned)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STONE;
     if (Slimed)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_SLIME;
     if (Strangled)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STRNGL;
     if (Sick && (u.usick_type & SICK_VOMITABLE) != 0)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FOODPOIS;
     if (Sick && (u.usick_type & SICK_NONVOMITABLE) != 0)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_TERMILL;
     /*
      * basic formatting puts hunger status and encumbrance here
      */
     if (Blind)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_BLIND;
     if (Deaf)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_DEAF;
     if (Stunned)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STUN;
     if (Confusion)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_CONF;
     if (Hallucination)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_HALLU;
     /* levitation and flying are mututally exclusive */
     if (Levitation)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_LEV;
     if (Flying)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FLY;
     if (u.usteed)
         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_RIDE;
     evaluate_and_notify_windowport(valset, idx, idx_p);
 }
 

evaluate_and_notify_windowport_field

 STATIC_OVL boolean
 evaluate_and_notify_windowport_field(fld, valsetlist, idx, idx_p)
 int fld, idx, idx_p;
 boolean *valsetlist;
 {
     static int oldrndencode = 0;
     int pc, chg, color = NO_COLOR;
     unsigned anytype;
     boolean updated = FALSE, reset;
     struct istat_s *curr = NULL, *prev = NULL;
     enum statusfields idxmax;
 
     /*
      *  Now pass the changed values to window port.
      */
     anytype = blstats[idx][fld].anytype;
     curr = &blstats[idx][fld];
     prev = &blstats[idx_p][fld];
     color = NO_COLOR;
 
     chg = update_all ? 0 : compare_blstats(prev, curr);
 
     /* Temporary? hack: moveloop()'s prolog for a new game sets
      * context.rndencode after the status window has been init'd,
      * so $:0 has already been encoded and cached by the window
      * port.  Without this hack, gold's \G sequence won't be
      * recognized and ends up being displayed as-is for 'update_all'.
      */
     if (context.rndencode != oldrndencode && fld == BL_GOLD) {
         chg = 2;
         oldrndencode = context.rndencode;
     }
 
     reset = FALSE;
 #ifdef STATUS_HILITES
     if (!update_all && !chg) {
         reset = hilite_reset_needed(prev, bl_hilite_moves);
         if (reset)
           curr->time = prev->time = 0L;
     }
 #endif
 
     if (update_all || chg || reset) {
         idxmax = curr->idxmax;
         pc = (idxmax > BL_FLUSH) ? percentage(curr, &blstats[idx][idxmax]) : 0;
 
         if (!valsetlist[fld])
             (void) anything_to_s(curr->val, &curr->a, anytype);
 
         if (anytype != ANY_MASK32) {
 #ifdef STATUS_HILITES
             if ((chg || *curr->val)) {
                 get_hilite_color(idx, fld, (genericptr_t)&curr->a,
                                  chg, pc, &color);
                 if (chg == 2) {
                     color = NO_COLOR;
                     chg = 0;
                 }
             }
 #endif /* STATUS_HILITES */
             status_update(fld, (genericptr_t) curr->val,
                           chg, pc, color, &cond_hilites[0]);
         } else {
             /* Color for conditions is done through cond_hilites[] */
             status_update(fld, (genericptr_t) &curr->a.a_ulong, chg, pc,
                           color, &cond_hilites[0]);
         }
         curr->chg = prev->chg = TRUE;
         updated = TRUE;
     }
     return updated;
 }
 

evaluate_and_notify_windowport

 static void
 evaluate_and_notify_windowport(valsetlist, idx, idx_p)
 int idx, idx_p;
 boolean *valsetlist;
 {
     int i;
     boolean updated = FALSE;
 
     /*
      *  Now pass the changed values to window port.
      */
     for (i = 0; i < MAXBLSTATS; i++) {
         if (((i == BL_SCORE) && !flags.showscore)
             || ((i == BL_EXP) && !flags.showexp)
             || ((i == BL_TIME) && !flags.time)
             || ((i == BL_HD) && !Upolyd)
             || ((i == BL_XP || i == BL_EXP) && Upolyd))
             continue;
         if (evaluate_and_notify_windowport_field(i, valsetlist, idx, idx_p))
             updated = TRUE;
     }
     /*
      * It is possible to get here, with nothing having been pushed
      * to the window port, when none of the info has changed. In that
      * case, we need to force a call to status_update() when
      * context.botlx is set. The tty port in particular has a problem
      * if that isn't done, since it sets context.botlx when a menu or
      * text display obliterates the status line.
      *
      * To work around it, we call status_update() with fictitious
      * index of BL_FLUSH (-1).
      */
     if ((context.botlx && !updated)
         || (windowprocs.wincap2 & WC2_FLUSH_STATUS) != 0L)
         status_update(BL_FLUSH, (genericptr_t) 0, 0, 0,
                       NO_COLOR, &cond_hilites[0]);
 
     context.botl = context.botlx = 0;
     update_all = FALSE;
 }
 

status_eval_next_unhilite

 void
 status_eval_next_unhilite()
 {
     int i;
     struct istat_s *curr = NULL;
     long next_unhilite, this_unhilite;
 
     bl_hilite_moves = moves;
     /* figure out when the next unhilight needs to be performed */
     next_unhilite = 0L;
     for (i = 0; i < MAXBLSTATS; ++i) {
         curr = &blstats[0][i]; /* blstats[0][*].time == blstats[1][*].time */
 
         if (curr->chg) {
             struct istat_s *prev = &blstats[1][i];
 
 #ifdef STATUS_HILITES
             curr->time = prev->time = (bl_hilite_moves + iflags.hilite_delta);
 #endif
             curr->chg = prev->chg = FALSE;
         }
 
         this_unhilite = curr->time;
         if (this_unhilite > 0L
             && (next_unhilite == 0L || this_unhilite < next_unhilite)
 #ifdef STATUS_HILITES
             && hilite_reset_needed(curr, this_unhilite + 1L)
 #endif
             )
             next_unhilite = this_unhilite;
     }
     if (next_unhilite > 0L && next_unhilite < bl_hilite_moves)
         context.botl = TRUE;
 }
 

status_initialize

 void
 status_initialize(reassessment)
 boolean
     reassessment; /* TRUE = just reassess fields w/o other initialization*/
 {
     int i;
     const char *fieldfmt = (const char *) 0;
     const char *fieldname = (const char *) 0;
 
     if (!reassessment) {
         if (blinit)
             impossible("2nd status_initialize with full init.");
         init_blstats();
         (*windowprocs.win_status_init)();
         blinit = TRUE;
     }
     for (i = 0; i < MAXBLSTATS; ++i) {
         enum statusfields fld = initblstats[i].fld;
         boolean fldenabled = (fld == BL_SCORE) ? flags.showscore
             : (fld == BL_XP) ? (boolean) !Upolyd
             : (fld == BL_HD) ? (boolean) Upolyd
             : (fld == BL_TIME) ? flags.time
             : (fld == BL_EXP) ? (boolean) (flags.showexp && !Upolyd)
             : TRUE;
 
         fieldname = initblstats[i].fldname;
         fieldfmt = initblstats[i].fldfmt;
         status_enablefield(fld, fieldname, fieldfmt, fldenabled);
     }
     update_all = TRUE;
 }
 

status_finish

 void
 status_finish()
 {
     int i;
 
     /* call the window port cleanup routine first */
     (*windowprocs.win_status_finish)();
 
     /* free memory that we alloc'd now */
     for (i = 0; i < MAXBLSTATS; ++i) {
         if (blstats[0][i].val)
             free((genericptr_t) blstats[0][i].val), blstats[0][i].val = 0;
         if (blstats[1][i].val)
             free((genericptr_t) blstats[1][i].val), blstats[1][i].val = 0;
 #ifdef STATUS_HILITES
         if (blstats[0][i].thresholds) {
             struct hilite_s *temp = blstats[0][i].thresholds,
                             *next = (struct hilite_s *)0;
             while (temp) {
                 next = temp->next;
                 free(temp);
                 blstats[0][i].thresholds = (struct hilite_s *)0;
                 blstats[1][i].thresholds = blstats[0][i].thresholds;
                 temp = next;
             }
 	}
 #endif /* STATUS_HILITES */
     }
 }
 

init_blstats

 STATIC_OVL void
 init_blstats()
 {
     static boolean initalready = FALSE;
     int i, j;
 
     if (initalready) {
         impossible("init_blstats called more than once.");
         return;
     }
 
     initalready = TRUE;
     for (i = BEFORE; i <= NOW; ++i) {
         for (j = 0; j < MAXBLSTATS; ++j) {
 #ifdef STATUS_HILITES
             struct hilite_s *keep_hilite_chain = blstats[i][j].thresholds;
 #endif
             blstats[i][j] = initblstats[j];
             blstats[i][j].a = zeroany;
             if (blstats[i][j].valwidth) {
                 blstats[i][j].val = (char *) alloc(blstats[i][j].valwidth);
                 blstats[i][j].val[0] = '\0';
             } else
                 blstats[i][j].val = (char *) 0;
 #ifdef STATUS_HILITES
             if (keep_hilite_chain)
                 blstats[i][j].thresholds = keep_hilite_chain;
 #endif
         }
     }
 }
 

compare_blstats

 /*
  * This compares the previous stat with the current stat,
  * and returns one of the following results based on that:
  *
  *   if prev_value < new_value (stat went up, increased)
  *      return 1
  *
  *   if prev_value > new_value (stat went down, decreased)
  *      return  -1
  *
  *   if prev_value == new_value (stat stayed the same)
  *      return 0
  *
  *   Special cases:
  *     - for bitmasks, 0 = stayed the same, 1 = changed
  *     - for strings,  0 = stayed the same, 1 = changed
  *
  */
 STATIC_OVL int
 compare_blstats(bl1, bl2)
 struct istat_s *bl1, *bl2;
 {
     int anytype, result = 0;
 
     if (!bl1 || !bl2) {
         panic("compare_blstat: bad istat pointer %s, %s",
               fmt_ptr((genericptr_t) bl1), fmt_ptr((genericptr_t) bl2));
     }
 
     anytype = bl1->anytype;
     if ((!bl1->a.a_void || !bl2->a.a_void)
         && (anytype == ANY_IPTR || anytype == ANY_UPTR || anytype == ANY_LPTR
             || anytype == ANY_ULPTR)) {
         panic("compare_blstat: invalid pointer %s, %s",
               fmt_ptr((genericptr_t) bl1->a.a_void),
               fmt_ptr((genericptr_t) bl2->a.a_void));
     }
 
     switch (anytype) {
     case ANY_INT:
         result = (bl1->a.a_int < bl2->a.a_int)
                      ? 1
                      : (bl1->a.a_int > bl2->a.a_int) ? -1 : 0;
         break;
     case ANY_IPTR:
         result = (*bl1->a.a_iptr < *bl2->a.a_iptr)
                      ? 1
                      : (*bl1->a.a_iptr > *bl2->a.a_iptr) ? -1 : 0;
         break;
     case ANY_LONG:
         result = (bl1->a.a_long < bl2->a.a_long)
                      ? 1
                      : (bl1->a.a_long > bl2->a.a_long) ? -1 : 0;
         break;
     case ANY_LPTR:
         result = (*bl1->a.a_lptr < *bl2->a.a_lptr)
                      ? 1
                      : (*bl1->a.a_lptr > *bl2->a.a_lptr) ? -1 : 0;
         break;
     case ANY_UINT:
         result = (bl1->a.a_uint < bl2->a.a_uint)
                      ? 1
                      : (bl1->a.a_uint > bl2->a.a_uint) ? -1 : 0;
         break;
     case ANY_UPTR:
         result = (*bl1->a.a_uptr < *bl2->a.a_uptr)
                      ? 1
                      : (*bl1->a.a_uptr > *bl2->a.a_uptr) ? -1 : 0;
         break;
     case ANY_ULONG:
         result = (bl1->a.a_ulong < bl2->a.a_ulong)
                      ? 1
                      : (bl1->a.a_ulong > bl2->a.a_ulong) ? -1 : 0;
         break;
     case ANY_ULPTR:
         result = (*bl1->a.a_ulptr < *bl2->a.a_ulptr)
                      ? 1
                      : (*bl1->a.a_ulptr > *bl2->a.a_ulptr) ? -1 : 0;
         break;
     case ANY_STR:
         result = sgn(strcmp(bl1->val, bl2->val));
         break;
     case ANY_MASK32:
         result = (bl1->a.a_ulong != bl2->a.a_ulong);
         break;
     default:
         result = 1;
     }
     return result;
 }
 

anything_to_s

 STATIC_OVL char *
 anything_to_s(buf, a, anytype)
 char *buf;
 anything *a;
 int anytype;
 {
     if (!buf)
         return (char *) 0;
 
     switch (anytype) {
     case ANY_ULONG:
         Sprintf(buf, "%lu", a->a_ulong);
         break;
     case ANY_MASK32:
         Sprintf(buf, "%lx", a->a_ulong);
         break;
     case ANY_LONG:
         Sprintf(buf, "%ld", a->a_long);
         break;
     case ANY_INT:
         Sprintf(buf, "%d", a->a_int);
         break;
     case ANY_UINT:
         Sprintf(buf, "%u", a->a_uint);
         break;
     case ANY_IPTR:
         Sprintf(buf, "%d", *a->a_iptr);
         break;
     case ANY_LPTR:
         Sprintf(buf, "%ld", *a->a_lptr);
         break;
     case ANY_ULPTR:
         Sprintf(buf, "%lu", *a->a_ulptr);
         break;
     case ANY_UPTR:
         Sprintf(buf, "%u", *a->a_uptr);
         break;
     case ANY_STR: /* do nothing */
         ;
         break;
     default:
         buf[0] = '\0';
     }
     return buf;
 }
 

s_to_anything

 #ifdef STATUS_HILITES
 STATIC_OVL void
 s_to_anything(a, buf, anytype)
 anything *a;
 char *buf;
 int anytype;
 {
     if (!buf || !a)
         return;
 
     switch (anytype) {
     case ANY_LONG:
         a->a_long = atol(buf);
         break;
     case ANY_INT:
         a->a_int = atoi(buf);
         break;
     case ANY_UINT:
         a->a_uint = (unsigned) atoi(buf);
         break;
     case ANY_ULONG:
         a->a_ulong = (unsigned long) atol(buf);
         break;
     case ANY_IPTR:
         if (a->a_iptr)
             *a->a_iptr = atoi(buf);
         break;
     case ANY_UPTR:
         if (a->a_uptr)
             *a->a_uptr = (unsigned) atoi(buf);
         break;
     case ANY_LPTR:
         if (a->a_lptr)
             *a->a_lptr = atol(buf);
         break;
     case ANY_ULPTR:
         if (a->a_ulptr)
             *a->a_ulptr = (unsigned long) atol(buf);
         break;
     case ANY_MASK32:
         a->a_ulong = (unsigned long) atol(buf);
         break;
     default:
         a->a_void = 0;
         break;
     }
     return;
 }
 #endif /* STATUS_HILITES */
 

percentage

 STATIC_OVL int
 percentage(bl, maxbl)
 struct istat_s *bl, *maxbl;
 {
     int result = 0;
     int anytype;
     int ival;
     long lval;
     unsigned uval;
     unsigned long ulval;
 
     if (!bl || !maxbl) {
         impossible("percentage: bad istat pointer %s, %s",
                    fmt_ptr((genericptr_t) bl), fmt_ptr((genericptr_t) maxbl));
         return 0;
     }
 
     ival = 0, lval = 0L, uval = 0U, ulval = 0UL;
     anytype = bl->anytype;
     if (maxbl->a.a_void) {
         switch (anytype) {
         case ANY_INT:
             ival = bl->a.a_int;
             result = ((100 * ival) / maxbl->a.a_int);
             break;
         case ANY_LONG:
             lval  = bl->a.a_long;
             result = (int) ((100L * lval) / maxbl->a.a_long);
             break;
         case ANY_UINT:
             uval = bl->a.a_uint;
             result = (int) ((100U * uval) / maxbl->a.a_uint);
             break;
         case ANY_ULONG:
             ulval = bl->a.a_ulong;
             result = (int) ((100UL * ulval) / maxbl->a.a_ulong);
             break;
         case ANY_IPTR:
             ival = *bl->a.a_iptr;
             result = ((100 * ival) / (*maxbl->a.a_iptr));
             break;
         case ANY_LPTR:
             lval = *bl->a.a_lptr;
             result = (int) ((100L * lval) / (*maxbl->a.a_lptr));
             break;
         case ANY_UPTR:
             uval = *bl->a.a_uptr;
             result = (int) ((100U * uval) / (*maxbl->a.a_uptr));
             break;
         case ANY_ULPTR:
             ulval = *bl->a.a_ulptr;
             result = (int) ((100UL * ulval) / (*maxbl->a.a_ulptr));
             break;
         }
     }
     /* don't let truncation from integer division produce a zero result
        from a non-zero input; note: if we ever change to something like
        ((((1000 * val) / max) + 5) / 10) for a rounded result, we'll
        also need to check for and convert false 100 to 99 */
     if (result == 0 && (ival != 0 || lval != 0L || uval != 0U || ulval != 0UL))
         result = 1;
 
     return result;
 }
 
 
 #ifdef STATUS_HILITES
 
 /****************************************************************************/
 /* Core status hiliting support */
 /****************************************************************************/
 
 struct hilite_s status_hilites[MAXBLSTATS];
 
 static struct fieldid_t {
     const char *fieldname;
     enum statusfields fldid;
 } fieldids_alias[] = {
     {"characteristics", BL_CHARACTERISTICS},
     {"dx",       BL_DX},
     {"co",       BL_CO},
     {"con",      BL_CO},
     {"points",   BL_SCORE},
     {"cap",      BL_CAP},
     {"pw",       BL_ENE},
     {"pw-max",   BL_ENEMAX},
     {"xl",       BL_XP},
     {"xplvl",    BL_XP},
     {"ac",       BL_AC},
     {"hit-dice", BL_HD},
     {"turns",    BL_TIME},
     {"hp",       BL_HP},
     {"hp-max",   BL_HPMAX},
     {"dgn",      BL_LEVELDESC},
     {"xp",       BL_EXP},
     {"exp",      BL_EXP},
     {"flags",    BL_CONDITION},
     {0,          BL_FLUSH}
 };
 

fldname_to_bl_indx

 /* field name to bottom line index */
 STATIC_OVL enum statusfields
 fldname_to_bl_indx(name)
 const char *name;
 {
     int i, nmatches = 0, fld = 0;
 
     if (name && *name) {
         /* check matches to canonical names */
         for (i = 0; i < SIZE(initblstats); i++)
             if (fuzzymatch(initblstats[i].fldname, name, " -_", TRUE)) {
                 fld = initblstats[i].fld;
                 nmatches++;
             }
 
         if (!nmatches) {
             /* check aliases */
             for (i = 0; fieldids_alias[i].fieldname; i++)
                 if (fuzzymatch(fieldids_alias[i].fieldname, name,
                                " -_", TRUE)) {
                     fld = fieldids_alias[i].fldid;
                     nmatches++;
                 }
         }
 
         if (!nmatches) {
             /* check partial matches to canonical names */
             int len = (int) strlen(name);
             for (i = 0; i < SIZE(initblstats); i++)
                 if (!strncmpi(name, initblstats[i].fldname, len)) {
                     fld = initblstats[i].fld;
                     nmatches++;
                 }
         }
 
     }
     return (nmatches == 1) ? fld : BL_FLUSH;
 }
 

hilite_reset_needed

 STATIC_OVL boolean
 hilite_reset_needed(bl_p, augmented_time)
 struct istat_s *bl_p;
 long augmented_time;
 {
     struct hilite_s *tl = bl_p->thresholds;
 
     /*
      * This 'multi' handling may need some tuning...
      */
     if (multi)
         return FALSE;
 
     if (bl_p->time == 0 || bl_p->time >= augmented_time)
         return FALSE;
 
     while (tl) {
         /* only this style times out */
         if (tl->behavior == BL_TH_UPDOWN)
             return TRUE;
         tl = tl->next;
     }
 
     return FALSE;
 }
 

reset_status_hilites

 /* called by options handling when 'statushilites' boolean is toggled */
 void
 reset_status_hilites()
 {
     if (iflags.hilite_delta) {
         int i;
 
         for (i = 0; i < MAXBLSTATS; ++i)
             blstats[0][i].time = blstats[1][i].time = 0L;
         update_all = TRUE;
     }
     context.botlx = TRUE;
 }
 

merge_bestcolor

 STATIC_OVL void
 merge_bestcolor(bestcolor, newcolor)
 int *bestcolor;
 int newcolor;
 {
     int natr = HL_UNDEF, nclr = NO_COLOR;
 
     split_clridx(newcolor, &nclr, &natr);
 
     if (nclr != NO_COLOR)
         *bestcolor = (*bestcolor & 0xff00) | nclr;
 
     if (natr != HL_UNDEF) {
         if (natr == HL_NONE)
             *bestcolor = *bestcolor & 0x00ff; /* reset all attributes */
         else
             *bestcolor |= (natr << 8); /* merge attributes */
     }
 }
 

get_hilite_color

 /*
  * get_hilite_color
  * 
  * Figures out, based on the value and the
  * direction it is moving, the color that the field
  * should be displayed in.
  *
  *
  * Provide get_hilite_color() with the following
  * to work with:
  *     actual value vp
  *          useful for BL_TH_VAL_ABSOLUTE
  *     indicator of down, up, or the same (-1, 1, 0) chg
  *          useful for BL_TH_UPDOWN or change detection
  *     percentage (current value percentage of max value) pc
  *          useful for BL_TH_VAL_PERCENTAGE
  *
  * Get back:
  *     color     based on user thresholds set in config file.
  *               The rightmost 8 bits contain a color index.
  *               The 8 bits to the left of that contain
  *               the attribute bits.
  *                   color = 0x00FF
  *                   attrib= 0xFF00
  */
 
 STATIC_OVL void
 get_hilite_color(idx, fldidx, vp, chg, pc, colorptr)
 int idx, fldidx, chg, pc;
 genericptr_t vp;
 int *colorptr;
 {
     int bestcolor = NO_COLOR;
     struct hilite_s *hl;
     anything *value = (anything *)vp;
     char *txtstr, *cmpstr;
 
     if (!colorptr || fldidx < 0 || fldidx >= MAXBLSTATS)
         return;
 
     if (blstats[idx][fldidx].thresholds) {
         /* there are hilites set here */
         int max_pc = 0, min_pc = 100;
         int max_val = 0, min_val = LARGEST_INT;
         boolean exactmatch = FALSE;
 
         hl = blstats[idx][fldidx].thresholds;
 
         while (hl) {
             switch (hl->behavior) {
             case BL_TH_VAL_PERCENTAGE:
                 if (hl->rel == EQ_VALUE && pc == hl->value.a_int) {
                     merge_bestcolor(&bestcolor, hl->coloridx);
                     min_pc = max_pc = hl->value.a_int;
                     exactmatch = TRUE;
                 } else if (hl->rel == LT_VALUE && !exactmatch
                            && (hl->value.a_int >= pc)
                            && (hl->value.a_int <= min_pc)) {
                     merge_bestcolor(&bestcolor, hl->coloridx);
                     min_pc = hl->value.a_int;
                 } else if (hl->rel == GT_VALUE && !exactmatch
                            && (hl->value.a_int <= pc)
                            && (hl->value.a_int >= max_pc)) {
                     merge_bestcolor(&bestcolor, hl->coloridx);
                     max_pc = hl->value.a_int;
                 }
                 break;
             case BL_TH_UPDOWN:
                 if (chg < 0 && hl->rel == LT_VALUE) {
                     merge_bestcolor(&bestcolor, hl->coloridx);
                 } else if (chg > 0 && hl->rel == GT_VALUE) {
                     merge_bestcolor(&bestcolor, hl->coloridx);
                 } else if (hl->rel == EQ_VALUE && chg) {
                     merge_bestcolor(&bestcolor, hl->coloridx);
                     min_val = max_val = hl->value.a_int;
                 }
                 break;
             case BL_TH_VAL_ABSOLUTE:
                 if (hl->rel == EQ_VALUE && hl->value.a_int == value->a_int) {
                     merge_bestcolor(&bestcolor, hl->coloridx);
                     min_val = max_val = hl->value.a_int;
                     exactmatch = TRUE;
                 } else if (hl->rel == LT_VALUE && !exactmatch
                            && (hl->value.a_int >= value->a_int)
                            && (hl->value.a_int < min_val)) {
                     merge_bestcolor(&bestcolor, hl->coloridx);
                     min_val = hl->value.a_int;
                 } else if (hl->rel == GT_VALUE && !exactmatch
                            && (hl->value.a_int <= value->a_int)
                            && (hl->value.a_int > max_val)) {
                     merge_bestcolor(&bestcolor, hl->coloridx);
                     max_val = hl->value.a_int;
                 }
                 break;
             case BL_TH_TEXTMATCH:
                 txtstr = dupstr(blstats[idx][fldidx].val);
                 cmpstr = txtstr;
                 if (fldidx == BL_TITLE) {
                     int len = (strlen(plname) + sizeof(" the"));
                     cmpstr += len;
                 }
                 (void) trimspaces(cmpstr);
                 if (hl->rel == TXT_VALUE && hl->textmatch[0] &&
                     !strcmpi(hl->textmatch, cmpstr)) {
                     merge_bestcolor(&bestcolor, hl->coloridx);
                 }
                 free(txtstr);
                 break;
             case BL_TH_ALWAYS_HILITE:
                 merge_bestcolor(&bestcolor, hl->coloridx);
                 break;
             case BL_TH_NONE:
                 break;
             default:
                 break;
             }
             hl = hl->next;
 	}
     }
     *colorptr = bestcolor;
     return;
 }
 

split_clridx

 STATIC_OVL void
 split_clridx(idx, coloridx, attrib)
 int idx;
 int *coloridx, *attrib;
 {
     if (idx && coloridx && attrib) {
            *coloridx = idx & 0x00FF;
            *attrib = (idx & 0xFF00) >> 8;
     }
 }
 
 

parse_status_hl1

 /*
  * This is the parser for the hilite options
  *
  * parse_status_hl1() separates each hilite entry into
  * a set of field threshold/action component strings,
  * then calls parse_status_hl2() to parse further
  * and configure the hilite.
  */
 boolean
 parse_status_hl1(op, from_configfile)
 char *op;
 boolean from_configfile;
 {
 #define MAX_THRESH 21
     char hsbuf[MAX_THRESH][QBUFSZ];
     boolean rslt, badopt = FALSE;
     int i, fldnum, ccount = 0;
     char c;
 
     fldnum = 0;
     for (i = 0; i < MAX_THRESH; ++i) {
         hsbuf[i][0] = '\0';
     }
     while (*op && fldnum < MAX_THRESH && ccount < (QBUFSZ - 2)) {
         c = lowc(*op);
         if (c == ' ') {
             if (fldnum >= 1) {
                 rslt = parse_status_hl2(hsbuf, from_configfile);
                 if (!rslt) {
                     badopt = TRUE;
                     break;
                 }
             }
             for (i = 0; i < MAX_THRESH; ++i) {
                 hsbuf[i][0] = '\0';
             }
             fldnum = 0;
             ccount = 0;
         } else if (c == '/') {
             fldnum++;
             ccount = 0;
         } else {
             hsbuf[fldnum][ccount++] = c;
             hsbuf[fldnum][ccount] = '\0';
         }
         op++;
     }
     if (fldnum >= 1 && !badopt) {
         rslt = parse_status_hl2(hsbuf, from_configfile);
         if (!rslt)
             badopt = TRUE;
     }
     if (badopt)
         return FALSE;
     return TRUE;
 }
 

is_ltgt_percentnumber

 /* is str in the format of "(<>)?[0-9]+%?" regex */
 STATIC_OVL boolean
 is_ltgt_percentnumber(str)
 const char *str;
 {
     const char *s = str;
 
     if (*s == '<' || *s == '>') s++;
     while (digit(*s)) s++;
     if (*s == '%') s++;
 
     return (*s == '\0');
 }
 

has_ltgt_percentnumber

 /* does str only contain "<>0-9%" chars */
 STATIC_OVL boolean
 has_ltgt_percentnumber(str)
 const char *str;
 {
     const char *s = str;
 
     while (*s) {
         if (!index("<>0123456789%", *s))
             return FALSE;
         s++;
     }
     return TRUE;
 }
 

splitsubfields

 /* splitsubfields(): splits str in place into '+' or '&' separated strings.
  * returns number of strings, or -1 if more than maxsf or MAX_SUBFIELDS
  */
 #define MAX_SUBFIELDS 16
 STATIC_OVL int
 splitsubfields(str, sfarr, maxsf)
 char *str;
 char ***sfarr;
 int maxsf;
 {
     static char *subfields[MAX_SUBFIELDS];
     char *st = (char *) 0;
     int sf = 0;
 
     if (!str)
         return 0;
     for (sf = 0; sf < MAX_SUBFIELDS; ++sf)
         subfields[sf] = (char *) 0;
 
     maxsf = (maxsf == 0) ? MAX_SUBFIELDS : min(maxsf, MAX_SUBFIELDS);
 
     if (index(str, '+') || index(str, '&')) {
         char *c = str;
 
         sf = 0;
         st = c;
         while (*c && sf < maxsf) {
             if (*c == '&' || *c == '+') {
                 *c = '\0';
                 subfields[sf] = st;
                 st = c+1;
                 sf++;
             }
             c++;
         }
         if (sf >= maxsf - 1)
             return -1;
         if (!*c && c != st)
             subfields[sf++] = st;
     } else {
         sf = 1;
         subfields[0] = str;
     }
     *sfarr = subfields;
     return sf;
 }
 #undef MAX_SUBFIELDS
 

is_fld_arrayvalues

 boolean
 is_fld_arrayvalues(str, arr, arrmin, arrmax, retidx)
 const char *str;
 const char *const *arr;
 int arrmin, arrmax;
 int *retidx;
 {
     int i;
 
     for (i = arrmin; i < arrmax; i++)
         if (!strcmpi(str, arr[i])) {
             *retidx = i;
             return TRUE;
         }
     return FALSE;
 }
 

query_arrayvalue

 int
 query_arrayvalue(querystr, arr, arrmin, arrmax)
 const char *querystr;
 const char *const *arr;
 int arrmin, arrmax;
 {
     int i, res, ret = arrmin - 1;
     winid tmpwin;
     anything any;
     menu_item *picks = (menu_item *) 0;
     int adj = (arrmin > 0) ? 1 : arrmax;
 
     tmpwin = create_nhwindow(NHW_MENU);
     start_menu(tmpwin);
 
     for (i = arrmin; i < arrmax; i++) {
         any = zeroany;
         any.a_int = i + adj;
         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
                  arr[i], MENU_UNSELECTED);
     }
 
     end_menu(tmpwin, querystr);
 
     res = select_menu(tmpwin, PICK_ONE, &picks);
     destroy_nhwindow(tmpwin);
     if (res > 0) {
         ret = picks->item.a_int - adj;
         free((genericptr_t) picks);
     }
 
     return ret;
 }
 

status_hilite_add_threshold

 void
 status_hilite_add_threshold(fld, hilite)
 int fld;
 struct hilite_s *hilite;
 {
     struct hilite_s *new_hilite;
 
     if (!hilite)
         return;
 
     /* alloc and initialize a new hilite_s struct */
     new_hilite = (struct hilite_s *) alloc(sizeof(struct hilite_s));
     *new_hilite = *hilite;   /* copy struct */
 
     new_hilite->set = TRUE;
     new_hilite->fld = fld;
     new_hilite->next = (struct hilite_s *)0;
 
     /* Does that status field currently have any hilite thresholds? */
     if (!blstats[0][fld].thresholds) {
         blstats[0][fld].thresholds = new_hilite;
     } else {
         struct hilite_s *temp_hilite = blstats[0][fld].thresholds;
         new_hilite->next = temp_hilite;
         blstats[0][fld].thresholds = new_hilite;
         /* sort_hilites(fld) */
     }
     /* current and prev must both point at the same hilites */
     blstats[1][fld].thresholds = blstats[0][fld].thresholds;
 }
 
 

parse_status_hl2

 STATIC_OVL boolean
 parse_status_hl2(s, from_configfile)
 char (*s)[QBUFSZ];
 boolean from_configfile;
 {
     char *tmp, *how;
     int sidx = 0, i = -1, dt = -1;
     int coloridx = -1, successes = 0;
     int disp_attrib = 0;
     boolean percent = FALSE, changed = FALSE, numeric = FALSE;
     boolean down= FALSE, up = FALSE;
     boolean gt = FALSE, lt = FALSE, eq = FALSE, neq = FALSE;
     boolean txtval = FALSE;
     boolean always = FALSE;
     const char *txt;
     enum statusfields fld = BL_FLUSH;
     struct hilite_s hilite;
     char tmpbuf[BUFSZ];
     const char *aligntxt[] = {"chaotic", "neutral", "lawful"};
     /* hu_stat[] from eat.c has trailing spaces which foul up comparisons */
     const char *hutxt[] = {"Satiated", "", "Hungry", "Weak",
                            "Fainting", "Fainted", "Starved"};
 
     /* Examples:
         3.6.1:
       OPTION=hilite_status: hitpoints/<10%/red
       OPTION=hilite_status: hitpoints/<10%/red/<5%/purple/1/red+blink+inverse
       OPTION=hilite_status: experience/down/red/up/green
       OPTION=hilite_status: cap/strained/yellow/overtaxed/orange
       OPTION=hilite_status: title/always/blue
       OPTION=hilite_status: title/blue
     */
 
     /* field name to statusfield */
     fld = fldname_to_bl_indx(s[sidx]);
 
     if (fld == BL_CHARACTERISTICS) {
         boolean res = FALSE;
 
         /* recursively set each of strength, dexterity, constitution, &c */
         for (fld = BL_STR; fld <= BL_CH; fld++) {
             Strcpy(s[sidx], initblstats[fld].fldname);
             res = parse_status_hl2(s, from_configfile);
             if (!res)
                 return FALSE;
         }
         return TRUE;
     }
     if (fld == BL_FLUSH) {
         config_error_add("Unknown status field '%s'", s[sidx]);
         return FALSE;
     }
     if (fld == BL_CONDITION)
         return parse_condition(s, sidx);
 
     ++sidx;
     while(s[sidx]) {
         char buf[BUFSZ], **subfields;
         int sf = 0;     /* subfield count */
         int kidx;
 
         txt = (const char *)0;
         percent = changed = numeric = FALSE;
         down = up = FALSE;
         gt = eq = lt = neq = txtval = FALSE;
         always = FALSE;
 
         /* threshold value */
         if (!s[sidx][0])
             return TRUE;
 
         memset((genericptr_t) &hilite, 0, sizeof(struct hilite_s));
         hilite.set = FALSE; /* mark it "unset" */
         hilite.fld = fld;
 
         if (*s[sidx+1] == '\0' || !strcmpi(s[sidx], "always")) {
             /* "field/always/color" OR "field/color" */
             always = TRUE;
             if (*s[sidx+1] == '\0')
                 sidx--;
             goto do_rel;
         } else if (!strcmpi(s[sidx], "up") || !strcmpi(s[sidx], "down")) {
             if (!strcmpi(s[sidx], "down"))
                 down = TRUE;
             else
                 up = TRUE;
             changed = TRUE;
             goto do_rel;
 	} else if (fld == BL_CAP
                    && is_fld_arrayvalues(s[sidx], enc_stat,
                                          SLT_ENCUMBER, OVERLOADED+1, &kidx)) {
             txt = enc_stat[kidx];
             txtval = TRUE;
 	    goto do_rel;
         } else if (fld == BL_ALIGN
                    && is_fld_arrayvalues(s[sidx], aligntxt, 0, 3, &kidx)) {
             txt = aligntxt[kidx];
             txtval = TRUE;
             goto do_rel;
         } else if (fld == BL_HUNGER
                    && is_fld_arrayvalues(s[sidx], hutxt,
                                          SATIATED, STARVED+1, &kidx)) {
             txt = hu_stat[kidx];   /* store hu_stat[] val, not hutxt[] */
             txtval = TRUE;
             goto do_rel;
         } else if (!strcmpi(s[sidx], "changed")) {
             changed = TRUE;
             goto do_rel;
         } else if (is_ltgt_percentnumber(s[sidx])) {
             tmp = s[sidx];
             if (strchr(tmp, '%'))
                percent = TRUE;
             if (strchr(tmp, '<'))
                 lt = TRUE;
             if (strchr(tmp, '>'))
                 gt = TRUE;
             (void) stripchars(tmpbuf, "%<>", tmp);
             tmp = tmpbuf;
             while (*tmp) {
                 if (!index("0123456789", *tmp))
                     return FALSE;
                 tmp++;
             }
             numeric = TRUE;
             tmp = tmpbuf;
             if (strlen(tmp) > 0) {
                 dt = initblstats[fld].anytype;
                 if (percent)
                     dt = ANY_INT;
                 (void) s_to_anything(&hilite.value, tmp, dt);
             } else
                 return FALSE;
             if (!hilite.value.a_void && (strcmp(tmp, "0") != 0))
                return FALSE;
         } else if (initblstats[fld].anytype == ANY_STR) {
             txt = s[sidx];
             txtval = TRUE;
             goto do_rel;
         } else {
             config_error_add(has_ltgt_percentnumber(s[sidx])
                  ? "Wrong format '%s', expected a threshold number or percent"
                  : "Unknown behavior '%s'", s[sidx]);
             return FALSE;
         }
 do_rel:
         /* relationships { LT_VALUE, GT_VALUE, EQ_VALUE} */
         if (gt)
             hilite.rel = GT_VALUE;
         else if (eq)
             hilite.rel = EQ_VALUE;
         else if (lt)
             hilite.rel = LT_VALUE;
         else if (percent)
             hilite.rel = EQ_VALUE;
         else if (numeric)
             hilite.rel = EQ_VALUE;
         else if (down)
             hilite.rel = LT_VALUE;
         else if (up)
             hilite.rel = GT_VALUE;
         else if (changed)
             hilite.rel = EQ_VALUE;
         else if (txtval)
             hilite.rel = TXT_VALUE;
         else
             hilite.rel = LT_VALUE;
 
         if (initblstats[fld].anytype == ANY_STR
             && (percent || numeric)) {
             config_error_add("Field '%s' does not support numeric values",
                              initblstats[fld].fldname);
             return FALSE;
         }
 
         if (percent) {
             if (initblstats[fld].idxmax <= BL_FLUSH) {
                 config_error_add("Cannot use percent with '%s'",
                                  initblstats[fld].fldname);
                 return FALSE;
             } else if ((hilite.value.a_int < 0)
                        || (hilite.value.a_int == 0
                            && hilite.rel == LT_VALUE)
                        || (hilite.value.a_int > 100)
                        || (hilite.value.a_int == 100
                            && hilite.rel == GT_VALUE)) {
                 config_error_add("Illegal percentage value");
                 return FALSE;
             }
         }
 
         /* actions */
         sidx++;
         how = s[sidx];
         if (!how) {
             if (!successes)
                 return FALSE;
         }
         coloridx = -1;
         Strcpy(buf, how);
         sf = splitsubfields(buf, &subfields, 0);
 
         if (sf < 1)
             return FALSE;
 
         disp_attrib = HL_UNDEF;
 
         for (i = 0; i < sf; ++i) {
             int a = match_str2attr(subfields[i], FALSE);
             if (a == ATR_DIM)
                 disp_attrib |= HL_DIM;
             else if (a == ATR_BLINK)
                 disp_attrib |= HL_BLINK;
             else if (a == ATR_ULINE)
                 disp_attrib |= HL_ULINE;
             else if (a == ATR_INVERSE)
                 disp_attrib |= HL_INVERSE;
             else if (a == ATR_BOLD)
                 disp_attrib |= HL_BOLD;
             else if (a == ATR_NONE)
                 disp_attrib = HL_NONE;
             else {
                 int c = match_str2clr(subfields[i]);
 
                 if (c >= CLR_MAX || coloridx != -1)
                     return FALSE;
                 coloridx = c;
 	    }
         }
         if (coloridx == -1)
             coloridx = NO_COLOR;
 
         /* Assign the values */
         hilite.coloridx = coloridx | (disp_attrib << 8);
 
         if (always)
             hilite.behavior = BL_TH_ALWAYS_HILITE;
         else if (percent)
             hilite.behavior = BL_TH_VAL_PERCENTAGE;
         else if (changed)
             hilite.behavior = BL_TH_UPDOWN;
         else if (numeric)
             hilite.behavior = BL_TH_VAL_ABSOLUTE;
         else if (txtval)
             hilite.behavior = BL_TH_TEXTMATCH;
         else if (hilite.value.a_void)
             hilite.behavior = BL_TH_VAL_ABSOLUTE;
        else
             hilite.behavior = BL_TH_NONE;
 
         hilite.anytype = dt;
 
         if (hilite.behavior == BL_TH_TEXTMATCH && txt
             && strlen(txt) < QBUFSZ-1) {
             Strcpy(hilite.textmatch, txt);
             (void) trimspaces(hilite.textmatch);
         }
 
         status_hilite_add_threshold(fld, &hilite);
 
         successes++;
         sidx++;
     }
 
     return TRUE;
 }
 
 
 
 const struct condmap valid_conditions[] = {
     {"stone",    BL_MASK_STONE},
     {"slime",    BL_MASK_SLIME},
     {"strngl",   BL_MASK_STRNGL},
     {"foodPois", BL_MASK_FOODPOIS},
     {"termIll",  BL_MASK_TERMILL},
     {"blind",    BL_MASK_BLIND},
     {"deaf",     BL_MASK_DEAF},
     {"stun",     BL_MASK_STUN},
     {"conf",     BL_MASK_CONF},
     {"hallu",    BL_MASK_HALLU},
     {"lev",      BL_MASK_LEV},
     {"fly",      BL_MASK_FLY},
     {"ride",     BL_MASK_RIDE},
 };
 
 const struct condmap condition_aliases[] = {
     {"strangled",      BL_MASK_STRNGL},
     {"all",            BL_MASK_STONE | BL_MASK_SLIME | BL_MASK_STRNGL |
                        BL_MASK_FOODPOIS | BL_MASK_TERMILL |
                        BL_MASK_BLIND | BL_MASK_DEAF | BL_MASK_STUN |
                        BL_MASK_CONF | BL_MASK_HALLU |
                        BL_MASK_LEV | BL_MASK_FLY | BL_MASK_RIDE },
     {"major_troubles", BL_MASK_STONE | BL_MASK_SLIME | BL_MASK_STRNGL |
                        BL_MASK_FOODPOIS | BL_MASK_TERMILL},
     {"minor_troubles", BL_MASK_BLIND | BL_MASK_DEAF | BL_MASK_STUN |
                        BL_MASK_CONF | BL_MASK_HALLU},
     {"movement",       BL_MASK_LEV | BL_MASK_FLY | BL_MASK_RIDE}
 };
 

query_conditions

 unsigned long
 query_conditions()
 {
     int i,res;
     unsigned long ret = 0UL;
     winid tmpwin;
     anything any;
     menu_item *picks = (menu_item *) 0;
 
     tmpwin = create_nhwindow(NHW_MENU);
     start_menu(tmpwin);
 
     for (i = 0; i < SIZE(valid_conditions); i++) {
         any = zeroany;
         any.a_ulong = valid_conditions[i].bitmask;
         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
                  valid_conditions[i].id, MENU_UNSELECTED);
     }
 
     end_menu(tmpwin, "Choose status conditions");
 
     res = select_menu(tmpwin, PICK_ANY, &picks);
     destroy_nhwindow(tmpwin);
     if (res > 0) {
         for (i = 0; i < res; i++)
             ret |= picks[i].item.a_ulong;
         free((genericptr_t) picks);
     }
     return ret;
 }
 

conditionbitmask2str

 STATIC_OVL char *
 conditionbitmask2str(ul)
 unsigned long ul;
 {
     static char buf[BUFSZ];
     int i;
     boolean first = TRUE;
     const char *alias = (char *) 0;
 
 
     buf[0] = '\0';
     if (!ul)
         return buf;
 
     for (i = 1; i < SIZE(condition_aliases); i++)
         if (condition_aliases[i].bitmask == ul)
             alias = condition_aliases[i].id;
 
     for (i = 0; i < SIZE(valid_conditions); i++)
         if ((valid_conditions[i].bitmask & ul) != 0UL) {
             Sprintf(eos(buf), "%s%s", (first) ? "" : "+",
                     valid_conditions[i].id);
             first = FALSE;
         }
 
     if (!first && alias)
         Sprintf(buf, "%s", alias);
 
     return buf;
 }
 

match_str2conditionbitmask

 STATIC_OVL unsigned long
 match_str2conditionbitmask(str)
 const char *str;
 {
     int i, nmatches = 0;
     unsigned long mask = 0UL;
 
     if (str && *str) {
         /* check matches to canonical names */
         for (i = 0; i < SIZE(valid_conditions); i++)
             if (fuzzymatch(valid_conditions[i].id, str, " -_", TRUE)) {
                 mask |= valid_conditions[i].bitmask;
                 nmatches++;
             }
 
         if (!nmatches) {
             /* check aliases */
             for (i = 0; i < SIZE(condition_aliases); i++)
                 if (fuzzymatch(condition_aliases[i].id, str, " -_", TRUE)) {
                     mask |= condition_aliases[i].bitmask;
                     nmatches++;
                 }
         }
 
         if (!nmatches) {
             /* check partial matches to aliases */
             int len = (int) strlen(str);
             for (i = 0; i < SIZE(condition_aliases); i++)
                 if (!strncmpi(str, condition_aliases[i].id, len)) {
                     mask |= condition_aliases[i].bitmask;
                     nmatches++;
                 }
         }
     }
 
     return mask;
 }
 

str2conditionbitmask

 STATIC_OVL unsigned long
 str2conditionbitmask(str)
 char *str;
 {
     unsigned long conditions_bitmask = 0UL;
     char **subfields;
     int i, sf;
 
     sf = splitsubfields(str, &subfields, SIZE(valid_conditions));
 
     if (sf < 1)
         return 0UL;
 
     for (i = 0; i < sf; ++i) {
         unsigned long bm = match_str2conditionbitmask(subfields[i]);
 
         if (!bm) {
             config_error_add("Unknown condition '%s'", subfields[i]);
             return 0UL;
         }
         conditions_bitmask |= bm;
     }
     return conditions_bitmask;
 }
 

parse_condition

 STATIC_OVL boolean
 parse_condition(s, sidx)
 char (*s)[QBUFSZ];
 int sidx;
 {
     int i;
     int coloridx = NO_COLOR;
     char *tmp, *how;
     unsigned long conditions_bitmask = 0UL;
     boolean success = FALSE;
 
     if (!s)
         return FALSE;
 
     /*3.6.1:
       OPTION=hilite_status: condition/stone+slime+foodPois/red&inverse */
 
     sidx++;
     while(s[sidx]) {
         int sf = 0;     /* subfield count */
         char buf[BUFSZ], **subfields;
 
         tmp = s[sidx];
         if (!*tmp) {
             if (!success)
                 config_error_add("Missing condition(s)");
             return success;
 	}
 
         Strcpy(buf, tmp);
         conditions_bitmask = str2conditionbitmask(buf);
 
         if (!conditions_bitmask)
             return FALSE;
 
         /*
          * We have the conditions_bitmask with bits set for
          * each ailment we want in a particular color and/or
          * attribute, but we need to assign it to an arry of
          * bitmasks indexed by the color chosen
          *        (0 to (CLR_MAX - 1))
          * and/or attributes chosen
          *        (HL_ATTCLR_DIM to (BL_ATTCLR_MAX - 1))
          * We still have to parse the colors and attributes out.
          */
 
         /* actions */
         sidx++;
         how = s[sidx];
         if (!how || !*how) {
             config_error_add("Missing color+attribute");
             return FALSE;
         }
 
         Strcpy(buf, how);
         sf = splitsubfields(buf, &subfields, 0);
 
         /*
          * conditions_bitmask now has bits set representing
          * the conditions that player wants represented, but
          * now we parse out *how* they will be represented.
          *
          * Only 1 colour is allowed, but potentially multiple
          * attributes are allowed.
          *
          * We have the following additional array offsets to
          * use for storing the attributes beyond the end of
          * the color indexes, all of which are less than CLR_MAX.
          * HL_ATTCLR_DIM        = CLR_MAX
          * HL_ATTCLR_BLINK      = CLR_MAX + 1
          * HL_ATTCLR_ULINE      = CLR_MAX + 2
          * HL_ATTCLR_INVERSE    = CLR_MAX + 3
          * HL_ATTCLR_BOLD       = CLR_MAX + 4
          * HL_ATTCLR_MAX        = CLR_MAX + 5 (this is past array boundary)
          *
          */
 
         for (i = 0; i < sf; ++i) {
             int a = match_str2attr(subfields[i], FALSE);
             if (a == ATR_DIM)
                 cond_hilites[HL_ATTCLR_DIM] |= conditions_bitmask;
             else if (a == ATR_BLINK)
                 cond_hilites[HL_ATTCLR_BLINK] |= conditions_bitmask;
             else if (a == ATR_ULINE)
                 cond_hilites[HL_ATTCLR_ULINE] |= conditions_bitmask;
             else if (a == ATR_INVERSE)
                 cond_hilites[HL_ATTCLR_INVERSE] |= conditions_bitmask;
             else if (a == ATR_BOLD)
                 cond_hilites[HL_ATTCLR_BOLD] |= conditions_bitmask;
             else if (a == ATR_NONE) {
                 cond_hilites[HL_ATTCLR_DIM]     = 0UL;
                 cond_hilites[HL_ATTCLR_BLINK]   = 0UL;
                 cond_hilites[HL_ATTCLR_ULINE]   = 0UL;
                 cond_hilites[HL_ATTCLR_INVERSE] = 0UL;
                 cond_hilites[HL_ATTCLR_BOLD]    = 0UL;
             } else {
                 int k = match_str2clr(subfields[i]);
 
                 if (k >= CLR_MAX)
                     return FALSE;
                 coloridx = k;
 	    }
         }
         /* set the bits in the appropriate member of the
            condition array according to color chosen as index */
 
         cond_hilites[coloridx] |= conditions_bitmask;
         success = TRUE;
         sidx++;
     }
     return TRUE;
 }
 

clear_status_hilites

 void
 clear_status_hilites()
 {
     int i;
 
     for (i = 0; i < MAXBLSTATS; ++i) {
         if (blstats[0][i].thresholds) {
             struct hilite_s *temp = blstats[0][i].thresholds,
                             *next = (struct hilite_s *)0;
             while (temp) {
                 next = temp->next;
                 free(temp);
                 blstats[0][i].thresholds = (struct hilite_s *)0;
                 blstats[1][i].thresholds = blstats[0][i].thresholds;
                 temp = next;
             }
 	}
     }
 }
 

hlattr2attrname

 STATIC_OVL char *
 hlattr2attrname(attrib, buf, bufsz)
 int attrib, bufsz;
 char *buf;
 {
     if (attrib && buf) {
         char attbuf[BUFSZ];
         int k, first = 0;
 
         attbuf[0] = '\0';
         if (attrib & HL_NONE) {
             Strcpy(buf, "normal");
             return buf;
         }
 
         if (attrib & HL_BOLD)
             Strcat(attbuf, first++ ? "+bold" : "bold");
         if (attrib & HL_INVERSE)
             Strcat(attbuf, first++ ? "+inverse" : "inverse");
         if (attrib & HL_ULINE)
             Strcat(attbuf, first++ ? "+underline" : "underline");
         if (attrib & HL_BLINK)
             Strcat(attbuf, first++ ? "+blink" : "blink");
         if (attrib & HL_DIM)
             Strcat(attbuf, first++ ? "+dim" : "dim");
 
         k = strlen(attbuf);
         if (k < (bufsz - 1))
             Strcpy(buf, attbuf);
         return buf;
     }
     return (char *) 0;
 }
 
 
 struct _status_hilite_line_str {
     int id;
     int fld;
     struct hilite_s *hl;
     unsigned long mask;
     char str[BUFSZ];
     struct _status_hilite_line_str *next;
 };
 
 struct _status_hilite_line_str *status_hilite_str =
     (struct _status_hilite_line_str *) 0;
 static int status_hilite_str_id = 0;
 

status_hilite_linestr_add

 STATIC_OVL void
 status_hilite_linestr_add(fld, hl, mask, str)
 int fld;
 struct hilite_s *hl;
 unsigned long mask;
 const char *str;
 {
     struct _status_hilite_line_str *tmp = (struct _status_hilite_line_str *)
         alloc(sizeof(struct _status_hilite_line_str));
     struct _status_hilite_line_str *nxt = status_hilite_str;
 
     (void) memset(tmp, 0, sizeof(struct _status_hilite_line_str));
 
     ++status_hilite_str_id;
     tmp->fld = fld;
     tmp->hl = hl;
     tmp->mask = mask;
     (void) stripchars(tmp->str, " ", str);
 
     tmp->id = status_hilite_str_id;
 
     if (nxt) {
         while (nxt && nxt->next)
             nxt = nxt->next;
         nxt->next = tmp;
     } else {
         tmp->next = (struct _status_hilite_line_str *) 0;
         status_hilite_str = tmp;
     }
 }
 

status_hilite_linestr_done

 STATIC_OVL void
 status_hilite_linestr_done()
 {
     struct _status_hilite_line_str *tmp = status_hilite_str;
     struct _status_hilite_line_str *nxt;
 
     while (tmp) {
         nxt = tmp->next;
         free(tmp);
         tmp = nxt;
     }
     status_hilite_str = (struct _status_hilite_line_str *) 0;
     status_hilite_str_id = 0;
 }
 

status_hilite_linestr_countfield

 STATIC_OVL int
 status_hilite_linestr_countfield(fld)
 int fld;
 {
     struct _status_hilite_line_str *tmp = status_hilite_str;
     int count = 0;
 
     while (tmp) {
         if (tmp->fld == fld || fld == BL_FLUSH)
             count++;
         tmp = tmp->next;
     }
     return count;
 }
 

count_status_hilites

 int
 count_status_hilites(VOID_ARGS)
 {
     int count;
     status_hilite_linestr_gather();
     count = status_hilite_linestr_countfield(BL_FLUSH);
     status_hilite_linestr_done();
     return count;
 }
 

status_hilite_linestr_gather_conditions

 STATIC_OVL void
 status_hilite_linestr_gather_conditions()
 {
     int i;
     struct _cond_map {
         unsigned long bm;
         unsigned long clratr;
     } cond_maps[SIZE(valid_conditions)];
 
     (void)memset(cond_maps, 0,
                  sizeof(struct _cond_map) * SIZE(valid_conditions));
 
     for (i = 0; i < SIZE(valid_conditions); i++) {
         int clr = NO_COLOR;
         int atr = HL_NONE;
         int j;
         for (j = 0; j < CLR_MAX; j++)
             if (cond_hilites[j] & valid_conditions[i].bitmask)
                 clr = j;
         if (cond_hilites[HL_ATTCLR_DIM] & valid_conditions[i].bitmask)
             atr |= HL_DIM;
         if (cond_hilites[HL_ATTCLR_BOLD] & valid_conditions[i].bitmask)
             atr |= HL_BOLD;
         if (cond_hilites[HL_ATTCLR_BLINK] & valid_conditions[i].bitmask)
             atr |= HL_BLINK;
         if (cond_hilites[HL_ATTCLR_ULINE] & valid_conditions[i].bitmask)
             atr |= HL_ULINE;
         if (cond_hilites[HL_ATTCLR_INVERSE] & valid_conditions[i].bitmask)
             atr |= HL_INVERSE;
 
         if (clr != NO_COLOR || atr != HL_NONE) {
             unsigned long ca = clr | (atr << 8);
             boolean added_condmap = FALSE;
             for (j = 0; j < SIZE(valid_conditions); j++)
                 if (cond_maps[j].clratr == ca) {
                     cond_maps[j].bm |= valid_conditions[i].bitmask;
                     added_condmap = TRUE;
                     break;
                 }
             if (!added_condmap) {
                 for (j = 0; j < SIZE(valid_conditions); j++)
                     if (!cond_maps[j].bm) {
                         cond_maps[j].bm = valid_conditions[i].bitmask;
                         cond_maps[j].clratr = ca;
                         break;
                     }
             }
         }
     }
 
     for (i = 0; i < SIZE(valid_conditions); i++)
         if (cond_maps[i].bm) {
             int clr = NO_COLOR, atr = HL_NONE;
             split_clridx(cond_maps[i].clratr, &clr, &atr);
             if (clr != NO_COLOR || atr != HL_NONE) {
                 char clrbuf[BUFSZ];
                 char attrbuf[BUFSZ];
                 char condbuf[BUFSZ];
                 char *tmpattr;
                 (void) stripchars(clrbuf, " ", clr2colorname(clr));
                 tmpattr = hlattr2attrname(atr, attrbuf, BUFSZ);
                 if (tmpattr)
                     Sprintf(eos(clrbuf), "&%s", tmpattr);
                 Sprintf(condbuf, "condition/%s/%s",
                         conditionbitmask2str(cond_maps[i].bm), clrbuf);
                 status_hilite_linestr_add(BL_CONDITION, 0,
                                           cond_maps[i].bm, condbuf);
             }
         }
 }
 

status_hilite_linestr_gather

 STATIC_OVL void
 status_hilite_linestr_gather()
 {
     int i;
     struct hilite_s *hl;
 
     status_hilite_linestr_done();
 
     for (i = 0; i < MAXBLSTATS; i++) {
         hl = blstats[0][i].thresholds;
         while (hl) {
             status_hilite_linestr_add(i, hl, 0UL, status_hilite2str(hl));
             hl = hl->next;
         }
     }
 
     status_hilite_linestr_gather_conditions();
 }
 
 

status_hilite2str

 char *
 status_hilite2str(hl)
 struct hilite_s *hl;
 {
     static char buf[BUFSZ];
     int clr = 0, attr = 0;
     char behavebuf[BUFSZ];
     char clrbuf[BUFSZ];
     char attrbuf[BUFSZ];
     char *tmpattr;
 
     if (!hl)
         return (char *) 0;
 
     behavebuf[0] = '\0';
     clrbuf[0] = '\0';
 
     switch (hl->behavior) {
     case BL_TH_VAL_PERCENTAGE:
         if (hl->rel == LT_VALUE)
             Sprintf(behavebuf, "<%i%%", hl->value.a_int);
         else if (hl->rel == GT_VALUE)
             Sprintf(behavebuf, ">%i%%", hl->value.a_int);
         else if (hl->rel == EQ_VALUE)
             Sprintf(behavebuf, "%i%%", hl->value.a_int);
         else
             impossible("hl->behavior=percentage, rel error");
         break;
     case BL_TH_UPDOWN:
         if (hl->rel == LT_VALUE)
             Sprintf(behavebuf, "down");
         else if (hl->rel == GT_VALUE)
             Sprintf(behavebuf, "up");
         else if (hl->rel == EQ_VALUE)
             Sprintf(behavebuf, "changed");
         else
             impossible("hl->behavior=updown, rel error");
         break;
     case BL_TH_VAL_ABSOLUTE:
         if (hl->rel == LT_VALUE)
             Sprintf(behavebuf, "<%i", hl->value.a_int);
         else if (hl->rel == GT_VALUE)
             Sprintf(behavebuf, ">%i", hl->value.a_int);
         else if (hl->rel == EQ_VALUE)
             Sprintf(behavebuf, "%i", hl->value.a_int);
         else
             impossible("hl->behavior=absolute, rel error");
         break;
     case BL_TH_TEXTMATCH:
         if (hl->rel == TXT_VALUE && hl->textmatch[0])
             Sprintf(behavebuf, "%s", hl->textmatch);
         else
             impossible("hl->behavior=textmatch, rel or textmatch error");
         break;
     case BL_TH_CONDITION:
         if (hl->rel == EQ_VALUE)
             Sprintf(behavebuf, "%s", conditionbitmask2str(hl->value.a_ulong));
         else
             impossible("hl->behavior=condition, rel error");
         break;
     case BL_TH_ALWAYS_HILITE:
         Sprintf(behavebuf, "always");
         break;
     case BL_TH_NONE:
         break;
     default:
         break;
     }
 
     split_clridx(hl->coloridx, &clr, &attr);
     if (clr != NO_COLOR)
         (void) stripchars(clrbuf, " ", clr2colorname(clr));
     if (attr != HL_UNDEF) {
         tmpattr = hlattr2attrname(attr, attrbuf, BUFSZ);
         if (tmpattr)
             Sprintf(eos(clrbuf), "%s%s",
                     (clr != NO_COLOR) ? "&" : "",
                     tmpattr);
     }
     Sprintf(buf, "%s/%s/%s", initblstats[hl->fld].fldname, behavebuf, clrbuf);
 
     return buf;
 }
 

status_hilite_menu_choose_field

 int
 status_hilite_menu_choose_field()
 {
     winid tmpwin;
     int i, res, fld = BL_FLUSH;
     anything any;
     menu_item *picks = (menu_item *) 0;
 
     tmpwin = create_nhwindow(NHW_MENU);
     start_menu(tmpwin);
 
     for (i = 0; i < MAXBLSTATS; i++) {
         any = zeroany;
         any.a_int = (i+1);
         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
                  initblstats[i].fldname, MENU_UNSELECTED);
     }
 
     end_menu(tmpwin, "Select a hilite field:");
 
     res = select_menu(tmpwin, PICK_ONE, &picks);
     destroy_nhwindow(tmpwin);
     if (res > 0) {
         fld = picks->item.a_int - 1;
         free((genericptr_t) picks);
     }
     return fld;
 }
 

status_hilite_menu_choose_behavior

 int
 status_hilite_menu_choose_behavior(fld)
 int fld;
 {
     winid tmpwin;
     int res = 0, beh = BL_TH_NONE-1;
     anything any;
     menu_item *picks = (menu_item *) 0;
     char buf[BUFSZ];
     int at;
     int onlybeh = BL_TH_NONE, nopts = 0;
 
     if (fld <= BL_FLUSH || fld >= MAXBLSTATS)
         return BL_TH_NONE;
 
     at = initblstats[fld].anytype;
 
     tmpwin = create_nhwindow(NHW_MENU);
     start_menu(tmpwin);
 
     if (fld != BL_CONDITION) {
         any = zeroany;
         any.a_int = onlybeh = BL_TH_ALWAYS_HILITE;
         Sprintf(buf, "Always highlight %s", initblstats[fld].fldname);
         add_menu(tmpwin, NO_GLYPH, &any, 'a', 0, ATR_NONE,
                  buf, MENU_UNSELECTED);
         nopts++;
     }
 
     if (fld == BL_CONDITION) {
         any = zeroany;
         any.a_int = onlybeh = BL_TH_CONDITION;
         add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, ATR_NONE,
                  "Bitmask of conditions", MENU_UNSELECTED);
         nopts++;
     }
 
     if (fld != BL_CONDITION) {
         any = zeroany;
         any.a_int = onlybeh = BL_TH_UPDOWN;
         Sprintf(buf, "%s value changes", initblstats[fld].fldname);
         add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE,
                  buf, MENU_UNSELECTED);
         nopts++;
     }
 
     if (fld != BL_CAP && fld != BL_HUNGER && (at == ANY_INT || at == ANY_LONG || at == ANY_UINT)) {
         any = zeroany;
         any.a_int = onlybeh = BL_TH_VAL_ABSOLUTE;
         add_menu(tmpwin, NO_GLYPH, &any, 'n', 0, ATR_NONE,
                  "Number threshold", MENU_UNSELECTED);
         nopts++;
     }
 
     if (initblstats[fld].idxmax > BL_FLUSH) {
         any = zeroany;
         any.a_int = onlybeh = BL_TH_VAL_PERCENTAGE;
         add_menu(tmpwin, NO_GLYPH, &any, 'p', 0, ATR_NONE,
                  "Percentage threshold", MENU_UNSELECTED);
         nopts++;
     }
 
     if (initblstats[fld].anytype == ANY_STR || fld == BL_CAP || fld == BL_HUNGER) {
         any = zeroany;
         any.a_int = onlybeh = BL_TH_TEXTMATCH;
         Sprintf(buf, "%s text match", initblstats[fld].fldname);
         add_menu(tmpwin, NO_GLYPH, &any, 't', 0, ATR_NONE,
                  buf, MENU_UNSELECTED);
         nopts++;
     }
 
     Sprintf(buf, "Select %s field hilite behavior:", initblstats[fld].fldname);
     end_menu(tmpwin, buf);
 
     if (nopts > 1) {
         res = select_menu(tmpwin, PICK_ONE, &picks);
         if (res == 0) /* none chosen*/
             beh = BL_TH_NONE;
         else if (res == -1) /* menu cancelled */
             beh = (BL_TH_NONE - 1);
     } else if (onlybeh != BL_TH_NONE)
         beh = onlybeh;
     destroy_nhwindow(tmpwin);
     if (res > 0) {
         beh = picks->item.a_int;
         free((genericptr_t) picks);
     }
     return beh;
 }
 

status_hilite_menu_choose_updownboth

 int
 status_hilite_menu_choose_updownboth(fld, str)
 int fld;
 const char *str;
 {
     int res, ret = -2;
     winid tmpwin;
     char buf[BUFSZ];
     anything any;
     menu_item *picks = (menu_item *) 0;
 
     tmpwin = create_nhwindow(NHW_MENU);
     start_menu(tmpwin);
 
     if (str)
         Sprintf(buf, "%s or less", str);
     else
         Sprintf(buf, "Value goes down");
     any = zeroany;
     any.a_int = 10 + LT_VALUE;
     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
              buf, MENU_UNSELECTED);
 
     if (str)
         Sprintf(buf, "Exactly %s", str);
     else
         Sprintf(buf, "Value changes");
     any = zeroany;
     any.a_int = 10 + EQ_VALUE;
     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
              buf, MENU_UNSELECTED);
 
     if (str)
         Sprintf(buf, "%s or more", str);
     else
         Sprintf(buf, "Value goes up");
     any = zeroany;
     any.a_int = 10 + GT_VALUE;
     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
              buf, MENU_UNSELECTED);
 
     Sprintf(buf, "Select field %s value:", initblstats[fld].fldname);
     end_menu(tmpwin, buf);
 
     res = select_menu(tmpwin, PICK_ONE, &picks);
     destroy_nhwindow(tmpwin);
     if (res > 0) {
         ret = picks->item.a_int - 10;
         free((genericptr_t) picks);
     }
 
     return ret;
 }
 

status_hilite_menu_add

 STATIC_OVL boolean
 status_hilite_menu_add(origfld)
 int origfld;
 {
     int fld;
     int behavior;
     int lt_gt_eq = 0;
     int clr = NO_COLOR, atr = HL_UNDEF;
     struct hilite_s hilite;
     unsigned long cond = 0UL;
     char colorqry[BUFSZ];
     char attrqry[BUFSZ];
 
 choose_field:
     fld = origfld;
     if (fld == BL_FLUSH) {
         fld = status_hilite_menu_choose_field();
         if (fld == BL_FLUSH)
             return FALSE;
     }
 
     if (fld == BL_FLUSH)
         return FALSE;
 
     colorqry[0] = '\0';
     attrqry[0] = '\0';
 
     memset((genericptr_t) &hilite, 0, sizeof(struct hilite_s));
     hilite.set = FALSE; /* mark it "unset" */
     hilite.fld = fld;
 
 choose_behavior:
 
     behavior = status_hilite_menu_choose_behavior(fld);
 
     if (behavior == (BL_TH_NONE-1)) {
         return FALSE;
     } else if (behavior == BL_TH_NONE) {
         if (origfld == BL_FLUSH)
             goto choose_field;
         else
             return FALSE;
     }
 
     hilite.behavior = behavior;
 
 choose_value:
 
     if (behavior == BL_TH_VAL_PERCENTAGE
         || behavior == BL_TH_VAL_ABSOLUTE) {
         char inbuf[BUFSZ] = DUMMY, buf[BUFSZ];
         int val;
         boolean skipltgt = FALSE;
         boolean gotnum = FALSE;
         char *inp = inbuf;
         char *numstart = inbuf;
 
         inbuf[0] = '\0';
         Sprintf(buf, "Enter %svalue for %s threshold:",
                 (behavior == BL_TH_VAL_PERCENTAGE) ? "percentage " : "",
                 initblstats[fld].fldname);
         getlin(buf, inbuf);
         if (inbuf[0] == '\0' || inbuf[0] == '\033')
             goto choose_behavior;
 
         inp = trimspaces(inbuf);
         if (!*inp)
             goto choose_behavior;
 
         /* allow user to enter "<50%" or ">50" or just "50" */
         if (*inp == '>' || *inp == '<' || *inp == '=') {
             lt_gt_eq = (*inp == '>') ? GT_VALUE
                 : (*inp == '<') ? LT_VALUE : EQ_VALUE;
             skipltgt = TRUE;
             *inp = ' ';
             inp++;
             numstart++;
         }
         while (digit(*inp)) {
             inp++;
             gotnum = TRUE;
         }
         if (*inp == '%') {
             behavior = BL_TH_VAL_PERCENTAGE;
             *inp = '\0';
         } else if (!*inp) {
             behavior = BL_TH_VAL_ABSOLUTE;
         } else {
             /* some random characters */
             pline("\"%s\" is not a recognized number.", inp);
             goto choose_value;
         }
         if (!gotnum) {
             pline("Is that an invisible number?");
             goto choose_value;
         }
 
         val = atoi(numstart);
         if (behavior == BL_TH_VAL_PERCENTAGE) {
             if (initblstats[fld].idxmax == -1) {
                 pline("Field '%s' does not support percentage values.",
                       initblstats[fld].fldname);
                 behavior = BL_TH_VAL_ABSOLUTE;
                 goto choose_value;
             }
             if (val < 0 || val > 100) {
                 pline("Not a valid percent value.");
                 goto choose_value;
             }
         }
 
         if (!skipltgt) {
             lt_gt_eq = status_hilite_menu_choose_updownboth(fld, inbuf);
             if (lt_gt_eq == -2)
                 goto choose_value;
         }
 
         Sprintf(colorqry, "Choose a color for when %s is %s%s:",
                 initblstats[fld].fldname,
                 numstart,
                 (lt_gt_eq == EQ_VALUE) ? ""
                 : (lt_gt_eq == LT_VALUE) ? " or less"
                 : " or more");
 
         Sprintf(attrqry, "Choose attribute for when %s is %s%s:",
                 initblstats[fld].fldname,
                 inbuf,
                 (lt_gt_eq == EQ_VALUE) ? ""
                 : (lt_gt_eq == LT_VALUE) ? " or less"
                 : " or more");
 
         hilite.rel = lt_gt_eq;
         hilite.value.a_int = val;
     } else if (behavior == BL_TH_UPDOWN) {
         lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *)0);
         if (lt_gt_eq == -2)
             goto choose_behavior;
         Sprintf(colorqry, "Choose a color for when %s %s:",
                 initblstats[fld].fldname,
                 (lt_gt_eq == EQ_VALUE) ? "changes"
                 : (lt_gt_eq == LT_VALUE) ? "decreases"
                 : "increases");
         Sprintf(attrqry, "Choose attribute for when %s %s:",
                 initblstats[fld].fldname,
                 (lt_gt_eq == EQ_VALUE) ? "changes"
                 : (lt_gt_eq == LT_VALUE) ? "decreases"
                 : "increases");
         hilite.rel = lt_gt_eq;
     } else if (behavior == BL_TH_CONDITION) {
         cond = query_conditions();
         if (!cond) {
             if (origfld == BL_FLUSH)
                 goto choose_field;
             else
                 return FALSE;
         }
         Sprintf(colorqry, "Choose a color for conditions %s:",
                 conditionbitmask2str(cond));
         Sprintf(attrqry, "Choose attribute for conditions %s:",
                 conditionbitmask2str(cond));
     } else if (behavior == BL_TH_TEXTMATCH) {
         char qry_buf[BUFSZ];
         Sprintf(qry_buf, "%s %s text value to match:",
                 (fld == BL_CAP
                  || fld == BL_ALIGN
                  || fld == BL_HUNGER
                  || fld == BL_TITLE) ? "Choose" : "Enter",
                 initblstats[fld].fldname);
         if (fld == BL_CAP) {
             int rv = query_arrayvalue(qry_buf,
                                       enc_stat,
                                       SLT_ENCUMBER, OVERLOADED+1);
             if (rv < SLT_ENCUMBER)
                 goto choose_behavior;
 
             hilite.rel = TXT_VALUE;
             Strcpy(hilite.textmatch, enc_stat[rv]);
         } else if (fld == BL_ALIGN) {
             const char *aligntxt[] = {"chaotic", "neutral", "lawful"};
             int rv = query_arrayvalue(qry_buf,
                                       aligntxt, 0, 3);
             if (rv < 0)
                 goto choose_behavior;
 
             hilite.rel = TXT_VALUE;
             Strcpy(hilite.textmatch, aligntxt[rv]);
         } else if (fld == BL_HUNGER) {
             const char *hutxt[] = {"Satiated", (char *)0, "Hungry", "Weak",
                                    "Fainting", "Fainted", "Starved"};
             int rv = query_arrayvalue(qry_buf,
                                       hutxt,
                                       SATIATED, STARVED+1);
             if (rv < SATIATED)
                 goto choose_behavior;
 
             hilite.rel = TXT_VALUE;
             Strcpy(hilite.textmatch, hutxt[rv]);
         } else if (fld == BL_TITLE) {
             const char *rolelist[9];
             int i, rv;
 
             for (i = 0; i < 9; i++)
                 rolelist[i] = (flags.female && urole.rank[i].f)
                     ? urole.rank[i].f : urole.rank[i].m;
 
             rv = query_arrayvalue(qry_buf, rolelist, 0, 9);
             if (rv < 0)
                 goto choose_behavior;
 
             hilite.rel = TXT_VALUE;
             Strcpy(hilite.textmatch, rolelist[rv]);
         } else {
             char inbuf[BUFSZ] = DUMMY;
 
             inbuf[0] = '\0';
             getlin(qry_buf, inbuf);
             if (inbuf[0] == '\0' || inbuf[0] == '\033')
                 goto choose_behavior;
 
             hilite.rel = TXT_VALUE;
             if (strlen(inbuf) < QBUFSZ-1)
                 Strcpy(hilite.textmatch, inbuf);
             else
                 return FALSE;
         }
         Sprintf(colorqry, "Choose a color for when %s is '%s':",
                 initblstats[fld].fldname, hilite.textmatch);
         Sprintf(colorqry, "Choose attribute for when %s is '%s':",
                 initblstats[fld].fldname, hilite.textmatch);
     } else if (behavior == BL_TH_ALWAYS_HILITE) {
         Sprintf(colorqry, "Choose a color to always hilite %s:",
                 initblstats[fld].fldname);
         Sprintf(attrqry, "Choose attribute to always hilite %s:",
                 initblstats[fld].fldname);
     }
 
 choose_color:
 
     clr = query_color(colorqry);
     if (clr == -1) {
         if (behavior != BL_TH_ALWAYS_HILITE)
             goto choose_value;
         else
             goto choose_behavior;
     }
 
     atr = query_attr(attrqry); /* FIXME: pick multiple attrs */
     if (atr == -1)
         goto choose_color;
     if (atr == ATR_DIM)
         atr = HL_DIM;
     else if (atr == ATR_BLINK)
         atr = HL_BLINK;
     else if (atr == ATR_ULINE)
         atr = HL_ULINE;
     else if (atr == ATR_INVERSE)
         atr = HL_INVERSE;
     else if (atr == ATR_BOLD)
         atr = HL_BOLD;
     else if (atr == ATR_NONE)
         atr = HL_NONE;
     else
         atr = HL_UNDEF;
 
     if (clr == -1)
         clr = NO_COLOR;
 
     if (behavior == BL_TH_CONDITION) {
         char clrbuf[BUFSZ];
         char attrbuf[BUFSZ];
         char *tmpattr;
         if (atr == HL_DIM)
             cond_hilites[HL_ATTCLR_DIM] |= cond;
         else if (atr == HL_BLINK)
             cond_hilites[HL_ATTCLR_BLINK] |= cond;
         else if (atr == HL_ULINE)
             cond_hilites[HL_ATTCLR_ULINE] |= cond;
         else if (atr == HL_INVERSE)
             cond_hilites[HL_ATTCLR_INVERSE] |= cond;
         else if (atr == HL_BOLD)
             cond_hilites[HL_ATTCLR_BOLD] |= cond;
         else if (atr == HL_NONE) {
             cond_hilites[HL_ATTCLR_DIM]     = 0UL;
             cond_hilites[HL_ATTCLR_BLINK]   = 0UL;
             cond_hilites[HL_ATTCLR_ULINE]   = 0UL;
             cond_hilites[HL_ATTCLR_INVERSE] = 0UL;
             cond_hilites[HL_ATTCLR_BOLD]    = 0UL;
         }
         cond_hilites[clr] |= cond;
         (void) stripchars(clrbuf, " ", clr2colorname(clr));
         tmpattr = hlattr2attrname(atr, attrbuf, BUFSZ);
         if (tmpattr)
             Sprintf(eos(clrbuf), "&%s", tmpattr);
         pline("Added hilite condition/%s/%s",
               conditionbitmask2str(cond), clrbuf);
     } else {
         hilite.coloridx = clr | (atr << 8);
         hilite.anytype = initblstats[fld].anytype;
 
         status_hilite_add_threshold(fld, &hilite);
         pline("Added hilite %s", status_hilite2str(&hilite));
     }
     reset_status_hilites();
     return TRUE;
 }
 

status_hilite_remove

 boolean
 status_hilite_remove(id)
 int id;
 {
     struct _status_hilite_line_str *hlstr = status_hilite_str;
 
     while (hlstr && hlstr->id != id) {
         hlstr = hlstr->next;
     }
 
     if (!hlstr)
         return FALSE;
 
     if (hlstr->fld == BL_CONDITION) {
         int i;
 
         for (i = 0; i < CLR_MAX; i++)
             cond_hilites[i] &= ~hlstr->mask;
         cond_hilites[HL_ATTCLR_DIM] &= ~hlstr->mask;
         cond_hilites[HL_ATTCLR_BOLD] &= ~hlstr->mask;
         cond_hilites[HL_ATTCLR_BLINK] &= ~hlstr->mask;
         cond_hilites[HL_ATTCLR_ULINE] &= ~hlstr->mask;
         cond_hilites[HL_ATTCLR_INVERSE] &= ~hlstr->mask;
         return TRUE;
     } else {
         int fld = hlstr->fld;
         struct hilite_s *hl = blstats[0][fld].thresholds;
         struct hilite_s *hlprev = (struct hilite_s *) 0;
 
         if (hl) {
             while (hl) {
                 if (hlstr->hl == hl) {
                     if (hlprev)
                         hlprev->next = hl->next;
                     else {
                         blstats[0][fld].thresholds = hl->next;
                         blstats[1][fld].thresholds =
                             blstats[0][fld].thresholds;
                     }
                     free(hl);
                     return TRUE;
                 }
                 hlprev = hl;
                 hl = hl->next;
             }
         }
     }
     return FALSE;
 }
 

status_hilite_menu_fld

 boolean
 status_hilite_menu_fld(fld)
 int fld;
 {
     winid tmpwin;
     int i, res;
     menu_item *picks = (menu_item *) 0;
     anything any;
     int count = status_hilite_linestr_countfield(fld);
     struct _status_hilite_line_str *hlstr;
     char buf[BUFSZ];
     boolean acted = FALSE;
 
     if (!count) {
         if (status_hilite_menu_add(fld)) {
             status_hilite_linestr_done();
             status_hilite_linestr_gather();
             count = status_hilite_linestr_countfield(fld);
         } else
             return FALSE;
     }
 
     tmpwin = create_nhwindow(NHW_MENU);
     start_menu(tmpwin);
 
     if (count) {
         hlstr = status_hilite_str;
         while (hlstr) {
             if (hlstr->fld == fld) {
                 any = zeroany;
                 any.a_int = hlstr->id;
                 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
                          hlstr->str, MENU_UNSELECTED);
             }
             hlstr = hlstr->next;
         }
     } else {
         any = zeroany;
         Sprintf(buf, "No current hilites for %s", initblstats[fld].fldname);
         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
     }
 
     /* separator line */
     any = zeroany;
     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
 
     if (count) {
         any = zeroany;
         any.a_int = -1;
         add_menu(tmpwin, NO_GLYPH, &any, 'X', 0, ATR_NONE,
                  "Remove selected hilites", MENU_UNSELECTED);
     }
 
     any = zeroany;
     any.a_int = -2;
     add_menu(tmpwin, NO_GLYPH, &any, 'Z', 0, ATR_NONE,
              "Add a new hilite", MENU_UNSELECTED);
 
 
     Sprintf(buf, "Current %s hilites:", initblstats[fld].fldname);
     end_menu(tmpwin, buf);
 
     if ((res = select_menu(tmpwin, PICK_ANY, &picks)) > 0) {
         int mode = 0;
         for (i = 0; i < res; i++) {
             int idx = picks[i].item.a_int;
             if (idx == -1) {
                 /* delete selected hilites */
                 if (mode)
                     goto shlmenu_free;
                 mode = -1;
                 break;
             } else if (idx == -2) {
                 /* create a new hilite */
                 if (mode)
                     goto shlmenu_free;
                 mode = -2;
                 break;
             }
         }
 
         if (mode == -1) {
             /* delete selected hilites */
             for (i = 0; i < res; i++) {
                 int idx = picks[i].item.a_int;
                 if (idx > 0)
                     (void) status_hilite_remove(idx);
             }
             reset_status_hilites();
             acted = TRUE;
         } else if (mode == -2) {
             /* create a new hilite */
             if (status_hilite_menu_add(fld))
                 acted = TRUE;
         }
 
         free((genericptr_t) picks);
     }
 
 shlmenu_free:
 
     picks = (menu_item *) 0;
     destroy_nhwindow(tmpwin);
     return acted;
 }
 

status_hilites_viewall

 void
 status_hilites_viewall()
 {
     winid datawin;
     struct _status_hilite_line_str *hlstr = status_hilite_str;
     char buf[BUFSZ];
 
     datawin = create_nhwindow(NHW_TEXT);
 
     while (hlstr) {
         Sprintf(buf, "OPTIONS=hilite_status: %.*s",
                 (int)(BUFSZ - sizeof "OPTIONS=hilite_status: " - 1),
                 hlstr->str);
         putstr(datawin, 0, buf);
         hlstr = hlstr->next;
     }
 
     display_nhwindow(datawin, FALSE);
     destroy_nhwindow(datawin);
 }
 

status_hilite_menu

 boolean
 status_hilite_menu()
 {
     winid tmpwin;
     int i, res;
     menu_item *picks = (menu_item *) 0;
     anything any;
     boolean redo;
     int countall;
 
 shlmenu_redo:
     redo = FALSE;
 
     tmpwin = create_nhwindow(NHW_MENU);
     start_menu(tmpwin);
 
     status_hilite_linestr_gather();
 
     countall = status_hilite_linestr_countfield(BL_FLUSH);
 
     if (countall) {
         any = zeroany;
         any.a_int = -1;
         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
                  "View all hilites in config format", MENU_UNSELECTED);
 
         any = zeroany;
         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
     }
 
     for (i = 0; i < MAXBLSTATS; i++) {
         int count = status_hilite_linestr_countfield(i);
         char buf[BUFSZ];
 
         any = zeroany;
         any.a_int = (i+1);
         if (count)
             Sprintf(buf, "%-18s (%i defined)",
                     initblstats[i].fldname, count);
         else
             Sprintf(buf, "%-18s", initblstats[i].fldname);
         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
                  buf, MENU_UNSELECTED);
     }
 
 
     end_menu(tmpwin, "Status hilites:");
     if ((res = select_menu(tmpwin, PICK_ONE, &picks)) > 0) {
         i = picks->item.a_int - 1;
         if (i < 0)
             status_hilites_viewall();
         else
             (void) status_hilite_menu_fld(i);
         free((genericptr_t) picks);
         redo = TRUE;
     }
 
     picks = (menu_item *) 0;
     destroy_nhwindow(tmpwin);
     status_hilite_linestr_done();
 
     if (redo)
         goto shlmenu_redo;
 
     return TRUE;
 }
 
 #endif /* STATUS_HILITES */
 
 /*botl.c*/