OSDN Git Service

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