NHDT->ANH, in most cases
[aNetHack.git] / src / botl.c
blob84988604123311ed381f2b7ec06de8f5aad2a2f3
1 /* NetHack 3.6 botl.c $ANH-Date: 1469930895 2016/07/31 02:08:15 $ $ANH-Branch: master $:$ANH-Revision: 1.75 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include <limits.h>
8 extern const char *hu_stat[]; /* defined in eat.c */
10 const char *const enc_stat[] = { "", "Burdened", "Stressed",
11 "Strained", "Overtaxed", "Overloaded" };
13 STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */
14 STATIC_DCL const char *NDECL(rank);
16 #if !defined(STATUS_VIA_WINDOWPORT) || defined(DUMPLOG)
18 char *
19 do_statusline1()
21 static char newbot1[BUFSZ];
22 register char *nb;
23 register int i, j;
25 Strcpy(newbot1, plname);
26 if ('a' <= newbot1[0] && newbot1[0] <= 'z')
27 newbot1[0] += 'A' - 'a';
28 newbot1[10] = 0;
29 Sprintf(nb = eos(newbot1), " the ");
31 if (Upolyd) {
32 char mbot[BUFSZ];
33 int k = 0;
35 Strcpy(mbot, mons[u.umonnum].mname);
36 while (mbot[k] != 0) {
37 if ((k == 0 || (k > 0 && mbot[k - 1] == ' ')) && 'a' <= mbot[k]
38 && mbot[k] <= 'z')
39 mbot[k] += 'A' - 'a';
40 k++;
42 Strcpy(nb = eos(nb), mbot);
43 } else
44 Strcpy(nb = eos(nb), rank());
46 Sprintf(nb = eos(nb), " ");
47 i = mrank_sz + 15;
48 j = (int) ((nb + 2) - newbot1); /* strlen(newbot1) but less computation */
49 if ((i - j) > 0)
50 Sprintf(nb = eos(nb), "%*s", i - j, " "); /* pad with spaces */
51 if (ACURR(A_STR) > 18) {
52 if (ACURR(A_STR) > STR18(100))
53 Sprintf(nb = eos(nb), "St:%2d ", ACURR(A_STR) - 100);
54 else if (ACURR(A_STR) < STR18(100))
55 Sprintf(nb = eos(nb), "St:18/%02d ", ACURR(A_STR) - 18);
56 else
57 Sprintf(nb = eos(nb), "St:18/** ");
58 } else
59 Sprintf(nb = eos(nb), "St:%-1d ", ACURR(A_STR));
60 Sprintf(nb = eos(nb), "Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d",
61 ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS),
62 ACURR(A_CHA));
63 Sprintf(nb = eos(nb),
64 (u.ualign.type == A_CHAOTIC)
65 ? " Chaotic"
66 : (u.ualign.type == A_NEUTRAL) ? " Neutral" : " Lawful");
67 #ifdef SCORE_ON_BOTL
68 if (flags.showscore)
69 Sprintf(nb = eos(nb), " S:%ld", botl_score());
70 #endif
71 return newbot1;
74 char *
75 do_statusline2()
77 static char newbot2[BUFSZ], /* MAXCO: botl.h */
78 /* dungeon location (and gold), hero health (HP, PW, AC),
79 experience (HD if poly'd, else Exp level and maybe Exp points),
80 time (in moves), varying number of status conditions */
81 dloc[QBUFSZ], hlth[QBUFSZ], expr[QBUFSZ], tmmv[QBUFSZ], cond[QBUFSZ];
82 register char *nb;
83 unsigned dln, dx, hln, xln, tln, cln;
84 int hp, hpmax, cap;
85 long money;
88 * Various min(x,9999)'s are to avoid having excessive values
89 * violate the field width assumptions in botl.h and should not
90 * impact normal play. Particularly 64-bit long for gold which
91 * could require many more digits if someone figures out a way
92 * to get and carry a really large (or negative) amount of it.
93 * Turn counter is also long, but we'll risk that.
96 /* dungeon location plus gold */
97 (void) describe_level(dloc); /* includes at least one trailing space */
98 if ((money = money_cnt(invent)) < 0L)
99 money = 0L; /* ought to issue impossible() and then discard gold */
100 Sprintf(eos(dloc), "%s:%-2ld", /* strongest hero can lift ~300000 gold */
101 iflags.in_dumplog ? "$" : encglyph(objnum_to_glyph(GOLD_PIECE)),
102 min(money, 999999L));
103 dln = strlen(dloc);
104 /* '$' encoded as \GXXXXNNNN is 9 chars longer than display will need */
105 dx = strstri(dloc, "\\G") ? 9 : 0;
107 /* health and armor class (has trailing space for AC 0..9) */
108 hp = Upolyd ? u.mh : u.uhp;
109 hpmax = Upolyd ? u.mhmax : u.uhpmax;
110 if (hp < 0)
111 hp = 0;
112 Sprintf(hlth, "HP:%d(%d) Pw:%d(%d) AC:%-2d",
113 min(hp, 9999), min(hpmax, 9999),
114 min(u.uen, 9999), min(u.uenmax, 9999), u.uac);
115 hln = strlen(hlth);
117 /* experience */
118 if (Upolyd)
119 Sprintf(expr, "HD:%d", mons[u.umonnum].mlevel);
120 else if (flags.showexp)
121 Sprintf(expr, "Xp:%u/%-1ld", u.ulevel, u.uexp);
122 else
123 Sprintf(expr, "Exp:%u", u.ulevel);
124 xln = strlen(expr);
126 /* time/move counter */
127 if (flags.time)
128 Sprintf(tmmv, "T:%ld", moves);
129 else
130 tmmv[0] = '\0';
131 tln = strlen(tmmv);
133 /* status conditions; worst ones first */
134 cond[0] = '\0'; /* once non-empty, cond will have a leading space */
135 nb = cond;
137 * Stoned, Slimed, Strangled, and both types of Sick are all fatal
138 * unless remedied before timeout expires. Should we order them by
139 * shortest time left? [Probably not worth the effort, since it's
140 * unusual for more than one of them to apply at a time.]
142 if (Stoned)
143 Strcpy(nb = eos(nb), " Stone");
144 if (Slimed)
145 Strcpy(nb = eos(nb), " Slime");
146 if (Strangled)
147 Strcpy(nb = eos(nb), " Strngl");
148 if (Sick) {
149 if (u.usick_type & SICK_VOMITABLE)
150 Strcpy(nb = eos(nb), " FoodPois");
151 if (u.usick_type & SICK_NONVOMITABLE)
152 Strcpy(nb = eos(nb), " TermIll");
154 if (u.uhs != NOT_HUNGRY)
155 Sprintf(nb = eos(nb), " %s", hu_stat[u.uhs]);
156 if ((cap = near_capacity()) > UNENCUMBERED)
157 Sprintf(nb = eos(nb), " %s", enc_stat[cap]);
158 if (Blind)
159 Strcpy(nb = eos(nb), " Blind");
160 if (Deaf)
161 Strcpy(nb = eos(nb), " Deaf");
162 if (Stunned)
163 Strcpy(nb = eos(nb), " Stun");
164 if (Confusion)
165 Strcpy(nb = eos(nb), " Conf");
166 if (Hallucination)
167 Strcpy(nb = eos(nb), " Hallu");
168 /* levitation and flying are mutually exclusive; riding is not */
169 if (Levitation)
170 Strcpy(nb = eos(nb), " Lev");
171 if (Flying)
172 Strcpy(nb = eos(nb), " Fly");
173 if (u.usteed)
174 Strcpy(nb = eos(nb), " Ride");
175 cln = strlen(cond);
178 * Put the pieces together. If they all fit, keep the traditional
179 * sequence. Otherwise, move least important parts to the end in
180 * case the interface side of things has to truncate. Note that
181 * dloc[] contains '$' encoded in ten character sequence \GXXXXNNNN
182 * so we want to test its display length rather than buffer length.
184 * We don't have an actual display limit here, so have to go by the
185 * width of the map. Since we're reordering rather than truncating,
186 * wider displays can still show wider status than the map if the
187 * interface supports that.
189 if ((dln - dx) + 1 + hln + 1 + xln + 1 + tln + 1 + cln <= COLNO) {
190 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, expr, tmmv, cond);
191 } else {
192 if (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1 > MAXCO) {
193 panic("bot2: second status line exceeds MAXCO (%u > %d)",
194 (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1), MAXCO);
195 } else if ((dln - dx) + 1 + hln + 1 + xln + 1 + cln <= COLNO) {
196 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, expr, cond, tmmv);
197 } else if ((dln - dx) + 1 + hln + 1 + cln <= COLNO) {
198 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, cond, expr, tmmv);
199 } else {
200 Sprintf(newbot2, "%s %s %s %s %s", hlth, cond, dloc, expr, tmmv);
202 /* only two or three consecutive spaces available to squeeze out */
203 mungspaces(newbot2);
205 return newbot2;
208 #ifndef STATUS_VIA_WINDOWPORT
209 void
210 bot()
212 if (youmonst.data && iflags.status_updates) {
213 curs(WIN_STATUS, 1, 0);
214 putstr(WIN_STATUS, 0, do_statusline1());
215 curs(WIN_STATUS, 1, 1);
216 putmixed(WIN_STATUS, 0, do_statusline2());
218 context.botl = context.botlx = 0;
220 #endif /* !STATUS_VIA_WINDOWPORT */
222 #endif /* !STATUS_VIA_WINDOWPORT || DUMPLOG */
224 /* convert experience level (1..30) to rank index (0..8) */
226 xlev_to_rank(xlev)
227 int xlev;
229 return (xlev <= 2) ? 0 : (xlev <= 30) ? ((xlev + 2) / 4) : 8;
232 #if 0 /* not currently needed */
233 /* convert rank index (0..8) to experience level (1..30) */
235 rank_to_xlev(rank)
236 int rank;
238 return (rank <= 0) ? 1 : (rank <= 8) ? ((rank * 4) - 2) : 30;
240 #endif
242 const char *
243 rank_of(lev, monnum, female)
244 int lev;
245 short monnum;
246 boolean female;
248 register const struct Role *role;
249 register int i;
251 /* Find the role */
252 for (role = roles; role->name.m; role++)
253 if (monnum == role->malenum || monnum == role->femalenum)
254 break;
255 if (!role->name.m)
256 role = &urole;
258 /* Find the rank */
259 for (i = xlev_to_rank((int) lev); i >= 0; i--) {
260 if (female && role->rank[i].f)
261 return role->rank[i].f;
262 if (role->rank[i].m)
263 return role->rank[i].m;
266 /* Try the role name, instead */
267 if (female && role->name.f)
268 return role->name.f;
269 else if (role->name.m)
270 return role->name.m;
271 return "Player";
274 STATIC_OVL const char *
275 rank()
277 return rank_of(u.ulevel, Role_switch, flags.female);
281 title_to_mon(str, rank_indx, title_length)
282 const char *str;
283 int *rank_indx, *title_length;
285 register int i, j;
287 /* Loop through each of the roles */
288 for (i = 0; roles[i].name.m; i++)
289 for (j = 0; j < 9; j++) {
290 if (roles[i].rank[j].m
291 && !strncmpi(str, roles[i].rank[j].m,
292 strlen(roles[i].rank[j].m))) {
293 if (rank_indx)
294 *rank_indx = j;
295 if (title_length)
296 *title_length = strlen(roles[i].rank[j].m);
297 return roles[i].malenum;
299 if (roles[i].rank[j].f
300 && !strncmpi(str, roles[i].rank[j].f,
301 strlen(roles[i].rank[j].f))) {
302 if (rank_indx)
303 *rank_indx = j;
304 if (title_length)
305 *title_length = strlen(roles[i].rank[j].f);
306 return (roles[i].femalenum != NON_PM) ? roles[i].femalenum
307 : roles[i].malenum;
310 return NON_PM;
313 void
314 max_rank_sz()
316 register int i, r, maxr = 0;
317 for (i = 0; i < 9; i++) {
318 if (urole.rank[i].m && (r = strlen(urole.rank[i].m)) > maxr)
319 maxr = r;
320 if (urole.rank[i].f && (r = strlen(urole.rank[i].f)) > maxr)
321 maxr = r;
323 mrank_sz = maxr;
324 return;
327 #ifdef SCORE_ON_BOTL
328 long
329 botl_score()
331 long deepest = deepest_lev_reached(FALSE);
332 long utotal;
334 utotal = money_cnt(invent) + hidden_gold();
335 if ((utotal -= u.umoney0) < 0L)
336 utotal = 0L;
337 utotal += u.urexp + (50 * (deepest - 1))
338 + (deepest > 30 ? 10000 : deepest > 20 ? 1000 * (deepest - 20) : 0);
339 if (utotal < u.urexp)
340 utotal = LONG_MAX; /* wrap around */
341 return utotal;
343 #endif /* SCORE_ON_BOTL */
345 /* provide the name of the current level for display by various ports */
347 describe_level(buf)
348 char *buf;
350 int ret = 1;
352 /* TODO: Add in dungeon name */
353 if (Is_knox(&u.uz))
354 Sprintf(buf, "%s ", dungeons[u.uz.dnum].dname);
355 else if (In_quest(&u.uz))
356 Sprintf(buf, "Home %d ", dunlev(&u.uz));
357 else if (In_endgame(&u.uz))
358 Sprintf(buf, Is_astralevel(&u.uz) ? "Astral Plane " : "End Game ");
359 else {
360 /* ports with more room may expand this one */
361 Sprintf(buf, "Dlvl:%-2d ", depth(&u.uz));
362 ret = 0;
364 return ret;
367 #ifdef STATUS_VIA_WINDOWPORT
368 /* =======================================================================*/
370 /* structure that tracks the status details in the core */
371 struct istat_s {
372 long time;
373 unsigned anytype;
374 anything a;
375 char *val;
376 int valwidth;
377 enum statusfields idxmax;
378 enum statusfields fld;
382 STATIC_DCL void NDECL(init_blstats);
383 STATIC_DCL char *FDECL(anything_to_s, (char *, anything *, int));
384 STATIC_OVL int FDECL(percentage, (struct istat_s *, struct istat_s *));
385 STATIC_OVL int FDECL(compare_blstats, (struct istat_s *, struct istat_s *));
386 #ifdef STATUS_HILITES
387 STATIC_DCL void FDECL(s_to_anything, (anything *, char *, int));
388 STATIC_DCL boolean FDECL(assign_hilite, (char *, char *, char *, char *,
389 BOOLEAN_P));
390 STATIC_DCL const char *FDECL(clridx_to_s, (char *, int));
391 #endif
393 /* If entries are added to this, botl.h will require updating too */
394 STATIC_DCL struct istat_s initblstats[MAXBLSTATS] = {
395 { 0L, ANY_STR, { (genericptr_t) 0 }, (char *) 0, 80, 0, BL_TITLE},
396 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_STR},
397 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_DX},
398 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_CO},
399 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_IN},
400 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_WI},
401 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_CH},
402 { 0L, ANY_STR, { (genericptr_t) 0 }, (char *) 0, 40, 0, BL_ALIGN},
403 { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_SCORE},
404 { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_CAP},
405 { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 30, 0, BL_GOLD},
406 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, BL_ENEMAX, BL_ENE},
407 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_ENEMAX},
408 { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_XP},
409 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_AC},
410 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_HD},
411 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_TIME},
412 { 0L, ANY_UINT, { (genericptr_t) 0 }, (char *) 0, 40, 0, BL_HUNGER},
413 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, BL_HPMAX, BL_HP},
414 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_HPMAX},
415 { 0L, ANY_STR, { (genericptr_t) 0 }, (char *) 0, 80, 0, BL_LEVELDESC},
416 { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_EXP},
417 { 0L, ANY_MASK32,
418 { (genericptr_t) 0 }, (char *) 0, 0, 0, BL_CONDITION}
421 struct istat_s blstats[2][MAXBLSTATS];
422 static boolean blinit = FALSE, update_all = FALSE;
424 void
425 bot()
427 char buf[BUFSZ];
428 register char *nb;
429 static int idx = 0, idx_p, idxmax;
430 unsigned anytype;
431 long money;
432 int i, pc, chg, cap;
433 struct istat_s *curr, *prev;
434 boolean valset[MAXBLSTATS], chgval = FALSE, updated = FALSE;
436 if (!blinit)
437 panic("bot before init.");
438 if (!youmonst.data || !iflags.status_updates) {
439 context.botl = context.botlx = 0;
440 update_all = FALSE;
441 return;
444 idx_p = idx;
445 idx = 1 - idx; /* 0 -> 1, 1 -> 0 */
447 /* clear the "value set" indicators */
448 (void) memset((genericptr_t) valset, 0, MAXBLSTATS * sizeof (boolean));
451 * Note: min(x,9999) - we enforce the same maximum on hp, maxhp,
452 * pw, maxpw, and gold as basic status formatting so that the two
453 * modes of status display don't produce different information.
457 * Player name and title.
459 Strcpy(nb = buf, plname);
460 nb[0] = highc(nb[0]);
461 nb[10] = '\0';
462 Sprintf(nb = eos(nb), " the ");
463 if (Upolyd) {
464 for (i = 0, nb = strcpy(eos(nb), mons[u.umonnum].mname); nb[i]; i++)
465 if (i == 0 || nb[i - 1] == ' ')
466 nb[i] = highc(nb[i]);
467 } else
468 Strcpy(nb = eos(nb), rank());
469 Sprintf(blstats[idx][BL_TITLE].val, "%-29s", buf);
470 valset[BL_TITLE] = TRUE; /* indicate val already set */
472 /* Strength */
473 buf[0] = '\0';
474 blstats[idx][BL_STR].a.a_int = ACURR(A_STR);
475 if (ACURR(A_STR) > 18) {
476 if (ACURR(A_STR) > STR18(100))
477 Sprintf(buf, "%2d", ACURR(A_STR) - 100);
478 else if (ACURR(A_STR) < STR18(100))
479 Sprintf(buf, "18/%02d", ACURR(A_STR) - 18);
480 else
481 Sprintf(buf, "18/**");
482 } else
483 Sprintf(buf, "%-1d", ACURR(A_STR));
484 Strcpy(blstats[idx][BL_STR].val, buf);
485 valset[BL_STR] = TRUE; /* indicate val already set */
487 /* Dexterity, constitution, intelligence, wisdom, charisma. */
488 blstats[idx][BL_DX].a.a_int = ACURR(A_DEX);
489 blstats[idx][BL_CO].a.a_int = ACURR(A_CON);
490 blstats[idx][BL_IN].a.a_int = ACURR(A_INT);
491 blstats[idx][BL_WI].a.a_int = ACURR(A_WIS);
492 blstats[idx][BL_CH].a.a_int = ACURR(A_CHA);
494 /* Alignment */
495 Strcpy(blstats[idx][BL_ALIGN].val, (u.ualign.type == A_CHAOTIC)
496 ? "Chaotic"
497 : (u.ualign.type == A_NEUTRAL)
498 ? "Neutral"
499 : "Lawful");
501 /* Score */
502 blstats[idx][BL_SCORE].a.a_long =
503 #ifdef SCORE_ON_BOTL
504 botl_score()
505 #else
507 #endif
510 /* Hit points */
511 i = Upolyd ? u.mh : u.uhp;
512 if (i < 0)
513 i = 0;
514 blstats[idx][BL_HP].a.a_int = min(i, 9999);
515 i = Upolyd ? u.mhmax : u.uhpmax;
516 blstats[idx][BL_HPMAX].a.a_int = min(i, 9999);
518 /* Dungeon level. */
519 (void) describe_level(blstats[idx][BL_LEVELDESC].val);
520 valset[BL_LEVELDESC] = TRUE; /* indicate val already set */
522 /* Gold */
523 if ((money = money_cnt(invent)) < 0L)
524 money = 0L; /* ought to issue impossible() and then discard gold */
525 blstats[idx][BL_GOLD].a.a_long = min(money, 999999L);
527 * The tty port needs to display the current symbol for gold
528 * as a field header, so to accommodate that we pass gold with
529 * that already included. If a window port needs to use the text
530 * gold amount without the leading "$:" the port will have to
531 * skip past ':' to the value pointer it was passed in status_update()
532 * for the BL_GOLD case.
534 * Another quirk of BL_GOLD is that the field display may have
535 * changed if a new symbol set was loaded, or we entered or left
536 * the rogue level.
538 * The currency prefix is encoded as ten character \GXXXXNNNN
539 * sequence.
541 Sprintf(blstats[idx][BL_GOLD].val, "%s:%ld",
542 encglyph(objnum_to_glyph(GOLD_PIECE)),
543 blstats[idx][BL_GOLD].a.a_long);
544 valset[BL_GOLD] = TRUE; /* indicate val already set */
546 /* Power (magical energy) */
547 blstats[idx][BL_ENE].a.a_int = min(u.uen, 9999);
548 blstats[idx][BL_ENEMAX].a.a_int = min(u.uenmax, 9999);
550 /* Armor class */
551 blstats[idx][BL_AC].a.a_int = u.uac;
553 /* Monster level (if Upolyd) */
554 blstats[idx][BL_HD].a.a_int = Upolyd ? mons[u.umonnum].mlevel : 0;
556 /* Experience */
557 blstats[idx][BL_XP].a.a_int = u.ulevel;
558 blstats[idx][BL_EXP].a.a_int = u.uexp;
560 /* Time (moves) */
561 blstats[idx][BL_TIME].a.a_long = moves;
563 /* Hunger */
564 blstats[idx][BL_HUNGER].a.a_uint = u.uhs;
565 Strcpy(blstats[idx][BL_HUNGER].val,
566 (u.uhs != NOT_HUNGRY) ? hu_stat[u.uhs] : "");
567 valset[BL_HUNGER] = TRUE;
569 /* Carrying capacity */
570 cap = near_capacity();
571 blstats[idx][BL_CAP].a.a_int = cap;
572 Strcpy(blstats[idx][BL_CAP].val,
573 (cap > UNENCUMBERED) ? enc_stat[cap] : "");
574 valset[BL_CAP] = TRUE;
576 /* Conditions */
577 blstats[idx][BL_CONDITION].a.a_ulong = 0L;
578 if (Stoned)
579 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STONE;
580 if (Slimed)
581 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_SLIME;
582 if (Strangled)
583 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STRNGL;
584 if (Sick && (u.usick_type & SICK_VOMITABLE) != 0)
585 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FOODPOIS;
586 if (Sick && (u.usick_type & SICK_NONVOMITABLE) != 0)
587 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_TERMILL;
589 * basic formatting puts hunger status and encumbrance here
591 if (Blind)
592 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_BLIND;
593 if (Deaf)
594 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_DEAF;
595 if (Stunned)
596 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STUN;
597 if (Confusion)
598 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_CONF;
599 if (Hallucination)
600 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_HALLU;
601 /* levitation and flying are mututally exclusive */
602 if (Levitation)
603 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_LEV;
604 if (Flying)
605 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FLY;
606 if (u.usteed)
607 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_RIDE;
610 * Now pass the changed values to window port.
612 for (i = 0; i < MAXBLSTATS; i++) {
613 if (((i == BL_SCORE) && !flags.showscore)
614 || ((i == BL_EXP) && !flags.showexp)
615 || ((i == BL_TIME) && !flags.time)
616 || ((i == BL_HD) && !Upolyd)
617 || ((i == BL_XP || i == BL_EXP) && Upolyd))
618 continue;
619 anytype = blstats[idx][i].anytype;
620 curr = &blstats[idx][i];
621 prev = &blstats[idx_p][i];
622 chg = 0;
623 if (update_all
624 || ((chg = compare_blstats(prev, curr)) != 0)
625 || ((chgval = (valset[i]
626 && strcmp(blstats[idx][i].val,
627 blstats[idx_p][i].val))) != 0)) {
628 idxmax = blstats[idx][i].idxmax;
629 pc = (idxmax) ? percentage(curr, &blstats[idx][idxmax]) : 0;
630 if (!valset[i])
631 (void) anything_to_s(curr->val, &curr->a, anytype);
632 if (anytype != ANY_MASK32) {
633 status_update(i, (genericptr_t) curr->val,
634 valset[i] ? chgval : chg, pc);
635 } else {
636 /* send pointer to mask */
637 status_update(i, (genericptr_t) &curr->a.a_ulong, chg, 0);
639 updated = TRUE;
643 * It is possible to get here, with nothing having been pushed
644 * to the window port, when none of the info has changed. In that
645 * case, we need to force a call to status_update() when
646 * context.botlx is set. The tty port in particular has a problem
647 * if that isn't done, since it sets context.botlx when a menu or
648 * text display obliterates the status line.
650 * To work around it, we call status_update() with fictitious
651 * index of BL_FLUSH (-1).
653 if ((context.botlx && !updated)
654 || windowprocs.win_status_update == genl_status_update)
655 status_update(BL_FLUSH, (genericptr_t) 0, 0, 0);
657 context.botl = context.botlx = 0;
658 update_all = FALSE;
661 void
662 status_initialize(reassessment)
663 boolean
664 reassessment; /* TRUE = just reassess fields w/o other initialization*/
666 int i;
667 const char *fieldfmt = (const char *) 0;
668 const char *fieldname = (const char *) 0;
670 if (!reassessment) {
671 init_blstats();
672 (*windowprocs.win_status_init)();
673 blinit = TRUE;
674 #ifdef STATUS_HILITES
675 status_notify_windowport(TRUE);
676 #endif
678 for (i = 0; i < MAXBLSTATS; ++i) {
679 enum statusfields fld = initblstats[i].fld;
681 switch (fld) {
682 case BL_TITLE:
683 fieldfmt = "%s";
684 fieldname = "title";
685 status_enablefield(fld, fieldname, fieldfmt, TRUE);
686 break;
687 case BL_STR:
688 fieldfmt = " St:%s";
689 fieldname = "strength";
690 status_enablefield(fld, fieldname, fieldfmt, TRUE);
691 break;
692 case BL_DX:
693 fieldfmt = " Dx:%s";
694 fieldname = "dexterity";
695 status_enablefield(fld, fieldname, fieldfmt, TRUE);
696 break;
697 case BL_CO:
698 fieldfmt = " Co:%s";
699 fieldname = "constitution";
700 status_enablefield(fld, fieldname, fieldfmt, TRUE);
701 break;
702 case BL_IN:
703 fieldfmt = " In:%s";
704 fieldname = "intelligence";
705 status_enablefield(fld, fieldname, fieldfmt, TRUE);
706 break;
707 case BL_WI:
708 fieldfmt = " Wi:%s";
709 fieldname = "wisdom";
710 status_enablefield(fld, fieldname, fieldfmt, TRUE);
711 break;
712 case BL_CH:
713 fieldfmt = " Ch:%s";
714 fieldname = "charisma";
715 status_enablefield(fld, fieldname, fieldfmt, TRUE);
716 break;
717 case BL_ALIGN:
718 fieldfmt = " %s";
719 fieldname = "alignment";
720 status_enablefield(fld, fieldname, fieldfmt, TRUE);
721 break;
722 case BL_SCORE:
723 fieldfmt = " S:%s";
724 fieldname = "score";
725 status_enablefield(fld, fieldname, fieldfmt,
726 (!flags.showscore) ? FALSE : TRUE);
727 break;
728 case BL_CAP:
729 fieldfmt = " %s";
730 fieldname = "carrying-capacity";
731 status_enablefield(fld, fieldname, fieldfmt, TRUE);
732 break;
733 case BL_GOLD:
734 fieldfmt = " %s";
735 fieldname = "gold";
736 status_enablefield(fld, fieldname, fieldfmt, TRUE);
737 break;
738 case BL_ENE:
739 fieldfmt = " Pw:%s";
740 fieldname = "power";
741 status_enablefield(fld, fieldname, fieldfmt, TRUE);
742 break;
743 case BL_ENEMAX:
744 fieldfmt = "(%s)";
745 fieldname = "power-max";
746 status_enablefield(fld, fieldname, fieldfmt, TRUE);
747 break;
748 case BL_XP:
749 fieldfmt = " Xp:%s";
750 fieldname = "experience-level";
751 status_enablefield(fld, fieldname, fieldfmt,
752 (Upolyd) ? FALSE : TRUE);
753 break;
754 case BL_AC:
755 fieldfmt = " AC:%s";
756 fieldname = "armor-class";
757 status_enablefield(fld, fieldname, fieldfmt, TRUE);
758 break;
759 case BL_HD:
760 fieldfmt = " HD:%s";
761 fieldname = "HD";
762 status_enablefield(fld, fieldname, fieldfmt,
763 (!Upolyd) ? FALSE : TRUE);
764 break;
765 case BL_TIME:
766 fieldfmt = " T:%s";
767 fieldname = "time";
768 status_enablefield(fld, fieldname, fieldfmt,
769 (!flags.time) ? FALSE : TRUE);
770 break;
771 case BL_HUNGER:
772 fieldfmt = " %s";
773 fieldname = "hunger";
774 status_enablefield(fld, fieldname, fieldfmt, TRUE);
775 break;
776 case BL_HP:
777 fieldfmt = " HP:%s";
778 fieldname = "hitpoints";
779 status_enablefield(fld, fieldname, fieldfmt, TRUE);
780 break;
781 case BL_HPMAX:
782 fieldfmt = "(%s)";
783 fieldname = "hitpoint-max";
784 status_enablefield(fld, fieldname, fieldfmt, TRUE);
785 break;
786 case BL_LEVELDESC:
787 fieldfmt = "%s";
788 fieldname = "dungeon-level";
789 status_enablefield(fld, fieldname, fieldfmt, TRUE);
790 break;
791 case BL_EXP:
792 fieldfmt = "/%s";
793 fieldname = "experience";
794 status_enablefield(fld, fieldname, fieldfmt,
795 (!flags.showexp || Upolyd) ? FALSE : TRUE);
796 break;
797 case BL_CONDITION:
798 fieldfmt = "%s";
799 fieldname = "condition";
800 status_enablefield(fld, fieldname, fieldfmt, TRUE);
801 break;
802 case BL_FLUSH:
803 default:
804 break;
807 update_all = TRUE;
810 void
811 status_finish()
813 int i;
815 /* call the window port cleanup routine first */
816 (*windowprocs.win_status_finish)();
818 /* free memory that we alloc'd now */
819 for (i = 0; i < MAXBLSTATS; ++i) {
820 if (blstats[0][i].val)
821 free((genericptr_t) blstats[0][i].val);
822 if (blstats[1][i].val)
823 free((genericptr_t) blstats[1][i].val);
827 STATIC_OVL void
828 init_blstats()
830 static boolean initalready = FALSE;
831 int i, j;
833 if (initalready) {
834 impossible("init_blstats called more than once.");
835 return;
838 initalready = TRUE;
839 for (i = BEFORE; i <= NOW; ++i) {
840 for (j = 0; j < MAXBLSTATS; ++j) {
841 blstats[i][j] = initblstats[j];
842 blstats[i][j].a = zeroany;
843 if (blstats[i][j].valwidth) {
844 blstats[i][j].val = (char *) alloc(blstats[i][j].valwidth);
845 blstats[i][j].val[0] = '\0';
846 } else
847 blstats[i][j].val = (char *) 0;
852 STATIC_OVL char *
853 anything_to_s(buf, a, anytype)
854 char *buf;
855 anything *a;
856 int anytype;
858 if (!buf)
859 return (char *) 0;
861 switch (anytype) {
862 case ANY_ULONG:
863 Sprintf(buf, "%lu", a->a_ulong);
864 break;
865 case ANY_MASK32:
866 Sprintf(buf, "%lx", a->a_ulong);
867 break;
868 case ANY_LONG:
869 Sprintf(buf, "%ld", a->a_long);
870 break;
871 case ANY_INT:
872 Sprintf(buf, "%d", a->a_int);
873 break;
874 case ANY_UINT:
875 Sprintf(buf, "%u", a->a_uint);
876 break;
877 case ANY_IPTR:
878 Sprintf(buf, "%d", *a->a_iptr);
879 break;
880 case ANY_LPTR:
881 Sprintf(buf, "%ld", *a->a_lptr);
882 break;
883 case ANY_ULPTR:
884 Sprintf(buf, "%lu", *a->a_ulptr);
885 break;
886 case ANY_UPTR:
887 Sprintf(buf, "%u", *a->a_uptr);
888 break;
889 case ANY_STR: /* do nothing */
891 break;
892 default:
893 buf[0] = '\0';
895 return buf;
898 #ifdef STATUS_HILITES
900 STATIC_OVL void
901 s_to_anything(a, buf, anytype)
902 anything *a;
903 char *buf;
904 int anytype;
906 if (!buf || !a)
907 return;
909 switch (anytype) {
910 case ANY_LONG:
911 a->a_long = atol(buf);
912 break;
913 case ANY_INT:
914 a->a_int = atoi(buf);
915 break;
916 case ANY_UINT:
917 a->a_uint = (unsigned) atoi(buf);
918 break;
919 case ANY_ULONG:
920 a->a_ulong = (unsigned long) atol(buf);
921 break;
922 case ANY_IPTR:
923 if (a->a_iptr)
924 *a->a_iptr = atoi(buf);
925 break;
926 case ANY_UPTR:
927 if (a->a_uptr)
928 *a->a_uptr = (unsigned) atoi(buf);
929 break;
930 case ANY_LPTR:
931 if (a->a_lptr)
932 *a->a_lptr = atol(buf);
933 break;
934 case ANY_ULPTR:
935 if (a->a_ulptr)
936 *a->a_ulptr = (unsigned long) atol(buf);
937 break;
938 case ANY_MASK32:
939 a->a_ulong = (unsigned long) atol(buf);
940 break;
941 default:
942 a->a_void = 0;
943 break;
945 return;
948 #endif
950 STATIC_OVL int
951 compare_blstats(bl1, bl2)
952 struct istat_s *bl1, *bl2;
954 int anytype, result = 0;
956 if (!bl1 || !bl2) {
957 panic("compare_blstat: bad istat pointer %s, %s",
958 fmt_ptr((genericptr_t) bl1), fmt_ptr((genericptr_t) bl2));
961 anytype = bl1->anytype;
962 if ((!bl1->a.a_void || !bl2->a.a_void)
963 && (anytype == ANY_IPTR || anytype == ANY_UPTR || anytype == ANY_LPTR
964 || anytype == ANY_ULPTR)) {
965 panic("compare_blstat: invalid pointer %s, %s",
966 fmt_ptr((genericptr_t) bl1->a.a_void),
967 fmt_ptr((genericptr_t) bl2->a.a_void));
970 switch (anytype) {
971 case ANY_INT:
972 result = (bl1->a.a_int < bl2->a.a_int)
974 : (bl1->a.a_int > bl2->a.a_int) ? -1 : 0;
975 break;
976 case ANY_IPTR:
977 result = (*bl1->a.a_iptr < *bl2->a.a_iptr)
979 : (*bl1->a.a_iptr > *bl2->a.a_iptr) ? -1 : 0;
980 break;
981 case ANY_LONG:
982 result = (bl1->a.a_long < bl2->a.a_long)
984 : (bl1->a.a_long > bl2->a.a_long) ? -1 : 0;
985 break;
986 case ANY_LPTR:
987 result = (*bl1->a.a_lptr < *bl2->a.a_lptr)
989 : (*bl1->a.a_lptr > *bl2->a.a_lptr) ? -1 : 0;
990 break;
991 case ANY_UINT:
992 result = (bl1->a.a_uint < bl2->a.a_uint)
994 : (bl1->a.a_uint > bl2->a.a_uint) ? -1 : 0;
995 break;
996 case ANY_UPTR:
997 result = (*bl1->a.a_uptr < *bl2->a.a_uptr)
999 : (*bl1->a.a_uptr > *bl2->a.a_uptr) ? -1 : 0;
1000 break;
1001 case ANY_ULONG:
1002 result = (bl1->a.a_ulong < bl2->a.a_ulong)
1004 : (bl1->a.a_ulong > bl2->a.a_ulong) ? -1 : 0;
1005 break;
1006 case ANY_ULPTR:
1007 result = (*bl1->a.a_ulptr < *bl2->a.a_ulptr)
1009 : (*bl1->a.a_ulptr > *bl2->a.a_ulptr) ? -1 : 0;
1010 break;
1011 case ANY_STR:
1012 if (strcmp(bl1->val, bl2->val) == 0)
1013 result = 0;
1014 else
1015 result = 1;
1016 break;
1017 case ANY_MASK32:
1018 if (bl1->a.a_ulong == bl2->a.a_ulong)
1019 result = 0;
1020 else
1021 result = 1;
1022 break;
1023 default:
1024 result = 1;
1026 return result;
1029 STATIC_OVL int
1030 percentage(bl, maxbl)
1031 struct istat_s *bl, *maxbl;
1033 int result = 0;
1034 int anytype;
1036 if (!bl || !maxbl) {
1037 impossible("percentage: bad istat pointer %s, %s",
1038 fmt_ptr((genericptr_t) bl), fmt_ptr((genericptr_t) maxbl));
1039 return 0;
1042 anytype = bl->anytype;
1043 if (maxbl->a.a_void) {
1044 switch (anytype) {
1045 case ANY_INT:
1046 result = ((100 * bl->a.a_int) / maxbl->a.a_int);
1047 break;
1048 case ANY_LONG:
1049 result = (int) ((100L * bl->a.a_long) / maxbl->a.a_long);
1050 break;
1051 case ANY_UINT:
1052 result = (int) ((100U * bl->a.a_uint) / maxbl->a.a_uint);
1053 break;
1054 case ANY_ULONG:
1055 result = (int) ((100UL * bl->a.a_ulong) / maxbl->a.a_ulong);
1056 break;
1057 case ANY_IPTR:
1058 result = ((100 * (*bl->a.a_iptr)) / (*maxbl->a.a_iptr));
1059 break;
1060 case ANY_LPTR:
1061 result = (int) ((100L * (*bl->a.a_lptr)) / (*maxbl->a.a_lptr));
1062 break;
1063 case ANY_UPTR:
1064 result = (int) ((100U * (*bl->a.a_uptr)) / (*maxbl->a.a_uptr));
1065 break;
1066 case ANY_ULPTR:
1067 result = (int) ((100UL * (*bl->a.a_ulptr)) / (*maxbl->a.a_ulptr));
1068 break;
1071 return result;
1075 #ifdef STATUS_HILITES
1077 /****************************************************************************/
1078 /* Core status hiliting support */
1079 /****************************************************************************/
1081 static struct fieldid_t {
1082 const char *fieldname;
1083 enum statusfields fldid;
1084 } fieldids[] = {
1085 {"title", BL_TITLE},
1086 {"strength", BL_STR},
1087 {"dexterity", BL_DX},
1088 {"constitution", BL_CO},
1089 {"intelligence", BL_IN},
1090 {"wisdom", BL_WI},
1091 {"charisma", BL_CH},
1092 {"alignment", BL_ALIGN},
1093 {"score", BL_SCORE},
1094 {"carrying-capacity", BL_CAP},
1095 {"gold", BL_GOLD},
1096 {"power", BL_ENE},
1097 {"power-max", BL_ENEMAX},
1098 {"experience-level", BL_XP},
1099 {"armor-class", BL_AC},
1100 {"HD", BL_HD},
1101 {"time", BL_TIME},
1102 {"hunger", BL_HUNGER},
1103 {"hitpoints", BL_HP},
1104 {"hitpoints-max", BL_HPMAX},
1105 {"dungeon-level", BL_LEVELDESC},
1106 {"experience", BL_EXP},
1107 {"condition", BL_CONDITION},
1110 struct hilite_s {
1111 boolean set;
1112 unsigned anytype;
1113 anything threshold;
1114 int behavior;
1115 int coloridx[2];
1118 struct hilite_s status_hilites[MAXBLSTATS];
1121 * This is the parser for the hilite options
1122 * Example:
1123 * OPTION=hilite_status: hitpoints/10%/red/normal
1125 * set_hilite_status() separates each hilite entry into its 4 component
1126 * strings, then calls assign_hilite() to make the adjustments.
1128 boolean
1129 set_status_hilites(op, from_configfile)
1130 char *op;
1131 boolean from_configfile;
1133 char hsbuf[4][QBUFSZ];
1134 boolean rslt, badopt = FALSE;
1135 int fldnum, num = 0, ccount = 0;
1136 char c;
1138 num = fldnum = 0;
1139 hsbuf[0][0] = hsbuf[1][0] = hsbuf[2][0] = hsbuf[3][0] = '\0';
1140 while (*op && fldnum < 4 && ccount < (QBUFSZ - 2)) {
1141 c = lowc(*op);
1142 if (c == ' ') {
1143 if (fldnum >= 2) {
1144 rslt = assign_hilite(&hsbuf[0][0], &hsbuf[1][0], &hsbuf[2][0],
1145 &hsbuf[3][0], from_configfile);
1146 if (!rslt) {
1147 badopt = TRUE;
1148 break;
1151 hsbuf[0][0] = hsbuf[1][0] = '\0';
1152 hsbuf[2][0] = hsbuf[3][0] = '\0';
1153 fldnum = 0;
1154 ccount = 0;
1155 } else if (c == '/') {
1156 fldnum++;
1157 ccount = 0;
1158 } else {
1159 hsbuf[fldnum][ccount++] = c;
1160 hsbuf[fldnum][ccount] = '\0';
1162 op++;
1164 if (fldnum >= 2 && !badopt) {
1165 rslt = assign_hilite(&hsbuf[0][0], &hsbuf[1][0], &hsbuf[2][0],
1166 &hsbuf[3][0], from_configfile);
1167 if (!rslt)
1168 badopt = TRUE;
1170 if (badopt)
1171 return FALSE;
1172 return TRUE;
1175 void
1176 clear_status_hilites(from_configfile)
1177 boolean from_configfile;
1179 int i;
1180 anything it;
1182 it = zeroany;
1183 for (i = 0; i < MAXBLSTATS; ++i) {
1184 (void) memset((genericptr_t) &status_hilites[i], 0,
1185 sizeof(struct hilite_s));
1186 /* notify window port */
1187 if (!from_configfile)
1188 status_threshold(i, blstats[0][i].anytype, it, 0, 0, 0);
1192 STATIC_OVL boolean
1193 assign_hilite(sa, sb, sc, sd, from_configfile)
1194 char *sa, *sb, *sc, *sd;
1195 boolean from_configfile;
1197 char *tmp, *how;
1198 int i = -1, dt = -1, idx = -1;
1199 int coloridx[2] = { -1, -1 };
1200 boolean inverse[2] = { FALSE, FALSE };
1201 boolean bold[2] = { FALSE, FALSE };
1202 boolean normal[2] = { 0, 0 };
1203 boolean percent = FALSE, down_up = FALSE, changed = FALSE;
1204 anything threshold;
1205 enum statusfields fld = BL_FLUSH;
1206 threshold.a_void = 0;
1208 /* Example:
1209 * hilite_status: hitpoints/10%/red/normal
1212 /* field name to statusfield */
1213 for (i = 0; sa && i < SIZE(fieldids); ++i) {
1214 if (strcmpi(sa, fieldids[i].fieldname) == 0) {
1215 idx = i;
1216 fld = fieldids[i].fldid;
1217 break;
1220 if (idx == -1)
1221 return FALSE;
1222 status_hilites[idx].set = FALSE; /* mark it "unset" */
1224 /* threshold */
1225 if (!sb)
1226 return FALSE;
1227 if ((strcmpi(sb, "updown") == 0) || (strcmpi(sb, "downup") == 0)
1228 || (strcmpi(sb, "up") == 0) || (strcmpi(sb, "down") == 0)) {
1229 down_up = TRUE;
1230 } else if ((strcmpi(sb, "changed") == 0)
1231 && (fld == BL_TITLE || fld == BL_ALIGN || fld == BL_LEVELDESC
1232 || fld == BL_CONDITION)) {
1233 changed = TRUE; /* changed is only thing allowed */
1234 } else {
1235 tmp = sb;
1236 while (*tmp) {
1237 if (*tmp == '%') {
1238 *tmp = '\0';
1239 percent = TRUE;
1240 break;
1241 } else if (!index("0123456789", *tmp))
1242 return FALSE;
1243 tmp++;
1245 if (strlen(sb) > 0) {
1246 dt = blstats[0][idx].anytype;
1247 if (percent)
1248 dt = ANY_INT;
1249 (void) s_to_anything(&threshold, sb, dt);
1250 } else
1251 return FALSE;
1252 if (percent && (threshold.a_int < 1 || threshold.a_int > 100))
1253 return FALSE;
1254 if (!threshold.a_void && (strcmp(sb, "0") != 0))
1255 return FALSE;
1258 /* actions */
1259 for (i = 0; i < 2; ++i) {
1260 how = !i ? sc : sd;
1261 if (!how) {
1262 if (!i)
1263 return FALSE;
1264 break; /* sc is mandatory; sd is not */
1267 if (strcmpi(how, "bold") == 0) {
1268 bold[i] = TRUE;
1269 } else if (strcmpi(how, "inverse") == 0) {
1270 inverse[i] = TRUE;
1271 } else if (strcmpi(how, "normal") == 0) {
1272 normal[i] = TRUE;
1273 } else {
1274 int k = match_str2clr(how);
1276 if (k >= CLR_MAX)
1277 return FALSE;
1278 coloridx[i] = k;
1282 /* Assign the values */
1284 for (i = 0; i < 2; ++i) {
1285 if (inverse[i])
1286 status_hilites[idx].coloridx[i] = BL_HILITE_INVERSE;
1287 else if (bold[i])
1288 status_hilites[idx].coloridx[i] = BL_HILITE_BOLD;
1289 else if (coloridx[i])
1290 status_hilites[idx].coloridx[i] = coloridx[i];
1291 else
1292 status_hilites[idx].coloridx[i] = BL_HILITE_NONE;
1295 if (percent)
1296 status_hilites[idx].behavior = BL_TH_VAL_PERCENTAGE;
1297 else if (down_up)
1298 status_hilites[idx].behavior = BL_TH_UPDOWN;
1299 else if (threshold.a_void)
1300 status_hilites[idx].behavior = BL_TH_VAL_ABSOLUTE;
1301 else
1302 status_hilites[idx].behavior = BL_TH_NONE;
1304 if (status_hilites[idx].behavior != BL_TH_NONE) {
1305 status_hilites[idx].threshold = threshold;
1306 status_hilites[idx].set = TRUE;
1308 status_hilites[idx].anytype = dt;
1310 /* Now finally, we notify the window port */
1311 if (!from_configfile)
1312 status_threshold(idx, status_hilites[idx].anytype,
1313 status_hilites[idx].threshold,
1314 status_hilites[idx].behavior,
1315 status_hilites[idx].coloridx[0],
1316 status_hilites[idx].coloridx[1]);
1318 return TRUE;
1321 /*ARGUSED*/
1322 void
1323 status_notify_windowport(all)
1324 boolean all UNUSED;
1326 int idx;
1327 anything it;
1329 it = zeroany;
1330 for (idx = 0; idx < MAXBLSTATS; ++idx) {
1331 if (status_hilites[idx].set)
1332 status_threshold(idx, status_hilites[idx].anytype,
1333 status_hilites[idx].threshold,
1334 status_hilites[idx].behavior,
1335 status_hilites[idx].coloridx[0],
1336 status_hilites[idx].coloridx[1]);
1337 else
1338 status_threshold(idx, blstats[0][idx].anytype, it, 0, 0, 0);
1344 * get_status_hilites
1346 * Returns a string containing all the status hilites in the
1347 * same format that is used to specify a status hilite preference
1348 * in the config file.
1350 char *
1351 get_status_hilites(buf, bufsiz)
1352 char *buf;
1353 int bufsiz;
1355 int i, j, k, coloridx;
1356 const char *text = (char *) 0;
1357 char tmp[BUFSZ], colorname[BUFSZ];
1358 boolean val_percentage, val_absolute, up_down;
1359 boolean added_one = FALSE;
1361 if (!buf)
1362 return (char *) 0;
1363 *buf = '\0';
1365 bufsiz--; /* required trailing null */
1366 for (i = 0; i < MAXBLSTATS; ++i) {
1367 val_percentage = val_absolute = up_down = FALSE;
1368 if (status_hilites[i].set) {
1369 if (!added_one)
1370 added_one = TRUE;
1371 else {
1372 Strcat(buf, " ");
1373 bufsiz--;
1375 k = strlen(fieldids[i].fieldname);
1376 if (k < bufsiz) {
1377 Strcat(buf, fieldids[i].fieldname);
1378 bufsiz -= k;
1380 if (bufsiz > 1) {
1381 Strcat(buf, "/");
1382 bufsiz--;
1384 if (status_hilites[i].behavior == BL_TH_VAL_PERCENTAGE) {
1385 val_percentage = TRUE;
1386 } else if (status_hilites[i].behavior == BL_TH_VAL_ABSOLUTE) {
1387 val_absolute = TRUE;
1388 } else if (status_hilites[i].behavior == BL_TH_UPDOWN) {
1389 up_down = TRUE;
1390 text = "updown";
1393 if (status_hilites[i].behavior != BL_TH_UPDOWN) {
1394 anything_to_s(tmp, &status_hilites[i].threshold,
1395 blstats[0][i].anytype);
1396 text = tmp;
1398 k = strlen(text);
1399 if (k < (bufsiz - 1)) {
1400 Strcat(buf, text);
1401 if (val_percentage)
1402 Strcat(buf, "%"), k++;
1403 bufsiz -= k;
1405 for (j = 0; j < 2; ++j) {
1406 if (bufsiz > 1) {
1407 Strcat(buf, "/");
1408 bufsiz--;
1410 coloridx = status_hilites[i].coloridx[j];
1411 if (coloridx < 0) {
1412 if (coloridx == BL_HILITE_BOLD)
1413 text = "bold";
1414 else if (coloridx == BL_HILITE_INVERSE)
1415 text = "inverse";
1416 else
1417 text = "normal";
1418 } else {
1419 char *blank;
1421 (void) strcpy(colorname, c_obj_colors[coloridx]);
1422 for (blank = index(colorname, ' '); blank;
1423 blank = index(colorname, ' '))
1424 *blank = '-';
1425 text = colorname;
1427 k = strlen(text);
1428 if (k < bufsiz) {
1429 Strcat(buf, text);
1430 bufsiz -= k;
1435 return buf;
1438 STATIC_OVL const char *
1439 clridx_to_s(buf, idx)
1440 char *buf;
1441 int idx;
1443 static const char *a[] = { "bold", "inverse", "normal" };
1444 char* p = 0;
1446 if (buf) {
1447 buf[0] = '\0';
1448 if (idx < 0 && idx >= BL_HILITE_BOLD)
1449 Strcpy(buf, a[idx + 3]);
1450 else if (idx >= 0 && idx < CLR_MAX)
1451 Strcpy(buf, c_obj_colors[idx]);
1452 /* replace spaces with - */
1453 for(p = buf; *p; p++)
1454 if(*p == ' ') *p = '-';
1456 return buf;
1459 boolean
1460 status_hilite_menu()
1462 int i, j, k, pick_cnt, pick_idx, opt_idx;
1463 menu_item *statfield_picks = (menu_item *) 0;
1464 const char *fieldname;
1465 int field_picks[MAXBLSTATS], res;
1466 struct hilite_s hltemp[MAXBLSTATS];
1467 char buf[BUFSZ], thresholdbuf[BUFSZ], below[BUFSZ], above[BUFSZ];
1468 winid tmpwin;
1469 anything any;
1471 tmpwin = create_nhwindow(NHW_MENU);
1472 start_menu(tmpwin);
1473 for (i = 0; i < MAXBLSTATS; i++) {
1474 (void) memset(&hltemp[i], 0, sizeof(struct hilite_s));
1475 fieldname = fieldids[i].fieldname;
1476 any.a_int = i + 1;
1477 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, fieldname,
1478 MENU_UNSELECTED);
1479 field_picks[i] = 0;
1481 end_menu(tmpwin, "Change hilite on which status field(s):");
1482 if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &statfield_picks)) > 0) {
1483 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
1484 opt_idx = statfield_picks[pick_idx].item.a_int - 1;
1485 field_picks[opt_idx] = 1;
1487 free((genericptr_t) statfield_picks);
1488 statfield_picks = (menu_item *) 0;
1490 destroy_nhwindow(tmpwin);
1491 if (pick_cnt < 0)
1492 return FALSE;
1494 for (i = 0; i < MAXBLSTATS; i++) {
1495 if (field_picks[i]) {
1496 menu_item *pick = (menu_item *) 0;
1498 Sprintf(buf, "Threshold behavior options for %s:",
1499 fieldids[i].fieldname);
1500 tmpwin = create_nhwindow(NHW_MENU);
1501 start_menu(tmpwin);
1502 if (i == BL_CONDITION) {
1503 any = zeroany;
1504 any.a_int = BL_TH_CONDITION + 1;
1505 add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE,
1506 "Condition bitmask threshold.", MENU_UNSELECTED);
1508 any = zeroany;
1509 any.a_int = BL_TH_NONE + 1;
1510 add_menu(tmpwin, NO_GLYPH, &any, 'n', 0, ATR_NONE, "None",
1511 MENU_UNSELECTED);
1512 if (i != BL_CONDITION) {
1513 if (blstats[0][i].idxmax > 0) {
1514 any = zeroany;
1515 any.a_int = BL_TH_VAL_PERCENTAGE + 1;
1516 add_menu(tmpwin, NO_GLYPH, &any, 'p', 0, ATR_NONE,
1517 "Percentage threshold.", MENU_UNSELECTED);
1519 any = zeroany;
1520 any.a_int = BL_TH_UPDOWN + 1;
1521 add_menu(tmpwin, NO_GLYPH, &any, 'u', 0, ATR_NONE,
1522 "UpDown threshold.", MENU_UNSELECTED);
1523 any = zeroany;
1524 any.a_int = BL_TH_VAL_ABSOLUTE + 1;
1525 add_menu(tmpwin, NO_GLYPH, &any, 'v', 0, ATR_NONE,
1526 "Value threshold.", MENU_UNSELECTED);
1528 end_menu(tmpwin, buf);
1529 if ((res = select_menu(tmpwin, PICK_ONE, &pick)) > 0) {
1530 hltemp[i].behavior = pick->item.a_int - 1;
1531 free((genericptr_t) pick);
1533 destroy_nhwindow(tmpwin);
1534 if (res < 0)
1535 return FALSE;
1537 if (hltemp[i].behavior == BL_TH_UPDOWN) {
1538 Sprintf(below, "%s decreases", fieldids[i].fieldname);
1539 Sprintf(above, "%s increases", fieldids[i].fieldname);
1540 } else if (hltemp[i].behavior) {
1541 /* Have them enter the threshold*/
1542 Sprintf(
1543 buf, "Set %s threshold to what%s?", fieldids[i].fieldname,
1544 (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE)
1545 ? " percentage"
1546 : (hltemp[i].behavior == BL_TH_CONDITION) ? " mask"
1547 : "");
1548 getlin(buf, thresholdbuf);
1549 if (thresholdbuf[0] == '\033')
1550 return FALSE;
1551 (void) s_to_anything(&hltemp[i].threshold, thresholdbuf,
1552 blstats[0][i].anytype);
1553 if (!hltemp[i].threshold.a_void)
1554 return FALSE;
1556 Sprintf(below, "%s falls below %s%s", fieldids[i].fieldname,
1557 thresholdbuf,
1558 (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE) ? "%"
1559 : "");
1560 Sprintf(above, "%s rises above %s%s", fieldids[i].fieldname,
1561 thresholdbuf,
1562 (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE) ? "%"
1563 : "");
1565 for (j = 0; j < 2 && (hltemp[i].behavior != BL_TH_NONE); ++j) {
1566 char prompt[QBUFSZ];
1567 /* j == 0 below, j == 1 above */
1568 menu_item *pick2 = (menu_item *) 0;
1570 Sprintf(prompt, "Display how when %s?", j ? above : below);
1571 tmpwin = create_nhwindow(NHW_MENU);
1572 start_menu(tmpwin);
1573 for (k = -3; k < CLR_MAX; ++k) {
1574 /* if (k == -1) continue; */
1575 any = zeroany;
1576 any.a_int = (k >= 0) ? k + 1 : k;
1577 if (k > 0)
1578 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1579 c_obj_colors[k], MENU_UNSELECTED);
1580 else if (k == -1)
1581 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1582 "normal", MENU_UNSELECTED);
1583 else if (k == -2)
1584 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1585 "inverse", MENU_UNSELECTED);
1586 else if (k == -3)
1587 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1588 "bold", MENU_UNSELECTED);
1590 end_menu(tmpwin, prompt);
1591 if ((res = select_menu(tmpwin, PICK_ONE, &pick2)) > 0) {
1592 hltemp[i].coloridx[j] = (pick2->item.a_char > 0)
1593 ? pick2->item.a_int - 1
1594 : pick2->item.a_int;
1595 free((genericptr_t) pick2);
1597 destroy_nhwindow(tmpwin);
1598 if (res < 0)
1599 return FALSE;
1603 buf[0] = '\0';
1604 for (i = 0; i < MAXBLSTATS; i++) {
1605 if (field_picks[i]) {
1606 Sprintf(eos(buf), "%s/%s%s/", fieldids[i].fieldname,
1607 (hltemp[i].behavior == BL_TH_UPDOWN)
1608 ? "updown"
1609 : anything_to_s(thresholdbuf, &hltemp[i].threshold,
1610 blstats[0][i].anytype),
1611 (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE) ? "%" : "");
1612 /* borrow thresholdbuf for use with these last two */
1613 Sprintf(eos(buf), "%s/",
1614 clridx_to_s(thresholdbuf, hltemp[i].coloridx[0]));
1615 Sprintf(eos(buf), "%s ",
1616 clridx_to_s(thresholdbuf, hltemp[i].coloridx[1]));
1619 return set_status_hilites(buf, FALSE);
1621 #endif /*STATUS_HILITES*/
1622 #endif /*STATUS_VIA_WINDOWPORT*/
1624 /*botl.c*/