Files
nethack/win/Qt/qt_stat.cpp
PatR cb8baa1d1c Qt status overhaul: add support for 'statuslines'
Condense the Qt status slightly, moving Alignment field from the
Conditons line to the Characteristics line and the Time and Score
fields from their own possibly blank line to the HP,&c,Gold line.

That's for statuslines:2, which is the default.  statuslines:3
restores the previous layout.  I tried to make that become the
default for Qt but it got messy fast and I gave up.

I also tried to make changing 'statuslines' back and forth on the
fly work but failed.  I left the code in as #if DYNAMIC_STATUSLINES
but that isn't defined anywhere.  For the time being at least,
'statuslines' is config file or NETHACKOPTIONS only for Qt, not
changeable via 'O' like for curses and tty.

Change the option description for 'statuslines'.  That depended
upon whether curses was compiled in when it should depend on which
interface is active.  This moves the alternate info to Guidebook.
2020-11-17 05:07:09 -08:00

876 lines
30 KiB
C++

// Copyright (c) Warwick Allison, 1999.
// Qt4 conversion copyright (c) Ray Chason, 2012-2014.
// NetHack may be freely redistributed. See license for details.
// qt_stat.cpp -- status window, upper right portion of the overall window
//
// The Qt status window consists of many lines:
//
// hitpoint bar (when enabled)
// Title (plname the Rank or plname the MonsterSpecies)
// Dungeon location (branch and level)
// separator line
// six icons (special 40x40 tiles, paired with...)
// six characteristic texts ("Str:18/03", "Dex:15", &c)
// separator line
// five status fields without icons (some containing two values:
// HP/HPmax, Energy/Enmax, AC, XpLevel/ExpPoints or HD, [blank], Gold)
// separator line
// optional line with two text fields (Time:1234, Score:89)
// varying number of icons (one or more, each paired with...)
// corresponding text (Alignment plus zero or more status conditions
// including Hunger if not "normal" and encumbrance if not "normal")
//
// The hitpoint bar spans the width of the status window when enabled.
// Title and location are centered.
// The icons and text for the size characteristics are evenly spaced.
// The five main stats are padded with an empty sixth and spaced to
// match the characteristics.
// Time and Score are spaced as if each were three fields wide.
// Icons and texts for alignment and conditions are left justified.
// The separator lines are thin and don't take up much vertical space.
// When enabled, the hitpoint bar bisects the margin above Title,
// increasing the overall status height by 9 pixels; when disabled,
// the status shifts up by those 9 pixels.
// When Time+Score line is empty, it still takes up the vertical space
// that would be used to show those values.
//
// FIXME:
// When hitpoint bar is shown, attempting to resize horizontally won't
// do anything. Toggling it off, then resizing, and back On works.
// (Caused by specifying min-width and max-width constraints in the
// style sheets used to control color, but removing those constraints
// causes the bar display to get screwed up.)
// There are separate icons for Satiated and Hungry, but Weak, Fainting,
// and Fainted all share the Hungry one when they should be different.
//
// TODO:
// If/when status conditions become too wide for the status window, scale
// down their icons and switch their text to a smaller font to match.
// Title and Location are explicitly rendered with a bigger font than
// the rest of status. That takes up more space, which is ok, but it
// also increases the vertical margin in between them by more than is
// necessary. Should squeeze some of that excess blank space out.
// Changed values are highlighted as "gone Up" (green) or "gone Down" (red)
// with NetHackQtLabelledIcon::setLabel() taking an optional boolean
// argument indicating "lower is better" (for AC). That flag should
// have other choices: "changed" (third color with no better or worse
// judgement, for alignment and dungeon location) and "ignore" (don't
// highlight, to suppress the bogus highlighting that currently happens
// when toggling 'showexp' or 'showscore').
// The condensed display (statuslines:2; Alignment with Characteristics
// instead of with Conditions and Time,Score on HP,...,Gold row) has
// vertical padding when the Conditions row is empty, but having the
// first Cond come On or the last one go Off still makes the rest of
// status shift a little as if the padding (same size icon plus empty
// text label) was slightly taller than a regular Cond.
//
extern "C" {
#include "hack.h"
}
#include "qt_pre.h"
#include <QtGui/QtGui>
#if QT_VERSION >= 0x050000
#include <QtWidgets/QtWidgets>
#endif
#include "qt_post.h"
#include "qt_stat.h"
#include "qt_stat.moc"
#include "qt_set.h"
#include "qt_str.h"
#include "qt_xpms.h"
extern const char *enc_stat[]; /* from botl.c */
extern const char *hu_stat[]; /* from eat.c */
extern int qt_compact_mode;
namespace nethack_qt_ {
NetHackQtStatusWindow::NetHackQtStatusWindow() :
/* first three rows: hitpoint bar, title (plname the Rank), location */
hpbar_health(this),
hpbar_injury(this),
name(this,"(name)"),
dlevel(this,"(dlevel)"),
/* next two rows: icon over text label for the six characteristics */
str(this, "Str"),
dex(this, "Dex"),
con(this, "Con"),
intel(this, "Int"),
wis(this, "Wis"),
cha(this, "Cha"),
/* sixth row, text only: some contain two slash-separated values */
hp(this,"Hit Points"),
power(this,"Power"),
ac(this,"Armor Class"),
level(this,"Level"), // Xp level, with "/"+Exp points optionally appended
blank1(this, ""), // used for padding to align columns (was once 'exp')
gold(this,"Gold"), // gold used to be this row's first column, now last
/* seventh row: two optionally displayed values (just text, no icons) */
time(this,"Time"), // if 'time' option On
score(this,"Score"), // if SCORE_ON_BOTL defined and 'showscore' option On
/* last two rows: alignment followed by conditions (icons over text) */
align(this,"Alignment"),
blank2(this, ""), // used to prevent Conditions row from being empty
hunger(this,""),
encumber(this,""),
stoned(this,"Stone"), // major conditions
slimed(this,"Slime"),
strngld(this,"Strngl"),
sick_fp(this,"FoodPois"),
sick_il(this,"TermIll"),
stunned(this,"Stun"), // minor conditions
confused(this,"Conf"),
hallu(this,"Hallu"),
blind(this,"Blind"),
deaf(this,"Deaf"),
lev(this,"Lev"), // 'other' conditions
fly(this,"Fly"),
ride(this,"Ride"),
hline1(this), // separators
hline2(this),
hline3(this),
/* miscellaneous; not display fields */
cursy(0),
first_set(true),
alreadyfullhp(false)
{
if (!qt_compact_mode) {
int w = NetHackQtBind::mainWidget()->width();
setMaximumWidth(w / 2);
}
p_str = QPixmap(str_xpm);
p_str = QPixmap(str_xpm);
p_dex = QPixmap(dex_xpm);
p_con = QPixmap(cns_xpm);
p_int = QPixmap(int_xpm);
p_wis = QPixmap(wis_xpm);
p_cha = QPixmap(cha_xpm);
p_chaotic = QPixmap(chaotic_xpm);
p_neutral = QPixmap(neutral_xpm);
p_lawful = QPixmap(lawful_xpm);
p_blank2 = QPixmap(blank_xpm);
p_satiated = QPixmap(satiated_xpm);
p_hungry = QPixmap(hungry_xpm);
p_encumber[0] = QPixmap(slt_enc_xpm);
p_encumber[1] = QPixmap(mod_enc_xpm);
p_encumber[2] = QPixmap(hvy_enc_xpm);
p_encumber[3] = QPixmap(ext_enc_xpm);
p_encumber[4] = QPixmap(ovr_enc_xpm);
p_stoned = QPixmap(stone_xpm);
p_slimed = QPixmap(slime_xpm);
p_strngld = QPixmap(strngl_xpm);
p_sick_fp = QPixmap(sick_fp_xpm);
p_sick_il = QPixmap(sick_il_xpm);
p_stunned = QPixmap(stunned_xpm);
p_confused = QPixmap(confused_xpm);
p_hallu = QPixmap(hallu_xpm);
p_blind = QPixmap(blind_xpm);
p_deaf = QPixmap(deaf_xpm);
p_lev = QPixmap(lev_xpm);
p_fly = QPixmap(fly_xpm);
p_ride = QPixmap(ride_xpm);
str.setIcon(p_str);
dex.setIcon(p_dex);
con.setIcon(p_con);
intel.setIcon(p_int);
wis.setIcon(p_wis);
cha.setIcon(p_cha);
align.setIcon(p_neutral);
blank2.setIcon(p_blank2); // used for spacing when Conditions row is empty
hunger.setIcon(p_hungry);
encumber.setIcon(p_encumber[0]);
stoned.setIcon(p_stoned);
slimed.setIcon(p_slimed);
strngld.setIcon(p_strngld);
sick_fp.setIcon(p_sick_fp);
sick_il.setIcon(p_sick_il);
stunned.setIcon(p_stunned);
confused.setIcon(p_confused);
hallu.setIcon(p_hallu);
blind.setIcon(p_blind);
deaf.setIcon(p_deaf);
lev.setIcon(p_lev);
fly.setIcon(p_fly);
ride.setIcon(p_ride);
// separator lines
hline1.setFrameStyle(QFrame::HLine|QFrame::Sunken);
hline2.setFrameStyle(QFrame::HLine|QFrame::Sunken);
hline3.setFrameStyle(QFrame::HLine|QFrame::Sunken);
hline1.setLineWidth(1);
hline2.setLineWidth(1);
hline3.setLineWidth(1);
// set up last but shown first (above name) via layout below */
QHBoxLayout *hpbar = InitHitpointBar();
// 'statuslines' takes a value of 2 or 3; we use 3 as a request to put
// Alignment in front of status conditions so that line is never empty
// and to show Time and/or Score on their own line which might be empty
boolean spreadout = (::iflags.wc2_statuslines != 2);
#if 1 //RLC
name.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
dlevel.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
QVBoxLayout *vbox = new QVBoxLayout();
vbox->setSpacing(0);
vbox->addLayout(hpbar); // when 'hitpointbar' is enabled, it comes first
vbox->addWidget(&name);
vbox->addWidget(&dlevel);
vbox->addWidget(&hline1);
QHBoxLayout *charbox = new QHBoxLayout(); // Characteristics
charbox->addWidget(&str);
charbox->addWidget(&dex);
charbox->addWidget(&con);
charbox->addWidget(&intel);
charbox->addWidget(&wis);
charbox->addWidget(&cha);
if (!spreadout) {
// when condensed, include Alignment with Characteristics
charbox->addWidget(&align);
}
vbox->addLayout(charbox);
vbox->addWidget(&hline2);
QHBoxLayout *statbox = new QHBoxLayout(); // core status fields
statbox->addWidget(&hp);
statbox->addWidget(&power);
statbox->addWidget(&ac);
statbox->addWidget(&level);
if (spreadout) {
// when not condensed, put a blank field in front of Gold;
// Time and Score will be shown on their own separate line
statbox->addWidget(&blank1); // empty column #5 of 6
statbox->addWidget(&gold);
} else {
// when condensed, display Time and Score on HP,...,Gold row
#ifndef SCORE_ON_BOTL
statbox->addWidget(&blank1); // empty column #5 of 7
#else
statbox->addWidget(&score); // usually empty column #5
#endif
statbox->addWidget(&gold); // columns 6 and maybe empty 7
statbox->addWidget(&time);
}
vbox->addLayout(statbox);
vbox->addWidget(&hline3); // separtor before Time+Score or Conditions
if (spreadout) {
// when not condensed, put Time and Score on an extra row; since
// they're both optionally displayed, their row might be empty
// TODO? when neither will be shown, set their heights smaller
// and if either gets toggled On, set height back to normal
QHBoxLayout *timebox = new QHBoxLayout();
timebox->addWidget(&time);
timebox->addWidget(&score);
vbox->addLayout(timebox);
}
QHBoxLayout *condbox = new QHBoxLayout(); // Conditions
if (spreadout) {
// when not condensed, include Alignment with Conditions to
// spread things out and also so that their row is never empty
condbox->addWidget(&align);
} else {
// otherwise place a padding widget on this row; it will be
// hidden if any Conditions are shown, or shown (with blank
// icon and empty text) when there aren't any, reserving
// space (the height of the row) for later conditions
condbox->addWidget(&blank2);
}
condbox->addWidget(&hunger);
condbox->addWidget(&encumber);
condbox->addWidget(&stoned);
condbox->addWidget(&slimed);
condbox->addWidget(&strngld);
condbox->addWidget(&sick_fp);
condbox->addWidget(&sick_il);
condbox->addWidget(&stunned);
condbox->addWidget(&confused);
condbox->addWidget(&hallu);
condbox->addWidget(&blind);
condbox->addWidget(&deaf);
condbox->addWidget(&lev);
condbox->addWidget(&fly);
condbox->addWidget(&ride);
condbox->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
vbox->addLayout(condbox);
setLayout(vbox);
#endif
connect(qt_settings, SIGNAL(fontChanged()), this, SLOT(doUpdate()));
doUpdate();
}
void NetHackQtStatusWindow::doUpdate()
{
const QFont& large=qt_settings->largeFont();
name.setFont(large);
dlevel.setFont(large);
const QFont& normal=qt_settings->normalFont();
str.setFont(normal);
dex.setFont(normal);
con.setFont(normal);
intel.setFont(normal);
wis.setFont(normal);
cha.setFont(normal);
hp.setFont(normal);
power.setFont(normal);
ac.setFont(normal);
level.setFont(normal);
gold.setFont(normal);
time.setFont(normal);
score.setFont(normal);
align.setFont(normal);
hunger.setFont(normal);
encumber.setFont(normal);
stoned.setFont(normal);
slimed.setFont(normal);
strngld.setFont(normal);
sick_fp.setFont(normal);
sick_il.setFont(normal);
stunned.setFont(normal);
confused.setFont(normal);
hallu.setFont(normal);
blind.setFont(normal);
deaf.setFont(normal);
lev.setFont(normal);
fly.setFont(normal);
ride.setFont(normal);
updateStats();
}
QWidget* NetHackQtStatusWindow::Widget() { return this; }
void NetHackQtStatusWindow::Clear()
{
}
void NetHackQtStatusWindow::Display(bool block UNUSED)
{
}
void NetHackQtStatusWindow::CursorTo(int,int y)
{
cursy=y;
}
void NetHackQtStatusWindow::PutStr(int attr UNUSED, const QString& text UNUSED)
{
// do a complete update when line 0 is done (as per X11 fancy status)
if (cursy==0) updateStats();
}
#if 0 // RLC
void NetHackQtStatusWindow::resizeEvent(QResizeEvent*)
{
#if 0
const float SP_name=0.13; // <Name> the <Class> (large)
const float SP_dlev=0.13; // Level 3 in The Dungeons of Doom (large)
const float SP_atr1=0.25; // STR DEX CON INT WIS CHA
const float SP_hln1=0.02; // ---
const float SP_atr2=0.09; // Au HP PW AC LVL EXP
const float SP_hln2=0.02; // ---
const float SP_time=0.09; // time score
const float SP_hln3=0.02; // ---
const float SP_stat=0.25; // Alignment, Poisoned, Hungry, Sick, etc.
int h=height();
int x=0,y=0;
int iw; // Width of an item across line
int lh; // Height of a line of values
lh=int(h*SP_name);
name.setGeometry(0,0,width(),lh); y+=lh;
lh=int(h*SP_dlev);
dlevel.setGeometry(0,y,width(),lh); y+=lh;
lh=int(h*SP_hln1);
hline1.setGeometry(0,y,width(),lh); y+=lh;
lh=int(h*SP_atr1);
iw=width()/6;
str.setGeometry(x,y,iw,lh); x+=iw;
dex.setGeometry(x,y,iw,lh); x+=iw;
con.setGeometry(x,y,iw,lh); x+=iw;
intel.setGeometry(x,y,iw,lh); x+=iw;
wis.setGeometry(x,y,iw,lh); x+=iw;
cha.setGeometry(x,y,iw,lh); x+=iw;
x=0; y+=lh;
lh=int(h*SP_hln2);
hline2.setGeometry(0,y,width(),lh); y+=lh;
lh=int(h*SP_atr2);
iw=width()/6;
gold.setGeometry(x,y,iw,lh); x+=iw;
hp.setGeometry(x,y,iw,lh); x+=iw;
power.setGeometry(x,y,iw,lh); x+=iw;
ac.setGeometry(x,y,iw,lh); x+=iw;
level.setGeometry(x,y,iw,lh); x+=iw;
//exp.setGeometry(x,y,iw,lh); x+=iw;
x=0; y+=lh;
lh=int(h*SP_hln3);
hline3.setGeometry(0,y,width(),lh); y+=lh;
lh=int(h*SP_time);
iw=width()/3; x+=iw/2;
time.setGeometry(x,y,iw,lh); x+=iw;
score.setGeometry(x,y,iw,lh); x+=iw;
x=0; y+=lh;
lh=int(h*SP_stat);
iw=width()/9;
align.setGeometry(x,y,iw,lh); x+=iw;
hunger.setGeometry(x,y,iw,lh); x+=iw;
encumber.setGeometry(x,y,iw,lh); x+=iw;
stoned.setGeometry(x,y,iw,lh); x+=iw;
slimed.setGeometry(x,y,iw,lh); x+=iw;
strngld.setGeometry(x,y,iw,lh); x+=iw;
sick_fp.setGeometry(x,y,iw,lh); x+=iw;
sick_il.setGeometry(x,y,iw,lh); x+=iw;
stunned.setGeometry(x,y,iw,lh); x+=iw;
confused.setGeometry(x,y,iw,lh); x+=iw;
hallu.setGeometry(x,y,iw,lh); x+=iw;
blind.setGeometry(x,y,iw,lh); x+=iw;
deaf.setGeometry(x,y,iw,lh); x+=iw;
lev.setGeometry(x,y,iw,lh); x+=iw;
fly.setGeometry(x,y,iw,lh); x+=iw;
ride.setGeometry(x,y,iw,lh); x+=iw;
x=0; y+=lh;
#else
// This is clumsy. But QLayout objects are proving balky.
int row[10];
row[0] = name.sizeHint().height();
row[1] = dlevel.sizeHint().height();
row[2] = h.sizeHint().height();
#endif
}
#endif
/*
* 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. [Actually it isn't used at all.]
*/
void NetHackQtStatusWindow::nullOut()
{
}
void NetHackQtStatusWindow::fadeHighlighting()
{
name.dissipateHighlight();
dlevel.dissipateHighlight();
str.dissipateHighlight();
dex.dissipateHighlight();
con.dissipateHighlight();
intel.dissipateHighlight();
wis.dissipateHighlight();
cha.dissipateHighlight();
gold.dissipateHighlight();
hp.dissipateHighlight();
power.dissipateHighlight();
ac.dissipateHighlight();
level.dissipateHighlight();
align.dissipateHighlight();
time.dissipateHighlight();
score.dissipateHighlight();
hunger.dissipateHighlight();
encumber.dissipateHighlight();
stoned.dissipateHighlight();
slimed.dissipateHighlight();
strngld.dissipateHighlight();
sick_fp.dissipateHighlight();
sick_il.dissipateHighlight();
stunned.dissipateHighlight();
confused.dissipateHighlight();
hallu.dissipateHighlight();
blind.dissipateHighlight();
deaf.dissipateHighlight();
lev.dissipateHighlight();
fly.dissipateHighlight();
ride.dissipateHighlight();
}
// hitpointbar: two panels: left==current health, right==missing max health
QHBoxLayout *NetHackQtStatusWindow::InitHitpointBar()
{
hpbar_health.setFrameStyle(QFrame::NoFrame);
hpbar_health.setMaximumHeight(9);
hpbar_health.setAutoFillBackground(true);
if (!iflags.wc2_hitpointbar)
hpbar_health.hide();
hpbar_injury.setFrameStyle(QFrame::NoFrame);
/* health portion has thickness 9, injury portion just 3 */
hpbar_injury.setMaximumHeight(3);
hpbar_injury.setContentsMargins(0, 3, 0, 3); // left,top,right,bottom
hpbar_injury.setAutoFillBackground(true);
hpbar_injury.hide(); // only shown when hitpointbar is On and uhp < uhpmax
QHBoxLayout *hpbar = new QHBoxLayout;
hpbar->setSpacing(0);
hpbar->setMargin(0);
hpbar->addWidget(&hpbar_health);
hpbar->setAlignment(&hpbar_health, Qt::AlignLeft);
hpbar->addWidget(&hpbar_injury);
hpbar->setAlignment(&hpbar_injury, Qt::AlignRight);
return hpbar; // caller will add our result to vbox layout
}
// when hitpoint bar is enabled, calculate and draw it, otherwise remove it
void NetHackQtStatusWindow::HitpointBar()
{
// a style sheet is used to specify color for otherwise blank labels;
// barcolors[][*]: column [0=left] is current health, [1=right] is injury
static const char
*styleformat = "QLabel { background-color : %s ; color : transparent ;"
" min-width : %d ; max-width %d }",
*barcolors[6][2] = {
{ "black", "black" }, // 100% /* second black never shown */
{ "blue", "darkBlue" }, //75..99
/* gray is darker than darkGray for some reason (at least on OSX);
default green is too dark compared to blue, yellow, orange,
and red so is changed here to green.lighter(150) */
{ "#00c000", "gray" }, //50..74 /* "green"=="#008000" */
{ "yellow", "darkGray" }, //25..49
{ "orange", "lightGray" }, //10..24
{ "red", "white" }, // 0..9
};
/*
* tty and curses use inverse video characters in the left portion
* of the name+rank string to reflect hero's health. We draw a
* separate line above the name+rank field instead. The left side
* of the line indicates current health. The right side is only
* shown when injured and indicates missing amount of maximum health.
*/
if (iflags.wc2_hitpointbar) {
int colorindx, w,
ihp = Upolyd ? u.mh : u.uhp,
ihpmax = Upolyd ? u.mhmax : u.uhpmax;
ihp = std::max(std::min(ihp, ihpmax), 0);
int pct = 100 * ihp / ihpmax,
lox = hline1.x(),
hix = lox + hline1.width() - 1;
QRect geoH = hpbar_health.geometry(),
geoI = hpbar_injury.geometry();
QString styleH, styleI;
if (ihp < ihpmax) {
// health is less than full;
// use red for extreme low health even if the percentage is
// above the usual threshold (which will happen when maximum
// health is very low); do a similar threshold override for
// orange even though it can be distracting for low level hero
colorindx = (pct < 10 || ihp < 5) ? 5 // red | white
: (pct < 25 || ihp < 10 ) ? 4 // orange | lightGray
: (pct < 50) ? 3 // yellow | darkGray*
: (pct < 75) ? 2 // green | gray*
: 1; // blue | darkBlue
int pxl_health = (hix - lox + 1) * ihp / ihpmax;
geoH.setRight(std::min(lox + pxl_health - 1, hix));
hpbar_health.setGeometry(geoH);
w = geoH.right() - geoH.left() + 1; // might yield 0 (ie, if dead)
styleH.sprintf(styleformat, barcolors[colorindx][0], w, w);
hpbar_health.setStyleSheet(styleH);
// when healing, having the old injury-side shown while the new
// health-side expands pushes the injury farther right and it's
// momentarily visible there before it gets recalculated+redrawn
hpbar_injury.hide(); // will re-show below
hpbar_health.show(); // don't need to hide() if/when width is 0
int oldleft = geoI.left();
geoI.setLeft(geoH.right() + 1);
geoI.setRight(hix);
hpbar_injury.setGeometry(geoI);
w = geoI.right() - geoI.left() + 1;
styleI.sprintf(styleformat, barcolors[colorindx][1], w, w);
hpbar_injury.setStyleSheet(styleI);
if (geoI.left() != oldleft)
hpbar_injury.move(geoI.left(), geoI.top());
hpbar_injury.show();
alreadyfullhp = false;
} else if (!alreadyfullhp) { // skip if unchanged
// health is full
colorindx = 0; // black | (not used)
hpbar_injury.hide();
geoI.setLeft(hix); // hix + 1
hpbar_injury.setGeometry(geoI);
geoH.setRight(hix);
hpbar_health.setGeometry(geoH);
w = geoH.right() - geoH.left() + 1;
styleH.sprintf(styleformat, barcolors[colorindx][0], w, w);
hpbar_health.setStyleSheet(styleH);
hpbar_health.show();
alreadyfullhp = true;
}
} else {
// hitpoint bar is disabled
hpbar_health.hide();
hpbar_injury.hide();
alreadyfullhp = false;
}
}
/*
* 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, Str/Dex/&c characteristics, alignment, score
*
* Information on the second line:
* dlvl, gold, hp, power, ac, {level & exp or HD **}
* status (hunger, encumbrance, sick, stun, conf, halu, blind), time
*
* [**] HD is shown instead of level and exp when hero is polymorphed.
*/
void NetHackQtStatusWindow::updateStats()
{
if (!parentWidget()) return;
QString buf;
const char *text;
if (cursy != 0) return; /* do a complete update when line 0 is done */
HitpointBar();
int st = ACURR(A_STR);
if (st > STR18(100)) {
buf.sprintf("Str:%d", st - 100); // 19..25
} else if (st == STR18(100)) {
buf.sprintf("Str:18/**"); // 18/100
} else if (st > 18) {
buf.sprintf("Str:18/%02d", st - 18); // 18/01..18/99
} else {
buf.sprintf("Str:%d", st); // 3..18
}
str.setLabel(buf, NetHackQtLabelledIcon::NoNum, (long) st);
dex.setLabel("Dex:", (long) ACURR(A_DEX));
con.setLabel("Con:", (long) ACURR(A_CON));
intel.setLabel("Int:", (long) ACURR(A_INT));
wis.setLabel("Wis:", (long) ACURR(A_WIS));
cha.setLabel("Cha:", (long) ACURR(A_CHA));
boolean spreadout = (::iflags.wc2_statuslines != 2);
int k = 0; // number of conditions shown
const char* hung=hu_stat[u.uhs];
if (hung[0]==' ') {
hunger.hide();
} else {
hunger.setIcon(u.uhs ? p_hungry : p_satiated);
hunger.setLabel(hung);
hunger.ForceResize();
++k, hunger.show();
}
const char *enc = enc_stat[near_capacity()];
if (enc[0]==' ' || !enc[0]) {
encumber.hide();
} else {
encumber.setIcon(p_encumber[near_capacity() - 1]);
encumber.setLabel(enc);
encumber.ForceResize();
++k, encumber.show();
}
if (Stoned) ++k, stoned.show(); else stoned.hide();
if (Slimed) ++k, slimed.show(); else slimed.hide();
if (Strangled) ++k, strngld.show(); else strngld.hide();
if (Sick) {
/* FoodPois or TermIll or both */
if (u.usick_type & SICK_VOMITABLE) { /* food poisoning */
++k, sick_fp.show();
} else {
sick_fp.hide();
}
if (u.usick_type & SICK_NONVOMITABLE) { /* terminally ill */
++k, sick_il.show();
} else {
sick_il.hide();
}
} else {
sick_fp.hide();
sick_il.hide();
}
if (Stunned) ++k, stunned.show(); else stunned.hide();
if (Confusion) ++k, confused.show(); else confused.hide();
if (Hallucination) ++k, hallu.show(); else hallu.hide();
if (Blind) ++k, blind.show(); else blind.hide();
if (Deaf) ++k, deaf.show(); else deaf.hide();
// flying is blocked when levitating, so Lev and Fly are mutually exclusive
if (Levitation) ++k, lev.show(); else lev.hide();
if (Flying) ++k, fly.show(); else fly.hide();
if (u.usteed) ++k, ride.show(); else ride.hide();
if (Upolyd) {
buf = nh_capitalize_words(mons[u.umonnum].mname);
} else {
buf = rank_of(u.ulevel, g.pl_character[0], ::flags.female);
}
QString buf2;
buf2.sprintf("%s the %s", g.plname, buf.toLatin1().constData());
name.setLabel(buf2, NetHackQtLabelledIcon::NoNum, u.ulevel);
char buf3[BUFSZ];
if (!describe_level(buf3)) {
Sprintf(buf3, "%s, level %d",
g.dungeons[u.uz.dnum].dname, ::depth(&u.uz));
}
// false: always highlight as 'change for the better' regardless of
// new depth compared to old
dlevel.setLabel(buf3, false);
if (Upolyd) {
// You're a monster!
buf.sprintf("/%d", u.mhmax);
hp.setLabel("HP:", std::max((long) u.mh, 0L), buf);
level.setLabel("HD:", (long) mons[u.umonnum].mlevel); // hit dice
// Exp points are not shown when HD is displayed instead of Xp level
} else {
// You're normal.
buf.sprintf("/%d", u.uhpmax);
hp.setLabel("HP:", std::max((long) u.uhp, 0L), buf);
// if Exp points are to be displayed, append them to Xp level;
// up/down highlighting becomes tricky--don't try very hard
if (::flags.showexp) {
buf.sprintf("%ld/%ld", (long) u.ulevel, (long) u.uexp);
// at levels above 20, "Level:NN/nnnnnnnn" doesn't fit so
// shorten "Level" to "Lvl" at that stage;
// at level 30, a few pixels are truncated from the start
// and end of "Lvl:30/nnnnnnnnn" but the result is ledgible
level.setLabel(((u.ulevel <= 20) ? "Level:" : "Lvl:") + buf,
NetHackQtLabelledIcon::NoNum, (long) u.uexp);
} else {
level.setLabel("Level:", (long) u.ulevel);
}
}
buf.sprintf("/%d", u.uenmax);
power.setLabel("Pow:", u.uen, buf);
ac.setLabel("AC:", (long) u.uac);
// label prefix used to be "Au:", tty uses "$:"
gold.setLabel("Gold:", money_cnt(g.invent));
text = NULL;
if (u.ualign.type==A_CHAOTIC) {
align.setIcon(p_chaotic);
text = "Chaotic";
} else if (u.ualign.type==A_NEUTRAL) {
align.setIcon(p_neutral);
text = "Neutral";
} else {
align.setIcon(p_lawful);
text = "Lawful";
}
if (text) {
// false: don't highlight as 'became lower' even if the internal
// numeric value is becoming lower (N -> C, L -> N || C)
align.setLabel(text, false);
// without this, the ankh pixmap shifts from centered to left
// justified relative to the label text for some unknown reason...
align.ForceResize();
}
if (spreadout)
++k; // when not condensed, Alignment is shown on the Conditions row
if (!k) {
blank2.show(); // for vertical spacing: force the row to be non-empty
} else
blank2.hide();
if (::flags.time)
time.setLabel("Time:", (long) g.moves);
else
time.setLabel("");
#ifdef SCORE_ON_BOTL
if (::flags.showscore) {
score.setLabel("Score:", (long) botl_score());
} else
#endif
{
score.setLabel("");
}
if (first_set) {
first_set=false;
name.highlightWhenChanging();
dlevel.highlightWhenChanging();
str.highlightWhenChanging();
dex.highlightWhenChanging();
con.highlightWhenChanging();
intel.highlightWhenChanging();
wis.highlightWhenChanging();
cha.highlightWhenChanging();
gold.highlightWhenChanging();
hp.highlightWhenChanging();
power.highlightWhenChanging();
ac.highlightWhenChanging(); ac.lowIsGood();
level.highlightWhenChanging();
align.highlightWhenChanging();
// don't highlight 'time' because it changes almost continuously
//time.highlightWhenChanging();
score.highlightWhenChanging();
hunger.highlightWhenChanging();
encumber.highlightWhenChanging();
stoned.highlightWhenChanging();
slimed.highlightWhenChanging();
strngld.highlightWhenChanging();
sick_fp.highlightWhenChanging();
sick_il.highlightWhenChanging();
stunned.highlightWhenChanging();
confused.highlightWhenChanging();
hallu.highlightWhenChanging();
blind.highlightWhenChanging();
deaf.highlightWhenChanging();
lev.highlightWhenChanging();
fly.highlightWhenChanging();
ride.highlightWhenChanging();
}
}
/*
* Turn off hilighted status values after a certain amount of turns.
*/
void NetHackQtStatusWindow::checkTurnEvents()
{
}
// clicking on status window runs #attributes (^X)
void NetHackQtStatusWindow::mousePressEvent(QMouseEvent *event UNUSED)
{
QWidget *main = NetHackQtBind::mainWidget();
(static_cast <NetHackQtMainWindow *> (main))->FuncAsCommand(doattributes);
}
} // namespace nethack_qt_