I started out adding a few new status conditions to X11's "fancy status"
(the default) to gauge how difficult it was going to be. In the process
I found several latent bugs. After fixing those, I decided that the same
status conditions should be added to the alternate "tty-style status".
Lots more latent bugs, some of the same nature, others different. Things
spiraled until the code change is very substantial.
Code for the old two-line status is still present but I don't know how
to activate it. Unlike tty-style status, it composes and displays two
lines of text and isn't capable of highlighting portions of that text,
so it would be considered deprecated anyway.
All testing was done with the default NetHack.ad (except when turning
'fancy_status' off) so I don't know whether the new code might override
previously customizable status settings. I'm not sure whether this list
covers all the fixes....
both tty-style and fancy
add new status conditions 'grabbed' (by eel), 'held', 'trapped', and
'sinking-into-lava' (others will eventually follow); grab and lava
are on by default, the others have to be enabled via options
both tty-style (not handled) and fancy (faulty boolean logic)
polymorphing didn't change Xp to HD (silver lining: rehumanizing
didn't need to reverse it)
tty-style only; fancy was ok
force white text (on black background) instead of settling for gray
turning on optional showexp, showscore, and/or time worked but turning
them back off again didn't remove the relevant fields
polymorphing when showexp was on didn't suppress Exp-points
tty-style only; fancy uses different layout
condense conditions into simple left-to-right space separated list
instead of giving them specific locations and having gaps of blank
space for conditions that aren't in effect
tty-style only; not applicable for fancy (status_hilites not implemented)
all highlights stuck if 'statushilites' was reset to 0 to disable them
displaying anything with bold attribute stuck; it wouldn't revert to
normal text if a different highlight rule without bold was used for
subsequent updates
avoid inverting leading space that separates from preceding field when
highlighting with inverse video attribute
add support for 'dim' attribute using gray foreground (only viable
after the fix for white foreground)
fancy only
reorganize the field layout so that things line up nicely instead of
having columns with six, seven, or eight lines be spread over same
amount of vertical space
line up the values of the six characteristics, similar to how vertical
status works in curses: all two digits; when exceptional strength is
present, the '18' lines up and rest goes past implicit right margin
use status conditions as provided by core instead of duplicating them
(other fields still duplicate stuff done in botl.c); doing this
required forcing 'VIA_WINDOWPORT()' if built without STATUS_HILITES
2232 lines
73 KiB
C
2232 lines
73 KiB
C
/* NetHack 3.6 winstat.c $NHDT-Date: 1582833042 2020/02/27 19:50:42 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.24 $ */
|
|
/* Copyright (c) Dean Luick, 1992 */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/*
|
|
* Status window routines. This file supports both the "traditional"
|
|
* tty status display and a "fancy" status display. A tty status is
|
|
* made if a popup window is requested, otherewise a fancy status is
|
|
* made. This code assumes that only one fancy status will ever be made.
|
|
* Currently, only one status window (of any type) is _ever_ made.
|
|
*/
|
|
|
|
#ifndef SYSV
|
|
#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
|
|
#endif
|
|
|
|
#include <X11/Intrinsic.h>
|
|
#include <X11/IntrinsicP.h> /* for XtResizeWidget() and XtConfigureWidget() */
|
|
#include <X11/StringDefs.h>
|
|
#include <X11/Shell.h>
|
|
#include <X11/Xaw/AsciiText.h>
|
|
#include <X11/Xaw/Cardinals.h> /* just for ONE, TWO */
|
|
#include <X11/Xaw/Form.h>
|
|
#include <X11/Xaw/Paned.h>
|
|
#include <X11/Xaw/Label.h>
|
|
#include <X11/Xaw/Viewport.h>
|
|
/*#include <X11/Xatom.h>*/
|
|
|
|
#ifdef PRESERVE_NO_SYSV
|
|
#ifdef SYSV
|
|
#undef SYSV
|
|
#endif
|
|
#undef PRESERVE_NO_SYSV
|
|
#endif
|
|
|
|
#include "hack.h"
|
|
#include "winX.h"
|
|
#include "xwindow.h"
|
|
|
|
/*
|
|
* Fancy status form entry storage indices.
|
|
*/
|
|
#define F_DUMMY 0
|
|
#define F_STR 1
|
|
#define F_DEX 2
|
|
#define F_CON 3
|
|
#define F_INT 4
|
|
#define F_WIS 5
|
|
#define F_CHA 6
|
|
|
|
#define F_NAME 7 /* title: "Name the Rank" where rank is role-specific */
|
|
#define F_DLEVEL 8 /* location: dungeon branch and level */
|
|
#define F_GOLD 9
|
|
#define F_HP 10
|
|
#define F_MAXHP 11
|
|
#define F_POWER 12
|
|
#define F_MAXPOWER 13
|
|
#define F_AC 14
|
|
#define F_XP_LEVL 15
|
|
/*#define F_HD F_XP_LEVL*/
|
|
#define F_EXP_PTS 16
|
|
#define F_ALIGN 17
|
|
#define F_TIME 18
|
|
#define F_SCORE 19
|
|
|
|
/* status conditions grouped by columns; tty orders these differently;
|
|
hunger/encumbrance/movement used to be in the middle with fatal
|
|
conditions on the left but those columns have been swapped and
|
|
renumbered to match new order (forcing shown_stat[] to be reordered) */
|
|
#define F_HUNGER 20
|
|
#define F_ENCUMBER 21
|
|
#define F_TRAPPED 22
|
|
#define F_LEV 23
|
|
#define F_FLY 24
|
|
#define F_RIDE 25
|
|
|
|
#define F_GRABBED 26
|
|
#define F_STONE 27
|
|
#define F_SLIME 28
|
|
#define F_STRNGL 29
|
|
#define F_FOODPOIS 30
|
|
#define F_TERMILL 31
|
|
#define F_IN_LAVA 32
|
|
|
|
#define F_HELD 33
|
|
#define F_BLIND 34
|
|
#define F_DEAF 35
|
|
#define F_STUN 36
|
|
#define F_CONF 37
|
|
#define F_HALLU 38
|
|
|
|
#define NUM_STATS 39
|
|
|
|
static int FDECL(condcolor, (long, unsigned long *));
|
|
static int FDECL(condattr, (long, unsigned long *));
|
|
static void FDECL(HiliteField, (Widget, int, int, int, XFontStruct **));
|
|
static void FDECL(PrepStatusField, (int, Widget, const char *));
|
|
static void FDECL(DisplayCond, (int, unsigned long *));
|
|
static int FDECL(render_conditions, (int, int));
|
|
#ifdef STATUS_HILITES
|
|
static void FDECL(tt_reset_color, (int, int, unsigned long *));
|
|
#endif
|
|
static void NDECL(tt_status_fixup);
|
|
static Widget FDECL(create_tty_status_field, (int, int, Widget, Widget));
|
|
static Widget FDECL(create_tty_status, (Widget, Widget));
|
|
static void FDECL(update_fancy_status_field, (int));
|
|
static void FDECL(update_fancy_status, (BOOLEAN_P));
|
|
static Widget FDECL(create_fancy_status, (Widget, Widget));
|
|
static void FDECL(destroy_fancy_status, (struct xwindow *));
|
|
static void FDECL(create_status_window_fancy, (struct xwindow *,
|
|
BOOLEAN_P, Widget));
|
|
static void FDECL(create_status_window_tty, (struct xwindow *,
|
|
BOOLEAN_P, Widget));
|
|
static void FDECL(destroy_status_window_fancy, (struct xwindow *));
|
|
static void FDECL(destroy_status_window_tty, (struct xwindow *));
|
|
static void FDECL(adjust_status_fancy, (struct xwindow *, const char *));
|
|
static void FDECL(adjust_status_tty, (struct xwindow *, const char *));
|
|
|
|
extern const char *status_fieldfmt[MAXBLSTATS];
|
|
extern char *status_vals[MAXBLSTATS];
|
|
extern boolean status_activefields[MAXBLSTATS];
|
|
|
|
static unsigned long X11_condition_bits, old_condition_bits;
|
|
static int X11_status_colors[MAXBLSTATS],
|
|
old_field_colors[MAXBLSTATS],
|
|
old_cond_colors[32];
|
|
static int hpbar_percent, hpbar_color;
|
|
/* Number of conditions displayed during this update and last update.
|
|
When the last update had more, the excess need to be erased. */
|
|
static int next_cond_indx = 0, prev_cond_indx = 0;
|
|
|
|
/* TODO: support statuslines:3 in addition to 2 for the tty-style status */
|
|
#define X11_NUM_STATUS_LINES 2
|
|
#define X11_NUM_STATUS_FIELD 15
|
|
|
|
static enum statusfields X11_fieldorder[][X11_NUM_STATUS_FIELD] = {
|
|
{ BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN,
|
|
BL_SCORE, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH,
|
|
BL_FLUSH },
|
|
{ BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX,
|
|
BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER,
|
|
BL_CAP, BL_CONDITION, BL_FLUSH }
|
|
};
|
|
|
|
/* condition list for tty-style display, roughly in order of importance */
|
|
static struct tt_condinfo {
|
|
unsigned long mask;
|
|
const char *text;
|
|
} tt_condorder[] = {
|
|
{ BL_MASK_GRAB, "Grab!" },
|
|
{ BL_MASK_STONE, "Stone" },
|
|
{ BL_MASK_SLIME, "Slime" },
|
|
{ BL_MASK_STRNGL, "Strngl" },
|
|
{ BL_MASK_FOODPOIS, "FoodPois" },
|
|
{ BL_MASK_TERMILL, "TermIll" },
|
|
{ BL_MASK_INLAVA, "Lava" },
|
|
{ BL_MASK_HELD, "Held" },
|
|
{ BL_MASK_BLIND, "Blind" },
|
|
{ BL_MASK_DEAF, "Deaf" },
|
|
{ BL_MASK_STUN, "Stun" },
|
|
{ BL_MASK_CONF, "Conf" },
|
|
{ BL_MASK_HALLU, "Hallu" },
|
|
{ BL_MASK_TRAPPED, "Trap" },
|
|
{ BL_MASK_LEV, "Lev" },
|
|
{ BL_MASK_FLY, "Fly" },
|
|
{ BL_MASK_RIDE, "Ride" },
|
|
};
|
|
|
|
static Widget X11_status_widget;
|
|
static Widget X11_status_labels[MAXBLSTATS];
|
|
static Widget X11_cond_labels[32]; /* Ugh */
|
|
static XFontStruct *X11_status_font;
|
|
static Pixel X11_status_fg, X11_status_bg;
|
|
|
|
struct xwindow *xw_status_win;
|
|
|
|
static int
|
|
condcolor(bm, bmarray)
|
|
long bm;
|
|
unsigned long *bmarray;
|
|
{
|
|
int i;
|
|
|
|
if (bm && bmarray)
|
|
for (i = 0; i < CLR_MAX; ++i) {
|
|
if (bmarray[i] && (bm & bmarray[i]))
|
|
return i;
|
|
}
|
|
return NO_COLOR;
|
|
}
|
|
|
|
static int
|
|
condattr(bm, bmarray)
|
|
long bm;
|
|
unsigned long *bmarray;
|
|
{
|
|
int attr = 0;
|
|
int i;
|
|
|
|
if (bm && bmarray) {
|
|
for (i = HL_ATTCLR_DIM; i < BL_ATTCLR_MAX; ++i) {
|
|
if (bmarray[i] && (bm & bmarray[i])) {
|
|
switch(i) {
|
|
case HL_ATTCLR_DIM:
|
|
attr |= HL_DIM;
|
|
break;
|
|
case HL_ATTCLR_BLINK:
|
|
attr |= HL_BLINK;
|
|
break;
|
|
case HL_ATTCLR_ULINE:
|
|
attr |= HL_ULINE;
|
|
break;
|
|
case HL_ATTCLR_INVERSE:
|
|
attr |= HL_INVERSE;
|
|
break;
|
|
case HL_ATTCLR_BOLD:
|
|
attr |= HL_BOLD;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return attr;
|
|
}
|
|
|
|
void
|
|
X11_status_init()
|
|
{
|
|
int i;
|
|
|
|
/* no color and no attributes */
|
|
for (i = 0; i < MAXBLSTATS; ++i)
|
|
X11_status_colors[i] = old_field_colors[i] = NO_COLOR;
|
|
for (i = 0; i < SIZE(old_cond_colors); ++i)
|
|
old_cond_colors[i] = NO_COLOR;
|
|
hpbar_percent = 0, hpbar_color = NO_COLOR;
|
|
X11_condition_bits = old_condition_bits = 0L;
|
|
/* let genl_status_init do most of the initialization */
|
|
genl_status_init();
|
|
}
|
|
|
|
void
|
|
X11_status_finish()
|
|
{
|
|
/* nothing */
|
|
return;
|
|
}
|
|
|
|
void
|
|
X11_status_enablefield(fieldidx, nm, fmt, enable)
|
|
int fieldidx;
|
|
const char *nm;
|
|
const char *fmt;
|
|
boolean enable;
|
|
{
|
|
genl_status_enablefield(fieldidx, nm, fmt, enable);
|
|
}
|
|
|
|
#if 0
|
|
int
|
|
cond_bm2idx(bm)
|
|
unsigned long bm;
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 32; i++)
|
|
if ((1 << i) == bm)
|
|
return i;
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
/* highlight a tty-style status field (or condition) */
|
|
static void
|
|
HiliteField(label, fld, cond, colrattr, font_p)
|
|
Widget label;
|
|
int fld, cond, colrattr;
|
|
XFontStruct **font_p;
|
|
{
|
|
#ifdef STATUS_HILITES
|
|
static Pixel grayPxl, blackPxl, whitePxl;
|
|
Arg args[6];
|
|
Cardinal num_args;
|
|
XFontStruct *font = X11_status_font;
|
|
Pixel px, fg = X11_status_fg, bg = X11_status_bg;
|
|
struct xwindow *xw = xw_status_win;
|
|
int colr, attr;
|
|
|
|
#ifdef TEXTCOLOR
|
|
if ((colrattr & 0x00ff) >= CLR_MAX)
|
|
/* for !TEXTCOLOR, the following line is unconditional */
|
|
#endif
|
|
colrattr = (colrattr & ~0x00ff) | NO_COLOR;
|
|
colr = colrattr & 0x00ff; /* guaranteed to be >= 0 and < CLR_MAX */
|
|
attr = (colrattr >> 8) & 0x00ff;
|
|
|
|
/* potentially used even for !TEXTCOLOR configuration */
|
|
if (!grayPxl) {/* one-time init */
|
|
grayPxl = get_nhcolor(xw, CLR_GRAY).pixel;
|
|
blackPxl = get_nhcolor(xw, CLR_BLACK).pixel;
|
|
whitePxl = get_nhcolor(xw, CLR_WHITE).pixel;
|
|
}
|
|
/* [shouldn't be necessary; setting up gray will set up all colors] */
|
|
if (colr != NO_COLOR && !xw->nh_colors[colr].pixel)
|
|
(void) get_nhcolor(xw, colr);
|
|
|
|
/* handle highlighting if caller has specified that; set foreground,
|
|
background, and font even if not specified this time in case they
|
|
used modified values last time (which would stick if not reset) */
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
if (colr != NO_COLOR)
|
|
fg = xw->nh_colors[colr].pixel;
|
|
if ((attr & HL_INVERSE) != 0) {
|
|
px = fg;
|
|
fg = bg;
|
|
bg = px;
|
|
}
|
|
/* foreground and background might both default to black, so we
|
|
need to force one to be different if/when they're the same
|
|
(actually, tt_status_fixup() takes care of that nowadays);
|
|
using gray to implement 'dim' only works for black and white
|
|
(or color+'inverse' when former background was black or white) */
|
|
if (fg == bg
|
|
|| ((attr & HL_DIM) != 0 && (fg == whitePxl || fg == blackPxl)))
|
|
fg = (fg != grayPxl) ? grayPxl
|
|
: (fg != blackPxl) ? blackPxl
|
|
: whitePxl;
|
|
XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++;
|
|
if (attr & HL_BOLD) {
|
|
load_boldfont(xw_status_win, label);
|
|
if (xw_status_win->boldfs)
|
|
font = xw_status_win->boldfs;
|
|
}
|
|
XtSetArg(args[num_args], nhStr(XtNfont), font); num_args++;
|
|
XtSetValues(label, args, num_args);
|
|
|
|
/* return possibly modified font to caller so that text width
|
|
measurement can use it */
|
|
if (font_p)
|
|
*font_p = font;
|
|
#else /*!STATUS_HILITES*/
|
|
nhUse(label);
|
|
nhUse(font_p);
|
|
#endif /*?STATUS_HILITES*/
|
|
if (fld != BL_CONDITION)
|
|
old_field_colors[fld] = colrattr;
|
|
else
|
|
old_cond_colors[cond] = colrattr;
|
|
}
|
|
|
|
/* set up a specific field other than 'condition'; its general location
|
|
was specified during widget creation but it might need adjusting */
|
|
static void
|
|
PrepStatusField(fld, label, text)
|
|
int fld;
|
|
Widget label;
|
|
const char *text;
|
|
{
|
|
Arg args[6];
|
|
Cardinal num_args;
|
|
Dimension lbl_wid;
|
|
XFontStruct *font = X11_status_font;
|
|
int colrattr = X11_status_colors[fld];
|
|
struct status_info_t *si = xw_status_win->Win_info.Status_info;
|
|
|
|
/* highlight if color and/or attribute(s) are different from last time */
|
|
if (colrattr != old_field_colors[fld])
|
|
HiliteField(label, fld, 0, colrattr, &font);
|
|
|
|
num_args = 0;
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
/* set up the current text to be displayed */
|
|
if (text && *text) {
|
|
lbl_wid = 2 * si->in_wd + XTextWidth(font, text, (int) strlen(text));
|
|
} else {
|
|
text = "";
|
|
lbl_wid = 1;
|
|
}
|
|
XtSetArg(args[num_args], nhStr(XtNlabel), text); num_args++;
|
|
/*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
|
|
XtSetValues(label, args, num_args);
|
|
XtResizeWidget(label, lbl_wid, si->ht, si->brd);
|
|
}
|
|
|
|
/* set up one status condition for tty-style status display */
|
|
static void
|
|
DisplayCond(c_idx, colormasks)
|
|
int c_idx; /* index into tt_condorder[] */
|
|
unsigned long *colormasks;
|
|
{
|
|
Widget label;
|
|
Arg args[6];
|
|
Cardinal num_args;
|
|
Dimension lbl_wid;
|
|
XFontStruct *font = X11_status_font;
|
|
int coloridx, attrmask, colrattr, idx;
|
|
unsigned long bm = tt_condorder[c_idx].mask;
|
|
const char *text = tt_condorder[c_idx].text;
|
|
struct status_info_t *si = xw_status_win->Win_info.Status_info;
|
|
|
|
if ((X11_condition_bits & bm) == 0)
|
|
return;
|
|
|
|
/* widgets have been created for every condition; we allocate them
|
|
from left to right rather than keeping their original assignments */
|
|
idx = next_cond_indx;
|
|
label = X11_cond_labels[idx];
|
|
|
|
/* handle highlighting if caller requests it */
|
|
coloridx = condcolor(bm, colormasks);
|
|
attrmask = condattr(bm, colormasks);
|
|
colrattr = (attrmask << 8) | coloridx;
|
|
if (colrattr != old_cond_colors[c_idx])
|
|
HiliteField(label, BL_CONDITION, c_idx, colrattr, &font);
|
|
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
/* set the condition text and its width; this widget might have
|
|
been displaying a different condition last time around */
|
|
XtSetArg(args[num_args], nhStr(XtNlabel), text); num_args++;
|
|
/* measure width after maybe changing font [HiliteField()] */
|
|
lbl_wid = 2 * si->in_wd + XTextWidth(font, text, (int) strlen(text));
|
|
/*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
|
|
|
|
/* make this condition widget be ready for display */
|
|
XtSetValues(label, args, num_args);
|
|
XtResizeWidget(label, lbl_wid, si->ht, si->brd);
|
|
|
|
++next_cond_indx;
|
|
}
|
|
|
|
/* display the tty-style status conditions; the number shown varies and
|
|
we might be showing more, same, or fewer than during previous status */
|
|
static int
|
|
render_conditions(row, dx)
|
|
int row, dx;
|
|
{
|
|
Widget label;
|
|
Arg args[6];
|
|
Cardinal num_args;
|
|
Position lbl_x;
|
|
int i, gap = 5;
|
|
struct status_info_t *si = xw_status_win->Win_info.Status_info;
|
|
Dimension lbl_wid, brd_wid = si->brd;
|
|
|
|
for (i = 0; i < next_cond_indx; i++) {
|
|
label = X11_cond_labels[i];
|
|
|
|
/* width of this widget was set in DisplayCond(); fetch it */
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], nhStr(XtNwidth), &lbl_wid); num_args++;
|
|
XtGetValues(label, args, num_args);
|
|
|
|
/* figure out where to draw this widget and place it there */
|
|
lbl_x = (Position) (dx + 1 + gap);
|
|
|
|
XtConfigureWidget(label, lbl_x, si->y[row], lbl_wid, si->ht, brd_wid);
|
|
|
|
/* keep track of where the end of our text appears */
|
|
dx = (int) lbl_x + (int) (lbl_wid + 2 * brd_wid) - 1;
|
|
}
|
|
|
|
/* if we have fewer conditions shown now than last time, set the
|
|
excess ones to blank; unlike the set drawn above, these haven't
|
|
been prepared in advance by DisplayCond because they aren't
|
|
being displayed; they might have been highlighted last time so
|
|
we need to specify more than just an empty text string */
|
|
if (next_cond_indx < prev_cond_indx) {
|
|
XFontStruct *font = X11_status_font;
|
|
Pixel fg = X11_status_fg, bg = X11_status_bg;
|
|
|
|
lbl_x = dx + 1;
|
|
lbl_wid = 1 + 2 * brd_wid;
|
|
for (i = next_cond_indx; i < prev_cond_indx; ++i) {
|
|
label = X11_cond_labels[i];
|
|
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], nhStr(XtNlabel), ""); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNfont), font); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++;
|
|
/*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
|
|
/*XtSetArg(args[num_args], nhStr(XtNx), lbl_x); num_args++;*/
|
|
XtSetValues(label, args, num_args);
|
|
old_cond_colors[i] = NO_COLOR; /* fg, bg, font were just reset */
|
|
XtConfigureWidget(label, lbl_x, si->y[row], lbl_wid, si->ht, 0);
|
|
/* don't advance 'dx' here */
|
|
}
|
|
}
|
|
|
|
return dx;
|
|
}
|
|
|
|
#ifdef STATUS_HILITES
|
|
/* reset status_hilite for BL_RESET; if highlighting has been disabled or
|
|
this field is disabled, clear highlighting for this field or condition */
|
|
static void
|
|
tt_reset_color(fld, cond, colormasks)
|
|
int fld, cond;
|
|
unsigned long *colormasks;
|
|
{
|
|
Widget label;
|
|
int colrattr = NO_COLOR;
|
|
|
|
if (fld != BL_CONDITION) {
|
|
if (iflags.hilite_delta != 0L && status_activefields[fld])
|
|
colrattr = X11_status_colors[fld];
|
|
cond = 0;
|
|
label = X11_status_labels[fld];
|
|
} else {
|
|
unsigned long bm = tt_condorder[cond].mask;
|
|
|
|
if (iflags.hilite_delta != 0L && (X11_condition_bits & bm) != 0) {
|
|
/* BL_RESET before first BL_CONDITION will have colormasks==Null
|
|
but condcolor() and condattr() can cope with that */
|
|
#ifdef TEXTCOLOR
|
|
colrattr = condcolor(bm, colormasks);
|
|
#endif
|
|
colrattr |= (condattr(bm, colormasks) << 8);
|
|
}
|
|
label = X11_cond_labels[cond];
|
|
}
|
|
HiliteField(label, fld, cond, colrattr, (XFontStruct **) 0);
|
|
}
|
|
#endif
|
|
|
|
/* make sure foreground, background, and font have reasonable values,
|
|
then explicitly set them for all the status widgets;
|
|
also cache some geometry settings in (*xw_status_win).Status_info */
|
|
static void
|
|
tt_status_fixup()
|
|
{
|
|
Arg args[6];
|
|
Cardinal num_args;
|
|
Widget w;
|
|
Position lbl_y;
|
|
int x, y, ci, fld;
|
|
XFontStruct *font = 0;
|
|
Pixel fg = 0, bg = 0;
|
|
struct status_info_t *si = xw_status_win->Win_info.Status_info;
|
|
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], nhStr(XtNfont), &font); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNforeground), &fg); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNbackground), &bg); num_args++;
|
|
XtGetValues(X11_status_widget, args, num_args);
|
|
if (fg == bg) {
|
|
XColor black = get_nhcolor(xw_status_win, CLR_BLACK),
|
|
white = get_nhcolor(xw_status_win, CLR_WHITE);
|
|
|
|
fg = (bg == white.pixel) ? black.pixel : white.pixel;
|
|
}
|
|
X11_status_fg = si->fg = fg, X11_status_bg = si->bg = bg;
|
|
|
|
if (!font) {
|
|
w = X11_status_widget;
|
|
XtSetArg(args[0], nhStr(XtNfont), &font);
|
|
do {
|
|
XtGetValues(w, args, ONE);
|
|
} while (!font && (w = XtParent(w)) != 0);
|
|
|
|
if (!font) {
|
|
/* trial and error time -- this is where we've actually
|
|
been obtaining the font even though we aren't setting
|
|
it for any of the field widgets (until for(y,x) below) */
|
|
XtGetValues(X11_status_labels[0], args, ONE);
|
|
|
|
if (!font) { /* this bit is untested... */
|
|
/* write some text and hope Xaw sets up font for us */
|
|
XtSetArg(args[0], nhStr(XtNlabel), "NetHack");
|
|
XtSetValues(X11_status_labels[0], args, ONE);
|
|
(void) XFlush(XtDisplay(X11_status_labels[0]));
|
|
|
|
XtSetArg(args[0], nhStr(XtNfont), &font);
|
|
XtGetValues(X11_status_labels[0], args, ONE);
|
|
|
|
/* if still Null, XTextWidth() would crash so bail out */
|
|
if (!font)
|
|
panic("X11 status can't determine font.");
|
|
}
|
|
}
|
|
}
|
|
X11_status_font = si->fs = font;
|
|
|
|
/* amount of space to advance a widget's location by one space;
|
|
increase width a tiny bit beyond the actual space */
|
|
si->spacew = XTextWidth(X11_status_font, " ", 1) + (Dimension) 1;
|
|
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], nhStr(XtNfont), font); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNforeground), fg); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNbackground), bg); num_args++;
|
|
XtSetValues(X11_status_widget, args, num_args);
|
|
|
|
for (y = 0; y < X11_NUM_STATUS_LINES; y++) {
|
|
for (x = 0; x < X11_NUM_STATUS_FIELD; x++) {
|
|
fld = X11_fieldorder[y][x]; /* next field to handle */
|
|
if (fld <= BL_FLUSH)
|
|
continue; /* skip fieldorder[][] padding */
|
|
|
|
XtSetValues(X11_status_labels[fld], args, num_args);
|
|
old_field_colors[fld] = NO_COLOR;
|
|
|
|
if (fld == BL_CONDITION) {
|
|
for (ci = 0; ci < SIZE(X11_cond_labels); ++ci) { /* 0..31 */
|
|
XtSetValues(X11_cond_labels[ci], args, num_args);
|
|
old_cond_colors[ci] = NO_COLOR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* cache the y coordinate of each row for XtConfigureWidget() */
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
XtSetArg(args[0], nhStr(XtNy), &lbl_y); num_args++;
|
|
for (y = 0; y < X11_NUM_STATUS_LINES; y++) {
|
|
fld = X11_fieldorder[y][0];
|
|
XtGetValues(X11_status_labels[fld], args, num_args);
|
|
si->y[y] = lbl_y;
|
|
}
|
|
|
|
/* cache height, borderWidth, and internalWidth for XtResizeWidget() */
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], nhStr(XtNheight), &si->ht); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNborderWidth), &si->brd); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNinternalWidth), &si->in_wd); num_args++;
|
|
XtGetValues(X11_status_labels[0], args, num_args);
|
|
}
|
|
|
|
/* core requests updating one status field (or is indicating that it's time
|
|
to flush all updated fields); tty-style handling */
|
|
static void
|
|
X11_status_update_tty(fld, ptr, chg, percent, color, colormasks)
|
|
int fld, chg UNUSED, percent, color;
|
|
genericptr_t ptr;
|
|
unsigned long *colormasks; /* bitmask of highlights for conditions */
|
|
{
|
|
static int xtra_space[MAXBLSTATS];
|
|
static unsigned long *cond_colormasks = (unsigned long *) 0;
|
|
|
|
Arg args[6];
|
|
Cardinal num_args;
|
|
Position lbl_x;
|
|
Dimension lbl_wid, brd_wid;
|
|
Widget label;
|
|
|
|
struct status_info_t *si;
|
|
char goldbuf[40];
|
|
const char *fmt;
|
|
const char *text;
|
|
unsigned long *condptr;
|
|
int f, x, y, dx;
|
|
|
|
if (X11_status_fg == X11_status_bg || !X11_status_font)
|
|
tt_status_fixup();
|
|
|
|
if (fld == BL_RESET) {
|
|
#ifdef STATUS_HILITES
|
|
for (y = 0; y < X11_NUM_STATUS_LINES; y++) {
|
|
for (x = 0; x < X11_NUM_STATUS_FIELD; x++) {
|
|
f = X11_fieldorder[y][x];
|
|
if (f <= BL_FLUSH)
|
|
continue; /* skip padding in the fieldorder[][] layout */
|
|
if (f != BL_CONDITION) {
|
|
tt_reset_color(f, 0, (unsigned long *) 0);
|
|
} else {
|
|
int c_i;
|
|
|
|
for (c_i = 0; c_i < SIZE(tt_condorder); ++c_i)
|
|
tt_reset_color(f, c_i, cond_colormasks);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
fld = BL_FLUSH;
|
|
}
|
|
|
|
if (fld == BL_CONDITION) {
|
|
condptr = (unsigned long *) ptr;
|
|
X11_condition_bits = *condptr;
|
|
cond_colormasks = colormasks; /* expected to be non-Null */
|
|
|
|
prev_cond_indx = next_cond_indx;
|
|
next_cond_indx = 0;
|
|
/* if any conditions are active, set up their widgets */
|
|
if (X11_condition_bits)
|
|
for (f = 0; f < SIZE(tt_condorder); ++f)
|
|
DisplayCond(f, cond_colormasks);
|
|
return;
|
|
|
|
} else if (fld != BL_FLUSH) {
|
|
/* set up a specific field other than 'condition' */
|
|
text = (char *) ptr;
|
|
if (fld == BL_GOLD)
|
|
text = decode_mixed(goldbuf, text);
|
|
xtra_space[fld] = 0;
|
|
if (status_activefields[fld]) {
|
|
fmt = (fld == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30s"
|
|
: status_fieldfmt[fld] ? status_fieldfmt[fld] : "%s";
|
|
if (*fmt == ' ') {
|
|
++xtra_space[fld];
|
|
++fmt;
|
|
}
|
|
Sprintf(status_vals[fld], fmt, text);
|
|
} else {
|
|
/* don't expect this since core won't call status_update()
|
|
for a field which isn't active */
|
|
*status_vals[fld] = '\0';
|
|
}
|
|
#ifndef TEXTCOLOR
|
|
/* even without color, attribute(s) bits still apply */
|
|
color = (color & ~0x00ff) | NO_COLOR;
|
|
#endif
|
|
#ifdef STATUS_HILITES
|
|
if (!iflags.hilite_delta)
|
|
color = NO_COLOR;
|
|
#endif
|
|
X11_status_colors[fld] = color;
|
|
if (iflags.wc2_hitpointbar && fld == BL_HP) {
|
|
hpbar_percent = percent;
|
|
hpbar_color = color;
|
|
}
|
|
|
|
label = X11_status_labels[fld];
|
|
text = status_vals[fld];
|
|
PrepStatusField(fld, label, text);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* BL_FLUSH: draw all the status fields.
|
|
*/
|
|
si = xw_status_win->Win_info.Status_info; /* for cached geometry */
|
|
|
|
for (y = 0; y < X11_NUM_STATUS_LINES; y++) { /* row */
|
|
dx = 0; /* no pixels written to this row yet */
|
|
|
|
for (x = 0; x < X11_NUM_STATUS_FIELD; x++) { /* 'column' */
|
|
f = X11_fieldorder[y][x];
|
|
if (f <= BL_FLUSH)
|
|
continue; /* skip padding in the fieldorder[][] layout */
|
|
|
|
if (f == BL_CONDITION) {
|
|
if (next_cond_indx > 0 || prev_cond_indx > 0)
|
|
dx = render_conditions(y, dx);
|
|
continue;
|
|
}
|
|
|
|
label = X11_status_labels[f];
|
|
text = status_vals[f];
|
|
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], nhStr(XtNwidth), &lbl_wid); num_args++;
|
|
XtGetValues(label, args, num_args);
|
|
brd_wid = si->brd;
|
|
|
|
/* for a field which shouldn't be shown, we can't just skip
|
|
it because we might need to remove its previous content
|
|
if it has just been toggled off */
|
|
if (!status_activefields[f]) {
|
|
if (lbl_wid <= 1)
|
|
continue;
|
|
text = "";
|
|
lbl_wid = (Dimension) 1;
|
|
brd_wid = (Dimension) 0;
|
|
} else if (xtra_space[f]) {
|
|
/* if this field was to be formatted with a leading space
|
|
to separate it from the preceding field, we suppressed
|
|
that space during formatting; insert separation between
|
|
fields here; this prevents inverse video highlighting
|
|
from inverting the separating space along with the text */
|
|
dx += xtra_space[f] * si->spacew;
|
|
}
|
|
|
|
/* where to display the current widget */
|
|
lbl_x = (Position) (dx + 1);
|
|
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], nhStr(XtNlabel), text); num_args++;
|
|
/*XtSetArg(args[num_args], nhStr(XtNwidth), lbl_wid); num_args++;*/
|
|
/*XtSetArg(args[num_args], nhStr(XtNx), lbl_x); num_args++;*/
|
|
XtSetValues(label, args, num_args);
|
|
XtConfigureWidget(label, lbl_x, si->y[y],
|
|
lbl_wid, si->ht, brd_wid);
|
|
|
|
/* track the right-most pixel written so far */
|
|
dx = (int) lbl_x + (int) (lbl_wid + 2 * brd_wid) - 1;
|
|
} /* [x] fields/columns in current row */
|
|
} /* [y] rows */
|
|
|
|
/* this probably doesn't buy us anything but it isn't a sure thing
|
|
that nethack will immediately ask for input (triggering auto-flush) */
|
|
(void) XFlush(XtDisplay(X11_status_labels[0]));
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void
|
|
X11_status_update_fancy(fld, ptr, chg, percent, color, colormasks)
|
|
int fld, chg UNUSED, percent UNUSED, color UNUSED;
|
|
genericptr_t ptr;
|
|
unsigned long *colormasks UNUSED;
|
|
{
|
|
static const struct bl_to_ff {
|
|
int bl, ff;
|
|
} bl_to_fancyfield[] = {
|
|
{ BL_TITLE, F_NAME },
|
|
{ BL_STR, F_STR },
|
|
{ BL_DX, F_DEX },
|
|
{ BL_CO, F_CON },
|
|
{ BL_IN, F_INT },
|
|
{ BL_WI, F_WIS },
|
|
{ BL_CH, F_CHA },
|
|
{ BL_ALIGN, F_ALIGN },
|
|
{ BL_SCORE, F_SCORE },
|
|
{ BL_CAP, F_ENCUMBER },
|
|
{ BL_GOLD, F_GOLD },
|
|
{ BL_ENE, F_POWER },
|
|
{ BL_ENEMAX, F_MAXPOWER },
|
|
{ BL_XP, F_XP_LEVL }, /* shares with BL_HD, depending upon Upolyd */
|
|
{ BL_AC, F_AC },
|
|
{ BL_TIME, F_TIME },
|
|
{ BL_HUNGER, F_HUNGER },
|
|
{ BL_HP, F_HP },
|
|
{ BL_HPMAX, F_MAXHP },
|
|
{ BL_LEVELDESC, F_DLEVEL },
|
|
{ BL_EXP, F_EXP_PTS }
|
|
};
|
|
static const struct mask_to_ff {
|
|
unsigned long mask;
|
|
int ff;
|
|
} mask_to_fancyfield[] = {
|
|
{ BL_MASK_GRAB, F_GRABBED },
|
|
{ BL_MASK_STONE, F_STONE },
|
|
{ BL_MASK_SLIME, F_SLIME },
|
|
{ BL_MASK_STRNGL, F_STRNGL },
|
|
{ BL_MASK_FOODPOIS, F_FOODPOIS },
|
|
{ BL_MASK_TERMILL, F_TERMILL },
|
|
{ BL_MASK_INLAVA, F_IN_LAVA },
|
|
{ BL_MASK_HELD, F_HELD },
|
|
{ BL_MASK_BLIND, F_BLIND },
|
|
{ BL_MASK_DEAF, F_DEAF },
|
|
{ BL_MASK_STUN, F_STUN },
|
|
{ BL_MASK_CONF, F_CONF },
|
|
{ BL_MASK_HALLU, F_HALLU },
|
|
{ BL_MASK_TRAPPED, F_TRAPPED },
|
|
{ BL_MASK_LEV, F_LEV },
|
|
{ BL_MASK_FLY, F_FLY },
|
|
{ BL_MASK_RIDE, F_RIDE }
|
|
};
|
|
int i;
|
|
|
|
if (fld == BL_RESET || fld == BL_FLUSH) {
|
|
if (WIN_STATUS != WIN_ERR) {
|
|
update_fancy_status(FALSE);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (fld == BL_CONDITION) {
|
|
unsigned long changed_bits, *condptr = (unsigned long *) ptr;
|
|
|
|
X11_condition_bits = *condptr;
|
|
/* process the bits that are different from last time */
|
|
changed_bits = (X11_condition_bits ^ old_condition_bits);
|
|
if (changed_bits) {
|
|
for (i = 0; i < SIZE(mask_to_fancyfield); i++)
|
|
if ((changed_bits & mask_to_fancyfield[i].mask) != 0L)
|
|
update_fancy_status_field(mask_to_fancyfield[i].ff);
|
|
old_condition_bits = X11_condition_bits;
|
|
}
|
|
} else {
|
|
for (i = 0; i < SIZE(bl_to_fancyfield); i++)
|
|
if (bl_to_fancyfield[i].bl == fld) {
|
|
update_fancy_status_field(bl_to_fancyfield[i].ff);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
X11_status_update(fld, ptr, chg, percent, color, colormasks)
|
|
int fld, chg UNUSED, percent UNUSED, color;
|
|
genericptr_t ptr;
|
|
unsigned long *colormasks;
|
|
{
|
|
if (fld < BL_RESET || fld >= MAXBLSTATS)
|
|
panic("X11_status_update(%d) -- invalid field", fld);
|
|
|
|
if (appResources.fancy_status)
|
|
X11_status_update_fancy(fld, ptr, chg, percent, color, colormasks);
|
|
else
|
|
X11_status_update_tty(fld, ptr, chg, percent, color, colormasks);
|
|
}
|
|
|
|
/* create a widget for a particular status field or potential condition */
|
|
static Widget
|
|
create_tty_status_field(fld, condindx, above, left)
|
|
int fld, condindx;
|
|
Widget above, left;
|
|
{
|
|
Arg args[16];
|
|
Cardinal num_args;
|
|
char labelname[40];
|
|
int gap = condindx ? 5 : 0;
|
|
|
|
if (!condindx)
|
|
Sprintf(labelname, "label_%s", bl_idx_to_fldname(fld));
|
|
else
|
|
Sprintf(labelname, "cond_%02d", condindx);
|
|
|
|
/* set up widget attributes which (mostly) aren't going to be changing */
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
if (above)
|
|
XtSetArg(args[num_args], nhStr(XtNfromVert), above); num_args++;
|
|
if (left)
|
|
XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++;
|
|
|
|
XtSetArg(args[num_args], nhStr(XtNhorizDistance), gap); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNvertDistance), 0); num_args++;
|
|
|
|
XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNbottomMargin), 0); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNleftMargin), 0); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNrightMargin), 0); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++;
|
|
/* internalWidth: default is 4; cut that it half and adjust regular
|
|
width to have it on both left and right instead of just on the left */
|
|
XtSetArg(args[num_args], nhStr(XtNinternalWidth), 2); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNborderWidth), 0); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNlabel), ""); num_args++;
|
|
return XtCreateManagedWidget(labelname, labelWidgetClass,
|
|
X11_status_widget, args, num_args);
|
|
}
|
|
|
|
/* create an overall status widget (X11_status_widget) and also
|
|
separate widgets for all status fields and potential conditions */
|
|
static Widget
|
|
create_tty_status(parent, top)
|
|
Widget parent, top;
|
|
{
|
|
Widget form; /* viewport that holds the form that surrounds everything */
|
|
Widget w, over_w, prev_w;
|
|
Arg args[6];
|
|
Cardinal num_args;
|
|
int x, y, i, fld;
|
|
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
if (top)
|
|
XtSetArg(args[num_args], nhStr(XtNfromVert), top); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++;
|
|
XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
|
|
XtSetArg(args[num_args], XtNwidth, 400); num_args++;
|
|
XtSetArg(args[num_args], XtNheight, 100); num_args++;
|
|
form = XtCreateManagedWidget("status_viewport", viewportWidgetClass,
|
|
parent, args, num_args);
|
|
|
|
(void) memset((genericptr_t) args, 0, sizeof args);
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], XtNwidth, 400); num_args++;
|
|
XtSetArg(args[num_args], XtNheight, 100); num_args++;
|
|
X11_status_widget = XtCreateManagedWidget("status_form", formWidgetClass,
|
|
form, args, num_args);
|
|
|
|
for (y = 0; y < X11_NUM_STATUS_LINES; y++) {
|
|
fld = (y > 0) ? X11_fieldorder[y - 1][0] : 0; /* temp, for over_w */
|
|
/* for row(s) beyond the first, pick a widget in the previous
|
|
row to put this one underneath (in 'y' terms; 'x' is fluid) */
|
|
over_w = (y > 0) ? X11_status_labels[fld] : (Widget) 0;
|
|
/* widget on current row to put the next one to the right of ('x') */
|
|
prev_w = (Widget) 0;
|
|
for (x = 0; x < X11_NUM_STATUS_FIELD; x++) {
|
|
fld = X11_fieldorder[y][x]; /* next field to handle */
|
|
if (fld <= BL_FLUSH)
|
|
continue; /* skip fieldorder[][] padding */
|
|
|
|
w = create_tty_status_field(fld, 0, over_w, prev_w);
|
|
X11_status_labels[fld] = prev_w = w;
|
|
|
|
if (fld == BL_CONDITION) {
|
|
for (i = 1; i <= SIZE(X11_cond_labels); ++i) { /* 1..32 */
|
|
w = create_tty_status_field(fld, i, over_w, prev_w);
|
|
X11_cond_labels[i - 1] = prev_w = w;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return X11_status_widget;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
void
|
|
create_status_window_tty(wp, create_popup, parent)
|
|
struct xwindow *wp; /* window pointer */
|
|
boolean create_popup UNUSED;
|
|
Widget parent;
|
|
{
|
|
wp->type = NHW_STATUS;
|
|
wp->w = create_tty_status(parent, (Widget) 0);
|
|
}
|
|
|
|
void
|
|
destroy_status_window_tty(wp)
|
|
struct xwindow *wp;
|
|
{
|
|
/* If status_information is defined, then it a "text" status window. */
|
|
if (wp->status_information) {
|
|
if (wp->popup) {
|
|
nh_XtPopdown(wp->popup);
|
|
if (!wp->keep_window)
|
|
XtDestroyWidget(wp->popup), wp->popup = (Widget) 0;
|
|
}
|
|
free((genericptr_t) wp->status_information);
|
|
wp->status_information = 0;
|
|
} else {
|
|
;
|
|
}
|
|
if (!wp->keep_window)
|
|
wp->type = NHW_NONE;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
void
|
|
adjust_status_tty(wp, str)
|
|
struct xwindow *wp UNUSED;
|
|
const char *str UNUSED;
|
|
{
|
|
/* nothing */
|
|
return;
|
|
}
|
|
|
|
void
|
|
create_status_window(wp, create_popup, parent)
|
|
struct xwindow *wp; /* window pointer */
|
|
boolean create_popup;
|
|
Widget parent;
|
|
{
|
|
struct status_info_t *si = (struct status_info_t *) alloc(sizeof *si);
|
|
|
|
xw_status_win = wp;
|
|
if (wp->Win_info.Status_info)
|
|
free((genericptr_t) wp->Win_info.Status_info);
|
|
wp->Win_info.Status_info = si;
|
|
(void) memset((genericptr_t) si, 0, sizeof *si);
|
|
|
|
if (!appResources.fancy_status)
|
|
create_status_window_tty(wp, create_popup, parent);
|
|
else
|
|
create_status_window_fancy(wp, create_popup, parent);
|
|
}
|
|
|
|
void
|
|
destroy_status_window(wp)
|
|
struct xwindow *wp;
|
|
{
|
|
if (appResources.fancy_status)
|
|
destroy_status_window_fancy(wp);
|
|
else
|
|
destroy_status_window_tty(wp);
|
|
}
|
|
|
|
void
|
|
adjust_status(wp, str)
|
|
struct xwindow *wp;
|
|
const char *str;
|
|
{
|
|
if (appResources.fancy_status)
|
|
adjust_status_fancy(wp, str);
|
|
else
|
|
adjust_status_tty(wp, str);
|
|
}
|
|
|
|
void
|
|
create_status_window_fancy(wp, create_popup, parent)
|
|
struct xwindow *wp; /* window pointer */
|
|
boolean create_popup;
|
|
Widget parent;
|
|
{
|
|
XFontStruct *fs;
|
|
Arg args[8];
|
|
Cardinal num_args;
|
|
Position top_margin, bottom_margin, left_margin, right_margin;
|
|
|
|
wp->type = NHW_STATUS;
|
|
|
|
if (!create_popup) {
|
|
/*
|
|
* If we are not creating a popup, then we must be the "main" status
|
|
* window.
|
|
*/
|
|
if (!parent)
|
|
panic("create_status_window_fancy: no parent for fancy status");
|
|
wp->status_information = 0;
|
|
wp->w = create_fancy_status(parent, (Widget) 0);
|
|
return;
|
|
}
|
|
|
|
wp->status_information =
|
|
(struct status_info_t *) alloc(sizeof (struct status_info_t));
|
|
|
|
init_text_buffer(&wp->status_information->text);
|
|
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], XtNallowShellResize, False); num_args++;
|
|
XtSetArg(args[num_args], XtNinput, False); num_args++;
|
|
|
|
wp->popup = parent = XtCreatePopupShell("status_popup",
|
|
topLevelShellWidgetClass,
|
|
toplevel, args, num_args);
|
|
/*
|
|
* If we're here, then this is an auxiliary status window. If we're
|
|
* cancelled via a delete window message, we should just pop down.
|
|
*/
|
|
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], nhStr(XtNdisplayCaret), False); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNscrollHorizontal),
|
|
XawtextScrollWhenNeeded); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNscrollVertical),
|
|
XawtextScrollWhenNeeded); num_args++;
|
|
|
|
wp->w = XtCreateManagedWidget("status", /* name */
|
|
asciiTextWidgetClass,
|
|
parent, /* parent widget */
|
|
args, /* set some values */
|
|
num_args); /* number of values to set */
|
|
|
|
/*
|
|
* Adjust the height and width of the message window so that it
|
|
* is two lines high and COLNO of the widest characters wide.
|
|
*/
|
|
|
|
/* Get the font and margin information. */
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], XtNfont, &fs); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNtopMargin), &top_margin); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNbottomMargin),
|
|
&bottom_margin); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNleftMargin), &left_margin); num_args++;
|
|
XtSetArg(args[num_args], nhStr(XtNrightMargin),
|
|
&right_margin); num_args++;
|
|
XtGetValues(wp->w, args, num_args);
|
|
|
|
wp->pixel_height = 2 * nhFontHeight(wp->w) + top_margin + bottom_margin;
|
|
wp->pixel_width = COLNO * fs->max_bounds.width
|
|
+ left_margin + right_margin;
|
|
|
|
/* Set the new width and height. */
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], XtNwidth, wp->pixel_width); num_args++;
|
|
XtSetArg(args[num_args], XtNheight, wp->pixel_height); num_args++;
|
|
XtSetValues(wp->w, args, num_args);
|
|
}
|
|
|
|
void
|
|
destroy_status_window_fancy(wp)
|
|
struct xwindow *wp;
|
|
{
|
|
/* If status_information is defined, then it a "text" status window. */
|
|
if (wp->status_information) {
|
|
if (wp->popup) {
|
|
nh_XtPopdown(wp->popup);
|
|
if (!wp->keep_window)
|
|
XtDestroyWidget(wp->popup), wp->popup = (Widget) 0;
|
|
}
|
|
free((genericptr_t) wp->status_information);
|
|
wp->status_information = 0;
|
|
} else {
|
|
destroy_fancy_status(wp);
|
|
}
|
|
if (!wp->keep_window)
|
|
wp->type = NHW_NONE;
|
|
}
|
|
|
|
/*
|
|
* This assumes several things:
|
|
* + Status has only 2 lines
|
|
* + That both lines are updated in succession in line order.
|
|
* + We didn't set stringInPlace on the widget.
|
|
*/
|
|
void
|
|
adjust_status_fancy(wp, str)
|
|
struct xwindow *wp;
|
|
const char *str;
|
|
{
|
|
Arg args[2];
|
|
Cardinal num_args;
|
|
|
|
if (!wp->status_information) {
|
|
update_fancy_status(TRUE);
|
|
return;
|
|
}
|
|
|
|
if (wp->cursy == 0) {
|
|
clear_text_buffer(&wp->status_information->text);
|
|
append_text_buffer(&wp->status_information->text, str, FALSE);
|
|
return;
|
|
}
|
|
append_text_buffer(&wp->status_information->text, str, FALSE);
|
|
|
|
/* Set new buffer as text. */
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], XtNstring,
|
|
wp->status_information->text.text); num_args++;
|
|
XtSetValues(wp->w, args, num_args);
|
|
}
|
|
|
|
/* Fancy ================================================================== */
|
|
static int hilight_time = 1; /* number of turns to hilight a changed value */
|
|
|
|
struct X_status_value {
|
|
/* we have to cast away 'const' when assigning new names */
|
|
const char *name; /* text name */
|
|
int type; /* status type */
|
|
Widget w; /* widget of name/value pair */
|
|
long last_value; /* value displayed */
|
|
int turn_count; /* last time the value changed */
|
|
boolean set; /* if highlighted */
|
|
boolean after_init; /* don't highlight on first change (init) */
|
|
};
|
|
|
|
/* valid type values */
|
|
#define SV_VALUE 0 /* displays a label:value pair */
|
|
#define SV_LABEL 1 /* displays a changable label */
|
|
#define SV_NAME 2 /* displays an unchangeable name */
|
|
|
|
static void FDECL(hilight_label, (Widget));
|
|
static void FDECL(update_val, (struct X_status_value *, long));
|
|
static const char *FDECL(width_string, (int));
|
|
static void FDECL(create_widget, (Widget, struct X_status_value *, int));
|
|
static void FDECL(get_widths, (struct X_status_value *, int *, int *));
|
|
static void FDECL(set_widths, (struct X_status_value *, int, int));
|
|
static Widget FDECL(init_column, (const char *, Widget, Widget, Widget,
|
|
int *, int));
|
|
static void NDECL(fixup_cond_widths);
|
|
static Widget FDECL(init_info_form, (Widget, Widget, Widget));
|
|
|
|
/*
|
|
* Notes:
|
|
* + Alignment needs a different init value, because -1 is an alignment.
|
|
* + Armor Class is an schar, so 256 is out of range.
|
|
* + Blank value is 0 and should never change.
|
|
*
|
|
* - These must be in the same order as the F_foo numbers.
|
|
*/
|
|
static struct X_status_value shown_stats[NUM_STATS] = {
|
|
{ "", SV_NAME, (Widget) 0, -1, 0, FALSE, FALSE }, /* 0, F_DUMMY */
|
|
|
|
{ "Strength", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /* 1*/
|
|
{ "Dexterity", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
|
|
{ "Constitution", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
|
|
{ "Intelligence", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
|
|
{ "Wisdom", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /* 5*/
|
|
{ "Charisma", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
|
|
|
|
{ "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* 7, F_NAME */
|
|
{ "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* F_DLEVEL */
|
|
{ "Gold", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
|
|
{ "Hit Points", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /*10*/
|
|
{ "Max HP", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
|
|
{ "Power", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
|
|
{ "Max Power", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
|
|
{ "Armor Class", SV_VALUE, (Widget) 0, 256, 0, FALSE, FALSE },
|
|
{ "Xp Level", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /*15*/
|
|
/*{ "Hit Dice", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },==15*/
|
|
{ "Exp Points", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
|
|
{ "Alignment", SV_VALUE, (Widget) 0, -2, 0, FALSE, FALSE },
|
|
{ "Time", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
|
|
{ "Score", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE },
|
|
|
|
{ "", SV_NAME, (Widget) 0, -1, 0, FALSE, TRUE }, /*20, F_HUNGER */
|
|
{ "", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /* F_ENCUMBER */
|
|
{ "Trapped", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
|
|
{ "Levitating", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
|
|
{ "Flying", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
|
|
{ "Riding", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*25, F_RIDE */
|
|
|
|
{ "Grabbed!", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*26, F_GRAB */
|
|
{ "Petrifying", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /* F_STONE */
|
|
{ "Slimed", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
|
|
{ "Strangled", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
|
|
{ "Food Pois", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*30*/
|
|
{ "Term Ill", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
|
|
{ "Sinking", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /* 32, F_IN_LAVA */
|
|
|
|
{ "Held", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*33*/
|
|
{ "Blind", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
|
|
{ "Deaf", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*35*/
|
|
{ "Stunned", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
|
|
{ "Confused", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE },
|
|
{ "Hallucinating", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*38*/
|
|
};
|
|
|
|
extern const char *hu_stat[]; /* from eat.c */
|
|
extern const char *enc_stat[]; /* from botl.c */
|
|
|
|
/*
|
|
* Set all widget values to a null string. This is used after all spacings
|
|
* have been calculated so that when the window is popped up we don't get all
|
|
* kinds of funny values being displayed.
|
|
*/
|
|
void
|
|
null_out_status()
|
|
{
|
|
int i;
|
|
struct X_status_value *sv;
|
|
Arg args[1];
|
|
|
|
for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++) {
|
|
switch (sv->type) {
|
|
case SV_VALUE:
|
|
set_value(sv->w, "");
|
|
break;
|
|
|
|
case SV_LABEL:
|
|
case SV_NAME:
|
|
XtSetArg(args[0], XtNlabel, "");
|
|
XtSetValues(sv->w, args, ONE);
|
|
break;
|
|
|
|
default:
|
|
impossible("null_out_status: unknown type %d\n", sv->type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* this is almost an exact duplicate of hilight_value() */
|
|
static void
|
|
hilight_label(w)
|
|
Widget w; /* label widget */
|
|
{
|
|
Arg args[2];
|
|
Pixel fg, bg;
|
|
/*
|
|
* This predates STATUS_HILITES.
|
|
* It is used to show any changed item in inverse and gets
|
|
* reset on the next turn.
|
|
*/
|
|
|
|
XtSetArg(args[0], XtNforeground, &fg);
|
|
XtSetArg(args[1], XtNbackground, &bg);
|
|
XtGetValues(w, args, TWO);
|
|
|
|
XtSetArg(args[0], XtNforeground, bg);
|
|
XtSetArg(args[1], XtNbackground, fg);
|
|
XtSetValues(w, args, TWO);
|
|
}
|
|
|
|
static void
|
|
update_val(attr_rec, new_value)
|
|
struct X_status_value *attr_rec;
|
|
long new_value;
|
|
{
|
|
static boolean Exp_shown = TRUE, time_shown = TRUE, score_shown = TRUE,
|
|
Xp_was_HD = FALSE;
|
|
char buf[BUFSZ];
|
|
Arg args[4];
|
|
|
|
if (attr_rec->type == SV_LABEL) {
|
|
if (attr_rec == &shown_stats[F_NAME]) {
|
|
Strcpy(buf, g.plname);
|
|
buf[0] = highc(buf[0]);
|
|
Strcat(buf, " the ");
|
|
if (Upolyd) {
|
|
char mname[BUFSZ];
|
|
int k;
|
|
|
|
Strcpy(mname, mons[u.umonnum].mname);
|
|
for (k = 0; mname[k] != '\0'; k++) {
|
|
if (k == 0 || mname[k - 1] == ' ')
|
|
mname[k] = highc(mname[k]);
|
|
}
|
|
Strcat(buf, mname);
|
|
} else
|
|
Strcat(buf,
|
|
rank_of(u.ulevel, g.pl_character[0], flags.female));
|
|
|
|
} else if (attr_rec == &shown_stats[F_DLEVEL]) {
|
|
if (!describe_level(buf)) {
|
|
Strcpy(buf, g.dungeons[u.uz.dnum].dname);
|
|
Sprintf(eos(buf), ", level %d", depth(&u.uz));
|
|
}
|
|
} else {
|
|
impossible("update_val: unknown label type \"%s\"",
|
|
attr_rec->name);
|
|
return;
|
|
}
|
|
|
|
if (strcmp(buf, attr_rec->name) == 0)
|
|
return; /* same */
|
|
|
|
/* Set the label. 'name' field is const for most entries;
|
|
we need to cast away that const for this assignment */
|
|
Strcpy((char *) attr_rec->name, buf);
|
|
XtSetArg(args[0], XtNlabel, buf);
|
|
XtSetValues(attr_rec->w, args, ONE);
|
|
|
|
} else if (attr_rec->type == SV_NAME) {
|
|
if (attr_rec->last_value == new_value)
|
|
return; /* no change */
|
|
|
|
attr_rec->last_value = new_value;
|
|
|
|
/* special cases: hunger and encumbrance */
|
|
if (attr_rec == &shown_stats[F_HUNGER]) {
|
|
XtSetArg(args[0], XtNlabel, hu_stat[new_value]);
|
|
} else if (attr_rec == &shown_stats[F_ENCUMBER]) {
|
|
XtSetArg(args[0], XtNlabel, enc_stat[new_value]);
|
|
} else if (new_value) {
|
|
XtSetArg(args[0], XtNlabel, attr_rec->name);
|
|
} else {
|
|
XtSetArg(args[0], XtNlabel, "");
|
|
}
|
|
XtSetValues(attr_rec->w, args, ONE);
|
|
|
|
} else { /* a value pair */
|
|
boolean force_update = FALSE;
|
|
|
|
/* special case: time can be enabled & disabled */
|
|
if (attr_rec == &shown_stats[F_TIME]) {
|
|
if (flags.time && !time_shown) {
|
|
set_name(attr_rec->w, shown_stats[F_TIME].name);
|
|
force_update = TRUE;
|
|
time_shown = TRUE;
|
|
} else if (!flags.time && time_shown) {
|
|
set_name(attr_rec->w, "");
|
|
set_value(attr_rec->w, "");
|
|
time_shown = FALSE;
|
|
}
|
|
if (!time_shown)
|
|
return;
|
|
|
|
/* special case: experience points can be enabled & disabled */
|
|
} else if (attr_rec == &shown_stats[F_EXP_PTS]) {
|
|
boolean showexp = flags.showexp && !Upolyd;
|
|
|
|
if (showexp && !Exp_shown) {
|
|
set_name(attr_rec->w, shown_stats[F_EXP_PTS].name);
|
|
force_update = TRUE;
|
|
Exp_shown = TRUE;
|
|
} else if (!showexp && Exp_shown) {
|
|
set_name(attr_rec->w, "");
|
|
set_value(attr_rec->w, "");
|
|
Exp_shown = FALSE;
|
|
}
|
|
if (!Exp_shown)
|
|
return;
|
|
|
|
/* special case: when available, score can be enabled & disabled */
|
|
} else if (attr_rec == &shown_stats[F_SCORE]) {
|
|
#ifdef SCORE_ON_BOTL
|
|
if (flags.showscore && !score_shown) {
|
|
set_name(attr_rec->w, shown_stats[F_SCORE].name);
|
|
force_update = TRUE;
|
|
score_shown = TRUE;
|
|
} else
|
|
#endif
|
|
if (!flags.showscore && score_shown) {
|
|
set_name(attr_rec->w, "");
|
|
set_value(attr_rec->w, "");
|
|
score_shown = FALSE;
|
|
}
|
|
if (!score_shown)
|
|
return;
|
|
|
|
/* special case: when polymorphed, show "Hit Dice" and disable Exp */
|
|
} else if (attr_rec == &shown_stats[F_XP_LEVL]) {
|
|
if (Upolyd && !Xp_was_HD) {
|
|
force_update = TRUE;
|
|
set_name(attr_rec->w, "Hit Dice");
|
|
Xp_was_HD = TRUE;
|
|
} else if (!Upolyd && Xp_was_HD) {
|
|
force_update = TRUE;
|
|
set_name(attr_rec->w, shown_stats[F_XP_LEVL].name);
|
|
Xp_was_HD = FALSE;
|
|
}
|
|
/* core won't call status_update() for Exp when it hasn't changed
|
|
so do so ourselves (to get Exp_shown flag to match display) */
|
|
if (force_update)
|
|
update_fancy_status_field(F_EXP_PTS);
|
|
}
|
|
|
|
if (attr_rec->last_value == new_value && !force_update) /* same */
|
|
return;
|
|
|
|
attr_rec->last_value = new_value;
|
|
|
|
/* Special cases: strength and other characteristics, alignment
|
|
and "clear". */
|
|
if (attr_rec >= &shown_stats[F_STR]
|
|
&& attr_rec <= &shown_stats[F_CHA]) {
|
|
static const char fmt1[] = "%ld%s", fmt2[] = "%2ld%s";
|
|
struct xwindow *wp;
|
|
const char *fmt = fmt1, *padding = "";
|
|
|
|
/* for full-fledged fancy status, force two digits for all
|
|
six characteristics, followed by three spaces of padding
|
|
to match "/xx" exceptional strength */
|
|
wp = (WIN_STATUS != WIN_ERR) ? &window_list[WIN_STATUS] : 0;
|
|
if (wp && !wp->status_information)
|
|
fmt = fmt2, padding = " ";
|
|
|
|
if (new_value > 18L && attr_rec == &shown_stats[F_STR]) {
|
|
if (new_value > 118L) /* 19..25 encoded as 119..125 */
|
|
Sprintf(buf, fmt, new_value - 100L, padding);
|
|
else if (new_value < 118L) /* 18/01..18/99 as 19..117*/
|
|
Sprintf(buf, "18/%02ld", new_value - 18L);
|
|
else
|
|
Strcpy(buf, "18/**"); /* 18/100 encoded as 118 */
|
|
} else { /* non-strength or less than 18/01 strength (3..18) */
|
|
Sprintf(buf, fmt, new_value, padding); /* 3..25 */
|
|
}
|
|
} else if (attr_rec == &shown_stats[F_ALIGN]) {
|
|
Strcpy(buf, (new_value == A_CHAOTIC) ? "Chaotic"
|
|
: (new_value == A_NEUTRAL) ? "Neutral" : "Lawful");
|
|
} else {
|
|
Sprintf(buf, "%ld", new_value);
|
|
}
|
|
set_value(attr_rec->w, buf);
|
|
}
|
|
|
|
/*
|
|
* Now hilight the changed information. Names, time and score don't
|
|
* hilight. If first time, don't hilight. If already lit, don't do
|
|
* it again.
|
|
*/
|
|
if (attr_rec->type != SV_NAME && attr_rec != &shown_stats[F_TIME]) {
|
|
if (attr_rec->after_init) {
|
|
if (!attr_rec->set) {
|
|
if (attr_rec->type == SV_LABEL)
|
|
hilight_label(attr_rec->w);
|
|
else
|
|
hilight_value(attr_rec->w);
|
|
attr_rec->set = TRUE;
|
|
}
|
|
attr_rec->turn_count = 0;
|
|
} else {
|
|
attr_rec->after_init = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update the displayed status. The current code in botl.c updates
|
|
* two lines of information. Both lines are always updated one after
|
|
* the other. So only do our update when we update the second line.
|
|
*
|
|
* Information on the first line:
|
|
* name, characteristics, alignment, score
|
|
*
|
|
* Information on the second line:
|
|
* dlvl, gold, hp, power, ac, {level & exp or HD **}, time,
|
|
* status * (stone, slime, strngl, foodpois, termill,
|
|
* hunger, encumbrance, lev, fly, ride,
|
|
* blind, deaf, stun, conf, hallu)
|
|
*
|
|
* [*] order of status fields is different on tty.
|
|
* [**] HD is shown instead of level and exp if Upolyd.
|
|
*/
|
|
static void
|
|
update_fancy_status_field(i)
|
|
int i;
|
|
{
|
|
struct X_status_value *sv = &shown_stats[i];
|
|
unsigned long condmask = 0L;
|
|
long val = 0L;
|
|
|
|
switch (i) {
|
|
case F_DUMMY:
|
|
val = 0L;
|
|
break;
|
|
case F_STR:
|
|
val = (long) ACURR(A_STR);
|
|
break;
|
|
case F_DEX:
|
|
val = (long) ACURR(A_DEX);
|
|
break;
|
|
case F_CON:
|
|
val = (long) ACURR(A_CON);
|
|
break;
|
|
case F_INT:
|
|
val = (long) ACURR(A_INT);
|
|
break;
|
|
case F_WIS:
|
|
val = (long) ACURR(A_WIS);
|
|
break;
|
|
case F_CHA:
|
|
val = (long) ACURR(A_CHA);
|
|
break;
|
|
/*
|
|
* Label stats. With the exceptions of hunger and encumbrance
|
|
* these are either on or off. Pleae leave the ternary operators
|
|
* the way they are. I want to specify 0 or 1, not a boolean.
|
|
*/
|
|
case F_HUNGER:
|
|
val = (long) u.uhs;
|
|
break;
|
|
case F_ENCUMBER:
|
|
val = (long) near_capacity();
|
|
break;
|
|
|
|
case F_TRAPPED:
|
|
condmask = BL_MASK_TRAPPED;
|
|
break;
|
|
case F_LEV:
|
|
condmask = BL_MASK_LEV;
|
|
break;
|
|
case F_FLY:
|
|
condmask = BL_MASK_FLY;
|
|
break;
|
|
case F_RIDE:
|
|
condmask = BL_MASK_RIDE;
|
|
break;
|
|
/* fatal status conditions */
|
|
case F_GRABBED:
|
|
condmask = BL_MASK_GRAB;
|
|
break;
|
|
case F_STONE:
|
|
condmask = BL_MASK_STONE;
|
|
break;
|
|
case F_SLIME:
|
|
condmask = BL_MASK_SLIME;
|
|
break;
|
|
case F_STRNGL:
|
|
condmask = BL_MASK_STRNGL;
|
|
break;
|
|
case F_FOODPOIS:
|
|
condmask = BL_MASK_FOODPOIS;
|
|
break;
|
|
case F_TERMILL:
|
|
condmask = BL_MASK_TERMILL;
|
|
break;
|
|
case F_IN_LAVA:
|
|
condmask = BL_MASK_INLAVA;
|
|
break;
|
|
/* non-fatal status conditions */
|
|
case F_HELD:
|
|
condmask = BL_MASK_HELD;
|
|
break;
|
|
case F_BLIND:
|
|
condmask = BL_MASK_BLIND;
|
|
break;
|
|
case F_DEAF:
|
|
condmask = BL_MASK_DEAF;
|
|
break;
|
|
case F_STUN:
|
|
condmask = BL_MASK_STUN;
|
|
break;
|
|
case F_CONF:
|
|
condmask = BL_MASK_CONF;
|
|
break;
|
|
case F_HALLU:
|
|
condmask = BL_MASK_HALLU;
|
|
break;
|
|
|
|
case F_NAME:
|
|
case F_DLEVEL:
|
|
val = (long) 0L;
|
|
break; /* special */
|
|
|
|
case F_GOLD:
|
|
val = money_cnt(g.invent);
|
|
if (val < 0L)
|
|
val = 0L; /* ought to issue impossible() and discard gold */
|
|
break;
|
|
case F_HP:
|
|
val = (long) (Upolyd ? (u.mh > 0 ? u.mh : 0)
|
|
: (u.uhp > 0 ? u.uhp : 0));
|
|
break;
|
|
case F_MAXHP:
|
|
val = (long) (Upolyd ? u.mhmax : u.uhpmax);
|
|
break;
|
|
case F_POWER:
|
|
val = (long) u.uen;
|
|
break;
|
|
case F_MAXPOWER:
|
|
val = (long) u.uenmax;
|
|
break;
|
|
case F_AC:
|
|
val = (long) u.uac;
|
|
break;
|
|
case F_XP_LEVL:
|
|
val = (long) (Upolyd ? mons[u.umonnum].mlevel : u.ulevel);
|
|
break;
|
|
case F_EXP_PTS:
|
|
val = flags.showexp ? u.uexp : 0L;
|
|
break;
|
|
case F_ALIGN:
|
|
val = (long) u.ualign.type;
|
|
break;
|
|
case F_TIME:
|
|
val = flags.time ? (long) g.moves : 0L;
|
|
break;
|
|
case F_SCORE:
|
|
#ifdef SCORE_ON_BOTL
|
|
val = flags.showscore ? botl_score() : 0L;
|
|
#else
|
|
val = 0L;
|
|
#endif
|
|
break;
|
|
default: {
|
|
/*
|
|
* There is a possible infinite loop that occurs with:
|
|
*
|
|
* impossible->pline->flush_screen->bot->bot{1,2}->
|
|
* putstr->adjust_status->update_other->impossible
|
|
*
|
|
* Break out with this.
|
|
*/
|
|
static boolean active = FALSE;
|
|
|
|
if (!active) {
|
|
active = TRUE;
|
|
impossible("update_other: unknown shown value");
|
|
active = FALSE;
|
|
}
|
|
val = 0L;
|
|
break;
|
|
} /* default */
|
|
} /* switch */
|
|
|
|
if (condmask)
|
|
val = ((X11_condition_bits & condmask) != 0L);
|
|
update_val(sv, val);
|
|
}
|
|
|
|
/* fully update status after bl_flush or window resize */
|
|
static void
|
|
update_fancy_status(force_update)
|
|
boolean force_update;
|
|
{
|
|
static boolean old_showtime, old_showexp, old_showscore;
|
|
static int old_upolyd = -1; /* -1: force first time update */
|
|
int i;
|
|
|
|
if (force_update
|
|
|| Upolyd != old_upolyd /* Xp vs HD */
|
|
|| flags.time != old_showtime
|
|
|| flags.showexp != old_showexp
|
|
|| flags.showscore != old_showscore) {
|
|
/* update everything; usually only need this on the very first
|
|
time, then later if the window gets resized or if poly/unpoly
|
|
triggers Xp <-> HD switch or if an optional field gets
|
|
toggled off since there won't be a status_update() call for
|
|
the no longer displayed field; we're a bit more conservative
|
|
than that and do this when toggling on as well as off */
|
|
for (i = 0; i < NUM_STATS; i++)
|
|
update_fancy_status_field(i);
|
|
|
|
old_upolyd = Upolyd;
|
|
old_showtime = flags.time;
|
|
old_showexp = flags.showexp;
|
|
old_showscore = flags.showscore;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Turn off hilighted status values after a certain amount of turns.
|
|
*/
|
|
void
|
|
check_turn_events()
|
|
{
|
|
int i;
|
|
struct X_status_value *sv;
|
|
|
|
for (sv = shown_stats, i = 0; i < NUM_STATS; i++, sv++) {
|
|
if (!sv->set)
|
|
continue;
|
|
|
|
if (sv->turn_count++ >= hilight_time) {
|
|
/* unhighlights by toggling a highlit item back off again */
|
|
if (sv->type == SV_LABEL)
|
|
hilight_label(sv->w);
|
|
else
|
|
hilight_value(sv->w);
|
|
sv->set = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Initialize alternate status ============================================ */
|
|
|
|
/* Return a string for the initial width, so use longest possible value. */
|
|
static const char *
|
|
width_string(sv_index)
|
|
int sv_index;
|
|
{
|
|
switch (sv_index) {
|
|
case F_DUMMY:
|
|
return " ";
|
|
|
|
case F_STR:
|
|
return "018/**";
|
|
case F_DEX:
|
|
case F_CON:
|
|
case F_INT:
|
|
case F_WIS:
|
|
case F_CHA:
|
|
return "088"; /* all but str never get bigger */
|
|
|
|
case F_HUNGER:
|
|
return "Satiated";
|
|
case F_ENCUMBER:
|
|
return "Overloaded";
|
|
|
|
case F_LEV:
|
|
case F_FLY:
|
|
case F_RIDE:
|
|
case F_TRAPPED:
|
|
case F_GRABBED:
|
|
case F_STONE:
|
|
case F_SLIME:
|
|
case F_STRNGL:
|
|
case F_FOODPOIS:
|
|
case F_TERMILL:
|
|
case F_IN_LAVA:
|
|
case F_HELD:
|
|
case F_BLIND:
|
|
case F_DEAF:
|
|
case F_STUN:
|
|
case F_CONF:
|
|
case F_HALLU:
|
|
return shown_stats[sv_index].name;
|
|
|
|
case F_NAME:
|
|
case F_DLEVEL:
|
|
return ""; /* longest possible value not needed for these */
|
|
|
|
case F_HP:
|
|
case F_MAXHP:
|
|
return "9999";
|
|
case F_POWER:
|
|
case F_MAXPOWER:
|
|
return "9999";
|
|
case F_AC:
|
|
return "-127";
|
|
case F_XP_LEVL:
|
|
return "99";
|
|
case F_GOLD:
|
|
/* strongest hero can pick up roughly 30% of this much */
|
|
return "999999"; /* same limit as tty */
|
|
case F_EXP_PTS:
|
|
case F_TIME:
|
|
case F_SCORE:
|
|
return "123456789"; /* a tenth digit will still fit legibly */
|
|
case F_ALIGN:
|
|
return "Neutral";
|
|
}
|
|
impossible("width_string: unknown index %d\n", sv_index);
|
|
return "";
|
|
}
|
|
|
|
static void
|
|
create_widget(parent, sv, sv_index)
|
|
Widget parent;
|
|
struct X_status_value *sv;
|
|
int sv_index;
|
|
{
|
|
Arg args[4];
|
|
Cardinal num_args;
|
|
|
|
switch (sv->type) {
|
|
case SV_VALUE:
|
|
sv->w = create_value(parent, sv->name);
|
|
set_value(sv->w, width_string(sv_index));
|
|
break;
|
|
case SV_LABEL:
|
|
/* Labels get their own buffer. */
|
|
sv->name = (char *) alloc(BUFSZ);
|
|
/* we need to cast away 'const' when assigning a value */
|
|
*(char *) (sv->name) = '\0';
|
|
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
|
|
XtSetArg(args[num_args], XtNinternalHeight, 0); num_args++;
|
|
sv->w = XtCreateManagedWidget((sv_index == F_NAME)
|
|
? "name"
|
|
: "dlevel",
|
|
labelWidgetClass, parent,
|
|
args, num_args);
|
|
break;
|
|
case SV_NAME:
|
|
num_args = 0;
|
|
XtSetArg(args[0], XtNlabel, width_string(sv_index)); num_args++;
|
|
XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
|
|
XtSetArg(args[num_args], XtNinternalHeight, 0); num_args++;
|
|
sv->w = XtCreateManagedWidget(sv->name, labelWidgetClass, parent,
|
|
args, num_args);
|
|
break;
|
|
default:
|
|
panic("create_widget: unknown type %d", sv->type);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get current width of value. width2p is only valid for SV_VALUE types.
|
|
*/
|
|
static void
|
|
get_widths(sv, width1p, width2p)
|
|
struct X_status_value *sv;
|
|
int *width1p, *width2p;
|
|
{
|
|
Arg args[1];
|
|
Dimension width;
|
|
|
|
switch (sv->type) {
|
|
case SV_VALUE:
|
|
*width1p = get_name_width(sv->w);
|
|
*width2p = get_value_width(sv->w);
|
|
break;
|
|
case SV_LABEL:
|
|
case SV_NAME:
|
|
XtSetArg(args[0], XtNwidth, &width);
|
|
XtGetValues(sv->w, args, ONE);
|
|
*width1p = width;
|
|
*width2p = 0;
|
|
break;
|
|
default:
|
|
panic("get_widths: unknown type %d", sv->type);
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_widths(sv, width1, width2)
|
|
struct X_status_value *sv;
|
|
int width1, width2;
|
|
{
|
|
Arg args[1];
|
|
|
|
switch (sv->type) {
|
|
case SV_VALUE:
|
|
set_name_width(sv->w, width1);
|
|
set_value_width(sv->w, width2);
|
|
break;
|
|
case SV_LABEL:
|
|
case SV_NAME:
|
|
XtSetArg(args[0], XtNwidth, (width1 + width2));
|
|
XtSetValues(sv->w, args, ONE);
|
|
break;
|
|
default:
|
|
panic("set_widths: unknown type %d", sv->type);
|
|
}
|
|
}
|
|
|
|
static Widget
|
|
init_column(name, parent, top, left, col_indices, xtrawidth)
|
|
const char *name;
|
|
Widget parent, top, left;
|
|
int *col_indices, xtrawidth;
|
|
{
|
|
Widget form;
|
|
Arg args[4];
|
|
Cardinal num_args;
|
|
int max_width1, width1, max_width2, width2;
|
|
int *ip;
|
|
struct X_status_value *sv;
|
|
|
|
num_args = 0;
|
|
if (top != (Widget) 0) {
|
|
XtSetArg(args[num_args], nhStr(XtNfromVert), top); num_args++;
|
|
}
|
|
if (left != (Widget) 0) {
|
|
XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++;
|
|
}
|
|
/* this was 0 but that resulted in the text being crammed together */
|
|
XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 2); num_args++;
|
|
form = XtCreateManagedWidget(name, formWidgetClass, parent,
|
|
args, num_args);
|
|
|
|
max_width1 = max_width2 = 0;
|
|
for (ip = col_indices; *ip >= 0; ip++) {
|
|
sv = &shown_stats[*ip];
|
|
create_widget(form, sv, *ip); /* will set init width */
|
|
if (ip != col_indices) { /* not first */
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], nhStr(XtNfromVert),
|
|
shown_stats[*(ip - 1)].w); num_args++;
|
|
XtSetValues(sv->w, args, num_args);
|
|
}
|
|
get_widths(sv, &width1, &width2);
|
|
if (width1 > max_width1)
|
|
max_width1 = width1;
|
|
if (width2 > max_width2)
|
|
max_width2 = width2;
|
|
}
|
|
|
|
/* insert some extra spacing between columns */
|
|
max_width1 += xtrawidth;
|
|
|
|
for (ip = col_indices; *ip >= 0; ip++) {
|
|
set_widths(&shown_stats[*ip], max_width1, max_width2);
|
|
}
|
|
|
|
/* There is room behind the end marker for the two widths. */
|
|
*++ip = max_width1;
|
|
*++ip = max_width2;
|
|
|
|
return form;
|
|
}
|
|
|
|
/*
|
|
* These are the orders of the displayed columns. Change to suit. The -1
|
|
* indicates the end of the column. The two numbers after that are used
|
|
* to store widths that are calculated at run-time.
|
|
*
|
|
* 3.7: changed so that all 6 columns have 8 rows, but a few entries
|
|
* are left blank <>. Exp-points, Score, and Time are optional depending
|
|
* on run-time settings; Xp-level is replaced by Hit-Dice (and Exp-points
|
|
* suppressed) when the hero is polymorphed. Title and Dungeon-Level span
|
|
* two columns and might expand to more if 'hitpointbar' is implemented.
|
|
*
|
|
Title ("Plname the Rank") <> <> <> <>
|
|
Dungeon-Branch-and-Level <> Hunger Grabbed Held
|
|
Hit-points Max-HP Strength Encumbrance Petrifying Blind
|
|
Power-points Max-Power Dexterity Trapped Slimed Deaf
|
|
Armor-class Alignment Constitution Levitation Strangled Stunned
|
|
Xp-level [Exp-points] Intelligence Flying Food-Pois Confused
|
|
Gold [Score] Wisdom Riding Term-Ill Hallucinatg
|
|
<> [Time] Charisma <> Sinking <>
|
|
*
|
|
* A seventh column is going to be needed to fit in more conditions.
|
|
*
|
|
* Possible enhancement: if Exp-points and Score are both disabled, move
|
|
* Gold to the Exp-points slot.
|
|
*/
|
|
|
|
/* including F_DUMMY makes the three status condition columns evenly
|
|
spaced with regard to the adjacent characteristics (Str,Dex,&c) column;
|
|
we lose track of the Widget pointer for F_DUMMY, each use clobbering the
|
|
one before, leaving the one from leftover_indices[]; since they're never
|
|
updated, that shouldn't matter */
|
|
static int status_indices[3][11] = {
|
|
{ F_DUMMY, F_HUNGER, F_ENCUMBER, F_TRAPPED,
|
|
F_LEV, F_FLY, F_RIDE, F_DUMMY, -1, 0, 0 },
|
|
{ F_DUMMY, F_GRABBED, F_STONE, F_SLIME, F_STRNGL,
|
|
F_FOODPOIS, F_TERMILL, F_IN_LAVA, -1, 0, 0 },
|
|
{ F_DUMMY, F_HELD, F_BLIND, F_DEAF, F_STUN,
|
|
F_CONF, F_HALLU, F_DUMMY, -1, 0, 0 },
|
|
};
|
|
/* used to fill up the empty space to right of 3rd status condition column */
|
|
static int leftover_indices[] = { F_DUMMY, -1, 0, 0 };
|
|
/* -2: top two rows of these columns are reserved for title and location */
|
|
static int col1_indices[11 - 2] = {
|
|
F_HP, F_POWER, F_AC, F_XP_LEVL, F_GOLD, F_DUMMY, -1, 0, 0
|
|
};
|
|
static int col2_indices[11 - 2] = {
|
|
F_MAXHP, F_MAXPOWER, F_ALIGN, F_EXP_PTS, F_SCORE, F_TIME, -1, 0, 0
|
|
};
|
|
static int characteristics_indices[11 - 2] = {
|
|
F_STR, F_DEX, F_CON, F_INT, F_WIS, F_CHA, -1, 0, 0
|
|
};
|
|
|
|
/*
|
|
* Produce a form that looks like the following:
|
|
*
|
|
* title
|
|
* location
|
|
* col1_indices[0] col2_indices[0] col3_indices[0]
|
|
* col1_indices[1] col2_indices[1] col3_indices[1]
|
|
* ... ... ...
|
|
* col1_indices[5] col2_indices[5] col3_indices[5]
|
|
*
|
|
* The status conditions are managed separately and appear to the right
|
|
* of this form.
|
|
*
|
|
* TODO: widen title field and implement hitpoint bar on it.
|
|
*/
|
|
static Widget
|
|
init_info_form(parent, top, left)
|
|
Widget parent, top, left;
|
|
{
|
|
Widget form, col1, col2;
|
|
struct X_status_value *sv_name, *sv_dlevel;
|
|
Arg args[6];
|
|
Cardinal num_args;
|
|
int total_width, *ip;
|
|
|
|
num_args = 0;
|
|
if (top != (Widget) 0) {
|
|
XtSetArg(args[num_args], nhStr(XtNfromVert), top); num_args++;
|
|
}
|
|
if (left != (Widget) 0) {
|
|
XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++;
|
|
}
|
|
XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 2); num_args++;
|
|
form = XtCreateManagedWidget("status_info", formWidgetClass, parent,
|
|
args, num_args);
|
|
|
|
/* top line/row of form */
|
|
sv_name = &shown_stats[F_NAME]; /* title */
|
|
create_widget(form, sv_name, F_NAME);
|
|
|
|
/* second line/row */
|
|
sv_dlevel = &shown_stats[F_DLEVEL]; /* location */
|
|
create_widget(form, sv_dlevel, F_DLEVEL);
|
|
|
|
num_args = 0;
|
|
XtSetArg(args[num_args], nhStr(XtNfromVert), sv_name->w); num_args++;
|
|
XtSetValues(sv_dlevel->w, args, num_args);
|
|
|
|
/* there are 3 columns beneath but top 2 rows are centered over first 2 */
|
|
col1 = init_column("name_col1", form, sv_dlevel->w, (Widget) 0,
|
|
col1_indices, 0);
|
|
col2 = init_column("name_col2", form, sv_dlevel->w, col1,
|
|
col2_indices, 5);
|
|
(void) init_column("status_characteristics", form, sv_dlevel->w, col2,
|
|
characteristics_indices, 15);
|
|
|
|
/* Add calculated widths. */
|
|
for (ip = col1_indices; *ip >= 0; ip++)
|
|
; /* skip to end */
|
|
total_width = *++ip;
|
|
total_width += *++ip;
|
|
for (ip = col2_indices; *ip >= 0; ip++)
|
|
; /* skip to end */
|
|
total_width += *++ip;
|
|
total_width += *++ip;
|
|
|
|
XtSetArg(args[0], XtNwidth, total_width);
|
|
XtSetValues(sv_name->w, args, ONE);
|
|
XtSetArg(args[0], XtNwidth, total_width);
|
|
XtSetValues(sv_dlevel->w, args, ONE);
|
|
|
|
return form;
|
|
}
|
|
|
|
/* give the three status condition columns the same width */
|
|
static void
|
|
fixup_cond_widths()
|
|
{
|
|
int pass, i, *ip, w1, w2;
|
|
|
|
w1 = w2 = 0;
|
|
for (pass = 1; pass <= 2; ++pass) { /* two passes... */
|
|
for (i = 0; i < 3; i++) { /* three columns */
|
|
for (ip = status_indices[i]; *ip != -1; ++ip) { /* X fields */
|
|
/* pass 1: find -1; pass 2: update field widths, find -1 */
|
|
if (pass == 2)
|
|
set_widths(&shown_stats[*ip], w1, w2);
|
|
}
|
|
/* found -1; the two slots beyond it contain column widths */
|
|
if (pass == 1) { /* pass 1: collect maxima */
|
|
if (ip[1] > w1)
|
|
w1 = ip[1];
|
|
if (ip[2] > w2)
|
|
w2 = ip[2];
|
|
} else { /* pass 2: update column widths with maxima */
|
|
ip[1] = w1;
|
|
ip[2] = w2;
|
|
}
|
|
}
|
|
/* ascetics: expand the maximum width to make cond columns wider */
|
|
if (pass == 1) {
|
|
w1 += 15;
|
|
if (w2 > 0)
|
|
w2 += 15;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create the layout for the fancy status. Return a form widget that
|
|
* contains everything.
|
|
*/
|
|
static Widget
|
|
create_fancy_status(parent, top)
|
|
Widget parent, top;
|
|
{
|
|
Widget form; /* The form that surrounds everything. */
|
|
Widget w;
|
|
Arg args[8];
|
|
Cardinal num_args;
|
|
char buf[32];
|
|
int i;
|
|
|
|
num_args = 0;
|
|
if (top != (Widget) 0) {
|
|
XtSetArg(args[num_args], nhStr(XtNfromVert), top); num_args++;
|
|
}
|
|
XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 2); num_args++;
|
|
XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
|
|
XtSetArg(args[num_args], XtNorientation, XtorientHorizontal); num_args++;
|
|
form = XtCreateManagedWidget("fancy_status", panedWidgetClass, parent,
|
|
args, num_args);
|
|
|
|
w = init_info_form(form, (Widget) 0, (Widget) 0);
|
|
#if 0 /* moved to init_info_form() */
|
|
w = init_column("status_characteristics", form, (Widget) 0, w,
|
|
characteristics_indices, 15);
|
|
#endif
|
|
for (i = 0; i < 3; i++) {
|
|
Sprintf(buf, "status_condition%d", i + 1);
|
|
w = init_column(buf, form, (Widget) 0, w, status_indices[i], 0);
|
|
}
|
|
fixup_cond_widths(); /* make all 3 status_conditionN columns same width */
|
|
w = init_column("status_leftover", form, (Widget) 0, w,
|
|
leftover_indices, 0);
|
|
nhUse(w);
|
|
return form;
|
|
}
|
|
|
|
static void
|
|
destroy_fancy_status(wp)
|
|
struct xwindow *wp;
|
|
{
|
|
int i;
|
|
struct X_status_value *sv;
|
|
|
|
if (!wp->keep_window)
|
|
XtDestroyWidget(wp->w), wp->w = (Widget) 0;
|
|
|
|
for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++)
|
|
if (sv->type == SV_LABEL) {
|
|
free((genericptr_t) sv->name);
|
|
sv->name = 0;
|
|
}
|
|
}
|
|
|
|
/*winstat.c*/
|