You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 lines
4.6 KiB

#include <vector>
#include <string>
#include <unordered_set>
#include "inv.h"
#include "gmenu.h"
#include "cursor.h"
#include "common.h"
#include "control.h"
#include "itemlabels.h"
#include "utils/language.h"
#include "engine/render/cel_render.hpp"
namespace devilution {
namespace {
struct itemLabel {
int id, width;
Point pos;
std::string text;
};
std::vector<itemLabel> labelQueue;
bool altPressed = false;
bool isLabelHighlighted = false;
std::array<std::optional<int>, ITEMTYPES> labelCenterOffsets;
bool invertHighlightToggle = false;
const int borderX = 4; // minimal horizontal space between labels
const int borderY = 2; // minimal vertical space between labels
const int marginX = 2; // horizontal margins between text and edges of the label
const int marginY = 1; // vertical margins between text and edges of the label
const int height = 11 + marginY * 2; // going above 13 scatters labels of items that are next to each other
} // namespace
void ToggleItemLabelHighlight()
{
invertHighlightToggle = !invertHighlightToggle;
}
void AltPressed(bool pressed)
{
altPressed = pressed;
}
bool IsItemLabelHighlighted()
{
return isLabelHighlighted;
}
bool IsHighlightingLabelsEnabled()
{
return altPressed != invertHighlightToggle;
}
void AddItemToLabelQueue(int id, int x, int y)
{
if (!IsHighlightingLabelsEnabled())
return;
ItemStruct *it = &items[id];
const char *textOnGround;
if (it->_itype == ITYPE_GOLD) {
std::sprintf(tempstr, _("%i gold"), it->_ivalue);
textOnGround = tempstr;
} else {
textOnGround = it->_iIdentified ? it->_iIName : it->_iName;
}
int nameWidth = GetLineWidth(textOnGround);
nameWidth += marginX * 2;
int index = ItemCAnimTbl[it->_iCurs];
if (!labelCenterOffsets[index]) {
std::pair<int, int> itemBounds = MeasureSolidHorizontalBounds(*it->_iAnimData, it->_iAnimFrame);
labelCenterOffsets[index].emplace((itemBounds.first + itemBounds.second) / 2);
}
x += *labelCenterOffsets[index];
y -= TILE_HEIGHT;
if (!zoomflag) {
x *= 2;
y *= 2;
}
x -= nameWidth / 2;
labelQueue.push_back(itemLabel { id, nameWidth, { x, y }, textOnGround });
}
bool IsMouseOverGameArea()
{
if ((invflag || sbookflag) && MouseX > RIGHT_PANEL && MouseY <= SPANEL_HEIGHT)
return false;
if ((chrflag || questlog) && MouseX < SPANEL_WIDTH && MouseY <= SPANEL_HEIGHT)
return false;
if (MouseY >= PANEL_TOP && MouseX >= PANEL_LEFT && MouseX <= PANEL_LEFT + PANEL_WIDTH)
return false;
return true;
}
void FillRect(const CelOutputBuffer &out, int x, int y, int width, int height, Uint8 col)
{
for (int j = 0; j < height; j++) {
DrawHorizontalLine(out, { x, y + j }, width, col);
}
}
void DrawItemNameLabels(const CelOutputBuffer &out)
{
isLabelHighlighted = false;
for (unsigned int i = 0; i < labelQueue.size(); ++i) {
std::unordered_set<int> backtrace;
bool canShow;
do {
canShow = true;
for (unsigned int j = 0; j < i; ++j) {
itemLabel &a = labelQueue[i], &b = labelQueue[j];
if (abs(b.pos.y - a.pos.y) < height + borderY) {
int widthA = a.width + borderX + marginX * 2;
int widthB = b.width + borderX + marginX * 2;
int newpos = b.pos.x;
if (b.pos.x >= a.pos.x && b.pos.x - a.pos.x < widthA) {
newpos -= widthA;
if (backtrace.find(newpos) != backtrace.end())
newpos = b.pos.x + widthB;
} else if (b.pos.x < a.pos.x && a.pos.x - b.pos.x < widthB) {
newpos += widthB;
if (backtrace.find(newpos) != backtrace.end())
newpos = b.pos.x - widthA;
} else
continue;
canShow = false;
a.pos.x = newpos;
backtrace.insert(newpos);
}
}
} while (!canShow);
}
for (const itemLabel &label : labelQueue) {
ItemStruct &itm = items[label.id];
if (MouseX >= label.pos.x && MouseX < label.pos.x + label.width && MouseY >= label.pos.y - height + marginY && MouseY < label.pos.y + marginY) {
if (!gmenu_is_active() && PauseMode == 0 && !deathflag && IsMouseOverGameArea()) {
isLabelHighlighted = true;
cursmx = itm.position.x;
cursmy = itm.position.y;
pcursitem = label.id;
}
}
if (pcursitem == label.id)
FillRect(out, label.pos.x, label.pos.y - height + marginY, label.width, height, PAL8_BLUE + 6);
else
DrawHalfTransparentRectTo(out, label.pos.x, label.pos.y - height + marginY, label.width, height);
DrawString(out, label.text.c_str(), { label.pos.x + marginX, label.pos.y, label.width, height }, itm.getTextColor());
}
labelQueue.clear();
}
} // namespace devilution