option parsing buffer overflow vulnerability
[aNetHack.git] / src / botl.c
blobfd1880cf50b98e85168f940fd758fc8bdb4a9e40
1 /* NetHack 3.6 botl.c $NHDT-Date: 1452660188 2016/01/13 04:43:08 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.70 $ */
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 #ifndef STATUS_VIA_WINDOWPORT
18 STATIC_DCL void NDECL(bot1);
19 STATIC_DCL void NDECL(bot2);
21 STATIC_OVL void
22 bot1()
24 char newbot1[MAXCO];
25 register char *nb;
26 register int i, j;
28 Strcpy(newbot1, plname);
29 if ('a' <= newbot1[0] && newbot1[0] <= 'z')
30 newbot1[0] += 'A' - 'a';
31 newbot1[10] = 0;
32 Sprintf(nb = eos(newbot1), " the ");
34 if (Upolyd) {
35 char mbot[BUFSZ];
36 int k = 0;
38 Strcpy(mbot, mons[u.umonnum].mname);
39 while (mbot[k] != 0) {
40 if ((k == 0 || (k > 0 && mbot[k - 1] == ' ')) && 'a' <= mbot[k]
41 && mbot[k] <= 'z')
42 mbot[k] += 'A' - 'a';
43 k++;
45 Strcpy(nb = eos(nb), mbot);
46 } else
47 Strcpy(nb = eos(nb), rank());
49 Sprintf(nb = eos(nb), " ");
50 i = mrank_sz + 15;
51 j = (int) ((nb + 2) - newbot1); /* strlen(newbot1) but less computation */
52 if ((i - j) > 0)
53 Sprintf(nb = eos(nb), "%*s", i - j, " "); /* pad with spaces */
54 if (ACURR(A_STR) > 18) {
55 if (ACURR(A_STR) > STR18(100))
56 Sprintf(nb = eos(nb), "St:%2d ", ACURR(A_STR) - 100);
57 else if (ACURR(A_STR) < STR18(100))
58 Sprintf(nb = eos(nb), "St:18/%02d ", ACURR(A_STR) - 18);
59 else
60 Sprintf(nb = eos(nb), "St:18/** ");
61 } else
62 Sprintf(nb = eos(nb), "St:%-1d ", ACURR(A_STR));
63 Sprintf(nb = eos(nb), "Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d",
64 ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS),
65 ACURR(A_CHA));
66 Sprintf(nb = eos(nb),
67 (u.ualign.type == A_CHAOTIC)
68 ? " Chaotic"
69 : (u.ualign.type == A_NEUTRAL) ? " Neutral" : " Lawful");
70 #ifdef SCORE_ON_BOTL
71 if (flags.showscore)
72 Sprintf(nb = eos(nb), " S:%ld", botl_score());
73 #endif
74 curs(WIN_STATUS, 1, 0);
75 putstr(WIN_STATUS, 0, newbot1);
78 STATIC_OVL void
79 bot2()
81 char newbot2[MAXCO], /* MAXCO: botl.h */
82 /* dungeon location (and gold), hero health (HP, PW, AC),
83 experience (HD if poly'd, else Exp level and maybe Exp points),
84 time (in moves), varying number of status conditions */
85 dloc[QBUFSZ], hlth[QBUFSZ], expr[QBUFSZ], tmmv[QBUFSZ], cond[QBUFSZ];
86 register char *nb;
87 unsigned dln, dx, hln, xln, tln, cln;
88 int hp, hpmax, cap;
89 long money;
92 * Various min(x,9999)'s are to avoid having excessive values
93 * violate the field width assumptions in botl.h and should not
94 * impact normal play. Particularly 64-bit long for gold which
95 * could require many more digits if someone figures out a way
96 * to get and carry a really large (or negative) amount of it.
97 * Turn counter is also long, but we'll risk that.
100 /* dungeon location plus gold */
101 (void) describe_level(dloc); /* includes at least one trailing space */
102 if ((money = money_cnt(invent)) < 0L)
103 money = 0L; /* ought to issue impossible() and then discard gold */
104 Sprintf(eos(dloc), "%s:%-2ld", /* strongest hero can lift ~300000 gold */
105 encglyph(objnum_to_glyph(GOLD_PIECE)), min(money, 999999L));
106 dln = strlen(dloc);
107 /* '$' encoded as \GXXXXNNNN is 9 chars longer than display will need */
108 dx = strstri(dloc, "\\G") ? 9 : 0;
110 /* health and armor class (has trailing space for AC 0..9) */
111 hp = Upolyd ? u.mh : u.uhp;
112 hpmax = Upolyd ? u.mhmax : u.uhpmax;
113 if (hp < 0)
114 hp = 0;
115 Sprintf(hlth, "HP:%d(%d) Pw:%d(%d) AC:%-2d",
116 min(hp, 9999), min(hpmax, 9999),
117 min(u.uen, 9999), min(u.uenmax, 9999), u.uac);
118 hln = strlen(hlth);
120 /* experience */
121 if (Upolyd)
122 Sprintf(expr, "HD:%d", mons[u.umonnum].mlevel);
123 else if (flags.showexp)
124 Sprintf(expr, "Xp:%u/%-1ld", u.ulevel, u.uexp);
125 else
126 Sprintf(expr, "Exp:%u", u.ulevel);
127 xln = strlen(expr);
129 /* time/move counter */
130 if (flags.time)
131 Sprintf(tmmv, "T:%ld", moves);
132 else
133 tmmv[0] = '\0';
134 tln = strlen(tmmv);
136 /* status conditions; worst ones first */
137 cond[0] = '\0'; /* once non-empty, cond will have a leading space */
138 nb = cond;
140 * Stoned, Slimed, Strangled, and both types of Sick are all fatal
141 * unless remedied before timeout expires. Should we order them by
142 * shortest time left? [Probably not worth the effort, since it's
143 * unusual for more than one of them to apply at a time.]
145 if (Stoned)
146 Strcpy(nb = eos(nb), " Stone");
147 if (Slimed)
148 Strcpy(nb = eos(nb), " Slime");
149 if (Strangled)
150 Strcpy(nb = eos(nb), " Strngl");
151 if (Sick) {
152 if (u.usick_type & SICK_VOMITABLE)
153 Strcpy(nb = eos(nb), " FoodPois");
154 if (u.usick_type & SICK_NONVOMITABLE)
155 Strcpy(nb = eos(nb), " TermIll");
157 if (u.uhs != NOT_HUNGRY)
158 Sprintf(nb = eos(nb), " %s", hu_stat[u.uhs]);
159 if ((cap = near_capacity()) > UNENCUMBERED)
160 Sprintf(nb = eos(nb), " %s", enc_stat[cap]);
161 if (Blind)
162 Strcpy(nb = eos(nb), " Blind");
163 if (Deaf)
164 Strcpy(nb = eos(nb), " Deaf");
165 if (Stunned)
166 Strcpy(nb = eos(nb), " Stun");
167 if (Confusion)
168 Strcpy(nb = eos(nb), " Conf");
169 if (Hallucination)
170 Strcpy(nb = eos(nb), " Hallu");
171 /* levitation and flying are mutually exclusive; riding is not */
172 if (Levitation)
173 Strcpy(nb = eos(nb), " Lev");
174 if (Flying)
175 Strcpy(nb = eos(nb), " Fly");
176 if (u.usteed)
177 Strcpy(nb = eos(nb), " Ride");
178 cln = strlen(cond);
181 * Put the pieces together. If they all fit, keep the traditional
182 * sequence. Otherwise, move least important parts to the end in
183 * case the interface side of things has to truncate. Note that
184 * dloc[] contains '$' encoded in ten character sequence \GXXXXNNNN
185 * so we want to test its display length rather than buffer length.
187 * We don't have an actual display limit here, so have to go by the
188 * width of the map. Since we're reordering rather than truncating,
189 * wider displays can still show wider status than the map if the
190 * interface supports that.
192 if ((dln - dx) + 1 + hln + 1 + xln + 1 + tln + 1 + cln <= COLNO) {
193 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, expr, tmmv, cond);
194 } else {
195 if (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1 > MAXCO) {
196 panic("bot2: second status line exceeds MAXCO (%u > %d)",
197 (dln + 1 + hln + 1 + xln + 1 + tln + 1 + cln + 1), MAXCO);
198 } else if ((dln - dx) + 1 + hln + 1 + xln + 1 + cln <= COLNO) {
199 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, expr, cond, tmmv);
200 } else if ((dln - dx) + 1 + hln + 1 + cln <= COLNO) {
201 Sprintf(newbot2, "%s %s %s %s %s", dloc, hlth, cond, expr, tmmv);
202 } else {
203 Sprintf(newbot2, "%s %s %s %s %s", hlth, cond, dloc, expr, tmmv);
205 /* only two or three consecutive spaces available to squeeze out */
206 mungspaces(newbot2);
209 curs(WIN_STATUS, 1, 1);
210 putmixed(WIN_STATUS, 0, newbot2);
213 void
214 bot()
216 if (youmonst.data) {
217 bot1();
218 bot2();
220 context.botl = context.botlx = 0;
223 #endif /* !STATUS_VIA_WINDOWPORT */
225 /* convert experience level (1..30) to rank index (0..8) */
227 xlev_to_rank(xlev)
228 int xlev;
230 return (xlev <= 2) ? 0 : (xlev <= 30) ? ((xlev + 2) / 4) : 8;
233 #if 0 /* not currently needed */
234 /* convert rank index (0..8) to experience level (1..30) */
236 rank_to_xlev(rank)
237 int rank;
239 return (rank <= 0) ? 1 : (rank <= 8) ? ((rank * 4) - 2) : 30;
241 #endif
243 const char *
244 rank_of(lev, monnum, female)
245 int lev;
246 short monnum;
247 boolean female;
249 register const struct Role *role;
250 register int i;
252 /* Find the role */
253 for (role = roles; role->name.m; role++)
254 if (monnum == role->malenum || monnum == role->femalenum)
255 break;
256 if (!role->name.m)
257 role = &urole;
259 /* Find the rank */
260 for (i = xlev_to_rank((int) lev); i >= 0; i--) {
261 if (female && role->rank[i].f)
262 return role->rank[i].f;
263 if (role->rank[i].m)
264 return role->rank[i].m;
267 /* Try the role name, instead */
268 if (female && role->name.f)
269 return role->name.f;
270 else if (role->name.m)
271 return role->name.m;
272 return "Player";
275 STATIC_OVL const char *
276 rank()
278 return rank_of(u.ulevel, Role_switch, flags.female);
282 title_to_mon(str, rank_indx, title_length)
283 const char *str;
284 int *rank_indx, *title_length;
286 register int i, j;
288 /* Loop through each of the roles */
289 for (i = 0; roles[i].name.m; i++)
290 for (j = 0; j < 9; j++) {
291 if (roles[i].rank[j].m
292 && !strncmpi(str, roles[i].rank[j].m,
293 strlen(roles[i].rank[j].m))) {
294 if (rank_indx)
295 *rank_indx = j;
296 if (title_length)
297 *title_length = strlen(roles[i].rank[j].m);
298 return roles[i].malenum;
300 if (roles[i].rank[j].f
301 && !strncmpi(str, roles[i].rank[j].f,
302 strlen(roles[i].rank[j].f))) {
303 if (rank_indx)
304 *rank_indx = j;
305 if (title_length)
306 *title_length = strlen(roles[i].rank[j].f);
307 return (roles[i].femalenum != NON_PM) ? roles[i].femalenum
308 : roles[i].malenum;
311 return NON_PM;
314 void
315 max_rank_sz()
317 register int i, r, maxr = 0;
318 for (i = 0; i < 9; i++) {
319 if (urole.rank[i].m && (r = strlen(urole.rank[i].m)) > maxr)
320 maxr = r;
321 if (urole.rank[i].f && (r = strlen(urole.rank[i].f)) > maxr)
322 maxr = r;
324 mrank_sz = maxr;
325 return;
328 #ifdef SCORE_ON_BOTL
329 long
330 botl_score()
332 long deepest = deepest_lev_reached(FALSE);
333 long utotal;
335 utotal = money_cnt(invent) + hidden_gold();
336 if ((utotal -= u.umoney0) < 0L)
337 utotal = 0L;
338 utotal += u.urexp + (50 * (deepest - 1))
339 + (deepest > 30 ? 10000 : deepest > 20 ? 1000 * (deepest - 20) : 0);
340 if (utotal < u.urexp)
341 utotal = LONG_MAX; /* wrap around */
342 return utotal;
344 #endif /* SCORE_ON_BOTL */
346 /* provide the name of the current level for display by various ports */
348 describe_level(buf)
349 char *buf;
351 int ret = 1;
353 /* TODO: Add in dungeon name */
354 if (Is_knox(&u.uz))
355 Sprintf(buf, "%s ", dungeons[u.uz.dnum].dname);
356 else if (In_quest(&u.uz))
357 Sprintf(buf, "Home %d ", dunlev(&u.uz));
358 else if (In_endgame(&u.uz))
359 Sprintf(buf, Is_astralevel(&u.uz) ? "Astral Plane " : "End Game ");
360 else {
361 /* ports with more room may expand this one */
362 Sprintf(buf, "Dlvl:%-2d ", depth(&u.uz));
363 ret = 0;
365 return ret;
368 #ifdef STATUS_VIA_WINDOWPORT
369 /* =======================================================================*/
371 /* structure that tracks the status details in the core */
372 struct istat_s {
373 long time;
374 unsigned anytype;
375 anything a;
376 char *val;
377 int valwidth;
378 enum statusfields idxmax;
379 enum statusfields fld;
383 STATIC_DCL void NDECL(init_blstats);
384 STATIC_DCL char *FDECL(anything_to_s, (char *, anything *, int));
385 STATIC_OVL int FDECL(percentage, (struct istat_s *, struct istat_s *));
386 STATIC_OVL int FDECL(compare_blstats, (struct istat_s *, struct istat_s *));
387 #ifdef STATUS_HILITES
388 STATIC_DCL void FDECL(s_to_anything, (anything *, char *, int));
389 STATIC_DCL boolean FDECL(assign_hilite, (char *, char *, char *, char *,
390 BOOLEAN_P));
391 STATIC_DCL const char *FDECL(clridx_to_s, (char *, int));
392 #endif
394 /* If entries are added to this, botl.h will require updating too */
395 STATIC_DCL struct istat_s initblstats[MAXBLSTATS] = {
396 { 0L, ANY_STR, { (genericptr_t) 0 }, (char *) 0, 80, 0, BL_TITLE},
397 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_STR},
398 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_DX},
399 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_CO},
400 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_IN},
401 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_WI},
402 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_CH},
403 { 0L, ANY_STR, { (genericptr_t) 0 }, (char *) 0, 40, 0, BL_ALIGN},
404 { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_SCORE},
405 { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_CAP},
406 { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 30, 0, BL_GOLD},
407 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, BL_ENEMAX, BL_ENE},
408 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_ENEMAX},
409 { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_XP},
410 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_AC},
411 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_HD},
412 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_TIME},
413 { 0L, ANY_UINT, { (genericptr_t) 0 }, (char *) 0, 40, 0, BL_HUNGER},
414 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, BL_HPMAX, BL_HP},
415 { 0L, ANY_INT, { (genericptr_t) 0 }, (char *) 0, 10, 0, BL_HPMAX},
416 { 0L, ANY_STR, { (genericptr_t) 0 }, (char *) 0, 80, 0, BL_LEVELDESC},
417 { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20, 0, BL_EXP},
418 { 0L, ANY_MASK32,
419 { (genericptr_t) 0 }, (char *) 0, 0, 0, BL_CONDITION}
422 struct istat_s blstats[2][MAXBLSTATS];
423 static boolean blinit = FALSE, update_all = FALSE;
425 void
426 bot()
428 char buf[BUFSZ];
429 register char *nb;
430 static int idx = 0, idx_p, idxmax;
431 unsigned anytype;
432 long money;
433 int i, pc, chg, cap;
434 struct istat_s *curr, *prev;
435 boolean valset[MAXBLSTATS], chgval = FALSE, updated = FALSE;
437 if (!blinit)
438 panic("bot before init.");
439 if (!youmonst.data) {
440 context.botl = context.botlx = 0;
441 update_all = FALSE;
442 return;
445 idx_p = idx;
446 idx = 1 - idx; /* 0 -> 1, 1 -> 0 */
448 /* clear the "value set" indicators */
449 (void) memset((genericptr_t) valset, 0, MAXBLSTATS * sizeof (boolean));
452 * Note: min(x,9999) - we enforce the same maximum on hp, maxhp,
453 * pw, maxpw, and gold as basic status formatting so that the two
454 * modes of status display don't produce different information.
458 * Player name and title.
460 Strcpy(nb = buf, plname);
461 nb[0] = highc(nb[0]);
462 nb[10] = '\0';
463 Sprintf(nb = eos(nb), " the ");
464 if (Upolyd) {
465 for (i = 0, nb = strcpy(eos(nb), mons[u.umonnum].mname); nb[i]; i++)
466 if (i == 0 || nb[i - 1] == ' ')
467 nb[i] = highc(nb[i]);
468 } else
469 Strcpy(nb = eos(nb), rank());
470 Sprintf(blstats[idx][BL_TITLE].val, "%-29s", buf);
471 valset[BL_TITLE] = TRUE; /* indicate val already set */
473 /* Strength */
474 buf[0] = '\0';
475 blstats[idx][BL_STR].a.a_int = ACURR(A_STR);
476 if (ACURR(A_STR) > 18) {
477 if (ACURR(A_STR) > STR18(100))
478 Sprintf(buf, "%2d", ACURR(A_STR) - 100);
479 else if (ACURR(A_STR) < STR18(100))
480 Sprintf(buf, "18/%02d", ACURR(A_STR) - 18);
481 else
482 Sprintf(buf, "18/**");
483 } else
484 Sprintf(buf, "%-1d", ACURR(A_STR));
485 Strcpy(blstats[idx][BL_STR].val, buf);
486 valset[BL_STR] = TRUE; /* indicate val already set */
488 /* Dexterity, constitution, intelligence, wisdom, charisma. */
489 blstats[idx][BL_DX].a.a_int = ACURR(A_DEX);
490 blstats[idx][BL_CO].a.a_int = ACURR(A_CON);
491 blstats[idx][BL_IN].a.a_int = ACURR(A_INT);
492 blstats[idx][BL_WI].a.a_int = ACURR(A_WIS);
493 blstats[idx][BL_CH].a.a_int = ACURR(A_CHA);
495 /* Alignment */
496 Strcpy(blstats[idx][BL_ALIGN].val, (u.ualign.type == A_CHAOTIC)
497 ? "Chaotic"
498 : (u.ualign.type == A_NEUTRAL)
499 ? "Neutral"
500 : "Lawful");
502 /* Score */
503 blstats[idx][BL_SCORE].a.a_long =
504 #ifdef SCORE_ON_BOTL
505 botl_score()
506 #else
508 #endif
511 /* Hit points */
512 i = Upolyd ? u.mh : u.uhp;
513 if (i < 0)
514 i = 0;
515 blstats[idx][BL_HP].a.a_int = min(i, 9999);
516 i = Upolyd ? u.mhmax : u.uhpmax;
517 blstats[idx][BL_HPMAX].a.a_int = min(i, 9999);
519 /* Dungeon level. */
520 (void) describe_level(blstats[idx][BL_LEVELDESC].val);
521 valset[BL_LEVELDESC] = TRUE; /* indicate val already set */
523 /* Gold */
524 if ((money = money_cnt(invent)) < 0L)
525 money = 0L; /* ought to issue impossible() and then discard gold */
526 blstats[idx][BL_GOLD].a.a_long = min(money, 999999L);
528 * The tty port needs to display the current symbol for gold
529 * as a field header, so to accommodate that we pass gold with
530 * that already included. If a window port needs to use the text
531 * gold amount without the leading "$:" the port will have to
532 * skip past ':' to the value pointer it was passed in status_update()
533 * for the BL_GOLD case.
535 * Another quirk of BL_GOLD is that the field display may have
536 * changed if a new symbol set was loaded, or we entered or left
537 * the rogue level.
539 * The currency prefix is encoded as ten character \GXXXXNNNN
540 * sequence.
542 Sprintf(blstats[idx][BL_GOLD].val, "%s:%ld",
543 encglyph(objnum_to_glyph(GOLD_PIECE)),
544 blstats[idx][BL_GOLD].a.a_long);
545 valset[BL_GOLD] = TRUE; /* indicate val already set */
547 /* Power (magical energy) */
548 blstats[idx][BL_ENE].a.a_int = min(u.uen, 9999);
549 blstats[idx][BL_ENEMAX].a.a_int = min(u.uenmax, 9999);
551 /* Armor class */
552 blstats[idx][BL_AC].a.a_int = u.uac;
554 /* Monster level (if Upolyd) */
555 blstats[idx][BL_HD].a.a_int = Upolyd ? mons[u.umonnum].mlevel : 0;
557 /* Experience */
558 blstats[idx][BL_XP].a.a_int = u.ulevel;
559 blstats[idx][BL_EXP].a.a_int = u.uexp;
561 /* Time (moves) */
562 blstats[idx][BL_TIME].a.a_long = moves;
564 /* Hunger */
565 blstats[idx][BL_HUNGER].a.a_uint = u.uhs;
566 Strcpy(blstats[idx][BL_HUNGER].val,
567 (u.uhs != NOT_HUNGRY) ? hu_stat[u.uhs] : "");
568 valset[BL_HUNGER] = TRUE;
570 /* Carrying capacity */
571 cap = near_capacity();
572 blstats[idx][BL_CAP].a.a_int = cap;
573 Strcpy(blstats[idx][BL_CAP].val,
574 (cap > UNENCUMBERED) ? enc_stat[cap] : "");
575 valset[BL_CAP] = TRUE;
577 /* Conditions */
578 blstats[idx][BL_CONDITION].a.a_ulong = 0L;
579 if (Stoned)
580 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STONE;
581 if (Slimed)
582 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_SLIME;
583 if (Strangled)
584 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STRNGL;
585 if (Sick && (u.usick_type & SICK_VOMITABLE) != 0)
586 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FOODPOIS;
587 if (Sick && (u.usick_type & SICK_NONVOMITABLE) != 0)
588 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_TERMILL;
590 * basic formatting puts hunger status and encumbrance here
592 if (Blind)
593 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_BLIND;
594 if (Deaf)
595 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_DEAF;
596 if (Stunned)
597 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STUN;
598 if (Confusion)
599 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_CONF;
600 if (Hallucination)
601 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_HALLU;
602 /* levitation and flying are mututally exclusive */
603 if (Levitation)
604 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_LEV;
605 if (Flying)
606 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FLY;
607 if (u.usteed)
608 blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_RIDE;
611 * Now pass the changed values to window port.
613 for (i = 0; i < MAXBLSTATS; i++) {
614 if (((i == BL_SCORE) && !flags.showscore)
615 || ((i == BL_EXP) && !flags.showexp)
616 || ((i == BL_TIME) && !flags.time)
617 || ((i == BL_HD) && !Upolyd)
618 || ((i == BL_XP || i == BL_EXP) && Upolyd))
619 continue;
620 anytype = blstats[idx][i].anytype;
621 curr = &blstats[idx][i];
622 prev = &blstats[idx_p][i];
623 chg = 0;
624 if (update_all
625 || ((chg = compare_blstats(prev, curr)) != 0)
626 || ((chgval = (valset[i]
627 && strcmp(blstats[idx][i].val,
628 blstats[idx_p][i].val))) != 0)) {
629 idxmax = blstats[idx][i].idxmax;
630 pc = (idxmax) ? percentage(curr, &blstats[idx][idxmax]) : 0;
631 if (!valset[i])
632 (void) anything_to_s(curr->val, &curr->a, anytype);
633 if (anytype != ANY_MASK32) {
634 status_update(i, (genericptr_t) curr->val,
635 valset[i] ? chgval : chg, pc);
636 } else {
637 /* send pointer to mask */
638 status_update(i, (genericptr_t) &curr->a.a_ulong, chg, 0);
640 updated = TRUE;
644 * It is possible to get here, with nothing having been pushed
645 * to the window port, when none of the info has changed. In that
646 * case, we need to force a call to status_update() when
647 * context.botlx is set. The tty port in particular has a problem
648 * if that isn't done, since it sets context.botlx when a menu or
649 * text display obliterates the status line.
651 * To work around it, we call status_update() with fictitious
652 * index of BL_FLUSH (-1).
654 if ((context.botlx && !updated)
655 || windowprocs.win_status_update == genl_status_update)
656 status_update(BL_FLUSH, (genericptr_t) 0, 0, 0);
658 context.botl = context.botlx = 0;
659 update_all = FALSE;
662 void
663 status_initialize(reassessment)
664 boolean
665 reassessment; /* TRUE = just reassess fields w/o other initialization*/
667 int i;
668 const char *fieldfmt = (const char *) 0;
669 const char *fieldname = (const char *) 0;
671 if (!reassessment) {
672 init_blstats();
673 (*windowprocs.win_status_init)();
674 blinit = TRUE;
675 #ifdef STATUS_HILITES
676 status_notify_windowport(TRUE);
677 #endif
679 for (i = 0; i < MAXBLSTATS; ++i) {
680 enum statusfields fld = initblstats[i].fld;
682 switch (fld) {
683 case BL_TITLE:
684 fieldfmt = "%s";
685 fieldname = "title";
686 status_enablefield(fld, fieldname, fieldfmt, TRUE);
687 break;
688 case BL_STR:
689 fieldfmt = " St:%s";
690 fieldname = "strength";
691 status_enablefield(fld, fieldname, fieldfmt, TRUE);
692 break;
693 case BL_DX:
694 fieldfmt = " Dx:%s";
695 fieldname = "dexterity";
696 status_enablefield(fld, fieldname, fieldfmt, TRUE);
697 break;
698 case BL_CO:
699 fieldfmt = " Co:%s";
700 fieldname = "constitution";
701 status_enablefield(fld, fieldname, fieldfmt, TRUE);
702 break;
703 case BL_IN:
704 fieldfmt = " In:%s";
705 fieldname = "intelligence";
706 status_enablefield(fld, fieldname, fieldfmt, TRUE);
707 break;
708 case BL_WI:
709 fieldfmt = " Wi:%s";
710 fieldname = "wisdom";
711 status_enablefield(fld, fieldname, fieldfmt, TRUE);
712 break;
713 case BL_CH:
714 fieldfmt = " Ch:%s";
715 fieldname = "charisma";
716 status_enablefield(fld, fieldname, fieldfmt, TRUE);
717 break;
718 case BL_ALIGN:
719 fieldfmt = " %s";
720 fieldname = "alignment";
721 status_enablefield(fld, fieldname, fieldfmt, TRUE);
722 break;
723 case BL_SCORE:
724 fieldfmt = " S:%s";
725 fieldname = "score";
726 status_enablefield(fld, fieldname, fieldfmt,
727 (!flags.showscore) ? FALSE : TRUE);
728 break;
729 case BL_CAP:
730 fieldfmt = " %s";
731 fieldname = "carrying-capacity";
732 status_enablefield(fld, fieldname, fieldfmt, TRUE);
733 break;
734 case BL_GOLD:
735 fieldfmt = " %s";
736 fieldname = "gold";
737 status_enablefield(fld, fieldname, fieldfmt, TRUE);
738 break;
739 case BL_ENE:
740 fieldfmt = " Pw:%s";
741 fieldname = "power";
742 status_enablefield(fld, fieldname, fieldfmt, TRUE);
743 break;
744 case BL_ENEMAX:
745 fieldfmt = "(%s)";
746 fieldname = "power-max";
747 status_enablefield(fld, fieldname, fieldfmt, TRUE);
748 break;
749 case BL_XP:
750 fieldfmt = " Xp:%s";
751 fieldname = "experience-level";
752 status_enablefield(fld, fieldname, fieldfmt,
753 (Upolyd) ? FALSE : TRUE);
754 break;
755 case BL_AC:
756 fieldfmt = " AC:%s";
757 fieldname = "armor-class";
758 status_enablefield(fld, fieldname, fieldfmt, TRUE);
759 break;
760 case BL_HD:
761 fieldfmt = " HD:%s";
762 fieldname = "HD";
763 status_enablefield(fld, fieldname, fieldfmt,
764 (!Upolyd) ? FALSE : TRUE);
765 break;
766 case BL_TIME:
767 fieldfmt = " T:%s";
768 fieldname = "time";
769 status_enablefield(fld, fieldname, fieldfmt,
770 (!flags.time) ? FALSE : TRUE);
771 break;
772 case BL_HUNGER:
773 fieldfmt = " %s";
774 fieldname = "hunger";
775 status_enablefield(fld, fieldname, fieldfmt, TRUE);
776 break;
777 case BL_HP:
778 fieldfmt = " HP:%s";
779 fieldname = "hitpoints";
780 status_enablefield(fld, fieldname, fieldfmt, TRUE);
781 break;
782 case BL_HPMAX:
783 fieldfmt = "(%s)";
784 fieldname = "hitpoint-max";
785 status_enablefield(fld, fieldname, fieldfmt, TRUE);
786 break;
787 case BL_LEVELDESC:
788 fieldfmt = "%s";
789 fieldname = "dungeon-level";
790 status_enablefield(fld, fieldname, fieldfmt, TRUE);
791 break;
792 case BL_EXP:
793 fieldfmt = "/%s";
794 fieldname = "experience";
795 status_enablefield(fld, fieldname, fieldfmt,
796 (!flags.showexp || Upolyd) ? FALSE : TRUE);
797 break;
798 case BL_CONDITION:
799 fieldfmt = "%s";
800 fieldname = "condition";
801 status_enablefield(fld, fieldname, fieldfmt, TRUE);
802 break;
803 case BL_FLUSH:
804 default:
805 break;
808 update_all = TRUE;
811 void
812 status_finish()
814 int i;
816 /* call the window port cleanup routine first */
817 (*windowprocs.win_status_finish)();
819 /* free memory that we alloc'd now */
820 for (i = 0; i < MAXBLSTATS; ++i) {
821 if (blstats[0][i].val)
822 free((genericptr_t) blstats[0][i].val);
823 if (blstats[1][i].val)
824 free((genericptr_t) blstats[1][i].val);
828 STATIC_OVL void
829 init_blstats()
831 static boolean initalready = FALSE;
832 int i, j;
834 if (initalready) {
835 impossible("init_blstats called more than once.");
836 return;
839 initalready = TRUE;
840 for (i = BEFORE; i <= NOW; ++i) {
841 for (j = 0; j < MAXBLSTATS; ++j) {
842 blstats[i][j] = initblstats[j];
843 blstats[i][j].a = zeroany;
844 if (blstats[i][j].valwidth) {
845 blstats[i][j].val = (char *) alloc(blstats[i][j].valwidth);
846 blstats[i][j].val[0] = '\0';
847 } else
848 blstats[i][j].val = (char *) 0;
853 STATIC_OVL char *
854 anything_to_s(buf, a, anytype)
855 char *buf;
856 anything *a;
857 int anytype;
859 if (!buf)
860 return (char *) 0;
862 switch (anytype) {
863 case ANY_ULONG:
864 Sprintf(buf, "%lu", a->a_ulong);
865 break;
866 case ANY_MASK32:
867 Sprintf(buf, "%lx", a->a_ulong);
868 break;
869 case ANY_LONG:
870 Sprintf(buf, "%ld", a->a_long);
871 break;
872 case ANY_INT:
873 Sprintf(buf, "%d", a->a_int);
874 break;
875 case ANY_UINT:
876 Sprintf(buf, "%u", a->a_uint);
877 break;
878 case ANY_IPTR:
879 Sprintf(buf, "%d", *a->a_iptr);
880 break;
881 case ANY_LPTR:
882 Sprintf(buf, "%ld", *a->a_lptr);
883 break;
884 case ANY_ULPTR:
885 Sprintf(buf, "%lu", *a->a_ulptr);
886 break;
887 case ANY_UPTR:
888 Sprintf(buf, "%u", *a->a_uptr);
889 break;
890 case ANY_STR: /* do nothing */
892 break;
893 default:
894 buf[0] = '\0';
896 return buf;
899 #ifdef STATUS_HILITES
901 STATIC_OVL void
902 s_to_anything(a, buf, anytype)
903 anything *a;
904 char *buf;
905 int anytype;
907 if (!buf || !a)
908 return;
910 switch (anytype) {
911 case ANY_LONG:
912 a->a_long = atol(buf);
913 break;
914 case ANY_INT:
915 a->a_int = atoi(buf);
916 break;
917 case ANY_UINT:
918 a->a_uint = (unsigned) atoi(buf);
919 break;
920 case ANY_ULONG:
921 a->a_ulong = (unsigned long) atol(buf);
922 break;
923 case ANY_IPTR:
924 if (a->a_iptr)
925 *a->a_iptr = atoi(buf);
926 break;
927 case ANY_UPTR:
928 if (a->a_uptr)
929 *a->a_uptr = (unsigned) atoi(buf);
930 break;
931 case ANY_LPTR:
932 if (a->a_lptr)
933 *a->a_lptr = atol(buf);
934 break;
935 case ANY_ULPTR:
936 if (a->a_ulptr)
937 *a->a_ulptr = (unsigned long) atol(buf);
938 break;
939 case ANY_MASK32:
940 a->a_ulong = (unsigned long) atol(buf);
941 break;
942 default:
943 a->a_void = 0;
944 break;
946 return;
949 #endif
951 STATIC_OVL int
952 compare_blstats(bl1, bl2)
953 struct istat_s *bl1, *bl2;
955 int anytype, result = 0;
957 if (!bl1 || !bl2) {
958 panic("compare_blstat: bad istat pointer %s, %s",
959 fmt_ptr((genericptr_t) bl1), fmt_ptr((genericptr_t) bl2));
962 anytype = bl1->anytype;
963 if ((!bl1->a.a_void || !bl2->a.a_void)
964 && (anytype == ANY_IPTR || anytype == ANY_UPTR || anytype == ANY_LPTR
965 || anytype == ANY_ULPTR)) {
966 panic("compare_blstat: invalid pointer %s, %s",
967 fmt_ptr((genericptr_t) bl1->a.a_void),
968 fmt_ptr((genericptr_t) bl2->a.a_void));
971 switch (anytype) {
972 case ANY_INT:
973 result = (bl1->a.a_int < bl2->a.a_int)
975 : (bl1->a.a_int > bl2->a.a_int) ? -1 : 0;
976 break;
977 case ANY_IPTR:
978 result = (*bl1->a.a_iptr < *bl2->a.a_iptr)
980 : (*bl1->a.a_iptr > *bl2->a.a_iptr) ? -1 : 0;
981 break;
982 case ANY_LONG:
983 result = (bl1->a.a_long < bl2->a.a_long)
985 : (bl1->a.a_long > bl2->a.a_long) ? -1 : 0;
986 break;
987 case ANY_LPTR:
988 result = (*bl1->a.a_lptr < *bl2->a.a_lptr)
990 : (*bl1->a.a_lptr > *bl2->a.a_lptr) ? -1 : 0;
991 break;
992 case ANY_UINT:
993 result = (bl1->a.a_uint < bl2->a.a_uint)
995 : (bl1->a.a_uint > bl2->a.a_uint) ? -1 : 0;
996 break;
997 case ANY_UPTR:
998 result = (*bl1->a.a_uptr < *bl2->a.a_uptr)
1000 : (*bl1->a.a_uptr > *bl2->a.a_uptr) ? -1 : 0;
1001 break;
1002 case ANY_ULONG:
1003 result = (bl1->a.a_ulong < bl2->a.a_ulong)
1005 : (bl1->a.a_ulong > bl2->a.a_ulong) ? -1 : 0;
1006 break;
1007 case ANY_ULPTR:
1008 result = (*bl1->a.a_ulptr < *bl2->a.a_ulptr)
1010 : (*bl1->a.a_ulptr > *bl2->a.a_ulptr) ? -1 : 0;
1011 break;
1012 case ANY_STR:
1013 if (strcmp(bl1->val, bl2->val) == 0)
1014 result = 0;
1015 else
1016 result = 1;
1017 break;
1018 case ANY_MASK32:
1019 if (bl1->a.a_ulong == bl2->a.a_ulong)
1020 result = 0;
1021 else
1022 result = 1;
1023 break;
1024 default:
1025 result = 1;
1027 return result;
1030 STATIC_OVL int
1031 percentage(bl, maxbl)
1032 struct istat_s *bl, *maxbl;
1034 int result = 0;
1035 int anytype;
1037 if (!bl || !maxbl) {
1038 impossible("percentage: bad istat pointer %s, %s",
1039 fmt_ptr((genericptr_t) bl), fmt_ptr((genericptr_t) maxbl));
1040 return 0;
1043 anytype = bl->anytype;
1044 if (maxbl->a.a_void) {
1045 switch (anytype) {
1046 case ANY_INT:
1047 result = ((100 * bl->a.a_int) / maxbl->a.a_int);
1048 break;
1049 case ANY_LONG:
1050 result = (int) ((100L * bl->a.a_long) / maxbl->a.a_long);
1051 break;
1052 case ANY_UINT:
1053 result = (int) ((100U * bl->a.a_uint) / maxbl->a.a_uint);
1054 break;
1055 case ANY_ULONG:
1056 result = (int) ((100UL * bl->a.a_ulong) / maxbl->a.a_ulong);
1057 break;
1058 case ANY_IPTR:
1059 result = ((100 * (*bl->a.a_iptr)) / (*maxbl->a.a_iptr));
1060 break;
1061 case ANY_LPTR:
1062 result = (int) ((100L * (*bl->a.a_lptr)) / (*maxbl->a.a_lptr));
1063 break;
1064 case ANY_UPTR:
1065 result = (int) ((100U * (*bl->a.a_uptr)) / (*maxbl->a.a_uptr));
1066 break;
1067 case ANY_ULPTR:
1068 result = (int) ((100UL * (*bl->a.a_ulptr)) / (*maxbl->a.a_ulptr));
1069 break;
1072 return result;
1076 #ifdef STATUS_HILITES
1078 /****************************************************************************/
1079 /* Core status hiliting support */
1080 /****************************************************************************/
1082 static struct fieldid_t {
1083 const char *fieldname;
1084 enum statusfields fldid;
1085 } fieldids[] = {
1086 {"title", BL_TITLE},
1087 {"strength", BL_STR},
1088 {"dexterity", BL_DX},
1089 {"constitution", BL_CO},
1090 {"intelligence", BL_IN},
1091 {"wisdom", BL_WI},
1092 {"charisma", BL_CH},
1093 {"alignment", BL_ALIGN},
1094 {"score", BL_SCORE},
1095 {"carrying-capacity", BL_CAP},
1096 {"gold", BL_GOLD},
1097 {"power", BL_ENE},
1098 {"power-max", BL_ENEMAX},
1099 {"experience-level", BL_XP},
1100 {"armor-class", BL_AC},
1101 {"HD", BL_HD},
1102 {"time", BL_TIME},
1103 {"hunger", BL_HUNGER},
1104 {"hitpoints", BL_HP},
1105 {"hitpoints-max", BL_HPMAX},
1106 {"dungeon-level", BL_LEVELDESC},
1107 {"experience", BL_EXP},
1108 {"condition", BL_CONDITION},
1111 struct hilite_s {
1112 boolean set;
1113 unsigned anytype;
1114 anything threshold;
1115 int behavior;
1116 int coloridx[2];
1119 struct hilite_s status_hilites[MAXBLSTATS];
1122 * This is the parser for the hilite options
1123 * Example:
1124 * OPTION=hilite_status: hitpoints/10%/red/normal
1126 * set_hilite_status() separates each hilite entry into its 4 component
1127 * strings, then calls assign_hilite() to make the adjustments.
1129 boolean
1130 set_status_hilites(op, from_configfile)
1131 char *op;
1132 boolean from_configfile;
1134 char hsbuf[4][QBUFSZ];
1135 boolean rslt, badopt = FALSE;
1136 int fldnum, num = 0, ccount = 0;
1137 char c;
1139 num = fldnum = 0;
1140 hsbuf[0][0] = hsbuf[1][0] = hsbuf[2][0] = hsbuf[3][0] = '\0';
1141 while (*op && fldnum < 4 && ccount < (QBUFSZ - 2)) {
1142 c = lowc(*op);
1143 if (c == ' ') {
1144 if (fldnum >= 2) {
1145 rslt = assign_hilite(&hsbuf[0][0], &hsbuf[1][0], &hsbuf[2][0],
1146 &hsbuf[3][0], from_configfile);
1147 if (!rslt) {
1148 badopt = TRUE;
1149 break;
1152 hsbuf[0][0] = hsbuf[1][0] = '\0';
1153 hsbuf[2][0] = hsbuf[3][0] = '\0';
1154 fldnum = 0;
1155 ccount = 0;
1156 } else if (c == '/') {
1157 fldnum++;
1158 ccount = 0;
1159 } else {
1160 hsbuf[fldnum][ccount++] = c;
1161 hsbuf[fldnum][ccount] = '\0';
1163 op++;
1165 if (fldnum >= 2 && !badopt) {
1166 rslt = assign_hilite(&hsbuf[0][0], &hsbuf[1][0], &hsbuf[2][0],
1167 &hsbuf[3][0], from_configfile);
1168 if (!rslt)
1169 badopt = TRUE;
1171 if (badopt)
1172 return FALSE;
1173 return TRUE;
1176 void
1177 clear_status_hilites(from_configfile)
1178 boolean from_configfile;
1180 int i;
1181 anything it;
1183 it = zeroany;
1184 for (i = 0; i < MAXBLSTATS; ++i) {
1185 (void) memset((genericptr_t) &status_hilites[i], 0,
1186 sizeof(struct hilite_s));
1187 /* notify window port */
1188 if (!from_configfile)
1189 status_threshold(i, blstats[0][i].anytype, it, 0, 0, 0);
1193 STATIC_OVL boolean
1194 assign_hilite(sa, sb, sc, sd, from_configfile)
1195 char *sa, *sb, *sc, *sd;
1196 boolean from_configfile;
1198 char *tmp, *how;
1199 int i = -1, dt = -1, idx = -1;
1200 int coloridx[2] = { -1, -1 };
1201 boolean inverse[2] = { FALSE, FALSE };
1202 boolean bold[2] = { FALSE, FALSE };
1203 boolean normal[2] = { 0, 0 };
1204 boolean percent = FALSE, down_up = FALSE, changed = FALSE;
1205 anything threshold;
1206 enum statusfields fld = BL_FLUSH;
1207 threshold.a_void = 0;
1209 /* Example:
1210 * hilite_status: hitpoints/10%/red/normal
1213 /* field name to statusfield */
1214 for (i = 0; sa && i < SIZE(fieldids); ++i) {
1215 if (strcmpi(sa, fieldids[i].fieldname) == 0) {
1216 idx = i;
1217 fld = fieldids[i].fldid;
1218 break;
1221 if (idx == -1)
1222 return FALSE;
1223 status_hilites[idx].set = FALSE; /* mark it "unset" */
1225 /* threshold */
1226 if (!sb)
1227 return FALSE;
1228 if ((strcmpi(sb, "updown") == 0) || (strcmpi(sb, "downup") == 0)
1229 || (strcmpi(sb, "up") == 0) || (strcmpi(sb, "down") == 0)) {
1230 down_up = TRUE;
1231 } else if ((strcmpi(sb, "changed") == 0)
1232 && (fld == BL_TITLE || fld == BL_ALIGN || fld == BL_LEVELDESC
1233 || fld == BL_CONDITION)) {
1234 changed = TRUE; /* changed is only thing allowed */
1235 } else {
1236 tmp = sb;
1237 while (*tmp) {
1238 if (*tmp == '%') {
1239 *tmp = '\0';
1240 percent = TRUE;
1241 break;
1242 } else if (!index("0123456789", *tmp))
1243 return FALSE;
1244 tmp++;
1246 if (strlen(sb) > 0) {
1247 dt = blstats[0][idx].anytype;
1248 if (percent)
1249 dt = ANY_INT;
1250 (void) s_to_anything(&threshold, sb, dt);
1251 } else
1252 return FALSE;
1253 if (percent && (threshold.a_int < 1 || threshold.a_int > 100))
1254 return FALSE;
1255 if (!threshold.a_void && (strcmp(sb, "0") != 0))
1256 return FALSE;
1259 /* actions */
1260 for (i = 0; i < 2; ++i) {
1261 how = !i ? sc : sd;
1262 if (!how) {
1263 if (!i)
1264 return FALSE;
1265 break; /* sc is mandatory; sd is not */
1268 if (strcmpi(how, "bold") == 0) {
1269 bold[i] = TRUE;
1270 } else if (strcmpi(how, "inverse") == 0) {
1271 inverse[i] = TRUE;
1272 } else if (strcmpi(how, "normal") == 0) {
1273 normal[i] = TRUE;
1274 } else {
1275 int k = match_str2clr(how);
1277 if (k >= CLR_MAX)
1278 return FALSE;
1279 coloridx[i] = k;
1283 /* Assign the values */
1285 for (i = 0; i < 2; ++i) {
1286 if (inverse[i])
1287 status_hilites[idx].coloridx[i] = BL_HILITE_INVERSE;
1288 else if (bold[i])
1289 status_hilites[idx].coloridx[i] = BL_HILITE_BOLD;
1290 else if (coloridx[i])
1291 status_hilites[idx].coloridx[i] = coloridx[i];
1292 else
1293 status_hilites[idx].coloridx[i] = BL_HILITE_NONE;
1296 if (percent)
1297 status_hilites[idx].behavior = BL_TH_VAL_PERCENTAGE;
1298 else if (down_up)
1299 status_hilites[idx].behavior = BL_TH_UPDOWN;
1300 else if (threshold.a_void)
1301 status_hilites[idx].behavior = BL_TH_VAL_ABSOLUTE;
1302 else
1303 status_hilites[idx].behavior = BL_TH_NONE;
1305 if (status_hilites[idx].behavior != BL_TH_NONE) {
1306 status_hilites[idx].threshold = threshold;
1307 status_hilites[idx].set = TRUE;
1309 status_hilites[idx].anytype = dt;
1311 /* Now finally, we notify the window port */
1312 if (!from_configfile)
1313 status_threshold(idx, status_hilites[idx].anytype,
1314 status_hilites[idx].threshold,
1315 status_hilites[idx].behavior,
1316 status_hilites[idx].coloridx[0],
1317 status_hilites[idx].coloridx[1]);
1319 return TRUE;
1322 void
1323 status_notify_windowport(all)
1324 boolean all;
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*/