1 /* NetHack 3.7 winstat.c $NHDT-Date: 1649269127 2022/04/06 18:18:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.37 $ */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
6 * Status window routines. This file supports both the "traditional"
7 * tty status display and a "fancy" status display. A tty status is
8 * made if a popup window is requested, otherwise a fancy status is
9 * made. This code assumes that only one fancy status will ever be made.
10 * Currently, only one status window (of any type) is _ever_ made.
14 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
17 #include <X11/Intrinsic.h>
18 #include <X11/IntrinsicP.h> /* for XtResizeWidget() and XtConfigureWidget() */
19 #include <X11/StringDefs.h>
20 #include <X11/Shell.h>
21 #include <X11/Xaw/AsciiText.h>
22 #include <X11/Xaw/Cardinals.h> /* just for ONE, TWO */
23 #include <X11/Xaw/Form.h>
24 #include <X11/Xaw/Paned.h>
25 #include <X11/Xaw/Label.h>
26 #include <X11/Xaw/Viewport.h>
27 /*#include <X11/Xatom.h>*/
29 #ifdef PRESERVE_NO_SYSV
33 #undef PRESERVE_NO_SYSV
41 * Fancy status form entry storage indices.
51 #define F_NAME 7 /* title: "Name the Rank" where rank is role-specific */
52 #define F_DLEVEL 8 /* location: dungeon branch and level */
60 /*#define F_HD F_XP_LEVL*/
66 /* status conditions grouped by columns; tty orders these differently;
67 hunger/encumbrance/movement used to be in the middle with fatal
68 conditions on the left but those columns have been swapped and
69 renumbered to match new order (forcing shown_stats[] to be reordered);
70 some mutually exclusive conditions are overloaded during display--
71 they're separate within shown_stats[] but share the same widget */
75 #define F_TETHERED 23 /* overloads trapped rather than having its own slot */
86 #define F_IN_LAVA 33 /* could overload trapped but severity differs a lot */
88 #define F_HELD 34 /* could overload grabbed but severity differs a lot */
89 #define F_HOLDING 35 /* overloads held */
96 #define F_VERS 41 /* version info */
99 static int condcolor(long, unsigned long *);
100 static int condattr(long, unsigned long *);
101 static void HiliteField(Widget
, int, int, int, XFontStruct
**);
102 static void PrepStatusField(int, Widget
, const char *);
103 static void DisplayCond(int, unsigned long *);
104 static int render_conditions(int, int);
105 #ifdef STATUS_HILITES
106 static void tt_reset_color(int, int, unsigned long *);
108 static void tt_status_fixup(void);
109 static Widget
create_tty_status_field(int, int, Widget
, Widget
);
110 static Widget
create_tty_status(Widget
, Widget
);
111 static void stat_resized(Widget
, XtPointer
, XtPointer
);
112 static void update_fancy_status_field(int, int, int);
113 static void update_fancy_status(boolean
);
114 static Widget
create_fancy_status(Widget
, Widget
);
115 static void destroy_fancy_status(struct xwindow
*);
116 static void create_status_window_fancy(struct xwindow
*, boolean
, Widget
);
117 static void create_status_window_tty(struct xwindow
*, boolean
, Widget
);
118 static void destroy_status_window_fancy(struct xwindow
*);
119 static void destroy_status_window_tty(struct xwindow
*);
120 static void adjust_status_fancy(struct xwindow
*, const char *);
121 static void adjust_status_tty(struct xwindow
*, const char *);
123 extern const char *status_fieldfmt
[MAXBLSTATS
];
124 extern char *status_vals
[MAXBLSTATS
];
125 extern boolean status_activefields
[MAXBLSTATS
];
127 static unsigned long X11_condition_bits
, old_condition_bits
;
128 static int X11_status_colors
[MAXBLSTATS
],
129 old_field_colors
[MAXBLSTATS
],
131 static int hpbar_percent
, hpbar_color
;
132 /* Number of conditions displayed during this update and last update.
133 When the last update had more, the excess need to be erased. */
134 static int next_cond_indx
= 0, prev_cond_indx
= 0;
136 /* TODO: support statuslines:3 in addition to 2 for the tty-style status */
137 #define X11_NUM_STATUS_LINES 2
138 #define X11_NUM_STATUS_FIELD 16
140 static enum statusfields X11_fieldorder
[][X11_NUM_STATUS_FIELD
] = {
141 { BL_TITLE
, BL_STR
, BL_DX
, BL_CO
, BL_IN
, BL_WI
, BL_CH
, BL_ALIGN
,
142 BL_SCORE
, BL_FLUSH
, BL_FLUSH
, BL_FLUSH
, BL_FLUSH
, BL_FLUSH
,
143 BL_FLUSH
, BL_FLUSH
},
144 { BL_LEVELDESC
, BL_GOLD
, BL_HP
, BL_HPMAX
, BL_ENE
, BL_ENEMAX
,
145 BL_AC
, BL_XP
, BL_EXP
, BL_HD
, BL_TIME
, BL_HUNGER
,
146 BL_CAP
, BL_CONDITION
, BL_VERS
, BL_FLUSH
}
149 /* condition list for tty-style display, roughly in order of importance */
150 static struct tt_condinfo
{
154 { BL_MASK_GRAB
, "Grabbed!" },
155 { BL_MASK_STONE
, "Stone" },
156 { BL_MASK_SLIME
, "Slime" },
157 { BL_MASK_STRNGL
, "Strngl" },
158 { BL_MASK_FOODPOIS
, "FoodPois" },
159 { BL_MASK_TERMILL
, "TermIll" },
160 { BL_MASK_INLAVA
, "InLava" },
161 { BL_MASK_HELD
, "Held" },
162 { BL_MASK_HOLDING
, "Holding" },
163 { BL_MASK_BLIND
, "Blind" },
164 { BL_MASK_DEAF
, "Deaf" },
165 { BL_MASK_STUN
, "Stun" },
166 { BL_MASK_CONF
, "Conf" },
167 { BL_MASK_HALLU
, "Hallu" },
168 { BL_MASK_TRAPPED
, "Trapped" },
169 { BL_MASK_TETHERED
, "Tethered", },
170 { BL_MASK_LEV
, "Lev" },
171 { BL_MASK_FLY
, "Fly" },
172 { BL_MASK_RIDE
, "Ride" },
175 static const char *const fancy_status_hilite_colors
[] = {
194 static Widget X11_status_widget
;
195 static Widget X11_status_labels
[MAXBLSTATS
];
196 static Widget X11_cond_labels
[32]; /* Ugh */
197 static XFontStruct
*X11_status_font
;
198 static Pixel X11_status_fg
, X11_status_bg
;
200 struct xwindow
*xw_status_win
;
203 condcolor(long bm
, unsigned long *bmarray
)
208 for (i
= 0; i
< CLR_MAX
; ++i
) {
209 if (bmarray
[i
] && (bm
& bmarray
[i
]))
216 condattr(long bm
, unsigned long *bmarray
)
222 for (i
= HL_ATTCLR_BOLD
; i
< BL_ATTCLR_MAX
; ++i
) {
223 if (bmarray
[i
] && (bm
& bmarray
[i
])) {
231 case HL_ATTCLR_ITALIC
:
234 case HL_ATTCLR_ULINE
:
237 case HL_ATTCLR_BLINK
:
240 case HL_ATTCLR_INVERSE
:
251 X11_status_init(void)
255 /* no color and no attributes */
256 for (i
= 0; i
< MAXBLSTATS
; ++i
)
257 X11_status_colors
[i
] = old_field_colors
[i
] = NO_COLOR
;
258 for (i
= 0; i
< SIZE(old_cond_colors
); ++i
)
259 old_cond_colors
[i
] = NO_COLOR
;
260 hpbar_percent
= 0, hpbar_color
= NO_COLOR
;
261 X11_condition_bits
= old_condition_bits
= 0L;
262 /* let genl_status_init do most of the initialization */
267 X11_status_finish(void)
274 X11_status_enablefield(int fieldidx
, const char *nm
,
275 const char *fmt
, boolean enable
)
277 genl_status_enablefield(fieldidx
, nm
, fmt
, enable
);
282 cond_bm2idx(unsigned long bm
)
286 for (i
= 0; i
< 32; i
++)
293 /* highlight a tty-style status field (or condition) */
295 HiliteField(Widget label
,
296 int fld
, int cond
, int colrattr
,
297 XFontStruct
**font_p
)
299 #ifdef STATUS_HILITES
300 static Pixel grayPxl
, blackPxl
, whitePxl
;
303 XFontStruct
*font
= X11_status_font
;
304 Pixel px
, fg
= X11_status_fg
, bg
= X11_status_bg
;
305 struct xwindow
*xw
= xw_status_win
;
308 if ((colrattr
& 0x00ff) >= CLR_MAX
)
309 colrattr
= (colrattr
& ~0x00ff) | NO_COLOR
;
310 colr
= colrattr
& 0x00ff; /* guaranteed to be >= 0 and < CLR_MAX */
311 attr
= (colrattr
>> 8) & 0x00ff;
313 if (!grayPxl
) {/* one-time init */
314 grayPxl
= get_nhcolor(xw
, CLR_GRAY
).pixel
;
315 blackPxl
= get_nhcolor(xw
, CLR_BLACK
).pixel
;
316 whitePxl
= get_nhcolor(xw
, CLR_WHITE
).pixel
;
318 /* [shouldn't be necessary; setting up gray will set up all colors] */
319 if (colr
!= NO_COLOR
&& !xw
->nh_colors
[colr
].pixel
)
320 (void) get_nhcolor(xw
, colr
);
322 /* handle highlighting if caller has specified that; set foreground,
323 background, and font even if not specified this time in case they
324 used modified values last time (which would stick if not reset) */
325 (void) memset((genericptr_t
) args
, 0, sizeof args
);
327 if (colr
!= NO_COLOR
)
328 fg
= xw
->nh_colors
[colr
].pixel
;
329 if ((attr
& HL_INVERSE
) != 0) {
334 /* foreground and background might both default to black, so we
335 need to force one to be different if/when they're the same
336 (actually, tt_status_fixup() takes care of that nowadays);
337 using gray to implement 'dim' only works for black and white
338 (or color+'inverse' when former background was black or white) */
340 || ((attr
& HL_DIM
) != 0 && (fg
== whitePxl
|| fg
== blackPxl
)))
341 fg
= (fg
!= grayPxl
) ? grayPxl
342 : (fg
!= blackPxl
) ? blackPxl
344 XtSetArg(args
[num_args
], nhStr(XtNforeground
), fg
); num_args
++;
345 XtSetArg(args
[num_args
], nhStr(XtNbackground
), bg
); num_args
++;
346 if (attr
& HL_BOLD
) {
347 load_boldfont(xw_status_win
, label
);
348 if (xw_status_win
->boldfs
)
349 font
= xw_status_win
->boldfs
;
351 XtSetArg(args
[num_args
], nhStr(XtNfont
), font
); num_args
++;
352 XtSetValues(label
, args
, num_args
);
354 /* return possibly modified font to caller so that text width
355 measurement can use it */
358 #else /*!STATUS_HILITES*/
361 #endif /*?STATUS_HILITES*/
362 if (fld
!= BL_CONDITION
)
363 old_field_colors
[fld
] = colrattr
;
365 old_cond_colors
[cond
] = colrattr
;
368 /* set up a specific field other than 'condition'; its general location
369 was specified during widget creation but it might need adjusting */
371 PrepStatusField(int fld
, Widget label
, const char *text
)
376 XFontStruct
*font
= X11_status_font
;
377 int colrattr
= X11_status_colors
[fld
];
378 struct status_info_t
*si
= xw_status_win
->Win_info
.Status_info
;
380 /* highlight if color and/or attribute(s) are different from last time */
381 if (colrattr
!= old_field_colors
[fld
])
382 HiliteField(label
, fld
, 0, colrattr
, &font
);
385 (void) memset((genericptr_t
) args
, 0, sizeof args
);
386 /* set up the current text to be displayed */
388 lbl_wid
= 2 * si
->in_wd
+ XTextWidth(font
, text
, (int) strlen(text
));
393 XtSetArg(args
[num_args
], nhStr(XtNlabel
), text
); num_args
++;
394 /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
395 XtSetValues(label
, args
, num_args
);
396 XtResizeWidget(label
, lbl_wid
, si
->ht
, si
->brd
);
399 /* set up one status condition for tty-style status display */
402 int c_idx
, /* index into tt_condorder[] */
403 unsigned long *colormasks
)
409 XFontStruct
*font
= X11_status_font
;
410 int coloridx
, attrmask
, colrattr
, idx
;
411 unsigned long bm
= tt_condorder
[c_idx
].mask
;
412 const char *text
= tt_condorder
[c_idx
].text
;
413 struct status_info_t
*si
= xw_status_win
->Win_info
.Status_info
;
415 if ((X11_condition_bits
& bm
) == 0)
418 /* widgets have been created for every condition; we allocate them
419 from left to right rather than keeping their original assignments */
420 idx
= next_cond_indx
;
421 label
= X11_cond_labels
[idx
];
423 /* handle highlighting if caller requests it */
424 coloridx
= condcolor(bm
, colormasks
);
425 attrmask
= condattr(bm
, colormasks
);
426 colrattr
= (attrmask
<< 8) | coloridx
;
427 if (colrattr
!= old_cond_colors
[c_idx
])
428 HiliteField(label
, BL_CONDITION
, c_idx
, colrattr
, &font
);
430 (void) memset((genericptr_t
) args
, 0, sizeof args
);
432 /* set the condition text and its width; this widget might have
433 been displaying a different condition last time around */
434 XtSetArg(args
[num_args
], nhStr(XtNlabel
), text
); num_args
++;
435 /* measure width after maybe changing font [HiliteField()] */
436 lbl_wid
= 2 * si
->in_wd
+ XTextWidth(font
, text
, (int) strlen(text
));
437 /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
439 /* make this condition widget be ready for display */
440 XtSetValues(label
, args
, num_args
);
441 XtResizeWidget(label
, lbl_wid
, si
->ht
, si
->brd
);
446 /* display the tty-style status conditions; the number shown varies and
447 we might be showing more, same, or fewer than during previous status */
449 render_conditions(int row
, int dx
)
456 struct status_info_t
*si
= xw_status_win
->Win_info
.Status_info
;
457 Dimension lbl_wid
, brd_wid
= si
->brd
;
459 for (i
= 0; i
< next_cond_indx
; i
++) {
460 label
= X11_cond_labels
[i
];
462 /* width of this widget was set in DisplayCond(); fetch it */
463 (void) memset((genericptr_t
) args
, 0, sizeof args
);
465 XtSetArg(args
[num_args
], nhStr(XtNwidth
), &lbl_wid
); num_args
++;
466 XtGetValues(label
, args
, num_args
);
468 /* figure out where to draw this widget and place it there */
469 lbl_x
= (Position
) (dx
+ 1 + gap
);
471 XtConfigureWidget(label
, lbl_x
, si
->y
[row
], lbl_wid
, si
->ht
, brd_wid
);
473 /* keep track of where the end of our text appears */
474 dx
= (int) lbl_x
+ (int) (lbl_wid
+ 2 * brd_wid
) - 1;
477 /* if we have fewer conditions shown now than last time, set the
478 excess ones to blank; unlike the set drawn above, these haven't
479 been prepared in advance by DisplayCond because they aren't
480 being displayed; they might have been highlighted last time so
481 we need to specify more than just an empty text string */
482 if (next_cond_indx
< prev_cond_indx
) {
483 XFontStruct
*font
= X11_status_font
;
484 Pixel fg
= X11_status_fg
, bg
= X11_status_bg
;
487 lbl_wid
= 1 + 2 * brd_wid
;
488 for (i
= next_cond_indx
; i
< prev_cond_indx
; ++i
) {
489 label
= X11_cond_labels
[i
];
491 (void) memset((genericptr_t
) args
, 0, sizeof args
);
493 XtSetArg(args
[num_args
], nhStr(XtNlabel
), ""); num_args
++;
494 XtSetArg(args
[num_args
], nhStr(XtNfont
), font
); num_args
++;
495 XtSetArg(args
[num_args
], nhStr(XtNforeground
), fg
); num_args
++;
496 XtSetArg(args
[num_args
], nhStr(XtNbackground
), bg
); num_args
++;
497 /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
498 /*XtSetArg(args[num_args], nhStr(XtNx), lbl_x); num_args++;*/
499 XtSetValues(label
, args
, num_args
);
500 old_cond_colors
[i
] = NO_COLOR
; /* fg, bg, font were just reset */
501 XtConfigureWidget(label
, lbl_x
, si
->y
[row
], lbl_wid
, si
->ht
, 0);
502 /* don't advance 'dx' here */
509 #ifdef STATUS_HILITES
510 /* reset status_hilite for BL_RESET; if highlighting has been disabled or
511 this field is disabled, clear highlighting for this field or condition */
516 unsigned long *colormasks
)
519 int colrattr
= NO_COLOR
;
521 if (fld
!= BL_CONDITION
) {
522 if (iflags
.hilite_delta
!= 0L && status_activefields
[fld
])
523 colrattr
= X11_status_colors
[fld
];
525 label
= X11_status_labels
[fld
];
527 unsigned long bm
= tt_condorder
[cond
].mask
;
529 if (iflags
.hilite_delta
!= 0L && (X11_condition_bits
& bm
) != 0) {
530 /* BL_RESET before first BL_CONDITION will have colormasks==Null
531 but condcolor() and condattr() can cope with that */
532 colrattr
= condcolor(bm
, colormasks
);
533 colrattr
|= (condattr(bm
, colormasks
) << 8);
535 label
= X11_cond_labels
[cond
];
537 HiliteField(label
, fld
, cond
, colrattr
, (XFontStruct
**) 0);
541 /* make sure foreground, background, and font have reasonable values,
542 then explicitly set them for all the status widgets;
543 also cache some geometry settings in (*xw_status_win).Status_info */
545 tt_status_fixup(void)
552 XFontStruct
*font
= 0;
553 Pixel fg
= 0, bg
= 0;
554 struct status_info_t
*si
= xw_status_win
->Win_info
.Status_info
;
556 (void) memset((genericptr_t
) args
, 0, sizeof args
);
558 XtSetArg(args
[num_args
], nhStr(XtNfont
), &font
); num_args
++;
559 XtSetArg(args
[num_args
], nhStr(XtNforeground
), &fg
); num_args
++;
560 XtSetArg(args
[num_args
], nhStr(XtNbackground
), &bg
); num_args
++;
561 XtGetValues(X11_status_widget
, args
, num_args
);
563 XColor black
= get_nhcolor(xw_status_win
, CLR_BLACK
),
564 white
= get_nhcolor(xw_status_win
, CLR_WHITE
);
566 fg
= (bg
== white
.pixel
) ? black
.pixel
: white
.pixel
;
568 X11_status_fg
= si
->fg
= fg
, X11_status_bg
= si
->bg
= bg
;
571 w
= X11_status_widget
;
572 XtSetArg(args
[0], nhStr(XtNfont
), &font
);
574 XtGetValues(w
, args
, ONE
);
575 } while (!font
&& (w
= XtParent(w
)) != 0);
578 /* trial and error time -- this is where we've actually
579 been obtaining the font even though we aren't setting
580 it for any of the field widgets (until for(y,x) below) */
581 XtGetValues(X11_status_labels
[0], args
, ONE
);
583 if (!font
) { /* this bit is untested... */
584 /* write some text and hope Xaw sets up font for us */
585 XtSetArg(args
[0], nhStr(XtNlabel
), "NetHack");
586 XtSetValues(X11_status_labels
[0], args
, ONE
);
587 (void) XFlush(XtDisplay(X11_status_labels
[0]));
589 XtSetArg(args
[0], nhStr(XtNfont
), &font
);
590 XtGetValues(X11_status_labels
[0], args
, ONE
);
592 /* if still Null, XTextWidth() would crash so bail out */
594 panic("X11 status can't determine font.");
598 X11_status_font
= si
->fs
= font
;
600 /* amount of space to advance a widget's location by one space;
601 increase width a tiny bit beyond the actual space */
602 si
->spacew
= XTextWidth(X11_status_font
, " ", 1) + (Dimension
) 1;
604 (void) memset((genericptr_t
) args
, 0, sizeof args
);
606 XtSetArg(args
[num_args
], nhStr(XtNfont
), font
); num_args
++;
607 XtSetArg(args
[num_args
], nhStr(XtNforeground
), fg
); num_args
++;
608 XtSetArg(args
[num_args
], nhStr(XtNbackground
), bg
); num_args
++;
609 XtSetValues(X11_status_widget
, args
, num_args
);
611 for (y
= 0; y
< X11_NUM_STATUS_LINES
; y
++) {
612 for (x
= 0; x
< X11_NUM_STATUS_FIELD
; x
++) {
613 fld
= X11_fieldorder
[y
][x
]; /* next field to handle */
615 continue; /* skip fieldorder[][] padding */
617 XtSetValues(X11_status_labels
[fld
], args
, num_args
);
618 old_field_colors
[fld
] = NO_COLOR
;
620 if (fld
== BL_CONDITION
) {
621 for (ci
= 0; ci
< SIZE(X11_cond_labels
); ++ci
) { /* 0..31 */
622 XtSetValues(X11_cond_labels
[ci
], args
, num_args
);
623 old_cond_colors
[ci
] = NO_COLOR
;
629 /* cache the y coordinate of each row for XtConfigureWidget() */
630 (void) memset((genericptr_t
) args
, 0, sizeof args
);
632 XtSetArg(args
[0], nhStr(XtNy
), &lbl_y
); num_args
++;
633 for (y
= 0; y
< X11_NUM_STATUS_LINES
; y
++) {
634 fld
= X11_fieldorder
[y
][0];
635 XtGetValues(X11_status_labels
[fld
], args
, num_args
);
639 /* cache height, borderWidth, and internalWidth for XtResizeWidget() */
640 (void) memset((genericptr_t
) args
, 0, sizeof args
);
642 XtSetArg(args
[num_args
], nhStr(XtNheight
), &si
->ht
); num_args
++;
643 XtSetArg(args
[num_args
], nhStr(XtNborderWidth
), &si
->brd
); num_args
++;
644 XtSetArg(args
[num_args
], nhStr(XtNinternalWidth
), &si
->in_wd
); num_args
++;
645 XtGetValues(X11_status_labels
[0], args
, num_args
);
647 /* X11_status_update_tty() wants this in order to right justify 'vers' */
648 if (!xw_status_win
->pixel_width
) {
649 XtSetArg(args
[0], nhStr(XtNwidth
), &xw_status_win
->pixel_width
);
650 XtGetValues(xw_status_win
->w
, args
, ONE
);
654 DISABLE_WARNING_FORMAT_NONLITERAL
656 /* core requests updating one status field (or is indicating that it's time
657 to flush all updated fields); tty-style handling */
659 X11_status_update_tty(
665 unsigned long *colormasks
) /* bitmask of highlights for conditions */
667 static int xtra_space
[MAXBLSTATS
];
668 static unsigned long *cond_colormasks
= (unsigned long *) 0;
673 Dimension lbl_wid
, brd_wid
;
676 struct status_info_t
*si
;
680 unsigned long *condptr
;
683 if (X11_status_fg
== X11_status_bg
|| !X11_status_font
)
686 if (fld
== BL_RESET
) {
687 #ifdef STATUS_HILITES
688 for (y
= 0; y
< X11_NUM_STATUS_LINES
; y
++) {
689 for (x
= 0; x
< X11_NUM_STATUS_FIELD
; x
++) {
690 f
= X11_fieldorder
[y
][x
];
692 continue; /* skip padding in the fieldorder[][] layout */
693 if (f
!= BL_CONDITION
) {
694 tt_reset_color(f
, 0, (unsigned long *) 0);
698 for (c_i
= 0; c_i
< SIZE(tt_condorder
); ++c_i
)
699 tt_reset_color(f
, c_i
, cond_colormasks
);
707 if (fld
== BL_CONDITION
) {
708 condptr
= (unsigned long *) ptr
;
709 X11_condition_bits
= *condptr
;
710 cond_colormasks
= colormasks
; /* expected to be non-Null */
712 prev_cond_indx
= next_cond_indx
;
714 /* if any conditions are active, set up their widgets */
715 if (X11_condition_bits
)
716 for (f
= 0; f
< SIZE(tt_condorder
); ++f
)
717 DisplayCond(f
, cond_colormasks
);
720 } else if (fld
!= BL_FLUSH
) {
721 /* set up a specific field other than 'condition' */
724 text
= decode_mixed(goldbuf
, text
);
726 if (status_activefields
[fld
]) {
727 fmt
= (fld
== BL_TITLE
&& iflags
.wc2_hitpointbar
) ? "%-30s"
728 : status_fieldfmt
[fld
] ? status_fieldfmt
[fld
] : "%s";
733 Sprintf(status_vals
[fld
], fmt
, text
);
735 /* don't expect this since core won't call status_update()
736 for a field which isn't active */
737 *status_vals
[fld
] = '\0';
739 #ifdef STATUS_HILITES
740 if (!iflags
.hilite_delta
)
743 X11_status_colors
[fld
] = color
;
744 if (iflags
.wc2_hitpointbar
&& fld
== BL_HP
) {
745 hpbar_percent
= percent
;
749 label
= X11_status_labels
[fld
];
750 text
= status_vals
[fld
];
751 PrepStatusField(fld
, label
, text
);
756 * BL_FLUSH: draw all the status fields.
758 si
= xw_status_win
->Win_info
.Status_info
; /* for cached geometry */
760 for (y
= 0; y
< X11_NUM_STATUS_LINES
; y
++) { /* row */
761 dx
= 0; /* no pixels written to this row yet */
763 for (x
= 0; x
< X11_NUM_STATUS_FIELD
; x
++) { /* 'column' */
764 f
= X11_fieldorder
[y
][x
];
766 continue; /* skip padding in the fieldorder[][] layout */
768 if (f
== BL_CONDITION
) {
769 if (next_cond_indx
> 0 || prev_cond_indx
> 0)
770 dx
= render_conditions(y
, dx
);
774 label
= X11_status_labels
[f
];
775 text
= status_vals
[f
];
777 (void) memset((genericptr_t
) args
, 0, sizeof args
);
779 XtSetArg(args
[num_args
], nhStr(XtNwidth
), &lbl_wid
); num_args
++;
780 XtGetValues(label
, args
, num_args
);
783 /* for a field which shouldn't be shown, we can't just skip
784 it because we might need to remove its previous content
785 if it has just been toggled off */
786 if (!status_activefields
[f
]) {
790 lbl_wid
= (Dimension
) 1;
791 brd_wid
= (Dimension
) 0;
792 } else if (xtra_space
[f
]) {
793 /* if this field was to be formatted with a leading space
794 to separate it from the preceding field, we suppressed
795 that space during formatting; insert separation between
796 fields here; this prevents inverse video highlighting
797 from inverting the separating space along with the text */
798 dx
+= xtra_space
[f
] * si
->spacew
;
801 /* where to display the current widget */
802 lbl_x
= (Position
) (dx
+ 1);
804 Dimension win_wid
= xw_status_win
->pixel_width
,
805 fld_wid
= lbl_wid
+ 2 * brd_wid
;
807 /* in case the doodad for resizing the window via click and
808 drag is in the lower right corner; justifying all the way
809 to the edge results in the last character being obscured;
810 avoid that by treating the 'vers' widget as one char
811 bigger than it actually is (an implicit trailing space) */
812 fld_wid
+= si
->spacew
;
813 /* right justify if there's room */
814 if (dx
< win_wid
- fld_wid
)
815 lbl_x
= win_wid
- fld_wid
;
818 (void) memset((genericptr_t
) args
, 0, sizeof args
);
820 XtSetArg(args
[num_args
], nhStr(XtNlabel
), text
); num_args
++;
821 /*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
822 /*XtSetArg(args[num_args], nhStr(XtNx), lbl_x); num_args++;*/
823 XtSetValues(label
, args
, num_args
);
824 XtConfigureWidget(label
, lbl_x
, si
->y
[y
],
825 lbl_wid
, si
->ht
, brd_wid
);
827 /* track the right-most pixel written so far */
828 dx
= (int) lbl_x
+ (int) (lbl_wid
+ 2 * brd_wid
) - 1;
829 } /* [x] fields/columns in current row */
832 /* this probably doesn't buy us anything but it isn't a sure thing
833 that nethack will immediately ask for input (triggering auto-flush) */
834 (void) XFlush(XtDisplay(X11_status_labels
[0]));
837 RESTORE_WARNING_FORMAT_NONLITERAL
841 X11_status_update_fancy(
844 int chg UNUSED
, int percent UNUSED
,
846 unsigned long *colormasks UNUSED
)
848 static const struct bl_to_ff
{
850 } bl_to_fancyfield
[] = {
851 { BL_TITLE
, F_NAME
},
858 { BL_ALIGN
, F_ALIGN
},
859 { BL_SCORE
, F_SCORE
},
860 { BL_CAP
, F_ENCUMBER
},
863 { BL_ENEMAX
, F_MAXPOWER
},
864 { BL_XP
, F_XP_LEVL
}, /* shares with BL_HD, depending upon Upolyd */
867 { BL_HUNGER
, F_HUNGER
},
869 { BL_HPMAX
, F_MAXHP
},
870 { BL_LEVELDESC
, F_DLEVEL
},
872 { BL_EXP
, F_EXP_PTS
}
874 static const struct mask_to_ff
{
877 } mask_to_fancyfield
[] = {
878 { BL_MASK_GRAB
, F_GRABBED
},
879 { BL_MASK_STONE
, F_STONE
},
880 { BL_MASK_SLIME
, F_SLIME
},
881 { BL_MASK_STRNGL
, F_STRNGL
},
882 { BL_MASK_FOODPOIS
, F_FOODPOIS
},
883 { BL_MASK_TERMILL
, F_TERMILL
},
884 { BL_MASK_INLAVA
, F_IN_LAVA
},
885 { BL_MASK_HELD
, F_HELD
},
886 { BL_MASK_HOLDING
, F_HOLDING
},
887 { BL_MASK_BLIND
, F_BLIND
},
888 { BL_MASK_DEAF
, F_DEAF
},
889 { BL_MASK_STUN
, F_STUN
},
890 { BL_MASK_CONF
, F_CONF
},
891 { BL_MASK_HALLU
, F_HALLU
},
892 { BL_MASK_TRAPPED
, F_TRAPPED
},
893 { BL_MASK_TETHERED
, F_TETHERED
},
894 { BL_MASK_LEV
, F_LEV
},
895 { BL_MASK_FLY
, F_FLY
},
896 { BL_MASK_RIDE
, F_RIDE
}
900 if (fld
== BL_RESET
|| fld
== BL_FLUSH
) {
901 if (WIN_STATUS
!= WIN_ERR
) {
902 update_fancy_status(FALSE
);
907 if (fld
== BL_CONDITION
) {
908 unsigned long changed_bits
, *condptr
= (unsigned long *) ptr
;
910 X11_condition_bits
= *condptr
;
911 /* process the bits that are different from last time */
912 changed_bits
= (X11_condition_bits
^ old_condition_bits
);
914 for (i
= 0; i
< SIZE(mask_to_fancyfield
); i
++)
915 if ((changed_bits
& mask_to_fancyfield
[i
].mask
) != 0L)
916 update_fancy_status_field(mask_to_fancyfield
[i
].ff
,
917 condcolor(mask_to_fancyfield
[i
].mask
, colormasks
),
918 condattr(mask_to_fancyfield
[i
].mask
, colormasks
));
919 old_condition_bits
= X11_condition_bits
; /* remember 'On' bits */
924 if ((colrattr
& 0x00ff) >= CLR_MAX
)
925 colrattr
= (colrattr
& ~0x00ff) | NO_COLOR
;
926 colr
= colrattr
& 0x00ff; /* guaranteed to be >= 0 and < CLR_MAX */
927 attr
= (colrattr
>> 8) & 0x00ff;
929 for (i
= 0; i
< SIZE(bl_to_fancyfield
); i
++)
930 if (bl_to_fancyfield
[i
].bl
== fld
) {
931 update_fancy_status_field(bl_to_fancyfield
[i
].ff
, colr
, attr
);
941 int chg
, int percent
,
943 unsigned long *colormasks
)
945 if (fld
< BL_RESET
|| fld
>= MAXBLSTATS
)
946 panic("X11_status_update(%d) -- invalid field", fld
);
948 if (appResources
.fancy_status
)
949 X11_status_update_fancy(fld
, ptr
, chg
, percent
, color
, colormasks
);
951 X11_status_update_tty(fld
, ptr
, chg
, percent
, color
, colormasks
);
954 /* create a widget for a particular status field or potential condition */
956 create_tty_status_field(int fld
, int condindx
, Widget above
, Widget left
)
961 int gap
= condindx
? 5 : 0;
964 Sprintf(labelname
, "label_%s", bl_idx_to_fldname(fld
));
966 Sprintf(labelname
, "cond_%02d", condindx
);
968 /* set up widget attributes which (mostly) aren't going to be changing */
969 (void) memset((genericptr_t
) args
, 0, sizeof args
);
972 XtSetArg(args
[num_args
], nhStr(XtNfromVert
), above
); num_args
++;
975 XtSetArg(args
[num_args
], nhStr(XtNfromHoriz
), left
); num_args
++;
978 XtSetArg(args
[num_args
], nhStr(XtNhorizDistance
), gap
); num_args
++;
979 XtSetArg(args
[num_args
], nhStr(XtNvertDistance
), 0); num_args
++;
981 XtSetArg(args
[num_args
], nhStr(XtNtopMargin
), 0); num_args
++;
982 XtSetArg(args
[num_args
], nhStr(XtNbottomMargin
), 0); num_args
++;
983 XtSetArg(args
[num_args
], nhStr(XtNleftMargin
), 0); num_args
++;
984 XtSetArg(args
[num_args
], nhStr(XtNrightMargin
), 0); num_args
++;
985 XtSetArg(args
[num_args
], nhStr(XtNjustify
), XtJustifyLeft
); num_args
++;
986 /* internalWidth: default is 4; cut that it half and adjust regular
987 width to have it on both left and right instead of just on the left */
988 XtSetArg(args
[num_args
], nhStr(XtNinternalWidth
), 2); num_args
++;
989 XtSetArg(args
[num_args
], nhStr(XtNborderWidth
), 0); num_args
++;
990 XtSetArg(args
[num_args
], nhStr(XtNlabel
), ""); num_args
++;
991 return XtCreateManagedWidget(labelname
, labelWidgetClass
,
992 X11_status_widget
, args
, num_args
);
995 /* create an overall status widget (X11_status_widget) and also
996 separate widgets for all status fields and potential conditions */
998 create_tty_status(Widget parent
, Widget top
)
1000 Widget form
; /* viewport that holds the form that surrounds everything */
1001 Widget w
, over_w
, prev_w
;
1006 (void) memset((genericptr_t
) args
, 0, sizeof args
);
1009 XtSetArg(args
[num_args
], nhStr(XtNfromVert
), top
); num_args
++;
1011 XtSetArg(args
[num_args
], nhStr(XtNdefaultDistance
), 0); num_args
++;
1012 XtSetArg(args
[num_args
], XtNborderWidth
, 0); num_args
++;
1013 XtSetArg(args
[num_args
], XtNwidth
, 400); num_args
++;
1014 XtSetArg(args
[num_args
], XtNheight
, 100); num_args
++;
1015 form
= XtCreateManagedWidget("status_viewport", viewportWidgetClass
,
1016 parent
, args
, num_args
);
1018 (void) memset((genericptr_t
) args
, 0, sizeof args
);
1020 XtSetArg(args
[num_args
], XtNwidth
, 400); num_args
++;
1021 XtSetArg(args
[num_args
], XtNheight
, 100); num_args
++;
1022 X11_status_widget
= XtCreateManagedWidget("status_form", formWidgetClass
,
1023 form
, args
, num_args
);
1025 for (y
= 0; y
< X11_NUM_STATUS_LINES
; y
++) {
1026 fld
= (y
> 0) ? X11_fieldorder
[y
- 1][0] : 0; /* temp, for over_w */
1027 /* for row(s) beyond the first, pick a widget in the previous
1028 row to put this one underneath (in 'y' terms; 'x' is fluid) */
1029 over_w
= (y
> 0) ? X11_status_labels
[fld
] : (Widget
) 0;
1030 /* widget on current row to put the next one to the right of ('x') */
1031 prev_w
= (Widget
) 0;
1032 for (x
= 0; x
< X11_NUM_STATUS_FIELD
; x
++) {
1033 fld
= X11_fieldorder
[y
][x
]; /* next field to handle */
1034 if (fld
<= BL_FLUSH
)
1035 continue; /* skip fieldorder[][] padding */
1037 w
= create_tty_status_field(fld
, 0, over_w
, prev_w
);
1038 X11_status_labels
[fld
] = prev_w
= w
;
1040 if (fld
== BL_CONDITION
) {
1041 for (i
= 1; i
<= SIZE(X11_cond_labels
); ++i
) { /* 1..32 */
1042 w
= create_tty_status_field(fld
, i
, over_w
, prev_w
);
1043 X11_cond_labels
[i
- 1] = prev_w
= w
;
1048 return X11_status_widget
;
1053 create_status_window_tty(struct xwindow
*wp
, /* window pointer */
1054 boolean create_popup UNUSED
, Widget parent
)
1056 wp
->type
= NHW_STATUS
;
1057 wp
->w
= create_tty_status(parent
, (Widget
) 0);
1061 destroy_status_window_tty(struct xwindow
*wp
)
1063 /* if status_information is defined, then it is a "text" status window */
1064 if (wp
->status_information
) {
1066 nh_XtPopdown(wp
->popup
);
1067 if (!wp
->keep_window
)
1068 XtDestroyWidget(wp
->popup
), wp
->popup
= (Widget
) 0;
1070 free((genericptr_t
) wp
->status_information
);
1071 wp
->status_information
= 0;
1075 if (!wp
->keep_window
)
1076 wp
->type
= NHW_NONE
;
1081 adjust_status_tty(struct xwindow
*wp UNUSED
, const char *str UNUSED
)
1088 create_status_window(
1089 struct xwindow
*wp
, /* window pointer */
1090 boolean create_popup
,
1093 struct status_info_t
*si
= (struct status_info_t
*) alloc(sizeof *si
);
1096 if (wp
->Win_info
.Status_info
)
1097 free((genericptr_t
) wp
->Win_info
.Status_info
);
1098 wp
->Win_info
.Status_info
= si
;
1099 (void) memset((genericptr_t
) si
, 0, sizeof *si
);
1101 if (!appResources
.fancy_status
)
1102 create_status_window_tty(wp
, create_popup
, parent
);
1104 create_status_window_fancy(wp
, create_popup
, parent
);
1107 * this does not work as intended; it triggers
1108 * "Warning: Cannot find callback list in XtAddCallback"
1110 XtAddCallback(wp
->w
, XtNresizeCallback
, stat_resized
, (XtPointer
) 0);
1112 nhUse(stat_resized
);
1116 /* callback to deal with the game window being resized */
1118 stat_resized(Widget w
, XtPointer call_data
, XtPointer client_data
)
1122 struct xwindow
*wp
= xw_status_win
;
1129 XtSetArg(args
[num_args
], XtNwidth
, &wp
->pixel_width
); num_args
++;
1130 XtSetArg(args
[num_args
], XtNwidth
, &wp
->pixel_height
); num_args
++;
1131 XtGetValues(w
, args
, num_args
);
1133 impossible("Status Window resized, but of what widget?");
1136 /* tell core to call us back for a full status update */
1141 destroy_status_window(struct xwindow
*wp
)
1143 if (appResources
.fancy_status
)
1144 destroy_status_window_fancy(wp
);
1146 destroy_status_window_tty(wp
);
1150 adjust_status(struct xwindow
*wp
, const char *str
)
1152 if (appResources
.fancy_status
)
1153 adjust_status_fancy(wp
, str
);
1155 adjust_status_tty(wp
, str
);
1159 create_status_window_fancy(struct xwindow
*wp
, /* window pointer */
1160 boolean create_popup
, Widget parent
)
1165 Position top_margin
, bottom_margin
, left_margin
, right_margin
;
1167 wp
->type
= NHW_STATUS
;
1169 if (!create_popup
) {
1171 * If we are not creating a popup, then we must be the "main" status
1175 panic("create_status_window_fancy: no parent for fancy status");
1176 wp
->status_information
= 0;
1177 wp
->w
= create_fancy_status(parent
, (Widget
) 0);
1181 wp
->status_information
=
1182 (struct status_info_t
*) alloc(sizeof (struct status_info_t
));
1184 init_text_buffer(&wp
->status_information
->text
);
1187 XtSetArg(args
[num_args
], XtNallowShellResize
, False
); num_args
++;
1188 XtSetArg(args
[num_args
], XtNinput
, False
); num_args
++;
1190 wp
->popup
= parent
= XtCreatePopupShell("status_popup",
1191 topLevelShellWidgetClass
,
1192 toplevel
, args
, num_args
);
1194 * If we're here, then this is an auxiliary status window. If we're
1195 * cancelled via a delete window message, we should just pop down.
1199 XtSetArg(args
[num_args
], nhStr(XtNdisplayCaret
), False
); num_args
++;
1200 XtSetArg(args
[num_args
], nhStr(XtNscrollHorizontal
),
1201 XawtextScrollWhenNeeded
); num_args
++;
1202 XtSetArg(args
[num_args
], nhStr(XtNscrollVertical
),
1203 XawtextScrollWhenNeeded
); num_args
++;
1205 wp
->w
= XtCreateManagedWidget("status", /* name */
1206 asciiTextWidgetClass
,
1207 parent
, /* parent widget */
1208 args
, /* set some values */
1209 num_args
); /* number of values to set */
1212 * Adjust the height and width of the message window so that it
1213 * is two lines high and COLNO of the widest characters wide.
1216 /* Get the font and margin information. */
1218 XtSetArg(args
[num_args
], XtNfont
, &fs
); num_args
++;
1219 XtSetArg(args
[num_args
], nhStr(XtNtopMargin
), &top_margin
); num_args
++;
1220 XtSetArg(args
[num_args
], nhStr(XtNbottomMargin
),
1221 &bottom_margin
); num_args
++;
1222 XtSetArg(args
[num_args
], nhStr(XtNleftMargin
), &left_margin
); num_args
++;
1223 XtSetArg(args
[num_args
], nhStr(XtNrightMargin
),
1224 &right_margin
); num_args
++;
1225 XtGetValues(wp
->w
, args
, num_args
);
1227 wp
->pixel_height
= 2 * nhFontHeight(wp
->w
) + top_margin
+ bottom_margin
;
1228 wp
->pixel_width
= COLNO
* fs
->max_bounds
.width
1229 + left_margin
+ right_margin
;
1231 /* Set the new width and height. */
1233 XtSetArg(args
[num_args
], XtNwidth
, wp
->pixel_width
); num_args
++;
1234 XtSetArg(args
[num_args
], XtNheight
, wp
->pixel_height
); num_args
++;
1235 XtSetValues(wp
->w
, args
, num_args
);
1239 destroy_status_window_fancy(struct xwindow
*wp
)
1241 /* If status_information is defined, then it a "text" status window. */
1242 if (wp
->status_information
) {
1244 nh_XtPopdown(wp
->popup
);
1245 if (!wp
->keep_window
)
1246 XtDestroyWidget(wp
->popup
), wp
->popup
= (Widget
) 0;
1248 free((genericptr_t
) wp
->status_information
);
1249 wp
->status_information
= 0;
1251 destroy_fancy_status(wp
);
1253 if (!wp
->keep_window
)
1254 wp
->type
= NHW_NONE
;
1258 * This assumes several things:
1259 * + Status has only 2 lines
1260 * + That both lines are updated in succession in line order.
1261 * + We didn't set stringInPlace on the widget.
1264 adjust_status_fancy(struct xwindow
*wp
, const char *str
)
1269 if (!wp
->status_information
) {
1270 update_fancy_status(TRUE
);
1274 if (wp
->cursy
== 0) {
1275 clear_text_buffer(&wp
->status_information
->text
);
1276 append_text_buffer(&wp
->status_information
->text
, str
, FALSE
);
1279 append_text_buffer(&wp
->status_information
->text
, str
, FALSE
);
1281 /* Set new buffer as text. */
1283 XtSetArg(args
[num_args
], XtNstring
,
1284 wp
->status_information
->text
.text
); num_args
++;
1285 XtSetValues(wp
->w
, args
, num_args
);
1288 /* Fancy ================================================================== */
1289 extern const char *const hu_stat
[]; /* from eat.c */
1290 extern const char *const enc_stat
[]; /* from botl.c */
1292 struct X_status_value
{
1293 /* we have to cast away 'const' when assigning new names */
1294 const char *name
; /* text name */
1295 int type
; /* status type */
1296 Widget w
; /* widget of name/value pair */
1297 long last_value
; /* value displayed */
1298 int turn_count
; /* last time the value changed */
1299 boolean set
; /* if highlighted */
1300 boolean after_init
; /* don't highlight on first change (init) */
1301 boolean inverted_hilite
; /* if highlit due to hilite_status inverse rule */
1302 Pixel default_fg
; /* what FG color it initialized with */
1303 int colr
, attr
; /* color and attribute */
1306 /* valid type values */
1307 #define SV_VALUE 0 /* displays a label:value pair */
1308 #define SV_LABEL 1 /* displays a changeable label */
1309 #define SV_NAME 2 /* displays an unchangeable name */
1311 /* for overloaded conditions */
1313 unsigned long ovl_mask
;
1316 #define NUM_OVLD 4 /* peak number of overloads for a single field */
1318 unsigned long all_mask
;
1319 struct ovld_item conds
[NUM_OVLD
];
1322 static const struct f_overload
*ff_ovld_from_mask(unsigned long);
1323 static const struct f_overload
*ff_ovld_from_indx(int);
1324 static void hilight_label(Widget
);
1325 static void update_val(struct X_status_value
*, long);
1326 static void skip_cond_val(struct X_status_value
*);
1327 static void update_color(struct X_status_value
*, int);
1328 static boolean
name_widget_has_label(struct X_status_value
*);
1329 static void apply_hilite_attributes(struct X_status_value
*, int);
1330 static const char *width_string(int);
1331 static void create_widget(Widget
, struct X_status_value
*, int);
1332 static void get_widths(struct X_status_value
*, int *, int *);
1333 static void set_widths(struct X_status_value
*, int, int);
1334 static Widget
init_column(const char *, Widget
, Widget
, Widget
, int *, int);
1335 static void fixup_cond_widths(void);
1336 static Widget
init_info_form(Widget
, Widget
, Widget
);
1338 /* narrower values for the array initializer */
1339 #define W0 (Widget) 0
1340 #define P0 (Pixel) 0
1343 * + Alignment needs a different init value, because -1 is an alignment.
1344 * + Armor Class is an schar, so 256 is out of range.
1345 * + Blank value is 0 and should never change.
1347 * - These must be in the same order as the F_foo numbers.
1349 static struct X_status_value shown_stats
[NUM_STATS
] = {
1351 { "", SV_NAME
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1353 { "Strength", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1354 { "Dexterity", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1355 { "Constitution", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1356 { "Intelligence", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1358 { "Wisdom", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1359 { "Charisma", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1361 { "", SV_LABEL
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1363 { "", SV_LABEL
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1364 { "Gold", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1366 { "Hit Points", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1367 { "Max HP", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1368 { "Power", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1369 { "Max Power", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1370 { "Armor Class", SV_VALUE
, W0
, 256L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1372 { "Xp Level", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1373 /* also 15 (overloaded field) */
1374 /*{ "Hit Dice", SV_VALUE, W0, -1L, 0, FALSE, FALSE, FALSE, P0, 0, 0 },*/
1375 /* F_EXP_PTS: 16 (optionally displayed) */
1376 { "Exp Points", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1377 { "Alignment", SV_VALUE
, W0
, -2L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1378 /* 18, optionally displayed */
1379 { "Time", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1380 /* 19, conditionally present, optionally displayed when present */
1381 { "Score", SV_VALUE
, W0
, -1L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1382 /* F_HUNGER: 20 (blank if 'normal') */
1383 { "", SV_NAME
, W0
, -1L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1384 /* F_ENCUMBER: 21 (blank if unencumbered) */
1385 { "", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1386 { "Trapped", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1387 { "Tethered", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1388 { "Levitating", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1390 { "Flying", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1391 { "Riding", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1392 { "Grabbed!", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1394 { "Petrifying", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1395 { "Slimed", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1397 { "Strangled", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1398 { "Food Pois", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1399 { "Term Ill", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1401 { "Sinking", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1402 { "Held", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1404 { "Holding", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1405 { "Blind", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1406 { "Deaf", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1407 { "Stunned", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1408 { "Confused", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1409 /* F_HALLU: 40 (full spelling truncated due to space limitations) */
1410 { "Hallucinat", SV_NAME
, W0
, 0L, 0, FALSE
, TRUE
, FALSE
, P0
, 0, 0 },
1411 /* F_VERS; optionally shown, generally treated as a pseudo-condition */
1412 { "Version 1.2.3", SV_LABEL
, W0
, 0L, 0, FALSE
, FALSE
, FALSE
, P0
, 0, 0 },
1417 * The following are supported by the core but not yet handled here:
1418 * bareh 'bare handed' (no weapon and no gloves)
1419 * busy involved in some multi-turn activity, possibly involuntarily
1420 * elf_iron elf being harmed by contact with iron (not implemented)
1421 * glowhands 'glowing hands' (inflict confuse monster for next N melee hits)
1422 * icy on or above ice terrain (temporary fumbling; might melt)
1423 * parlyz paralyzed (can't move)
1424 * sleeping asleep (can't move; might wake if attacked)
1425 * slippery 'slippery hands' or gloves (will drop non-cursed weapons)
1426 * submerged underwater (severely restricted vision, hampered movement)
1427 * unconsc unconscious (can't move; includes fainted)
1428 * woundedl 'wounded legs' (can't kick; temporary dex loss)
1431 /* some conditions are mutually exclusive so we overload their fields in
1432 order to share same display slot */
1433 static const struct f_overload cond_ovl
[] = {
1434 { (BL_MASK_TRAPPED
| BL_MASK_TETHERED
),
1435 { { BL_MASK_TRAPPED
, F_TRAPPED
},
1436 { BL_MASK_TETHERED
, F_TETHERED
} },
1439 /* BL_GRABBED is mutually exclusive with these but is more severe so
1440 is shown separately rather than being overloaded with them */
1441 (BL_MASK_HELD
| BL_MASK_HOLDING
),
1442 { { BL_MASK_HELD
, F_HELD
},
1443 { BL_MASK_HOLDING
, F_HOLDING
} },
1445 #if 0 /* not yet implemented */
1446 { (BL_MASK_BUSY
| BL_MASK_PARALYZ
| BL_MASK_SLEEPING
| BL_MASK_UNCONSC
),
1447 { { BL_MASK_BUSY
, F_BUSY
}, /* can't move but none of the below... */
1448 { BL_MASK_PARALYZ
, F_PARALYZED
}
1449 { BL_MASK_SLEEPING
, F_SLEEPING
}
1450 { BL_MASK_UNCONSC
, F_UNCONSCIOUS
} },
1455 static const struct f_overload
*
1456 ff_ovld_from_mask(unsigned long mask
)
1458 const struct f_overload
*fo
;
1460 for (fo
= cond_ovl
; fo
< cond_ovl
+ SIZE(cond_ovl
); ++fo
) {
1461 if ((fo
->all_mask
& mask
) != 0L)
1464 return (struct f_overload
*) 0;
1467 static const struct f_overload
*
1468 ff_ovld_from_indx(int indx
) /* F_foo number, index into shown_stats[] */
1470 const struct f_overload
*fo
;
1473 if (indx
> 0) { /* skip 0 (F_DUMMY) */
1474 for (fo
= cond_ovl
; fo
< cond_ovl
+ SIZE(cond_ovl
); ++fo
) {
1475 for (i
= 0; i
< NUM_OVLD
&& (ff
= fo
->conds
[i
].ff
) > 0; ++i
)
1480 return (struct f_overload
*) 0;
1484 * Set all widget values to a null string. This is used after all spacings
1485 * have been calculated so that when the window is popped up we don't get all
1486 * kinds of funny values being displayed.
1489 null_out_status(void)
1492 struct X_status_value
*sv
;
1495 for (i
= 0, sv
= shown_stats
; i
< NUM_STATS
; i
++, sv
++) {
1498 set_value(sv
->w
, "");
1503 XtSetArg(args
[0], XtNlabel
, "");
1504 XtSetValues(sv
->w
, args
, ONE
);
1508 impossible("null_out_status: unknown type %d\n", sv
->type
);
1514 /* this is almost an exact duplicate of hilight_value() */
1516 hilight_label(Widget w
) /* label widget */
1519 * This predates STATUS_HILITES.
1520 * It is used to show any changed item in inverse and gets
1521 * reset on the next turn.
1526 DISABLE_WARNING_FORMAT_NONLITERAL
1529 update_val(struct X_status_value
*attr_rec
, long new_value
)
1531 static boolean Exp_shown
= TRUE
, time_shown
= TRUE
, score_shown
= TRUE
,
1536 if (attr_rec
->type
== SV_LABEL
) {
1537 if (attr_rec
== &shown_stats
[F_NAME
]) {
1538 Strcpy(buf
, svp
.plname
);
1539 buf
[0] = highc(buf
[0]);
1540 Strcat(buf
, " the ");
1545 Strcpy(mnam
, pmname(&mons
[u
.umonnum
], Ugender
));
1546 for (k
= 0; mnam
[k
] != '\0'; k
++) {
1547 if (k
== 0 || mnam
[k
- 1] == ' ')
1548 mnam
[k
] = highc(mnam
[k
]);
1553 rank_of(u
.ulevel
, svp
.pl_character
[0], flags
.female
));
1556 } else if (attr_rec
== &shown_stats
[F_DLEVEL
]) {
1557 if (!describe_level(buf
, 0)) {
1558 Strcpy(buf
, svd
.dungeons
[u
.uz
.dnum
].dname
);
1559 Sprintf(eos(buf
), ", level %d", depth(&u
.uz
));
1561 } else if (attr_rec
== &shown_stats
[F_VERS
]) {
1563 (void) status_version(buf
, sizeof buf
, FALSE
);
1567 impossible("update_val: unknown label type \"%s\"",
1572 if (!strcmp(buf
, attr_rec
->name
))
1575 /* Set the label. 'name' field is const for most entries;
1576 we need to cast away that const for this assignment */
1577 Strcpy((char *) attr_rec
->name
, buf
);
1578 XtSetArg(args
[0], XtNlabel
, buf
);
1579 XtSetValues(attr_rec
->w
, args
, ONE
);
1581 } else if (attr_rec
->type
== SV_NAME
) {
1582 if (attr_rec
->last_value
== new_value
)
1583 return; /* no change */
1585 attr_rec
->last_value
= new_value
;
1587 /* special cases: hunger and encumbrance */
1588 if (attr_rec
== &shown_stats
[F_HUNGER
]) {
1589 Strcpy(buf
, hu_stat
[new_value
]);
1590 (void) mungspaces(buf
);
1591 } else if (attr_rec
== &shown_stats
[F_ENCUMBER
]) {
1592 Strcpy(buf
, enc_stat
[new_value
]);
1593 } else if (new_value
) {
1594 Strcpy(buf
, attr_rec
->name
); /* condition name On */
1596 *buf
= '\0'; /* condition name Off */
1599 XtSetArg(args
[0], XtNlabel
, buf
);
1600 XtSetValues(attr_rec
->w
, args
, ONE
);
1602 } else { /* a value pair */
1603 boolean force_update
= FALSE
;
1605 /* special case: time can be enabled & disabled */
1606 if (attr_rec
== &shown_stats
[F_TIME
]) {
1607 if (flags
.time
&& !time_shown
) {
1608 set_name(attr_rec
->w
, shown_stats
[F_TIME
].name
);
1609 force_update
= TRUE
;
1611 } else if (!flags
.time
&& time_shown
) {
1612 set_name(attr_rec
->w
, "");
1613 set_value(attr_rec
->w
, "");
1619 /* special case: experience points can be enabled & disabled */
1620 } else if (attr_rec
== &shown_stats
[F_EXP_PTS
]) {
1621 boolean showexp
= flags
.showexp
&& !Upolyd
;
1623 if (showexp
&& !Exp_shown
) {
1624 set_name(attr_rec
->w
, shown_stats
[F_EXP_PTS
].name
);
1625 force_update
= TRUE
;
1627 } else if (!showexp
&& Exp_shown
) {
1628 set_name(attr_rec
->w
, "");
1629 set_value(attr_rec
->w
, "");
1635 /* special case: when available, score can be enabled & disabled */
1636 } else if (attr_rec
== &shown_stats
[F_SCORE
]) {
1637 #ifdef SCORE_ON_BOTL
1638 if (flags
.showscore
&& !score_shown
) {
1639 set_name(attr_rec
->w
, shown_stats
[F_SCORE
].name
);
1640 force_update
= TRUE
;
1644 if (!flags
.showscore
&& score_shown
) {
1645 set_name(attr_rec
->w
, "");
1646 set_value(attr_rec
->w
, "");
1647 score_shown
= FALSE
;
1652 /* special case: when polymorphed, show "Hit Dice" and disable Exp */
1653 } else if (attr_rec
== &shown_stats
[F_XP_LEVL
]) {
1654 if (Upolyd
&& !Xp_was_HD
) {
1655 force_update
= TRUE
;
1656 set_name(attr_rec
->w
, "Hit Dice");
1658 } else if (!Upolyd
&& Xp_was_HD
) {
1659 force_update
= TRUE
;
1660 set_name(attr_rec
->w
, shown_stats
[F_XP_LEVL
].name
);
1663 /* core won't call status_update() for Exp when it hasn't changed
1664 so do so ourselves (to get Exp_shown flag to match display) */
1666 update_fancy_status_field(F_EXP_PTS
, NO_COLOR
, HL_UNDEF
);
1669 if (attr_rec
->last_value
== new_value
&& !force_update
) /* same */
1672 attr_rec
->last_value
= new_value
;
1674 /* Special cases: strength and other characteristics, alignment
1676 if (attr_rec
>= &shown_stats
[F_STR
]
1677 && attr_rec
<= &shown_stats
[F_CHA
]) {
1678 static const char fmt1
[] = "%ld%s", fmt2
[] = "%2ld%s";
1680 const char *fmt
= fmt1
, *padding
= "";
1682 /* for full-fledged fancy status, force two digits for all
1683 six characteristics, followed by three spaces of padding
1684 to match "/xx" exceptional strength */
1685 wp
= (WIN_STATUS
!= WIN_ERR
) ? &window_list
[WIN_STATUS
] : 0;
1686 if (wp
&& !wp
->status_information
)
1687 fmt
= fmt2
, padding
= " ";
1689 if (new_value
> 18L && attr_rec
== &shown_stats
[F_STR
]) {
1690 if (new_value
> 118L) /* 19..25 encoded as 119..125 */
1691 Sprintf(buf
, fmt
, new_value
- 100L, padding
);
1692 else if (new_value
< 118L) /* 18/01..18/99 as 19..117*/
1693 Sprintf(buf
, "18/%02ld", new_value
- 18L);
1695 Strcpy(buf
, "18/**"); /* 18/100 encoded as 118 */
1696 } else { /* non-strength or less than 18/01 strength (3..18) */
1697 Sprintf(buf
, fmt
, new_value
, padding
); /* 3..25 */
1699 } else if (attr_rec
== &shown_stats
[F_ALIGN
]) {
1700 Strcpy(buf
, (new_value
== A_CHAOTIC
) ? "Chaotic"
1701 : (new_value
== A_NEUTRAL
) ? "Neutral" : "Lawful");
1703 Sprintf(buf
, "%ld", new_value
);
1705 set_value(attr_rec
->w
, buf
);
1709 * Now highlight the changed information. Don't highlight Time because
1710 * it's continually changing. Don't highlight version because once set
1711 * it only changes if player modifies 'versinfo' option. For others,
1712 * don't highlight if this is the first update.
1713 * If already highlighted, don't change it unless
1714 * it's being set to blank (where that item should be reset now instead
1715 * of showing highlighted blank until the next expiration check).
1717 * 3.7: highlight non-labelled 'name' items (conditions plus hunger
1718 * and encumbrance) when they come On. For all conditions going Off,
1719 * or changing to not-hungry or not-encumbered, there's nothing to
1720 * highlight because the field becomes blank.
1722 if (attr_rec
->after_init
) {
1723 /* toggle if not highlighted and being set to nonblank or if
1724 already highlighted and being set to blank */
1725 if (attr_rec
!= &shown_stats
[F_TIME
]
1726 && attr_rec
!= &shown_stats
[F_VERS
]
1727 && !attr_rec
->set
^ !*buf
) {
1728 /* But don't hilite if inverted from status_hilite since
1729 it will already be hilited by apply_hilite_attributes(). */
1730 if (!attr_rec
->inverted_hilite
) {
1731 if (attr_rec
->type
== SV_VALUE
)
1732 hilight_value(attr_rec
->w
);
1734 hilight_label(attr_rec
->w
);
1736 attr_rec
->set
= !attr_rec
->set
;
1738 attr_rec
->turn_count
= 0;
1740 XtSetArg(args
[0], XtNforeground
, &attr_rec
->default_fg
);
1741 XtGetValues(attr_rec
->w
, args
, ONE
);
1742 attr_rec
->after_init
= TRUE
;
1746 RESTORE_WARNING_FORMAT_NONLITERAL
1748 /* overloaded condition is being cleared without going through update_val()
1749 so that an alternate can be shown; put this one back to default settings */
1751 skip_cond_val(struct X_status_value
*sv
)
1753 sv
->last_value
= 0L; /* Off */
1755 /* if condition was highlighted and the alternate value has
1756 also requested to be highlighted, it used its own copy of
1757 'set' but the same widget so the highlighting got toggled
1758 off; this will turn in back on in that exceptional case */
1759 hilight_label(sv
->w
);
1765 update_color(struct X_status_value
*sv
, int color
)
1771 Widget w
= (sv
->type
== SV_LABEL
|| sv
->type
== SV_NAME
) ? sv
->w
1772 : get_value_widget(sv
->w
);
1774 if (color
== NO_COLOR
) {
1776 pixel
= sv
->default_fg
;
1777 sv
->colr
= NO_COLOR
;
1779 source
.addr
= (XPointer
) fancy_status_hilite_colors
[color
];
1780 source
.size
= (unsigned int) strlen((const char *) source
.addr
) + 1;
1781 dest
.size
= (unsigned int) sizeof (Pixel
);
1782 dest
.addr
= (XPointer
) &pixel
;
1783 if (XtConvertAndStore(w
, XtRString
, &source
, XtRPixel
, &dest
))
1787 char *arg_name
= (sv
->set
|| sv
->inverted_hilite
) ? XtNbackground
1790 XtSetArg(args
[0], arg_name
, pixel
);
1791 XtSetValues(w
, args
, ONE
);
1796 name_widget_has_label(struct X_status_value
*sv
)
1801 XtSetArg(args
[0], XtNlabel
, &label
);
1802 XtGetValues(sv
->w
, args
, ONE
);
1803 return (*label
!= '\0');
1807 apply_hilite_attributes(struct X_status_value
*sv
, int attributes
)
1809 boolean attr_inversion
= ((HL_INVERSE
& attributes
)
1810 && (sv
->type
!= SV_NAME
1811 || name_widget_has_label(sv
)));
1813 if (sv
->inverted_hilite
!= attr_inversion
) {
1814 sv
->inverted_hilite
= attr_inversion
;
1816 if (sv
->type
== SV_VALUE
)
1817 hilight_value(sv
->w
);
1819 hilight_label(sv
->w
);
1822 sv
->attr
= attributes
;
1823 /* Could possibly add more attributes here: HL_ATTCLR_DIM,
1824 HL_ATTCLR_BLINK, HL_ATTCLR_ULINE, and HL_ATTCLR_BOLD. If so,
1825 extract the above into its own function apply_hilite_inverse()
1826 and each other attribute into its own to keep the code clean. */
1830 * Update the displayed status. The current code in botl.c updates
1831 * two lines of information. Both lines are always updated one after
1832 * the other. So only do our update when we update the second line.
1834 * Information on the first line:
1835 * name, characteristics, alignment, score
1837 * Information on the second line:
1838 * dlvl, gold, hp, power, ac, {level & exp or HD **}, time,
1839 * status * (stone, slime, strngl, foodpois, termill,
1840 * hunger, encumbrance, lev, fly, ride,
1841 * blind, deaf, stun, conf, hallu, version ***)
1843 * [*] order of status fields is different on tty.
1844 * [**] HD is shown instead of level and exp if Upolyd.
1845 * [***] version is optional, right-justified after conditions
1848 update_fancy_status_field(int i
, int color
, int attributes
)
1850 struct X_status_value
*sv
= &shown_stats
[i
];
1851 unsigned long condmask
= 0L;
1859 val
= (long) ACURR(A_STR
);
1862 val
= (long) ACURR(A_DEX
);
1865 val
= (long) ACURR(A_CON
);
1868 val
= (long) ACURR(A_INT
);
1871 val
= (long) ACURR(A_WIS
);
1874 val
= (long) ACURR(A_CHA
);
1877 * Label stats. With the exceptions of hunger and encumbrance
1878 * these are either on or off. Please leave the ternary operators
1879 * the way they are. I want to specify 0 or 1, not a boolean.
1885 val
= (long) near_capacity();
1888 case F_TRAPPED
: /* belongs with non-fatal but fits with 'other' */
1889 condmask
= BL_MASK_TRAPPED
;
1891 case F_TETHERED
: /* overloaded with 'trapped' */
1892 condmask
= BL_MASK_TETHERED
;
1894 /* 'other' status conditions */
1896 condmask
= BL_MASK_LEV
;
1899 condmask
= BL_MASK_FLY
;
1902 condmask
= BL_MASK_RIDE
;
1904 /* fatal status conditions */
1906 condmask
= BL_MASK_GRAB
;
1909 condmask
= BL_MASK_STONE
;
1912 condmask
= BL_MASK_SLIME
;
1915 condmask
= BL_MASK_STRNGL
;
1918 condmask
= BL_MASK_FOODPOIS
;
1921 condmask
= BL_MASK_TERMILL
;
1923 case F_IN_LAVA
: /* could overload with 'trapped' but is more severe */
1924 condmask
= BL_MASK_INLAVA
;
1926 /* non-fatal status conditions */
1928 condmask
= BL_MASK_HELD
;
1930 case F_HOLDING
: /* belongs with 'other' but overloads 'held' */
1931 condmask
= BL_MASK_HOLDING
;
1934 condmask
= BL_MASK_BLIND
;
1937 condmask
= BL_MASK_DEAF
;
1940 condmask
= BL_MASK_STUN
;
1943 condmask
= BL_MASK_CONF
;
1946 condmask
= BL_MASK_HALLU
;
1949 /* pseudo-condition */
1951 val
= (long) flags
.versinfo
; /* 1..7 */
1957 break; /* special */
1960 val
= money_cnt(gi
.invent
);
1962 val
= 0L; /* ought to issue impossible() and discard gold */
1965 val
= (long) (Upolyd
? (u
.mh
> 0 ? u
.mh
: 0)
1966 : (u
.uhp
> 0 ? u
.uhp
: 0));
1969 val
= (long) (Upolyd
? u
.mhmax
: u
.uhpmax
);
1975 val
= (long) u
.uenmax
;
1981 val
= (long) (Upolyd
? mons
[u
.umonnum
].mlevel
: u
.ulevel
);
1984 val
= flags
.showexp
? u
.uexp
: 0L;
1987 val
= (long) u
.ualign
.type
;
1990 val
= flags
.time
? (long) svm
.moves
: 0L;
1993 #ifdef SCORE_ON_BOTL
1994 val
= flags
.showscore
? botl_score() : 0L;
2001 * There is a possible infinite loop that occurs with:
2003 * impossible->pline->flush_screen->bot->bot{1,2}->
2004 * putstr->adjust_status->update_other->impossible
2006 * Break out with this.
2008 static boolean isactive
= FALSE
;
2012 impossible("update_other: unknown shown value");
2021 const struct f_overload
*fo
= ff_ovld_from_mask(condmask
);
2023 val
= ((X11_condition_bits
& condmask
) != 0L);
2024 /* if we're turning an overloaded field Off, don't do it if any
2025 of the other alternatives are being set On because we would
2026 clobber that if the other one happens to be drawn first */
2027 if (!val
&& fo
&& (X11_condition_bits
& fo
->all_mask
) != 0L) {
2032 update_val(sv
, val
);
2033 if (color
!= sv
->colr
)
2034 update_color(sv
, color
);
2035 if (attributes
!= sv
->attr
)
2036 apply_hilite_attributes(sv
, attributes
);
2039 /* fully update status after bl_flush or window resize */
2041 update_fancy_status(boolean force_update
)
2043 static boolean old_showtime
, old_showexp
, old_showscore
, old_showvers
;
2044 static int old_upolyd
= -1; /* -1: force first time update */
2048 || Upolyd
!= old_upolyd
/* Xp vs HD */
2049 || flags
.time
!= old_showtime
2050 || flags
.showexp
!= old_showexp
2051 || flags
.showscore
!= old_showscore
2052 || flags
.showvers
!= old_showvers
) {
2053 /* update everything; usually only need this on the very first
2054 time, then later if the window gets resized or if poly/unpoly
2055 triggers Xp <-> HD switch or if an optional field gets
2056 toggled off since there won't be a status_update() call for
2057 the no longer displayed field; we're a bit more conservative
2058 than that and do this when toggling on as well as off */
2059 for (i
= 0; i
< NUM_STATS
; i
++)
2060 update_fancy_status_field(i
, NO_COLOR
, HL_UNDEF
);
2061 old_condition_bits
= X11_condition_bits
;
2063 old_upolyd
= Upolyd
;
2064 old_showtime
= flags
.time
;
2065 old_showexp
= flags
.showexp
;
2066 old_showscore
= flags
.showscore
;
2067 old_showvers
= flags
.showvers
;
2072 * Turn off hilighted status values after a certain amount of turns.
2075 check_turn_events(void)
2078 struct X_status_value
*sv
;
2079 int hilight_time
= 1;
2081 #ifdef STATUS_HILITES
2082 if (iflags
.hilite_delta
)
2083 hilight_time
= (int) iflags
.hilite_delta
;
2085 for (sv
= shown_stats
, i
= 0; i
< NUM_STATS
; i
++, sv
++) {
2089 if (sv
->turn_count
++ >= hilight_time
) {
2090 /* unhighlights by toggling a highlighted item back off again,
2091 unless forced inverted by a status_hilite rule */
2092 if (!sv
->inverted_hilite
) {
2093 if (sv
->type
== SV_VALUE
)
2094 hilight_value(sv
->w
);
2096 hilight_label(sv
->w
);
2103 /* Initialize alternate status ============================================ */
2105 /* Return a string for the initial width, so use longest possible value. */
2107 width_string(int sv_index
)
2120 return "088"; /* all but str never get bigger */
2125 return "Overloaded";
2146 return shown_stats
[sv_index
].name
;
2150 return ""; /* longest possible value not needed for these */
2163 /* strongest hero can pick up roughly 30% of this much */
2164 return "999999"; /* same limit as tty */
2168 return "123456789"; /* a tenth digit will still fit legibly */
2172 impossible("width_string: unknown index %d\n", sv_index
);
2177 create_widget(Widget parent
, struct X_status_value
*sv
, int sv_index
)
2184 sv
->w
= create_value(parent
, sv
->name
);
2185 set_value(sv
->w
, width_string(sv_index
));
2188 /* Labels get their own buffer. */
2189 sv
->name
= (char *) alloc(BUFSZ
);
2190 /* we need to cast away 'const' when assigning a value */
2191 *(char *) (sv
->name
) = '\0';
2194 XtSetArg(args
[num_args
], XtNborderWidth
, 0); num_args
++;
2195 XtSetArg(args
[num_args
], XtNinternalHeight
, 0); num_args
++;
2196 sv
->w
= XtCreateManagedWidget((sv_index
== F_NAME
)
2199 labelWidgetClass
, parent
,
2205 const struct f_overload
*fo
= ff_ovld_from_indx(sv_index
);
2206 int baseindx
= fo
? fo
->conds
[0].ff
: sv_index
;
2208 if (sv_index
!= baseindx
) {
2209 /* this code isn't actually executed; only the base condition
2210 is in one of the fancy status columns and only the fields
2211 in those columns are passed to this routine; the real
2212 initialization--this same assignment--for overloaded
2213 conditions takes place at the end of create_fancy_status() */
2214 sv
->w
= shown_stats
[baseindx
].w
;
2217 txt
= width_string(sv_index
); /* for conditions, it's just sv->name */
2219 int i
, ff
, altln
, ln
= (int) strlen(txt
);
2221 /* make the initial value have the width of the longest of
2222 these overloaded conditions; used for widget sizing, not for
2223 display, and ultimately only matters if one of the overloads
2224 happens to be the longest string in its whole column */
2225 for (i
= 1; i
< NUM_OVLD
&& (ff
= fo
->conds
[i
].ff
) > 0; ++i
)
2226 if ((altln
= (int) strlen(width_string(ff
))) > ln
)
2228 Sprintf(buf
, "%*s", ln
, txt
);
2232 XtSetArg(args
[0], XtNlabel
, txt
); num_args
++;
2233 XtSetArg(args
[num_args
], XtNborderWidth
, 0); num_args
++;
2234 XtSetArg(args
[num_args
], XtNinternalHeight
, 0); num_args
++;
2235 sv
->w
= XtCreateManagedWidget(sv
->name
, labelWidgetClass
, parent
,
2240 panic("create_widget: unknown type %d", sv
->type
);
2245 * Get current width of value. width2p is only valid for SV_VALUE types.
2248 get_widths(struct X_status_value
*sv
, int *width1p
, int *width2p
)
2255 *width1p
= get_name_width(sv
->w
);
2256 *width2p
= get_value_width(sv
->w
);
2260 XtSetArg(args
[0], XtNwidth
, &width
);
2261 XtGetValues(sv
->w
, args
, ONE
);
2266 panic("get_widths: unknown type %d", sv
->type
);
2271 set_widths(struct X_status_value
*sv
, int width1
, int width2
)
2277 set_name_width(sv
->w
, width1
);
2278 set_value_width(sv
->w
, width2
);
2282 XtSetArg(args
[0], XtNwidth
, (width1
+ width2
));
2283 XtSetValues(sv
->w
, args
, ONE
);
2286 panic("set_widths: unknown type %d", sv
->type
);
2293 Widget parent
, Widget top
, Widget left
,
2294 int *col_indices
, int xtrawidth
)
2299 int max_width1
, width1
, max_width2
, width2
;
2301 struct X_status_value
*sv
;
2304 if (top
!= (Widget
) 0) {
2305 XtSetArg(args
[num_args
], nhStr(XtNfromVert
), top
); num_args
++;
2307 if (left
!= (Widget
) 0) {
2308 XtSetArg(args
[num_args
], nhStr(XtNfromHoriz
), left
); num_args
++;
2310 /* this was 0 but that resulted in the text being crammed together */
2311 XtSetArg(args
[num_args
], nhStr(XtNdefaultDistance
), 2); num_args
++;
2312 form
= XtCreateManagedWidget(name
, formWidgetClass
, parent
,
2315 max_width1
= max_width2
= 0;
2316 for (ip
= col_indices
; *ip
>= 0; ip
++) {
2317 sv
= &shown_stats
[*ip
];
2318 create_widget(form
, sv
, *ip
); /* will set init width */
2319 if (ip
!= col_indices
) { /* not first */
2321 XtSetArg(args
[num_args
], nhStr(XtNfromVert
),
2322 shown_stats
[*(ip
- 1)].w
); num_args
++;
2323 XtSetValues(sv
->w
, args
, num_args
);
2325 get_widths(sv
, &width1
, &width2
);
2326 if (width1
> max_width1
)
2327 max_width1
= width1
;
2328 if (width2
> max_width2
)
2329 max_width2
= width2
;
2332 /* insert some extra spacing between columns */
2333 max_width1
+= xtrawidth
;
2335 for (ip
= col_indices
; *ip
>= 0; ip
++) {
2336 set_widths(&shown_stats
[*ip
], max_width1
, max_width2
);
2339 /* There is room behind the end marker for the two widths. */
2347 * These are the orders of the displayed columns. Change to suit. The -1
2348 * indicates the end of the column. The two numbers after that are used
2349 * to store widths that are calculated at run-time.
2351 * 3.7: changed so that all 6 columns have 8 rows, but a few entries
2352 * are left blank <>. Exp-points, Score, and Time are optional depending
2353 * on run-time settings; Xp-level is replaced by Hit-Dice (and Exp-points
2354 * suppressed) when the hero is polymorphed. Title and Dungeon-Level span
2355 * two columns and might expand to more if 'hitpointbar' is implemented.
2356 * Version is optional, right justified, and much wider than the others.
2358 Title ("Plname the Rank") <> <> <> <>
2359 Dungeon-Branch-and-Level <> Hunger Grabbed Held
2360 Hit-points Max-HP Strength Encumbrance Petrifying Blind
2361 Power-points Max-Power Dexterity Trapped Slimed Deaf
2362 Armor-class Alignment Constitution Levitation Strangled Stunned
2363 Xp-level [Exp-points] Intelligence Flying Food-Pois Confused
2364 Gold [Score] Wisdom Riding Term-Ill Hallucinat
2365 <> [Time] Charisma <> Sinking Version
2367 * A seventh column is going to be needed to fit in more conditions.
2370 /* including F_DUMMY makes the three status condition columns evenly
2371 spaced with regard to the adjacent characteristics (Str,Dex,&c) column;
2372 we lose track of the Widget pointer for F_DUMMY, each use clobbering the
2373 one before, leaving the one from leftover_indices[]; since they're never
2374 updated, that shouldn't matter */
2375 static int status_indices
[3][11] = {
2376 { F_DUMMY
, F_HUNGER
, F_ENCUMBER
, F_TRAPPED
,
2377 F_LEV
, F_FLY
, F_RIDE
, F_DUMMY
, -1, 0, 0 },
2378 { F_DUMMY
, F_GRABBED
, F_STONE
, F_SLIME
, F_STRNGL
,
2379 F_FOODPOIS
, F_TERMILL
, F_IN_LAVA
, -1, 0, 0 },
2380 { F_DUMMY
, F_HELD
, F_BLIND
, F_DEAF
, F_STUN
,
2381 F_CONF
, F_HALLU
, F_VERS
, -1, 0, 0 },
2383 /* used to fill up the empty space to right of 3rd status condition column */
2384 static int leftover_indices
[] = { F_DUMMY
, -1, 0, 0 };
2385 /* -2: top two rows of these columns are reserved for title and location */
2386 static int col1_indices
[11 - 2] = {
2387 F_HP
, F_POWER
, F_AC
, F_XP_LEVL
, F_GOLD
, F_DUMMY
, -1, 0, 0
2389 static int col2_indices
[11 - 2] = {
2390 F_MAXHP
, F_MAXPOWER
, F_ALIGN
, F_EXP_PTS
, F_SCORE
, F_TIME
, -1, 0, 0
2392 static int characteristics_indices
[11 - 2] = {
2393 F_STR
, F_DEX
, F_CON
, F_INT
, F_WIS
, F_CHA
, -1, 0, 0
2397 * Produce a form that looks like the following:
2401 * col1_indices[0] col2_indices[0] col3_indices[0]
2402 * col1_indices[1] col2_indices[1] col3_indices[1]
2404 * col1_indices[5] col2_indices[5] col3_indices[5]
2406 * The status conditions are managed separately and appear to the right
2409 * TODO: widen title field and implement hitpoint bar on it.
2412 init_info_form(Widget parent
, Widget top
, Widget left
)
2414 Widget form
, col1
, col2
;
2415 struct X_status_value
*sv_name
, *sv_dlevel
;
2418 int total_width
, *ip
;
2421 if (top
!= (Widget
) 0) {
2422 XtSetArg(args
[num_args
], nhStr(XtNfromVert
), top
); num_args
++;
2424 if (left
!= (Widget
) 0) {
2425 XtSetArg(args
[num_args
], nhStr(XtNfromHoriz
), left
); num_args
++;
2427 XtSetArg(args
[num_args
], nhStr(XtNdefaultDistance
), 2); num_args
++;
2428 form
= XtCreateManagedWidget("status_info", formWidgetClass
, parent
,
2431 /* top line/row of form */
2432 sv_name
= &shown_stats
[F_NAME
]; /* title */
2433 create_widget(form
, sv_name
, F_NAME
);
2435 /* second line/row */
2436 sv_dlevel
= &shown_stats
[F_DLEVEL
]; /* location */
2437 create_widget(form
, sv_dlevel
, F_DLEVEL
);
2440 XtSetArg(args
[num_args
], nhStr(XtNfromVert
), sv_name
->w
); num_args
++;
2441 XtSetValues(sv_dlevel
->w
, args
, num_args
);
2443 /* there are 3 columns beneath but top 2 rows are centered over first 2 */
2444 col1
= init_column("name_col1", form
, sv_dlevel
->w
, (Widget
) 0,
2446 col2
= init_column("name_col2", form
, sv_dlevel
->w
, col1
,
2448 (void) init_column("status_characteristics", form
, sv_dlevel
->w
, col2
,
2449 characteristics_indices
, 15);
2451 /* Add calculated widths. */
2452 for (ip
= col1_indices
; *ip
>= 0; ip
++)
2454 total_width
= *++ip
;
2455 total_width
+= *++ip
;
2456 for (ip
= col2_indices
; *ip
>= 0; ip
++)
2458 total_width
+= *++ip
;
2459 total_width
+= *++ip
;
2461 XtSetArg(args
[0], XtNwidth
, total_width
);
2462 XtSetValues(sv_name
->w
, args
, ONE
);
2463 XtSetArg(args
[0], XtNwidth
, total_width
);
2464 XtSetValues(sv_dlevel
->w
, args
, ONE
);
2469 /* give the three status condition columns the same width */
2471 fixup_cond_widths(void)
2473 int pass
, i
, *ip
, w1
, w2
;
2476 for (pass
= 1; pass
<= 2; ++pass
) { /* two passes... */
2477 for (i
= 0; i
< 3; i
++) { /* three columns */
2478 for (ip
= status_indices
[i
]; *ip
!= -1; ++ip
) { /* X fields */
2479 /* pass 1: find -1; pass 2: update field widths, find -1 */
2481 set_widths(&shown_stats
[*ip
], w1
, w2
);
2483 /* found -1; the two slots beyond it contain column widths */
2484 if (pass
== 1) { /* pass 1: collect maxima */
2489 } else { /* pass 2: update column widths with maxima */
2494 /* ascetics: expand the maximum width to make cond columns wider */
2504 Dimension vers_width
= 0;
2505 struct X_status_value
*sv
= &shown_stats
[F_VERS
];
2508 XtSetArg(args
[0], XtNwidth
, &vers_width
);
2509 XtGetValues(sv
->w
, args
, ONE
);
2512 XtSetArg(args
[0], XtNwidth
, vers_width
);
2513 XtSetArg(args
[1], nhStr(XtNjustify
), XtJustifyRight
);
2514 XtSetValues(sv
->w
, args
, TWO
);
2521 * Create the layout for the fancy status. Return a form widget that
2522 * contains everything.
2525 create_fancy_status(Widget parent
, Widget top
)
2527 Widget form
; /* The form that surrounds everything. */
2532 const struct f_overload
*fo
;
2536 if (top
!= (Widget
) 0) {
2537 XtSetArg(args
[num_args
], nhStr(XtNfromVert
), top
); num_args
++;
2539 XtSetArg(args
[num_args
], nhStr(XtNdefaultDistance
), 2); num_args
++;
2540 XtSetArg(args
[num_args
], XtNborderWidth
, 0); num_args
++;
2541 XtSetArg(args
[num_args
], XtNorientation
, XtorientHorizontal
); num_args
++;
2542 form
= XtCreateManagedWidget("fancy_status", panedWidgetClass
, parent
,
2545 w
= init_info_form(form
, (Widget
) 0, (Widget
) 0);
2546 #if 0 /* moved to init_info_form() */
2547 w
= init_column("status_characteristics", form
, (Widget
) 0, w
,
2548 characteristics_indices
, 15);
2550 for (i
= 0; i
< 3; i
++) {
2551 Sprintf(buf
, "status_condition%d", i
+ 1);
2552 w
= init_column(buf
, form
, (Widget
) 0, w
, status_indices
[i
], 0);
2554 fixup_cond_widths(); /* make all 3 status_conditionN columns same width
2555 * (actually, the slot for F_VERS is much wider) */
2557 * Calculate and set the width of the F_VERS widjet to be from the
2558 * start of the third condition column through the right edge and
2559 * get rid of the dummy column.
2562 /* extra dummy 'column' to allocate any remaining space below the map */
2563 (void) init_column("status_leftover", form
, (Widget
) 0, w
,
2564 leftover_indices
, 0);
2566 /* handle overloading; extra conditions don't start out in any column
2567 so need to be initialized separately; the only initialization they
2568 need is to share the widget of the base condition which is present
2569 in one of the columns [could be deferred until first use] */
2570 for (fo
= cond_ovl
; fo
< cond_ovl
+ SIZE(cond_ovl
); ++fo
)
2571 for (i
= 1; i
< NUM_OVLD
&& (ff
= fo
->conds
[i
].ff
) > 0; ++i
)
2572 if (!shown_stats
[ff
].w
)
2573 shown_stats
[ff
].w
= shown_stats
[fo
->conds
[0].ff
].w
;
2579 destroy_fancy_status(struct xwindow
*wp
)
2582 struct X_status_value
*sv
;
2584 if (!wp
->keep_window
)
2585 XtDestroyWidget(wp
->w
), wp
->w
= (Widget
) 0;
2587 for (i
= 0, sv
= shown_stats
; i
< NUM_STATS
; i
++, sv
++)
2588 if (sv
->type
== SV_LABEL
) {
2589 free((genericptr_t
) sv
->name
);