1 /* NetHack 3.6 botl.c $NHDT-Date: 1573178085 2019/11/08 01:54:45 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.148 $ */
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-2021 */
9 /* JNetHack may be freely redistributed. See license for details. */
16 extern const char *hu_stat[]; /* defined in eat.c */
19 const char *const enc_stat[] = { "", "Burdened", "Stressed",
20 "Strained", "Overtaxed", "Overloaded" };
22 /*
\83I
\83v
\83V
\83\87\83\93\82Ì
\83p
\81[
\83X
\82Å
\89p
\8cê
\94Å
\82à
\95K
\97v*/
23 const char *const enc_stat[] = { "", "
\82æ
\82ë
\82ß
\82«", "
\88³
\94\97",
24 "
\8cÀ
\8aE", "
\89×
\8fd", "
\92´
\89ß"};
25 const char *const enc_stat_opt[] = { "", "Burdened", "Stressed",
26 "Strained", "Overtaxed", "Overloaded" };
29 STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */
30 STATIC_DCL const char *NDECL(rank);
31 STATIC_DCL void NDECL(bot_via_windowport);
32 STATIC_DCL void NDECL(stat_update_time);
38 int st = ACURR(A_STR);
42 Sprintf(buf, "%2d", st - 100);
43 else if (st < STR18(100))
44 Sprintf(buf, "18/%02d", st - 18);
46 Sprintf(buf, "18/**");
48 Sprintf(buf, "%-1d", st);
56 nhsym goldch = showsyms[COIN_CLASS + SYM_OFF_O];
58 iflags.invis_goldsym = (goldch <= (nhsym) ' ');
64 static char newbot1[BUFSZ];
68 Strcpy(newbot1, plname);
69 if ('a' <= newbot1[0] && newbot1[0] <= 'z')
70 newbot1[0] += 'A' - 'a';
72 if(is_kanji1(newbot1, 9))
77 Sprintf(nb = eos(newbot1), " the ");
79 Sprintf(nb = eos(newbot1), " ");
86 Strcpy(mbot, mons[u.umonnum].mname);
87 while (mbot[k] != 0) {
88 if ((k == 0 || (k > 0 && mbot[k - 1] == ' ')) && 'a' <= mbot[k]
93 Strcpy(nb = eos(nb), mbot);
95 Strcpy(nb = eos(nb), rank());
97 Sprintf(nb = eos(nb), " ");
99 j = (int) ((nb + 2) - newbot1); /* strlen(newbot1) but less computation */
101 Sprintf(nb = eos(nb), "%*s", i - j, " "); /* pad with spaces */
104 Sprintf(nb = eos(nb), "St:%s Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d",
106 ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS),
109 Sprintf(nb = eos(nb), "
\8b:%s
\91\81:%-1d
\91Ï:%-1d
\92m:%-1d
\8c«:%-1d
\96£:%-1d ",
111 ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS),
115 Sprintf(nb = eos(nb),
116 (u.ualign.type == A_CHAOTIC)
118 : (u.ualign.type == A_NEUTRAL) ? " Neutral" : " Lawful");
120 Sprintf(nb = eos(nb),
121 (u.ualign.type == A_CHAOTIC)
123 : (u.ualign.type == A_NEUTRAL) ? "
\92\86\97§" : "
\92\81\8f\98");
128 Sprintf(nb = eos(nb), " S:%ld", botl_score());
130 Sprintf(nb = eos(nb), "%ld
\93_", botl_score());
138 static char newbot2[BUFSZ], /* MAXCO: botl.h */
139 /* dungeon location (and gold), hero health (HP, PW, AC),
140 experience (HD if poly'd, else Exp level and maybe Exp points),
141 time (in moves), varying number of status conditions */
142 dloc[QBUFSZ], hlth[QBUFSZ], expr[QBUFSZ], tmmv[QBUFSZ], cond[QBUFSZ];
144 unsigned dln, dx, hln, xln, tln, cln;
149 * Various min(x,9999)'s are to avoid having excessive values
150 * violate the field width assumptions in botl.h and should not
151 * impact normal play. Particularly 64-bit long for gold which
152 * could require many more digits if someone figures out a way
153 * to get and carry a really large (or negative) amount of it.
154 * Turn counter is also long, but we'll risk that.
157 /* dungeon location plus gold */
158 (void) describe_level(dloc); /* includes at least one trailing space */
159 if ((money = money_cnt(invent)) < 0L)
160 money = 0L; /* ought to issue impossible() and then discard gold */
161 Sprintf(eos(dloc), "%s:%-2ld", /* strongest hero can lift ~300000 gold */
162 (iflags.in_dumplog || iflags.invis_goldsym) ? "$"
163 : encglyph(objnum_to_glyph(GOLD_PIECE)),
164 min(money, 999999L));
166 /* '$' encoded as \GXXXXNNNN is 9 chars longer than display will need */
167 dx = strstri(dloc, "\\G") ? 9 : 0;
169 /* health and armor class (has trailing space for AC 0..9) */
170 hp = Upolyd ? u.mh : u.uhp;
171 hpmax = Upolyd ? u.mhmax : u.uhpmax;
175 Sprintf(hlth, "HP:%d(%d) Pw:%d(%d) AC:%-2d",
176 min(hp, 9999), min(hpmax, 9999),
177 min(u.uen, 9999), min(u.uenmax, 9999), u.uac);
179 Sprintf(hlth, "
\91Ì:%d(%d)
\96\82:%d(%d)
\8aZ:%-2d",
180 min(hp, 9999), min(hpmax, 9999),
181 min(u.uen, 9999), min(u.uenmax, 9999), u.uac);
187 Sprintf(expr, "HD:%d", mons[u.umonnum].mlevel);
188 else if (flags.showexp)
190 Sprintf(expr, "Xp:%d/%-1ld", u.ulevel, u.uexp);
192 Sprintf(expr, "
\8co
\8c±:%d/%-1ld", u.ulevel, u.uexp);
195 Sprintf(expr, "Exp:%d", u.ulevel);
197 Sprintf(expr, "
\8co
\8c±:%d", u.ulevel);
200 /* time/move counter */
203 Sprintf(tmmv, "T:%ld", moves);
205 Sprintf(tmmv, "
\95à:%ld", moves);
210 /* status conditions; worst ones first */
211 cond[0] = '\0'; /* once non-empty, cond will have a leading space */
214 * Stoned, Slimed, Strangled, and both types of Sick are all fatal
215 * unless remedied before timeout expires. Should we order them by
216 * shortest time left? [Probably not worth the effort, since it's
217 * unusual for more than one of them to apply at a time.]
221 Strcpy(nb = eos(nb), " Stone");
223 Strcpy(nb = eos(nb), "
\90Î
\89»");
226 Strcpy(nb = eos(nb), " Slime");
228 Strcpy(nb = eos(nb), "
\82Ç
\82ë
\82Ç
\82ë");
231 Strcpy(nb = eos(nb), " Strngl");
233 Strcpy(nb = eos(nb), "
\92\82\91§");
235 if (u.usick_type & SICK_VOMITABLE)
237 Strcpy(nb = eos(nb), " FoodPois");
239 Strcpy(nb = eos(nb), "
\90H
\93Å");
240 if (u.usick_type & SICK_NONVOMITABLE)
242 Strcpy(nb = eos(nb), " TermIll");
244 Strcpy(nb = eos(nb), "
\95a
\8bC");
246 if (u.uhs != NOT_HUNGRY)
247 Sprintf(nb = eos(nb), " %s", hu_stat[u.uhs]);
248 if ((cap = near_capacity()) > UNENCUMBERED)
249 Sprintf(nb = eos(nb), " %s", enc_stat[cap]);
252 Strcpy(nb = eos(nb), " Blind");
254 Strcpy(nb = eos(nb), "
\96Ó
\96Ú");
257 Strcpy(nb = eos(nb), " Deaf");
259 Strcpy(nb = eos(nb), "
\8e¨
\98W");
262 Strcpy(nb = eos(nb), " Stun");
264 Strcpy(nb = eos(nb), " á¿
\9dò");
267 Strcpy(nb = eos(nb), " Conf");
269 Strcpy(nb = eos(nb), "
\8d¬
\97\90");
272 Strcpy(nb = eos(nb), " Hallu");
274 Strcpy(nb = eos(nb), "
\8c¶
\8ao");
275 /* levitation and flying are mutually exclusive; riding is not */
278 Strcpy(nb = eos(nb), " Lev");
280 Strcpy(nb = eos(nb), "
\95\82\97V");
283 Strcpy(nb = eos(nb), " Fly");
285 Strcpy(nb = eos(nb), "
\94ò
\8ds");
288 Strcpy(nb = eos(nb), " Ride");
290 Strcpy(nb = eos(nb), "
\8bR
\8fæ");
294 * Put the pieces together. If they all fit, keep the traditional
295 * sequence. Otherwise, move least important parts to the end in
296 * case the interface side of things has to truncate. Note that
297 * dloc[] contains '$' encoded in ten character sequence \GXXXXNNNN
298 * so we want to test its display length rather than buffer length.
300 * We don't have an actual display limit here, so have to go by the
301 * width of the map. Since we're reordering rather than truncating,
302 * wider displays can still show wider status than the map if the
303 * interface supports that.
305 if ((dln - dx) + 1 + hln + 1 + xln + 1 + tln + 1 + cln <= COLNO) {
306 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, expr, tmmv, cond);
308 if (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1 > MAXCO) {
309 panic("bot2: second status line exceeds MAXCO (%u > %d)",
310 (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1), MAXCO);
311 } else if ((dln - dx) + 1 + hln + 1 + xln + 1 + cln <= COLNO) {
312 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, expr, cond, tmmv);
313 } else if ((dln - dx) + 1 + hln + 1 + cln <= COLNO) {
314 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, cond, expr, tmmv);
316 Sprintf(newbot2, "%s %s %s %s %s", hlth, cond, dloc, expr, tmmv);
318 /* only two or three consecutive spaces available to squeeze out */
327 /* dosave() flags completion by setting u.uhp to -1 */
328 if ((u.uhp != -1) && youmonst.data && iflags.status_updates) {
329 if (VIA_WINDOWPORT()) {
330 bot_via_windowport();
332 curs(WIN_STATUS, 1, 0);
333 putstr(WIN_STATUS, 0, do_statusline1());
334 curs(WIN_STATUS, 1, 1);
335 putmixed(WIN_STATUS, 0, do_statusline2());
338 context.botl = context.botlx = iflags.time_botl = FALSE;
344 if (flags.time && iflags.status_updates) {
345 if (VIA_WINDOWPORT()) {
348 /* old status display updates everything */
352 iflags.time_botl = FALSE;
355 /* convert experience level (1..30) to rank index (0..8) */
360 return (xlev <= 2) ? 0 : (xlev <= 30) ? ((xlev + 2) / 4) : 8;
363 #if 0 /* not currently needed */
364 /* convert rank index (0..8) to experience level (1..30) */
369 return (rank <= 0) ? 1 : (rank <= 8) ? ((rank * 4) - 2) : 30;
374 rank_of(lev, monnum, female)
379 register const struct Role *role;
383 for (role = roles; role->name.m; role++)
384 if (monnum == role->malenum || monnum == role->femalenum)
390 for (i = xlev_to_rank((int) lev); i >= 0; i--) {
391 if (female && role->rank[i].f)
392 return role->rank[i].f;
394 return role->rank[i].m;
397 /* Try the role name, instead */
398 if (female && role->name.f)
400 else if (role->name.m)
405 return "
\83v
\83\8c\83C
\83\84\81[";
408 STATIC_OVL const char *
411 return rank_of(u.ulevel, Role_switch, flags.female);
415 title_to_mon(str, rank_indx, title_length)
417 int *rank_indx, *title_length;
421 /* Loop through each of the roles */
422 for (i = 0; roles[i].name.m; i++)
423 for (j = 0; j < 9; j++) {
424 if (roles[i].rank[j].m
425 && !strncmpi(str, roles[i].rank[j].m,
426 strlen(roles[i].rank[j].m))) {
430 *title_length = strlen(roles[i].rank[j].m);
431 return roles[i].malenum;
433 if (roles[i].rank[j].f
434 && !strncmpi(str, roles[i].rank[j].f,
435 strlen(roles[i].rank[j].f))) {
439 *title_length = strlen(roles[i].rank[j].f);
440 return (roles[i].femalenum != NON_PM) ? roles[i].femalenum
450 register int i, r, maxr = 0;
451 for (i = 0; i < 9; i++) {
452 if (urole.rank[i].m && (r = strlen(urole.rank[i].m)) > maxr)
454 if (urole.rank[i].f && (r = strlen(urole.rank[i].f)) > maxr)
465 long deepest = deepest_lev_reached(FALSE);
468 utotal = money_cnt(invent) + hidden_gold();
469 if ((utotal -= u.umoney0) < 0L)
471 utotal += u.urexp + (50 * (deepest - 1))
472 + (deepest > 30 ? 10000 : deepest > 20 ? 1000 * (deepest - 20) : 0);
473 if (utotal < u.urexp)
474 utotal = LONG_MAX; /* wrap around */
477 #endif /* SCORE_ON_BOTL */
479 /* provide the name of the current level for display by various ports */
486 /* TODO: Add in dungeon name */
487 if (Is_knox(&u.uz)) {
488 Sprintf(buf, "%s ", dungeons[u.uz.dnum].dname);
489 } else if (In_quest(&u.uz)) {
491 Sprintf(buf, "Home %d ", dunlev(&u.uz));
493 Sprintf(buf, "
\8cÌ
\8b½ %d ", dunlev(&u.uz));
494 } else if (In_endgame(&u.uz)) {
495 /* [3.6.2: this used to be "Astral Plane" or generic "End Game"] */
496 (void) endgamelevelname(buf, depth(&u.uz));
498 (void) strsubst(buf, "Plane of ", ""); /* just keep <element> */
502 /* ports with more room may expand this one */
504 Sprintf(buf, "Dlvl:%-2d ", depth(&u.uz));
506 Sprintf(buf, "
\92n
\89º:%-2d ", depth(&u.uz));
512 /* =======================================================================*/
513 /* statusnew routines */
514 /* =======================================================================*/
516 /* structure that tracks the status details in the core */
518 #define MAXVALWIDTH 80 /* actually less, but was using 80 to allocate title
519 * and leveldesc then using QBUFSZ everywhere else */
520 #ifdef STATUS_HILITES
522 enum statusfields fld;
527 char textmatch[MAXVALWIDTH];
528 enum relationships rel;
530 struct hilite_s *next;
532 #endif /* STATUS_HILITES */
537 long time; /* moves when this field hilite times out */
538 boolean chg; /* need to recalc time? */
539 boolean percent_matters;
545 enum statusfields idxmax;
546 enum statusfields fld;
547 #ifdef STATUS_HILITES
548 struct hilite_s *hilite_rule; /* the entry, if any, in 'thresholds'
549 * list that currently applies */
550 struct hilite_s *thresholds;
554 STATIC_DCL boolean FDECL(eval_notify_windowport_field, (int, boolean *, int));
555 STATIC_DCL void FDECL(evaluate_and_notify_windowport, (boolean *, int));
556 STATIC_DCL void NDECL(init_blstats);
557 STATIC_DCL int FDECL(compare_blstats, (struct istat_s *, struct istat_s *));
558 STATIC_DCL char *FDECL(anything_to_s, (char *, anything *, int));
559 STATIC_DCL int FDECL(percentage, (struct istat_s *, struct istat_s *));
560 STATIC_DCL int NDECL(exp_percentage);
562 #ifdef STATUS_HILITES
563 STATIC_DCL void FDECL(s_to_anything, (anything *, char *, int));
564 STATIC_DCL enum statusfields FDECL(fldname_to_bl_indx, (const char *));
565 STATIC_DCL boolean FDECL(hilite_reset_needed, (struct istat_s *, long));
566 STATIC_DCL boolean FDECL(noneoftheabove, (const char *));
567 STATIC_DCL struct hilite_s *FDECL(get_hilite, (int, int, genericptr_t,
569 STATIC_DCL void FDECL(split_clridx, (int, int *, int *));
570 STATIC_DCL boolean FDECL(is_ltgt_percentnumber, (const char *));
571 STATIC_DCL boolean FDECL(has_ltgt_percentnumber, (const char *));
572 STATIC_DCL int FDECL(splitsubfields, (char *, char ***, int));
573 STATIC_DCL boolean FDECL(is_fld_arrayvalues, (const char *,
576 STATIC_DCL int FDECL(query_arrayvalue, (const char *, const char *const *,
578 STATIC_DCL void FDECL(status_hilite_add_threshold, (int, struct hilite_s *));
579 STATIC_DCL boolean FDECL(parse_status_hl2, (char (*)[QBUFSZ], BOOLEAN_P));
580 STATIC_DCL char *FDECL(conditionbitmask2str, (unsigned long));
581 STATIC_DCL unsigned long FDECL(match_str2conditionbitmask, (const char *));
582 STATIC_DCL unsigned long FDECL(str2conditionbitmask, (char *));
583 STATIC_DCL boolean FDECL(parse_condition, (char (*)[QBUFSZ], int));
584 STATIC_DCL char *FDECL(hlattr2attrname, (int, char *, int));
585 STATIC_DCL void FDECL(status_hilite_linestr_add, (int, struct hilite_s *,
586 unsigned long, const char *));
587 STATIC_DCL void NDECL(status_hilite_linestr_done);
588 STATIC_DCL int FDECL(status_hilite_linestr_countfield, (int));
589 STATIC_DCL void NDECL(status_hilite_linestr_gather_conditions);
590 STATIC_DCL void NDECL(status_hilite_linestr_gather);
591 STATIC_DCL char *FDECL(status_hilite2str, (struct hilite_s *));
592 STATIC_DCL int NDECL(status_hilite_menu_choose_field);
593 STATIC_DCL int FDECL(status_hilite_menu_choose_behavior, (int));
594 STATIC_DCL int FDECL(status_hilite_menu_choose_updownboth, (int, const char *,
595 BOOLEAN_P, BOOLEAN_P));
596 STATIC_DCL boolean FDECL(status_hilite_menu_add, (int));
597 #define has_hilite(i) (blstats[0][(i)].thresholds)
598 /* TH_UPDOWN encompasses specific 'up' and 'down' also general 'changed' */
599 #define Is_Temp_Hilite(rule) ((rule) && (rule)->behavior == BL_TH_UPDOWN)
601 /* pointers to current hilite rule and list of this field's defined rules */
602 #define INIT_THRESH , (struct hilite_s *) 0, (struct hilite_s *) 0
603 #else /* !STATUS_HILITES */
604 #define INIT_THRESH /*empty*/
607 #define INIT_BLSTAT(name, fmtstr, anytyp, wid, fld) \
608 { name, fmtstr, 0L, FALSE, FALSE, 0, anytyp, \
609 { (genericptr_t) 0 }, (char *) 0, \
610 wid, -1, fld INIT_THRESH }
611 #define INIT_BLSTATP(name, fmtstr, anytyp, wid, maxfld, fld) \
612 { name, fmtstr, 0L, FALSE, TRUE, 0, anytyp, \
613 { (genericptr_t) 0 }, (char *) 0, \
614 wid, maxfld, fld INIT_THRESH }
616 /* If entries are added to this, botl.h will require updating too.
617 'max' value of BL_EXP gets special handling since the percentage
618 involved isn't a direct 100*current/maximum calculation. */
619 STATIC_VAR struct istat_s initblstats[MAXBLSTATS] = {
620 INIT_BLSTAT("title", "%s", ANY_STR, MAXVALWIDTH, BL_TITLE),
622 INIT_BLSTAT("strength", " St:%s", ANY_INT, 10, BL_STR),
624 INIT_BLSTAT("strength", "
\8b:%s", ANY_INT, 10, BL_STR),
626 INIT_BLSTAT("dexterity", " Dx:%s", ANY_INT, 10, BL_DX),
628 INIT_BLSTAT("dexterity", "
\91\81:%s", ANY_INT, 10, BL_DX),
630 INIT_BLSTAT("constitution", " Co:%s", ANY_INT, 10, BL_CO),
632 INIT_BLSTAT("constitution", "
\91Ï:%s", ANY_INT, 10, BL_CO),
634 INIT_BLSTAT("intelligence", " In:%s", ANY_INT, 10, BL_IN),
636 INIT_BLSTAT("intelligence", "
\92m:%s", ANY_INT, 10, BL_IN),
638 INIT_BLSTAT("wisdom", " Wi:%s", ANY_INT, 10, BL_WI),
640 INIT_BLSTAT("wisdom", "
\8c«:%s", ANY_INT, 10, BL_WI),
642 INIT_BLSTAT("charisma", " Ch:%s", ANY_INT, 10, BL_CH),
644 INIT_BLSTAT("charisma", "
\96£:%s", ANY_INT, 10, BL_CH),
645 INIT_BLSTAT("alignment", " %s", ANY_STR, 40, BL_ALIGN),
646 INIT_BLSTAT("score", " S:%s", ANY_LONG, 20, BL_SCORE),
647 INIT_BLSTAT("carrying-capacity", " %s", ANY_INT, 20, BL_CAP),
648 INIT_BLSTAT("gold", " %s", ANY_LONG, 30, BL_GOLD),
650 INIT_BLSTATP("power", " Pw:%s", ANY_INT, 10, BL_ENEMAX, BL_ENE),
652 INIT_BLSTATP("power", "
\96\82:%s", ANY_INT, 10, BL_ENEMAX, BL_ENE),
653 INIT_BLSTAT("power-max", "(%s)", ANY_INT, 10, BL_ENEMAX),
655 INIT_BLSTATP("experience-level", " Xp:%s", ANY_INT, 10, BL_EXP, BL_XP),
657 INIT_BLSTATP("experience-level", "
\8co
\8c±:%s", ANY_INT, 10, BL_EXP, BL_XP),
659 INIT_BLSTAT("armor-class", " AC:%s", ANY_INT, 10, BL_AC),
661 INIT_BLSTAT("armor-class", "
\8aZ:%s", ANY_INT, 10, BL_AC),
662 INIT_BLSTAT("HD", " HD:%s", ANY_INT, 10, BL_HD),
664 INIT_BLSTAT("time", " T:%s", ANY_LONG, 20, BL_TIME),
666 INIT_BLSTAT("time", "
\95à:%s", ANY_LONG, 20, BL_TIME),
667 /* hunger used to be 'ANY_UINT'; see note below in bot_via_windowport() */
668 INIT_BLSTAT("hunger", " %s", ANY_INT, 40, BL_HUNGER),
670 INIT_BLSTATP("hitpoints", " HP:%s", ANY_INT, 10, BL_HPMAX, BL_HP),
672 INIT_BLSTATP("hitpoints", "
\91Ì:%s", ANY_INT, 10, BL_HPMAX, BL_HP),
673 INIT_BLSTAT("hitpoints-max", "(%s)", ANY_INT, 10, BL_HPMAX),
674 INIT_BLSTAT("dungeon-level", "%s", ANY_STR, MAXVALWIDTH, BL_LEVELDESC),
675 INIT_BLSTATP("experience", "/%s", ANY_LONG, 20, BL_EXP, BL_EXP),
676 INIT_BLSTAT("condition", "%s", ANY_MASK32, 0, BL_CONDITION)
683 struct istat_s blstats[2][MAXBLSTATS];
684 static boolean blinit = FALSE, update_all = FALSE;
685 static boolean valset[MAXBLSTATS];
686 #ifdef STATUS_HILITES
687 static long bl_hilite_moves = 0L;
690 /* we don't put this next declaration in #ifdef STATUS_HILITES.
691 * In the absence of STATUS_HILITES, each array
692 * element will be 0 however, and quite meaningless,
693 * but we need to pass the first array element as
694 * the final argument of status_update, with or
695 * without STATUS_HILITES.
697 static unsigned long cond_hilites[BL_ATTCLR_MAX];
698 static int now_or_before_idx = 0; /* 0..1 for array[2][] first index */
710 panic("bot before init.");
712 /* toggle from previous iteration */
713 idx = 1 - now_or_before_idx; /* 0 -> 1, 1 -> 0 */
714 now_or_before_idx = idx;
716 /* clear the "value set" indicators */
717 (void) memset((genericptr_t) valset, 0, MAXBLSTATS * sizeof (boolean));
720 * Note: min(x,9999) - we enforce the same maximum on hp, maxhp,
721 * pw, maxpw, and gold as basic status formatting so that the two
722 * modes of status display don't produce different information.
726 * Player name and title.
728 Strcpy(nb = buf, plname);
729 nb[0] = highc(nb[0]);
730 titl = !Upolyd ? rank() : mons[u.umonnum].mname;
732 i = (int) (strlen(buf) + sizeof " the " + strlen(titl) - sizeof "");
734 i = (int) (strlen(buf) + sizeof " " + strlen(titl) - sizeof "");
736 /* if "Name the Rank/monster" is too long, we truncate the name
737 but always keep at least 10 characters of it; when hitpintbar is
738 enabled, anything beyond 30 (long monster name) will be truncated */
741 i = 30 - (int) (sizeof " the " + strlen(titl) - sizeof "");
743 i = 30 - (int) (sizeof " " + strlen(titl) - sizeof "");
745 nb[max(i, 10)] = '\0';
748 Strcpy(nb = eos(nb), " the ");
750 Strcpy(nb = eos(nb), " ");
752 Strcpy(nb = eos(nb), titl);
753 if (Upolyd) { /* when poly'd, capitalize monster name */
754 for (i = 0; nb[i]; i++)
755 if (i == 0 || nb[i - 1] == ' ')
756 nb[i] = highc(nb[i]);
758 Sprintf(blstats[idx][BL_TITLE].val, "%-30s", buf);
759 valset[BL_TITLE] = TRUE; /* indicate val already set */
762 blstats[idx][BL_STR].a.a_int = ACURR(A_STR);
763 Strcpy(blstats[idx][BL_STR].val, get_strength_str());
764 valset[BL_STR] = TRUE; /* indicate val already set */
766 /* Dexterity, constitution, intelligence, wisdom, charisma. */
767 blstats[idx][BL_DX].a.a_int = ACURR(A_DEX);
768 blstats[idx][BL_CO].a.a_int = ACURR(A_CON);
769 blstats[idx][BL_IN].a.a_int = ACURR(A_INT);
770 blstats[idx][BL_WI].a.a_int = ACURR(A_WIS);
771 blstats[idx][BL_CH].a.a_int = ACURR(A_CHA);
775 Strcpy(blstats[idx][BL_ALIGN].val, (u.ualign.type == A_CHAOTIC)
777 : (u.ualign.type == A_NEUTRAL)
781 Strcpy(blstats[idx][BL_ALIGN].val, (u.ualign.type == A_CHAOTIC)
783 : (u.ualign.type == A_NEUTRAL)
789 blstats[idx][BL_SCORE].a.a_long =
791 flags.showscore ? botl_score() :
796 i = Upolyd ? u.mh : u.uhp;
799 blstats[idx][BL_HP].a.a_int = min(i, 9999);
800 i = Upolyd ? u.mhmax : u.uhpmax;
801 blstats[idx][BL_HPMAX].a.a_int = min(i, 9999);
804 (void) describe_level(blstats[idx][BL_LEVELDESC].val);
805 valset[BL_LEVELDESC] = TRUE; /* indicate val already set */
808 if ((money = money_cnt(invent)) < 0L)
809 money = 0L; /* ought to issue impossible() and then discard gold */
810 blstats[idx][BL_GOLD].a.a_long = min(money, 999999L);
812 * The tty port needs to display the current symbol for gold
813 * as a field header, so to accommodate that we pass gold with
814 * that already included. If a window port needs to use the text
815 * gold amount without the leading "$:" the port will have to
816 * skip past ':' to the value pointer it was passed in status_update()
817 * for the BL_GOLD case.
819 * Another quirk of BL_GOLD is that the field display may have
820 * changed if a new symbol set was loaded, or we entered or left
823 * The currency prefix is encoded as ten character \GXXXXNNNN
826 Sprintf(blstats[idx][BL_GOLD].val, "%s:%ld",
827 (iflags.in_dumplog || iflags.invis_goldsym) ? "$"
828 : encglyph(objnum_to_glyph(GOLD_PIECE)),
829 blstats[idx][BL_GOLD].a.a_long);
830 valset[BL_GOLD] = TRUE; /* indicate val already set */
832 /* Power (magical energy) */
833 blstats[idx][BL_ENE].a.a_int = min(u.uen, 9999);
834 blstats[idx][BL_ENEMAX].a.a_int = min(u.uenmax, 9999);
837 blstats[idx][BL_AC].a.a_int = u.uac;
839 /* Monster level (if Upolyd) */
840 blstats[idx][BL_HD].a.a_int = Upolyd ? (int) mons[u.umonnum].mlevel : 0;
843 blstats[idx][BL_XP].a.a_int = u.ulevel;
844 blstats[idx][BL_EXP].a.a_long = u.uexp;
847 blstats[idx][BL_TIME].a.a_long = moves;
850 /* note: u.uhs is unsigned, and 3.6.1's STATUS_HILITE defined
851 BL_HUNGER to be ANY_UINT, but that was the only non-int/non-long
852 numeric field so it's far simpler to treat it as plain int and
853 not need ANY_UINT handling at all */
854 blstats[idx][BL_HUNGER].a.a_int = (int) u.uhs;
855 Strcpy(blstats[idx][BL_HUNGER].val,
856 (u.uhs != NOT_HUNGRY) ? hu_stat[u.uhs] : "");
857 valset[BL_HUNGER] = TRUE;
859 /* Carrying capacity */
860 cap = near_capacity();
861 blstats[idx][BL_CAP].a.a_int = cap;
862 Strcpy(blstats[idx][BL_CAP].val,
863 (cap > UNENCUMBERED) ? enc_stat[cap] : "");
864 valset[BL_CAP] = TRUE;
867 blstats[idx][BL_CONDITION].a.a_ulong = 0L;
869 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STONE;
871 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_SLIME;
873 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STRNGL;
874 if (Sick && (u.usick_type & SICK_VOMITABLE) != 0)
875 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FOODPOIS;
876 if (Sick && (u.usick_type & SICK_NONVOMITABLE) != 0)
877 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_TERMILL;
879 * basic formatting puts hunger status and encumbrance here
882 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_BLIND;
884 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_DEAF;
886 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STUN;
888 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_CONF;
890 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_HALLU;
891 /* levitation and flying are mututally exclusive */
893 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_LEV;
895 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FLY;
897 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_RIDE;
898 evaluate_and_notify_windowport(valset, idx);
901 /* update just the status lines' 'time' field */
905 int idx = now_or_before_idx; /* no 0/1 toggle */
909 blstats[idx][fld].a.a_long = moves;
912 eval_notify_windowport_field(fld, valset, idx);
913 if ((windowprocs.wincap2 & WC2_FLUSH_STATUS) != 0L)
914 status_update(BL_FLUSH, (genericptr_t) 0, 0, 0,
915 NO_COLOR, (unsigned long *) 0);
920 eval_notify_windowport_field(fld, valsetlist, idx)
924 static int oldrndencode = 0;
925 static nhsym oldgoldsym = 0;
926 int pc, chg, color = NO_COLOR;
928 boolean updated = FALSE, reset;
929 struct istat_s *curr, *prev;
930 enum statusfields fldmax;
933 * Now pass the changed values to window port.
935 anytype = blstats[idx][fld].anytype;
936 curr = &blstats[idx][fld];
937 prev = &blstats[1 - idx][fld];
940 chg = update_all ? 0 : compare_blstats(prev, curr);
943 * Dynamically update 'percent_matters' as rules are added or
944 * removed to track whether any of them are precentage rules.
945 * Then there'll be no need to assume that non-Null 'thresholds'
946 * means that percentages need to be kept up to date.
947 * [Affects exp_percent_changing() too.]
949 if (((chg || update_all || fld == BL_XP)
950 && curr->percent_matters && curr->thresholds)
951 /* when 'hitpointbar' is On, percent matters even if HP
952 hasn't changed and has no percentage rules (in case HPmax
953 has changed when HP hasn't, where we ordinarily wouldn't
954 update HP so would miss an update of the hitpoint bar) */
955 || (fld == BL_HP && iflags.wc2_hitpointbar)) {
956 fldmax = curr->idxmax;
957 pc = (fldmax == BL_EXP) ? exp_percentage()
958 : (fldmax >= 0) ? percentage(curr, &blstats[idx][fldmax])
959 : 0; /* bullet proofing; can't get here */
960 if (pc != prev->percent_value)
962 curr->percent_value = pc;
967 /* Temporary? hack: moveloop()'s prolog for a new game sets
968 * context.rndencode after the status window has been init'd,
969 * so $:0 has already been encoded and cached by the window
970 * port. Without this hack, gold's \G sequence won't be
971 * recognized and ends up being displayed as-is for 'update_all'.
973 * Also, even if context.rndencode hasn't changed and the
974 * gold amount itself hasn't changed, the glyph portion of the
975 * encoding may have changed if a new symset was put into effect.
978 * XXXX = the context.rndencode portion
979 * NNNN = the glyph portion
980 * 25 = the gold amount
982 * Setting 'chg = 2' is enough to render the field properly, but
983 * not to honor an initial highlight, so force 'update_all = TRUE'.
986 && (context.rndencode != oldrndencode
987 || showsyms[COIN_CLASS + SYM_OFF_O] != oldgoldsym)) {
988 update_all = TRUE; /* chg = 2; */
989 oldrndencode = context.rndencode;
990 oldgoldsym = showsyms[COIN_CLASS + SYM_OFF_O];
994 #ifdef STATUS_HILITES
995 if (!update_all && !chg && curr->time) {
996 reset = hilite_reset_needed(prev, bl_hilite_moves);
998 curr->time = prev->time = 0L;
1002 if (update_all || chg || reset) {
1003 if (!valsetlist[fld])
1004 (void) anything_to_s(curr->val, &curr->a, anytype);
1006 if (anytype != ANY_MASK32) {
1007 #ifdef STATUS_HILITES
1008 if (chg || *curr->val) {
1009 /* if Xp percentage changed, we set 'chg' to 1 above;
1010 reset that if the Xp value hasn't actually changed
1011 or possibly went down rather than up (level loss) */
1012 if (chg == 1 && fld == BL_XP)
1013 chg = compare_blstats(prev, curr);
1015 curr->hilite_rule = get_hilite(idx, fld,
1016 (genericptr_t) &curr->a,
1018 prev->hilite_rule = curr->hilite_rule;
1024 #endif /* STATUS_HILITES */
1025 status_update(fld, (genericptr_t) curr->val,
1026 chg, pc, color, (unsigned long *) 0);
1028 /* Color for conditions is done through cond_hilites[] */
1029 status_update(fld, (genericptr_t) &curr->a.a_ulong,
1030 chg, pc, color, cond_hilites);
1032 curr->chg = prev->chg = TRUE;
1039 evaluate_and_notify_windowport(valsetlist, idx)
1041 boolean *valsetlist;
1043 int i, updated = 0, notpresent = 0;
1046 * Now pass the changed values to window port.
1048 for (i = 0; i < MAXBLSTATS; i++) {
1049 if (((i == BL_SCORE) && !flags.showscore)
1050 || ((i == BL_EXP) && !flags.showexp)
1051 || ((i == BL_TIME) && !flags.time)
1052 || ((i == BL_HD) && !Upolyd)
1053 || ((i == BL_XP || i == BL_EXP) && Upolyd)) {
1057 if (eval_notify_windowport_field(i, valsetlist, idx))
1062 * 1. It is possible to get here, with nothing having been pushed
1063 * to the window port, when none of the info has changed.
1065 * 2. Some window ports are also known to optimize by only drawing
1066 * fields that have changed since the previous update.
1068 * In both of those situations, we need to force updates to
1069 * all of the fields when context.botlx is set. The tty port in
1070 * particular has a problem if that isn't done, since the core sets
1071 * context.botlx when a menu or text display obliterates the status
1074 * For those situations, to trigger the full update of every field
1075 * whether changed or not, call status_update() with BL_RESET.
1077 * For regular processing and to notify the window port that a
1078 * bot() round has finished and it's time to trigger a flush of
1079 * all buffered changes received thus far but not reflected in
1080 * the display, call status_update() with BL_FLUSH.
1083 if (context.botlx && (windowprocs.wincap2 & WC2_RESET_STATUS) != 0L)
1084 status_update(BL_RESET, (genericptr_t) 0, 0, 0,
1085 NO_COLOR, (unsigned long *) 0);
1086 else if ((updated || context.botlx)
1087 && (windowprocs.wincap2 & WC2_FLUSH_STATUS) != 0L)
1088 status_update(BL_FLUSH, (genericptr_t) 0, 0, 0,
1089 NO_COLOR, (unsigned long *) 0);
1091 context.botl = context.botlx = iflags.time_botl = FALSE;
1096 status_initialize(reassessment)
1097 boolean reassessment; /* TRUE: just recheck fields w/o other initialization */
1099 enum statusfields fld;
1102 const char *fieldfmt, *fieldname;
1104 if (!reassessment) {
1106 impossible("2nd status_initialize with full init.");
1108 (*windowprocs.win_status_init)();
1110 } else if (!blinit) {
1111 panic("status 'reassess' before init");
1113 for (i = 0; i < MAXBLSTATS; ++i) {
1114 fld = initblstats[i].fld;
1115 fldenabl = (fld == BL_SCORE) ? flags.showscore
1116 : (fld == BL_TIME) ? flags.time
1117 : (fld == BL_EXP) ? (boolean) (flags.showexp && !Upolyd)
1118 : (fld == BL_XP) ? (boolean) !Upolyd
1119 : (fld == BL_HD) ? (boolean) Upolyd
1122 fieldname = initblstats[i].fldname;
1123 fieldfmt = (fld == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30.30s"
1124 : initblstats[i].fldfmt;
1125 status_enablefield(fld, fieldname, fieldfmt, fldenabl);
1128 context.botlx = TRUE;
1136 /* call the window port cleanup routine first */
1137 if (windowprocs.win_status_finish)
1138 (*windowprocs.win_status_finish)();
1140 /* free memory that we alloc'd now */
1141 for (i = 0; i < MAXBLSTATS; ++i) {
1142 if (blstats[0][i].val)
1143 free((genericptr_t) blstats[0][i].val), blstats[0][i].val = 0;
1144 if (blstats[1][i].val)
1145 free((genericptr_t) blstats[1][i].val), blstats[1][i].val = 0;
1146 #ifdef STATUS_HILITES
1147 /* pointer to an entry in thresholds list; Null it out since
1148 that list is about to go away */
1149 blstats[0][i].hilite_rule = blstats[1][i].hilite_rule = 0;
1150 if (blstats[0][i].thresholds) {
1151 struct hilite_s *temp, *next;
1153 for (temp = blstats[0][i].thresholds; temp; temp = next) {
1155 free((genericptr_t) temp);
1157 blstats[0][i].thresholds = blstats[1][i].thresholds = 0;
1159 #endif /* STATUS_HILITES */
1166 static boolean initalready = FALSE;
1170 impossible("init_blstats called more than once.");
1173 for (i = 0; i <= 1; ++i) {
1174 for (j = 0; j < MAXBLSTATS; ++j) {
1175 #ifdef STATUS_HILITES
1176 struct hilite_s *keep_hilite_chain = blstats[i][j].thresholds;
1179 blstats[i][j] = initblstats[j];
1180 blstats[i][j].a = zeroany;
1181 if (blstats[i][j].valwidth) {
1182 blstats[i][j].val = (char *) alloc(blstats[i][j].valwidth);
1183 blstats[i][j].val[0] = '\0';
1185 blstats[i][j].val = (char *) 0;
1186 #ifdef STATUS_HILITES
1187 blstats[i][j].thresholds = keep_hilite_chain;
1195 * This compares the previous stat with the current stat,
1196 * and returns one of the following results based on that:
1198 * if prev_value < new_value (stat went up, increased)
1201 * if prev_value > new_value (stat went down, decreased)
1204 * if prev_value == new_value (stat stayed the same)
1208 * - for bitmasks, 0 = stayed the same, 1 = changed
1209 * - for strings, 0 = stayed the same, 1 = changed
1213 compare_blstats(bl1, bl2)
1214 struct istat_s *bl1, *bl2;
1216 int anytype, result = 0;
1219 panic("compare_blstat: bad istat pointer %s, %s",
1220 fmt_ptr((genericptr_t) bl1), fmt_ptr((genericptr_t) bl2));
1223 anytype = bl1->anytype;
1224 if ((!bl1->a.a_void || !bl2->a.a_void)
1225 && (anytype == ANY_IPTR || anytype == ANY_UPTR || anytype == ANY_LPTR
1226 || anytype == ANY_ULPTR)) {
1227 panic("compare_blstat: invalid pointer %s, %s",
1228 fmt_ptr((genericptr_t) bl1->a.a_void),
1229 fmt_ptr((genericptr_t) bl2->a.a_void));
1234 result = (bl1->a.a_int < bl2->a.a_int)
1236 : (bl1->a.a_int > bl2->a.a_int) ? -1 : 0;
1239 result = (*bl1->a.a_iptr < *bl2->a.a_iptr)
1241 : (*bl1->a.a_iptr > *bl2->a.a_iptr) ? -1 : 0;
1244 result = (bl1->a.a_long < bl2->a.a_long)
1246 : (bl1->a.a_long > bl2->a.a_long) ? -1 : 0;
1249 result = (*bl1->a.a_lptr < *bl2->a.a_lptr)
1251 : (*bl1->a.a_lptr > *bl2->a.a_lptr) ? -1 : 0;
1254 result = (bl1->a.a_uint < bl2->a.a_uint)
1256 : (bl1->a.a_uint > bl2->a.a_uint) ? -1 : 0;
1259 result = (*bl1->a.a_uptr < *bl2->a.a_uptr)
1261 : (*bl1->a.a_uptr > *bl2->a.a_uptr) ? -1 : 0;
1264 result = (bl1->a.a_ulong < bl2->a.a_ulong)
1266 : (bl1->a.a_ulong > bl2->a.a_ulong) ? -1 : 0;
1269 result = (*bl1->a.a_ulptr < *bl2->a.a_ulptr)
1271 : (*bl1->a.a_ulptr > *bl2->a.a_ulptr) ? -1 : 0;
1274 result = sgn(strcmp(bl1->val, bl2->val));
1277 result = (bl1->a.a_ulong != bl2->a.a_ulong);
1286 anything_to_s(buf, a, anytype)
1296 Sprintf(buf, "%lu", a->a_ulong);
1299 Sprintf(buf, "%lx", a->a_ulong);
1302 Sprintf(buf, "%ld", a->a_long);
1305 Sprintf(buf, "%d", a->a_int);
1308 Sprintf(buf, "%u", a->a_uint);
1311 Sprintf(buf, "%d", *a->a_iptr);
1314 Sprintf(buf, "%ld", *a->a_lptr);
1317 Sprintf(buf, "%lu", *a->a_ulptr);
1320 Sprintf(buf, "%u", *a->a_uptr);
1322 case ANY_STR: /* do nothing */
1331 #ifdef STATUS_HILITES
1333 s_to_anything(a, buf, anytype)
1343 a->a_long = atol(buf);
1346 a->a_int = atoi(buf);
1349 a->a_uint = (unsigned) atoi(buf);
1352 a->a_ulong = (unsigned long) atol(buf);
1356 *a->a_iptr = atoi(buf);
1360 *a->a_uptr = (unsigned) atoi(buf);
1364 *a->a_lptr = atol(buf);
1368 *a->a_ulptr = (unsigned long) atol(buf);
1371 a->a_ulong = (unsigned long) atol(buf);
1379 #endif /* STATUS_HILITES */
1382 percentage(bl, maxbl)
1383 struct istat_s *bl, *maxbl;
1390 unsigned long ulval;
1392 if (!bl || !maxbl) {
1393 impossible("percentage: bad istat pointer %s, %s",
1394 fmt_ptr((genericptr_t) bl), fmt_ptr((genericptr_t) maxbl));
1398 ival = 0, lval = 0L, uval = 0U, ulval = 0UL;
1399 anytype = bl->anytype;
1400 if (maxbl->a.a_void) {
1404 result = ((100 * ival) / maxbl->a.a_int);
1407 lval = bl->a.a_long;
1408 result = (int) ((100L * lval) / maxbl->a.a_long);
1411 uval = bl->a.a_uint;
1412 result = (int) ((100U * uval) / maxbl->a.a_uint);
1415 ulval = bl->a.a_ulong;
1416 result = (int) ((100UL * ulval) / maxbl->a.a_ulong);
1419 ival = *bl->a.a_iptr;
1420 result = ((100 * ival) / (*maxbl->a.a_iptr));
1423 lval = *bl->a.a_lptr;
1424 result = (int) ((100L * lval) / (*maxbl->a.a_lptr));
1427 uval = *bl->a.a_uptr;
1428 result = (int) ((100U * uval) / (*maxbl->a.a_uptr));
1431 ulval = *bl->a.a_ulptr;
1432 result = (int) ((100UL * ulval) / (*maxbl->a.a_ulptr));
1436 /* don't let truncation from integer division produce a zero result
1437 from a non-zero input; note: if we ever change to something like
1438 ((((1000 * val) / max) + 5) / 10) for a rounded result, we'll
1439 also need to check for and convert false 100 to 99 */
1440 if (result == 0 && (ival != 0 || lval != 0L || uval != 0U || ulval != 0UL))
1446 /* percentage for both xp (level) and exp (points) is the percentage for
1447 (curr_exp - this_level_start) in (next_level_start - this_level_start) */
1453 if (u.ulevel < 30) {
1454 long exp_val, nxt_exp_val, curlvlstart;
1456 curlvlstart = newuexp(u.ulevel - 1);
1457 exp_val = u.uexp - curlvlstart;
1458 nxt_exp_val = newuexp(u.ulevel) - curlvlstart;
1459 if (exp_val == nxt_exp_val - 1L) {
1461 * Full 100% is unattainable since hero gains a level
1462 * and the threshold for next level increases, but treat
1463 * (next_level_start - 1 point) as a special case. It's a
1464 * key value after being level drained so is something that
1465 * some players would like to be able to highlight distinctly.
1469 struct istat_s curval, maxval;
1471 curval.anytype = maxval.anytype = ANY_LONG;
1472 curval.a = maxval.a = zeroany;
1473 curval.a.a_long = exp_val;
1474 maxval.a.a_long = nxt_exp_val;
1475 /* maximum delta between levels is 10000000; calculation of
1476 100 * (10000000 - N) / 10000000 fits within 32-bit long */
1477 res = percentage(&curval, &maxval);
1483 /* experience points have changed but experience level hasn't; decide whether
1484 botl update is needed for a different percentage highlight rule for Xp */
1486 exp_percent_changing()
1488 int pc, color_dummy;
1490 struct hilite_s *rule;
1491 struct istat_s *curr;
1493 /* if status update is already requested, skip this processing */
1494 if (!context.botl) {
1496 * Status update is warranted iff percent integer changes and the new
1497 * percentage results in a different highlighting rule being selected.
1499 curr = &blstats[now_or_before_idx][BL_XP];
1500 /* TODO: [see eval_notify_windowport_field() about percent_matters
1501 and the check against 'thresholds'] */
1502 if (curr->percent_matters && curr->thresholds
1503 && (pc = exp_percentage()) != curr->percent_value) {
1505 a.a_int = (int) u.ulevel;
1506 rule = get_hilite(now_or_before_idx, BL_XP,
1507 (genericptr_t) &a, 0, pc, &color_dummy);
1508 if (rule != curr->hilite_rule)
1509 return TRUE; /* caller should set 'context.botl' to True */
1515 /* callback so that interface can get capacity index rather than trying
1516 to reconstruct that from the encumbrance string or asking the general
1517 core what the value is */
1523 #ifdef STATUS_HILITES
1524 cap = blstats[now_or_before_idx][BL_CAP].a.a_int;
1526 cap = near_capacity();
1531 /* callback so that interface can get hunger index rather than trying to
1532 reconstruct that from the hunger string or dipping into core internals */
1538 #ifdef STATUS_HILITES
1539 uhs = blstats[now_or_before_idx][BL_HUNGER].a.a_int;
1546 /* used by X11 for "tty status" even when STATUS_HILITES is disabled */
1548 bl_idx_to_fldname(idx)
1551 if (idx >= 0 && idx < MAXBLSTATS)
1552 return initblstats[idx].fldname;
1553 return (const char *) 0;
1556 #ifdef STATUS_HILITES
1558 /****************************************************************************/
1559 /* Core status hiliting support */
1560 /****************************************************************************/
1562 struct hilite_s status_hilites[MAXBLSTATS];
1564 static struct fieldid_t {
1565 const char *fieldname;
1566 enum statusfields fldid;
1567 } fieldids_alias[] = {
1568 { "characteristics", BL_CHARACTERISTICS },
1569 { "encumbrance", BL_CAP },
1570 { "experience-points", BL_EXP },
1574 { "points", BL_SCORE },
1577 { "pw-max", BL_ENEMAX },
1581 { "hit-dice", BL_HD },
1582 { "turns", BL_TIME },
1584 { "hp-max", BL_HPMAX },
1585 { "dgn", BL_LEVELDESC },
1588 { "flags", BL_CONDITION },
1592 /* format arguments */
1593 static const char threshold_value[] = "hilite_status threshold ",
1594 is_out_of_range[] = " is out of range";
1597 /* field name to bottom line index */
1598 STATIC_OVL enum statusfields
1599 fldname_to_bl_indx(name)
1602 int i, nmatches = 0, fld = 0;
1604 if (name && *name) {
1605 /* check matches to canonical names */
1606 for (i = 0; i < SIZE(initblstats); i++)
1607 if (fuzzymatch(initblstats[i].fldname, name, " -_", TRUE)) {
1608 fld = initblstats[i].fld;
1614 for (i = 0; fieldids_alias[i].fieldname; i++)
1615 if (fuzzymatch(fieldids_alias[i].fieldname, name,
1617 fld = fieldids_alias[i].fldid;
1623 /* check partial matches to canonical names */
1624 int len = (int) strlen(name);
1626 for (i = 0; i < SIZE(initblstats); i++)
1627 if (!strncmpi(name, initblstats[i].fldname, len)) {
1628 fld = initblstats[i].fld;
1634 return (nmatches == 1) ? fld : BL_FLUSH;
1638 hilite_reset_needed(bl_p, augmented_time)
1639 struct istat_s *bl_p;
1640 long augmented_time; /* no longer augmented; it once encoded fractional
1641 * amounts for multiple moves within same turn */
1644 * This 'multi' handling may need some tuning...
1649 if (!Is_Temp_Hilite(bl_p->hilite_rule))
1652 if (bl_p->time == 0 || bl_p->time >= augmented_time)
1658 /* called from moveloop(); sets context.botl if temp hilites have timed out */
1660 status_eval_next_unhilite()
1663 struct istat_s *curr;
1664 long next_unhilite, this_unhilite;
1666 bl_hilite_moves = moves; /* simpllfied; used to try to encode fractional
1667 * amounts for multiple moves within same turn */
1668 /* figure out whether an unhilight needs to be performed now */
1670 for (i = 0; i < MAXBLSTATS; ++i) {
1671 curr = &blstats[0][i]; /* blstats[0][*].time == blstats[1][*].time */
1674 struct istat_s *prev = &blstats[1][i];
1676 if (Is_Temp_Hilite(curr->hilite_rule))
1677 curr->time = prev->time = (bl_hilite_moves
1678 + iflags.hilite_delta);
1680 curr->time = prev->time = 0L;
1682 curr->chg = prev->chg = FALSE;
1683 context.botl = TRUE;
1686 continue; /* just process other blstats[][].time and .chg */
1688 this_unhilite = curr->time;
1689 if (this_unhilite > 0L
1690 && (next_unhilite == 0L || this_unhilite < next_unhilite)
1691 && hilite_reset_needed(curr, this_unhilite + 1L)) {
1692 next_unhilite = this_unhilite;
1693 if (next_unhilite < bl_hilite_moves)
1694 context.botl = TRUE;
1699 /* called by options handling when 'statushilites' value is changed */
1701 reset_status_hilites()
1703 if (iflags.hilite_delta) {
1706 for (i = 0; i < MAXBLSTATS; ++i)
1707 blstats[0][i].time = blstats[1][i].time = 0L;
1710 context.botlx = TRUE;
1713 /* test whether the text from a title rule matches the string for
1714 title-while-polymorphed in the 'textmatch' menu */
1716 noneoftheabove(hl_text)
1717 const char *hl_text;
1719 if (fuzzymatch(hl_text, "none of the above", "\" -_", TRUE)
1720 || fuzzymatch(hl_text, "(polymorphed)", "\"()", TRUE)
1721 || fuzzymatch(hl_text, "none of the above (polymorphed)",
1730 * Returns, based on the value and the direction it is moving,
1731 * the highlight rule that applies to the specified field.
1733 * Provide get_hilite() with the following to work with:
1735 * useful for BL_TH_VAL_ABSOLUTE
1736 * indicator of down, up, or the same (-1, 1, 0) chg
1737 * useful for BL_TH_UPDOWN or change detection
1738 * percentage (current value percentage of max value) pc
1739 * useful for BL_TH_VAL_PERCENTAGE
1742 * pointer to rule that applies; Null if no rule does.
1744 STATIC_OVL struct hilite_s *
1745 get_hilite(idx, fldidx, vp, chg, pc, colorptr)
1746 int idx, fldidx, chg, pc;
1750 struct hilite_s *hl, *rule = 0;
1751 anything *value = (anything *) vp;
1754 if (fldidx < 0 || fldidx >= MAXBLSTATS)
1755 return (struct hilite_s *) 0;
1757 if (has_hilite(fldidx)) {
1759 /* there are hilites set here */
1760 int max_pc = -1, min_pc = 101;
1761 /* LARGEST_INT isn't INT_MAX; it fits within 16 bits, but that
1762 value is big enough to handle all 'int' status fields */
1763 int max_ival = -LARGEST_INT, min_ival = LARGEST_INT;
1764 /* LONG_MAX comes from <limits.h> which might not be available for
1765 ancient configurations; we don't need LONG_MIN */
1766 long max_lval = -LONG_MAX, min_lval = LONG_MAX;
1767 boolean exactmatch = FALSE, updown = FALSE, changed = FALSE,
1768 perc_or_abs = FALSE;
1770 /* min_/max_ are used to track best fit */
1771 for (hl = blstats[0][fldidx].thresholds; hl; hl = hl->next) {
1772 dt = initblstats[fldidx].anytype; /* only needed for 'absolute' */
1773 /* if we've already matched a temporary highlight, it takes
1774 precedence over all persistent ones; we still process
1775 updown rules to get the last one which qualifies */
1776 if ((updown || changed) && hl->behavior != BL_TH_UPDOWN)
1778 /* among persistent highlights, if a 'percentage' or 'absolute'
1779 rule has been matched, it takes precedence over 'always' */
1780 if (perc_or_abs && hl->behavior == BL_TH_ALWAYS_HILITE)
1783 switch (hl->behavior) {
1784 case BL_TH_VAL_PERCENTAGE: /* percent values are always ANY_INT */
1785 if (hl->rel == EQ_VALUE && pc == hl->value.a_int) {
1787 min_pc = max_pc = hl->value.a_int;
1788 exactmatch = perc_or_abs = TRUE;
1789 } else if (exactmatch) {
1790 ; /* already found best fit, skip lt,ge,&c */
1791 } else if (hl->rel == LT_VALUE
1792 && (pc < hl->value.a_int)
1793 && (hl->value.a_int <= min_pc)) {
1795 min_pc = hl->value.a_int;
1797 } else if (hl->rel == LE_VALUE
1798 && (pc <= hl->value.a_int)
1799 && (hl->value.a_int <= min_pc)) {
1801 min_pc = hl->value.a_int;
1803 } else if (hl->rel == GT_VALUE
1804 && (pc > hl->value.a_int)
1805 && (hl->value.a_int >= max_pc)) {
1807 max_pc = hl->value.a_int;
1809 } else if (hl->rel == GE_VALUE
1810 && (pc >= hl->value.a_int)
1811 && (hl->value.a_int >= max_pc)) {
1813 max_pc = hl->value.a_int;
1817 case BL_TH_UPDOWN: /* uses 'chg' (set by caller), not 'dt' */
1818 /* specific 'up' or 'down' takes precedence over general
1819 'changed' regardless of their order in the rule set */
1820 if (chg < 0 && hl->rel == LT_VALUE) {
1823 } else if (chg > 0 && hl->rel == GT_VALUE) {
1826 } else if (chg != 0 && hl->rel == EQ_VALUE && !updown) {
1831 case BL_TH_VAL_ABSOLUTE: /* either ANY_INT or ANY_LONG */
1833 * The int and long variations here are identical aside from
1834 * union field and min_/max_ variable names. If you change
1835 * one, be sure to make a corresponding change in the other.
1837 if (dt == ANY_INT) {
1838 if (hl->rel == EQ_VALUE
1839 && hl->value.a_int == value->a_int) {
1841 min_ival = max_ival = hl->value.a_int;
1842 exactmatch = perc_or_abs = TRUE;
1843 } else if (exactmatch) {
1844 ; /* already found best fit, skip lt,ge,&c */
1845 } else if (hl->rel == LT_VALUE
1846 && (value->a_int < hl->value.a_int)
1847 && (hl->value.a_int <= min_ival)) {
1849 min_ival = hl->value.a_int;
1851 } else if (hl->rel == LE_VALUE
1852 && (value->a_int <= hl->value.a_int)
1853 && (hl->value.a_int <= min_ival)) {
1855 min_ival = hl->value.a_int;
1857 } else if (hl->rel == GT_VALUE
1858 && (value->a_int > hl->value.a_int)
1859 && (hl->value.a_int >= max_ival)) {
1861 max_ival = hl->value.a_int;
1863 } else if (hl->rel == GE_VALUE
1864 && (value->a_int >= hl->value.a_int)
1865 && (hl->value.a_int >= max_ival)) {
1867 max_ival = hl->value.a_int;
1870 } else { /* ANY_LONG */
1871 if (hl->rel == EQ_VALUE
1872 && hl->value.a_long == value->a_long) {
1874 min_lval = max_lval = hl->value.a_long;
1875 exactmatch = perc_or_abs = TRUE;
1876 } else if (exactmatch) {
1877 ; /* already found best fit, skip lt,ge,&c */
1878 } else if (hl->rel == LT_VALUE
1879 && (value->a_long < hl->value.a_long)
1880 && (hl->value.a_long <= min_lval)) {
1882 min_lval = hl->value.a_long;
1884 } else if (hl->rel == LE_VALUE
1885 && (value->a_long <= hl->value.a_long)
1886 && (hl->value.a_long <= min_lval)) {
1888 min_lval = hl->value.a_long;
1890 } else if (hl->rel == GT_VALUE
1891 && (value->a_long > hl->value.a_long)
1892 && (hl->value.a_long >= max_lval)) {
1894 max_lval = hl->value.a_long;
1896 } else if (hl->rel == GE_VALUE
1897 && (value->a_long >= hl->value.a_long)
1898 && (hl->value.a_long >= max_lval)) {
1900 max_lval = hl->value.a_long;
1905 case BL_TH_TEXTMATCH: /* ANY_STR */
1906 txtstr = blstats[idx][fldidx].val;
1907 if (fldidx == BL_TITLE)
1908 /* "<name> the <rank-title>", skip past "<name> the " */
1909 txtstr += (strlen(plname) + sizeof " the " - sizeof "");
1910 if (hl->rel == TXT_VALUE && hl->textmatch[0]) {
1911 if (fuzzymatch(hl->textmatch, txtstr, "\" -_", TRUE)) {
1914 } else if (exactmatch) {
1915 ; /* already found best fit, skip "noneoftheabove" */
1916 } else if (fldidx == BL_TITLE
1917 && Upolyd && noneoftheabove(hl->textmatch)) {
1922 case BL_TH_ALWAYS_HILITE:
1932 *colorptr = rule ? rule->coloridx : NO_COLOR;
1937 split_clridx(idx, coloridx, attrib)
1939 int *coloridx, *attrib;
1942 *coloridx = idx & 0x00FF;
1944 *attrib = (idx >> 8) & 0x00FF;
1948 * This is the parser for the hilite options.
1950 * parse_status_hl1() separates each hilite entry into
1951 * a set of field threshold/action component strings,
1952 * then calls parse_status_hl2() to parse further
1953 * and configure the hilite.
1956 parse_status_hl1(op, from_configfile)
1958 boolean from_configfile;
1960 #define MAX_THRESH 21
1961 char hsbuf[MAX_THRESH][QBUFSZ];
1962 boolean rslt, badopt = FALSE;
1963 int i, fldnum, ccount = 0;
1967 for (i = 0; i < MAX_THRESH; ++i) {
1970 while (*op && fldnum < MAX_THRESH && ccount < (QBUFSZ - 2)) {
1974 if (fldnum == 1 && strcmpi(hsbuf[0], "title") == 0) {
1975 /* spaces are allowed in title */
1976 hsbuf[fldnum][ccount++] = c;
1977 hsbuf[fldnum][ccount] = '\0';
1981 rslt = parse_status_hl2(hsbuf, from_configfile);
1987 for (i = 0; i < MAX_THRESH; ++i) {
1992 } else if (c == '/') {
1996 hsbuf[fldnum][ccount++] = c;
1997 hsbuf[fldnum][ccount] = '\0';
2001 if (fldnum >= 1 && !badopt) {
2002 rslt = parse_status_hl2(hsbuf, from_configfile);
2011 /* is str in the format of "[<>]?=?[-+]?[0-9]+%?" regex */
2013 is_ltgt_percentnumber(str)
2016 const char *s = str;
2018 if (*s == '<' || *s == '>')
2022 if (*s == '-' || *s == '+')
2030 return (*s == '\0');
2033 /* does str only contain "<>=-+0-9%" chars */
2035 has_ltgt_percentnumber(str)
2038 const char *s = str;
2041 if (!index("<>=-+0123456789%", *s))
2048 /* splitsubfields(): splits str in place into '+' or '&' separated strings.
2049 * returns number of strings, or -1 if more than maxsf or MAX_SUBFIELDS
2051 #define MAX_SUBFIELDS 16
2053 splitsubfields(str, sfarr, maxsf)
2058 static char *subfields[MAX_SUBFIELDS];
2059 char *st = (char *) 0;
2064 for (sf = 0; sf < MAX_SUBFIELDS; ++sf)
2065 subfields[sf] = (char *) 0;
2067 maxsf = (maxsf == 0) ? MAX_SUBFIELDS : min(maxsf, MAX_SUBFIELDS);
2069 if (index(str, '+') || index(str, '&')) {
2074 while (*c && sf < maxsf) {
2075 if (*c == '&' || *c == '+') {
2083 if (sf >= maxsf - 1)
2086 subfields[sf++] = st;
2094 #undef MAX_SUBFIELDS
2097 is_fld_arrayvalues(str, arr, arrmin, arrmax, retidx)
2099 const char *const *arr;
2105 for (i = arrmin; i < arrmax; i++)
2106 if (!strcmpi(str, arr[i])) {
2114 query_arrayvalue(querystr, arr, arrmin, arrmax)
2115 const char *querystr;
2116 const char *const *arr;
2119 int i, res, ret = arrmin - 1;
2122 menu_item *picks = (menu_item *) 0;
2123 int adj = (arrmin > 0) ? 1 : arrmax;
2125 tmpwin = create_nhwindow(NHW_MENU);
2128 for (i = arrmin; i < arrmax; i++) {
2130 any.a_int = i + adj;
2131 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
2132 arr[i], MENU_UNSELECTED);
2135 end_menu(tmpwin, querystr);
2137 res = select_menu(tmpwin, PICK_ONE, &picks);
2138 destroy_nhwindow(tmpwin);
2140 ret = picks->item.a_int - adj;
2141 free((genericptr_t) picks);
2148 status_hilite_add_threshold(fld, hilite)
2150 struct hilite_s *hilite;
2152 struct hilite_s *new_hilite;
2157 /* alloc and initialize a new hilite_s struct */
2158 new_hilite = (struct hilite_s *) alloc(sizeof (struct hilite_s));
2159 *new_hilite = *hilite; /* copy struct */
2161 new_hilite->set = TRUE;
2162 new_hilite->fld = fld;
2163 new_hilite->next = blstats[0][fld].thresholds;
2164 blstats[0][fld].thresholds = new_hilite;
2165 /* sort_hilites(fld) */
2167 /* current and prev must both point at the same hilites */
2168 blstats[1][fld].thresholds = blstats[0][fld].thresholds;
2173 parse_status_hl2(s, from_configfile)
2175 boolean from_configfile;
2178 int sidx = 0, i = -1, dt = -1;
2179 int coloridx = -1, successes = 0;
2180 int disp_attrib = 0;
2181 boolean percent, changed, numeric, down, up,
2182 gt, lt, ge, le, eq, txtval, always;
2184 enum statusfields fld = BL_FLUSH;
2185 struct hilite_s hilite;
2187 static const char *aligntxt[] = { "chaotic", "neutral", "lawful" };
2188 /* hu_stat[] from eat.c has trailing spaces which foul up comparisons */
2189 static const char *hutxt[] = { "Satiated", "", "Hungry", "Weak",
2190 "Fainting", "Fainted", "Starved" };
2194 OPTION=hilite_status: hitpoints/<10%/red
2195 OPTION=hilite_status: hitpoints/<10%/red/<5%/purple/1/red+blink+inverse
2196 OPTION=hilite_status: experience/down/red/up/green
2197 OPTION=hilite_status: cap/strained/yellow/overtaxed/orange
2198 OPTION=hilite_status: title/always/blue
2199 OPTION=hilite_status: title/blue
2202 /* field name to statusfield */
2203 fld = fldname_to_bl_indx(s[sidx]);
2205 if (fld == BL_CHARACTERISTICS) {
2206 boolean res = FALSE;
2208 /* recursively set each of strength, dexterity, constitution, &c */
2209 for (fld = BL_STR; fld <= BL_CH; fld++) {
2210 Strcpy(s[sidx], initblstats[fld].fldname);
2211 res = parse_status_hl2(s, from_configfile);
2217 if (fld == BL_FLUSH) {
2218 config_error_add("Unknown status field '%s'", s[sidx]);
2221 if (fld == BL_CONDITION)
2222 return parse_condition(s, sidx);
2226 char buf[BUFSZ], **subfields;
2227 int sf = 0; /* subfield count */
2230 txt = (const char *)0;
2231 percent = numeric = always = FALSE;
2232 down = up = changed = FALSE;
2233 gt = ge = eq = le = lt = txtval = FALSE;
2235 /* threshold value */
2239 memset((genericptr_t) &hilite, 0, sizeof (struct hilite_s));
2240 hilite.set = FALSE; /* mark it "unset" */
2243 if (*s[sidx + 1] == '\0' || !strcmpi(s[sidx], "always")) {
2244 /* "field/always/color" OR "field/color" */
2246 if (*s[sidx + 1] == '\0')
2248 } else if (!strcmpi(s[sidx], "up") || !strcmpi(s[sidx], "down")) {
2249 if (initblstats[fld].anytype == ANY_STR)
2250 /* ordered string comparison is supported but LT/GT for
2251 the string fields (title, dungeon-level, alignment)
2252 is pointless; treat 'up' or 'down' for string fields
2253 as 'changed' rather than rejecting them outright */
2255 else if (!strcmpi(s[sidx], "down"))
2260 } else if (fld == BL_CAP
2262 && is_fld_arrayvalues(s[sidx], enc_stat,
2263 SLT_ENCUMBER, OVERLOADED + 1,
2265 txt = enc_stat[kidx];
2267 && is_fld_arrayvalues(s[sidx], enc_stat_opt,
2268 SLT_ENCUMBER, OVERLOADED + 1,
2270 txt = enc_stat_opt[kidx];
2273 } else if (fld == BL_ALIGN
2274 && is_fld_arrayvalues(s[sidx], aligntxt, 0, 3, &kidx)) {
2275 txt = aligntxt[kidx];
2277 } else if (fld == BL_HUNGER
2278 && is_fld_arrayvalues(s[sidx], hutxt,
2279 SATIATED, STARVED + 1, &kidx)) {
2281 txt = hu_stat[kidx]; /* store hu_stat[] val, not hutxt[] */
2283 /*JP hu_stat
\82Í
\96|
\96ó
\82³
\82ê
\82Ä
\82¢
\82é
\82Ì
\82Åhutxt
\82ð
\8eg
\82¤ */
2287 } else if (!strcmpi(s[sidx], "changed")) {
2289 } else if (is_ltgt_percentnumber(s[sidx])) {
2292 tmp = s[sidx]; /* is_ltgt_() guarantees [<>]?=?[-+]?[0-9]+%? */
2293 if (strchr(tmp, '%'))
2300 } else if (*tmp == '>') {
2306 /* '%', '<', '>' have served their purpose, '=' is either
2307 part of '<' or '>' or optional for '=N', unary '+' is
2308 just decorative, so get rid of them, leaving -?[0-9]+ */
2309 tmp = stripchars(tmpbuf, "%<>=+", tmp);
2311 dt = percent ? ANY_INT : initblstats[fld].anytype;
2312 (void) s_to_anything(&hilite.value, tmp, dt);
2314 op = gt ? ">" : ge ? ">=" : lt ? "<" : le ? "<=" : "=";
2316 /* AC is the only field where negative values make sense but
2317 accept >-1 for other fields; reject <0 for non-AC */
2318 && (hilite.value.a_int
2319 < ((fld == BL_AC) ? -128 : gt ? -1 : lt ? 1 : 0)
2320 /* percentages have another more comprehensive check below */
2321 || hilite.value.a_int > (percent ? (lt ? 101 : 100)
2323 config_error_add("%s'%s%d%s'%s", threshold_value,
2324 op, hilite.value.a_int, percent ? "%" : "",
2327 } else if (dt == ANY_LONG
2328 && (hilite.value.a_long < (gt ? -1L : lt ? 1L : 0L))) {
2329 config_error_add("%s'%s%ld'%s", threshold_value,
2330 op, hilite.value.a_long, is_out_of_range);
2333 } else if (initblstats[fld].anytype == ANY_STR) {
2337 config_error_add(has_ltgt_percentnumber(s[sidx])
2338 ? "Wrong format '%s', expected a threshold number or percent"
2339 : "Unknown behavior '%s'",
2344 /* relationships {LT_VALUE, LE_VALUE, EQ_VALUE, GE_VALUE, GT_VALUE} */
2346 hilite.rel = GT_VALUE;
2347 else if (lt || down)
2348 hilite.rel = LT_VALUE;
2350 hilite.rel = GE_VALUE;
2352 hilite.rel = LE_VALUE;
2353 else if (eq || percent || numeric || changed)
2354 hilite.rel = EQ_VALUE;
2356 hilite.rel = TXT_VALUE;
2358 hilite.rel = LT_VALUE;
2360 if (initblstats[fld].anytype == ANY_STR && (percent || numeric)) {
2361 config_error_add("Field '%s' does not support numeric values",
2362 initblstats[fld].fldname);
2367 if (initblstats[fld].idxmax < 0) {
2368 config_error_add("Cannot use percent with '%s'",
2369 initblstats[fld].fldname);
2371 } else if ((hilite.value.a_int < -1)
2372 || (hilite.value.a_int == -1
2373 && hilite.value.a_int != GT_VALUE)
2374 || (hilite.value.a_int == 0
2375 && hilite.rel == LT_VALUE)
2376 || (hilite.value.a_int == 100
2377 && hilite.rel == GT_VALUE)
2378 || (hilite.value.a_int == 101
2379 && hilite.value.a_int != LT_VALUE)
2380 || (hilite.value.a_int > 101)) {
2382 "hilite_status: invalid percentage value '%s%d%%'",
2383 (hilite.rel == LT_VALUE) ? "<"
2384 : (hilite.rel == LE_VALUE) ? "<="
2385 : (hilite.rel == GT_VALUE) ? ">"
2386 : (hilite.rel == GE_VALUE) ? ">="
2388 hilite.value.a_int);
2402 sf = splitsubfields(buf, &subfields, 0);
2407 disp_attrib = HL_UNDEF;
2409 for (i = 0; i < sf; ++i) {
2410 int a = match_str2attr(subfields[i], FALSE);
2413 disp_attrib |= HL_DIM;
2414 else if (a == ATR_BLINK)
2415 disp_attrib |= HL_BLINK;
2416 else if (a == ATR_ULINE)
2417 disp_attrib |= HL_ULINE;
2418 else if (a == ATR_INVERSE)
2419 disp_attrib |= HL_INVERSE;
2420 else if (a == ATR_BOLD)
2421 disp_attrib |= HL_BOLD;
2422 else if (a == ATR_NONE)
2423 disp_attrib = HL_NONE;
2425 int c = match_str2clr(subfields[i]);
2427 if (c >= CLR_MAX || coloridx != -1)
2433 coloridx = NO_COLOR;
2435 /* Assign the values */
2436 hilite.coloridx = coloridx | (disp_attrib << 8);
2439 hilite.behavior = BL_TH_ALWAYS_HILITE;
2441 hilite.behavior = BL_TH_VAL_PERCENTAGE;
2443 hilite.behavior = BL_TH_UPDOWN;
2445 hilite.behavior = BL_TH_VAL_ABSOLUTE;
2447 hilite.behavior = BL_TH_TEXTMATCH;
2448 else if (hilite.value.a_void)
2449 hilite.behavior = BL_TH_VAL_ABSOLUTE;
2451 hilite.behavior = BL_TH_NONE;
2453 hilite.anytype = dt;
2455 if (hilite.behavior == BL_TH_TEXTMATCH && txt) {
2456 (void) strncpy(hilite.textmatch, txt, sizeof hilite.textmatch);
2457 hilite.textmatch[sizeof hilite.textmatch - 1] = '\0';
2458 (void) trimspaces(hilite.textmatch);
2461 status_hilite_add_threshold(fld, &hilite);
2469 #endif /* STATUS_HILITES */
2471 const struct condmap valid_conditions[] = {
2472 { "stone", BL_MASK_STONE },
2473 { "slime", BL_MASK_SLIME },
2474 { "strngl", BL_MASK_STRNGL },
2475 { "foodPois", BL_MASK_FOODPOIS },
2476 { "termIll", BL_MASK_TERMILL },
2477 { "blind", BL_MASK_BLIND },
2478 { "deaf", BL_MASK_DEAF },
2479 { "stun", BL_MASK_STUN },
2480 { "conf", BL_MASK_CONF },
2481 { "hallu", BL_MASK_HALLU },
2482 { "lev", BL_MASK_LEV },
2483 { "fly", BL_MASK_FLY },
2484 { "ride", BL_MASK_RIDE },
2487 #ifdef STATUS_HILITES
2489 const struct condmap condition_aliases[] = {
2490 { "strangled", BL_MASK_STRNGL },
2491 { "all", BL_MASK_STONE | BL_MASK_SLIME | BL_MASK_STRNGL
2492 | BL_MASK_FOODPOIS | BL_MASK_TERMILL
2493 | BL_MASK_BLIND | BL_MASK_DEAF | BL_MASK_STUN
2494 | BL_MASK_CONF | BL_MASK_HALLU
2495 | BL_MASK_LEV | BL_MASK_FLY | BL_MASK_RIDE },
2496 { "major_troubles", BL_MASK_STONE | BL_MASK_SLIME | BL_MASK_STRNGL
2497 | BL_MASK_FOODPOIS | BL_MASK_TERMILL },
2498 { "minor_troubles", BL_MASK_BLIND | BL_MASK_DEAF | BL_MASK_STUN
2499 | BL_MASK_CONF | BL_MASK_HALLU },
2500 { "movement", BL_MASK_LEV | BL_MASK_FLY | BL_MASK_RIDE }
2507 unsigned long ret = 0UL;
2510 menu_item *picks = (menu_item *) 0;
2512 tmpwin = create_nhwindow(NHW_MENU);
2515 for (i = 0; i < SIZE(valid_conditions); i++) {
2517 any.a_ulong = valid_conditions[i].bitmask;
2518 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
2519 valid_conditions[i].id, MENU_UNSELECTED);
2522 end_menu(tmpwin, "Choose status conditions");
2524 res = select_menu(tmpwin, PICK_ANY, &picks);
2525 destroy_nhwindow(tmpwin);
2527 for (i = 0; i < res; i++)
2528 ret |= picks[i].item.a_ulong;
2529 free((genericptr_t) picks);
2535 conditionbitmask2str(ul)
2538 static char buf[BUFSZ];
2540 boolean first = TRUE;
2541 const char *alias = (char *) 0;
2548 for (i = 1; i < SIZE(condition_aliases); i++)
2549 if (condition_aliases[i].bitmask == ul)
2550 alias = condition_aliases[i].id;
2552 for (i = 0; i < SIZE(valid_conditions); i++)
2553 if ((valid_conditions[i].bitmask & ul) != 0UL) {
2554 Sprintf(eos(buf), "%s%s", (first) ? "" : "+",
2555 valid_conditions[i].id);
2559 if (!first && alias)
2560 Sprintf(buf, "%s", alias);
2565 STATIC_OVL unsigned long
2566 match_str2conditionbitmask(str)
2569 int i, nmatches = 0;
2570 unsigned long mask = 0UL;
2573 /* check matches to canonical names */
2574 for (i = 0; i < SIZE(valid_conditions); i++)
2575 if (fuzzymatch(valid_conditions[i].id, str, " -_", TRUE)) {
2576 mask |= valid_conditions[i].bitmask;
2582 for (i = 0; i < SIZE(condition_aliases); i++)
2583 if (fuzzymatch(condition_aliases[i].id, str, " -_", TRUE)) {
2584 mask |= condition_aliases[i].bitmask;
2590 /* check partial matches to aliases */
2591 int len = (int) strlen(str);
2593 for (i = 0; i < SIZE(condition_aliases); i++)
2594 if (!strncmpi(str, condition_aliases[i].id, len)) {
2595 mask |= condition_aliases[i].bitmask;
2604 STATIC_OVL unsigned long
2605 str2conditionbitmask(str)
2608 unsigned long conditions_bitmask = 0UL;
2612 sf = splitsubfields(str, &subfields, SIZE(valid_conditions));
2617 for (i = 0; i < sf; ++i) {
2618 unsigned long bm = match_str2conditionbitmask(subfields[i]);
2621 config_error_add("Unknown condition '%s'", subfields[i]);
2624 conditions_bitmask |= bm;
2626 return conditions_bitmask;
2630 parse_condition(s, sidx)
2635 int coloridx = NO_COLOR;
2637 unsigned long conditions_bitmask = 0UL;
2638 boolean success = FALSE;
2644 OPTION=hilite_status: condition/stone+slime+foodPois/red&inverse */
2648 * It would be simpler to treat each condition (also hunger state
2649 * and encumbrance level) as if it were a separate field. That
2650 * way they could have either or both 'changed' temporary rule and
2651 * 'always' persistent rule and wouldn't need convoluted access to
2652 * the intended color and attributes.
2657 int sf = 0; /* subfield count */
2658 char buf[BUFSZ], **subfields;
2663 config_error_add("Missing condition(s)");
2668 conditions_bitmask = str2conditionbitmask(buf);
2670 if (!conditions_bitmask)
2674 * We have the conditions_bitmask with bits set for
2675 * each ailment we want in a particular color and/or
2676 * attribute, but we need to assign it to an array of
2677 * bitmasks indexed by the color chosen
2678 * (0 to (CLR_MAX - 1))
2679 * and/or attributes chosen
2680 * (HL_ATTCLR_DIM to (BL_ATTCLR_MAX - 1))
2681 * We still have to parse the colors and attributes out.
2687 if (!how || !*how) {
2688 config_error_add("Missing color+attribute");
2693 sf = splitsubfields(buf, &subfields, 0);
2696 * conditions_bitmask now has bits set representing
2697 * the conditions that player wants represented, but
2698 * now we parse out *how* they will be represented.
2700 * Only 1 colour is allowed, but potentially multiple
2701 * attributes are allowed.
2703 * We have the following additional array offsets to
2704 * use for storing the attributes beyond the end of
2705 * the color indexes, all of which are less than CLR_MAX.
2706 * HL_ATTCLR_DIM = CLR_MAX
2707 * HL_ATTCLR_BLINK = CLR_MAX + 1
2708 * HL_ATTCLR_ULINE = CLR_MAX + 2
2709 * HL_ATTCLR_INVERSE = CLR_MAX + 3
2710 * HL_ATTCLR_BOLD = CLR_MAX + 4
2711 * HL_ATTCLR_MAX = CLR_MAX + 5 (this is past array boundary)
2715 for (i = 0; i < sf; ++i) {
2716 int a = match_str2attr(subfields[i], FALSE);
2719 cond_hilites[HL_ATTCLR_DIM] |= conditions_bitmask;
2720 else if (a == ATR_BLINK)
2721 cond_hilites[HL_ATTCLR_BLINK] |= conditions_bitmask;
2722 else if (a == ATR_ULINE)
2723 cond_hilites[HL_ATTCLR_ULINE] |= conditions_bitmask;
2724 else if (a == ATR_INVERSE)
2725 cond_hilites[HL_ATTCLR_INVERSE] |= conditions_bitmask;
2726 else if (a == ATR_BOLD)
2727 cond_hilites[HL_ATTCLR_BOLD] |= conditions_bitmask;
2728 else if (a == ATR_NONE) {
2729 cond_hilites[HL_ATTCLR_DIM] &= ~conditions_bitmask;
2730 cond_hilites[HL_ATTCLR_BLINK] &= ~conditions_bitmask;
2731 cond_hilites[HL_ATTCLR_ULINE] &= ~conditions_bitmask;
2732 cond_hilites[HL_ATTCLR_INVERSE] &= ~conditions_bitmask;
2733 cond_hilites[HL_ATTCLR_BOLD] &= ~conditions_bitmask;
2735 int k = match_str2clr(subfields[i]);
2742 /* set the bits in the appropriate member of the
2743 condition array according to color chosen as index */
2745 cond_hilites[coloridx] |= conditions_bitmask;
2753 clear_status_hilites()
2757 for (i = 0; i < MAXBLSTATS; ++i) {
2758 struct hilite_s *temp, *next;
2760 for (temp = blstats[0][i].thresholds; temp; temp = next) {
2764 blstats[0][i].thresholds = blstats[1][i].thresholds = 0;
2765 /* pointer into thresholds list, now stale */
2766 blstats[0][i].hilite_rule = blstats[1][i].hilite_rule = 0;
2771 hlattr2attrname(attrib, buf, bufsz)
2775 if (attrib && buf) {
2780 if (attrib == HL_NONE) {
2781 Strcpy(buf, "normal");
2785 if (attrib & HL_BOLD)
2786 Strcat(attbuf, first++ ? "+bold" : "bold");
2787 if (attrib & HL_INVERSE)
2788 Strcat(attbuf, first++ ? "+inverse" : "inverse");
2789 if (attrib & HL_ULINE)
2790 Strcat(attbuf, first++ ? "+underline" : "underline");
2791 if (attrib & HL_BLINK)
2792 Strcat(attbuf, first++ ? "+blink" : "blink");
2793 if (attrib & HL_DIM)
2794 Strcat(attbuf, first++ ? "+dim" : "dim");
2797 if (k < (bufsz - 1))
2798 Strcpy(buf, attbuf);
2805 struct _status_hilite_line_str {
2808 struct hilite_s *hl;
2811 struct _status_hilite_line_str *next;
2814 static struct _status_hilite_line_str *status_hilite_str = 0;
2815 static int status_hilite_str_id = 0;
2818 status_hilite_linestr_add(fld, hl, mask, str)
2820 struct hilite_s *hl;
2824 struct _status_hilite_line_str *tmp, *nxt;
2826 tmp = (struct _status_hilite_line_str *) alloc(sizeof *tmp);
2827 (void) memset(tmp, 0, sizeof *tmp);
2828 tmp->next = (struct _status_hilite_line_str *) 0;
2830 tmp->id = ++status_hilite_str_id;
2834 if (fld == BL_TITLE)
2835 Strcpy(tmp->str, str);
2837 (void) stripchars(tmp->str, " ", str);
2839 if ((nxt = status_hilite_str) != 0) {
2844 status_hilite_str = tmp;
2849 status_hilite_linestr_done()
2851 struct _status_hilite_line_str *nxt, *tmp = status_hilite_str;
2858 status_hilite_str = (struct _status_hilite_line_str *) 0;
2859 status_hilite_str_id = 0;
2863 status_hilite_linestr_countfield(fld)
2866 struct _status_hilite_line_str *tmp;
2867 boolean countall = (fld == BL_FLUSH);
2870 for (tmp = status_hilite_str; tmp; tmp = tmp->next) {
2871 if (countall || tmp->fld == fld)
2877 /* used by options handling, doset(options.c) */
2879 count_status_hilites(VOID_ARGS)
2883 status_hilite_linestr_gather();
2884 count = status_hilite_linestr_countfield(BL_FLUSH);
2885 status_hilite_linestr_done();
2890 status_hilite_linestr_gather_conditions()
2895 unsigned long clratr;
2896 } cond_maps[SIZE(valid_conditions)];
2898 (void) memset(cond_maps, 0,
2899 SIZE(valid_conditions) * sizeof (struct _cond_map));
2901 for (i = 0; i < SIZE(valid_conditions); i++) {
2906 for (j = 0; j < CLR_MAX; j++)
2907 if (cond_hilites[j] & valid_conditions[i].bitmask) {
2911 if (cond_hilites[HL_ATTCLR_DIM] & valid_conditions[i].bitmask)
2913 if (cond_hilites[HL_ATTCLR_BOLD] & valid_conditions[i].bitmask)
2915 if (cond_hilites[HL_ATTCLR_BLINK] & valid_conditions[i].bitmask)
2917 if (cond_hilites[HL_ATTCLR_ULINE] & valid_conditions[i].bitmask)
2919 if (cond_hilites[HL_ATTCLR_INVERSE] & valid_conditions[i].bitmask)
2924 if (clr != NO_COLOR || atr != HL_NONE) {
2925 unsigned long ca = clr | (atr << 8);
2926 boolean added_condmap = FALSE;
2928 for (j = 0; j < SIZE(valid_conditions); j++)
2929 if (cond_maps[j].clratr == ca) {
2930 cond_maps[j].bm |= valid_conditions[i].bitmask;
2931 added_condmap = TRUE;
2934 if (!added_condmap) {
2935 for (j = 0; j < SIZE(valid_conditions); j++)
2936 if (!cond_maps[j].bm) {
2937 cond_maps[j].bm = valid_conditions[i].bitmask;
2938 cond_maps[j].clratr = ca;
2945 for (i = 0; i < SIZE(valid_conditions); i++)
2946 if (cond_maps[i].bm) {
2947 int clr = NO_COLOR, atr = HL_NONE;
2949 split_clridx(cond_maps[i].clratr, &clr, &atr);
2950 if (clr != NO_COLOR || atr != HL_NONE) {
2952 char attrbuf[BUFSZ];
2953 char condbuf[BUFSZ];
2956 (void) strNsubst(strcpy(clrbuf, clr2colorname(clr)),
2958 tmpattr = hlattr2attrname(atr, attrbuf, BUFSZ);
2960 Sprintf(eos(clrbuf), "&%s", tmpattr);
2961 Sprintf(condbuf, "condition/%s/%s",
2962 conditionbitmask2str(cond_maps[i].bm), clrbuf);
2963 status_hilite_linestr_add(BL_CONDITION, 0,
2964 cond_maps[i].bm, condbuf);
2970 status_hilite_linestr_gather()
2973 struct hilite_s *hl;
2975 status_hilite_linestr_done();
2977 for (i = 0; i < MAXBLSTATS; i++) {
2978 hl = blstats[0][i].thresholds;
2980 status_hilite_linestr_add(i, hl, 0UL, status_hilite2str(hl));
2985 status_hilite_linestr_gather_conditions();
2990 status_hilite2str(hl)
2991 struct hilite_s *hl;
2993 static char buf[BUFSZ];
2994 int clr = 0, attr = 0;
2995 char behavebuf[BUFSZ];
2997 char attrbuf[BUFSZ];
3004 behavebuf[0] = '\0';
3006 op = (hl->rel == LT_VALUE) ? "<"
3007 : (hl->rel == LE_VALUE) ? "<="
3008 : (hl->rel == GT_VALUE) ? ">"
3009 : (hl->rel == GE_VALUE) ? ">="
3010 : (hl->rel == EQ_VALUE) ? "="
3013 switch (hl->behavior) {
3014 case BL_TH_VAL_PERCENTAGE:
3016 Sprintf(behavebuf, "%s%d%%", op, hl->value.a_int);
3018 impossible("hl->behavior=percentage, rel error");
3021 if (hl->rel == LT_VALUE)
3022 Sprintf(behavebuf, "down");
3023 else if (hl->rel == GT_VALUE)
3024 Sprintf(behavebuf, "up");
3025 else if (hl->rel == EQ_VALUE)
3026 Sprintf(behavebuf, "changed");
3028 impossible("hl->behavior=updown, rel error");
3030 case BL_TH_VAL_ABSOLUTE:
3032 Sprintf(behavebuf, "%s%d", op, hl->value.a_int);
3034 impossible("hl->behavior=absolute, rel error");
3036 case BL_TH_TEXTMATCH:
3037 if (hl->rel == TXT_VALUE && hl->textmatch[0])
3038 Sprintf(behavebuf, "%s", hl->textmatch);
3040 impossible("hl->behavior=textmatch, rel or textmatch error");
3042 case BL_TH_CONDITION:
3043 if (hl->rel == EQ_VALUE)
3044 Sprintf(behavebuf, "%s", conditionbitmask2str(hl->value.a_ulong));
3046 impossible("hl->behavior=condition, rel error");
3048 case BL_TH_ALWAYS_HILITE:
3049 Sprintf(behavebuf, "always");
3057 split_clridx(hl->coloridx, &clr, &attr);
3058 (void) strNsubst(strcpy(clrbuf, clr2colorname(clr)), " ", "-", 0);
3059 if (attr != HL_UNDEF) {
3060 if ((tmpattr = hlattr2attrname(attr, attrbuf, BUFSZ)) != 0)
3061 Sprintf(eos(clrbuf), "&%s", tmpattr);
3063 Sprintf(buf, "%s/%s/%s", initblstats[hl->fld].fldname, behavebuf, clrbuf);
3069 status_hilite_menu_choose_field()
3072 int i, res, fld = BL_FLUSH;
3074 menu_item *picks = (menu_item *) 0;
3076 tmpwin = create_nhwindow(NHW_MENU);
3079 for (i = 0; i < MAXBLSTATS; i++) {
3080 #ifndef SCORE_ON_BOTL
3081 if (initblstats[i].fld == BL_SCORE
3082 && !blstats[0][BL_SCORE].thresholds)
3086 any.a_int = (i + 1);
3087 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
3088 initblstats[i].fldname, MENU_UNSELECTED);
3091 end_menu(tmpwin, "Select a hilite field:");
3093 res = select_menu(tmpwin, PICK_ONE, &picks);
3094 destroy_nhwindow(tmpwin);
3096 fld = picks->item.a_int - 1;
3097 free((genericptr_t) picks);
3103 status_hilite_menu_choose_behavior(fld)
3107 int res = 0, beh = BL_TH_NONE-1;
3109 menu_item *picks = (menu_item *) 0;
3112 int onlybeh = BL_TH_NONE, nopts = 0;
3114 if (fld < 0 || fld >= MAXBLSTATS)
3117 at = initblstats[fld].anytype;
3119 tmpwin = create_nhwindow(NHW_MENU);
3122 if (fld != BL_CONDITION) {
3124 any.a_int = onlybeh = BL_TH_ALWAYS_HILITE;
3126 Sprintf(buf, "Always highlight %s", initblstats[fld].fldname);
3128 Sprintf(buf, "%s
\82ð
\8fí
\82É
\83n
\83C
\83\89\83C
\83g", initblstats[fld].fldname);
3129 add_menu(tmpwin, NO_GLYPH, &any, 'a', 0, ATR_NONE,
3130 buf, MENU_UNSELECTED);
3134 if (fld == BL_CONDITION) {
3136 any.a_int = onlybeh = BL_TH_CONDITION;
3137 add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, ATR_NONE,
3139 "Bitmask of conditions", MENU_UNSELECTED);
3141 "
\8fð
\8c\8f\82Ì
\83r
\83b
\83g
\83}
\83X
\83N", MENU_UNSELECTED);
3145 if (fld != BL_CONDITION) {
3147 any.a_int = onlybeh = BL_TH_UPDOWN;
3149 Sprintf(buf, "%s value changes", initblstats[fld].fldname);
3151 Sprintf(buf, "%s
\82Ì
\92l
\82Ì
\95Ï
\8dX", initblstats[fld].fldname);
3152 add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE,
3153 buf, MENU_UNSELECTED);
3157 if (fld != BL_CAP && fld != BL_HUNGER
3158 && (at == ANY_INT || at == ANY_LONG)) {
3160 any.a_int = onlybeh = BL_TH_VAL_ABSOLUTE;
3161 add_menu(tmpwin, NO_GLYPH, &any, 'n', 0, ATR_NONE,
3163 "Number threshold", MENU_UNSELECTED);
3165 "
\90\94\92l
\82Ìè
\87\92l", MENU_UNSELECTED);
3169 if (initblstats[fld].idxmax >= 0) {
3171 any.a_int = onlybeh = BL_TH_VAL_PERCENTAGE;
3172 add_menu(tmpwin, NO_GLYPH, &any, 'p', 0, ATR_NONE,
3174 "Percentage threshold", MENU_UNSELECTED);
3176 "
\8a\84\8d\87\82Ìè
\87\92l", MENU_UNSELECTED);
3180 if (initblstats[fld].anytype == ANY_STR
3181 || fld == BL_CAP || fld == BL_HUNGER) {
3183 any.a_int = onlybeh = BL_TH_TEXTMATCH;
3185 Sprintf(buf, "%s text match", initblstats[fld].fldname);
3187 Sprintf(buf, "%s
\82Ì
\83e
\83L
\83X
\83g
\82Ì
\83}
\83b
\83`
\83\93\83O", initblstats[fld].fldname);
3188 add_menu(tmpwin, NO_GLYPH, &any, 't', 0, ATR_NONE,
3189 buf, MENU_UNSELECTED);
3194 Sprintf(buf, "Select %s field hilite behavior:", initblstats[fld].fldname);
3196 Sprintf(buf, "%s
\82Ì
\83n
\83C
\83\89\83C
\83g
\82Ì
\90U
\82é
\95\91\82¢
\82ð
\91I
\91ð:", initblstats[fld].fldname);
3197 end_menu(tmpwin, buf);
3200 res = select_menu(tmpwin, PICK_ONE, &picks);
3201 if (res == 0) /* none chosen*/
3203 else if (res == -1) /* menu cancelled */
3204 beh = (BL_TH_NONE - 1);
3205 } else if (onlybeh != BL_TH_NONE)
3207 destroy_nhwindow(tmpwin);
3209 beh = picks->item.a_int;
3210 free((genericptr_t) picks);
3216 status_hilite_menu_choose_updownboth(fld, str, ltok, gtok)
3221 int res, ret = NO_LTEQGT;
3225 menu_item *picks = (menu_item *) 0;
3227 tmpwin = create_nhwindow(NHW_MENU);
3233 Sprintf(buf, "%s than %s",
3234 (fld == BL_AC) ? "Better (lower)" : "Less", str);
3236 Sprintf(buf, "%s
\82æ
\82è%s",
3237 str, (fld == BL_AC) ? "
\97Ç
\82¢(
\8f¬
\82³
\82¢)" : "
\8f¬
\82³
\82¢");
3241 Sprintf(buf, "Value goes down");
3243 Sprintf(buf, "
\92l
\82Ì
\92á
\89º");
3245 any.a_int = 10 + LT_VALUE;
3246 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
3247 buf, MENU_UNSELECTED);
3251 Sprintf(buf, "%s or %s",
3252 str, (fld == BL_AC) ? "better (lower)" : "less");
3254 Sprintf(buf, "%s%s",
3255 str, (fld == BL_AC) ? "
\82æ
\82è
\97Ç
\82¢(
\88È
\89º)" : "
\88È
\89º");
3258 any.a_int = 10 + LE_VALUE;
3259 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
3260 buf, MENU_UNSELECTED);
3266 Sprintf(buf, "Exactly %s", str);
3268 Sprintf(buf, "
\82¿
\82å
\82¤
\82Ç%s", str);
3271 Sprintf(buf, "Value changes");
3273 Sprintf(buf, "
\92l
\82Ì
\95Ï
\8dX");
3275 any.a_int = 10 + EQ_VALUE;
3276 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
3277 buf, MENU_UNSELECTED);
3282 Sprintf(buf, "%s or %s",
3283 str, (fld == BL_AC) ? "worse (higher)" : "more");
3285 Sprintf(buf, "%s%s",
3286 str, (fld == BL_AC) ? "
\82æ
\82è
\88«
\82¢(
\88È
\8fã)" : "
\88È
\8fã");
3289 any.a_int = 10 + GE_VALUE;
3290 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
3291 buf, MENU_UNSELECTED);
3296 Sprintf(buf, "%s than %s",
3297 (fld == BL_AC) ? "Worse (higher)" : "More", str);
3299 Sprintf(buf, "%s
\82æ
\82è%s",
3300 str, (fld == BL_AC) ? "
\88«
\82¢(
\8d\82\82¢)" : "
\91å
\82«
\82¢");
3304 Sprintf(buf, "Value goes up");
3306 Sprintf(buf, "
\92l
\82Ì
\8fã
\8f¸");
3308 any.a_int = 10 + GT_VALUE;
3309 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
3310 buf, MENU_UNSELECTED);
3313 Sprintf(buf, "Select field %s value:", initblstats[fld].fldname);
3315 Sprintf(buf, "
\83t
\83B
\81[
\83\8b\83h%s
\82Ì
\92l:", initblstats[fld].fldname);
3316 end_menu(tmpwin, buf);
3318 res = select_menu(tmpwin, PICK_ONE, &picks);
3319 destroy_nhwindow(tmpwin);
3321 ret = picks->item.a_int - 10;
3322 free((genericptr_t) picks);
3329 status_hilite_menu_add(origfld)
3335 int clr = NO_COLOR, atr = HL_UNDEF;
3336 struct hilite_s hilite;
3337 unsigned long cond = 0UL;
3338 char colorqry[BUFSZ];
3339 char attrqry[BUFSZ];
3343 if (fld == BL_FLUSH) {
3344 fld = status_hilite_menu_choose_field();
3345 /* isn't this redundant given what follows? */
3346 if (fld == BL_FLUSH)
3350 if (fld == BL_FLUSH)
3356 memset((genericptr_t) &hilite, 0, sizeof (struct hilite_s));
3357 hilite.next = (struct hilite_s *) 0;
3358 hilite.set = FALSE; /* mark it "unset" */
3362 behavior = status_hilite_menu_choose_behavior(fld);
3364 if (behavior == (BL_TH_NONE - 1)) {
3366 } else if (behavior == BL_TH_NONE) {
3367 if (origfld == BL_FLUSH)
3372 hilite.behavior = behavior;
3375 if (behavior == BL_TH_VAL_PERCENTAGE
3376 || behavior == BL_TH_VAL_ABSOLUTE) {
3377 char inbuf[BUFSZ], buf[BUFSZ];
3380 boolean gotnum = FALSE, percent = (behavior == BL_TH_VAL_PERCENTAGE);
3381 char *inp, *numstart;
3384 lt_gt_eq = NO_LTEQGT; /* not set up yet */
3387 Sprintf(buf, "Enter %svalue for %s threshold:",
3388 percent ? "percentage " : "",
3389 initblstats[fld].fldname);
3391 Sprintf(buf, "%s
\82Ì%s
\82Ìè
\87\92l
\82ð
\93ü
\97Í
\82µ
\82Ä
\82
\82¾
\82³
\82¢:",
3392 initblstats[fld].fldname,
3393 percent ? "
\8a\84\8d\87" : "
\92l");
3396 if (inbuf[0] == '\0' || inbuf[0] == '\033')
3397 goto choose_behavior;
3399 inp = numstart = trimspaces(inbuf);
3401 goto choose_behavior;
3403 /* allow user to enter "<50%" or ">50" or just "50"
3404 or <=50% or >=50 or =50 */
3405 if (*inp == '>' || *inp == '<' || *inp == '=') {
3406 lt_gt_eq = (*inp == '>') ? ((inp[1] == '=') ? GE_VALUE : GT_VALUE)
3407 : (*inp == '<') ? ((inp[1] == '=') ? LE_VALUE : LT_VALUE)
3411 if (lt_gt_eq == GE_VALUE || lt_gt_eq == LE_VALUE) {
3418 } else if (*inp == '+') {
3422 while (digit(*inp)) {
3429 pline("Not expecting a percentage.");
3431 pline("
\8a\84\8d\87\82ð
\91z
\92è
\82µ
\82Ä
\82¢
\82Ü
\82¹
\82ñ
\81D");
3432 goto choose_behavior;
3434 *inp = '\0'; /* strip '%' [this accepts trailing junk!] */
3436 /* some random characters */
3438 pline("\"%s\" is not a recognized number.", inp);
3440 pline("\"%s\"
\82Í
\90\94\92l
\82Æ
\82µ
\82Ä
\94F
\8e¯
\82Å
\82«
\82Ü
\82¹
\82ñ
\81D", inp);
3445 pline("Is that an invisible number?");
3447 pline("
\82±
\82ê
\82Í
\8c©
\82¦
\82È
\82¢
\90\94\8e\9a\81H");
3450 op = (lt_gt_eq == LT_VALUE) ? "<"
3451 : (lt_gt_eq == LE_VALUE) ? "<="
3452 : (lt_gt_eq == GT_VALUE) ? ">"
3453 : (lt_gt_eq == GE_VALUE) ? ">="
3454 : (lt_gt_eq == EQ_VALUE) ? "="
3455 : ""; /* didn't specify lt_gt_eq with number */
3458 dt = percent ? ANY_INT : initblstats[fld].anytype;
3459 (void) s_to_anything(&aval, numstart, dt);
3463 if (initblstats[fld].idxmax == -1) {
3465 pline("Field '%s' does not support percentage values.",
3467 pline("
\83t
\83B
\81[
\83\8b\83h'%s'
\82Í
\8a\84\8d\87\82Ì
\92l
\82É
\91Î
\89\9e\82µ
\82Ä
\82¢
\82Ü
\82¹
\82ñ
\81D",
3468 initblstats[fld].fldname);
3469 behavior = BL_TH_VAL_ABSOLUTE;
3472 /* if player only specified a number then lt_gt_eq isn't set
3473 up yet and the >-1 and <101 exceptions can't be honored;
3474 deliberate use of those should be uncommon enough for
3475 that to be palatable; for 0 and 100, choose_updown_both()
3476 will prevent useless operations */
3477 if ((val < 0 && (val != -1 || lt_gt_eq != GT_VALUE))
3478 || (val == 0 && lt_gt_eq == LT_VALUE)
3479 || (val == 100 && lt_gt_eq == GT_VALUE)
3480 || (val > 100 && (val != 101 || lt_gt_eq != LT_VALUE))) {
3482 pline("'%s%d%%' is not a valid percent value.", op, val);
3484 pline("'%s%d%%'
\82Í
\97L
\8cø
\82È
\8a\84\8d\87\82Ì
\92l
\82Å
\82Í
\82 \82è
\82Ü
\82¹
\82ñ
\81D", op, val);
3487 /* restore suffix for use in color and attribute prompts */
3488 if (!index(numstart, '%'))
3489 Strcat(numstart, "%");
3491 /* reject negative values except for AC and >-1; reject 0 for < */
3492 } else if (dt == ANY_INT
3493 && (aval.a_int < ((fld == BL_AC) ? -128
3494 : (lt_gt_eq == GT_VALUE) ? -1
3495 : (lt_gt_eq == LT_VALUE) ? 1 : 0))) {
3496 pline("%s'%s%d'%s", threshold_value,
3497 op, aval.a_int, is_out_of_range);
3499 } else if (dt == ANY_LONG
3500 && (aval.a_long < ((lt_gt_eq == GT_VALUE) ? -1L
3501 : (lt_gt_eq == LT_VALUE) ? 1L : 0L))) {
3502 pline("%s'%s%ld'%s", threshold_value,
3503 op, aval.a_long, is_out_of_range);
3507 if (lt_gt_eq == NO_LTEQGT) {
3508 boolean ltok = ((dt == ANY_INT)
3509 ? (aval.a_int > 0 || fld == BL_AC)
3510 : (aval.a_long > 0L)),
3511 gtok = (!percent || aval.a_long < 100);
3513 lt_gt_eq = status_hilite_menu_choose_updownboth(fld, inbuf,
3515 if (lt_gt_eq == NO_LTEQGT)
3520 Sprintf(colorqry, "Choose a color for when %s is %s%s%s:",
3521 initblstats[fld].fldname,
3522 (lt_gt_eq == LT_VALUE) ? "less than "
3523 : (lt_gt_eq == GT_VALUE) ? "more than "
3526 (lt_gt_eq == LE_VALUE) ? " or less"
3527 : (lt_gt_eq == GE_VALUE) ? " or more"
3530 Sprintf(colorqry, "%s
\82ª%s%s
\8fê
\8d\87\82Ì
\90F
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢:",
3531 initblstats[fld].fldname,
3533 (lt_gt_eq == LT_VALUE) ? "
\82æ
\82è
\8f¬
\82³
\82¢"
3534 : (lt_gt_eq == GT_VALUE) ? "
\82æ
\82è
\91å
\82«
\82¢"
3535 : (lt_gt_eq == LE_VALUE) ? "
\88È
\89º"
3536 : (lt_gt_eq == GE_VALUE) ? "
\88È
\8fã"
3540 Sprintf(attrqry, "Choose attribute for when %s is %s%s%s:",
3541 initblstats[fld].fldname,
3542 (lt_gt_eq == LT_VALUE) ? "less than "
3543 : (lt_gt_eq == GT_VALUE) ? "more than "
3546 (lt_gt_eq == LE_VALUE) ? " or less"
3547 : (lt_gt_eq == GE_VALUE) ? " or more"
3550 Sprintf(attrqry, "%s
\82ª%s%s
\8fê
\8d\87\82Ì
\91®
\90«
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢:",
3551 initblstats[fld].fldname,
3553 (lt_gt_eq == LT_VALUE) ? "
\82æ
\82è
\8f¬
\82³
\82¢"
3554 : (lt_gt_eq == GT_VALUE) ? "
\82æ
\82è
\91å
\82«
\82¢"
3555 : (lt_gt_eq == LE_VALUE) ? "
\88È
\89º"
3556 : (lt_gt_eq == GE_VALUE) ? "
\88È
\8fã"
3560 hilite.rel = lt_gt_eq;
3561 hilite.value = aval;
3562 } else if (behavior == BL_TH_UPDOWN) {
3563 if (initblstats[fld].anytype != ANY_STR) {
3564 boolean ltok = (fld != BL_TIME), gtok = TRUE;
3566 lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *)0,
3568 if (lt_gt_eq == NO_LTEQGT)
3569 goto choose_behavior;
3570 } else { /* ANY_STR */
3571 /* player picked '<field> value changes' in outer menu;
3572 ordered string comparison is supported but LT/GT for the
3573 string status fields (title, dungeon level, alignment)
3574 is pointless; rather than calling ..._choose_updownboth()
3575 with ltok==False plus gtok=False and having a menu with a
3576 single choice, skip it altogether and just use 'changed' */
3577 lt_gt_eq = EQ_VALUE;
3580 Sprintf(colorqry, "Choose a color for when %s %s:",
3581 initblstats[fld].fldname,
3582 (lt_gt_eq == EQ_VALUE) ? "changes"
3583 : (lt_gt_eq == LT_VALUE) ? "decreases"
3586 Sprintf(colorqry, "%s
\82ª%s
\82½
\82Æ
\82«
\82Ì
\90F
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢:",
3587 initblstats[fld].fldname,
3588 (lt_gt_eq == EQ_VALUE) ? "
\95Ï
\82í
\82Á"
3589 : (lt_gt_eq == LT_VALUE) ? "
\8c¸
\82Á"
3593 Sprintf(attrqry, "Choose attribute for when %s %s:",
3594 initblstats[fld].fldname,
3595 (lt_gt_eq == EQ_VALUE) ? "changes"
3596 : (lt_gt_eq == LT_VALUE) ? "decreases"
3599 Sprintf(attrqry, "%s
\82ª%s
\82½
\82Æ
\82«
\82Ì
\91®
\90«
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢:",
3600 initblstats[fld].fldname,
3601 (lt_gt_eq == EQ_VALUE) ? "
\95Ï
\82í
\82Á"
3602 : (lt_gt_eq == LT_VALUE) ? "
\8c¸
\82Á"
3605 hilite.rel = lt_gt_eq;
3606 } else if (behavior == BL_TH_CONDITION) {
3607 cond = query_conditions();
3609 if (origfld == BL_FLUSH)
3614 Sprintf(colorqry, "Choose a color for conditions %s:",
3616 Sprintf(colorqry, "
\8fð
\8c\8f%s
\82Ì
\82Æ
\82«
\82Ì
\90F
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢:",
3617 conditionbitmask2str(cond));
3619 Sprintf(attrqry, "Choose attribute for conditions %s:",
3621 Sprintf(attrqry, "
\8fð
\8c\8f%s
\82Ì
\82Æ
\82«
\82Ì
\91®
\90«
\82ð
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢:",
3622 conditionbitmask2str(cond));
3623 } else if (behavior == BL_TH_TEXTMATCH) {
3624 char qry_buf[BUFSZ];
3627 Sprintf(qry_buf, "%s %s text value to match:",
3631 || fld == BL_TITLE) ? "Choose" : "Enter",
3632 initblstats[fld].fldname);
3634 Sprintf(qry_buf, "%s
\82Ì
\83e
\83L
\83X
\83g
\92l
\82É
\83}
\83b
\83`
\83\93\83O
\82·
\82é
\92l
\82ð%s
\82
\82¾
\82³
\82¢:",
3635 initblstats[fld].fldname,
3639 || fld == BL_TITLE) ? "
\91I
\82ñ
\82Å" : "
\93ü
\97Í
\82µ
\82Ä");
3641 if (fld == BL_CAP) {
3643 int rv = query_arrayvalue(qry_buf,
3645 SLT_ENCUMBER, OVERLOADED + 1);
3647 int rv = query_arrayvalue(qry_buf,
3649 SLT_ENCUMBER, OVERLOADED + 1);
3652 if (rv < SLT_ENCUMBER)
3653 goto choose_behavior;
3655 hilite.rel = TXT_VALUE;
3656 Strcpy(hilite.textmatch, enc_stat[rv]);
3657 } else if (fld == BL_ALIGN) {
3658 static const char *aligntxt[] = { "chaotic", "neutral", "lawful" };
3659 int rv = query_arrayvalue(qry_buf,
3660 aligntxt, 0, 2 + 1);
3663 goto choose_behavior;
3665 hilite.rel = TXT_VALUE;
3666 Strcpy(hilite.textmatch, aligntxt[rv]);
3667 } else if (fld == BL_HUNGER) {
3668 static const char *hutxt[] = { "Satiated", (char *) 0, "Hungry",
3669 "Weak", "Fainting", "Fainted",
3671 int rv = query_arrayvalue(qry_buf,
3673 SATIATED, STARVED + 1);
3676 goto choose_behavior;
3678 hilite.rel = TXT_VALUE;
3679 Strcpy(hilite.textmatch, hutxt[rv]);
3680 } else if (fld == BL_TITLE) {
3681 const char *rolelist[3 * 9 + 1];
3682 char mbuf[MAXVALWIDTH], fbuf[MAXVALWIDTH], obuf[MAXVALWIDTH];
3685 for (i = j = 0; i < 9; i++) {
3686 Sprintf(mbuf, "\"%s\"", urole.rank[i].m);
3687 if (urole.rank[i].f) {
3688 Sprintf(fbuf, "\"%s\"", urole.rank[i].f);
3689 Sprintf(obuf, "%s or %s",
3690 flags.female ? fbuf : mbuf,
3691 flags.female ? mbuf : fbuf);
3693 fbuf[0] = obuf[0] = '\0';
3697 rolelist[j++] = dupstr(fbuf);
3698 rolelist[j++] = dupstr(mbuf);
3700 rolelist[j++] = dupstr(obuf);
3702 rolelist[j++] = dupstr(mbuf);
3704 rolelist[j++] = dupstr(fbuf);
3706 rolelist[j++] = dupstr(obuf);
3710 rolelist[j++] = dupstr("\"none of the above (polymorphed)\"");
3712 rolelist[j++] = dupstr("\"
\8fã
\82Ì
\82Ç
\82ê
\82Å
\82à
\82È
\82¢(
\95Ï
\89»
\92\86)\"");
3714 rv = query_arrayvalue(qry_buf, rolelist, 0, j);
3716 hilite.rel = TXT_VALUE;
3717 Strcpy(hilite.textmatch, rolelist[rv]);
3719 for (i = 0; i < j; i++)
3720 free((genericptr_t) rolelist[i]), rolelist[i] = 0;
3722 goto choose_behavior;
3727 getlin(qry_buf, inbuf);
3728 if (inbuf[0] == '\0' || inbuf[0] == '\033')
3729 goto choose_behavior;
3731 hilite.rel = TXT_VALUE;
3732 if (strlen(inbuf) < sizeof hilite.textmatch)
3733 Strcpy(hilite.textmatch, inbuf);
3738 Sprintf(colorqry, "Choose a color for when %s is '%s':",
3740 Sprintf(colorqry, "%s
\82ª'%s'
\82Ì
\8e\9e\82Ì
\90F
\82ð
\91I
\91ð:",
3741 initblstats[fld].fldname, hilite.textmatch);
3743 Sprintf(attrqry, "Choose attribute for when %s is '%s':",
3745 Sprintf(attrqry, "%s
\82ª'%s'
\82Ì
\8e\9e\82Ì
\91®
\90«
\82ð
\91I
\91ð:",
3746 initblstats[fld].fldname, hilite.textmatch);
3747 } else if (behavior == BL_TH_ALWAYS_HILITE) {
3749 Sprintf(colorqry, "Choose a color to always hilite %s:",
3751 Sprintf(colorqry, "
\8fí
\82É%s
\82ð
\83n
\83C
\83\89\83C
\83g
\82·
\82é
\90F
\82ð
\91I
\91ð:",
3752 initblstats[fld].fldname);
3754 Sprintf(attrqry, "Choose attribute to always hilite %s:",
3756 Sprintf(attrqry, "
\8fí
\82É%s
\82ð
\83n
\83C
\83\89\83C
\83g
\82·
\82é
\91®
\90«
\82ð
\91I
\91ð:",
3757 initblstats[fld].fldname);
3761 clr = query_color(colorqry);
3763 if (behavior != BL_TH_ALWAYS_HILITE)
3766 goto choose_behavior;
3768 atr = query_attr(attrqry);
3772 if (behavior == BL_TH_CONDITION) {
3774 char attrbuf[BUFSZ];
3778 cond_hilites[HL_ATTCLR_DIM] |= cond;
3780 cond_hilites[HL_ATTCLR_BLINK] |= cond;
3782 cond_hilites[HL_ATTCLR_ULINE] |= cond;
3783 if (atr & HL_INVERSE)
3784 cond_hilites[HL_ATTCLR_INVERSE] |= cond;
3786 cond_hilites[HL_ATTCLR_BOLD] |= cond;
3787 if (atr == HL_NONE) {
3788 cond_hilites[HL_ATTCLR_DIM] &= ~cond;
3789 cond_hilites[HL_ATTCLR_BLINK] &= ~cond;
3790 cond_hilites[HL_ATTCLR_ULINE] &= ~cond;
3791 cond_hilites[HL_ATTCLR_INVERSE] &= ~cond;
3792 cond_hilites[HL_ATTCLR_BOLD] &= ~cond;
3794 cond_hilites[clr] |= cond;
3795 (void) strNsubst(strcpy(clrbuf, clr2colorname(clr)), " ", "-", 0);
3796 tmpattr = hlattr2attrname(atr, attrbuf, BUFSZ);
3798 Sprintf(eos(clrbuf), "&%s", tmpattr);
3800 pline("Added hilite condition/%s/%s",
3802 pline("
\83n
\83C
\83\89\83C
\83g
\8fð
\8c\8f/%s/%s
\82ð
\92Ç
\89Á
\82µ
\82½
\81D",
3803 conditionbitmask2str(cond), clrbuf);
3807 hilite.coloridx = clr | (atr << 8);
3808 hilite.anytype = initblstats[fld].anytype;
3810 if (fld == BL_TITLE && (p = strstri(hilite.textmatch, " or ")) != 0) {
3811 /* split menu choice "male-rank or female-rank" into two distinct
3812 but otherwise identical rules, "male-rank" and "female-rank" */
3813 *p = '\0'; /* chop off " or female-rank" */
3814 /* new rule for male-rank */
3815 status_hilite_add_threshold(fld, &hilite);
3817 pline("Added hilite %s", status_hilite2str(&hilite));
3819 pline("
\83n
\83C
\83\89\83C
\83g%s
\82ð
\92Ç
\89Á
\82µ
\82½
\81D", status_hilite2str(&hilite));
3820 /* transfer female-rank to start of hilite.textmatch buffer */
3821 p += sizeof " or " - sizeof "";
3822 q = hilite.textmatch;
3823 while ((*q++ = *p++) != '\0')
3825 /* proceed with normal addition of new rule */
3827 status_hilite_add_threshold(fld, &hilite);
3829 pline("Added hilite %s", status_hilite2str(&hilite));
3831 pline("
\83n
\83C
\83\89\83C
\83g%s
\82ð
\92Ç
\89Á
\82µ
\82½
\81D", status_hilite2str(&hilite));
3833 reset_status_hilites();
3838 status_hilite_remove(id)
3841 struct _status_hilite_line_str *hlstr = status_hilite_str;
3843 while (hlstr && hlstr->id != id) {
3844 hlstr = hlstr->next;
3850 if (hlstr->fld == BL_CONDITION) {
3853 for (i = 0; i < CLR_MAX; i++)
3854 cond_hilites[i] &= ~hlstr->mask;
3855 cond_hilites[HL_ATTCLR_DIM] &= ~hlstr->mask;
3856 cond_hilites[HL_ATTCLR_BOLD] &= ~hlstr->mask;
3857 cond_hilites[HL_ATTCLR_BLINK] &= ~hlstr->mask;
3858 cond_hilites[HL_ATTCLR_ULINE] &= ~hlstr->mask;
3859 cond_hilites[HL_ATTCLR_INVERSE] &= ~hlstr->mask;
3862 int fld = hlstr->fld;
3863 struct hilite_s *hl, *hlprev = (struct hilite_s *) 0;
3865 for (hl = blstats[0][fld].thresholds; hl; hl = hl->next) {
3866 if (hlstr->hl == hl) {
3868 hlprev->next = hl->next;
3870 blstats[0][fld].thresholds = hl->next;
3871 blstats[1][fld].thresholds = blstats[0][fld].thresholds;
3873 if (blstats[0][fld].hilite_rule == hl) {
3874 blstats[0][fld].hilite_rule
3875 = blstats[1][fld].hilite_rule = (struct hilite_s *) 0;
3876 blstats[0][fld].time = blstats[1][fld].time = 0L;
3878 free((genericptr_t) hl);
3888 status_hilite_menu_fld(fld)
3893 menu_item *picks = (menu_item *) 0;
3895 int count = status_hilite_linestr_countfield(fld);
3896 struct _status_hilite_line_str *hlstr;
3898 boolean acted = FALSE;
3901 if (status_hilite_menu_add(fld)) {
3902 status_hilite_linestr_done();
3903 status_hilite_linestr_gather();
3904 count = status_hilite_linestr_countfield(fld);
3909 tmpwin = create_nhwindow(NHW_MENU);
3913 hlstr = status_hilite_str;
3915 if (hlstr->fld == fld) {
3917 any.a_int = hlstr->id;
3918 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
3919 hlstr->str, MENU_UNSELECTED);
3921 hlstr = hlstr->next;
3926 Sprintf(buf, "No current hilites for %s", initblstats[fld].fldname);
3928 Sprintf(buf, "%s
\82Ì
\8c»
\8dÝ
\82Ì
\83n
\83C
\83\89\83C
\83g
\82Í
\82 \82è
\82Ü
\82¹
\82ñ", initblstats[fld].fldname);
3929 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
3932 /* separator line */
3934 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
3939 add_menu(tmpwin, NO_GLYPH, &any, 'X', 0, ATR_NONE,
3941 "Remove selected hilites", MENU_UNSELECTED);
3943 "
\91I
\91ð
\82µ
\82½
\83n
\83C
\83\89\83C
\83g
\82ð
\8dí
\8f\9c", MENU_UNSELECTED);
3946 #ifndef SCORE_ON_BOTL
3947 if (fld == BL_SCORE) {
3948 /* suppress 'Z - Add a new hilite' for 'score' when SCORE_ON_BOTL
3949 is disabled; we wouldn't be called for 'score' unless it has
3950 hilite rules from the config file, so count must be positive
3951 (hence there's no risk that we're putting up an empty menu) */
3958 add_menu(tmpwin, NO_GLYPH, &any, 'Z', 0, ATR_NONE,
3960 "Add a new hilite", MENU_UNSELECTED);
3962 "
\90V
\82µ
\82¢
\83n
\83C
\83\89\83C
\83g
\82ð
\92Ç
\89Á", MENU_UNSELECTED);
3966 Sprintf(buf, "Current %s hilites:", initblstats[fld].fldname);
3968 Sprintf(buf, "
\8c»
\8dÝ
\82Ì%s
\82Ì
\83n
\83C
\83\89\83C
\83g:", initblstats[fld].fldname);
3969 end_menu(tmpwin, buf);
3971 if ((res = select_menu(tmpwin, PICK_ANY, &picks)) > 0) {
3974 for (i = 0; i < res; i++) {
3975 int idx = picks[i].item.a_int;
3978 /* delete selected hilites */
3983 } else if (idx == -2) {
3984 /* create a new hilite */
3993 /* delete selected hilites */
3994 for (i = 0; i < res; i++) {
3995 int idx = picks[i].item.a_int;
3998 (void) status_hilite_remove(idx);
4000 reset_status_hilites();
4002 } else if (mode == -2) {
4003 /* create a new hilite */
4004 if (status_hilite_menu_add(fld))
4008 free((genericptr_t) picks);
4013 picks = (menu_item *) 0;
4014 destroy_nhwindow(tmpwin);
4019 status_hilites_viewall()
4022 struct _status_hilite_line_str *hlstr = status_hilite_str;
4025 datawin = create_nhwindow(NHW_TEXT);
4028 Sprintf(buf, "OPTIONS=hilite_status: %.*s",
4029 (int) (BUFSZ - sizeof "OPTIONS=hilite_status: " - 1),
4031 putstr(datawin, 0, buf);
4032 hlstr = hlstr->next;
4035 display_nhwindow(datawin, FALSE);
4036 destroy_nhwindow(datawin);
4040 status_hilite_menu()
4044 menu_item *picks = (menu_item *) 0;
4052 tmpwin = create_nhwindow(NHW_MENU);
4055 status_hilite_linestr_gather();
4056 countall = status_hilite_linestr_countfield(BL_FLUSH);
4060 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
4062 "View all hilites in config format", MENU_UNSELECTED);
4064 "
\90Ý
\92è
\83t
\83@
\83C
\83\8b\8c`
\8e®
\82Å
\91S
\82Ä
\82Ì
\83n
\83C
\83\89\83C
\83g
\82ð
\95\
\8e¦", MENU_UNSELECTED);
4067 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
4070 for (i = 0; i < MAXBLSTATS; i++) {
4071 int count = status_hilite_linestr_countfield(i);
4074 #ifndef SCORE_ON_BOTL
4075 /* config file might contain rules for highlighting 'score'
4076 even when SCORE_ON_BOTL is disabled; if so, 'O' command
4077 menus will show them and allow deletions but not additions,
4078 otherwise, it won't show 'score' at all */
4079 if (initblstats[i].fld == BL_SCORE && !count)
4084 Sprintf(buf, "%-18s", initblstats[i].fldname);
4087 Sprintf(eos(buf), " (%d defined)", count);
4089 Sprintf(eos(buf), " (%d
\8cÂ
\90Ý
\92è
\92\86)", count);
4090 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
4091 buf, MENU_UNSELECTED);
4095 end_menu(tmpwin, "Status hilites:");
4097 end_menu(tmpwin, "
\83X
\83e
\81[
\83^
\83X
\83n
\83C
\83\89\83C
\83g:");
4098 if ((res = select_menu(tmpwin, PICK_ONE, &picks)) > 0) {
4099 i = picks->item.a_int - 1;
4101 status_hilites_viewall();
4103 (void) status_hilite_menu_fld(i);
4104 free((genericptr_t) picks), picks = (menu_item *) 0;
4108 destroy_nhwindow(tmpwin);
4109 countall = status_hilite_linestr_countfield(BL_FLUSH);
4110 status_hilite_linestr_done();
4115 /* hilite_delta=='statushilites' does double duty: it is the
4116 number of turns for temporary highlights to remain visible
4117 and also when non-zero it is the flag to enable highlighting */
4118 if (countall > 0 && !iflags.hilite_delta)
4121 "To have highlights become active, set 'statushilites' option to non-zero.");
4123 "
\83n
\83C
\83\89\83C
\83g
\82ð
\97L
\8cø
\82É
\82·
\82é
\82É
\82Í
\81C'statushilites'
\83I
\83v
\83V
\83\87\83\93\82ð0
\88È
\8aO
\82É
\82µ
\82Ä
\82
\82¾
\82³
\82¢
\81D");
4128 #endif /* STATUS_HILITES */