Files
nethack/win/Qt4/qt4stat.cpp
Pasi Kallinen ed335dd0a7 Add Qt4 windowport
Originally by Ray Chason for 3.4.3, based on the Qt windowport by
Warwick Allison. The look and feel is mostly the same.

Some improvements over the Qt 3 interface are:

* Panes are resizable
* Full support for IBMgraphics, and walls and corridors are drawn with
  graphical primitives for a continuous appearance no matter what the font
  says
* Lots of irritating glitches fixed
* Menus support proportional fonts correctly

Adding this because the old Qt windowport cannot be compiled on Qt4,
even with Qt3 compatibility stuff.

TODO:
 - background map glyphs
 - status hilites
 - menucolors
2017-10-08 01:15:02 +03:00

541 lines
14 KiB
C++

// Copyright (c) Warwick Allison, 1999.
// Qt4 conversion copyright (c) Ray Chason, 2012-2014.
// NetHack may be freely redistributed. See license for details.
// qt4stat.cpp -- bindings between the Qt 4 interface and the main code
extern "C" {
#include "hack.h"
}
#undef Invisible
#undef Warning
#undef index
#undef msleep
#undef rindex
#undef wizard
#undef yn
#undef min
#undef max
#include <QtGui/QtGui>
#if QT_VERSION >= 0x050000
#include <QtWidgets/QtWidgets>
#endif
#include "qt4stat.h"
#include "qt4stat.moc"
#include "qt4set.h"
#include "qt4str.h"
#include "qt_xpms.h"
extern const char *enc_stat[]; /* from botl.c */
extern const char *hu_stat[]; /* from eat.c */
namespace nethack_qt4 {
NetHackQtStatusWindow::NetHackQtStatusWindow() :
// Notes:
// Alignment needs -2 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.
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,"Experience"),
align(this,"Alignment"),
time(this,"Time"),
score(this,"Score"),
hunger(this,""),
confused(this,"Confused"),
sick_fp(this,"Sick"),
sick_il(this,"Ill"),
blind(this,""),
stunned(this,"Stunned"),
hallu(this,"Hallu"),
encumber(this,""),
hline1(this),
hline2(this),
hline3(this),
first_set(true)
{
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_confused = QPixmap(confused_xpm);
p_sick_fp = QPixmap(sick_fp_xpm);
p_sick_il = QPixmap(sick_il_xpm);
p_blind = QPixmap(blind_xpm);
p_stunned = QPixmap(stunned_xpm);
p_hallu = QPixmap(hallu_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);
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);
confused.setIcon(p_confused);
sick_fp.setIcon(p_sick_fp);
sick_il.setIcon(p_sick_il);
blind.setIcon(p_blind);
stunned.setIcon(p_stunned);
hallu.setIcon(p_hallu);
encumber.setIcon(p_encumber[0]);
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);
#if 1 //RLC
name.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
dlevel.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
QVBoxLayout *vbox = new QVBoxLayout();
vbox->setSpacing(0);
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(&confused);
statbox->addWidget(&sick_fp);
statbox->addWidget(&sick_il);
statbox->addWidget(&blind);
statbox->addWidget(&stunned);
statbox->addWidget(&hallu);
statbox->addWidget(&encumber);
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);
confused.setFont(normal);
sick_fp.setFont(normal);
sick_il.setFont(normal);
blind.setFont(normal);
stunned.setFont(normal);
hallu.setFont(normal);
encumber.setFont(normal);
updateStats();
}
QWidget* NetHackQtStatusWindow::Widget() { return this; }
void NetHackQtStatusWindow::Clear()
{
}
void NetHackQtStatusWindow::Display(bool block)
{
}
void NetHackQtStatusWindow::CursorTo(int,int y)
{
cursy=y;
}
void NetHackQtStatusWindow::PutStr(int attr, const QString& text)
{
// 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;
confused.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;
blind.setGeometry(x,y,iw,lh); x+=iw;
stunned.setGeometry(x,y,iw,lh); x+=iw;
hallu.setGeometry(x,y,iw,lh); x+=iw;
encumber.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.
*/
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();
confused.dissipateHighlight();
sick_fp.dissipateHighlight();
sick_il.dissipateHighlight();
blind.dissipateHighlight();
stunned.dissipateHighlight();
hallu.dissipateHighlight();
encumber.dissipateHighlight();
}
/*
* 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, attributes, alignment, score
*
* Information on the second line:
* dlvl, gold, hp, power, ac, {level & exp or HD **}
* status (hunger, conf, halu, stun, sick, blind), time, encumbrance
*
* [**] HD is shown instead of level and exp if mtimedone is non-zero.
*/
void NetHackQtStatusWindow::updateStats()
{
if (!parentWidget()) return;
QString buf;
const char *text;
if (cursy != 0) return; /* do a complete update when line 0 is done */
if (ACURR(A_STR) > 118) {
buf.sprintf("STR:%d",ACURR(A_STR)-100);
} else if (ACURR(A_STR)==118) {
buf.sprintf("STR:18/**");
} else if(ACURR(A_STR) > 18) {
buf.sprintf("STR:18/%02d",ACURR(A_STR)-18);
} else {
buf.sprintf("STR:%d",ACURR(A_STR));
}
str.setLabel(buf,NetHackQtLabelledIcon::NoNum,ACURR(A_STR));
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.show();
}
if (Confusion) confused.show(); else confused.hide();
if (Sick) {
if (u.usick_type & SICK_VOMITABLE) {
sick_fp.show();
} else {
sick_fp.hide();
}
if (u.usick_type & SICK_NONVOMITABLE) {
sick_il.show();
} else {
sick_il.hide();
}
} else {
sick_fp.hide();
sick_il.hide();
}
if (Blind) {
blind.setLabel("Blind");
blind.show();
} else {
blind.hide();
}
if (Stunned) stunned.show(); else stunned.hide();
if (Hallucination) hallu.show(); else hallu.hide();
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.show();
}
if (u.mtimedone) {
buf = nh_capitalize_words(mons[u.umonnum].mname);
} else {
buf = rank_of(u.ulevel, pl_character[0], ::flags.female);
}
QString buf2;
buf2.sprintf("%s the %s", plname, buf.toLatin1().constData());
name.setLabel(buf2, NetHackQtLabelledIcon::NoNum, u.ulevel);
char buf3[BUFSZ];
if (describe_level(buf3)) {
dlevel.setLabel(buf3,true);
} else {
buf.sprintf("%s, level ", dungeons[u.uz.dnum].dname);
dlevel.setLabel(buf,(long)::depth(&u.uz));
}
gold.setLabel("Au:", money_cnt(invent));
if (u.mtimedone) {
// You're a monster!
buf.sprintf("/%d", u.mhmax);
hp.setLabel("HP:", u.mh > 0 ? u.mh : 0, buf);
level.setLabel("HD:",(long)mons[u.umonnum].mlevel);
} else {
// You're normal.
buf.sprintf("/%d", u.uhpmax);
hp.setLabel("HP:", u.uhp > 0 ? u.uhp : 0, buf);
level.setLabel("Level:",(long)u.ulevel);
}
buf.sprintf("/%d", u.uenmax);
power.setLabel("Pow:", u.uen, buf);
ac.setLabel("AC:",(long)u.uac);
#ifdef EXP_ON_BOTL
if (::flags.showexp) {
exp.setLabel("Exp:",(long)u.uexp);
} else
#endif
{
exp.setLabel("");
}
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";
}
align.setLabel(text);
if (::flags.time) time.setLabel("Time:",(long)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();
align.highlightWhenChanging();
//time.highlightWhenChanging();
score.highlightWhenChanging();
hunger.highlightWhenChanging();
confused.highlightWhenChanging();
sick_fp.highlightWhenChanging();
sick_il.highlightWhenChanging();
blind.highlightWhenChanging();
stunned.highlightWhenChanging();
hallu.highlightWhenChanging();
encumber.highlightWhenChanging();
}
}
/*
* Turn off hilighted status values after a certain amount of turns.
*/
void NetHackQtStatusWindow::checkTurnEvents()
{
}
} // namespace nethack_qt4