Try to set the initial window sizes to be big enough to show the full welcome line in the message window when the Qt settings (Preferences on OSX) specify Large font (Huge/Medium/Small/Tiny seemed ok but I wasn't systematic about checking them). While at it, I added a long comment about the status window format and noticed a bug with experience formatting there. Again only seemed to matter for Large font but the change to fix ignores font size. Plus add a couple of Qt "issues", one old and one just discovered.
805 lines
25 KiB
C++
805 lines
25 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:
|
|
// Gold, HP/HPmax, Energy/Enmax, AC, XpLevel/ExpPoints or HD)
|
|
// optional line with two text fields (Time:1234, Score:89)
|
|
// separator line
|
|
// 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.
|
|
// The hitpoint bar line and the Time+Score line are omitted when the
|
|
// corresponding items are disabled.
|
|
//
|
|
// FIXME:
|
|
// When hitpoint bar is shown, attempting to resize horizontally won't
|
|
// do anything. Toggling it off, then resizing, and back On works.
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
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() :
|
|
name(this,"(name)"),
|
|
dlevel(this,"(dlevel)"),
|
|
str(this, "Str"),
|
|
dex(this, "Dex"),
|
|
con(this, "Con"),
|
|
intel(this, "Int"),
|
|
wis(this, "Wis"),
|
|
cha(this, "Cha"),
|
|
gold(this,"Gold"),
|
|
hp(this,"Hit Points"),
|
|
power(this,"Power"),
|
|
ac(this,"Armour Class"),
|
|
level(this,"Level"),
|
|
exp(this, "_"), // exp displayed as Xp/Exp but exp widget used for padding
|
|
align(this,"Alignment"),
|
|
time(this,"Time"),
|
|
score(this,"Score"),
|
|
hunger(this,""),
|
|
encumber(this,""),
|
|
stoned(this,"Stone"),
|
|
slimed(this,"Slime"),
|
|
strngld(this,"Strngl"),
|
|
sick_fp(this,"FoodPois"),
|
|
sick_il(this,"TermIll"),
|
|
stunned(this,"Stun"),
|
|
confused(this,"Conf"),
|
|
hallu(this,"Hallu"),
|
|
blind(this,""),
|
|
deaf(this,"Deaf"),
|
|
lev(this,"Lev"),
|
|
fly(this,"Fly"),
|
|
ride(this,"Ride"),
|
|
hpbar_health(this),
|
|
hpbar_injury(this),
|
|
hline1(this),
|
|
hline2(this),
|
|
hline3(this),
|
|
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_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);
|
|
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);
|
|
|
|
QHBoxLayout *hpbar = InitHitpointBar();
|
|
|
|
#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);
|
|
vbox->addWidget(&name);
|
|
vbox->addWidget(&dlevel);
|
|
vbox->addWidget(&hline1);
|
|
QHBoxLayout *atr1box = new QHBoxLayout();
|
|
atr1box->addWidget(&str);
|
|
atr1box->addWidget(&dex);
|
|
atr1box->addWidget(&con);
|
|
atr1box->addWidget(&intel);
|
|
atr1box->addWidget(&wis);
|
|
atr1box->addWidget(&cha);
|
|
vbox->addLayout(atr1box);
|
|
vbox->addWidget(&hline2);
|
|
QHBoxLayout *atr2box = new QHBoxLayout();
|
|
atr2box->addWidget(&gold);
|
|
atr2box->addWidget(&hp);
|
|
atr2box->addWidget(&power);
|
|
atr2box->addWidget(&ac);
|
|
atr2box->addWidget(&level);
|
|
atr2box->addWidget(&exp);
|
|
vbox->addLayout(atr2box);
|
|
vbox->addWidget(&hline3);
|
|
QHBoxLayout *timebox = new QHBoxLayout();
|
|
timebox->addWidget(&time);
|
|
timebox->addWidget(&score);
|
|
vbox->addLayout(timebox);
|
|
QHBoxLayout *statbox = new QHBoxLayout();
|
|
statbox->addWidget(&align);
|
|
statbox->addWidget(&hunger);
|
|
statbox->addWidget(&encumber);
|
|
statbox->addWidget(&stoned);
|
|
statbox->addWidget(&slimed);
|
|
statbox->addWidget(&strngld);
|
|
statbox->addWidget(&sick_fp);
|
|
statbox->addWidget(&sick_il);
|
|
statbox->addWidget(&stunned);
|
|
statbox->addWidget(&confused);
|
|
statbox->addWidget(&hallu);
|
|
statbox->addWidget(&blind);
|
|
statbox->addWidget(&deaf);
|
|
statbox->addWidget(&lev);
|
|
statbox->addWidget(&fly);
|
|
statbox->addWidget(&ride);
|
|
statbox->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
|
|
vbox->addLayout(statbox);
|
|
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);
|
|
gold.setFont(normal);
|
|
hp.setFont(normal);
|
|
power.setFont(normal);
|
|
ac.setFont(normal);
|
|
level.setFont(normal);
|
|
//exp.setFont(normal);
|
|
align.setFont(normal);
|
|
time.setFont(normal);
|
|
score.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();
|
|
//exp.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);
|
|
// style sheet should be doing this but width was sticking at full
|
|
hpbar_health.setMaximumWidth(w);
|
|
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.setMaximumWidth(w); // (see above)
|
|
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));
|
|
|
|
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();
|
|
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();
|
|
encumber.show();
|
|
}
|
|
if (Stoned) stoned.show(); else stoned.hide();
|
|
if (Slimed) slimed.show(); else slimed.hide();
|
|
if (Strangled) strngld.show(); else strngld.hide();
|
|
if (Sick) {
|
|
/* FoodPois or TermIll or both */
|
|
if (u.usick_type & SICK_VOMITABLE) { /* food poisoning */
|
|
sick_fp.show();
|
|
} else {
|
|
sick_fp.hide();
|
|
}
|
|
if (u.usick_type & SICK_NONVOMITABLE) { /* terminally ill */
|
|
sick_il.show();
|
|
} else {
|
|
sick_il.hide();
|
|
}
|
|
} else {
|
|
sick_fp.hide();
|
|
sick_il.hide();
|
|
}
|
|
if (Stunned) stunned.show(); else stunned.hide();
|
|
if (Confusion) confused.show(); else confused.hide();
|
|
if (Hallucination) hallu.show(); else hallu.hide();
|
|
// [pr - Why is blind handled differently from other on/off conditions?]
|
|
if (Blind) {
|
|
blind.setLabel("Blind");
|
|
blind.show();
|
|
} else {
|
|
blind.hide();
|
|
}
|
|
if (Deaf) deaf.show(); else deaf.hide();
|
|
// flying is blocked when levitating, so Lev and Fly are mutually exclusive
|
|
if (Levitation) lev.show(); else lev.hide();
|
|
if (Flying) fly.show(); else fly.hide();
|
|
if (u.usteed) 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);
|
|
|
|
gold.setLabel("Au:", money_cnt(g.invent));
|
|
|
|
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);
|
|
//if (::flags.showexp) {
|
|
// exp.setLabel("Exp:", (long) u.uexp);
|
|
//} else {
|
|
// 'exp' is now only used to pad the line that Xp/Exp is displayed on
|
|
exp.setLabel("");
|
|
//}
|
|
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 (::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();
|
|
//exp.highlightWhenChanging(); -- 'exp' is just padding
|
|
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_
|