OSDN Git Service

patch src/
[jnethack/source.git] / src / botl.c
1 /* NetHack 3.6  botl.c  $NHDT-Date: 1447978683 2015/11/20 00:18:03 $  $NHDT-Branch: master $:$NHDT-Revision: 1.69 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 #include "hack.h"
6 #include <limits.h>
7
8 extern const char *hu_stat[]; /* defined in eat.c */
9
10 #if 0 /*JP*/
11 const char *const enc_stat[] = { "",         "Burdened",  "Stressed",
12                                  "Strained", "Overtaxed", "Overloaded" };
13 #else
14 const char *const enc_stat[] = { "",     "\82æ\82ë\82ß\82«", "\88³\94\97",
15                                  "\8cÀ\8aE", "\89×\8fd",     "\92´\89ß"};
16 #endif
17
18 STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */
19 STATIC_DCL const char *NDECL(rank);
20
21 #ifndef STATUS_VIA_WINDOWPORT
22
23 STATIC_DCL void NDECL(bot1);
24 STATIC_DCL void NDECL(bot2);
25
26 STATIC_OVL void
27 bot1()
28 {
29     char newbot1[MAXCO];
30     register char *nb;
31     register int i, j;
32
33     Strcpy(newbot1, plname);
34     if ('a' <= newbot1[0] && newbot1[0] <= 'z')
35         newbot1[0] += 'A' - 'a';
36 #if 1 /*JP*/
37     if(is_kanji1(newbot1, 9))
38         newbot1[9] = '_';
39 #endif
40     newbot1[10] = 0;
41 /*JP
42     Sprintf(nb = eos(newbot1), " the ");
43 */
44     Sprintf(nb = eos(newbot1)," ");
45
46     if (Upolyd) {
47         char mbot[BUFSZ];
48         int k = 0;
49
50         Strcpy(mbot, mons[u.umonnum].mname);
51         while (mbot[k] != 0) {
52             if ((k == 0 || (k > 0 && mbot[k - 1] == ' ')) && 'a' <= mbot[k]
53                 && mbot[k] <= 'z')
54                 mbot[k] += 'A' - 'a';
55             k++;
56         }
57         Strcpy(nb = eos(nb), mbot);
58     } else
59         Strcpy(nb = eos(nb), rank());
60
61     Sprintf(nb = eos(nb), "  ");
62     i = mrank_sz + 15;
63     j = (int) ((nb + 2) - newbot1); /* strlen(newbot1) but less computation */
64     if ((i - j) > 0)
65         Sprintf(nb = eos(nb), "%*s", i - j, " "); /* pad with spaces */
66     if (ACURR(A_STR) > 18) {
67         if (ACURR(A_STR) > STR18(100))
68 /*JP
69             Sprintf(nb = eos(nb), "St:%2d ", ACURR(A_STR) - 100);
70 */
71             Sprintf(nb = eos(nb), "\8b­:%2d ", ACURR(A_STR) - 100);
72         else if (ACURR(A_STR) < STR18(100))
73 /*JP
74             Sprintf(nb = eos(nb), "St:18/%02d ", ACURR(A_STR) - 18);
75 */
76             Sprintf(nb = eos(nb), "\8b­:18/%02d ", ACURR(A_STR) - 18);
77         else
78 /*JP
79             Sprintf(nb = eos(nb), "St:18/** ");
80 */
81             Sprintf(nb = eos(nb), "\8b­:18/** ");
82     } else
83 /*JP
84         Sprintf(nb = eos(nb), "St:%-1d ", ACURR(A_STR));
85 */
86         Sprintf(nb = eos(nb), "\8b­:%-1d ", ACURR(A_STR));
87 /*JP
88     Sprintf(nb = eos(nb), "Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d",
89 */
90     Sprintf(nb = eos(nb), "\91\81:%-1d \91Ï:%-1d \92m:%-1d \8c«:%-1d \96£:%-1d ",
91             ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS),
92             ACURR(A_CHA));
93 #if 0 /*JP*/
94     Sprintf(nb = eos(nb),
95             (u.ualign.type == A_CHAOTIC)
96                 ? "  Chaotic"
97                 : (u.ualign.type == A_NEUTRAL) ? "  Neutral" : "  Lawful");
98 #else
99     Sprintf(nb = eos(nb),
100             (u.ualign.type == A_CHAOTIC)
101                 ? "\8d¬\93×"
102                 : (u.ualign.type == A_NEUTRAL) ? "\92\86\97§" : "\92\81\8f\98");
103 #endif
104 #ifdef SCORE_ON_BOTL
105     if (flags.showscore)
106 /*JP
107         Sprintf(nb = eos(nb), " S:%ld", botl_score());
108 */
109         Sprintf(nb = eos(nb), "%ld\93_", botl_score());
110 #endif
111     curs(WIN_STATUS, 1, 0);
112     putstr(WIN_STATUS, 0, newbot1);
113 }
114
115 STATIC_OVL void
116 bot2()
117 {
118     char newbot2[MAXCO];
119     register char *nb;
120     int hp, hpmax;
121     int cap = near_capacity();
122
123     hp = Upolyd ? u.mh : u.uhp;
124     hpmax = Upolyd ? u.mhmax : u.uhpmax;
125
126     if (hp < 0)
127         hp = 0;
128     (void) describe_level(newbot2);
129 /*JP
130     Sprintf(nb = eos(newbot2), "%s:%-2ld HP:%d(%d) Pw:%d(%d) AC:%-2d",
131 */
132     Sprintf(nb = eos(newbot2), "%s:%-2ld \91Ì:%d(%d) \96\82:%d(%d) \8aZ:%-2d",
133             encglyph(objnum_to_glyph(GOLD_PIECE)), money_cnt(invent), hp,
134             hpmax, u.uen, u.uenmax, u.uac);
135
136     if (Upolyd)
137         Sprintf(nb = eos(nb), " HD:%d", mons[u.umonnum].mlevel);
138     else if (flags.showexp)
139 /*JP
140         Sprintf(nb = eos(nb), " Xp:%u/%-1ld", u.ulevel, u.uexp);
141 */
142         Sprintf(nb = eos(nb), " \8co\8c±:%u/%-1ld", u.ulevel,u.uexp);
143     else
144 /*JP
145         Sprintf(nb = eos(nb), " Exp:%u", u.ulevel);
146 */
147         Sprintf(nb = eos(nb), " \8co\8c±:%u", u.ulevel);
148
149     if (flags.time)
150 /*JP
151         Sprintf(nb = eos(nb), " T:%ld", moves);
152 */
153         Sprintf(nb = eos(nb), " \95à:%ld", moves);
154     if (strcmp(hu_stat[u.uhs], "        ")) {
155         Sprintf(nb = eos(nb), " ");
156         Strcat(newbot2, hu_stat[u.uhs]);
157     }
158     if (Confusion)
159 /*JP
160         Sprintf(nb = eos(nb), " Conf");
161 */
162         Sprintf(nb = eos(nb), " \8d¬\97\90");
163     if (Sick) {
164         if (u.usick_type & SICK_VOMITABLE)
165 /*JP
166             Sprintf(nb = eos(nb), " FoodPois");
167 */
168             Sprintf(nb = eos(nb), " \90H\93Å");
169         if (u.usick_type & SICK_NONVOMITABLE)
170 /*JP
171             Sprintf(nb = eos(nb), " Ill");
172 */
173             Sprintf(nb = eos(nb), " \95a\8bC");
174     }
175     if (Blind)
176 /*JP
177         Sprintf(nb = eos(nb), " Blind");
178 */
179         Sprintf(nb = eos(nb), " \96Ó\96Ú");
180     if (Stunned)
181 /*JP
182         Sprintf(nb = eos(nb), " Stun");
183 */
184         Sprintf(nb = eos(nb), " á¿\9dò");
185     if (Hallucination)
186 /*JP
187         Sprintf(nb = eos(nb), " Hallu");
188 */
189         Sprintf(nb = eos(nb), " \8c\8ao");
190     if (Slimed)
191 /*JP
192         Sprintf(nb = eos(nb), " Slime");
193 */
194         Sprintf(nb = eos(nb), " \82Ç\82ë\82Ç\82ë");
195     if (cap > UNENCUMBERED)
196         Sprintf(nb = eos(nb), " %s", enc_stat[cap]);
197     curs(WIN_STATUS, 1, 1);
198     putmixed(WIN_STATUS, 0, newbot2);
199 }
200
201 void
202 bot()
203 {
204     if (youmonst.data) {
205         bot1();
206         bot2();
207     }
208     context.botl = context.botlx = 0;
209 }
210
211 #endif /* !STATUS_VIA_WINDOWPORT */
212
213 /* convert experience level (1..30) to rank index (0..8) */
214 int
215 xlev_to_rank(xlev)
216 int xlev;
217 {
218     return (xlev <= 2) ? 0 : (xlev <= 30) ? ((xlev + 2) / 4) : 8;
219 }
220
221 #if 0 /* not currently needed */
222 /* convert rank index (0..8) to experience level (1..30) */
223 int
224 rank_to_xlev(rank)
225 int rank;
226 {
227     return (rank <= 0) ? 1 : (rank <= 8) ? ((rank * 4) - 2) : 30;
228 }
229 #endif
230
231 const char *
232 rank_of(lev, monnum, female)
233 int lev;
234 short monnum;
235 boolean female;
236 {
237     register const struct Role *role;
238     register int i;
239
240     /* Find the role */
241     for (role = roles; role->name.m; role++)
242         if (monnum == role->malenum || monnum == role->femalenum)
243             break;
244     if (!role->name.m)
245         role = &urole;
246
247     /* Find the rank */
248     for (i = xlev_to_rank((int) lev); i >= 0; i--) {
249         if (female && role->rank[i].f)
250             return role->rank[i].f;
251         if (role->rank[i].m)
252             return role->rank[i].m;
253     }
254
255     /* Try the role name, instead */
256     if (female && role->name.f)
257         return role->name.f;
258     else if (role->name.m)
259         return role->name.m;
260     return "Player";
261 }
262
263 STATIC_OVL const char *
264 rank()
265 {
266     return rank_of(u.ulevel, Role_switch, flags.female);
267 }
268
269 int
270 title_to_mon(str, rank_indx, title_length)
271 const char *str;
272 int *rank_indx, *title_length;
273 {
274     register int i, j;
275
276     /* Loop through each of the roles */
277     for (i = 0; roles[i].name.m; i++)
278         for (j = 0; j < 9; j++) {
279             if (roles[i].rank[j].m
280                 && !strncmpi(str, roles[i].rank[j].m,
281                              strlen(roles[i].rank[j].m))) {
282                 if (rank_indx)
283                     *rank_indx = j;
284                 if (title_length)
285                     *title_length = strlen(roles[i].rank[j].m);
286                 return roles[i].malenum;
287             }
288             if (roles[i].rank[j].f
289                 && !strncmpi(str, roles[i].rank[j].f,
290                              strlen(roles[i].rank[j].f))) {
291                 if (rank_indx)
292                     *rank_indx = j;
293                 if (title_length)
294                     *title_length = strlen(roles[i].rank[j].f);
295                 return (roles[i].femalenum != NON_PM) ? roles[i].femalenum
296                                                       : roles[i].malenum;
297             }
298         }
299     return NON_PM;
300 }
301
302 void
303 max_rank_sz()
304 {
305     register int i, r, maxr = 0;
306     for (i = 0; i < 9; i++) {
307         if (urole.rank[i].m && (r = strlen(urole.rank[i].m)) > maxr)
308             maxr = r;
309         if (urole.rank[i].f && (r = strlen(urole.rank[i].f)) > maxr)
310             maxr = r;
311     }
312     mrank_sz = maxr;
313     return;
314 }
315
316 #ifdef SCORE_ON_BOTL
317 long
318 botl_score()
319 {
320     long deepest = deepest_lev_reached(FALSE);
321     long utotal;
322
323     utotal = money_cnt(invent) + hidden_gold();
324     if ((utotal -= u.umoney0) < 0L)
325         utotal = 0L;
326     utotal += u.urexp + (50 * (deepest - 1))
327           + (deepest > 30 ? 10000 : deepest > 20 ? 1000 * (deepest - 20) : 0);
328     if (utotal < u.urexp)
329         utotal = LONG_MAX; /* wrap around */
330     return utotal;
331 }
332 #endif /* SCORE_ON_BOTL */
333
334 /* provide the name of the current level for display by various ports */
335 int
336 describe_level(buf)
337 char *buf;
338 {
339     int ret = 1;
340
341     /* TODO:    Add in dungeon name */
342     if (Is_knox(&u.uz))
343         Sprintf(buf, "%s ", dungeons[u.uz.dnum].dname);
344     else if (In_quest(&u.uz))
345 /*JP
346         Sprintf(buf, "Home %d ", dunlev(&u.uz));
347 */
348         Sprintf(buf, "\8cÌ\8b½ %d ", dunlev(&u.uz));
349     else if (In_endgame(&u.uz))
350 /*JP
351         Sprintf(buf, Is_astralevel(&u.uz) ? "Astral Plane " : "End Game ");
352 */
353         Sprintf(buf, Is_astralevel(&u.uz) ? "\90¸\97ì\8aE " : "\8dÅ\8fI\8e\8e\97û ");
354     else {
355         /* ports with more room may expand this one */
356 /*JP
357         Sprintf(buf, "Dlvl:%-2d ", depth(&u.uz));
358 */
359         Sprintf(buf, "\92n\89º:%-2d ", depth(&u.uz));
360         ret = 0;
361     }
362     return ret;
363 }
364
365 #ifdef STATUS_VIA_WINDOWPORT
366 /* =======================================================================*/
367
368 /* structure that tracks the status details in the core */
369 struct istat_s {
370     long time;
371     unsigned anytype;
372     anything a;
373     char *val;
374     int valwidth;
375     enum statusfields idxmax;
376     enum statusfields fld;
377 };
378
379
380 STATIC_DCL void NDECL(init_blstats);
381 STATIC_DCL char *FDECL(anything_to_s, (char *, anything *, int));
382 STATIC_DCL void FDECL(s_to_anything, (anything *, char *, int));
383 STATIC_OVL int FDECL(percentage, (struct istat_s *, struct istat_s *));
384 STATIC_OVL int FDECL(compare_blstats, (struct istat_s *, struct istat_s *));
385
386 #ifdef STATUS_HILITES
387 STATIC_DCL boolean FDECL(assign_hilite, (char *, char *, char *, char *,
388                                          BOOLEAN_P));
389 STATIC_DCL const char *FDECL(clridx_to_s, (char *, int));
390 #endif
391
392 /* If entries are added to this, botl.h will require updating too */
393 STATIC_DCL struct istat_s initblstats[MAXBLSTATS] = {
394     { 0L, ANY_STR,  { (genericptr_t) 0 }, (char *) 0, 80,  0, BL_TITLE},
395     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  0, BL_STR},
396     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  0, BL_DX},
397     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  0, BL_CO},
398     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  0, BL_IN},
399     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  0, BL_WI},
400     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  0, BL_CH},
401     { 0L, ANY_STR,  { (genericptr_t) 0 }, (char *) 0, 40,  0, BL_ALIGN},
402     { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20,  0, BL_SCORE},
403     { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20,  0, BL_CAP},
404     { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 30,  0, BL_GOLD},
405     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  BL_ENEMAX, BL_ENE},
406     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  0, BL_ENEMAX},
407     { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 10,  0, BL_XP},
408     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  0, BL_AC},
409     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  0, BL_HD},
410     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 20,  0, BL_TIME},
411     { 0L, ANY_UINT, { (genericptr_t) 0 }, (char *) 0, 40,  0, BL_HUNGER},
412     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  BL_HPMAX, BL_HP},
413     { 0L, ANY_INT,  { (genericptr_t) 0 }, (char *) 0, 10,  0, BL_HPMAX},
414     { 0L, ANY_STR,  { (genericptr_t) 0 }, (char *) 0, 80,  0, BL_LEVELDESC},
415     { 0L, ANY_LONG, { (genericptr_t) 0 }, (char *) 0, 20,  0, BL_EXP},
416     { 0L, ANY_MASK32,
417                     { (genericptr_t) 0 }, (char *) 0,  0,  0, BL_CONDITION}
418 };
419
420 static struct fieldid_t {
421         const char *fieldname;
422         enum statusfields fldid;
423 } fieldids[] = {
424         {"title",               BL_TITLE},
425         {"strength",            BL_STR},
426         {"dexterity",           BL_DX},
427         {"constitution",        BL_CO},
428         {"intelligence",        BL_IN},
429         {"wisdom",              BL_WI},
430         {"charisma",            BL_CH},
431         {"alignment",           BL_ALIGN},
432         {"score",               BL_SCORE},
433         {"carrying-capacity",   BL_CAP},
434         {"gold",                BL_GOLD},
435         {"power",               BL_ENE},
436         {"power-max",           BL_ENEMAX},
437         {"experience-level",    BL_XP},
438         {"armor-class",         BL_AC},
439         {"HD",                  BL_HD},
440         {"time",                BL_TIME},
441         {"hunger",              BL_HUNGER},
442         {"hitpoints",           BL_HP},
443         {"hitpoints-max",       BL_HPMAX},
444         {"dungeon-level",       BL_LEVELDESC},
445         {"experience",          BL_EXP},
446         {"condition",           BL_CONDITION},
447 };
448
449 struct istat_s blstats[2][MAXBLSTATS];
450 static boolean blinit = FALSE, update_all = FALSE;
451
452 void
453 bot()
454 {
455     char buf[BUFSZ];
456     register char *nb;
457     static int idx = 0, idx_p, idxmax;
458     boolean updated = FALSE;
459     unsigned anytype;
460     int i, pc, chg, cap;
461     struct istat_s *curr, *prev;
462     boolean valset[MAXBLSTATS], chgval = FALSE;
463
464     if (!blinit)
465         panic("bot before init.");
466     if (!youmonst.data) {
467         context.botl = context.botlx = 0;
468         update_all = FALSE;
469         return;
470     }
471
472     cap = near_capacity();
473     idx_p = idx;
474     idx = 1 - idx; /* 0 -> 1, 1 -> 0 */
475
476     /* clear the "value set" indicators */
477     (void) memset((genericptr_t) valset, 0, MAXBLSTATS * sizeof(boolean));
478
479     /*
480      *  Player name and title.
481      */
482     buf[0] = '\0';
483     Strcpy(buf, plname);
484     if ('a' <= buf[0] && buf[0] <= 'z')
485         buf[0] += 'A' - 'a';
486     buf[10] = 0;
487     Sprintf(nb = eos(buf), " the ");
488     if (Upolyd) {
489         char mbot[BUFSZ];
490         int k = 0;
491
492         Strcpy(mbot, mons[u.umonnum].mname);
493         while (mbot[k] != 0) {
494             if ((k == 0 || (k > 0 && mbot[k - 1] == ' ')) && 'a' <= mbot[k]
495                 && mbot[k] <= 'z')
496                 mbot[k] += 'A' - 'a';
497             k++;
498         }
499         Sprintf1(nb = eos(nb), mbot);
500     } else
501         Sprintf1(nb = eos(nb), rank());
502     Sprintf(blstats[idx][BL_TITLE].val, "%-29s", buf);
503     valset[BL_TITLE] = TRUE; /* indicate val already set */
504
505     /* Strength */
506
507     buf[0] = '\0';
508     blstats[idx][BL_STR].a.a_int = ACURR(A_STR);
509     if (ACURR(A_STR) > 18) {
510         if (ACURR(A_STR) > STR18(100))
511             Sprintf(buf, "%2d", ACURR(A_STR) - 100);
512         else if (ACURR(A_STR) < STR18(100))
513             Sprintf(buf, "18/%02d", ACURR(A_STR) - 18);
514         else
515             Sprintf(buf, "18/**");
516     } else
517         Sprintf(buf, "%-1d", ACURR(A_STR));
518     Strcpy(blstats[idx][BL_STR].val, buf);
519     valset[BL_STR] = TRUE; /* indicate val already set */
520
521     /*  Dexterity, constitution, intelligence, wisdom, charisma. */
522
523     blstats[idx][BL_DX].a.a_int = ACURR(A_DEX);
524     blstats[idx][BL_CO].a.a_int = ACURR(A_CON);
525     blstats[idx][BL_IN].a.a_int = ACURR(A_INT);
526     blstats[idx][BL_WI].a.a_int = ACURR(A_WIS);
527     blstats[idx][BL_CH].a.a_int = ACURR(A_CHA);
528
529     /* Alignment */
530
531     Strcpy(blstats[idx][BL_ALIGN].val,
532            (u.ualign.type == A_CHAOTIC)
533                ? "Chaotic"
534                : (u.ualign.type == A_NEUTRAL) ? "Neutral" : "Lawful");
535
536     /* Score */
537
538     blstats[idx][BL_SCORE].a.a_long =
539 #ifdef SCORE_ON_BOTL
540         botl_score();
541 #else
542         0;
543 #endif
544     /*  Hit points  */
545
546     blstats[idx][BL_HP].a.a_int = Upolyd ? u.mh : u.uhp;
547     blstats[idx][BL_HPMAX].a.a_int = Upolyd ? u.mhmax : u.uhpmax;
548     if (blstats[idx][BL_HP].a.a_int < 0)
549         blstats[idx][BL_HP].a.a_int = 0;
550
551     /*  Dungeon level. */
552
553     (void) describe_level(blstats[idx][BL_LEVELDESC].val);
554     valset[BL_LEVELDESC] = TRUE; /* indicate val already set */
555
556     /* Gold */
557
558     blstats[idx][BL_GOLD].a.a_long = money_cnt(invent);
559     /*
560      * The tty port needs to display the current symbol for gold
561      * as a field header, so to accommodate that we pass gold with
562      * that already included. If a window port needs to use the text
563      * gold amount without the leading "$:" the port will have to
564      * add 2 to the value pointer it was passed in status_update()
565      * for the BL_GOLD case.
566      *
567      * Another quirk of BL_GOLD is that the field display may have
568      * changed if a new symbol set was loaded, or we entered or left
569      * the rogue level.
570      */
571
572     Sprintf(blstats[idx][BL_GOLD].val, "%s:%ld",
573             encglyph(objnum_to_glyph(GOLD_PIECE)),
574             blstats[idx][BL_GOLD].a.a_long);
575     valset[BL_GOLD] = TRUE; /* indicate val already set */
576
577     /* Power (magical energy) */
578
579     blstats[idx][BL_ENE].a.a_int = u.uen;
580     blstats[idx][BL_ENEMAX].a.a_int = u.uenmax;
581
582     /* Armor class */
583
584     blstats[idx][BL_AC].a.a_int = u.uac;
585
586     /* Monster level (if Upolyd) */
587
588     if (Upolyd)
589         blstats[idx][BL_HD].a.a_int = mons[u.umonnum].mlevel;
590     else
591         blstats[idx][BL_HD].a.a_int = 0;
592
593     /* Experience */
594
595     blstats[idx][BL_XP].a.a_int = u.ulevel;
596     blstats[idx][BL_EXP].a.a_int = u.uexp;
597
598     /* Time (moves) */
599
600     blstats[idx][BL_TIME].a.a_long = moves;
601
602     /* Hunger */
603
604     blstats[idx][BL_HUNGER].a.a_uint = u.uhs;
605     *(blstats[idx][BL_HUNGER].val) = '\0';
606     if (strcmp(hu_stat[u.uhs], "        ") != 0)
607         Strcpy(blstats[idx][BL_HUNGER].val, hu_stat[u.uhs]);
608     valset[BL_HUNGER] = TRUE;
609
610     /* Carrying capacity */
611
612     *(blstats[idx][BL_CAP].val) = '\0';
613     blstats[idx][BL_CAP].a.a_int = cap;
614     if (cap > UNENCUMBERED)
615         Strcpy(blstats[idx][BL_CAP].val, enc_stat[cap]);
616     valset[BL_CAP] = TRUE;
617
618     /* Conditions */
619
620     if (Blind)
621         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_BLIND;
622     else
623         blstats[idx][BL_CONDITION].a.a_ulong &= ~BL_MASK_BLIND;
624
625     if (Confusion)
626         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_CONF;
627     else
628         blstats[idx][BL_CONDITION].a.a_ulong &= ~BL_MASK_CONF;
629
630     if (Sick && u.usick_type & SICK_VOMITABLE)
631         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FOODPOIS;
632     else
633         blstats[idx][BL_CONDITION].a.a_ulong &= ~BL_MASK_FOODPOIS;
634
635     if (Sick && u.usick_type & SICK_NONVOMITABLE)
636         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_ILL;
637     else
638         blstats[idx][BL_CONDITION].a.a_ulong &= ~BL_MASK_ILL;
639
640     if (Hallucination)
641         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_HALLU;
642     else
643         blstats[idx][BL_CONDITION].a.a_ulong &= ~BL_MASK_HALLU;
644
645     if (Stunned)
646         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_STUNNED;
647     else
648         blstats[idx][BL_CONDITION].a.a_ulong &= ~BL_MASK_STUNNED;
649
650     if (Slimed)
651         blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_SLIMED;
652     else
653         blstats[idx][BL_CONDITION].a.a_ulong &= ~BL_MASK_SLIMED;
654
655     /*
656      *  Now pass the changed values to window port.
657      */
658     for (i = 0; i < MAXBLSTATS; i++) {
659         if (((i == BL_SCORE) && !flags.showscore)
660             || ((i == BL_EXP) && !flags.showexp)
661             || ((i == BL_TIME) && !flags.time)
662             || ((i == BL_HD) && !Upolyd)
663             || ((i == BL_XP || i == BL_EXP) && Upolyd))
664             continue;
665         anytype = blstats[idx][i].anytype;
666         curr = &blstats[idx][i];
667         prev = &blstats[idx_p][i];
668         chg = 0;
669         if (update_all || ((chg = compare_blstats(prev, curr)) != 0)
670             || ((chgval = (valset[i] && strcmp(blstats[idx][i].val,
671                                                blstats[idx_p][i].val)))
672                 != 0)) {
673             idxmax = blstats[idx][i].idxmax;
674             pc = (idxmax) ? percentage(curr, &blstats[idx][idxmax]) : 0;
675             if (!valset[i])
676                 (void) anything_to_s(curr->val, &curr->a, anytype);
677             if (anytype != ANY_MASK32) {
678                 status_update(i, (genericptr_t) curr->val,
679                               valset[i] ? chgval : chg, pc);
680             } else {
681                 status_update(i,
682                               /* send pointer to mask */
683                               (genericptr_t) &curr->a.a_ulong, chg, 0);
684             }
685             updated = TRUE;
686         }
687     }
688     /*
689      * It is possible to get here, with nothing having been pushed
690      * to the window port, when none of the info has changed. In that
691      * case, we need to force a call to status_update() when
692      * context.botlx is set. The tty port in particular has a problem
693      * if that isn't done, since it sets context.botlx when a menu or
694      * text display obliterates the status line.
695      *
696      * To work around it, we call status_update() with fictitious
697      * index of BL_FLUSH (-1).
698      */
699     if (context.botlx && !updated)
700         status_update(BL_FLUSH, (genericptr_t) 0, 0, 0);
701
702     context.botl = context.botlx = 0;
703     update_all = FALSE;
704 }
705
706 void
707 status_initialize(reassessment)
708 boolean
709     reassessment; /* TRUE = just reassess fields w/o other initialization*/
710 {
711     int i;
712     const char *fieldfmt = (const char *)0;
713     const char *fieldname = (const char *)0;
714
715     if (!reassessment) {
716         init_blstats();
717         (*windowprocs.win_status_init)();
718         blinit = TRUE;
719 #ifdef STATUS_HILITES
720         status_notify_windowport(TRUE);
721 #endif
722     }
723     for (i = 0; i < MAXBLSTATS; ++i) {
724         enum statusfields fld = initblstats[i].fld;
725
726         switch (fld) {
727         case BL_TITLE:
728             fieldfmt = "%s";
729             fieldname = "title";
730             status_enablefield(fld, fieldname, fieldfmt, TRUE);
731             break;
732         case BL_STR:
733             fieldfmt = " St:%s";
734             fieldname = "strength";
735             status_enablefield(fld, fieldname, fieldfmt, TRUE);
736             break;
737         case BL_DX:
738             fieldfmt = " Dx:%s";
739             fieldname = "dexterity";
740             status_enablefield(fld, fieldname, fieldfmt, TRUE);
741             break;
742         case BL_CO:
743             fieldfmt = " Co:%s";
744             fieldname = "constitution";
745             status_enablefield(fld, fieldname, fieldfmt, TRUE);
746             break;
747         case BL_IN:
748             fieldfmt = " In:%s";
749             fieldname = "intelligence";
750             status_enablefield(fld, fieldname, fieldfmt, TRUE);
751             break;
752         case BL_WI:
753             fieldfmt = " Wi:%s";
754             fieldname = "wisdom";
755             status_enablefield(fld, fieldname, fieldfmt, TRUE);
756             break;
757         case BL_CH:
758             fieldfmt = " Ch:%s";
759             fieldname = "charisma";
760             status_enablefield(fld, fieldname, fieldfmt, TRUE);
761             break;
762         case BL_ALIGN:
763             fieldfmt = " %s";
764             fieldname = "alignment";
765             status_enablefield(fld, fieldname, fieldfmt, TRUE);
766             break;
767         case BL_SCORE:
768             fieldfmt = " S:%s";
769             fieldname = "score";
770             status_enablefield(fld, fieldname, fieldfmt,
771                                (!flags.showscore) ? FALSE : TRUE);
772             break;
773         case BL_CAP:
774             fieldfmt = " %s";
775             fieldname = "carrying-capacity";
776             status_enablefield(fld, fieldname, fieldfmt, TRUE);
777             break;
778         case BL_GOLD:
779             fieldfmt = " %s";
780             fieldname = "gold";
781             status_enablefield(fld, fieldname, fieldfmt, TRUE);
782             break;
783         case BL_ENE:
784             fieldfmt = " Pw:%s";
785             fieldname = "power";
786             status_enablefield(fld, fieldname, fieldfmt, TRUE);
787             break;
788         case BL_ENEMAX:
789             fieldfmt = "(%s)";
790             fieldname = "power-max";
791             status_enablefield(fld, fieldname, fieldfmt, TRUE);
792             break;
793         case BL_XP:
794             fieldfmt = " Xp:%s";
795             fieldname = "experience-level";
796             status_enablefield(fld, fieldname, fieldfmt,
797                                    (Upolyd) ? FALSE : TRUE);
798             break;
799         case BL_AC:
800             fieldfmt = " AC:%s";
801             fieldname = "armor-class";
802             status_enablefield(fld, fieldname, fieldfmt, TRUE);
803             break;
804         case BL_HD:
805             fieldfmt = " HD:%s";
806             fieldname = "HD";
807             status_enablefield(fld, fieldname, fieldfmt,
808                                    (!Upolyd) ? FALSE : TRUE);
809             break;
810         case BL_TIME:
811             fieldfmt = " T:%s";
812             fieldname = "time";
813             status_enablefield(fld, fieldname, fieldfmt,
814                                    (!flags.time) ? FALSE : TRUE);
815             break;
816         case BL_HUNGER:
817             fieldfmt = " %s";
818             fieldname = "hunger";
819             status_enablefield(fld, fieldname, fieldfmt, TRUE);
820             break;
821         case BL_HP:
822             fieldfmt = " HP:%s";
823             fieldname = "hitpoints";
824             status_enablefield(fld, fieldname, fieldfmt, TRUE);
825             break;
826         case BL_HPMAX:
827             fieldfmt = "(%s)";
828             fieldname = "hitpoint-max";
829             status_enablefield(fld, fieldname, fieldfmt, TRUE);
830             break;
831         case BL_LEVELDESC:
832             fieldfmt = "%s";
833             fieldname = "dungeon-level";
834             status_enablefield(fld, fieldname, fieldfmt, TRUE);
835             break;
836         case BL_EXP:
837             fieldfmt = "/%s";
838             fieldname = "experience";
839             status_enablefield(fld, fieldname, fieldfmt,
840                                   (!flags.showexp || Upolyd) ? FALSE : TRUE);
841             break;
842         case BL_CONDITION:
843             fieldfmt = "%S";
844             fieldname = "condition";
845             status_enablefield(fld, fieldname, fieldfmt, TRUE);
846             break;
847         case BL_FLUSH:
848         default:
849             break;
850         }
851     }
852     update_all = TRUE;
853 }
854
855 void
856 status_finish()
857 {
858     int i;
859
860     /* call the window port cleanup routine first */
861     (*windowprocs.win_status_finish)();
862
863     /* free memory that we alloc'd now */
864     for (i = 0; i < MAXBLSTATS; ++i) {
865         if (blstats[0][i].val)
866             free((genericptr_t) blstats[0][i].val);
867         if (blstats[1][i].val)
868             free((genericptr_t) blstats[1][i].val);
869     }
870 }
871
872 STATIC_OVL void
873 init_blstats()
874 {
875     static boolean initalready = FALSE;
876     int i, j;
877
878     if (initalready) {
879         impossible("init_blstats called more than once.");
880         return;
881     }
882
883     initalready = TRUE;
884     for (i = BEFORE; i <= NOW; ++i) {
885         for (j = 0; j < MAXBLSTATS; ++j) {
886             blstats[i][j] = initblstats[j];
887             blstats[i][j].a = zeroany;
888             if (blstats[i][j].valwidth) {
889                 blstats[i][j].val = (char *) alloc(blstats[i][j].valwidth);
890                 blstats[i][j].val[0] = '\0';
891             } else
892                 blstats[i][j].val = (char *) 0;
893         }
894     }
895 }
896
897 STATIC_OVL char *
898 anything_to_s(buf, a, anytype)
899 char *buf;
900 anything *a;
901 int anytype;
902 {
903     if (!buf)
904         return (char *) 0;
905
906     switch (anytype) {
907     case ANY_ULONG:
908         Sprintf(buf, "%lu", a->a_ulong);
909         break;
910     case ANY_MASK32:
911         Sprintf(buf, "%lx", a->a_ulong);
912         break;
913     case ANY_LONG:
914         Sprintf(buf, "%ld", a->a_long);
915         break;
916     case ANY_INT:
917         Sprintf(buf, "%d", a->a_int);
918         break;
919     case ANY_UINT:
920         Sprintf(buf, "%u", a->a_uint);
921         break;
922     case ANY_IPTR:
923         Sprintf(buf, "%d", *a->a_iptr);
924         break;
925     case ANY_LPTR:
926         Sprintf(buf, "%ld", *a->a_lptr);
927         break;
928     case ANY_ULPTR:
929         Sprintf(buf, "%lu", *a->a_ulptr);
930         break;
931     case ANY_UPTR:
932         Sprintf(buf, "%u", *a->a_uptr);
933         break;
934     case ANY_STR: /* do nothing */
935         ;
936         break;
937     default:
938         buf[0] = '\0';
939     }
940     return buf;
941 }
942
943 STATIC_OVL void
944 s_to_anything(a, buf, anytype)
945 anything *a;
946 char *buf;
947 int anytype;
948 {
949     if (!buf || !a)
950         return;
951
952     switch (anytype) {
953     case ANY_LONG:
954         a->a_long = atol(buf);
955         break;
956     case ANY_INT:
957         a->a_int = atoi(buf);
958         break;
959     case ANY_UINT:
960         a->a_uint = (unsigned) atoi(buf);
961         break;
962     case ANY_ULONG:
963         a->a_ulong = (unsigned long) atol(buf);
964         break;
965     case ANY_IPTR:
966         if (a->a_iptr)
967             *a->a_iptr = atoi(buf);
968         break;
969     case ANY_UPTR:
970         if (a->a_uptr)
971             *a->a_uptr = (unsigned) atoi(buf);
972         break;
973     case ANY_LPTR:
974         if (a->a_lptr)
975             *a->a_lptr = atol(buf);
976         break;
977     case ANY_ULPTR:
978         if (a->a_ulptr)
979             *a->a_ulptr = (unsigned long) atol(buf);
980         break;
981     case ANY_MASK32:
982         a->a_ulong = (unsigned long) atol(buf);
983         break;
984     default:
985         a->a_void = 0;
986         break;
987     }
988     return;
989 }
990
991 STATIC_OVL int
992 compare_blstats(bl1, bl2)
993 struct istat_s *bl1, *bl2;
994 {
995     int anytype, result = 0;
996
997     if (!bl1 || !bl2) {
998         panic("compare_blstat: bad istat pointer %s, %s",
999               fmt_ptr((genericptr_t) bl1), fmt_ptr((genericptr_t) bl2));
1000     }
1001
1002     anytype = bl1->anytype;
1003     if ((!bl1->a.a_void || !bl2->a.a_void)
1004         && (anytype == ANY_IPTR || anytype == ANY_UPTR || anytype == ANY_LPTR
1005             || anytype == ANY_ULPTR)) {
1006         panic("compare_blstat: invalid pointer %s, %s",
1007               fmt_ptr((genericptr_t) bl1->a.a_void),
1008               fmt_ptr((genericptr_t) bl2->a.a_void));
1009     }
1010
1011     switch (anytype) {
1012     case ANY_INT:
1013         result = (bl1->a.a_int < bl2->a.a_int)
1014                      ? 1
1015                      : (bl1->a.a_int > bl2->a.a_int) ? -1 : 0;
1016         break;
1017     case ANY_IPTR:
1018         result = (*bl1->a.a_iptr < *bl2->a.a_iptr)
1019                      ? 1
1020                      : (*bl1->a.a_iptr > *bl2->a.a_iptr) ? -1 : 0;
1021         break;
1022     case ANY_LONG:
1023         result = (bl1->a.a_long < bl2->a.a_long)
1024                      ? 1
1025                      : (bl1->a.a_long > bl2->a.a_long) ? -1 : 0;
1026         break;
1027     case ANY_LPTR:
1028         result = (*bl1->a.a_lptr < *bl2->a.a_lptr)
1029                      ? 1
1030                      : (*bl1->a.a_lptr > *bl2->a.a_lptr) ? -1 : 0;
1031         break;
1032     case ANY_UINT:
1033         result = (bl1->a.a_uint < bl2->a.a_uint)
1034                      ? 1
1035                      : (bl1->a.a_uint > bl2->a.a_uint) ? -1 : 0;
1036         break;
1037     case ANY_UPTR:
1038         result = (*bl1->a.a_uptr < *bl2->a.a_uptr)
1039                      ? 1
1040                      : (*bl1->a.a_uptr > *bl2->a.a_uptr) ? -1 : 0;
1041         break;
1042     case ANY_ULONG:
1043         result = (bl1->a.a_ulong < bl2->a.a_ulong)
1044                      ? 1
1045                      : (bl1->a.a_ulong > bl2->a.a_ulong) ? -1 : 0;
1046         break;
1047     case ANY_ULPTR:
1048         result = (*bl1->a.a_ulptr < *bl2->a.a_ulptr)
1049                      ? 1
1050                      : (*bl1->a.a_ulptr > *bl2->a.a_ulptr) ? -1 : 0;
1051         break;
1052     case ANY_STR:
1053         if (strcmp(bl1->val, bl2->val) == 0)
1054             result = 0;
1055         else
1056             result = 1;
1057         break;
1058     case ANY_MASK32:
1059         if (bl1->a.a_ulong == bl2->a.a_ulong)
1060             result = 0;
1061         else
1062             result = 1;
1063         break;
1064     default:
1065         result = 1;
1066     }
1067     return result;
1068 }
1069
1070 STATIC_OVL int
1071 percentage(bl, maxbl)
1072 struct istat_s *bl, *maxbl;
1073 {
1074     int result = 0;
1075     int anytype;
1076
1077     if (!bl || !maxbl) {
1078         impossible("percentage: bad istat pointer %s, %s",
1079                    fmt_ptr((genericptr_t) bl), fmt_ptr((genericptr_t) maxbl));
1080         return 0;
1081     }
1082
1083     anytype = bl->anytype;
1084     if (maxbl->a.a_void) {
1085         switch (anytype) {
1086         case ANY_INT:
1087             result = ((100 * bl->a.a_int) / maxbl->a.a_int);
1088             break;
1089         case ANY_LONG:
1090             result = (int) ((100L * bl->a.a_long) / maxbl->a.a_long);
1091             break;
1092         case ANY_UINT:
1093             result = (int) ((100U * bl->a.a_uint) / maxbl->a.a_uint);
1094             break;
1095         case ANY_ULONG:
1096             result = (int) ((100UL * bl->a.a_ulong) / maxbl->a.a_ulong);
1097             break;
1098         case ANY_IPTR:
1099             result = ((100 * (*bl->a.a_iptr)) / (*maxbl->a.a_iptr));
1100             break;
1101         case ANY_LPTR:
1102             result = (int) ((100L * (*bl->a.a_lptr)) / (*maxbl->a.a_lptr));
1103             break;
1104         case ANY_UPTR:
1105             result = (int) ((100U * (*bl->a.a_uptr)) / (*maxbl->a.a_uptr));
1106             break;
1107         case ANY_ULPTR:
1108             result = (int) ((100UL * (*bl->a.a_ulptr)) / (*maxbl->a.a_ulptr));
1109             break;
1110         }
1111     }
1112     return result;
1113 }
1114
1115
1116 #ifdef STATUS_HILITES
1117
1118 /****************************************************************************/
1119 /* Core status hiliting support */
1120 /****************************************************************************/
1121
1122 struct hilite_s {
1123     boolean set;
1124     unsigned anytype;
1125     anything threshold;
1126     int behavior;
1127     int coloridx[2];
1128 };
1129
1130 struct hilite_s status_hilites[MAXBLSTATS];
1131
1132 /*
1133  * This is the parser for the hilite options
1134  * Example:
1135  *    OPTION=hilite_status: hitpoints/10%/red/normal
1136  *
1137  * set_hilite_status() separates each hilite entry into its 4 component
1138  * strings, then calls assign_hilite() to make the adjustments.
1139  */
1140 boolean
1141 set_status_hilites(op, from_configfile)
1142 char *op;
1143 boolean from_configfile;
1144 {
1145     char hsbuf[4][QBUFSZ];
1146     boolean rslt, badopt = FALSE;
1147     int fldnum, num = 0, ccount = 0;
1148     char c;
1149
1150     num = fldnum = 0;
1151     hsbuf[0][0] = hsbuf[1][0] = hsbuf[2][0] = hsbuf[3][0] = '\0';
1152     while (*op && fldnum < 4 && ccount < (QBUFSZ - 2)) {
1153         c = lowc(*op);
1154         if (c == ' ') {
1155             if (fldnum >= 2) {
1156                 rslt = assign_hilite(&hsbuf[0][0], &hsbuf[1][0], &hsbuf[2][0],
1157                                      &hsbuf[3][0], from_configfile);
1158                 if (!rslt) {
1159                     badopt = TRUE;
1160                     break;
1161                 }
1162             }
1163             hsbuf[0][0] = hsbuf[1][0] = '\0';
1164             hsbuf[2][0] = hsbuf[3][0] = '\0';
1165             fldnum = 0;
1166             ccount = 0;
1167         } else if (c == '/') {
1168             fldnum++;
1169             ccount = 0;
1170         } else {
1171             hsbuf[fldnum][ccount++] = c;
1172             hsbuf[fldnum][ccount] = '\0';
1173         }
1174         op++;
1175     }
1176     if (fldnum >= 2 && !badopt) {
1177         rslt = assign_hilite(&hsbuf[0][0], &hsbuf[1][0], &hsbuf[2][0],
1178                              &hsbuf[3][0], from_configfile);
1179         if (!rslt)
1180             badopt = TRUE;
1181     }
1182     if (badopt)
1183         return FALSE;
1184     return TRUE;
1185 }
1186
1187 void
1188 clear_status_hilites(from_configfile)
1189 boolean from_configfile;
1190 {
1191     int i;
1192     anything it;
1193     it = zeroany;
1194     for (i = 0; i < MAXBLSTATS; ++i) {
1195         (void) memset((genericptr_t) &status_hilites[i], 0,
1196                       sizeof(struct hilite_s));
1197         /* notify window port */
1198         if (!from_configfile)
1199             status_threshold(i, blstats[0][i].anytype, it, 0, 0, 0);
1200     }
1201 }
1202
1203 STATIC_OVL boolean
1204 assign_hilite(sa, sb, sc, sd, from_configfile)
1205 char *sa, *sb, *sc, *sd;
1206 boolean from_configfile;
1207 {
1208     char *tmp, *how;
1209     int i = -1, dt = -1, idx = -1;
1210     int coloridx[2] = { -1, -1 };
1211     boolean inverse[2] = { FALSE, FALSE };
1212     boolean bold[2] = { FALSE, FALSE };
1213     boolean normal[2] = { 0, 0 };
1214     boolean percent = FALSE, down_up = FALSE, changed = FALSE;
1215     anything threshold;
1216     enum statusfields fld = BL_FLUSH;
1217     threshold.a_void = 0;
1218
1219     /* Example:
1220      *  hilite_status: hitpoints/10%/red/normal
1221      */
1222
1223     /* field name to statusfield */
1224     for (i = 0; sa && i < SIZE(fieldids); ++i) {
1225         if (strcmpi(sa, fieldids[i].fieldname) == 0) {
1226             idx = i;
1227             fld = fieldids[i].fldid;
1228             break;
1229         }
1230     }
1231     if (idx == -1)
1232         return FALSE;
1233     status_hilites[idx].set = FALSE; /* mark it "unset" */
1234
1235     /* threshold */
1236     if (!sb)
1237         return FALSE;
1238     if ((strcmpi(sb, "updown") == 0) || (strcmpi(sb, "downup") == 0)
1239         || (strcmpi(sb, "up") == 0) || (strcmpi(sb, "down") == 0)) {
1240         down_up = TRUE;
1241     } else if ((strcmpi(sb, "changed") == 0)
1242                && (fld == BL_TITLE || fld == BL_ALIGN || fld == BL_LEVELDESC
1243                    || fld == BL_CONDITION)) {
1244         changed = TRUE; /* changed is only thing allowed */
1245     } else {
1246         tmp = sb;
1247         while (*tmp) {
1248             if (*tmp == '%') {
1249                 *tmp = '\0';
1250                 percent = TRUE;
1251                 break;
1252             } else if (!index("0123456789", *tmp))
1253                 return FALSE;
1254             tmp++;
1255         }
1256         if (strlen(sb) > 0) {
1257             dt = blstats[0][idx].anytype;
1258             if (percent)
1259                 dt = ANY_INT;
1260             (void) s_to_anything(&threshold, sb, dt);
1261         } else
1262             return FALSE;
1263         if (percent && (threshold.a_int < 1 || threshold.a_int > 100))
1264             return FALSE;
1265         if (!threshold.a_void && (strcmp(sb, "0") != 0))
1266             return FALSE;
1267     }
1268
1269     /* actions */
1270     for (i = 0; i < 2; ++i) {
1271         if (!i)
1272             how = sc;
1273         else
1274             how = sd;
1275         if (!how) {
1276             if (!i)
1277                 return FALSE;
1278             else
1279                 break; /* sc is mandatory; sd is not */
1280         }
1281
1282         if (strcmpi(how, "bold") == 0) {
1283             bold[i] = TRUE;
1284         } else if (strcmpi(how, "inverse") == 0) {
1285             inverse[i] = TRUE;
1286         } else if (strcmpi(how, "normal") == 0) {
1287             normal[i] = TRUE;
1288         } else {
1289             int k;
1290             char colorname[BUFSZ];
1291             for (k = 0; k < CLR_MAX; ++k) {
1292                 /* we have to make a copy to change space to dash */
1293                 (void) strcpy(colorname, c_obj_colors[k]);
1294                 for (tmp = index(colorname, ' '); tmp;
1295                      tmp = index(colorname, ' '))
1296                     *tmp = '-';
1297                 if (strcmpi(how, colorname) == 0) {
1298                     coloridx[i] = k;
1299                     break;
1300                 }
1301             }
1302             if (k >= CLR_MAX)
1303                 return FALSE;
1304         }
1305     }
1306
1307     /* Assign the values */
1308
1309     for (i = 0; i < 2; ++i) {
1310         if (inverse[i])
1311             status_hilites[idx].coloridx[i] = BL_HILITE_INVERSE;
1312         else if (bold[i])
1313             status_hilites[idx].coloridx[i] = BL_HILITE_BOLD;
1314         else if (coloridx[i])
1315             status_hilites[idx].coloridx[i] = coloridx[i];
1316         else
1317             status_hilites[idx].coloridx[i] = BL_HILITE_NONE;
1318     }
1319
1320     if (percent)
1321         status_hilites[idx].behavior = BL_TH_VAL_PERCENTAGE;
1322     else if (down_up)
1323         status_hilites[idx].behavior = BL_TH_UPDOWN;
1324     else if (threshold.a_void)
1325         status_hilites[idx].behavior = BL_TH_VAL_ABSOLUTE;
1326     else
1327         status_hilites[idx].behavior = BL_TH_NONE;
1328
1329     if (status_hilites[idx].behavior != BL_TH_NONE) {
1330         status_hilites[idx].threshold = threshold;
1331         status_hilites[idx].set = TRUE;
1332     }
1333     status_hilites[idx].anytype = dt;
1334
1335     /* Now finally, we notify the window port */
1336     if (!from_configfile)
1337         status_threshold(idx, status_hilites[idx].anytype,
1338                         status_hilites[idx].threshold,
1339                         status_hilites[idx].behavior,
1340                         status_hilites[idx].coloridx[0],
1341                         status_hilites[idx].coloridx[1]);
1342
1343     return TRUE;
1344 }
1345
1346 void
1347 status_notify_windowport(all)
1348 boolean all;
1349 {
1350     int idx;
1351     anything it;
1352
1353     it = zeroany;
1354     for (idx = 0; idx < MAXBLSTATS; ++idx) {
1355         if (status_hilites[idx].set)
1356             status_threshold(idx, status_hilites[idx].anytype,
1357                             status_hilites[idx].threshold,
1358                             status_hilites[idx].behavior,
1359                             status_hilites[idx].coloridx[0],
1360                             status_hilites[idx].coloridx[1]);
1361         else
1362             status_threshold(idx, blstats[0][idx].anytype, it, 0, 0, 0);
1363
1364     }
1365 }
1366
1367 /*
1368  * get_status_hilites
1369  *
1370  * Returns a string containing all the status hilites in the
1371  * same format that is used to specify a status hilite preference
1372  * in the config file.
1373  */
1374 char *
1375 get_status_hilites(buf, bufsiz)
1376 char *buf;
1377 int bufsiz;
1378 {
1379     int i, j, k, coloridx;
1380     const char *text = (char *) 0;
1381     char tmp[BUFSZ], colorname[BUFSZ];
1382     boolean val_percentage, val_absolute, up_down;
1383     boolean added_one = FALSE;
1384
1385     if (!buf)
1386         return (char *) 0;
1387     *buf = '\0';
1388
1389     bufsiz--; /* required trailing null */
1390     for (i = 0; i < MAXBLSTATS; ++i) {
1391         val_percentage = val_absolute = up_down = FALSE;
1392         if (status_hilites[i].set) {
1393             if (!added_one)
1394                 added_one = TRUE;
1395             else {
1396                 Strcat(buf, " ");
1397                 bufsiz--;
1398             }
1399             k = strlen(fieldids[i].fieldname);
1400             if (k < bufsiz) {
1401                 Strcat(buf, fieldids[i].fieldname);
1402                 bufsiz -= k;
1403             }
1404             if (bufsiz > 1) {
1405                 Strcat(buf, "/");
1406                 bufsiz--;
1407             }
1408             if (status_hilites[i].behavior == BL_TH_VAL_PERCENTAGE) {
1409                 val_percentage = TRUE;
1410             } else if (status_hilites[i].behavior == BL_TH_VAL_ABSOLUTE) {
1411                 val_absolute = TRUE;
1412             } else if (status_hilites[i].behavior == BL_TH_UPDOWN) {
1413                 up_down = TRUE;
1414                 text = "updown";
1415             }
1416
1417             if (status_hilites[i].behavior != BL_TH_UPDOWN) {
1418                 anything_to_s(tmp, &status_hilites[i].threshold,
1419                           blstats[0][i].anytype);
1420                 text = tmp;
1421             }
1422             k = strlen(text);
1423             if (k < (bufsiz - 1)) {
1424                 Strcat(buf, text);
1425                 if (val_percentage)
1426                     Strcat(buf, "%"), k++;
1427                 bufsiz -= k;
1428             }
1429             for (j = 0; j < 2; ++j) {
1430                 if (bufsiz > 1) {
1431                     Strcat(buf, "/");
1432                     bufsiz--;
1433                 }
1434                 coloridx = status_hilites[i].coloridx[j];
1435                 if (coloridx < 0) {
1436                     if (coloridx == BL_HILITE_BOLD)
1437                         text = "bold";
1438                     else if (coloridx == BL_HILITE_INVERSE)
1439                         text = "inverse";
1440                     else
1441                         text = "normal";
1442                 } else {
1443                     char *blank;
1444                     (void) strcpy(colorname, c_obj_colors[coloridx]);
1445                     for (blank = index(colorname, ' '); blank;
1446                          blank = index(colorname, ' '))
1447                         *blank = '-';
1448                     text = colorname;
1449                 }
1450                 k = strlen(text);
1451                 if (k < bufsiz) {
1452                     Strcat(buf, text);
1453                     bufsiz -= k;
1454                 }
1455             }
1456         }
1457     }
1458     return buf;
1459 }
1460
1461 STATIC_OVL const char *
1462 clridx_to_s(buf, idx)
1463 char *buf;
1464 int idx;
1465 {
1466     static const char *a[] = { "bold", "inverse", "normal" };
1467     char* p = 0;
1468
1469     if (buf) {
1470         buf[0] = '\0';
1471         if (idx < 0 && idx >= BL_HILITE_BOLD)
1472             Strcpy(buf, a[idx + 3]);
1473         else if (idx >= 0 && idx < CLR_MAX)
1474             Strcpy(buf, c_obj_colors[idx]);
1475         /* replace spaces with - */
1476         for(p = buf; *p; p++)
1477             if(*p == ' ') *p = '-';
1478     }
1479     return buf;
1480 }
1481
1482 boolean
1483 status_hilite_menu()
1484 {
1485     int i, j, k, pick_cnt, pick_idx, opt_idx;
1486     menu_item *statfield_picks = (menu_item *) 0;
1487     const char *fieldname;
1488     int field_picks[MAXBLSTATS], res;
1489     struct hilite_s hltemp[MAXBLSTATS];
1490     char buf[BUFSZ], thresholdbuf[BUFSZ], below[BUFSZ], above[BUFSZ];
1491     winid tmpwin;
1492     anything any;
1493
1494     tmpwin = create_nhwindow(NHW_MENU);
1495     start_menu(tmpwin);
1496     for (i = 0; i < MAXBLSTATS; i++) {
1497         (void) memset(&hltemp[i], 0, sizeof(struct hilite_s));
1498         fieldname = fieldids[i].fieldname;
1499         any.a_int = i + 1;
1500         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, fieldname,
1501                  MENU_UNSELECTED);
1502         field_picks[i] = 0;
1503     }
1504     end_menu(tmpwin, "Change hilite on which status field(s):");
1505     if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &statfield_picks)) > 0) {
1506         for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
1507             opt_idx = statfield_picks[pick_idx].item.a_int - 1;
1508             field_picks[opt_idx] = 1;
1509         }
1510         free((genericptr_t) statfield_picks);
1511         statfield_picks = (menu_item *) 0;
1512     }
1513     destroy_nhwindow(tmpwin);
1514     if (pick_cnt < 0)
1515         return FALSE;
1516
1517     for (i = 0; i < MAXBLSTATS; i++) {
1518         if (field_picks[i]) {
1519             menu_item *pick = (menu_item *) 0;
1520             Sprintf(buf, "Threshold behavior options for %s:",
1521                     fieldids[i].fieldname);
1522             tmpwin = create_nhwindow(NHW_MENU);
1523             start_menu(tmpwin);
1524             if (i == BL_CONDITION) {
1525                 any = zeroany;
1526                 any.a_int = BL_TH_CONDITION + 1;
1527                 add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE,
1528                          "Condition bitmask threshold.", MENU_UNSELECTED);
1529             }
1530             any = zeroany;
1531             any.a_int = BL_TH_NONE + 1;
1532             add_menu(tmpwin, NO_GLYPH, &any, 'n', 0, ATR_NONE, "None",
1533                      MENU_UNSELECTED);
1534             if (i != BL_CONDITION) {
1535                 if (blstats[0][i].idxmax > 0) {
1536                     any = zeroany;
1537                     any.a_int = BL_TH_VAL_PERCENTAGE + 1;
1538                     add_menu(tmpwin, NO_GLYPH, &any, 'p', 0, ATR_NONE,
1539                              "Percentage threshold.", MENU_UNSELECTED);
1540                 }
1541                 any = zeroany;
1542                 any.a_int = BL_TH_UPDOWN + 1;
1543                 add_menu(tmpwin, NO_GLYPH, &any, 'u', 0, ATR_NONE,
1544                          "UpDown threshold.", MENU_UNSELECTED);
1545                 any = zeroany;
1546                 any.a_int = BL_TH_VAL_ABSOLUTE + 1;
1547                 add_menu(tmpwin, NO_GLYPH, &any, 'v', 0, ATR_NONE,
1548                          "Value threshold.", MENU_UNSELECTED);
1549             }
1550             end_menu(tmpwin, buf);
1551             if ((res = select_menu(tmpwin, PICK_ONE, &pick)) > 0) {
1552                 hltemp[i].behavior = pick->item.a_int - 1;
1553                 free((genericptr_t) pick);
1554             }
1555             destroy_nhwindow(tmpwin);
1556             if (res < 0)
1557                 return FALSE;
1558
1559             if (hltemp[i].behavior == BL_TH_UPDOWN) {
1560                 Sprintf(below, "%s decreases", fieldids[i].fieldname);
1561                 Sprintf(above, "%s increases", fieldids[i].fieldname);
1562             } else if (hltemp[i].behavior) {
1563                 /* Have them enter the threshold*/
1564                 Sprintf(
1565                     buf, "Set %s threshold to what%s?", fieldids[i].fieldname,
1566                     (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE)
1567                         ? " percentage"
1568                         : (hltemp[i].behavior == BL_TH_CONDITION) ? " mask"
1569                                                                   : "");
1570                 getlin(buf, thresholdbuf);
1571                 if (thresholdbuf[0] == '\033')
1572                     return FALSE;
1573                 (void) s_to_anything(&hltemp[i].threshold, thresholdbuf,
1574                                      blstats[0][i].anytype);
1575                 if (!hltemp[i].threshold.a_void)
1576                     return FALSE;
1577
1578                 Sprintf(below, "%s falls below %s%s", fieldids[i].fieldname,
1579                         thresholdbuf,
1580                         (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE) ? "%"
1581                                                                      : "");
1582                 Sprintf(above, "%s rises above %s%s", fieldids[i].fieldname,
1583                         thresholdbuf,
1584                         (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE) ? "%"
1585                                                                      : "");
1586             }
1587             for (j = 0; j < 2 && (hltemp[i].behavior != BL_TH_NONE); ++j) {
1588                 char prompt[QBUFSZ];
1589                 /* j == 0 below, j == 1 above */
1590                 menu_item *pick2 = (menu_item *) 0;
1591
1592                 Sprintf(prompt, "Display how when %s?", j ? above : below);
1593                 tmpwin = create_nhwindow(NHW_MENU);
1594                 start_menu(tmpwin);
1595                 for (k = -3; k < CLR_MAX; ++k) {
1596                     /* if (k == -1) continue; */
1597                     any = zeroany;
1598                     any.a_int = (k >= 0) ? k + 1 : k;
1599                     if (k > 0)
1600                         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1601                                  c_obj_colors[k], MENU_UNSELECTED);
1602                     else if (k == -1)
1603                         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1604                                  "normal", MENU_UNSELECTED);
1605                     else if (k == -2)
1606                         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1607                                  "inverse", MENU_UNSELECTED);
1608                     else if (k == -3)
1609                         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1610                                  "bold", MENU_UNSELECTED);
1611                 }
1612                 end_menu(tmpwin, prompt);
1613                 if ((res = select_menu(tmpwin, PICK_ONE, &pick2)) > 0) {
1614                     hltemp[i].coloridx[j] = (pick2->item.a_char > 0)
1615                                                 ? pick2->item.a_int - 1
1616                                                 : pick2->item.a_int;
1617                     free((genericptr_t) pick2);
1618                 }
1619                 destroy_nhwindow(tmpwin);
1620                 if (res < 0)
1621                     return FALSE;
1622             }
1623         }
1624     }
1625     buf[0] = '\0';
1626     for (i = 0; i < MAXBLSTATS; i++) {
1627         if (field_picks[i]) {
1628             Sprintf(eos(buf), "%s/%s%s/", fieldids[i].fieldname,
1629                     (hltemp[i].behavior == BL_TH_UPDOWN)
1630                         ? "updown"
1631                         : anything_to_s(thresholdbuf, &hltemp[i].threshold,
1632                                         blstats[0][i].anytype),
1633                     (hltemp[i].behavior == BL_TH_VAL_PERCENTAGE) ? "%" : "");
1634             /* borrow thresholdbuf for use with these last two */
1635             Sprintf(eos(buf), "%s/",
1636                     clridx_to_s(thresholdbuf, hltemp[i].coloridx[0]));
1637             Sprintf(eos(buf), "%s ",
1638                     clridx_to_s(thresholdbuf, hltemp[i].coloridx[1]));
1639         }
1640     }
1641     return set_status_hilites(buf, FALSE);
1642 }
1643 #endif /*STATUS_HILITES*/
1644 #endif /*STATUS_VIA_WINDOWPORT*/
1645
1646 /*botl.c*/