1 /* NetHack 3.6 botl.c $NHDT-Date: 1506903619 2017/10/02 00:20:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.81 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Michael Allison, 2006. */
4 /* NetHack may be freely redistributed. See license for details. */
6 /* JNetHack Copyright */
7 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
8 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2019 */
9 /* JNetHack may be freely redistributed. See license for details. */
14 extern const char *hu_stat[]; /* defined in eat.c */
17 const char *const enc_stat[] = { "", "Burdened", "Stressed",
18 "Strained", "Overtaxed", "Overloaded" };
20 const char *const enc_stat[] = { "", "
\82æ
\82ë
\82ß
\82«", "
\88³
\94\97",
21 "
\8cÀ
\8aE", "
\89×
\8fd", "
\92´
\89ß"};
24 STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */
25 STATIC_DCL const char *NDECL(rank);
27 STATIC_DCL void NDECL(bot_via_windowport);
34 int st = ACURR(A_STR);
38 Sprintf(buf, "%2d", st - 100);
39 else if (st < STR18(100))
40 Sprintf(buf, "18/%02d", st - 18);
42 Sprintf(buf, "18/**");
44 Sprintf(buf, "%-1d", st);
52 static char newbot1[BUFSZ];
56 Strcpy(newbot1, plname);
57 if ('a' <= newbot1[0] && newbot1[0] <= 'z')
58 newbot1[0] += 'A' - 'a';
60 if(is_kanji1(newbot1, 9))
65 Sprintf(nb = eos(newbot1), " the ");
67 Sprintf(nb = eos(newbot1)," ");
73 Strcpy(mbot, mons[u.umonnum].mname);
74 while (mbot[k] != 0) {
75 if ((k == 0 || (k > 0 && mbot[k - 1] == ' ')) && 'a' <= mbot[k]
80 Strcpy(nb = eos(nb), mbot);
82 Strcpy(nb = eos(nb), rank());
84 Sprintf(nb = eos(nb), " ");
86 j = (int) ((nb + 2) - newbot1); /* strlen(newbot1) but less computation */
88 Sprintf(nb = eos(nb), "%*s", i - j, " "); /* pad with spaces */
91 Sprintf(nb = eos(nb), "St:%s Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d",
93 ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS),
96 Sprintf(nb = eos(nb), "
\8b:%s
\91\81:%-1d
\91Ï:%-1d
\92m:%-1d
\8c«:%-1d
\96£:%-1d ",
98 ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS),
102 Sprintf(nb = eos(nb),
103 (u.ualign.type == A_CHAOTIC)
105 : (u.ualign.type == A_NEUTRAL) ? " Neutral" : " Lawful");
107 Sprintf(nb = eos(nb),
108 (u.ualign.type == A_CHAOTIC)
110 : (u.ualign.type == A_NEUTRAL) ? "
\92\86\97§" : "
\92\81\8f\98");
115 Sprintf(nb = eos(nb), " S:%ld", botl_score());
117 Sprintf(nb = eos(nb), "%ld
\93_", botl_score());
127 int goldglyph = objnum_to_glyph(GOLD_PIECE);
128 (void) mapglyph(goldglyph, &goldch, &goldoc, &goldos, 0, 0);
129 iflags.invis_goldsym = ((char)goldch <= ' ');
135 static char newbot2[BUFSZ], /* MAXCO: botl.h */
136 /* dungeon location (and gold), hero health (HP, PW, AC),
137 experience (HD if poly'd, else Exp level and maybe Exp points),
138 time (in moves), varying number of status conditions */
139 dloc[QBUFSZ], hlth[QBUFSZ], expr[QBUFSZ], tmmv[QBUFSZ], cond[QBUFSZ];
141 unsigned dln, dx, hln, xln, tln, cln;
146 * Various min(x,9999)'s are to avoid having excessive values
147 * violate the field width assumptions in botl.h and should not
148 * impact normal play. Particularly 64-bit long for gold which
149 * could require many more digits if someone figures out a way
150 * to get and carry a really large (or negative) amount of it.
151 * Turn counter is also long, but we'll risk that.
154 /* dungeon location plus gold */
155 (void) describe_level(dloc); /* includes at least one trailing space */
156 if ((money = money_cnt(invent)) < 0L)
157 money = 0L; /* ought to issue impossible() and then discard gold */
158 Sprintf(eos(dloc), "%s:%-2ld", /* strongest hero can lift ~300000 gold */
159 (iflags.in_dumplog || iflags.invis_goldsym) ? "$"
160 : encglyph(objnum_to_glyph(GOLD_PIECE)),
161 min(money, 999999L));
163 /* '$' encoded as \GXXXXNNNN is 9 chars longer than display will need */
164 dx = strstri(dloc, "\\G") ? 9 : 0;
166 /* health and armor class (has trailing space for AC 0..9) */
167 hp = Upolyd ? u.mh : u.uhp;
168 hpmax = Upolyd ? u.mhmax : u.uhpmax;
172 Sprintf(hlth, "HP:%d(%d) Pw:%d(%d) AC:%-2d",
173 min(hp, 9999), min(hpmax, 9999),
174 min(u.uen, 9999), min(u.uenmax, 9999), u.uac);
176 Sprintf(hlth, "
\91Ì:%d(%d)
\96\82:%d(%d)
\8aZ:%-2d",
177 min(hp, 9999), min(hpmax, 9999),
178 min(u.uen, 9999), min(u.uenmax, 9999), u.uac);
184 Sprintf(expr, "HD:%d", mons[u.umonnum].mlevel);
185 else if (flags.showexp)
187 Sprintf(expr, "Xp:%u/%-1ld", u.ulevel, u.uexp);
189 Sprintf(expr, "
\8co
\8c±:%u/%-1ld", u.ulevel, u.uexp);
192 Sprintf(expr, "Exp:%u", u.ulevel);
194 Sprintf(expr, "
\8co
\8c±:%u", u.ulevel);
197 /* time/move counter */
200 Sprintf(tmmv, "T:%ld", moves);
202 Sprintf(tmmv, "
\95à:%ld", moves);
207 /* status conditions; worst ones first */
208 cond[0] = '\0'; /* once non-empty, cond will have a leading space */
211 * Stoned, Slimed, Strangled, and both types of Sick are all fatal
212 * unless remedied before timeout expires. Should we order them by
213 * shortest time left? [Probably not worth the effort, since it's
214 * unusual for more than one of them to apply at a time.]
218 Strcpy(nb = eos(nb), " Stone");
220 Strcpy(nb = eos(nb), "
\90Î
\89»");
223 Strcpy(nb = eos(nb), " Slime");
225 Strcpy(nb = eos(nb), "
\82Ç
\82ë
\82Ç
\82ë");
228 Strcpy(nb = eos(nb), " Strngl");
230 Strcpy(nb = eos(nb), "
\92\82\91§");
232 if (u.usick_type & SICK_VOMITABLE)
234 Strcpy(nb = eos(nb), " FoodPois");
236 Strcpy(nb = eos(nb), "
\90H
\93Å");
237 if (u.usick_type & SICK_NONVOMITABLE)
239 Strcpy(nb = eos(nb), " TermIll");
241 Strcpy(nb = eos(nb), "
\95a
\8bC");
243 if (u.uhs != NOT_HUNGRY)
244 Sprintf(nb = eos(nb), " %s", hu_stat[u.uhs]);
245 if ((cap = near_capacity()) > UNENCUMBERED)
246 Sprintf(nb = eos(nb), " %s", enc_stat[cap]);
249 Strcpy(nb = eos(nb), " Blind");
251 Strcpy(nb = eos(nb), "
\96Ó
\96Ú");
254 Strcpy(nb = eos(nb), " Deaf");
256 Strcpy(nb = eos(nb), "
\8e¨
\98W");
259 Strcpy(nb = eos(nb), " Stun");
261 Strcpy(nb = eos(nb), " á¿
\9dò");
264 Strcpy(nb = eos(nb), " Conf");
266 Strcpy(nb = eos(nb), "
\8d¬
\97\90");
269 Strcpy(nb = eos(nb), " Hallu");
271 Strcpy(nb = eos(nb), "
\8c¶
\8ao");
272 /* levitation and flying are mutually exclusive; riding is not */
275 Strcpy(nb = eos(nb), " Lev");
277 Strcpy(nb = eos(nb), "
\95\82\97V");
280 Strcpy(nb = eos(nb), " Fly");
282 Strcpy(nb = eos(nb), "
\94ò
\8ds");
285 Strcpy(nb = eos(nb), " Ride");
287 Strcpy(nb = eos(nb), "
\8bR
\8fæ");
291 * Put the pieces together. If they all fit, keep the traditional
292 * sequence. Otherwise, move least important parts to the end in
293 * case the interface side of things has to truncate. Note that
294 * dloc[] contains '$' encoded in ten character sequence \GXXXXNNNN
295 * so we want to test its display length rather than buffer length.
297 * We don't have an actual display limit here, so have to go by the
298 * width of the map. Since we're reordering rather than truncating,
299 * wider displays can still show wider status than the map if the
300 * interface supports that.
302 if ((dln - dx) + 1 + hln + 1 + xln + 1 + tln + 1 + cln <= COLNO) {
303 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, expr, tmmv, cond);
305 if (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1 > MAXCO) {
306 panic("bot2: second status line exceeds MAXCO (%u > %d)",
307 (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1), MAXCO);
308 } else if ((dln - dx) + 1 + hln + 1 + xln + 1 + cln <= COLNO) {
309 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, expr, cond, tmmv);
310 } else if ((dln - dx) + 1 + hln + 1 + cln <= COLNO) {
311 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, cond, expr, tmmv);
313 Sprintf(newbot2, "%s %s %s %s %s", hlth, cond, dloc, expr, tmmv);
315 /* only two or three consecutive spaces available to squeeze out */
324 if (youmonst.data && iflags.status_updates) {
325 #ifdef STATUS_HILITES
326 bot_via_windowport();
328 curs(WIN_STATUS, 1, 0);
329 putstr(WIN_STATUS, 0, do_statusline1());
330 curs(WIN_STATUS, 1, 1);
331 putmixed(WIN_STATUS, 0, do_statusline2());
334 context.botl = context.botlx = 0;
337 /* convert experience level (1..30) to rank index (0..8) */
342 return (xlev <= 2) ? 0 : (xlev <= 30) ? ((xlev + 2) / 4) : 8;
345 #if 0 /* not currently needed */
346 /* convert rank index (0..8) to experience level (1..30) */
351 return (rank <= 0) ? 1 : (rank <= 8) ? ((rank * 4) - 2) : 30;
356 rank_of(lev, monnum, female)
361 register const struct Role *role;
365 for (role = roles; role->name.m; role++)
366 if (monnum == role->malenum || monnum == role->femalenum)
372 for (i = xlev_to_rank((int) lev); i >= 0; i--) {
373 if (female && role->rank[i].f)
374 return role->rank[i].f;
376 return role->rank[i].m;
379 /* Try the role name, instead */
380 if (female && role->name.f)
382 else if (role->name.m)
387 return "
\83v
\83\8c\83C
\83\84\81[";
390 STATIC_OVL const char *
393 return rank_of(u.ulevel, Role_switch, flags.female);
397 title_to_mon(str, rank_indx, title_length)
399 int *rank_indx, *title_length;
403 /* Loop through each of the roles */
404 for (i = 0; roles[i].name.m; i++)
405 for (j = 0; j < 9; j++) {
406 if (roles[i].rank[j].m
407 && !strncmpi(str, roles[i].rank[j].m,
408 strlen(roles[i].rank[j].m))) {
412 *title_length = strlen(roles[i].rank[j].m);
413 return roles[i].malenum;
415 if (roles[i].rank[j].f
416 && !strncmpi(str, roles[i].rank[j].f,
417 strlen(roles[i].rank[j].f))) {
421 *title_length = strlen(roles[i].rank[j].f);
422 return (roles[i].femalenum != NON_PM) ? roles[i].femalenum
432 register int i, r, maxr = 0;
433 for (i = 0; i < 9; i++) {
434 if (urole.rank[i].m && (r = strlen(urole.rank[i].m)) > maxr)
436 if (urole.rank[i].f && (r = strlen(urole.rank[i].f)) > maxr)
447 long deepest = deepest_lev_reached(FALSE);
450 utotal = money_cnt(invent) + hidden_gold();
451 if ((utotal -= u.umoney0) < 0L)
453 utotal += u.urexp + (50 * (deepest - 1))
454 + (deepest > 30 ? 10000 : deepest > 20 ? 1000 * (deepest - 20) : 0);
455 if (utotal < u.urexp)
456 utotal = LONG_MAX; /* wrap around */
459 #endif /* SCORE_ON_BOTL */
461 /* provide the name of the current level for display by various ports */
468 /* TODO: Add in dungeon name */
470 Sprintf(buf, "%s ", dungeons[u.uz.dnum].dname);
471 else if (In_quest(&u.uz))
473 Sprintf(buf, "Home %d ", dunlev(&u.uz));
475 Sprintf(buf, "
\8cÌ
\8b½ %d ", dunlev(&u.uz));
476 else if (In_endgame(&u.uz))
478 Sprintf(buf, Is_astralevel(&u.uz) ? "Astral Plane " : "End Game ");
480 Sprintf(buf, Is_astralevel(&u.uz) ? "
\90¸
\97ì
\8aE " : "
\8dÅ
\8fI
\8e\8e\97û ");
482 /* ports with more room may expand this one */
484 Sprintf(buf, "Dlvl:%-2d ", depth(&u.uz));
486 Sprintf(buf, "
\92n
\89º:%-2d ", depth(&u.uz));
492 /* =======================================================================*/
493 /* statusnew routines */
494 /* =======================================================================*/
496 /* structure that tracks the status details in the core */
498 #ifdef STATUS_HILITES
500 enum statusfields fld;
505 char textmatch[QBUFSZ];
506 enum relationships rel;
508 struct hilite_s *next;
513 unsigned long bitmask;
515 #endif /* STATUS_HILITES */
520 long time; /* moves when this field hilite times out */
521 boolean chg; /* need to recalc time? */
526 enum statusfields idxmax;
527 enum statusfields fld;
528 #ifdef STATUS_HILITES
529 struct hilite_s *thresholds;
534 STATIC_DCL void NDECL(init_blstats);
535 STATIC_DCL char *FDECL(anything_to_s, (char *, anything *, int));
536 STATIC_OVL int FDECL(percentage, (struct istat_s *, struct istat_s *));
537 STATIC_OVL int FDECL(compare_blstats, (struct istat_s *, struct istat_s *));
538 STATIC_DCL boolean FDECL(evaluate_and_notify_windowport_field,
539 (int, boolean *, int, int));
540 STATIC_DCL void FDECL(evaluate_and_notify_windowport, (boolean *, int, int));
542 #ifdef STATUS_HILITES
543 STATIC_DCL boolean FDECL(hilite_reset_needed, (struct istat_s *, long));
544 STATIC_DCL void FDECL(s_to_anything, (anything *, char *, int));
545 STATIC_DCL boolean FDECL(is_ltgt_percentnumber, (const char *));
546 STATIC_DCL boolean FDECL(has_ltgt_percentnumber, (const char *));
547 STATIC_DCL boolean FDECL(parse_status_hl2, (char (*)[QBUFSZ],BOOLEAN_P));
548 STATIC_DCL boolean FDECL(parse_condition, (char (*)[QBUFSZ], int));
549 STATIC_DCL void FDECL(merge_bestcolor, (int *, int));
550 STATIC_DCL void FDECL(get_hilite_color, (int, int, genericptr_t, int,
552 STATIC_DCL unsigned long FDECL(match_str2conditionbitmask, (const char *));
553 STATIC_DCL unsigned long FDECL(str2conditionbitmask, (char *));
554 STATIC_DCL void FDECL(split_clridx, (int, int *, int *));
555 STATIC_DCL char *FDECL(hlattr2attrname, (int, char *, int));
556 STATIC_DCL void FDECL(status_hilite_linestr_add, (int, struct hilite_s *,
557 unsigned long, const char *));
559 STATIC_DCL void NDECL(status_hilite_linestr_done);
560 STATIC_DCL int FDECL(status_hilite_linestr_countfield, (int));
561 STATIC_DCL void NDECL(status_hilite_linestr_gather_conditions);
562 STATIC_DCL void NDECL(status_hilite_linestr_gather);
563 STATIC_DCL char *FDECL(status_hilite2str, (struct hilite_s *));
564 STATIC_DCL boolean FDECL(status_hilite_menu_add, (int));
565 #define has_hilite(i) (blstats[0][(i)].thresholds)
568 #define INIT_BLSTAT(name, fmtstr, anytyp, wid, fld) \
569 { name, fmtstr, 0L, FALSE, anytyp, { (genericptr_t) 0 }, (char *) 0, \
571 #define INIT_BLSTATP(name, fmtstr, anytyp, wid, maxfld, fld) \
572 { name, fmtstr, 0L, FALSE, anytyp, { (genericptr_t) 0 }, (char *) 0, \
575 /* If entries are added to this, botl.h will require updating too */
576 STATIC_DCL struct istat_s initblstats[MAXBLSTATS] = {
577 INIT_BLSTAT("title", "%s", ANY_STR, 80, BL_TITLE),
578 INIT_BLSTAT("strength", " St:%s", ANY_INT, 10, BL_STR),
579 INIT_BLSTAT("dexterity", " Dx:%s", ANY_INT, 10, BL_DX),
580 INIT_BLSTAT("constitution", " Co:%s", ANY_INT, 10, BL_CO),
581 INIT_BLSTAT("intelligence", " In:%s", ANY_INT, 10, BL_IN),
582 INIT_BLSTAT("wisdom", " Wi:%s", ANY_INT, 10, BL_WI),
583 INIT_BLSTAT("charisma", " Ch:%s", ANY_INT, 10, BL_CH),
584 INIT_BLSTAT("alignment", " %s", ANY_STR, 40, BL_ALIGN),
585 INIT_BLSTAT("score", " S:%s", ANY_LONG, 20, BL_SCORE),
586 INIT_BLSTAT("carrying-capacity", " %s", ANY_LONG, 20, BL_CAP),
587 INIT_BLSTAT("gold", " %s", ANY_LONG, 30, BL_GOLD),
588 INIT_BLSTATP("power", " Pw:%s", ANY_INT, 10, BL_ENEMAX, BL_ENE),
589 INIT_BLSTAT("power-max", "(%s)", ANY_INT, 10, BL_ENEMAX),
590 INIT_BLSTAT("experience-level", " Xp:%s", ANY_LONG, 10, BL_XP),
591 INIT_BLSTAT("armor-class", " AC:%s", ANY_INT, 10, BL_AC),
592 INIT_BLSTAT("HD", " HD:%s", ANY_INT, 10, BL_HD),
593 INIT_BLSTAT("time", " T:%s", ANY_INT, 20, BL_TIME),
594 INIT_BLSTAT("hunger", " %s", ANY_UINT, 40, BL_HUNGER),
595 INIT_BLSTATP("hitpoints", " HP:%s", ANY_INT, 10, BL_HPMAX, BL_HP),
596 INIT_BLSTAT("hitpoints-max", "(%s)", ANY_INT, 10, BL_HPMAX),
597 INIT_BLSTAT("dungeon-level", "%s", ANY_STR, 80, BL_LEVELDESC),
598 INIT_BLSTAT("experience", "/%s", ANY_LONG, 20, BL_EXP),
599 INIT_BLSTAT("condition", "%s", ANY_MASK32, 0, BL_CONDITION)
605 struct istat_s blstats[2][MAXBLSTATS];
606 static boolean blinit = FALSE, update_all = FALSE;
607 static boolean valset[MAXBLSTATS];
608 unsigned long blcolormasks[CLR_MAX];
609 static long bl_hilite_moves = 0L;
611 /* we don't put this next declaration in #ifdef STATUS_HILITES.
612 * In the absence of STATUS_HILITES, each array
613 * element will be 0 however, and quite meaningless,
614 * but we need to pass the first array element as
615 * the final argument of status_update, with or
616 * without STATUS_HILITES.
618 unsigned long cond_hilites[BL_ATTCLR_MAX];
625 static int i, idx = 0, idx_p, cap;
629 panic("bot before init.");
632 idx = 1 - idx; /* 0 -> 1, 1 -> 0 */
634 /* clear the "value set" indicators */
635 (void) memset((genericptr_t) valset, 0, MAXBLSTATS * sizeof (boolean));
638 * Note: min(x,9999) - we enforce the same maximum on hp, maxhp,
639 * pw, maxpw, and gold as basic status formatting so that the two
640 * modes of status display don't produce different information.
644 * Player name and title.
646 Strcpy(nb = buf, plname);
647 nb[0] = highc(nb[0]);
650 Sprintf(nb = eos(nb), " the ");
652 Sprintf(nb = eos(nb), " ");
655 for (i = 0, nb = strcpy(eos(nb), mons[u.umonnum].mname); nb[i]; i++)
656 if (i == 0 || nb[i - 1] == ' ')
657 nb[i] = highc(nb[i]);
659 Strcpy(nb = eos(nb), rank());
660 Sprintf(blstats[idx][BL_TITLE].val, "%-29s", buf);
661 valset[BL_TITLE] = TRUE; /* indicate val already set */
664 blstats[idx][BL_STR].a.a_int = ACURR(A_STR);
665 Strcpy(blstats[idx][BL_STR].val, get_strength_str());
666 valset[BL_STR] = TRUE; /* indicate val already set */
668 /* Dexterity, constitution, intelligence, wisdom, charisma. */
669 blstats[idx][BL_DX].a.a_int = ACURR(A_DEX);
670 blstats[idx][BL_CO].a.a_int = ACURR(A_CON);
671 blstats[idx][BL_IN].a.a_int = ACURR(A_INT);
672 blstats[idx][BL_WI].a.a_int = ACURR(A_WIS);
673 blstats[idx][BL_CH].a.a_int = ACURR(A_CHA);
677 Strcpy(blstats[idx][BL_ALIGN].val, (u.ualign.type == A_CHAOTIC)
679 : (u.ualign.type == A_NEUTRAL)
683 Strcpy(blstats[idx][BL_ALIGN].val, (u.ualign.type == A_CHAOTIC)
685 : (u.ualign.type == A_NEUTRAL)
691 blstats[idx][BL_SCORE].a.a_long =
693 flags.showscore ? botl_score() :
698 i = Upolyd ? u.mh : u.uhp;
701 blstats[idx][BL_HP].a.a_int = min(i, 9999);
702 i = Upolyd ? u.mhmax : u.uhpmax;
703 blstats[idx][BL_HPMAX].a.a_int = min(i, 9999);
706 (void) describe_level(blstats[idx][BL_LEVELDESC].val);
707 valset[BL_LEVELDESC] = TRUE; /* indicate val already set */
710 if ((money = money_cnt(invent)) < 0L)
711 money = 0L; /* ought to issue impossible() and then discard gold */
712 blstats[idx][BL_GOLD].a.a_long = min(money, 999999L);
714 * The tty port needs to display the current symbol for gold
715 * as a field header, so to accommodate that we pass gold with
716 * that already included. If a window port needs to use the text
717 * gold amount without the leading "$:" the port will have to
718 * skip past ':' to the value pointer it was passed in status_update()
719 * for the BL_GOLD case.
721 * Another quirk of BL_GOLD is that the field display may have
722 * changed if a new symbol set was loaded, or we entered or left
725 * The currency prefix is encoded as ten character \GXXXXNNNN
728 Sprintf(blstats[idx][BL_GOLD].val, "%s:%ld",
729 encglyph(objnum_to_glyph(GOLD_PIECE)),
730 blstats[idx][BL_GOLD].a.a_long);
731 valset[BL_GOLD] = TRUE; /* indicate val already set */
733 /* Power (magical energy) */
734 blstats[idx][BL_ENE].a.a_int = min(u.uen, 9999);
735 blstats[idx][BL_ENEMAX].a.a_int = min(u.uenmax, 9999);
738 blstats[idx][BL_AC].a.a_int = u.uac;
740 /* Monster level (if Upolyd) */
741 blstats[idx][BL_HD].a.a_int = Upolyd ? (int) mons[u.umonnum].mlevel : 0;
744 blstats[idx][BL_XP].a.a_int = u.ulevel;
745 blstats[idx][BL_EXP].a.a_int = u.uexp;
748 blstats[idx][BL_TIME].a.a_long = moves;
751 blstats[idx][BL_HUNGER].a.a_uint = u.uhs;
752 Strcpy(blstats[idx][BL_HUNGER].val,
753 (u.uhs != NOT_HUNGRY) ? hu_stat[u.uhs] : "");
754 valset[BL_HUNGER] = TRUE;
756 /* Carrying capacity */
757 cap = near_capacity();
758 blstats[idx][BL_CAP].a.a_int = cap;
759 Strcpy(blstats[idx][BL_CAP].val,
760 (cap > UNENCUMBERED) ? enc_stat[cap] : "");
761 valset[BL_CAP] = TRUE;
764 blstats[idx][BL_CONDITION].a.a_ulong = 0L;
766 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STONE;
768 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_SLIME;
770 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STRNGL;
771 if (Sick && (u.usick_type & SICK_VOMITABLE) != 0)
772 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FOODPOIS;
773 if (Sick && (u.usick_type & SICK_NONVOMITABLE) != 0)
774 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_TERMILL;
776 * basic formatting puts hunger status and encumbrance here
779 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_BLIND;
781 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_DEAF;
783 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STUN;
785 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_CONF;
787 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_HALLU;
788 /* levitation and flying are mututally exclusive */
790 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_LEV;
792 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FLY;
794 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_RIDE;
795 evaluate_and_notify_windowport(valset, idx, idx_p);
799 evaluate_and_notify_windowport_field(fld, valsetlist, idx, idx_p)
803 static int oldrndencode = 0;
804 int pc, chg, color = NO_COLOR;
806 boolean updated = FALSE, reset;
807 struct istat_s *curr = NULL, *prev = NULL;
808 enum statusfields idxmax;
811 * Now pass the changed values to window port.
813 anytype = blstats[idx][fld].anytype;
814 curr = &blstats[idx][fld];
815 prev = &blstats[idx_p][fld];
818 chg = update_all ? 0 : compare_blstats(prev, curr);
820 /* Temporary? hack: moveloop()'s prolog for a new game sets
821 * context.rndencode after the status window has been init'd,
822 * so $:0 has already been encoded and cached by the window
823 * port. Without this hack, gold's \G sequence won't be
824 * recognized and ends up being displayed as-is for 'update_all'.
826 if (context.rndencode != oldrndencode && fld == BL_GOLD) {
828 oldrndencode = context.rndencode;
832 #ifdef STATUS_HILITES
833 if (!update_all && !chg) {
834 reset = hilite_reset_needed(prev, bl_hilite_moves);
836 curr->time = prev->time = 0L;
840 if (update_all || chg || reset) {
841 idxmax = curr->idxmax;
842 pc = (idxmax > BL_FLUSH) ? percentage(curr, &blstats[idx][idxmax]) : 0;
844 if (!valsetlist[fld])
845 (void) anything_to_s(curr->val, &curr->a, anytype);
847 if (anytype != ANY_MASK32) {
848 #ifdef STATUS_HILITES
849 if ((chg || *curr->val)) {
850 get_hilite_color(idx, fld, (genericptr_t)&curr->a,
857 #endif /* STATUS_HILITES */
858 status_update(fld, (genericptr_t) curr->val,
859 chg, pc, color, &cond_hilites[0]);
861 /* Color for conditions is done through cond_hilites[] */
862 status_update(fld, (genericptr_t) &curr->a.a_ulong, chg, pc,
863 color, &cond_hilites[0]);
865 curr->chg = prev->chg = TRUE;
872 evaluate_and_notify_windowport(valsetlist, idx, idx_p)
877 boolean updated = FALSE;
880 * Now pass the changed values to window port.
882 for (i = 0; i < MAXBLSTATS; i++) {
883 if (((i == BL_SCORE) && !flags.showscore)
884 || ((i == BL_EXP) && !flags.showexp)
885 || ((i == BL_TIME) && !flags.time)
886 || ((i == BL_HD) && !Upolyd)
887 || ((i == BL_XP || i == BL_EXP) && Upolyd))
889 if (evaluate_and_notify_windowport_field(i, valsetlist, idx, idx_p))
893 * It is possible to get here, with nothing having been pushed
894 * to the window port, when none of the info has changed. In that
895 * case, we need to force a call to status_update() when
896 * context.botlx is set. The tty port in particular has a problem
897 * if that isn't done, since it sets context.botlx when a menu or
898 * text display obliterates the status line.
900 * To work around it, we call status_update() with fictitious
901 * index of BL_FLUSH (-1).
903 if ((context.botlx && !updated)
904 || (windowprocs.wincap2 & WC2_FLUSH_STATUS) != 0L)
905 status_update(BL_FLUSH, (genericptr_t) 0, 0, 0,
906 NO_COLOR, &cond_hilites[0]);
908 context.botl = context.botlx = 0;
913 status_eval_next_unhilite()
916 struct istat_s *curr = NULL;
917 long next_unhilite, this_unhilite;
919 bl_hilite_moves = moves;
920 /* figure out when the next unhilight needs to be performed */
922 for (i = 0; i < MAXBLSTATS; ++i) {
923 curr = &blstats[0][i]; /* blstats[0][*].time == blstats[1][*].time */
926 struct istat_s *prev = &blstats[1][i];
928 #ifdef STATUS_HILITES
929 curr->time = prev->time = (bl_hilite_moves + iflags.hilite_delta);
931 curr->chg = prev->chg = FALSE;
934 this_unhilite = curr->time;
935 if (this_unhilite > 0L
936 && (next_unhilite == 0L || this_unhilite < next_unhilite)
937 #ifdef STATUS_HILITES
938 && hilite_reset_needed(curr, this_unhilite + 1L)
941 next_unhilite = this_unhilite;
943 if (next_unhilite > 0L && next_unhilite < bl_hilite_moves)
948 status_initialize(reassessment)
950 reassessment; /* TRUE = just reassess fields w/o other initialization*/
953 const char *fieldfmt = (const char *) 0;
954 const char *fieldname = (const char *) 0;
958 impossible("2nd status_initialize with full init.");
960 (*windowprocs.win_status_init)();
963 for (i = 0; i < MAXBLSTATS; ++i) {
964 enum statusfields fld = initblstats[i].fld;
965 boolean fldenabled = (fld == BL_SCORE) ? flags.showscore
966 : (fld == BL_XP) ? (boolean) !Upolyd
967 : (fld == BL_HD) ? (boolean) Upolyd
968 : (fld == BL_TIME) ? flags.time
969 : (fld == BL_EXP) ? (boolean) (flags.showexp && !Upolyd)
972 fieldname = initblstats[i].fldname;
973 fieldfmt = initblstats[i].fldfmt;
974 status_enablefield(fld, fieldname, fieldfmt, fldenabled);
984 /* call the window port cleanup routine first */
985 (*windowprocs.win_status_finish)();
987 /* free memory that we alloc'd now */
988 for (i = 0; i < MAXBLSTATS; ++i) {
989 if (blstats[0][i].val)
990 free((genericptr_t) blstats[0][i].val), blstats[0][i].val = 0;
991 if (blstats[1][i].val)
992 free((genericptr_t) blstats[1][i].val), blstats[1][i].val = 0;
993 #ifdef STATUS_HILITES
994 if (blstats[0][i].thresholds) {
995 struct hilite_s *temp = blstats[0][i].thresholds,
996 *next = (struct hilite_s *)0;
1000 blstats[0][i].thresholds = (struct hilite_s *)0;
1001 blstats[1][i].thresholds = blstats[0][i].thresholds;
1005 #endif /* STATUS_HILITES */
1012 static boolean initalready = FALSE;
1016 impossible("init_blstats called more than once.");
1021 for (i = BEFORE; i <= NOW; ++i) {
1022 for (j = 0; j < MAXBLSTATS; ++j) {
1023 #ifdef STATUS_HILITES
1024 struct hilite_s *keep_hilite_chain = blstats[i][j].thresholds;
1026 blstats[i][j] = initblstats[j];
1027 blstats[i][j].a = zeroany;
1028 if (blstats[i][j].valwidth) {
1029 blstats[i][j].val = (char *) alloc(blstats[i][j].valwidth);
1030 blstats[i][j].val[0] = '\0';
1032 blstats[i][j].val = (char *) 0;
1033 #ifdef STATUS_HILITES
1034 if (keep_hilite_chain)
1035 blstats[i][j].thresholds = keep_hilite_chain;
1042 * This compares the previous stat with the current stat,
1043 * and returns one of the following results based on that:
1045 * if prev_value < new_value (stat went up, increased)
1048 * if prev_value > new_value (stat went down, decreased)
1051 * if prev_value == new_value (stat stayed the same)
1055 * - for bitmasks, 0 = stayed the same, 1 = changed
1056 * - for strings, 0 = stayed the same, 1 = changed
1060 compare_blstats(bl1, bl2)
1061 struct istat_s *bl1, *bl2;
1063 int anytype, result = 0;
1066 panic("compare_blstat: bad istat pointer %s, %s",
1067 fmt_ptr((genericptr_t) bl1), fmt_ptr((genericptr_t) bl2));
1070 anytype = bl1->anytype;
1071 if ((!bl1->a.a_void || !bl2->a.a_void)
1072 && (anytype == ANY_IPTR || anytype == ANY_UPTR || anytype == ANY_LPTR
1073 || anytype == ANY_ULPTR)) {
1074 panic("compare_blstat: invalid pointer %s, %s",
1075 fmt_ptr((genericptr_t) bl1->a.a_void),
1076 fmt_ptr((genericptr_t) bl2->a.a_void));
1081 result = (bl1->a.a_int < bl2->a.a_int)
1083 : (bl1->a.a_int > bl2->a.a_int) ? -1 : 0;
1086 result = (*bl1->a.a_iptr < *bl2->a.a_iptr)
1088 : (*bl1->a.a_iptr > *bl2->a.a_iptr) ? -1 : 0;
1091 result = (bl1->a.a_long < bl2->a.a_long)
1093 : (bl1->a.a_long > bl2->a.a_long) ? -1 : 0;
1096 result = (*bl1->a.a_lptr < *bl2->a.a_lptr)
1098 : (*bl1->a.a_lptr > *bl2->a.a_lptr) ? -1 : 0;
1101 result = (bl1->a.a_uint < bl2->a.a_uint)
1103 : (bl1->a.a_uint > bl2->a.a_uint) ? -1 : 0;
1106 result = (*bl1->a.a_uptr < *bl2->a.a_uptr)
1108 : (*bl1->a.a_uptr > *bl2->a.a_uptr) ? -1 : 0;
1111 result = (bl1->a.a_ulong < bl2->a.a_ulong)
1113 : (bl1->a.a_ulong > bl2->a.a_ulong) ? -1 : 0;
1116 result = (*bl1->a.a_ulptr < *bl2->a.a_ulptr)
1118 : (*bl1->a.a_ulptr > *bl2->a.a_ulptr) ? -1 : 0;
1121 result = sgn(strcmp(bl1->val, bl2->val));
1124 result = (bl1->a.a_ulong != bl2->a.a_ulong);
1133 anything_to_s(buf, a, anytype)
1143 Sprintf(buf, "%lu", a->a_ulong);
1146 Sprintf(buf, "%lx", a->a_ulong);
1149 Sprintf(buf, "%ld", a->a_long);
1152 Sprintf(buf, "%d", a->a_int);
1155 Sprintf(buf, "%u", a->a_uint);
1158 Sprintf(buf, "%d", *a->a_iptr);
1161 Sprintf(buf, "%ld", *a->a_lptr);
1164 Sprintf(buf, "%lu", *a->a_ulptr);
1167 Sprintf(buf, "%u", *a->a_uptr);
1169 case ANY_STR: /* do nothing */
1178 #ifdef STATUS_HILITES
1180 s_to_anything(a, buf, anytype)
1190 a->a_long = atol(buf);
1193 a->a_int = atoi(buf);
1196 a->a_uint = (unsigned) atoi(buf);
1199 a->a_ulong = (unsigned long) atol(buf);
1203 *a->a_iptr = atoi(buf);
1207 *a->a_uptr = (unsigned) atoi(buf);
1211 *a->a_lptr = atol(buf);
1215 *a->a_ulptr = (unsigned long) atol(buf);
1218 a->a_ulong = (unsigned long) atol(buf);
1226 #endif /* STATUS_HILITES */
1229 percentage(bl, maxbl)
1230 struct istat_s *bl, *maxbl;
1237 unsigned long ulval;
1239 if (!bl || !maxbl) {
1240 impossible("percentage: bad istat pointer %s, %s",
1241 fmt_ptr((genericptr_t) bl), fmt_ptr((genericptr_t) maxbl));
1245 ival = 0, lval = 0L, uval = 0U, ulval = 0UL;
1246 anytype = bl->anytype;
1247 if (maxbl->a.a_void) {
1251 result = ((100 * ival) / maxbl->a.a_int);
1254 lval = bl->a.a_long;
1255 result = (int) ((100L * lval) / maxbl->a.a_long);
1258 uval = bl->a.a_uint;
1259 result = (int) ((100U * uval) / maxbl->a.a_uint);
1262 ulval = bl->a.a_ulong;
1263 result = (int) ((100UL * ulval) / maxbl->a.a_ulong);
1266 ival = *bl->a.a_iptr;
1267 result = ((100 * ival) / (*maxbl->a.a_iptr));
1270 lval = *bl->a.a_lptr;
1271 result = (int) ((100L * lval) / (*maxbl->a.a_lptr));
1274 uval = *bl->a.a_uptr;
1275 result = (int) ((100U * uval) / (*maxbl->a.a_uptr));
1278 ulval = *bl->a.a_ulptr;
1279 result = (int) ((100UL * ulval) / (*maxbl->a.a_ulptr));
1283 /* don't let truncation from integer division produce a zero result
1284 from a non-zero input; note: if we ever change to something like
1285 ((((1000 * val) / max) + 5) / 10) for a rounded result, we'll
1286 also need to check for and convert false 100 to 99 */
1287 if (result == 0 && (ival != 0 || lval != 0L || uval != 0U || ulval != 0UL))
1294 #ifdef STATUS_HILITES
1296 /****************************************************************************/
1297 /* Core status hiliting support */
1298 /****************************************************************************/
1300 struct hilite_s status_hilites[MAXBLSTATS];
1302 static struct fieldid_t {
1303 const char *fieldname;
1304 enum statusfields fldid;
1305 } fieldids_alias[] = {
1306 {"characteristics", BL_CHARACTERISTICS},
1310 {"points", BL_SCORE},
1313 {"pw-max", BL_ENEMAX},
1317 {"hit-dice", BL_HD},
1320 {"hp-max", BL_HPMAX},
1321 {"dgn", BL_LEVELDESC},
1324 {"flags", BL_CONDITION},
1328 /* field name to bottom line index */
1329 STATIC_OVL enum statusfields
1330 fldname_to_bl_indx(name)
1333 int i, nmatches = 0, fld = 0;
1335 if (name && *name) {
1336 /* check matches to canonical names */
1337 for (i = 0; i < SIZE(initblstats); i++)
1338 if (fuzzymatch(initblstats[i].fldname, name, " -_", TRUE)) {
1339 fld = initblstats[i].fld;
1345 for (i = 0; fieldids_alias[i].fieldname; i++)
1346 if (fuzzymatch(fieldids_alias[i].fieldname, name,
1348 fld = fieldids_alias[i].fldid;
1354 /* check partial matches to canonical names */
1355 int len = (int) strlen(name);
1356 for (i = 0; i < SIZE(initblstats); i++)
1357 if (!strncmpi(name, initblstats[i].fldname, len)) {
1358 fld = initblstats[i].fld;
1364 return (nmatches == 1) ? fld : BL_FLUSH;
1368 hilite_reset_needed(bl_p, augmented_time)
1369 struct istat_s *bl_p;
1370 long augmented_time;
1372 struct hilite_s *tl = bl_p->thresholds;
1375 * This 'multi' handling may need some tuning...
1380 if (bl_p->time == 0 || bl_p->time >= augmented_time)
1384 /* only this style times out */
1385 if (tl->behavior == BL_TH_UPDOWN)
1393 /* called by options handling when 'statushilites' boolean is toggled */
1395 reset_status_hilites()
1397 if (iflags.hilite_delta) {
1400 for (i = 0; i < MAXBLSTATS; ++i)
1401 blstats[0][i].time = blstats[1][i].time = 0L;
1404 context.botlx = TRUE;
1408 merge_bestcolor(bestcolor, newcolor)
1412 int natr = HL_UNDEF, nclr = NO_COLOR;
1414 split_clridx(newcolor, &nclr, &natr);
1416 if (nclr != NO_COLOR)
1417 *bestcolor = (*bestcolor & 0xff00) | nclr;
1419 if (natr != HL_UNDEF) {
1420 if (natr == HL_NONE)
1421 *bestcolor = *bestcolor & 0x00ff; /* reset all attributes */
1423 *bestcolor |= (natr << 8); /* merge attributes */
1430 * Figures out, based on the value and the
1431 * direction it is moving, the color that the field
1432 * should be displayed in.
1435 * Provide get_hilite_color() with the following
1438 * useful for BL_TH_VAL_ABSOLUTE
1439 * indicator of down, up, or the same (-1, 1, 0) chg
1440 * useful for BL_TH_UPDOWN or change detection
1441 * percentage (current value percentage of max value) pc
1442 * useful for BL_TH_VAL_PERCENTAGE
1445 * color based on user thresholds set in config file.
1446 * The rightmost 8 bits contain a color index.
1447 * The 8 bits to the left of that contain
1448 * the attribute bits.
1454 get_hilite_color(idx, fldidx, vp, chg, pc, colorptr)
1455 int idx, fldidx, chg, pc;
1459 int bestcolor = NO_COLOR;
1460 struct hilite_s *hl;
1461 anything *value = (anything *)vp;
1462 char *txtstr, *cmpstr;
1464 if (!colorptr || fldidx < 0 || fldidx >= MAXBLSTATS)
1467 if (blstats[idx][fldidx].thresholds) {
1468 /* there are hilites set here */
1469 int max_pc = 0, min_pc = 100;
1470 int max_val = 0, min_val = LARGEST_INT;
1471 boolean exactmatch = FALSE;
1473 hl = blstats[idx][fldidx].thresholds;
1476 switch (hl->behavior) {
1477 case BL_TH_VAL_PERCENTAGE:
1478 if (hl->rel == EQ_VALUE && pc == hl->value.a_int) {
1479 merge_bestcolor(&bestcolor, hl->coloridx);
1480 min_pc = max_pc = hl->value.a_int;
1482 } else if (hl->rel == LT_VALUE && !exactmatch
1483 && (hl->value.a_int >= pc)
1484 && (hl->value.a_int <= min_pc)) {
1485 merge_bestcolor(&bestcolor, hl->coloridx);
1486 min_pc = hl->value.a_int;
1487 } else if (hl->rel == GT_VALUE && !exactmatch
1488 && (hl->value.a_int <= pc)
1489 && (hl->value.a_int >= max_pc)) {
1490 merge_bestcolor(&bestcolor, hl->coloridx);
1491 max_pc = hl->value.a_int;
1495 if (chg < 0 && hl->rel == LT_VALUE) {
1496 merge_bestcolor(&bestcolor, hl->coloridx);
1497 } else if (chg > 0 && hl->rel == GT_VALUE) {
1498 merge_bestcolor(&bestcolor, hl->coloridx);
1499 } else if (hl->rel == EQ_VALUE && chg) {
1500 merge_bestcolor(&bestcolor, hl->coloridx);
1501 min_val = max_val = hl->value.a_int;
1504 case BL_TH_VAL_ABSOLUTE:
1505 if (hl->rel == EQ_VALUE && hl->value.a_int == value->a_int) {
1506 merge_bestcolor(&bestcolor, hl->coloridx);
1507 min_val = max_val = hl->value.a_int;
1509 } else if (hl->rel == LT_VALUE && !exactmatch
1510 && (hl->value.a_int >= value->a_int)
1511 && (hl->value.a_int < min_val)) {
1512 merge_bestcolor(&bestcolor, hl->coloridx);
1513 min_val = hl->value.a_int;
1514 } else if (hl->rel == GT_VALUE && !exactmatch
1515 && (hl->value.a_int <= value->a_int)
1516 && (hl->value.a_int > max_val)) {
1517 merge_bestcolor(&bestcolor, hl->coloridx);
1518 max_val = hl->value.a_int;
1521 case BL_TH_TEXTMATCH:
1522 txtstr = dupstr(blstats[idx][fldidx].val);
1524 if (fldidx == BL_TITLE) {
1525 int len = (strlen(plname) + sizeof(" the"));
1528 (void) trimspaces(cmpstr);
1529 if (hl->rel == TXT_VALUE && hl->textmatch[0] &&
1530 !strcmpi(hl->textmatch, cmpstr)) {
1531 merge_bestcolor(&bestcolor, hl->coloridx);
1535 case BL_TH_ALWAYS_HILITE:
1536 merge_bestcolor(&bestcolor, hl->coloridx);
1546 *colorptr = bestcolor;
1551 split_clridx(idx, coloridx, attrib)
1553 int *coloridx, *attrib;
1555 if (idx && coloridx && attrib) {
1556 *coloridx = idx & 0x00FF;
1557 *attrib = (idx & 0xFF00) >> 8;
1563 * This is the parser for the hilite options
1565 * parse_status_hl1() separates each hilite entry into
1566 * a set of field threshold/action component strings,
1567 * then calls parse_status_hl2() to parse further
1568 * and configure the hilite.
1571 parse_status_hl1(op, from_configfile)
1573 boolean from_configfile;
1575 #define MAX_THRESH 21
1576 char hsbuf[MAX_THRESH][QBUFSZ];
1577 boolean rslt, badopt = FALSE;
1578 int i, fldnum, ccount = 0;
1582 for (i = 0; i < MAX_THRESH; ++i) {
1585 while (*op && fldnum < MAX_THRESH && ccount < (QBUFSZ - 2)) {
1589 rslt = parse_status_hl2(hsbuf, from_configfile);
1595 for (i = 0; i < MAX_THRESH; ++i) {
1600 } else if (c == '/') {
1604 hsbuf[fldnum][ccount++] = c;
1605 hsbuf[fldnum][ccount] = '\0';
1609 if (fldnum >= 1 && !badopt) {
1610 rslt = parse_status_hl2(hsbuf, from_configfile);
1619 /* is str in the format of "(<>)?[0-9]+%?" regex */
1621 is_ltgt_percentnumber(str)
1624 const char *s = str;
1626 if (*s == '<' || *s == '>') s++;
1627 while (digit(*s)) s++;
1630 return (*s == '\0');
1633 /* does str only contain "<>0-9%" chars */
1635 has_ltgt_percentnumber(str)
1638 const char *s = str;
1641 if (!index("<>0123456789%", *s))
1648 /* splitsubfields(): splits str in place into '+' or '&' separated strings.
1649 * returns number of strings, or -1 if more than maxsf or MAX_SUBFIELDS
1651 #define MAX_SUBFIELDS 16
1653 splitsubfields(str, sfarr, maxsf)
1658 static char *subfields[MAX_SUBFIELDS];
1659 char *st = (char *) 0;
1664 for (sf = 0; sf < MAX_SUBFIELDS; ++sf)
1665 subfields[sf] = (char *) 0;
1667 maxsf = (maxsf == 0) ? MAX_SUBFIELDS : min(maxsf, MAX_SUBFIELDS);
1669 if (index(str, '+') || index(str, '&')) {
1674 while (*c && sf < maxsf) {
1675 if (*c == '&' || *c == '+') {
1683 if (sf >= maxsf - 1)
1686 subfields[sf++] = st;
1694 #undef MAX_SUBFIELDS
1697 is_fld_arrayvalues(str, arr, arrmin, arrmax, retidx)
1699 const char *const *arr;
1705 for (i = arrmin; i < arrmax; i++)
1706 if (!strcmpi(str, arr[i])) {
1714 query_arrayvalue(querystr, arr, arrmin, arrmax)
1715 const char *querystr;
1716 const char *const *arr;
1719 int i, res, ret = arrmin - 1;
1722 menu_item *picks = (menu_item *) 0;
1723 int adj = (arrmin > 0) ? 1 : arrmax;
1725 tmpwin = create_nhwindow(NHW_MENU);
1728 for (i = arrmin; i < arrmax; i++) {
1730 any.a_int = i + adj;
1731 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1732 arr[i], MENU_UNSELECTED);
1735 end_menu(tmpwin, querystr);
1737 res = select_menu(tmpwin, PICK_ONE, &picks);
1738 destroy_nhwindow(tmpwin);
1740 ret = picks->item.a_int - adj;
1741 free((genericptr_t) picks);
1748 status_hilite_add_threshold(fld, hilite)
1750 struct hilite_s *hilite;
1752 struct hilite_s *new_hilite;
1757 /* alloc and initialize a new hilite_s struct */
1758 new_hilite = (struct hilite_s *) alloc(sizeof(struct hilite_s));
1759 *new_hilite = *hilite; /* copy struct */
1761 new_hilite->set = TRUE;
1762 new_hilite->fld = fld;
1763 new_hilite->next = (struct hilite_s *)0;
1765 /* Does that status field currently have any hilite thresholds? */
1766 if (!blstats[0][fld].thresholds) {
1767 blstats[0][fld].thresholds = new_hilite;
1769 struct hilite_s *temp_hilite = blstats[0][fld].thresholds;
1770 new_hilite->next = temp_hilite;
1771 blstats[0][fld].thresholds = new_hilite;
1772 /* sort_hilites(fld) */
1774 /* current and prev must both point at the same hilites */
1775 blstats[1][fld].thresholds = blstats[0][fld].thresholds;
1780 parse_status_hl2(s, from_configfile)
1782 boolean from_configfile;
1785 int sidx = 0, i = -1, dt = -1;
1786 int coloridx = -1, successes = 0;
1787 int disp_attrib = 0;
1788 boolean percent = FALSE, changed = FALSE, numeric = FALSE;
1789 boolean down= FALSE, up = FALSE;
1790 boolean gt = FALSE, lt = FALSE, eq = FALSE, neq = FALSE;
1791 boolean txtval = FALSE;
1792 boolean always = FALSE;
1794 enum statusfields fld = BL_FLUSH;
1795 struct hilite_s hilite;
1797 const char *aligntxt[] = {"chaotic", "neutral", "lawful"};
1798 /* hu_stat[] from eat.c has trailing spaces which foul up comparisons */
1799 const char *hutxt[] = {"Satiated", "", "Hungry", "Weak",
1800 "Fainting", "Fainted", "Starved"};
1804 OPTION=hilite_status: hitpoints/<10%/red
1805 OPTION=hilite_status: hitpoints/<10%/red/<5%/purple/1/red+blink+inverse
1806 OPTION=hilite_status: experience/down/red/up/green
1807 OPTION=hilite_status: cap/strained/yellow/overtaxed/orange
1808 OPTION=hilite_status: title/always/blue
1809 OPTION=hilite_status: title/blue
1812 /* field name to statusfield */
1813 fld = fldname_to_bl_indx(s[sidx]);
1815 if (fld == BL_CHARACTERISTICS) {
1816 boolean res = FALSE;
1818 /* recursively set each of strength, dexterity, constitution, &c */
1819 for (fld = BL_STR; fld <= BL_CH; fld++) {
1820 Strcpy(s[sidx], initblstats[fld].fldname);
1821 res = parse_status_hl2(s, from_configfile);
1827 if (fld == BL_FLUSH) {
1828 config_error_add("Unknown status field '%s'", s[sidx]);
1831 if (fld == BL_CONDITION)
1832 return parse_condition(s, sidx);
1836 char buf[BUFSZ], **subfields;
1837 int sf = 0; /* subfield count */
1840 txt = (const char *)0;
1841 percent = changed = numeric = FALSE;
1843 gt = eq = lt = neq = txtval = FALSE;
1846 /* threshold value */
1850 memset((genericptr_t) &hilite, 0, sizeof(struct hilite_s));
1851 hilite.set = FALSE; /* mark it "unset" */
1854 if (*s[sidx+1] == '\0' || !strcmpi(s[sidx], "always")) {
1855 /* "field/always/color" OR "field/color" */
1857 if (*s[sidx+1] == '\0')
1860 } else if (!strcmpi(s[sidx], "up") || !strcmpi(s[sidx], "down")) {
1861 if (!strcmpi(s[sidx], "down"))
1867 } else if (fld == BL_CAP
1868 && is_fld_arrayvalues(s[sidx], enc_stat,
1869 SLT_ENCUMBER, OVERLOADED+1, &kidx)) {
1870 txt = enc_stat[kidx];
1873 } else if (fld == BL_ALIGN
1874 && is_fld_arrayvalues(s[sidx], aligntxt, 0, 3, &kidx)) {
1875 txt = aligntxt[kidx];
1878 } else if (fld == BL_HUNGER
1879 && is_fld_arrayvalues(s[sidx], hutxt,
1880 SATIATED, STARVED+1, &kidx)) {
1881 txt = hu_stat[kidx]; /* store hu_stat[] val, not hutxt[] */
1884 } else if (!strcmpi(s[sidx], "changed")) {
1887 } else if (is_ltgt_percentnumber(s[sidx])) {
1889 if (strchr(tmp, '%'))
1891 if (strchr(tmp, '<'))
1893 if (strchr(tmp, '>'))
1895 (void) stripchars(tmpbuf, "%<>", tmp);
1898 if (!index("0123456789", *tmp))
1904 if (strlen(tmp) > 0) {
1905 dt = initblstats[fld].anytype;
1908 (void) s_to_anything(&hilite.value, tmp, dt);
1911 if (!hilite.value.a_void && (strcmp(tmp, "0") != 0))
1913 } else if (initblstats[fld].anytype == ANY_STR) {
1918 config_error_add(has_ltgt_percentnumber(s[sidx])
1919 ? "Wrong format '%s', expected a threshold number or percent"
1920 : "Unknown behavior '%s'", s[sidx]);
1924 /* relationships { LT_VALUE, GT_VALUE, EQ_VALUE} */
1926 hilite.rel = GT_VALUE;
1928 hilite.rel = EQ_VALUE;
1930 hilite.rel = LT_VALUE;
1932 hilite.rel = EQ_VALUE;
1934 hilite.rel = EQ_VALUE;
1936 hilite.rel = LT_VALUE;
1938 hilite.rel = GT_VALUE;
1940 hilite.rel = EQ_VALUE;
1942 hilite.rel = TXT_VALUE;
1944 hilite.rel = LT_VALUE;
1946 if (initblstats[fld].anytype == ANY_STR
1947 && (percent || numeric)) {
1948 config_error_add("Field '%s' does not support numeric values",
1949 initblstats[fld].fldname);
1954 if (initblstats[fld].idxmax <= BL_FLUSH) {
1955 config_error_add("Cannot use percent with '%s'",
1956 initblstats[fld].fldname);
1958 } else if ((hilite.value.a_int < 0)
1959 || (hilite.value.a_int == 0
1960 && hilite.rel == LT_VALUE)
1961 || (hilite.value.a_int > 100)
1962 || (hilite.value.a_int == 100
1963 && hilite.rel == GT_VALUE)) {
1964 config_error_add("Illegal percentage value");
1978 sf = splitsubfields(buf, &subfields, 0);
1983 disp_attrib = HL_UNDEF;
1985 for (i = 0; i < sf; ++i) {
1986 int a = match_str2attr(subfields[i], FALSE);
1988 disp_attrib |= HL_DIM;
1989 else if (a == ATR_BLINK)
1990 disp_attrib |= HL_BLINK;
1991 else if (a == ATR_ULINE)
1992 disp_attrib |= HL_ULINE;
1993 else if (a == ATR_INVERSE)
1994 disp_attrib |= HL_INVERSE;
1995 else if (a == ATR_BOLD)
1996 disp_attrib |= HL_BOLD;
1997 else if (a == ATR_NONE)
1998 disp_attrib = HL_NONE;
2000 int c = match_str2clr(subfields[i]);
2002 if (c >= CLR_MAX || coloridx != -1)
2008 coloridx = NO_COLOR;
2010 /* Assign the values */
2011 hilite.coloridx = coloridx | (disp_attrib << 8);
2014 hilite.behavior = BL_TH_ALWAYS_HILITE;
2016 hilite.behavior = BL_TH_VAL_PERCENTAGE;
2018 hilite.behavior = BL_TH_UPDOWN;
2020 hilite.behavior = BL_TH_VAL_ABSOLUTE;
2022 hilite.behavior = BL_TH_TEXTMATCH;
2023 else if (hilite.value.a_void)
2024 hilite.behavior = BL_TH_VAL_ABSOLUTE;
2026 hilite.behavior = BL_TH_NONE;
2028 hilite.anytype = dt;
2030 if (hilite.behavior == BL_TH_TEXTMATCH && txt
2031 && strlen(txt) < QBUFSZ-1) {
2032 Strcpy(hilite.textmatch, txt);
2033 (void) trimspaces(hilite.textmatch);
2036 status_hilite_add_threshold(fld, &hilite);
2047 const struct condmap valid_conditions[] = {
2048 {"stone", BL_MASK_STONE},
2049 {"slime", BL_MASK_SLIME},
2050 {"strngl", BL_MASK_STRNGL},
2051 {"foodPois", BL_MASK_FOODPOIS},
2052 {"termIll", BL_MASK_TERMILL},
2053 {"blind", BL_MASK_BLIND},
2054 {"deaf", BL_MASK_DEAF},
2055 {"stun", BL_MASK_STUN},
2056 {"conf", BL_MASK_CONF},
2057 {"hallu", BL_MASK_HALLU},
2058 {"lev", BL_MASK_LEV},
2059 {"fly", BL_MASK_FLY},
2060 {"ride", BL_MASK_RIDE},
2063 const struct condmap condition_aliases[] = {
2064 {"strangled", BL_MASK_STRNGL},
2065 {"all", BL_MASK_STONE | BL_MASK_SLIME | BL_MASK_STRNGL |
2066 BL_MASK_FOODPOIS | BL_MASK_TERMILL |
2067 BL_MASK_BLIND | BL_MASK_DEAF | BL_MASK_STUN |
2068 BL_MASK_CONF | BL_MASK_HALLU |
2069 BL_MASK_LEV | BL_MASK_FLY | BL_MASK_RIDE },
2070 {"major_troubles", BL_MASK_STONE | BL_MASK_SLIME | BL_MASK_STRNGL |
2071 BL_MASK_FOODPOIS | BL_MASK_TERMILL},
2072 {"minor_troubles", BL_MASK_BLIND | BL_MASK_DEAF | BL_MASK_STUN |
2073 BL_MASK_CONF | BL_MASK_HALLU},
2074 {"movement", BL_MASK_LEV | BL_MASK_FLY | BL_MASK_RIDE}
2081 unsigned long ret = 0UL;
2084 menu_item *picks = (menu_item *) 0;
2086 tmpwin = create_nhwindow(NHW_MENU);
2089 for (i = 0; i < SIZE(valid_conditions); i++) {
2091 any.a_ulong = valid_conditions[i].bitmask;
2092 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
2093 valid_conditions[i].id, MENU_UNSELECTED);
2096 end_menu(tmpwin, "Choose status conditions");
2098 res = select_menu(tmpwin, PICK_ANY, &picks);
2099 destroy_nhwindow(tmpwin);
2101 for (i = 0; i < res; i++)
2102 ret |= picks[i].item.a_ulong;
2103 free((genericptr_t) picks);
2109 conditionbitmask2str(ul)
2112 static char buf[BUFSZ];
2114 boolean first = TRUE;
2115 const char *alias = (char *) 0;
2122 for (i = 1; i < SIZE(condition_aliases); i++)
2123 if (condition_aliases[i].bitmask == ul)
2124 alias = condition_aliases[i].id;
2126 for (i = 0; i < SIZE(valid_conditions); i++)
2127 if ((valid_conditions[i].bitmask & ul) != 0UL) {
2128 Sprintf(eos(buf), "%s%s", (first) ? "" : "+",
2129 valid_conditions[i].id);
2133 if (!first && alias)
2134 Sprintf(buf, "%s", alias);
2139 STATIC_OVL unsigned long
2140 match_str2conditionbitmask(str)
2143 int i, nmatches = 0;
2144 unsigned long mask = 0UL;
2147 /* check matches to canonical names */
2148 for (i = 0; i < SIZE(valid_conditions); i++)
2149 if (fuzzymatch(valid_conditions[i].id, str, " -_", TRUE)) {
2150 mask |= valid_conditions[i].bitmask;
2156 for (i = 0; i < SIZE(condition_aliases); i++)
2157 if (fuzzymatch(condition_aliases[i].id, str, " -_", TRUE)) {
2158 mask |= condition_aliases[i].bitmask;
2164 /* check partial matches to aliases */
2165 int len = (int) strlen(str);
2166 for (i = 0; i < SIZE(condition_aliases); i++)
2167 if (!strncmpi(str, condition_aliases[i].id, len)) {
2168 mask |= condition_aliases[i].bitmask;
2177 STATIC_OVL unsigned long
2178 str2conditionbitmask(str)
2181 unsigned long conditions_bitmask = 0UL;
2185 sf = splitsubfields(str, &subfields, SIZE(valid_conditions));
2190 for (i = 0; i < sf; ++i) {
2191 unsigned long bm = match_str2conditionbitmask(subfields[i]);
2194 config_error_add("Unknown condition '%s'", subfields[i]);
2197 conditions_bitmask |= bm;
2199 return conditions_bitmask;
2203 parse_condition(s, sidx)
2208 int coloridx = NO_COLOR;
2210 unsigned long conditions_bitmask = 0UL;
2211 boolean success = FALSE;
2217 OPTION=hilite_status: condition/stone+slime+foodPois/red&inverse */
2221 int sf = 0; /* subfield count */
2222 char buf[BUFSZ], **subfields;
2227 config_error_add("Missing condition(s)");
2232 conditions_bitmask = str2conditionbitmask(buf);
2234 if (!conditions_bitmask)
2238 * We have the conditions_bitmask with bits set for
2239 * each ailment we want in a particular color and/or
2240 * attribute, but we need to assign it to an arry of
2241 * bitmasks indexed by the color chosen
2242 * (0 to (CLR_MAX - 1))
2243 * and/or attributes chosen
2244 * (HL_ATTCLR_DIM to (BL_ATTCLR_MAX - 1))
2245 * We still have to parse the colors and attributes out.
2251 if (!how || !*how) {
2252 config_error_add("Missing color+attribute");
2257 sf = splitsubfields(buf, &subfields, 0);
2260 * conditions_bitmask now has bits set representing
2261 * the conditions that player wants represented, but
2262 * now we parse out *how* they will be represented.
2264 * Only 1 colour is allowed, but potentially multiple
2265 * attributes are allowed.
2267 * We have the following additional array offsets to
2268 * use for storing the attributes beyond the end of
2269 * the color indexes, all of which are less than CLR_MAX.
2270 * HL_ATTCLR_DIM = CLR_MAX
2271 * HL_ATTCLR_BLINK = CLR_MAX + 1
2272 * HL_ATTCLR_ULINE = CLR_MAX + 2
2273 * HL_ATTCLR_INVERSE = CLR_MAX + 3
2274 * HL_ATTCLR_BOLD = CLR_MAX + 4
2275 * HL_ATTCLR_MAX = CLR_MAX + 5 (this is past array boundary)
2279 for (i = 0; i < sf; ++i) {
2280 int a = match_str2attr(subfields[i], FALSE);
2282 cond_hilites[HL_ATTCLR_DIM] |= conditions_bitmask;
2283 else if (a == ATR_BLINK)
2284 cond_hilites[HL_ATTCLR_BLINK] |= conditions_bitmask;
2285 else if (a == ATR_ULINE)
2286 cond_hilites[HL_ATTCLR_ULINE] |= conditions_bitmask;
2287 else if (a == ATR_INVERSE)
2288 cond_hilites[HL_ATTCLR_INVERSE] |= conditions_bitmask;
2289 else if (a == ATR_BOLD)
2290 cond_hilites[HL_ATTCLR_BOLD] |= conditions_bitmask;
2291 else if (a == ATR_NONE) {
2292 cond_hilites[HL_ATTCLR_DIM] = 0UL;
2293 cond_hilites[HL_ATTCLR_BLINK] = 0UL;
2294 cond_hilites[HL_ATTCLR_ULINE] = 0UL;
2295 cond_hilites[HL_ATTCLR_INVERSE] = 0UL;
2296 cond_hilites[HL_ATTCLR_BOLD] = 0UL;
2298 int k = match_str2clr(subfields[i]);
2305 /* set the bits in the appropriate member of the
2306 condition array according to color chosen as index */
2308 cond_hilites[coloridx] |= conditions_bitmask;
2316 clear_status_hilites()
2320 for (i = 0; i < MAXBLSTATS; ++i) {
2321 if (blstats[0][i].thresholds) {
2322 struct hilite_s *temp = blstats[0][i].thresholds,
2323 *next = (struct hilite_s *)0;
2327 blstats[0][i].thresholds = (struct hilite_s *)0;
2328 blstats[1][i].thresholds = blstats[0][i].thresholds;
2336 hlattr2attrname(attrib, buf, bufsz)
2340 if (attrib && buf) {
2345 if (attrib & HL_NONE) {
2346 Strcpy(buf, "normal");
2350 if (attrib & HL_BOLD)
2351 Strcat(attbuf, first++ ? "+bold" : "bold");
2352 if (attrib & HL_INVERSE)
2353 Strcat(attbuf, first++ ? "+inverse" : "inverse");
2354 if (attrib & HL_ULINE)
2355 Strcat(attbuf, first++ ? "+underline" : "underline");
2356 if (attrib & HL_BLINK)
2357 Strcat(attbuf, first++ ? "+blink" : "blink");
2358 if (attrib & HL_DIM)
2359 Strcat(attbuf, first++ ? "+dim" : "dim");
2362 if (k < (bufsz - 1))
2363 Strcpy(buf, attbuf);
2370 struct _status_hilite_line_str {
2373 struct hilite_s *hl;
2376 struct _status_hilite_line_str *next;
2379 struct _status_hilite_line_str *status_hilite_str =
2380 (struct _status_hilite_line_str *) 0;
2381 static int status_hilite_str_id = 0;
2384 status_hilite_linestr_add(fld, hl, mask, str)
2386 struct hilite_s *hl;
2390 struct _status_hilite_line_str *tmp = (struct _status_hilite_line_str *)
2391 alloc(sizeof(struct _status_hilite_line_str));
2392 struct _status_hilite_line_str *nxt = status_hilite_str;
2394 (void) memset(tmp, 0, sizeof(struct _status_hilite_line_str));
2396 ++status_hilite_str_id;
2400 (void) stripchars(tmp->str, " ", str);
2402 tmp->id = status_hilite_str_id;
2405 while (nxt && nxt->next)
2409 tmp->next = (struct _status_hilite_line_str *) 0;
2410 status_hilite_str = tmp;
2415 status_hilite_linestr_done()
2417 struct _status_hilite_line_str *tmp = status_hilite_str;
2418 struct _status_hilite_line_str *nxt;
2425 status_hilite_str = (struct _status_hilite_line_str *) 0;
2426 status_hilite_str_id = 0;
2430 status_hilite_linestr_countfield(fld)
2433 struct _status_hilite_line_str *tmp = status_hilite_str;
2437 if (tmp->fld == fld || fld == BL_FLUSH)
2445 count_status_hilites(VOID_ARGS)
2448 status_hilite_linestr_gather();
2449 count = status_hilite_linestr_countfield(BL_FLUSH);
2450 status_hilite_linestr_done();
2455 status_hilite_linestr_gather_conditions()
2460 unsigned long clratr;
2461 } cond_maps[SIZE(valid_conditions)];
2463 (void)memset(cond_maps, 0,
2464 sizeof(struct _cond_map) * SIZE(valid_conditions));
2466 for (i = 0; i < SIZE(valid_conditions); i++) {
2470 for (j = 0; j < CLR_MAX; j++)
2471 if (cond_hilites[j] & valid_conditions[i].bitmask)
2473 if (cond_hilites[HL_ATTCLR_DIM] & valid_conditions[i].bitmask)
2475 if (cond_hilites[HL_ATTCLR_BOLD] & valid_conditions[i].bitmask)
2477 if (cond_hilites[HL_ATTCLR_BLINK] & valid_conditions[i].bitmask)
2479 if (cond_hilites[HL_ATTCLR_ULINE] & valid_conditions[i].bitmask)
2481 if (cond_hilites[HL_ATTCLR_INVERSE] & valid_conditions[i].bitmask)
2484 if (clr != NO_COLOR || atr != HL_NONE) {
2485 unsigned long ca = clr | (atr << 8);
2486 boolean added_condmap = FALSE;
2487 for (j = 0; j < SIZE(valid_conditions); j++)
2488 if (cond_maps[j].clratr == ca) {
2489 cond_maps[j].bm |= valid_conditions[i].bitmask;
2490 added_condmap = TRUE;
2493 if (!added_condmap) {
2494 for (j = 0; j < SIZE(valid_conditions); j++)
2495 if (!cond_maps[j].bm) {
2496 cond_maps[j].bm = valid_conditions[i].bitmask;
2497 cond_maps[j].clratr = ca;
2504 for (i = 0; i < SIZE(valid_conditions); i++)
2505 if (cond_maps[i].bm) {
2506 int clr = NO_COLOR, atr = HL_NONE;
2507 split_clridx(cond_maps[i].clratr, &clr, &atr);
2508 if (clr != NO_COLOR || atr != HL_NONE) {
2510 char attrbuf[BUFSZ];
2511 char condbuf[BUFSZ];
2513 (void) stripchars(clrbuf, " ", clr2colorname(clr));
2514 tmpattr = hlattr2attrname(atr, attrbuf, BUFSZ);
2516 Sprintf(eos(clrbuf), "&%s", tmpattr);
2517 Sprintf(condbuf, "condition/%s/%s",
2518 conditionbitmask2str(cond_maps[i].bm), clrbuf);
2519 status_hilite_linestr_add(BL_CONDITION, 0,
2520 cond_maps[i].bm, condbuf);
2526 status_hilite_linestr_gather()
2529 struct hilite_s *hl;
2531 status_hilite_linestr_done();
2533 for (i = 0; i < MAXBLSTATS; i++) {
2534 hl = blstats[0][i].thresholds;
2536 status_hilite_linestr_add(i, hl, 0UL, status_hilite2str(hl));
2541 status_hilite_linestr_gather_conditions();
2546 status_hilite2str(hl)
2547 struct hilite_s *hl;
2549 static char buf[BUFSZ];
2550 int clr = 0, attr = 0;
2551 char behavebuf[BUFSZ];
2553 char attrbuf[BUFSZ];
2559 behavebuf[0] = '\0';
2562 switch (hl->behavior) {
2563 case BL_TH_VAL_PERCENTAGE:
2564 if (hl->rel == LT_VALUE)
2565 Sprintf(behavebuf, "<%i%%", hl->value.a_int);
2566 else if (hl->rel == GT_VALUE)
2567 Sprintf(behavebuf, ">%i%%", hl->value.a_int);
2568 else if (hl->rel == EQ_VALUE)
2569 Sprintf(behavebuf, "%i%%", hl->value.a_int);
2571 impossible("hl->behavior=percentage, rel error");
2574 if (hl->rel == LT_VALUE)
2575 Sprintf(behavebuf, "down");
2576 else if (hl->rel == GT_VALUE)
2577 Sprintf(behavebuf, "up");
2578 else if (hl->rel == EQ_VALUE)
2579 Sprintf(behavebuf, "changed");
2581 impossible("hl->behavior=updown, rel error");
2583 case BL_TH_VAL_ABSOLUTE:
2584 if (hl->rel == LT_VALUE)
2585 Sprintf(behavebuf, "<%i", hl->value.a_int);
2586 else if (hl->rel == GT_VALUE)
2587 Sprintf(behavebuf, ">%i", hl->value.a_int);
2588 else if (hl->rel == EQ_VALUE)
2589 Sprintf(behavebuf, "%i", hl->value.a_int);
2591 impossible("hl->behavior=absolute, rel error");
2593 case BL_TH_TEXTMATCH:
2594 if (hl->rel == TXT_VALUE && hl->textmatch[0])
2595 Sprintf(behavebuf, "%s", hl->textmatch);
2597 impossible("hl->behavior=textmatch, rel or textmatch error");
2599 case BL_TH_CONDITION:
2600 if (hl->rel == EQ_VALUE)
2601 Sprintf(behavebuf, "%s", conditionbitmask2str(hl->value.a_ulong));
2603 impossible("hl->behavior=condition, rel error");
2605 case BL_TH_ALWAYS_HILITE:
2606 Sprintf(behavebuf, "always");
2614 split_clridx(hl->coloridx, &clr, &attr);
2615 if (clr != NO_COLOR)
2616 (void) stripchars(clrbuf, " ", clr2colorname(clr));
2617 if (attr != HL_UNDEF) {
2618 tmpattr = hlattr2attrname(attr, attrbuf, BUFSZ);
2620 Sprintf(eos(clrbuf), "%s%s",
2621 (clr != NO_COLOR) ? "&" : "",
2624 Sprintf(buf, "%s/%s/%s", initblstats[hl->fld].fldname, behavebuf, clrbuf);
2630 status_hilite_menu_choose_field()
2633 int i, res, fld = BL_FLUSH;
2635 menu_item *picks = (menu_item *) 0;
2637 tmpwin = create_nhwindow(NHW_MENU);
2640 for (i = 0; i < MAXBLSTATS; i++) {
2643 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
2644 initblstats[i].fldname, MENU_UNSELECTED);
2647 end_menu(tmpwin, "Select a hilite field:");
2649 res = select_menu(tmpwin, PICK_ONE, &picks);
2650 destroy_nhwindow(tmpwin);
2652 fld = picks->item.a_int - 1;
2653 free((genericptr_t) picks);
2659 status_hilite_menu_choose_behavior(fld)
2663 int res = 0, beh = BL_TH_NONE-1;
2665 menu_item *picks = (menu_item *) 0;
2668 int onlybeh = BL_TH_NONE, nopts = 0;
2670 if (fld <= BL_FLUSH || fld >= MAXBLSTATS)
2673 at = initblstats[fld].anytype;
2675 tmpwin = create_nhwindow(NHW_MENU);
2678 if (fld != BL_CONDITION) {
2680 any.a_int = onlybeh = BL_TH_ALWAYS_HILITE;
2681 Sprintf(buf, "Always highlight %s", initblstats[fld].fldname);
2682 add_menu(tmpwin, NO_GLYPH, &any, 'a', 0, ATR_NONE,
2683 buf, MENU_UNSELECTED);
2687 if (fld == BL_CONDITION) {
2689 any.a_int = onlybeh = BL_TH_CONDITION;
2690 add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, ATR_NONE,
2691 "Bitmask of conditions", MENU_UNSELECTED);
2695 if (fld != BL_CONDITION) {
2697 any.a_int = onlybeh = BL_TH_UPDOWN;
2698 Sprintf(buf, "%s value changes", initblstats[fld].fldname);
2699 add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE,
2700 buf, MENU_UNSELECTED);
2704 if (fld != BL_CAP && fld != BL_HUNGER && (at == ANY_INT || at == ANY_LONG || at == ANY_UINT)) {
2706 any.a_int = onlybeh = BL_TH_VAL_ABSOLUTE;
2707 add_menu(tmpwin, NO_GLYPH, &any, 'n', 0, ATR_NONE,
2708 "Number threshold", MENU_UNSELECTED);
2712 if (initblstats[fld].idxmax > BL_FLUSH) {
2714 any.a_int = onlybeh = BL_TH_VAL_PERCENTAGE;
2715 add_menu(tmpwin, NO_GLYPH, &any, 'p', 0, ATR_NONE,
2716 "Percentage threshold", MENU_UNSELECTED);
2720 if (initblstats[fld].anytype == ANY_STR || fld == BL_CAP || fld == BL_HUNGER) {
2722 any.a_int = onlybeh = BL_TH_TEXTMATCH;
2723 Sprintf(buf, "%s text match", initblstats[fld].fldname);
2724 add_menu(tmpwin, NO_GLYPH, &any, 't', 0, ATR_NONE,
2725 buf, MENU_UNSELECTED);
2729 Sprintf(buf, "Select %s field hilite behavior:", initblstats[fld].fldname);
2730 end_menu(tmpwin, buf);
2733 res = select_menu(tmpwin, PICK_ONE, &picks);
2734 if (res == 0) /* none chosen*/
2736 else if (res == -1) /* menu cancelled */
2737 beh = (BL_TH_NONE - 1);
2738 } else if (onlybeh != BL_TH_NONE)
2740 destroy_nhwindow(tmpwin);
2742 beh = picks->item.a_int;
2743 free((genericptr_t) picks);
2749 status_hilite_menu_choose_updownboth(fld, str)
2757 menu_item *picks = (menu_item *) 0;
2759 tmpwin = create_nhwindow(NHW_MENU);
2763 Sprintf(buf, "%s or less", str);
2765 Sprintf(buf, "Value goes down");
2767 any.a_int = 10 + LT_VALUE;
2768 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
2769 buf, MENU_UNSELECTED);
2772 Sprintf(buf, "Exactly %s", str);
2774 Sprintf(buf, "Value changes");
2776 any.a_int = 10 + EQ_VALUE;
2777 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
2778 buf, MENU_UNSELECTED);
2781 Sprintf(buf, "%s or more", str);
2783 Sprintf(buf, "Value goes up");
2785 any.a_int = 10 + GT_VALUE;
2786 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
2787 buf, MENU_UNSELECTED);
2789 Sprintf(buf, "Select field %s value:", initblstats[fld].fldname);
2790 end_menu(tmpwin, buf);
2792 res = select_menu(tmpwin, PICK_ONE, &picks);
2793 destroy_nhwindow(tmpwin);
2795 ret = picks->item.a_int - 10;
2796 free((genericptr_t) picks);
2803 status_hilite_menu_add(origfld)
2809 int clr = NO_COLOR, atr = HL_UNDEF;
2810 struct hilite_s hilite;
2811 unsigned long cond = 0UL;
2812 char colorqry[BUFSZ];
2813 char attrqry[BUFSZ];
2817 if (fld == BL_FLUSH) {
2818 fld = status_hilite_menu_choose_field();
2819 if (fld == BL_FLUSH)
2823 if (fld == BL_FLUSH)
2829 memset((genericptr_t) &hilite, 0, sizeof(struct hilite_s));
2830 hilite.set = FALSE; /* mark it "unset" */
2835 behavior = status_hilite_menu_choose_behavior(fld);
2837 if (behavior == (BL_TH_NONE-1)) {
2839 } else if (behavior == BL_TH_NONE) {
2840 if (origfld == BL_FLUSH)
2846 hilite.behavior = behavior;
2850 if (behavior == BL_TH_VAL_PERCENTAGE
2851 || behavior == BL_TH_VAL_ABSOLUTE) {
2852 char inbuf[BUFSZ] = DUMMY, buf[BUFSZ];
2854 boolean skipltgt = FALSE;
2855 boolean gotnum = FALSE;
2857 char *numstart = inbuf;
2860 Sprintf(buf, "Enter %svalue for %s threshold:",
2861 (behavior == BL_TH_VAL_PERCENTAGE) ? "percentage " : "",
2862 initblstats[fld].fldname);
2864 if (inbuf[0] == '\0' || inbuf[0] == '\033')
2865 goto choose_behavior;
2867 inp = trimspaces(inbuf);
2869 goto choose_behavior;
2871 /* allow user to enter "<50%" or ">50" or just "50" */
2872 if (*inp == '>' || *inp == '<' || *inp == '=') {
2873 lt_gt_eq = (*inp == '>') ? GT_VALUE
2874 : (*inp == '<') ? LT_VALUE : EQ_VALUE;
2880 while (digit(*inp)) {
2885 behavior = BL_TH_VAL_PERCENTAGE;
2888 behavior = BL_TH_VAL_ABSOLUTE;
2890 /* some random characters */
2891 pline("\"%s\" is not a recognized number.", inp);
2895 pline("Is that an invisible number?");
2899 val = atoi(numstart);
2900 if (behavior == BL_TH_VAL_PERCENTAGE) {
2901 if (initblstats[fld].idxmax == -1) {
2902 pline("Field '%s' does not support percentage values.",
2903 initblstats[fld].fldname);
2904 behavior = BL_TH_VAL_ABSOLUTE;
2907 if (val < 0 || val > 100) {
2908 pline("Not a valid percent value.");
2914 lt_gt_eq = status_hilite_menu_choose_updownboth(fld, inbuf);
2919 Sprintf(colorqry, "Choose a color for when %s is %s%s:",
2920 initblstats[fld].fldname,
2922 (lt_gt_eq == EQ_VALUE) ? ""
2923 : (lt_gt_eq == LT_VALUE) ? " or less"
2926 Sprintf(attrqry, "Choose attribute for when %s is %s%s:",
2927 initblstats[fld].fldname,
2929 (lt_gt_eq == EQ_VALUE) ? ""
2930 : (lt_gt_eq == LT_VALUE) ? " or less"
2933 hilite.rel = lt_gt_eq;
2934 hilite.value.a_int = val;
2935 } else if (behavior == BL_TH_UPDOWN) {
2936 lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *)0);
2938 goto choose_behavior;
2939 Sprintf(colorqry, "Choose a color for when %s %s:",
2940 initblstats[fld].fldname,
2941 (lt_gt_eq == EQ_VALUE) ? "changes"
2942 : (lt_gt_eq == LT_VALUE) ? "decreases"
2944 Sprintf(attrqry, "Choose attribute for when %s %s:",
2945 initblstats[fld].fldname,
2946 (lt_gt_eq == EQ_VALUE) ? "changes"
2947 : (lt_gt_eq == LT_VALUE) ? "decreases"
2949 hilite.rel = lt_gt_eq;
2950 } else if (behavior == BL_TH_CONDITION) {
2951 cond = query_conditions();
2953 if (origfld == BL_FLUSH)
2958 Sprintf(colorqry, "Choose a color for conditions %s:",
2959 conditionbitmask2str(cond));
2960 Sprintf(attrqry, "Choose attribute for conditions %s:",
2961 conditionbitmask2str(cond));
2962 } else if (behavior == BL_TH_TEXTMATCH) {
2963 char qry_buf[BUFSZ];
2964 Sprintf(qry_buf, "%s %s text value to match:",
2968 || fld == BL_TITLE) ? "Choose" : "Enter",
2969 initblstats[fld].fldname);
2970 if (fld == BL_CAP) {
2971 int rv = query_arrayvalue(qry_buf,
2973 SLT_ENCUMBER, OVERLOADED+1);
2974 if (rv < SLT_ENCUMBER)
2975 goto choose_behavior;
2977 hilite.rel = TXT_VALUE;
2978 Strcpy(hilite.textmatch, enc_stat[rv]);
2979 } else if (fld == BL_ALIGN) {
2980 const char *aligntxt[] = {"chaotic", "neutral", "lawful"};
2981 int rv = query_arrayvalue(qry_buf,
2984 goto choose_behavior;
2986 hilite.rel = TXT_VALUE;
2987 Strcpy(hilite.textmatch, aligntxt[rv]);
2988 } else if (fld == BL_HUNGER) {
2989 const char *hutxt[] = {"Satiated", (char *)0, "Hungry", "Weak",
2990 "Fainting", "Fainted", "Starved"};
2991 int rv = query_arrayvalue(qry_buf,
2993 SATIATED, STARVED+1);
2995 goto choose_behavior;
2997 hilite.rel = TXT_VALUE;
2998 Strcpy(hilite.textmatch, hutxt[rv]);
2999 } else if (fld == BL_TITLE) {
3000 const char *rolelist[9];
3003 for (i = 0; i < 9; i++)
3004 rolelist[i] = (flags.female && urole.rank[i].f)
3005 ? urole.rank[i].f : urole.rank[i].m;
3007 rv = query_arrayvalue(qry_buf, rolelist, 0, 9);
3009 goto choose_behavior;
3011 hilite.rel = TXT_VALUE;
3012 Strcpy(hilite.textmatch, rolelist[rv]);
3014 char inbuf[BUFSZ] = DUMMY;
3017 getlin(qry_buf, inbuf);
3018 if (inbuf[0] == '\0' || inbuf[0] == '\033')
3019 goto choose_behavior;
3021 hilite.rel = TXT_VALUE;
3022 if (strlen(inbuf) < QBUFSZ-1)
3023 Strcpy(hilite.textmatch, inbuf);
3027 Sprintf(colorqry, "Choose a color for when %s is '%s':",
3028 initblstats[fld].fldname, hilite.textmatch);
3029 Sprintf(colorqry, "Choose attribute for when %s is '%s':",
3030 initblstats[fld].fldname, hilite.textmatch);
3031 } else if (behavior == BL_TH_ALWAYS_HILITE) {
3032 Sprintf(colorqry, "Choose a color to always hilite %s:",
3033 initblstats[fld].fldname);
3034 Sprintf(attrqry, "Choose attribute to always hilite %s:",
3035 initblstats[fld].fldname);
3040 clr = query_color(colorqry);
3042 if (behavior != BL_TH_ALWAYS_HILITE)
3045 goto choose_behavior;
3048 atr = query_attr(attrqry); /* FIXME: pick multiple attrs */
3053 else if (atr == ATR_BLINK)
3055 else if (atr == ATR_ULINE)
3057 else if (atr == ATR_INVERSE)
3059 else if (atr == ATR_BOLD)
3061 else if (atr == ATR_NONE)
3069 if (behavior == BL_TH_CONDITION) {
3071 char attrbuf[BUFSZ];
3074 cond_hilites[HL_ATTCLR_DIM] |= cond;
3075 else if (atr == HL_BLINK)
3076 cond_hilites[HL_ATTCLR_BLINK] |= cond;
3077 else if (atr == HL_ULINE)
3078 cond_hilites[HL_ATTCLR_ULINE] |= cond;
3079 else if (atr == HL_INVERSE)
3080 cond_hilites[HL_ATTCLR_INVERSE] |= cond;
3081 else if (atr == HL_BOLD)
3082 cond_hilites[HL_ATTCLR_BOLD] |= cond;
3083 else if (atr == HL_NONE) {
3084 cond_hilites[HL_ATTCLR_DIM] = 0UL;
3085 cond_hilites[HL_ATTCLR_BLINK] = 0UL;
3086 cond_hilites[HL_ATTCLR_ULINE] = 0UL;
3087 cond_hilites[HL_ATTCLR_INVERSE] = 0UL;
3088 cond_hilites[HL_ATTCLR_BOLD] = 0UL;
3090 cond_hilites[clr] |= cond;
3091 (void) stripchars(clrbuf, " ", clr2colorname(clr));
3092 tmpattr = hlattr2attrname(atr, attrbuf, BUFSZ);
3094 Sprintf(eos(clrbuf), "&%s", tmpattr);
3095 pline("Added hilite condition/%s/%s",
3096 conditionbitmask2str(cond), clrbuf);
3098 hilite.coloridx = clr | (atr << 8);
3099 hilite.anytype = initblstats[fld].anytype;
3101 status_hilite_add_threshold(fld, &hilite);
3102 pline("Added hilite %s", status_hilite2str(&hilite));
3104 reset_status_hilites();
3109 status_hilite_remove(id)
3112 struct _status_hilite_line_str *hlstr = status_hilite_str;
3114 while (hlstr && hlstr->id != id) {
3115 hlstr = hlstr->next;
3121 if (hlstr->fld == BL_CONDITION) {
3124 for (i = 0; i < CLR_MAX; i++)
3125 cond_hilites[i] &= ~hlstr->mask;
3126 cond_hilites[HL_ATTCLR_DIM] &= ~hlstr->mask;
3127 cond_hilites[HL_ATTCLR_BOLD] &= ~hlstr->mask;
3128 cond_hilites[HL_ATTCLR_BLINK] &= ~hlstr->mask;
3129 cond_hilites[HL_ATTCLR_ULINE] &= ~hlstr->mask;
3130 cond_hilites[HL_ATTCLR_INVERSE] &= ~hlstr->mask;
3133 int fld = hlstr->fld;
3134 struct hilite_s *hl = blstats[0][fld].thresholds;
3135 struct hilite_s *hlprev = (struct hilite_s *) 0;
3139 if (hlstr->hl == hl) {
3141 hlprev->next = hl->next;
3143 blstats[0][fld].thresholds = hl->next;
3144 blstats[1][fld].thresholds =
3145 blstats[0][fld].thresholds;
3159 status_hilite_menu_fld(fld)
3164 menu_item *picks = (menu_item *) 0;
3166 int count = status_hilite_linestr_countfield(fld);
3167 struct _status_hilite_line_str *hlstr;
3169 boolean acted = FALSE;
3172 if (status_hilite_menu_add(fld)) {
3173 status_hilite_linestr_done();
3174 status_hilite_linestr_gather();
3175 count = status_hilite_linestr_countfield(fld);
3180 tmpwin = create_nhwindow(NHW_MENU);
3184 hlstr = status_hilite_str;
3186 if (hlstr->fld == fld) {
3188 any.a_int = hlstr->id;
3189 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
3190 hlstr->str, MENU_UNSELECTED);
3192 hlstr = hlstr->next;
3196 Sprintf(buf, "No current hilites for %s", initblstats[fld].fldname);
3197 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
3200 /* separator line */
3202 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
3207 add_menu(tmpwin, NO_GLYPH, &any, 'X', 0, ATR_NONE,
3208 "Remove selected hilites", MENU_UNSELECTED);
3213 add_menu(tmpwin, NO_GLYPH, &any, 'Z', 0, ATR_NONE,
3214 "Add a new hilite", MENU_UNSELECTED);
3217 Sprintf(buf, "Current %s hilites:", initblstats[fld].fldname);
3218 end_menu(tmpwin, buf);
3220 if ((res = select_menu(tmpwin, PICK_ANY, &picks)) > 0) {
3222 for (i = 0; i < res; i++) {
3223 int idx = picks[i].item.a_int;
3225 /* delete selected hilites */
3230 } else if (idx == -2) {
3231 /* create a new hilite */
3240 /* delete selected hilites */
3241 for (i = 0; i < res; i++) {
3242 int idx = picks[i].item.a_int;
3244 (void) status_hilite_remove(idx);
3246 reset_status_hilites();
3248 } else if (mode == -2) {
3249 /* create a new hilite */
3250 if (status_hilite_menu_add(fld))
3254 free((genericptr_t) picks);
3259 picks = (menu_item *) 0;
3260 destroy_nhwindow(tmpwin);
3265 status_hilites_viewall()
3268 struct _status_hilite_line_str *hlstr = status_hilite_str;
3271 datawin = create_nhwindow(NHW_TEXT);
3274 Sprintf(buf, "OPTIONS=hilite_status: %.*s",
3275 (int)(BUFSZ - sizeof "OPTIONS=hilite_status: " - 1),
3277 putstr(datawin, 0, buf);
3278 hlstr = hlstr->next;
3281 display_nhwindow(datawin, FALSE);
3282 destroy_nhwindow(datawin);
3286 status_hilite_menu()
3290 menu_item *picks = (menu_item *) 0;
3298 tmpwin = create_nhwindow(NHW_MENU);
3301 status_hilite_linestr_gather();
3303 countall = status_hilite_linestr_countfield(BL_FLUSH);
3308 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
3309 "View all hilites in config format", MENU_UNSELECTED);
3312 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
3315 for (i = 0; i < MAXBLSTATS; i++) {
3316 int count = status_hilite_linestr_countfield(i);
3322 Sprintf(buf, "%-18s (%i defined)",
3323 initblstats[i].fldname, count);
3325 Sprintf(buf, "%-18s", initblstats[i].fldname);
3326 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
3327 buf, MENU_UNSELECTED);
3331 end_menu(tmpwin, "Status hilites:");
3332 if ((res = select_menu(tmpwin, PICK_ONE, &picks)) > 0) {
3333 i = picks->item.a_int - 1;
3335 status_hilites_viewall();
3337 (void) status_hilite_menu_fld(i);
3338 free((genericptr_t) picks);
3342 picks = (menu_item *) 0;
3343 destroy_nhwindow(tmpwin);
3344 status_hilite_linestr_done();
3352 #endif /* STATUS_HILITES */