Files
nethack/win/Qt/qt_inv.cpp
2021-12-30 10:56:06 -08:00

334 lines
12 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_inv.cpp -- inventory subset of equipment in use,
// displayed in a rectangular grid of object tiles
//
// Essentially a "paper doll" style display. [grep fodder]
//
// This is at the top center of the main window, between messages and
// status. Qt settings (non-OSX) or Preferences (OSX) has a checkbox to
// show it or hide it, plus the tile size to use (independent of map's
// tile size). Supported tile size is 6..48x6..48 with default of 32x32.
//
// TODO?
// When yn_function() is asking for an inventory letter (not sure whether
// that is currently discernable...), allow clicking on a cell in the
// paper doll grid to return the invlet of the item clicked upon.
//
extern "C" {
#include "hack.h"
}
#undef C
#include "qt_pre.h"
#include <QtGui/QtGui>
#if QT_VERSION >= 0x050000
#include <QtWidgets/QtWidgets>
#endif
#include "qt_post.h"
#include "qt_inv.h"
#include "qt_glyph.h"
#include "qt_main.h"
#include "qt_set.h"
namespace nethack_qt_ {
static struct obj *
find_tool(int tooltyp)
{
struct obj *o;
for (o = g.invent; o; o = o->nobj) {
if ((tooltyp == LEASH && o->otyp == LEASH && o->leashmon)
// OIL_LAMP is used for candles, lamps, lantern, candelabrum too
|| (tooltyp == OIL_LAMP && o->lamplit))
break;
}
return o;
}
NetHackQtInvUsageWindow::NetHackQtInvUsageWindow(QWidget* parent) :
QWidget(parent)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
// needed to enable tool tips
setMouseTracking(true);
// paperdoll is 6x3 but the indices are column oriented: 0..2x0..5
for (int x = 0; x <= 2; ++x)
for (int y = 0; y <= 5; ++y)
tips[x][y] = NULL;
}
NetHackQtInvUsageWindow::~NetHackQtInvUsageWindow()
{
for (int x = 0; x <= 2; ++x)
for (int y = 0; y <= 5; ++y)
if (tips[x][y])
free((void *) tips[x][y]), tips[x][y] = NULL;
}
void NetHackQtInvUsageWindow::drawWorn(QPainter &painter, obj *nhobj,
int x, int y, // cell index, not pixels
const char *alttip, int flags)
{
short int glyph;
glyph_info gi;
int border;
char tipstr[1 + BUFSZ + 1]; // extra room for leading and trailing space
bool rev = (flags == dollReverse),
canbe = (flags != dollUnused);
if (nhobj) {
border = BORDER_DEFAULT;
// don't expect this to happen but check just in case;
// learn_unseen_invent() is normally called when regaining sight
// and sets dknown and maybe bknown, then updates perm_invent (do
// it regardless of ENHANCED_PAPERDOLL for same effect either way)
if (!Blind && (!nhobj->dknown
|| (Role_if(PM_CLERIC) && !nhobj->bknown)))
::learn_unseen_invent();
#ifdef ENHANCED_PAPERDOLL
// color margin around cell containing item whose BUC state is known
if (nhobj->bknown)
border = nhobj->cursed ? BORDER_CURSED
: !nhobj->blessed ? BORDER_UNCURSED
: BORDER_BLESSED;
// border color is used to indicate BUC state; make tip text match
boolean save_implicit_uncursed = ::flags.implicit_uncursed;
::flags.implicit_uncursed = FALSE;
// set up a tool tip describing the item that will be displayed here
Sprintf(tipstr, " %s ", // extra spaces for enhanced readability
// xprname: invlet, space, dash, space, object description
xprname(nhobj, (char *) NULL, nhobj->invlet, FALSE, 0L, 0L));
::flags.implicit_uncursed = save_implicit_uncursed;
// tips are managed with nethack's alloc(); we don't track allocation
// amount; allocated buffers get reused when big enough (usual case
// since paperdoll updates occur more often than equipment changes)
if (tips[x][y] && strlen(tipstr) > strlen(tips[x][y]))
free((void *) tips[x][y]), tips[x][y] = NULL;
if (tips[x][y])
Strcpy(tips[x][y], tipstr); // guaranteed to fit
else
tips[x][y] = dupstr(tipstr);
#endif
glyph = obj_to_glyph(nhobj, rn2_on_display_rng);
} else {
border = NO_BORDER;
#ifdef ENHANCED_PAPERDOLL
// caller usually passes an alternate tool tip for empty cells
size_t altlen = alttip ? 1U + strlen(alttip) + 1U : 0U;
if (tips[x][y] && (!alttip || altlen > strlen(tips[x][y])))
free((void *) tips[x][y]), tips[x][y] = NULL;
if (alttip) {
Sprintf(tipstr, " %s ", alttip);
if (tips[x][y])
Strcpy(tips[x][y], tipstr); // guaranteed to fit
else
tips[x][y] = dupstr(tipstr);
}
#else
nhUse(alttip);
#endif
// an empty slot is shown as floor tile unless it's always empty
glyph = canbe ? cmap_to_glyph(S_room) : GLYPH_UNEXPLORED;
}
map_glyphinfo(0, 0, glyph, 0, &gi); /* this skirts the defined
* interface unfortunately */
qt_settings->glyphs().drawBorderedCell(painter, glyph, gi.gm.tileidx,
x, y, border, rev);
}
// called to update the paper doll inventory subset
void NetHackQtInvUsageWindow::paintEvent(QPaintEvent*)
{
// Paper doll is a 6 row by 3 column grid of worn and wielded
// equipment showing the map tiles that the inventory objects
// would be displayed as if they were on the floor.
//
// 0 1 2 two- dual
// [ old ] normal hander wielding legend
// 0 [x H b] b H q b H q b H q b eyewear H helmet q quiver
// 1 [S " w] S " w W " W X " w S shield " amulet w weapon
// 2 [G C G] G C x G C x G C . G gloves C cloak x alt-weap
// 3 [= A =] = A = = A = = A = = left rg A suit = right ring
// 4 [. U .] l U L l U L l U L l leash U shirt L light
// 5 [. F .] . F . . F . . F . . blank F boots . blank
// W wielded two-handed weapon
// X wielded secondary weapon
//
// 3.7: use a different layout (also different legend for it, above):
// show gloves in only one slot;
// move alternate weapon to former right hand glove slot;
// move blindfold to former alternate weapon slot;
// add quiver to former blindfold slot;
// show secondary weapon in shield slot when two-weapon is active;
// show two-handed primary weapon in both shield and uwep slots;
// add lit lamp/lantern/candle/candelabrum on lower right side;
// add leash-in-use on lower left side
//
// Actually indexed by grid[column][row].
#ifdef ENHANCED_PAPERDOLL
if (iflags.wc_ascii_map)
qt_settings->doll_is_shown = false;
if (!qt_settings->doll_is_shown)
return;
// set glyphs() for the paper doll; might be different size than map's
qt_settings->glyphs().setSize(qt_settings->dollWidth,
qt_settings->dollHeight);
/* for drawWorn()'s use of obj->invlet */
if (!flags.invlet_constant)
reassign();
#endif
QPainter painter;
painter.begin(this);
// String argument is for a tool tip when the object in question is Null.
//
// left column
drawWorn(painter, ublindf, 0, 0, "no eyewear"); // bf|towel|lenses
/* shield slot varies depending upon weapon usage;
no alt tool tip is needed for first two cases because object will
never be Null when the corresponding tests pass */
if (u.twoweap)
drawWorn(painter, uswapwep, 0, 1, NULL); // secondary weapon, in use
else if (uwep && bimanual(uwep)) // show two-handed uwep twice
drawWorn(painter, uwep, 0, 1, NULL, dollReverse); // uwep on left
else
drawWorn(painter, uarms, 0, 1, "no shield");
drawWorn(painter, uarmg, 0, 2, "no gloves");
drawWorn(painter, uleft, 0, 3, "no left ring");
/* light source and leash aren't unique and don't have pointers defined */
drawWorn(painter, find_tool(LEASH), 0, 4, "no leashes in use");
drawWorn(painter, NULL, 0, 5, NULL, dollUnused); // always blank
// middle column; no unused slots
drawWorn(painter, uarmh, 1, 0, "no helmet");
drawWorn(painter, uamul, 1, 1, "no amulet");
drawWorn(painter, uarmc, 1, 2, "no cloak");
drawWorn(painter, uarm, 1, 3, "no suit");
drawWorn(painter, uarmu, 1, 4, "no shirt");
drawWorn(painter, uarmf, 1, 5, "no boots");
// right column
drawWorn(painter, uquiver, 2, 0, "nothing readied for firing"); // quiver
drawWorn(painter, uwep, 2, 1, "no weapon");
/* uswapwep slot varies depending upon dual-wielding state;
shown in shield slot when actively wielded, so uswapwep slot is empty
then and an alternate tool tip is used to explain that emptiness */
if (!u.twoweap)
drawWorn(painter, uswapwep, 2, 2, "no alternate weapon");
else
drawWorn(painter, NULL, 2, 2, "secondary weapon is wielded");
drawWorn(painter, uright, 2, 3, "no right ring");
/* OIL_LAMP matches lit candles, lamps, lantern, and candelabrum
(and might also duplicate Sunsword when it is wielded--hence lit--
depending upon whether another light source precedes it in invent) */
drawWorn(painter, find_tool(OIL_LAMP), 2, 4, "no active light sources");
drawWorn(painter, NULL, 2, 5, NULL, dollUnused); // always blank
painter.end();
#ifdef ENHANCED_PAPERDOLL
// reset glyphs() to the ones being used for the map
qt_settings->glyphs().setSize(qt_settings->tileWidth,
qt_settings->tileHeight);
#endif
}
QSize NetHackQtInvUsageWindow::sizeHint(void) const
{
if (qt_settings) {
int w = 0, h = 1; // one pixel margin at top
// 1+X+1: one pixel border surrounding each tile in the paper doll,
// so +1 left and +1 right, also +1 above and +1 below
#ifdef ENHANCED_PAPERDOLL
if (iflags.wc_ascii_map)
qt_settings->doll_is_shown = false;
if (qt_settings->doll_is_shown) {
w += (1 + qt_settings->dollWidth + 1) * 3;
h += (1 + qt_settings->dollHeight + 1) * 6;
}
#else
if (iflags.wc_tiled_map) {
w += (1 + qt_settings->glyphs().width() + 1) * 3;
h += (1 + qt_settings->glyphs().height() + 1) * 6;
}
#endif
return QSize(w, h);
} else {
return QWidget::sizeHint();
}
}
// ENHANCED_PAPERDOLL - called when a tool tip is triggered by hovering mouse
bool NetHackQtInvUsageWindow::tooltip_event(QHelpEvent *tipevent)
{
#ifdef ENHANCED_PAPERDOLL
if (iflags.wc_ascii_map)
qt_settings->doll_is_shown = false;
if (!qt_settings->doll_is_shown) {
tipevent->ignore();
return false;
}
int wd = qt_settings->dollWidth,
ht = qt_settings->dollHeight;
// inverse of drawBorderedCell();
int yoffset = 1, // tiny extra margin at top
ex = tipevent->pos().x(),
ey = tipevent->pos().y() - yoffset,
// KISS: treat 1-pixel margin around cells as part of enclosed cell
cellx = ex / (wd + 2),
celly = ey / (ht + 2);
const char *tip = (cellx >= 0 && cellx <= 2 && celly >= 0 && celly <= 5)
? tips[cellx][celly] : NULL;
if (tip && *tip) {
QToolTip::showText(tipevent->globalPos(), QString(tip));
} else {
QToolTip::hideText();
tipevent->ignore();
}
#else
nhUse(tipevent);
#endif /* ENHANCED_PAPERDOLL */
return true;
}
// ENHANCED_PAPERDOLL - event handler is necessary to support tool tips
bool NetHackQtInvUsageWindow::event(QEvent *event)
{
#ifdef ENHANCED_PAPERDOLL
if (event->type() == QEvent::ToolTip) {
QHelpEvent *tipevent = static_cast <QHelpEvent *> (event);
return tooltip_event(tipevent);
}
#endif
// with this routine intercepting events, we need to pass along
// paint and mouse-press events to have them handled
return QWidget::event(event);
}
// ENHANCED_PAPERDOLL - clicking on the PaperDoll runs #seeall ('*')
void NetHackQtInvUsageWindow::mousePressEvent(QMouseEvent *event UNUSED)
{
#ifdef ENHANCED_PAPERDOLL
QWidget *main = NetHackQtBind::mainWidget();
(static_cast <NetHackQtMainWindow *> (main))->FuncAsCommand(doprinuse);
#endif
}
} // namespace nethack_qt_