* Fix for #4284 -> The menus have the update menu thing emitted when they get triggered. | -> works surprising well https://youtu.be/KOOmhxvHA2c is a demo on a 10000ish card deck * changed my comment to make sense * fix to the issues that @ebbit1q found what caued them idk * Revert "fix to the issues that @ebbit1q found" This reverts commit 20b1ad9f7a675fd3b0d1be7452f71160ce06de71. * actual fix for the issues @ebbit1q found * its dirty but works * fix cards in zoneviews not having a menu * deleted isempty check as it is updated after the check * key binds should work now -> menus updated on zone change/attach/retranslate UI if selected * clangify * remove updateCardMenu from carditem entirely updateCardMenu is done by the player and having it in carditem led to it often being run multiple times, I've opted to instead run it in the player and remove the signal entirely the new logic updates the cardMenu every time a card is set as the activeCard in the game tab additionally a cardmenu can change while selected if the selected card: moves zone, is flipped, or is attached (it receives the unattach action) this is done in the player instead now, checking if the card is the activeCard this however exposes a flaw in the selection management where if you unselect a card the activeCard is set to nullptr, this was an existing bug and causes the action on selected cards to suddenly disappear, even if there are other cards selected! * revert null test of aCardMenu Co-authored-by: ebbit1q <ebbit1q@gmail.com>
446 lines
13 KiB
C++
446 lines
13 KiB
C++
#include "carditem.h"
|
|
|
|
#include "arrowitem.h"
|
|
#include "carddatabase.h"
|
|
#include "carddragitem.h"
|
|
#include "cardzone.h"
|
|
#include "gamescene.h"
|
|
#include "main.h"
|
|
#include "pb/serverinfo_card.pb.h"
|
|
#include "player.h"
|
|
#include "settingscache.h"
|
|
#include "tab_game.h"
|
|
#include "tablezone.h"
|
|
#include "zoneviewzone.h"
|
|
|
|
#include <QApplication>
|
|
#include <QGraphicsSceneMouseEvent>
|
|
#include <QMenu>
|
|
#include <QPainter>
|
|
|
|
CardItem::CardItem(Player *_owner,
|
|
const QString &_name,
|
|
int _cardid,
|
|
bool _revealedCard,
|
|
QGraphicsItem *parent,
|
|
CardZone *_zone)
|
|
: AbstractCardItem(_name, _owner, _cardid, parent), zone(_zone), revealedCard(_revealedCard), attacking(false),
|
|
destroyOnZoneChange(false), doesntUntap(false), dragItem(nullptr), attachedTo(nullptr)
|
|
{
|
|
owner->addCard(this);
|
|
|
|
cardMenu = new QMenu;
|
|
ptMenu = new QMenu;
|
|
moveMenu = new QMenu;
|
|
|
|
retranslateUi();
|
|
}
|
|
|
|
CardItem::~CardItem()
|
|
{
|
|
prepareDelete();
|
|
|
|
if (scene())
|
|
static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
|
|
|
|
delete cardMenu;
|
|
delete ptMenu;
|
|
delete moveMenu;
|
|
|
|
deleteDragItem();
|
|
}
|
|
|
|
void CardItem::prepareDelete()
|
|
{
|
|
if (owner != nullptr) {
|
|
if (owner->getCardMenu() == cardMenu) {
|
|
owner->setCardMenu(nullptr);
|
|
owner->getGame()->setActiveCard(nullptr);
|
|
}
|
|
owner = nullptr;
|
|
}
|
|
|
|
while (!attachedCards.isEmpty()) {
|
|
attachedCards.first()->setZone(nullptr); // so that it won't try to call reorganizeCards()
|
|
attachedCards.first()->setAttachedTo(nullptr);
|
|
}
|
|
|
|
if (attachedTo != nullptr) {
|
|
attachedTo->removeAttachedCard(this);
|
|
attachedTo = nullptr;
|
|
}
|
|
}
|
|
|
|
void CardItem::deleteLater()
|
|
{
|
|
prepareDelete();
|
|
AbstractCardItem::deleteLater();
|
|
}
|
|
|
|
void CardItem::setZone(CardZone *_zone)
|
|
{
|
|
zone = _zone;
|
|
}
|
|
|
|
void CardItem::retranslateUi()
|
|
{
|
|
moveMenu->setTitle(tr("&Move to"));
|
|
ptMenu->setTitle(tr("&Power / toughness"));
|
|
}
|
|
|
|
void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
|
{
|
|
painter->save();
|
|
AbstractCardItem::paint(painter, option, widget);
|
|
|
|
int i = 0;
|
|
QMapIterator<int, int> counterIterator(counters);
|
|
while (counterIterator.hasNext()) {
|
|
counterIterator.next();
|
|
QColor color;
|
|
color.setHsv(counterIterator.key() * 60, 150, 255);
|
|
|
|
paintNumberEllipse(counterIterator.value(), 14, color, i, counters.size(), painter);
|
|
++i;
|
|
}
|
|
|
|
QSizeF translatedSize = getTranslatedSize(painter);
|
|
qreal scaleFactor = translatedSize.width() / boundingRect().width();
|
|
|
|
if (!pt.isEmpty()) {
|
|
painter->save();
|
|
transformPainter(painter, translatedSize, tapAngle);
|
|
|
|
if (!getFaceDown() && info && pt == info->getPowTough()) {
|
|
painter->setPen(Qt::white);
|
|
} else {
|
|
painter->setPen(QColor(255, 150, 0)); // dark orange
|
|
}
|
|
|
|
painter->setBackground(Qt::black);
|
|
painter->setBackgroundMode(Qt::OpaqueMode);
|
|
|
|
painter->drawText(QRectF(4 * scaleFactor, 4 * scaleFactor, translatedSize.width() - 10 * scaleFactor,
|
|
translatedSize.height() - 8 * scaleFactor),
|
|
Qt::AlignRight | Qt::AlignBottom, pt);
|
|
painter->restore();
|
|
}
|
|
|
|
if (!annotation.isEmpty()) {
|
|
painter->save();
|
|
|
|
transformPainter(painter, translatedSize, tapAngle);
|
|
painter->setBackground(Qt::black);
|
|
painter->setBackgroundMode(Qt::OpaqueMode);
|
|
painter->setPen(Qt::white);
|
|
|
|
painter->drawText(QRectF(4 * scaleFactor, 4 * scaleFactor, translatedSize.width() - 8 * scaleFactor,
|
|
translatedSize.height() - 8 * scaleFactor),
|
|
Qt::AlignCenter | Qt::TextWrapAnywhere, annotation);
|
|
painter->restore();
|
|
}
|
|
|
|
if (getBeingPointedAt()) {
|
|
painter->fillRect(boundingRect(), QBrush(QColor(255, 0, 0, 100)));
|
|
}
|
|
|
|
if (doesntUntap) {
|
|
painter->save();
|
|
|
|
painter->setRenderHint(QPainter::Antialiasing, false);
|
|
transformPainter(painter, translatedSize, tapAngle);
|
|
|
|
QPen pen;
|
|
pen.setColor(Qt::magenta);
|
|
const int penWidth = 1;
|
|
pen.setWidth(penWidth);
|
|
painter->setPen(pen);
|
|
painter->drawRect(QRectF(0, 0, translatedSize.width() + penWidth, translatedSize.height() - penWidth));
|
|
|
|
painter->restore();
|
|
}
|
|
|
|
painter->restore();
|
|
}
|
|
|
|
void CardItem::setAttacking(bool _attacking)
|
|
{
|
|
attacking = _attacking;
|
|
update();
|
|
}
|
|
|
|
void CardItem::setCounter(int _id, int _value)
|
|
{
|
|
if (_value)
|
|
counters.insert(_id, _value);
|
|
else
|
|
counters.remove(_id);
|
|
update();
|
|
}
|
|
|
|
void CardItem::setAnnotation(const QString &_annotation)
|
|
{
|
|
annotation = _annotation;
|
|
update();
|
|
}
|
|
|
|
void CardItem::setDoesntUntap(bool _doesntUntap)
|
|
{
|
|
doesntUntap = _doesntUntap;
|
|
update();
|
|
}
|
|
|
|
void CardItem::setPT(const QString &_pt)
|
|
{
|
|
pt = _pt;
|
|
update();
|
|
}
|
|
|
|
void CardItem::setAttachedTo(CardItem *_attachedTo)
|
|
{
|
|
if (attachedTo != nullptr) {
|
|
attachedTo->removeAttachedCard(this);
|
|
}
|
|
|
|
gridPoint.setX(-1);
|
|
attachedTo = _attachedTo;
|
|
if (attachedTo != nullptr) {
|
|
setParentItem(attachedTo->getZone());
|
|
attachedTo->addAttachedCard(this);
|
|
if (zone != attachedTo->getZone()) {
|
|
attachedTo->getZone()->reorganizeCards();
|
|
}
|
|
} else {
|
|
setParentItem(zone);
|
|
}
|
|
|
|
if (zone != nullptr) {
|
|
zone->reorganizeCards();
|
|
}
|
|
}
|
|
|
|
void CardItem::resetState()
|
|
{
|
|
attacking = false;
|
|
facedown = false;
|
|
counters.clear();
|
|
pt.clear();
|
|
annotation.clear();
|
|
attachedTo = 0;
|
|
attachedCards.clear();
|
|
setTapped(false, false);
|
|
setDoesntUntap(false);
|
|
if (scene())
|
|
static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
|
|
update();
|
|
}
|
|
|
|
void CardItem::processCardInfo(const ServerInfo_Card &info)
|
|
{
|
|
counters.clear();
|
|
const int counterListSize = info.counter_list_size();
|
|
for (int i = 0; i < counterListSize; ++i) {
|
|
const ServerInfo_CardCounter &counterInfo = info.counter_list(i);
|
|
counters.insert(counterInfo.id(), counterInfo.value());
|
|
}
|
|
|
|
setId(info.id());
|
|
setName(QString::fromStdString(info.name()));
|
|
setAttacking(info.attacking());
|
|
setFaceDown(info.face_down());
|
|
setPT(QString::fromStdString(info.pt()));
|
|
setAnnotation(QString::fromStdString(info.annotation()));
|
|
setColor(QString::fromStdString(info.color()));
|
|
setTapped(info.tapped());
|
|
setDestroyOnZoneChange(info.destroy_on_zone_change());
|
|
setDoesntUntap(info.doesnt_untap());
|
|
}
|
|
|
|
CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown)
|
|
{
|
|
deleteDragItem();
|
|
dragItem = new CardDragItem(this, _id, _pos, faceDown);
|
|
dragItem->setVisible(false);
|
|
scene()->addItem(dragItem);
|
|
dragItem->updatePosition(_scenePos);
|
|
dragItem->setVisible(true);
|
|
|
|
return dragItem;
|
|
}
|
|
|
|
void CardItem::deleteDragItem()
|
|
{
|
|
if (dragItem) {
|
|
dragItem->deleteLater();
|
|
}
|
|
dragItem = nullptr;
|
|
}
|
|
|
|
void CardItem::drawArrow(const QColor &arrowColor)
|
|
{
|
|
if (static_cast<TabGame *>(owner->parent())->getSpectator())
|
|
return;
|
|
|
|
Player *arrowOwner = static_cast<TabGame *>(owner->parent())->getActiveLocalPlayer();
|
|
ArrowDragItem *arrow = new ArrowDragItem(arrowOwner, this, arrowColor);
|
|
scene()->addItem(arrow);
|
|
arrow->grabMouse();
|
|
|
|
QListIterator<QGraphicsItem *> itemIterator(scene()->selectedItems());
|
|
while (itemIterator.hasNext()) {
|
|
CardItem *c = qgraphicsitem_cast<CardItem *>(itemIterator.next());
|
|
if (!c || (c == this))
|
|
continue;
|
|
if (c->getZone() != zone)
|
|
continue;
|
|
|
|
ArrowDragItem *childArrow = new ArrowDragItem(arrowOwner, c, arrowColor);
|
|
scene()->addItem(childArrow);
|
|
arrow->addChildArrow(childArrow);
|
|
}
|
|
}
|
|
|
|
void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if (event->buttons().testFlag(Qt::RightButton)) {
|
|
if ((event->screenPos() - event->buttonDownScreenPos(Qt::RightButton)).manhattanLength() <
|
|
2 * QApplication::startDragDistance())
|
|
return;
|
|
|
|
QColor arrowColor = Qt::red;
|
|
if (event->modifiers().testFlag(Qt::ControlModifier))
|
|
arrowColor = Qt::yellow;
|
|
else if (event->modifiers().testFlag(Qt::AltModifier))
|
|
arrowColor = Qt::blue;
|
|
else if (event->modifiers().testFlag(Qt::ShiftModifier))
|
|
arrowColor = Qt::green;
|
|
|
|
drawArrow(arrowColor);
|
|
} else if (event->buttons().testFlag(Qt::LeftButton)) {
|
|
if ((event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton)).manhattanLength() <
|
|
2 * QApplication::startDragDistance())
|
|
return;
|
|
if (zone->getIsView()) {
|
|
const ZoneViewZone *view = static_cast<const ZoneViewZone *>(zone);
|
|
if (view->getRevealZone() && !view->getWriteableRevealZone())
|
|
return;
|
|
} else if (!owner->getLocalOrJudge())
|
|
return;
|
|
|
|
bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier);
|
|
|
|
createDragItem(id, event->pos(), event->scenePos(), facedown || forceFaceDown);
|
|
dragItem->grabMouse();
|
|
|
|
QList<QGraphicsItem *> sel = scene()->selectedItems();
|
|
int j = 0;
|
|
for (int i = 0; i < sel.size(); i++) {
|
|
CardItem *c = static_cast<CardItem *>(sel.at(i));
|
|
if ((c == this) || (c->getZone() != zone))
|
|
continue;
|
|
++j;
|
|
QPointF childPos;
|
|
if (zone->getHasCardAttr())
|
|
childPos = c->pos() - pos();
|
|
else
|
|
childPos = QPointF(j * CARD_WIDTH / 2, 0);
|
|
CardDragItem *drag = new CardDragItem(c, c->getId(), childPos, c->getFaceDown() || forceFaceDown, dragItem);
|
|
drag->setPos(dragItem->pos() + childPos);
|
|
scene()->addItem(drag);
|
|
}
|
|
}
|
|
setCursor(Qt::OpenHandCursor);
|
|
}
|
|
|
|
void CardItem::playCard(bool faceDown)
|
|
{
|
|
// Do nothing if the card belongs to another player
|
|
if (!owner->getLocalOrJudge())
|
|
return;
|
|
|
|
TableZone *tz = qobject_cast<TableZone *>(zone);
|
|
if (tz)
|
|
tz->toggleTapped();
|
|
else
|
|
zone->getPlayer()->playCard(this, faceDown, info ? info->getCipt() : false);
|
|
}
|
|
|
|
void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if (event->button() == Qt::RightButton) {
|
|
if (cardMenu != nullptr && !cardMenu->isEmpty() && owner != nullptr) {
|
|
cardMenu->exec(event->screenPos());
|
|
}
|
|
} else if ((event->modifiers() != Qt::AltModifier) && (event->button() == Qt::LeftButton) &&
|
|
(!SettingsCache::instance().getDoubleClickToPlay())) {
|
|
bool hideCard = false;
|
|
if (zone && zone->getIsView()) {
|
|
auto *view = static_cast<ZoneViewZone *>(zone);
|
|
if (view->getRevealZone() && !view->getWriteableRevealZone())
|
|
hideCard = true;
|
|
}
|
|
if (zone && hideCard) {
|
|
zone->removeCard(this);
|
|
} else {
|
|
playCard(event->modifiers().testFlag(Qt::ShiftModifier));
|
|
}
|
|
}
|
|
|
|
if (owner != nullptr) { // cards without owner will be deleted
|
|
setCursor(Qt::OpenHandCursor);
|
|
}
|
|
AbstractCardItem::mouseReleaseEvent(event);
|
|
}
|
|
|
|
void CardItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
|
|
{
|
|
if ((event->modifiers() != Qt::AltModifier) && (SettingsCache::instance().getDoubleClickToPlay()) &&
|
|
(event->buttons() == Qt::LeftButton)) {
|
|
if (revealedCard)
|
|
zone->removeCard(this);
|
|
else
|
|
playCard(event->modifiers().testFlag(Qt::ShiftModifier));
|
|
}
|
|
event->accept();
|
|
}
|
|
|
|
bool CardItem::animationEvent()
|
|
{
|
|
int rotation = ROTATION_DEGREES_PER_FRAME;
|
|
bool animationIncomplete = true;
|
|
if (!tapped)
|
|
rotation *= -1;
|
|
|
|
tapAngle += rotation;
|
|
if (tapped && (tapAngle > 90)) {
|
|
tapAngle = 90;
|
|
animationIncomplete = false;
|
|
}
|
|
if (!tapped && (tapAngle < 0)) {
|
|
tapAngle = 0;
|
|
animationIncomplete = false;
|
|
}
|
|
|
|
setTransform(QTransform()
|
|
.translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF)
|
|
.rotate(tapAngle)
|
|
.translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF));
|
|
setHovered(false);
|
|
update();
|
|
|
|
return animationIncomplete;
|
|
}
|
|
|
|
QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value)
|
|
{
|
|
if ((change == ItemSelectedHasChanged) && owner != nullptr) {
|
|
if (value == true) {
|
|
owner->setCardMenu(cardMenu);
|
|
owner->getGame()->setActiveCard(this);
|
|
} else if (owner->getCardMenu() == cardMenu) {
|
|
owner->setCardMenu(nullptr);
|
|
owner->getGame()->setActiveCard(nullptr);
|
|
}
|
|
}
|
|
return QGraphicsItem::itemChange(change, value);
|
|
}
|