mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-28 00:42:19 -07:00
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 15 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run
Took 32 minutes Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
221 lines
7.9 KiB
C++
221 lines
7.9 KiB
C++
#include "player_target.h"
|
|
|
|
#include "../../client/settings/cache_settings.h"
|
|
#include "../../game/player/player_logic.h"
|
|
#include "../../interface/pixel_map_generator.h"
|
|
#include "../game_scene.h"
|
|
|
|
#include <QApplication>
|
|
#include <QDebug>
|
|
#include <QPainter>
|
|
#include <QPixmapCache>
|
|
#include <QtMath>
|
|
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
|
|
|
PlayerCounter::PlayerCounter(CounterState *state, PlayerLogic *player, QGraphicsItem *parent)
|
|
: AbstractCounter(state, player, false, false, parent)
|
|
{
|
|
}
|
|
|
|
QRectF PlayerCounter::boundingRect() const
|
|
{
|
|
return {0, 0, 50, 30};
|
|
}
|
|
|
|
void PlayerCounter::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
|
{
|
|
const int radius = 15;
|
|
const qreal border = 1.5;
|
|
// The box is drawn with a border-wide stroke straddling the path, so the
|
|
// visible outline spans [inset, inset + border]. Fills that must not cover
|
|
// the outline (e.g. the life-change flash) use a path inset by `border`.
|
|
const auto makePath = [](qreal inset) {
|
|
QPainterPath path(QPointF(50 - inset, inset));
|
|
path.lineTo(radius, inset);
|
|
path.arcTo(inset, inset, 2 * radius, 2 * radius, 90, 90);
|
|
path.lineTo(inset, 30 - inset);
|
|
path.lineTo(50 - inset, 30 - inset);
|
|
path.closeSubpath();
|
|
return path;
|
|
};
|
|
QPainterPath path = makePath(border / 2);
|
|
|
|
QPen pen(QColor(100, 100, 100));
|
|
pen.setWidthF(border);
|
|
painter->setPen(pen);
|
|
painter->setBrush(hovered ? QColor(50, 50, 50, 160) : QColor(0, 0, 0, 160));
|
|
|
|
painter->drawPath(path);
|
|
|
|
QRectF translatedRect = path.controlPointRect();
|
|
QSize translatedSize = translatedRect.size().toSize();
|
|
QFont font("Serif");
|
|
font.setWeight(QFont::Bold);
|
|
font.setPixelSize(qMax(qRound(translatedSize.height() / 1.3), 9));
|
|
painter->setFont(font);
|
|
painter->setPen(Qt::white);
|
|
painter->drawText(translatedRect, Qt::AlignCenter, QString::number(value));
|
|
|
|
// Life-change flash: emerald on gain, red on loss, decaying over a few ticks.
|
|
if (flashAlpha > 0) {
|
|
painter->save();
|
|
QColor flashColor = flashDelta > 0 ? QColor(52, 224, 122) : QColor(239, 68, 68);
|
|
flashColor.setAlphaF(0.45 * flashAlpha);
|
|
painter->setPen(Qt::NoPen);
|
|
painter->setBrush(flashColor);
|
|
painter->setOpacity(0.85);
|
|
painter->drawPath(makePath(border));
|
|
painter->restore();
|
|
}
|
|
}
|
|
|
|
void PlayerCounter::onValueChanged(int oldValue, int newValue)
|
|
{
|
|
flashDelta = newValue - oldValue;
|
|
if (flashDelta == 0) {
|
|
return;
|
|
}
|
|
|
|
if (!SettingsCache::instance().userInterface().getLifeCounterAnimationsEnabled()) {
|
|
flashAlpha = 0.0;
|
|
return;
|
|
}
|
|
|
|
flashAlpha = 1.0;
|
|
flashClock.start();
|
|
if (scene()) {
|
|
static_cast<GameScene *>(scene())->registerAnimationItem(this);
|
|
}
|
|
}
|
|
|
|
bool PlayerCounter::animationEvent()
|
|
{
|
|
flashAlpha = 1.0 - flashClock.elapsed() / flashDurationMs;
|
|
if (flashAlpha <= 0.0) {
|
|
flashAlpha = 0.0;
|
|
return false;
|
|
}
|
|
update();
|
|
return true;
|
|
}
|
|
|
|
PlayerTarget::PlayerTarget(PlayerLogic *_owner, QGraphicsItem *parentItem)
|
|
: ArrowTarget(_owner, parentItem), playerCounter(nullptr)
|
|
{
|
|
setCacheMode(DeviceCoordinateCache);
|
|
|
|
const std::string &bmp = _owner->getPlayerInfo()->getUserInfo()->avatar_bmp();
|
|
if (!fullPixmap.loadFromData((const uchar *)bmp.data(), static_cast<uint>(bmp.size()))) {
|
|
fullPixmap = QPixmap();
|
|
}
|
|
}
|
|
|
|
PlayerTarget::~PlayerTarget()
|
|
{
|
|
// Explicit deletion is necessary in spite of parent/child relationship
|
|
// as we need this object to be alive to receive the destroyed() signal.
|
|
delete playerCounter;
|
|
}
|
|
|
|
QRectF PlayerTarget::boundingRect() const
|
|
{
|
|
return {0, 0, 160, 64};
|
|
}
|
|
|
|
void PlayerTarget::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
|
{
|
|
const ServerInfo_User *const info = owner->getPlayerInfo()->getUserInfo();
|
|
|
|
const qreal border = 2;
|
|
|
|
QRectF avatarBoundingRect = boundingRect().adjusted(border, border, -border, -border);
|
|
QRectF translatedRect = painter->combinedTransform().mapRect(avatarBoundingRect);
|
|
QSize translatedSize = translatedRect.size().toSize();
|
|
QPixmap cachedPixmap;
|
|
const QString cacheKey = "avatar" + QString::number(translatedSize.width()) + "_" +
|
|
QString::number(info->user_level()) + "_" + QString::number(fullPixmap.cacheKey());
|
|
if (!QPixmapCache::find(cacheKey, &cachedPixmap)) {
|
|
cachedPixmap = QPixmap(translatedSize.width(), translatedSize.height());
|
|
|
|
QPainter tempPainter(&cachedPixmap);
|
|
// pow(foo, 0.5) equals to sqrt(foo), but using sqrt(foo) in this context will produce a compile error with
|
|
// MSVC++
|
|
QRadialGradient grad(translatedRect.center(), qPow(translatedSize.width() * translatedSize.width() +
|
|
translatedSize.height() * translatedSize.height(),
|
|
0.5) /
|
|
2);
|
|
grad.setColorAt(1, Qt::black);
|
|
grad.setColorAt(0, QColor(180, 180, 180));
|
|
tempPainter.fillRect(QRectF(0, 0, translatedSize.width(), translatedSize.height()), grad);
|
|
|
|
if (fullPixmap.isNull()) {
|
|
int sideLength = translatedSize.height();
|
|
QPixmap tempPixmap = UserLevelPixmapGenerator::generatePixmap(
|
|
sideLength, UserLevelFlags(info->user_level()), info->pawn_colors(), false,
|
|
QString::fromStdString(info->privlevel()));
|
|
int x = (translatedSize.width() - sideLength) / 2;
|
|
int y = 0;
|
|
tempPainter.drawPixmap(x, y, tempPixmap);
|
|
} else {
|
|
QPixmap tempPixmap = fullPixmap.scaled(translatedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
int x = (translatedSize.width() - tempPixmap.width()) / 2;
|
|
int y = (translatedSize.height() - tempPixmap.height()) / 2;
|
|
tempPainter.drawPixmap(x, y, tempPixmap);
|
|
}
|
|
|
|
QPixmapCache::insert(cacheKey, cachedPixmap);
|
|
}
|
|
|
|
painter->save();
|
|
resetPainterTransform(painter);
|
|
painter->translate((translatedSize.width() - cachedPixmap.width()) / 2.0, 0);
|
|
painter->drawPixmap(translatedRect, cachedPixmap, cachedPixmap.rect());
|
|
painter->restore();
|
|
|
|
QRectF nameRect = QRectF(0, boundingRect().height() - 20, 110, 20);
|
|
painter->fillRect(nameRect, QColor(0, 0, 0, 160));
|
|
QRectF translatedNameRect = painter->combinedTransform().mapRect(nameRect);
|
|
|
|
painter->save();
|
|
resetPainterTransform(painter);
|
|
|
|
QString name = QString::fromStdString(info->name());
|
|
if (name.size() > 13) {
|
|
name = name.mid(0, 10) + "...";
|
|
}
|
|
|
|
QFont font;
|
|
font.setPixelSize(qMax(qRound(translatedNameRect.height() / 1.5), 9));
|
|
painter->setFont(font);
|
|
painter->setPen(Qt::white);
|
|
painter->drawText(translatedNameRect, Qt::AlignVCenter | Qt::AlignLeft, " " + name);
|
|
painter->restore();
|
|
|
|
QPen pen(QColor(100, 100, 100));
|
|
pen.setWidth(border);
|
|
pen.setJoinStyle(Qt::RoundJoin);
|
|
painter->setPen(pen);
|
|
painter->drawRect(boundingRect().adjusted(border / 2, border / 2, -border / 2, -border / 2));
|
|
|
|
if (getBeingPointedAt()) {
|
|
painter->fillRect(boundingRect(), QBrush(QColor(255, 0, 0, 100)));
|
|
}
|
|
}
|
|
|
|
AbstractCounter *PlayerTarget::addCounter(CounterState *state)
|
|
{
|
|
if (playerCounter) {
|
|
disconnect(playerCounter, nullptr, this, nullptr);
|
|
playerCounter->delCounter();
|
|
}
|
|
playerCounter = new PlayerCounter(state, owner, this);
|
|
playerCounter->setPos(boundingRect().width() - playerCounter->boundingRect().width(),
|
|
boundingRect().height() - playerCounter->boundingRect().height());
|
|
connect(playerCounter, &PlayerCounter::destroyed, this, &PlayerTarget::counterDeleted);
|
|
return playerCounter;
|
|
}
|
|
|
|
void PlayerTarget::counterDeleted()
|
|
{
|
|
playerCounter = nullptr;
|
|
}
|