From c9853734ad9b3333e8e6b2cc1ede6471c7a62de7 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Sat, 31 Oct 2026 18:44:12 +0100 Subject: [PATCH] [Refactor] Move card identity and card data into CardState (#7425) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lukas BrĂ¼bach --- cockatrice/src/game/board/card_state.cpp | 55 +++++++++++- cockatrice/src/game/board/card_state.h | 72 ++++++++++++++- .../board/abstract_card_item.cpp | 89 +++++++------------ .../game_graphics/board/abstract_card_item.h | 56 ++++++------ .../src/game_graphics/board/card_item.cpp | 15 ++-- .../src/game_graphics/board/card_item.h | 13 +-- 6 files changed, 200 insertions(+), 100 deletions(-) diff --git a/cockatrice/src/game/board/card_state.cpp b/cockatrice/src/game/board/card_state.cpp index 3f879a849..2bbc8429a 100644 --- a/cockatrice/src/game/board/card_state.cpp +++ b/cockatrice/src/game/board/card_state.cpp @@ -1,6 +1,9 @@ #include "card_state.h" #include +#include +#include +#include void CardState::resetState(bool keepAnnotations) { @@ -13,6 +16,56 @@ void CardState::resetState(bool keepAnnotations) attachedTo = nullptr; } +void CardState::setCardRef(const CardRef &_cardRef) +{ + if (cardRef == _cardRef) { + return; + } + + const CardRef oldCardRef = cardRef; + if (exactCard) { + disconnect(exactCard.getCardPtr().data(), nullptr, this, nullptr); + } + cardRef = _cardRef; + + emit cardRefChanged(oldCardRef, cardRef); + refreshCardInfo(); +} + +void CardState::refreshCardInfo() +{ + exactCard = CardDatabaseManager::query()->getCard(cardRef); + + if (!exactCard && !cardRef.name.isEmpty()) { + CardInfo::UiAttributes attributes = {.tableRow = -1}; + auto info = CardInfo::newInstance(cardRef.name, "", true, {}, {}, {}, {}, attributes); + exactCard = ExactCard(info); + } + if (exactCard) { + connect(exactCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &CardState::cardPixmapUpdated); + } + + emit cardInfoChanged(); +} + +void CardState::setTapped(bool _tapped, bool canAnimate) +{ + if (tapped == _tapped) { + return; + } + + tapped = _tapped; + emit tappedChanged(tapped, canAnimate); + emit stateChanged(); +} + +void CardState::setFaceDown(bool _facedown) +{ + facedown = _facedown; + emit facedownChanged(facedown); + emit stateChanged(); +} + void CardState::setZone(CardZoneLogic *_zone) { if (zone == _zone) { @@ -110,4 +163,4 @@ void CardState::setAttachedTo(CardItem *_attachedTo) attachedTo = _attachedTo; emit attachedToChanged(_attachedTo); emit stateChanged(); -} \ No newline at end of file +} diff --git a/cockatrice/src/game/board/card_state.h b/cockatrice/src/game/board/card_state.h index ca7aaf927..af85fea10 100644 --- a/cockatrice/src/game/board/card_state.h +++ b/cockatrice/src/game/board/card_state.h @@ -3,7 +3,10 @@ #include #include +#include #include +#include +#include #include class CardZoneLogic; @@ -14,6 +17,13 @@ class CardState : public QObject Q_OBJECT private: + int id = -1; + CardRef cardRef; + ExactCard exactCard; + bool tapped = false; + bool facedown = false; + QPoint gridPoint; + bool attacking = false; QMap counters; QString annotation; @@ -27,6 +37,12 @@ private: signals: void stateChanged(); + void cardRefChanged(const CardRef &oldCardRef, const CardRef &newCardRef); + void cardInfoChanged(); + void cardPixmapUpdated(); + void tappedChanged(bool newTapped, bool canAnimate); + void facedownChanged(bool newFaceDown); + void attackingChanged(bool newValue); void countersChanged(const QMap &newCounters); void annotationChanged(const QString &newAnnotation); @@ -37,12 +53,66 @@ signals: void zoneChanged(CardState *changedCard, CardZoneLogic *newZone); public: - explicit CardState(QObject *parent, CardZoneLogic *_zone) : QObject(parent), zone(_zone) + explicit CardState(QObject *parent, CardZoneLogic *_zone = nullptr, const CardRef &_cardRef = {}, int _id = -1) + : QObject(parent), id(_id), cardRef(_cardRef), zone(_zone) { } void resetState(bool keepAnnotations); + int getId() const + { + return id; + } + + void setId(int _id) + { + id = _id; + } + + const CardRef &getCardRef() const + { + return cardRef; + } + + void setCardRef(const CardRef &_cardRef); + + ExactCard getCard() const + { + return exactCard; + } + + const CardInfo &getCardInfo() const + { + return exactCard.getInfo(); + } + + void refreshCardInfo(); + + bool getTapped() const + { + return tapped; + } + + void setTapped(bool _tapped, bool canAnimate = false); + + bool getFaceDown() const + { + return facedown; + } + + void setFaceDown(bool _facedown); + + const QPoint &getGridPoint() const + { + return gridPoint; + } + + void setGridPoint(const QPoint &_gridPoint) + { + gridPoint = _gridPoint; + } + CardZoneLogic *getZone() const { return zone; diff --git a/cockatrice/src/game_graphics/board/abstract_card_item.cpp b/cockatrice/src/game_graphics/board/abstract_card_item.cpp index cc121cfb2..3bc0e1156 100644 --- a/cockatrice/src/game_graphics/board/abstract_card_item.cpp +++ b/cockatrice/src/game_graphics/board/abstract_card_item.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -43,13 +42,19 @@ class QSizeF; AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef, PlayerLogic *_owner, int _id) - : ArrowTarget(_owner, parent), id(_id), cardRef(cardRef), tapped(false), facedown(false), tapAngle(0), - bgColor(Qt::transparent), isHovered(false), realZValue(0), pendingSelectionCollapse(false) + : ArrowTarget(_owner, parent), state(new CardState(this, nullptr, cardRef, _id)), tapAngle(0), + bgColor(Qt::transparent), isHovered(false), realZValue(0) { setCursor(Qt::OpenHandCursor); setFlag(ItemIsSelectable); setCacheMode(DeviceCoordinateCache); + connect(state, &CardState::cardPixmapUpdated, this, &AbstractCardItem::pixmapUpdated); + connect(state, &CardState::cardInfoChanged, this, &AbstractCardItem::onCardInfoChanged); + connect(state, &CardState::cardRefChanged, this, &AbstractCardItem::onCardRefChanged); + connect(state, &CardState::tappedChanged, this, &AbstractCardItem::onTappedChanged); + connect(state, &CardState::facedownChanged, this, [this] { update(); }); + connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::displayCardNamesChanged, this, [this] { update(); }); connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this, @@ -67,7 +72,7 @@ AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef AbstractCardItem::~AbstractCardItem() { - emit deleteCardInfoPopup(cardRef.name); + emit deleteCardInfoPopup(state->getCardRef().name); } QRectF AbstractCardItem::boundingRect() const @@ -90,30 +95,29 @@ void AbstractCardItem::pixmapUpdated() emit sigPixmapUpdated(); } -void AbstractCardItem::refreshCardInfo() +void AbstractCardItem::onCardInfoChanged() { - exactCard = CardDatabaseManager::query()->getCard(cardRef); - - if (!exactCard && !cardRef.name.isEmpty()) { - CardInfo::UiAttributes attributes = {.tableRow = -1}; - auto info = CardInfo::newInstance(cardRef.name, "", true, {}, {}, {}, {}, attributes); - exactCard = ExactCard(info); - } - if (exactCard) { - connect(exactCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &AbstractCardItem::pixmapUpdated); - } - cacheBgColor(); update(); } +void AbstractCardItem::onCardRefChanged(const CardRef &oldCardRef, const CardRef & /*newCardRef*/) +{ + emit deleteCardInfoPopup(oldCardRef.name); +} + +void AbstractCardItem::refreshCardInfo() +{ + state->refreshCardInfo(); +} + /** * Convenience method to get the CardInfo of the exactCard * @return A const reference to the CardInfo, or an empty CardInfo if card was null */ const CardInfo &AbstractCardItem::getCardInfo() const { - return exactCard.getInfo(); + return state->getCardInfo(); } void AbstractCardItem::setRealZValue(qreal _zValue) @@ -158,13 +162,13 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS QPixmap translatedPixmap; bool paintImage = true; - if (facedown || cardRef.name.isEmpty()) { + if (state->getFaceDown() || state->getCardRef().name.isEmpty()) { // never reveal card color, always paint the card back CardPictureLoader::getCardBackPixmap(translatedPixmap, translatedSize.toSize()); } else { // don't even spend time trying to load the picture if our size is too small if (translatedSize.width() > 10) { - CardPictureLoader::getPixmap(translatedPixmap, exactCard, translatedSize.toSize()); + CardPictureLoader::getPixmap(translatedPixmap, state->getCard(), translatedSize.toSize()); if (translatedPixmap.isNull()) { paintImage = false; } @@ -185,19 +189,20 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS painter->drawPath(shape()); } - if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() || facedown) { + if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() || + state->getFaceDown()) { painter->save(); transformPainter(painter, translatedSize, angle); painter->setPen(Qt::white); painter->setBackground(Qt::black); painter->setBackgroundMode(Qt::OpaqueMode); QString nameStr; - if (facedown) { - nameStr = "# " + QString::number(id); + if (state->getFaceDown()) { + nameStr = "# " + QString::number(state->getId()); } else { QString prefix = ""; if (SettingsCache::instance().debug().getShowCardId()) { - prefix = "#" + QString::number(id) + " "; + prefix = "#" + QString::number(state->getId()) + " "; } nameStr = prefix + CardLocalization::displayName(getCardInfo()); } @@ -235,21 +240,6 @@ void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * painter->restore(); } -void AbstractCardItem::setCardRef(const CardRef &_cardRef) -{ - if (cardRef == _cardRef) { - return; - } - - emit deleteCardInfoPopup(cardRef.name); - if (exactCard) { - disconnect(exactCard.getCardPtr().data(), nullptr, this, nullptr); - } - cardRef = _cardRef; - - refreshCardInfo(); -} - void AbstractCardItem::setHovered(bool _hovered) { if (isHovered == _hovered) { @@ -284,7 +274,7 @@ void AbstractCardItem::cacheBgColor() { QChar colorChar; if (color.isEmpty()) { - colorChar = exactCard.getInfo().getColorChar(); + colorChar = state->getCardInfo().getColorChar(); } else { colorChar = color.at(0); } @@ -314,17 +304,12 @@ void AbstractCardItem::cacheBgColor() } } -void AbstractCardItem::setTapped(bool _tapped, bool canAnimate) +void AbstractCardItem::onTappedChanged(bool newTapped, bool canAnimate) { - if (tapped == _tapped) { - return; - } - - tapped = _tapped; if (SettingsCache::instance().cardsDisplay().getTapAnimation() && canAnimate) { static_cast(scene())->registerAnimationItem(this); } else { - tapAngle = tapped ? 90 : 0; + tapAngle = newTapped ? 90 : 0; setTransform(QTransform() .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F) .rotate(tapAngle) @@ -338,12 +323,6 @@ bool AbstractCardItem::animationEvent() return false; } -void AbstractCardItem::setFaceDown(bool _facedown) -{ - facedown = _facedown; - update(); -} - void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { cancelPendingSelectionCollapse(); @@ -357,7 +336,7 @@ void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event) } } if ((event->modifiers() & Qt::AltModifier) && event->button() == Qt::LeftButton) { - emit cardShiftClicked(cardRef.name); + emit cardShiftClicked(state->getCardRef().name); } else if ((event->modifiers() & Qt::ControlModifier)) { setSelected(!isSelected()); } else if (!isSelected()) { @@ -371,7 +350,7 @@ void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event) if (event->button() == Qt::LeftButton) { setCursor(Qt::ClosedHandCursor); } else if (event->button() == Qt::MiddleButton) { - emit showCardInfoPopup(event->screenPos(), cardRef); + emit showCardInfoPopup(event->screenPos(), state->getCardRef()); } event->accept(); } @@ -405,7 +384,7 @@ void AbstractCardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) } if (event->button() == Qt::MiddleButton) { - emit deleteCardInfoPopup(cardRef.name); + emit deleteCardInfoPopup(state->getCardRef().name); } // This function ensures the parent function doesn't mess around with our selection. diff --git a/cockatrice/src/game_graphics/board/abstract_card_item.h b/cockatrice/src/game_graphics/board/abstract_card_item.h index e2b150ce8..709265a2a 100644 --- a/cockatrice/src/game_graphics/board/abstract_card_item.h +++ b/cockatrice/src/game_graphics/board/abstract_card_item.h @@ -7,19 +7,11 @@ #ifndef ABSTRACTCARDITEM_H #define ABSTRACTCARDITEM_H +#include "../../game/board/card_state.h" #include "../animated_item.h" #include "arrow_target.h" #include "graphics_item_type.h" -#include -#include -#include -#include -#include -#include -#include -#include - class PlayerLogic; class CardInfo; class QPainter; @@ -29,11 +21,7 @@ class AbstractCardItem : public ArrowTarget, public IAnimatedItem { Q_OBJECT protected: - ExactCard exactCard; - int id; - CardRef cardRef; - bool tapped; - bool facedown; + CardState *state; int tapAngle; QString color; QColor bgColor; @@ -45,6 +33,9 @@ private: int selectionCollapseGeneration = 0; ///< Guards the deferred collapse timer against stale firings private slots: void pixmapUpdated(); + void onCardInfoChanged(); + void onCardRefChanged(const CardRef &oldCardRef, const CardRef &newCardRef); + void onTappedChanged(bool newTapped, bool canAnimate); public slots: void refreshCardInfo(); @@ -80,31 +71,38 @@ public: QSizeF getTranslatedSize(QPainter *painter) const; void paintPicture(QPainter *painter, const QSizeF &translatedSize, int angle); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + [[nodiscard]] CardState *getState() const + { + return state; + } ExactCard getCard() const { - return exactCard; + return state->getCard(); } const CardInfo &getCardInfo() const; int getId() const { - return id; + return state->getId(); } void setId(int _id) { - id = _id; + state->setId(_id); } QString getName() const { - return cardRef.name; + return state->getCardRef().name; } QString getProviderId() const { - return cardRef.providerId; + return state->getCardRef().providerId; + } + void setCardRef(const CardRef &_cardRef) + { + state->setCardRef(_cardRef); } - void setCardRef(const CardRef &_cardRef); CardRef getCardRef() const { - return cardRef; + return state->getCardRef(); } qreal getRealZValue() const { @@ -123,20 +121,26 @@ public: void setColor(const QString &_color); bool getTapped() const { - return tapped; + return state->getTapped(); + } + void setTapped(bool _tapped, bool canAnimate = false) + { + state->setTapped(_tapped, canAnimate); } - void setTapped(bool _tapped, bool canAnimate = false); bool getFaceDown() const { - return facedown; + return state->getFaceDown(); + } + void setFaceDown(bool _facedown) + { + state->setFaceDown(_facedown); } - void setFaceDown(bool _facedown); void processHoverEvent(); /** @brief Drop a pending collapse and disarm any deferred collapse timer. */ void cancelPendingSelectionCollapse(); void deleteCardInfoPopup() { - emit deleteCardInfoPopup(cardRef.name); + emit deleteCardInfoPopup(state->getCardRef().name); } /** @brief Default: no per-tick animation. Subclasses override to animate. */ diff --git a/cockatrice/src/game_graphics/board/card_item.cpp b/cockatrice/src/game_graphics/board/card_item.cpp index eae7cee18..6c91773fb 100644 --- a/cockatrice/src/game_graphics/board/card_item.cpp +++ b/cockatrice/src/game_graphics/board/card_item.cpp @@ -53,8 +53,9 @@ CardItem::CardItem(PlayerLogic *_owner, const CardRef &cardRef, int _cardid, CardZoneLogic *_zone) - : AbstractCardItem(parent, cardRef, _owner, _cardid), state(new CardState(this, _zone)), dragItem(nullptr) + : AbstractCardItem(parent, cardRef, _owner, _cardid), dragItem(nullptr) { + state->setZone(_zone); owner->addCard(this); connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, this, [this](int counterId) { @@ -128,7 +129,7 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, painter->save(); transformPainter(painter, translatedSize, tapAngle); - if (!getFaceDown() && state->getPT() == exactCard.getInfo().getPowTough()) { + if (!getFaceDown() && state->getPT() == state->getCardInfo().getPowTough()) { painter->setPen(Qt::white); } else { painter->setPen(QColor(255, 150, 0)); // dark orange @@ -214,7 +215,7 @@ void CardItem::setAttachedTo(CardItem *_attachedTo) state->getAttachedTo()->removeAttachedCard(this); } - gridPoint.setX(-1); + state->setGridPoint(QPoint(-1, state->getGridPoint().y())); state->setAttachedTo(_attachedTo); if (state->getAttachedTo() != nullptr) { // If the zone is being torn down, it might already be null by the time a card tries to un-attach all its @@ -391,7 +392,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) // Use the buttonDownPos to align the hot spot with the position when // the user originally clicked - createDragItem(id, event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown); + createDragItem(state->getId(), event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown); dragItem->grabMouse(); int childIndex = 0; @@ -538,16 +539,16 @@ bool CardItem::animationEvent() { int rotation = ROTATION_DEGREES_PER_FRAME; bool animationIncomplete = true; - if (!tapped) { + if (!state->getTapped()) { rotation *= -1; } tapAngle += rotation; - if (tapped && (tapAngle > 90)) { + if (state->getTapped() && (tapAngle > 90)) { tapAngle = 90; animationIncomplete = false; } - if (!tapped && (tapAngle < 0)) { + if (!state->getTapped() && (tapAngle < 0)) { tapAngle = 0; animationIncomplete = false; } diff --git a/cockatrice/src/game_graphics/board/card_item.h b/cockatrice/src/game_graphics/board/card_item.h index 0737d7f9c..841061bef 100644 --- a/cockatrice/src/game_graphics/board/card_item.h +++ b/cockatrice/src/game_graphics/board/card_item.h @@ -33,9 +33,6 @@ class CardItem : public AbstractCardItem { Q_OBJECT private: - CardState *state; - - QPoint gridPoint; CardDragItem *dragItem; QList attachedCards; @@ -60,10 +57,6 @@ public: CardZoneLogic *_zone = nullptr); void retranslateUi(); - [[nodiscard]] CardState *getState() const - { - return state; - } [[nodiscard]] CardZoneLogic *getZone() const { return state->getZone(); @@ -72,15 +65,15 @@ public: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; [[nodiscard]] QPoint getGridPoint() const { - return gridPoint; + return state->getGridPoint(); } void setGridPoint(const QPoint &_gridPoint) { - gridPoint = _gridPoint; + state->setGridPoint(_gridPoint); } [[nodiscard]] QPoint getGridPos() const { - return gridPoint; + return state->getGridPoint(); } [[nodiscard]] PlayerLogic *getOwner() const {