From 9fa1e3be55f0a077022194521f7fa2a68a7869d9 Mon Sep 17 00:00:00 2001 From: Bruno Alexandre Rosa <1791393+brunoalr@users.noreply.github.com> Date: Mon, 2 Mar 2026 23:37:19 -0300 Subject: [PATCH 01/28] fix build --- cockatrice/src/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index 02b06147e..5970d0800 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -238,7 +238,6 @@ int main(int argc, char *argv[]) } rng = new RNG_SFMT; - const auto bla = 42; themeManager = new ThemeManager; soundEngine = new SoundEngine; From 2828854d3203b0b85159309596c997820b34cb10 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Tue, 3 Mar 2026 14:17:35 -0800 Subject: [PATCH 02/28] [Server] Support face-down cards in all public zones (#6539) * [Server] Support face-down cards in all public zones * add null check * Check using zone names instead * add comment --- .../remote/game/server_abstract_player.cpp | 34 +++++++++++++++++-- .../protocol/pb/command_move_card.proto | 4 ++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp index b08495bad..00561bef2 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp @@ -198,6 +198,35 @@ shouldDestroyOnMove(const Server_Card *card, const Server_CardZone *startZone, c return true; } +/** + * @brief Determines whether the moved card should be face-down + */ +static bool +shouldBeFaceDown(const MoveCardStruct &cardStruct, const Server_CardZone *startZone, const Server_CardZone *targetZone) +{ + if (!targetZone) { + return false; + } + + // being face-down only makes sense for public zones + if (targetZone->getType() != ServerInfo_Zone::PublicZone) { + return false; + } + + // face-down property in proto takes precedence + if (cardStruct.cardToMove->has_face_down()) { + return cardStruct.cardToMove->face_down(); + } + + // Default to keep face-down the same if zone didn't change. + // Compare using zone names because face-down is maintained when changing controllers. + if (startZone && startZone->getName() == targetZone->getName()) { + return cardStruct.card->getFaceDown(); + } + + return false; +} + Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges, Server_CardZone *startzone, const QList &_cards, @@ -257,10 +286,7 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges, for (auto cardStruct : cardsToMove) { Server_Card *card = cardStruct.card; - const CardToMove *thisCardProperties = cardStruct.cardToMove; int originalPosition = cardStruct.position; - bool faceDown = targetzone->hasCoords() && - (thisCardProperties->has_face_down() ? thisCardProperties->face_down() : card->getFaceDown()); bool sourceBeingLookedAt; int position = startzone->removeCard(card, sourceBeingLookedAt); @@ -315,6 +341,8 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges, ++xIndex; int newX = isReversed ? targetzone->getCards().size() - xCoord + xIndex : xCoord + xIndex; + bool faceDown = shouldBeFaceDown(cardStruct, startzone, targetzone); + if (targetzone->hasCoords()) { newX = targetzone->getFreeGridColumn(newX, yCoord, card->getName(), faceDown); } else { diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_move_card.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_move_card.proto index 6e9301d27..a5b96da2e 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_move_card.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/command_move_card.proto @@ -6,7 +6,9 @@ message CardToMove { // Id of the card in its current zone optional sint32 card_id = 1 [default = -1]; - // Places the card face down, hiding its name + // If true, places the card face down, hiding its name. + // If false, forcibly turns the card face up. + // If not set, defers the resulting face down state to the server. optional bool face_down = 2; // When moving add this value to the power/toughness field of the card From 2fba5dcd202c9a98381fbfbe6022199dcad0f230 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Tue, 3 Mar 2026 14:18:21 -0800 Subject: [PATCH 03/28] [Game] Refactor CardDragItem faceDown logic (#6552) * Rename properties and only pass forceFaceDown * [Game] Refactor CardDragItem faceDown logic * revert refactor * leave face_down unset unless forced --- cockatrice/src/game/board/card_drag_item.cpp | 5 +++-- cockatrice/src/game/board/card_drag_item.h | 8 ++++---- cockatrice/src/game/board/card_item.cpp | 9 ++++----- cockatrice/src/game/board/card_item.h | 2 +- cockatrice/src/game/zones/pile_zone.cpp | 4 ++-- cockatrice/src/game/zones/table_zone.cpp | 6 ++++-- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/cockatrice/src/game/board/card_drag_item.cpp b/cockatrice/src/game/board/card_drag_item.cpp index 9eadadeda..5ae56ccba 100644 --- a/cockatrice/src/game/board/card_drag_item.cpp +++ b/cockatrice/src/game/board/card_drag_item.cpp @@ -13,9 +13,10 @@ CardDragItem::CardDragItem(CardItem *_item, int _id, const QPointF &_hotSpot, - bool _faceDown, + bool _forceFaceDown, AbstractCardDragItem *parentDrag) - : AbstractCardDragItem(_item, _hotSpot, parentDrag), id(_id), faceDown(_faceDown), occupied(false), currentZone(0) + : AbstractCardDragItem(_item, _hotSpot, parentDrag), id(_id), forceFaceDown(_forceFaceDown), occupied(false), + currentZone(0) { } diff --git a/cockatrice/src/game/board/card_drag_item.h b/cockatrice/src/game/board/card_drag_item.h index 257fde150..930c6be6f 100644 --- a/cockatrice/src/game/board/card_drag_item.h +++ b/cockatrice/src/game/board/card_drag_item.h @@ -16,7 +16,7 @@ class CardDragItem : public AbstractCardDragItem Q_OBJECT private: int id; - bool faceDown; + bool forceFaceDown; bool occupied; CardZone *currentZone; @@ -24,15 +24,15 @@ public: CardDragItem(CardItem *_item, int _id, const QPointF &_hotSpot, - bool _faceDown, + bool _forceFaceDown, AbstractCardDragItem *parentDrag = 0); int getId() const { return id; } - bool getFaceDown() const + bool isForceFaceDown() const { - return faceDown; + return forceFaceDown; } void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void updatePosition(const QPointF &cursorScenePos) override; diff --git a/cockatrice/src/game/board/card_item.cpp b/cockatrice/src/game/board/card_item.cpp index 34c2f9a98..baf04e993 100644 --- a/cockatrice/src/game/board/card_item.cpp +++ b/cockatrice/src/game/board/card_item.cpp @@ -251,10 +251,10 @@ void CardItem::processCardInfo(const ServerInfo_Card &_info) setDoesntUntap(_info.doesnt_untap()); } -CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown) +CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool forceFaceDown) { deleteDragItem(); - dragItem = new CardDragItem(this, _id, _pos, faceDown); + dragItem = new CardDragItem(this, _id, _pos, forceFaceDown); dragItem->setVisible(false); scene()->addItem(dragItem); dragItem->updatePosition(_scenePos); @@ -352,7 +352,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(), facedown || forceFaceDown); + createDragItem(id, event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown); dragItem->grabMouse(); int childIndex = 0; @@ -366,8 +366,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) childPos = card->pos() - pos(); else childPos = QPointF(childIndex * CARD_WIDTH / 2, 0); - CardDragItem *drag = - new CardDragItem(card, card->getId(), childPos, card->getFaceDown() || forceFaceDown, dragItem); + CardDragItem *drag = new CardDragItem(card, card->getId(), childPos, forceFaceDown, dragItem); drag->setPos(dragItem->pos() + childPos); scene()->addItem(drag); } diff --git a/cockatrice/src/game/board/card_item.h b/cockatrice/src/game/board/card_item.h index 86c4fe594..875c3f4d0 100644 --- a/cockatrice/src/game/board/card_item.h +++ b/cockatrice/src/game/board/card_item.h @@ -142,7 +142,7 @@ public: void processCardInfo(const ServerInfo_Card &_info); bool animationEvent(); - CardDragItem *createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown); + CardDragItem *createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool forceFaceDown); void deleteDragItem(); void drawArrow(const QColor &arrowColor); void drawAttachArrow(); diff --git a/cockatrice/src/game/zones/pile_zone.cpp b/cockatrice/src/game/zones/pile_zone.cpp index 2d1390826..521d13b99 100644 --- a/cockatrice/src/game/zones/pile_zone.cpp +++ b/cockatrice/src/game/zones/pile_zone.cpp @@ -101,12 +101,12 @@ void PileZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event) if (getLogic()->getCards().isEmpty()) return; - bool faceDown = event->modifiers().testFlag(Qt::ShiftModifier); + bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier); bool bottomCard = event->modifiers().testFlag(Qt::ControlModifier); CardItem *card = bottomCard ? getLogic()->getCards().last() : getLogic()->getCards().first(); const int cardid = getLogic()->contentsKnown() ? card->getId() : (bottomCard ? getLogic()->getCards().size() - 1 : 0); - CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), faceDown); + CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), forceFaceDown); drag->grabMouse(); setCursor(Qt::OpenHandCursor); } diff --git a/cockatrice/src/game/zones/table_zone.cpp b/cockatrice/src/game/zones/table_zone.cpp index c76514350..ca1052d20 100644 --- a/cockatrice/src/game/zones/table_zone.cpp +++ b/cockatrice/src/game/zones/table_zone.cpp @@ -134,8 +134,10 @@ void TableZone::handleDropEventByGrid(const QList &dragItems, for (const auto &item : dragItems) { CardToMove *ctm = cmd.mutable_cards_to_move()->add_card(); ctm->set_card_id(item->getId()); - ctm->set_face_down(item->getFaceDown()); - if (startZone->getName() != getLogic()->getName() && !item->getFaceDown()) { + if (item->isForceFaceDown()) { + ctm->set_face_down(true); + } + if (startZone->getName() != getLogic()->getName() && !item->isForceFaceDown()) { const auto &card = item->getItem()->getCard(); if (card) { ctm->set_pt(card.getInfo().getPowTough().toStdString()); From 846ecb7e8daa078022434acaf25f368259f3f748 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Tue, 3 Mar 2026 14:19:26 -0800 Subject: [PATCH 04/28] [Client] Support face-down cards in all public zones (#6602) * [Server] Support face-down cards in all public zones * add null check * Check using zone names instead * add comment * Rename properties and only pass forceFaceDown * [Game] Refactor CardDragItem faceDown logic * revert refactor * leave face_down unset unless forced * [Client] Support face-down cards in all public zones * leave face_down unset unless forced * log face down * update remaining logs --- cockatrice/src/game/board/card_item.cpp | 4 +++- .../src/game/log/message_log_widget.cpp | 24 +++++++++++++++---- .../src/game/zones/logic/card_zone_logic.cpp | 6 +++-- cockatrice/src/game/zones/pile_zone.cpp | 9 +++++-- cockatrice/src/game/zones/stack_zone.cpp | 6 ++++- cockatrice/src/game/zones/view_zone.cpp | 21 ++++++++++++---- 6 files changed, 55 insertions(+), 15 deletions(-) diff --git a/cockatrice/src/game/board/card_item.cpp b/cockatrice/src/game/board/card_item.cpp index baf04e993..8b9549f07 100644 --- a/cockatrice/src/game/board/card_item.cpp +++ b/cockatrice/src/game/board/card_item.cpp @@ -212,10 +212,12 @@ void CardItem::setAttachedTo(CardItem *_attachedTo) } } +/** + * @brief Resets the fields that should be reset after a zone transition + */ void CardItem::resetState(bool keepAnnotations) { attacking = false; - facedown = false; counters.clear(); pt.clear(); if (!keepAnnotations) { diff --git a/cockatrice/src/game/log/message_log_widget.cpp b/cockatrice/src/game/log/message_log_widget.cpp index 1ff3e32ff..645974994 100644 --- a/cockatrice/src/game/log/message_log_widget.cpp +++ b/cockatrice/src/game/log/message_log_widget.cpp @@ -314,9 +314,17 @@ void MessageLogWidget::logMoveCard(Player *player, finalStr = tr("%1 puts %2 into play%3."); } } else if (targetZoneName == GRAVE_ZONE_NAME) { - finalStr = tr("%1 puts %2%3 into their graveyard."); + if (card->getFaceDown()) { + finalStr = tr("%1 puts %2%3 into their graveyard face down."); + } else { + finalStr = tr("%1 puts %2%3 into their graveyard."); + } } else if (targetZoneName == EXILE_ZONE_NAME) { - finalStr = tr("%1 exiles %2%3."); + if (card->getFaceDown()) { + finalStr = tr("%1 exiles %2%3 face down."); + } else { + finalStr = tr("%1 exiles %2%3."); + } } else if (targetZoneName == HAND_ZONE_NAME) { finalStr = tr("%1 moves %2%3 to their hand."); } else if (targetZoneName == DECK_ZONE_NAME) { @@ -335,10 +343,18 @@ void MessageLogWidget::logMoveCard(Player *player, finalStr = tr("%1 moves %2%3 to sideboard."); } else if (targetZoneName == STACK_ZONE_NAME) { soundEngine->playSound("play_card"); - finalStr = tr("%1 plays %2%3."); + if (card->getFaceDown()) { + finalStr = tr("%1 plays %2%3 face down."); + } else { + finalStr = tr("%1 plays %2%3."); + } } else { fourthArg = targetZoneName; - finalStr = tr("%1 moves %2%3 to custom zone '%4'."); + if (card->getFaceDown()) { + finalStr = tr("%1 moves %2%3 to custom zone '%4' face down."); + } else { + finalStr = tr("%1 moves %2%3 to custom zone '%4'."); + } } QString message = finalStr.arg(sanitizeHtml(player->getPlayerInfo()->getName()), cardStr, nameFrom.second); diff --git a/cockatrice/src/game/zones/logic/card_zone_logic.cpp b/cockatrice/src/game/zones/logic/card_zone_logic.cpp index bd32eab3e..1872ba95e 100644 --- a/cockatrice/src/game/zones/logic/card_zone_logic.cpp +++ b/cockatrice/src/game/zones/logic/card_zone_logic.cpp @@ -43,8 +43,10 @@ void CardZoneLogic::addCard(CardItem *card, const bool reorganize, const int x, for (auto *view : views) { if (qobject_cast(view->getLogic())->prepareAddCard(x)) { - view->getLogic()->addCard(new CardItem(player, nullptr, card->getCardRef(), card->getId()), reorganize, x, - y); + auto copy = new CardItem(player, nullptr, card->getCardRef(), card->getId()); + copy->setFaceDown(card->getFaceDown()); + + view->getLogic()->addCard(copy, reorganize, x, y); } } diff --git a/cockatrice/src/game/zones/pile_zone.cpp b/cockatrice/src/game/zones/pile_zone.cpp index 521d13b99..87a60fc03 100644 --- a/cockatrice/src/game/zones/pile_zone.cpp +++ b/cockatrice/src/game/zones/pile_zone.cpp @@ -68,8 +68,13 @@ void PileZone::handleDropEvent(const QList &dragItems, CardZoneL cmd.set_x(0); cmd.set_y(0); - for (int i = 0; i < dragItems.size(); ++i) - cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId()); + for (int i = 0; i < dragItems.size(); ++i) { + auto cardToMove = cmd.mutable_cards_to_move()->add_card(); + cardToMove->set_card_id(dragItems[i]->getId()); + if (dragItems[i]->isForceFaceDown()) { + cardToMove->set_face_down(true); + } + } getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); } diff --git a/cockatrice/src/game/zones/stack_zone.cpp b/cockatrice/src/game/zones/stack_zone.cpp index 820008b27..a9d649a4a 100644 --- a/cockatrice/src/game/zones/stack_zone.cpp +++ b/cockatrice/src/game/zones/stack_zone.cpp @@ -78,7 +78,11 @@ void StackZone::handleDropEvent(const QList &dragItems, for (CardDragItem *item : dragItems) { if (item) { - cmd.mutable_cards_to_move()->add_card()->set_card_id(item->getId()); + auto cardToMove = cmd.mutable_cards_to_move()->add_card(); + cardToMove->set_card_id(item->getId()); + if (item->isForceFaceDown()) { + cardToMove->set_face_down(true); + } } } diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp index 4ae25989e..348ed6e87 100644 --- a/cockatrice/src/game/zones/view_zone.cpp +++ b/cockatrice/src/game/zones/view_zone.cpp @@ -73,7 +73,10 @@ void ZoneViewZone::initializeCards(const QList &cardLis for (int i = 0; i < cardList.size(); ++i) { auto card = cardList[i]; CardRef cardRef = {QString::fromStdString(card->name()), QString::fromStdString(card->provider_id())}; - getLogic()->addCard(new CardItem(getLogic()->getPlayer(), this, cardRef, card->id()), false, i); + auto copy = new CardItem(getLogic()->getPlayer(), this, cardRef, card->id()); + copy->setFaceDown(card->face_down()); + + getLogic()->addCard(copy, false, i); } reorganizeCards(); } else if (!qobject_cast(getLogic())->getOriginalZone()->contentsKnown()) { @@ -91,8 +94,10 @@ void ZoneViewZone::initializeCards(const QList &cardLis int number = numberCards == -1 ? c.size() : (numberCards < c.size() ? numberCards : c.size()); for (int i = 0; i < number; i++) { CardItem *card = c.at(i); - getLogic()->addCard(new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId()), false, - i); + auto copy = new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId()); + copy->setFaceDown(card->getFaceDown()); + + getLogic()->addCard(copy, false, i); } reorganizeCards(); } @@ -107,6 +112,7 @@ void ZoneViewZone::zoneDumpReceived(const Response &r) auto cardName = QString::fromStdString(cardInfo.name()); auto cardProviderId = QString::fromStdString(cardInfo.provider_id()); auto card = new CardItem(getLogic()->getPlayer(), this, {cardName, cardProviderId}, cardInfo.id(), getLogic()); + card->setFaceDown(cardInfo.face_down()); getLogic()->rawInsertCard(card, i); } @@ -279,8 +285,13 @@ void ZoneViewZone::handleDropEvent(const QList &dragItems, cmd.set_y(0); cmd.set_is_reversed(qobject_cast(getLogic())->getIsReversed()); - for (int i = 0; i < dragItems.size(); ++i) - cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId()); + for (int i = 0; i < dragItems.size(); ++i) { + auto cardToMove = cmd.mutable_cards_to_move()->add_card(); + cardToMove->set_card_id(dragItems[i]->getId()); + if (dragItems[i]->isForceFaceDown()) { + cardToMove->set_face_down(true); + } + } getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); } From 43acac5f5db0a8ddf5221ececf35f57af6d6ea9e Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Wed, 4 Mar 2026 00:16:19 +0100 Subject: [PATCH 05/28] empty card info when switching back to theme background (#6657) --- cockatrice/src/interface/widgets/general/home_widget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cockatrice/src/interface/widgets/general/home_widget.cpp b/cockatrice/src/interface/widgets/general/home_widget.cpp index 269326257..5176dde83 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.cpp +++ b/cockatrice/src/interface/widgets/general/home_widget.cpp @@ -63,6 +63,7 @@ void HomeWidget::initializeBackgroundFromSource() switch (backgroundSourceType) { case BackgroundSources::Theme: background = QPixmap("theme:backgrounds/home"); + backgroundSourceCard->setCard(ExactCard()); updateButtonsToBackgroundColor(); update(); break; From 566c876bdc7f716ee56cf11f08c1aa6626a5c4a4 Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Wed, 4 Mar 2026 00:16:45 +0100 Subject: [PATCH 06/28] fix scaling for background images on home tab (#6656) --- .../card_info_picture_art_crop_widget.cpp | 12 ++++-------- .../cards/card_info_picture_art_crop_widget.h | 4 ++-- .../interface/widgets/general/home_widget.cpp | 19 +++++++++++-------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.cpp b/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.cpp index 451104d17..e5b0c4872 100644 --- a/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.cpp @@ -8,7 +8,7 @@ CardInfoPictureArtCropWidget::CardInfoPictureArtCropWidget(QWidget *parent) hide(); } -QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &targetSize) +QPixmap CardInfoPictureArtCropWidget::getBackground() { // Load the full-resolution card image, not a pre-scaled one QPixmap fullResPixmap; @@ -17,10 +17,8 @@ QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &target } else { CardPictureLoader::getCardBackPixmap(fullResPixmap, QSize(745, 1040)); } - - // Fail-safe if loading failed if (fullResPixmap.isNull()) { - return QPixmap(targetSize); + return QPixmap(); // return null qpixmap } const QSize sz = fullResPixmap.size(); @@ -33,9 +31,7 @@ QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &target foilRect = foilRect.intersected(fullResPixmap.rect()); // always clamp to source bounds - // Crop first, then scale for best quality + // return full resolution image crop QPixmap cropped = fullResPixmap.copy(foilRect); - QPixmap scaled = cropped.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); - - return scaled; + return cropped; } diff --git a/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.h b/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.h index aed951cc6..0185f758c 100644 --- a/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.h +++ b/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.h @@ -16,8 +16,8 @@ class CardInfoPictureArtCropWidget : public CardInfoPictureWidget public: explicit CardInfoPictureArtCropWidget(QWidget *parent = nullptr); - // Returns a processed (cropped & scaled) version of the pixmap - QPixmap getProcessedBackground(const QSize &targetSize); + // Returns a cropped version of the pixmap + QPixmap getBackground(); }; #endif // CARD_INFO_PICTURE_ART_CROP_WIDGET_H diff --git a/cockatrice/src/interface/widgets/general/home_widget.cpp b/cockatrice/src/interface/widgets/general/home_widget.cpp index 5176dde83..7d0bad813 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.cpp +++ b/cockatrice/src/interface/widgets/general/home_widget.cpp @@ -125,7 +125,7 @@ void HomeWidget::updateRandomCard() connect(newCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &HomeWidget::updateBackgroundProperties); backgroundSourceCard->setCard(newCard); - background = backgroundSourceCard->getProcessedBackground(size()); + background = backgroundSourceCard->getBackground(); if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() <= 0) { cardChangeTimer->stop(); @@ -143,7 +143,7 @@ void HomeWidget::onBackgroundShuffleFrequencyChanged() void HomeWidget::updateBackgroundProperties() { - background = backgroundSourceCard->getProcessedBackground(size()); + background = backgroundSourceCard->getBackground(); updateButtonsToBackgroundColor(); update(); // Triggers repaint } @@ -301,14 +301,17 @@ void HomeWidget::paintEvent(QPaintEvent *event) QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); - background = background.scaled(size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + if (!background.isNull()) { + QSize widgetSize = size() * devicePixelRatio(); + QPixmap toDraw = background.scaled(widgetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); - // Draw already-scaled background centered - QSize widgetSize = size(); - QSize bgSize = background.size(); - QPoint topLeft((widgetSize.width() - bgSize.width()) / 2, (widgetSize.height() - bgSize.height()) / 2); + // Draw scaled background centered + QSize bgSize = toDraw.size(); + QPoint topLeft((widgetSize.width() - bgSize.width()) / (devicePixelRatio() * 2), // undo scaling for painter + (widgetSize.height() - bgSize.height()) / (devicePixelRatio() * 2)); - painter.drawPixmap(topLeft, background); + painter.drawPixmap(topLeft, toDraw); + } // Draw translucent black overlay with rounded corners QRectF overlayRect(5, 5, width() - 10, height() - 10); From b36ab66583ec60f1dbb9366f3246d274daa11595 Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Wed, 4 Mar 2026 00:49:50 +0100 Subject: [PATCH 07/28] nullcheck printing's set in home tab background art crop (#6646) * nullcheck printing's set in home tab background art crop * set warning and properly set timer * fix merge --- .../interface/widgets/general/home_widget.cpp | 46 ++++++++++++------- .../interface/widgets/general/home_widget.h | 1 + 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/cockatrice/src/interface/widgets/general/home_widget.cpp b/cockatrice/src/interface/widgets/general/home_widget.cpp index 7d0bad813..750ddd000 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.cpp +++ b/cockatrice/src/interface/widgets/general/home_widget.cpp @@ -58,21 +58,24 @@ void HomeWidget::initializeBackgroundFromSource() auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource()); - cardChangeTimer->stop(); - switch (backgroundSourceType) { case BackgroundSources::Theme: + cardChangeTimer->stop(); background = QPixmap("theme:backgrounds/home"); + backgroundSourceDeck = DeckList(); backgroundSourceCard->setCard(ExactCard()); updateButtonsToBackgroundColor(); update(); break; case BackgroundSources::RandomCardArt: - cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); + backgroundSourceDeck = DeckList(); + updateRandomCard(); + onBackgroundShuffleFrequencyChanged(); break; case BackgroundSources::DeckFileArt: loadBackgroundSourceDeck(); - cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); + updateRandomCard(); + onBackgroundShuffleFrequencyChanged(); break; } } @@ -84,6 +87,20 @@ void HomeWidget::loadBackgroundSourceDeck() backgroundSourceDeck = deckOpt.has_value() ? deckOpt.value().deckList : DeckList(); } +void HomeWidget::setRandomCard(ExactCard &newCard) +{ + static constexpr int ATTEMPTS = 10; + for (int i = 0; i < ATTEMPTS; ++i) { + ExactCard tmpCard = CardDatabaseManager::query()->getRandomCard(); + if (tmpCard != backgroundSourceCard->getCard() && tmpCard.getCardPtr()->getProperty("layout") == "normal" && + tmpCard.getPrinting().getSet() != nullptr) { + newCard = tmpCard; + return; + } + } + qWarning() << "failed to set random card image after" << ATTEMPTS << "attempts"; +} + void HomeWidget::updateRandomCard() { auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource()); @@ -94,10 +111,7 @@ void HomeWidget::updateRandomCard() case BackgroundSources::Theme: break; case BackgroundSources::RandomCardArt: - do { - newCard = CardDatabaseManager::query()->getRandomCard(); - } while (newCard == backgroundSourceCard->getCard() && - newCard.getCardPtr()->getProperty("layout") != "normal"); + setRandomCard(newCard); break; case BackgroundSources::DeckFileArt: QList cardRefs = backgroundSourceDeck.getCardRefList(); @@ -126,19 +140,14 @@ void HomeWidget::updateRandomCard() connect(newCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &HomeWidget::updateBackgroundProperties); backgroundSourceCard->setCard(newCard); background = backgroundSourceCard->getBackground(); - - if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() <= 0) { - cardChangeTimer->stop(); - } } void HomeWidget::onBackgroundShuffleFrequencyChanged() { cardChangeTimer->stop(); - if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() <= 0) { - return; + if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() > 0) { + cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); } - cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); } void HomeWidget::updateBackgroundProperties() @@ -325,8 +334,11 @@ void HomeWidget::paintEvent(QPaintEvent *event) QString cardName; ExactCard card = backgroundSourceCard->getCard(); if (card) { - cardName = card.getCardPtr()->getName() + " (" + card.getPrinting().getSet()->getCorrectedShortName() + ") " + - card.getPrinting().getProperty("num"); + cardName = card.getCardPtr()->getName(); + if (card.getPrinting().getSet() != nullptr) { + cardName += " (" + card.getPrinting().getSet()->getCorrectedShortName() + ") " + + card.getPrinting().getProperty("num"); + } } if (!cardName.isEmpty() && SettingsCache::instance().getHomeTabDisplayCardName()) { diff --git a/cockatrice/src/interface/widgets/general/home_widget.h b/cockatrice/src/interface/widgets/general/home_widget.h index 233fda543..b30bb5407 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.h +++ b/cockatrice/src/interface/widgets/general/home_widget.h @@ -45,6 +45,7 @@ private: QPair gradientColors; HomeStyledButton *connectButton; + void setRandomCard(ExactCard &newCard); void loadBackgroundSourceDeck(); }; From e7a3ad86eb8bc294da4b8d027392c4fb48a3bbb9 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Wed, 4 Mar 2026 06:00:18 -0800 Subject: [PATCH 08/28] [Game] Refactor move cards from library actions (#6658) * refactor move top/bottom cards actions * minor cleanup * translate zone display names --- cockatrice/src/game/player/player_actions.cpp | 84 +++++-------------- cockatrice/src/game/player/player_actions.h | 3 + 2 files changed, 25 insertions(+), 62 deletions(-) diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index ed77808b0..1a09836e4 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -424,37 +424,15 @@ void PlayerActions::actMoveTopCardToExile() void PlayerActions::actMoveTopCardsToGrave() { - const int maxCards = player->getDeckZone()->getCards().size(); - if (maxCards == 0) { - return; - } - - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move top cards to grave"), - tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1, - maxCards, 1, &ok); - if (!ok) { - return; - } else if (number > maxCards) { - number = maxCards; - } - defaultNumberTopCards = number; - - Command_MoveCard cmd; - cmd.set_start_zone("deck"); - cmd.set_target_player_id(player->getPlayerInfo()->getId()); - cmd.set_target_zone("grave"); - cmd.set_x(0); - cmd.set_y(0); - - for (int i = number - 1; i >= 0; --i) { - cmd.mutable_cards_to_move()->add_card()->set_card_id(i); - } - - sendGameCommand(cmd); + moveTopCardsTo("grave", tr("grave")); } void PlayerActions::actMoveTopCardsToExile() +{ + moveTopCardsTo("rfg", tr("exile")); +} + +void PlayerActions::moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName) { const int maxCards = player->getDeckZone()->getCards().size(); if (maxCards == 0) { @@ -462,12 +440,14 @@ void PlayerActions::actMoveTopCardsToExile() } bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move top cards to exile"), + int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move top cards to %1").arg(zoneDisplayName), tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1, maxCards, 1, &ok); if (!ok) { return; - } else if (number > maxCards) { + } + + if (number > maxCards) { number = maxCards; } defaultNumberTopCards = number; @@ -475,7 +455,7 @@ void PlayerActions::actMoveTopCardsToExile() Command_MoveCard cmd; cmd.set_start_zone("deck"); cmd.set_target_player_id(player->getPlayerInfo()->getId()); - cmd.set_target_zone("rfg"); + cmd.set_target_zone(targetZone.toStdString()); cmd.set_x(0); cmd.set_y(0); @@ -628,37 +608,15 @@ void PlayerActions::actMoveBottomCardToExile() void PlayerActions::actMoveBottomCardsToGrave() { - const int maxCards = player->getDeckZone()->getCards().size(); - if (maxCards == 0) { - return; - } - - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move bottom cards to grave"), - tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, - maxCards, 1, &ok); - if (!ok) { - return; - } else if (number > maxCards) { - number = maxCards; - } - defaultNumberBottomCards = number; - - Command_MoveCard cmd; - cmd.set_start_zone("deck"); - cmd.set_target_player_id(player->getPlayerInfo()->getId()); - cmd.set_target_zone("grave"); - cmd.set_x(0); - cmd.set_y(0); - - for (int i = maxCards - number; i < maxCards; ++i) { - cmd.mutable_cards_to_move()->add_card()->set_card_id(i); - } - - sendGameCommand(cmd); + moveBottomCardsTo("grave", tr("grave")); } void PlayerActions::actMoveBottomCardsToExile() +{ + moveBottomCardsTo("rfg", tr("exile")); +} + +void PlayerActions::moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName) { const int maxCards = player->getDeckZone()->getCards().size(); if (maxCards == 0) { @@ -666,12 +624,14 @@ void PlayerActions::actMoveBottomCardsToExile() } bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move bottom cards to exile"), + int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move bottom cards to %1").arg(zoneDisplayName), tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, maxCards, 1, &ok); if (!ok) { return; - } else if (number > maxCards) { + } + + if (number > maxCards) { number = maxCards; } defaultNumberBottomCards = number; @@ -679,7 +639,7 @@ void PlayerActions::actMoveBottomCardsToExile() Command_MoveCard cmd; cmd.set_start_zone("deck"); cmd.set_target_player_id(player->getPlayerInfo()->getId()); - cmd.set_target_zone("rfg"); + cmd.set_target_zone(targetZone.toStdString()); cmd.set_x(0); cmd.set_y(0); diff --git a/cockatrice/src/game/player/player_actions.h b/cockatrice/src/game/player/player_actions.h index b294b5946..beed2c241 100644 --- a/cockatrice/src/game/player/player_actions.h +++ b/cockatrice/src/game/player/player_actions.h @@ -180,6 +180,9 @@ private: FilterString movingCardsUntilFilter; int movingCardsUntilCounter = 0; + void moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName); + void moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName); + void createCard(const CardItem *sourceCard, const QString &dbCardName, CardRelationType attach = CardRelationType::DoesNotAttach, From 1bcea27a445c912b5d51462e5a35ab732942ded4 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Wed, 4 Mar 2026 18:19:41 -0800 Subject: [PATCH 09/28] [Game] Add face down versions of move cards from library actions (#6661) * implement actions * add new actions to menu * update shortcuts --- .../src/client/settings/shortcuts_settings.h | 16 +++++++ .../src/game/player/menu/library_menu.cpp | 28 ++++++++++++ .../src/game/player/menu/library_menu.h | 4 ++ cockatrice/src/game/player/player_actions.cpp | 44 +++++++++++++++---- cockatrice/src/game/player/player_actions.h | 8 +++- 5 files changed, 90 insertions(+), 10 deletions(-) diff --git a/cockatrice/src/client/settings/shortcuts_settings.h b/cockatrice/src/client/settings/shortcuts_settings.h index 156ee82ea..47332b8c4 100644 --- a/cockatrice/src/client/settings/shortcuts_settings.h +++ b/cockatrice/src/client/settings/shortcuts_settings.h @@ -598,11 +598,19 @@ private: {"Player/aMoveTopCardsToGraveyard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple)"), parseSequenceString("Alt+M"), ShortcutGroup::Move_top)}, + {"Player/aMoveTopCardsToGraveyardFaceDown", + ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple), Face Down"), + parseSequenceString(""), + ShortcutGroup::Move_top)}, {"Player/aMoveTopCardToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile"), parseSequenceString(""), ShortcutGroup::Move_top)}, {"Player/aMoveTopCardsToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple)"), parseSequenceString(""), ShortcutGroup::Move_top)}, + {"Player/aMoveTopCardsToExileFaceDown", + ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple), Face Down"), + parseSequenceString(""), + ShortcutGroup::Move_top)}, {"Player/aMoveTopCardsUntil", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Stack Until Found"), parseSequenceString("Ctrl+Shift+Y"), ShortcutGroup::Move_top)}, @@ -620,11 +628,19 @@ private: {"Player/aMoveBottomCardsToGrave", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple)"), parseSequenceString(""), ShortcutGroup::Move_bottom)}, + {"Player/aMoveBottomCardsToGraveFaceDown", + ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple), Face Down"), + parseSequenceString(""), + ShortcutGroup::Move_bottom)}, {"Player/aMoveBottomCardToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile"), parseSequenceString(""), ShortcutGroup::Move_bottom)}, {"Player/aMoveBottomCardsToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple)"), parseSequenceString(""), ShortcutGroup::Move_bottom)}, + {"Player/aMoveBottomCardsToExileFaceDown", + ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple), Face Down"), + parseSequenceString(""), + ShortcutGroup::Move_bottom)}, {"Player/aMoveBottomCardToTop", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Top of Library"), parseSequenceString(""), ShortcutGroup::Move_bottom)}, diff --git a/cockatrice/src/game/player/menu/library_menu.cpp b/cockatrice/src/game/player/menu/library_menu.cpp index 749439558..1bb647d06 100644 --- a/cockatrice/src/game/player/menu/library_menu.cpp +++ b/cockatrice/src/game/player/menu/library_menu.cpp @@ -51,8 +51,10 @@ LibraryMenu::LibraryMenu(Player *_player, QWidget *parent) : TearOffMenu(parent) topLibraryMenu->addSeparator(); topLibraryMenu->addAction(aMoveTopCardToGraveyard); topLibraryMenu->addAction(aMoveTopCardsToGraveyard); + topLibraryMenu->addAction(aMoveTopCardsToGraveyardFaceDown); topLibraryMenu->addAction(aMoveTopCardToExile); topLibraryMenu->addAction(aMoveTopCardsToExile); + topLibraryMenu->addAction(aMoveTopCardsToExileFaceDown); topLibraryMenu->addAction(aMoveTopCardsUntil); topLibraryMenu->addSeparator(); topLibraryMenu->addAction(aShuffleTopCards); @@ -66,8 +68,10 @@ LibraryMenu::LibraryMenu(Player *_player, QWidget *parent) : TearOffMenu(parent) bottomLibraryMenu->addSeparator(); bottomLibraryMenu->addAction(aMoveBottomCardToGraveyard); bottomLibraryMenu->addAction(aMoveBottomCardsToGraveyard); + bottomLibraryMenu->addAction(aMoveBottomCardsToGraveyardFaceDown); bottomLibraryMenu->addAction(aMoveBottomCardToExile); bottomLibraryMenu->addAction(aMoveBottomCardsToExile); + bottomLibraryMenu->addAction(aMoveBottomCardsToExileFaceDown); bottomLibraryMenu->addSeparator(); bottomLibraryMenu->addAction(aShuffleBottomCards); @@ -136,8 +140,14 @@ void LibraryMenu::createMoveActions() connect(aMoveTopCardToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardToExile); aMoveTopCardsToGraveyard = new QAction(this); connect(aMoveTopCardsToGraveyard, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsToGrave); + aMoveTopCardsToGraveyardFaceDown = new QAction(this); + connect(aMoveTopCardsToGraveyardFaceDown, &QAction::triggered, playerActions, + &PlayerActions::actMoveTopCardsToGraveFaceDown); aMoveTopCardsToExile = new QAction(this); connect(aMoveTopCardsToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsToExile); + aMoveTopCardsToExileFaceDown = new QAction(this); + connect(aMoveTopCardsToExileFaceDown, &QAction::triggered, playerActions, + &PlayerActions::actMoveTopCardsToExileFaceDown); aMoveTopCardsUntil = new QAction(this); connect(aMoveTopCardsUntil, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsUntil); aMoveTopCardToBottom = new QAction(this); @@ -156,8 +166,14 @@ void LibraryMenu::createMoveActions() aMoveBottomCardsToGraveyard = new QAction(this); connect(aMoveBottomCardsToGraveyard, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardsToGrave); + aMoveBottomCardsToGraveyardFaceDown = new QAction(this); + connect(aMoveBottomCardsToGraveyardFaceDown, &QAction::triggered, playerActions, + &PlayerActions::actMoveBottomCardsToGraveFaceDown); aMoveBottomCardsToExile = new QAction(this); connect(aMoveBottomCardsToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardsToExile); + aMoveBottomCardsToExileFaceDown = new QAction(this); + connect(aMoveBottomCardsToExileFaceDown, &QAction::triggered, playerActions, + &PlayerActions::actMoveBottomCardsToExileFaceDown); aMoveBottomCardToTop = new QAction(this); connect(aMoveBottomCardToTop, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardToTop); } @@ -216,7 +232,9 @@ void LibraryMenu::retranslateUi() aMoveTopCardToGraveyard->setText(tr("Move top card to grave&yard")); aMoveTopCardToExile->setText(tr("Move top card to e&xile")); aMoveTopCardsToGraveyard->setText(tr("Move top cards to &graveyard...")); + aMoveTopCardsToGraveyardFaceDown->setText(tr("Move top cards to graveyard face down...")); aMoveTopCardsToExile->setText(tr("Move top cards to &exile...")); + aMoveTopCardsToExileFaceDown->setText(tr("Move top cards to exile face down...")); aMoveTopCardsUntil->setText(tr("Put top cards on stack &until...")); aShuffleTopCards->setText(tr("Shuffle top cards...")); @@ -227,7 +245,9 @@ void LibraryMenu::retranslateUi() aMoveBottomCardToGraveyard->setText(tr("Move bottom card to grave&yard")); aMoveBottomCardToExile->setText(tr("Move bottom card to e&xile")); aMoveBottomCardsToGraveyard->setText(tr("Move bottom cards to &graveyard...")); + aMoveBottomCardsToGraveyardFaceDown->setText(tr("Move bottom cards to graveyard face down...")); aMoveBottomCardsToExile->setText(tr("Move bottom cards to &exile...")); + aMoveBottomCardsToExileFaceDown->setText(tr("Move bottom cards to exile face down...")); aMoveBottomCardToTop->setText(tr("Put bottom card on &top")); aShuffleBottomCards->setText(tr("Shuffle bottom cards...")); } @@ -335,8 +355,10 @@ void LibraryMenu::setShortcutsActive() aMoveTopToPlayFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveTopToPlayFaceDown")); aMoveTopCardToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToGraveyard")); aMoveTopCardsToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToGraveyard")); + aMoveTopCardsToGraveyardFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToGraveyardFaceDown")); aMoveTopCardToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToExile")); aMoveTopCardsToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToExile")); + aMoveTopCardsToExileFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToExileFaceDown")); aMoveTopCardsUntil->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsUntil")); aMoveTopCardToBottom->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToBottom")); aDrawBottomCard->setShortcuts(shortcuts.getShortcut("Player/aDrawBottomCard")); @@ -345,8 +367,10 @@ void LibraryMenu::setShortcutsActive() aMoveBottomToPlayFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomToPlayFaceDown")); aMoveBottomCardToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToGrave")); aMoveBottomCardsToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToGrave")); + aMoveBottomCardsToGraveyardFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToGraveFaceDown")); aMoveBottomCardToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToExile")); aMoveBottomCardsToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToExile")); + aMoveBottomCardsToExileFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToExileFaceDown")); aMoveBottomCardToTop->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToTop")); } @@ -367,8 +391,10 @@ void LibraryMenu::setShortcutsInactive() aMoveTopToPlayFaceDown->setShortcut(QKeySequence()); aMoveTopCardToGraveyard->setShortcut(QKeySequence()); aMoveTopCardsToGraveyard->setShortcut(QKeySequence()); + aMoveTopCardsToGraveyardFaceDown->setShortcut(QKeySequence()); aMoveTopCardToExile->setShortcut(QKeySequence()); aMoveTopCardsToExile->setShortcut(QKeySequence()); + aMoveTopCardsToExileFaceDown->setShortcut(QKeySequence()); aMoveTopCardsUntil->setShortcut(QKeySequence()); aDrawBottomCard->setShortcut(QKeySequence()); aDrawBottomCards->setShortcut(QKeySequence()); @@ -376,6 +402,8 @@ void LibraryMenu::setShortcutsInactive() aMoveBottomToPlayFaceDown->setShortcut(QKeySequence()); aMoveBottomCardToGraveyard->setShortcut(QKeySequence()); aMoveBottomCardsToGraveyard->setShortcut(QKeySequence()); + aMoveBottomCardsToGraveyardFaceDown->setShortcut(QKeySequence()); aMoveBottomCardToExile->setShortcut(QKeySequence()); aMoveBottomCardsToExile->setShortcut(QKeySequence()); + aMoveBottomCardsToExileFaceDown->setShortcut(QKeySequence()); } diff --git a/cockatrice/src/game/player/menu/library_menu.h b/cockatrice/src/game/player/menu/library_menu.h index 96d4b82b1..c0883107c 100644 --- a/cockatrice/src/game/player/menu/library_menu.h +++ b/cockatrice/src/game/player/menu/library_menu.h @@ -88,7 +88,9 @@ public: QAction *aMoveTopCardToGraveyard = nullptr; QAction *aMoveTopCardToExile = nullptr; QAction *aMoveTopCardsToGraveyard = nullptr; + QAction *aMoveTopCardsToGraveyardFaceDown = nullptr; QAction *aMoveTopCardsToExile = nullptr; + QAction *aMoveTopCardsToExileFaceDown = nullptr; QAction *aMoveTopCardsUntil = nullptr; QAction *aShuffleTopCards = nullptr; @@ -100,7 +102,9 @@ public: QAction *aMoveBottomCardToGraveyard = nullptr; QAction *aMoveBottomCardToExile = nullptr; QAction *aMoveBottomCardsToGraveyard = nullptr; + QAction *aMoveBottomCardsToGraveyardFaceDown = nullptr; QAction *aMoveBottomCardsToExile = nullptr; + QAction *aMoveBottomCardsToExileFaceDown = nullptr; QAction *aShuffleBottomCards = nullptr; int defaultNumberTopCards = 1; diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index 1a09836e4..514ac2e67 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -424,15 +424,25 @@ void PlayerActions::actMoveTopCardToExile() void PlayerActions::actMoveTopCardsToGrave() { - moveTopCardsTo("grave", tr("grave")); + moveTopCardsTo("grave", tr("grave"), false); +} + +void PlayerActions::actMoveTopCardsToGraveFaceDown() +{ + moveTopCardsTo("grave", tr("grave"), true); } void PlayerActions::actMoveTopCardsToExile() { - moveTopCardsTo("rfg", tr("exile")); + moveTopCardsTo("rfg", tr("exile"), false); } -void PlayerActions::moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName) +void PlayerActions::actMoveTopCardsToExileFaceDown() +{ + moveTopCardsTo("rfg", tr("exile"), true); +} + +void PlayerActions::moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown) { const int maxCards = player->getDeckZone()->getCards().size(); if (maxCards == 0) { @@ -460,7 +470,11 @@ void PlayerActions::moveTopCardsTo(const QString &targetZone, const QString &zon cmd.set_y(0); for (int i = number - 1; i >= 0; --i) { - cmd.mutable_cards_to_move()->add_card()->set_card_id(i); + auto card = cmd.mutable_cards_to_move()->add_card(); + card->set_card_id(i); + if (faceDown) { + card->set_face_down(true); + } } sendGameCommand(cmd); @@ -608,15 +622,25 @@ void PlayerActions::actMoveBottomCardToExile() void PlayerActions::actMoveBottomCardsToGrave() { - moveBottomCardsTo("grave", tr("grave")); + moveBottomCardsTo("grave", tr("grave"), false); +} + +void PlayerActions::actMoveBottomCardsToGraveFaceDown() +{ + moveBottomCardsTo("grave", tr("grave"), true); } void PlayerActions::actMoveBottomCardsToExile() { - moveBottomCardsTo("rfg", tr("exile")); + moveBottomCardsTo("rfg", tr("exile"), false); } -void PlayerActions::moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName) +void PlayerActions::actMoveBottomCardsToExileFaceDown() +{ + moveBottomCardsTo("rfg", tr("exile"), true); +} + +void PlayerActions::moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown) { const int maxCards = player->getDeckZone()->getCards().size(); if (maxCards == 0) { @@ -644,7 +668,11 @@ void PlayerActions::moveBottomCardsTo(const QString &targetZone, const QString & cmd.set_y(0); for (int i = maxCards - number; i < maxCards; ++i) { - cmd.mutable_cards_to_move()->add_card()->set_card_id(i); + auto card = cmd.mutable_cards_to_move()->add_card(); + card->set_card_id(i); + if (faceDown) { + card->set_face_down(true); + } } sendGameCommand(cmd); diff --git a/cockatrice/src/game/player/player_actions.h b/cockatrice/src/game/player/player_actions.h index beed2c241..1d695578d 100644 --- a/cockatrice/src/game/player/player_actions.h +++ b/cockatrice/src/game/player/player_actions.h @@ -98,7 +98,9 @@ public slots: void actMoveTopCardToGrave(); void actMoveTopCardToExile(); void actMoveTopCardsToGrave(); + void actMoveTopCardsToGraveFaceDown(); void actMoveTopCardsToExile(); + void actMoveTopCardsToExileFaceDown(); void actMoveTopCardsUntil(); void actMoveTopCardToBottom(); void actDrawBottomCard(); @@ -108,7 +110,9 @@ public slots: void actMoveBottomCardToGrave(); void actMoveBottomCardToExile(); void actMoveBottomCardsToGrave(); + void actMoveBottomCardsToGraveFaceDown(); void actMoveBottomCardsToExile(); + void actMoveBottomCardsToExileFaceDown(); void actMoveBottomCardToTop(); void actSelectAll(); @@ -180,8 +184,8 @@ private: FilterString movingCardsUntilFilter; int movingCardsUntilCounter = 0; - void moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName); - void moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName); + void moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown); + void moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown); void createCard(const CardItem *sourceCard, const QString &dbCardName, From 04f06206b7ae3ff3b4f21d50af3310965adac350 Mon Sep 17 00:00:00 2001 From: DawnFire42 Date: Thu, 5 Mar 2026 14:47:14 -0500 Subject: [PATCH 10/28] Refactor/z value constants (#6651) * feat(z-values): add centralized Z-value constants infrastructure Magic numbers scattered across the codebase make Z-value layering hard to understand and maintain. Centralizing them provides: - Self-documenting layer hierarchy - Validation utilities for development - Single source of truth for Z-value ranges Two-tier header design: - z_value_layer_manager.h: Foundation with constants and validation - z_values.h: User-facing namespace with semantic constants * refactor(z-values): replace magic Z-value numbers with ZValues constants Magic numbers like 2000000007 are impossible to understand without context. Named constants (ZValues::DRAG_ITEM) are self-documenting. This intentionally renumbers overlay Z-values to use a cleaner offset sequence. The relative stacking order is preserved, which is what matters for correct rendering. Each consumer now includes z_values.h and uses semantic constants instead of magic numbers. * refactor(z-values): removed redundant inline with contexpr, Updated doc, magic numbers removed from TableZone. --- .../game/board/abstract_card_drag_item.cpp | 5 +- .../src/game/board/abstract_card_item.cpp | 3 +- cockatrice/src/game/board/arrow_item.cpp | 3 +- cockatrice/src/game/z_value_layer_manager.h | 136 ++++++++++++++++++ cockatrice/src/game/z_values.h | 83 +++++++++++ cockatrice/src/game/zones/table_zone.cpp | 5 +- .../src/game/zones/view_zone_widget.cpp | 9 +- 7 files changed, 234 insertions(+), 10 deletions(-) create mode 100644 cockatrice/src/game/z_value_layer_manager.h create mode 100644 cockatrice/src/game/z_values.h diff --git a/cockatrice/src/game/board/abstract_card_drag_item.cpp b/cockatrice/src/game/board/abstract_card_drag_item.cpp index c961cbcb6..c9a964048 100644 --- a/cockatrice/src/game/board/abstract_card_drag_item.cpp +++ b/cockatrice/src/game/board/abstract_card_drag_item.cpp @@ -1,6 +1,7 @@ #include "abstract_card_drag_item.h" #include "../../client/settings/cache_settings.h" +#include "../z_values.h" #include #include @@ -18,13 +19,13 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, { if (parentDrag) { parentDrag->addChildDrag(this); - setZValue(2000000007 + hotSpot.x() * 1000000 + hotSpot.y() * 1000 + 1000); + setZValue(ZValues::childDragZValue(hotSpot.x(), hotSpot.y())); connect(parentDrag, &QObject::destroyed, this, &AbstractCardDragItem::deleteLater); } else { hotSpot = QPointF{qBound(0.0, hotSpot.x(), static_cast(CARD_WIDTH - 1)), qBound(0.0, hotSpot.y(), static_cast(CARD_HEIGHT - 1))}; setCursor(Qt::ClosedHandCursor); - setZValue(2000000007); + setZValue(ZValues::DRAG_ITEM); } if (item->getTapped()) setTransform(QTransform() diff --git a/cockatrice/src/game/board/abstract_card_item.cpp b/cockatrice/src/game/board/abstract_card_item.cpp index d5538011d..f8365c3d6 100644 --- a/cockatrice/src/game/board/abstract_card_item.cpp +++ b/cockatrice/src/game/board/abstract_card_item.cpp @@ -3,6 +3,7 @@ #include "../../client/settings/cache_settings.h" #include "../../interface/card_picture_loader/card_picture_loader.h" #include "../game_scene.h" +#include "../z_values.h" #include #include @@ -215,7 +216,7 @@ void AbstractCardItem::setHovered(bool _hovered) if (_hovered) processHoverEvent(); isHovered = _hovered; - setZValue(_hovered ? 2000000004 : realZValue); + setZValue(_hovered ? ZValues::HOVERED_CARD : realZValue); setScale(_hovered && SettingsCache::instance().getScaleCards() ? 1.1 : 1); setTransformOriginPoint(_hovered ? CARD_WIDTH / 2 : 0, _hovered ? CARD_HEIGHT / 2 : 0); update(); diff --git a/cockatrice/src/game/board/arrow_item.cpp b/cockatrice/src/game/board/arrow_item.cpp index 1352b3a05..257d96f8a 100644 --- a/cockatrice/src/game/board/arrow_item.cpp +++ b/cockatrice/src/game/board/arrow_item.cpp @@ -5,6 +5,7 @@ #include "../player/player.h" #include "../player/player_actions.h" #include "../player/player_target.h" +#include "../z_values.h" #include "../zones/card_zone.h" #include "card_item.h" @@ -23,7 +24,7 @@ ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTar : QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), targetLocked(false), color(_color), fullColor(true) { - setZValue(2000000005); + setZValue(ZValues::ARROWS); if (startItem) startItem->addArrowFrom(this); diff --git a/cockatrice/src/game/z_value_layer_manager.h b/cockatrice/src/game/z_value_layer_manager.h new file mode 100644 index 000000000..303edb8a5 --- /dev/null +++ b/cockatrice/src/game/z_value_layer_manager.h @@ -0,0 +1,136 @@ +/** + * @file z_value_layer_manager.h + * @ingroup GameGraphics + * @brief Semantic Z-value layer management for game scene rendering. + * + * This file provides a structured approach to Z-value allocation in the game scene. + * Z-values in Qt determine stacking order - higher values render on top of lower values. + * + * ## Layer Architecture + * + * The game scene is organized into three conceptual layers: + * + * 1. **Zone Layer (0-999)**: Zone backgrounds, containers, and static elements + * - Zone backgrounds (0.5-1.0) + * - Cards within zones (1.0 base + index) + * + * 2. **Card Layer (1-40,000,000)**: Dynamic card rendering on the table zone + * - Cards use formula: (actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100 + * - Maximum card Z-value: ~40,000,000 (with 3 rows, actualY <= ~289) + * + * 3. **Overlay Layer (2,000,000,000+)**: UI elements that must appear above all cards + * - Hovered cards (+1) + * - Arrows (+3) + * - Zone views (+4) + * - Drag items (+5, +6) + * - Top UI elements (+7) + * + * ## Design Rationale + * + * The large gap between card Z-values (max ~40M) and overlay base (2B) provides + * safety margin for future table zone expansions while ensuring overlays always + * render above cards regardless of table position. + * + * ## Usage + * + * Prefer using the semantic constants from ZValues namespace: + * @code + * card->setZValue(ZValues::HOVERED_CARD); + * arrow->setZValue(ZValues::ARROWS); + * @endcode + * + * Use validation functions to verify card Z-values during development: + * @code + * Q_ASSERT(ZValueLayerManager::isValidCardZValue(cardZ)); + * @endcode + */ + +#ifndef Z_VALUE_LAYER_MANAGER_H +#define Z_VALUE_LAYER_MANAGER_H + +#include + +/** + * @namespace ZValueLayerManager + * @brief Utilities for Z-value validation and layer management. + */ +namespace ZValueLayerManager +{ + +/** + * @enum Layer + * @brief Semantic layer identifiers for Z-value allocation. + * + * These represent conceptual rendering layers, not actual Z-values. + * Use the corresponding ZValues constants for actual rendering. + */ +enum class Layer +{ + /// Zone-level elements like backgrounds and containers + Zone, + /// Cards rendered in zones (uses sequential Z-values) + Card, + /// Temporary UI elements like hovered cards and drag items + Overlay +}; + +/** + * @brief Maximum Z-value a card can have on the table zone. + * + * Based on table zone formula: (actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100 + * With maximum 3 rows and CARD_HEIGHT ~96, actualY <= ~289. + * Maximum: (289 + 96) * 100000 + 100 * 100 = 38,510,000 + * + * We use 40,000,000 as a safe upper bound with margin. + */ +constexpr qreal CARD_Z_VALUE_MAX = 40000000.0; + +/** + * @brief Base Z-value for overlay elements. + * + * Must exceed CARD_Z_VALUE_MAX to ensure overlays render above all cards. + * The 50x margin (2B vs 40M) provides safety for future expansion. + */ +constexpr qreal OVERLAY_BASE = 2000000000.0; + +/** + * @brief Validates that a Z-value is within the valid card range. + * + * Cards should have Z-values between CARD_BASE (1.0) and CARD_Z_VALUE_MAX. + * Values outside this range may interfere with overlay rendering. + * + * @param zValue The Z-value to validate + * @return true if the Z-value is valid for a card + */ +[[nodiscard]] constexpr bool isValidCardZValue(qreal zValue) +{ + return zValue >= 1.0 && zValue <= CARD_Z_VALUE_MAX; +} + +/** + * @brief Validates that a Z-value is in the overlay layer. + * + * Overlay elements should have Z-values at or above OVERLAY_BASE. + * + * @param zValue The Z-value to validate + * @return true if the Z-value is valid for an overlay element + */ +[[nodiscard]] constexpr bool isOverlayZValue(qreal zValue) +{ + return zValue >= OVERLAY_BASE; +} + +/** + * @brief Returns the Z-value for a specific overlay element. + * + * @param offset Offset from OVERLAY_BASE (0-7 for current elements) + * @return The absolute Z-value for the overlay element + */ +[[nodiscard]] constexpr qreal overlayZValue(qreal offset) +{ + return OVERLAY_BASE + offset; +} + +} // namespace ZValueLayerManager + +#endif // Z_VALUE_LAYER_MANAGER_H diff --git a/cockatrice/src/game/z_values.h b/cockatrice/src/game/z_values.h new file mode 100644 index 000000000..2c7fe9066 --- /dev/null +++ b/cockatrice/src/game/z_values.h @@ -0,0 +1,83 @@ +#ifndef Z_VALUES_H +#define Z_VALUES_H + +#include "z_value_layer_manager.h" + +/** + * @file z_values.h + * @ingroup GameGraphics + * @brief Centralized Z-value constants for rendering layer order. + * + * Z-values in Qt determine stacking order. Higher values render on top. + * These constants define the visual layering hierarchy for the game scene. + * + * ## Layer Architecture + * + * See z_value_layer_manager.h for detailed documentation on the three-layer + * architecture (Zone, Card, Overlay) and the rationale for Z-value choices. + * + * ## Quick Reference + * + * | Layer | Z-Value Range | Purpose | + * |----------|------------------|-----------------------------------| + * | Zone | 0.5 - 1.0 | Zone backgrounds, containers | + * | Card | 1.0 - 40,000,000 | Cards on table (position-based) | + * | Overlay | 2,000,000,000+ | UI elements above all cards | + */ + +namespace ZValues +{ + +// Expose base for callers that need it +constexpr qreal OVERLAY_BASE = ZValueLayerManager::OVERLAY_BASE; + +// Overlay layer Z-values for items that should appear above normal cards +constexpr qreal HOVERED_CARD = ZValueLayerManager::overlayZValue(1.0); +constexpr qreal ARROWS = ZValueLayerManager::overlayZValue(3.0); +constexpr qreal ZONE_VIEW_WIDGET = ZValueLayerManager::overlayZValue(4.0); +constexpr qreal DRAG_ITEM = ZValueLayerManager::overlayZValue(5.0); +constexpr qreal DRAG_ITEM_CHILD = ZValueLayerManager::overlayZValue(6.0); +constexpr qreal TOP_UI = ZValueLayerManager::overlayZValue(7.0); + +/** + * @brief Compute Z-value for child drag items based on hotspot position. + * + * When dragging multiple cards together, each child card needs a unique Z-value + * to prevent Z-fighting (flickering/flashing). The Z-values are derived from + * their position when grabbed to conserve original stacking. The formula encodes + * 2D coordinates into a single value where X has higher weight, ensuring + * deterministic visual stacking. + * + * @param hotSpotX The X coordinate of the grab position + * @param hotSpotY The Y coordinate of the grab position + * @return Unique Z-value for the child drag item + */ +[[nodiscard]] constexpr qreal childDragZValue(qreal hotSpotX, qreal hotSpotY) +{ + return DRAG_ITEM_CHILD + hotSpotX * 1000000 + hotSpotY * 1000 + 1000; +} + +/** + * @brief Compute Z-value for cards on the table zone based on position. + * + * Cards lower on the table (higher Y) render above cards higher up, + * and cards to the right (higher X) render above cards to the left. + * This creates natural visual stacking for overlapping cards. + * + * @param x The X coordinate of the card position + * @param y The Y coordinate of the card position + * @return Z-value for the card's table position + */ +[[nodiscard]] constexpr qreal tableCardZValue(qreal x, qreal y) +{ + constexpr qreal CARD_HEIGHT_FOR_Z = 102.0; + return (y + CARD_HEIGHT_FOR_Z) * 100000.0 + (x + 1) * 100.0; +} + +// Card layering (general architecture, not command-zone specific) +constexpr qreal CARD_BASE = 1.0; +constexpr qreal CARD_MAX = ZValueLayerManager::CARD_Z_VALUE_MAX; + +} // namespace ZValues + +#endif // Z_VALUES_H diff --git a/cockatrice/src/game/zones/table_zone.cpp b/cockatrice/src/game/zones/table_zone.cpp index ca1052d20..33c867c43 100644 --- a/cockatrice/src/game/zones/table_zone.cpp +++ b/cockatrice/src/game/zones/table_zone.cpp @@ -7,6 +7,7 @@ #include "../board/card_item.h" #include "../player/player.h" #include "../player/player_actions.h" +#include "../z_values.h" #include "logic/table_zone_logic.h" #include @@ -169,7 +170,7 @@ void TableZone::reorganizeCards() actualY += 15; getLogic()->getCards()[i]->setPos(actualX, actualY); - getLogic()->getCards()[i]->setRealZValue((actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100); + getLogic()->getCards()[i]->setRealZValue(ZValues::tableCardZValue(actualX, actualY)); QListIterator attachedCardIterator(getLogic()->getCards()[i]->getAttachedCards()); int j = 0; @@ -179,7 +180,7 @@ void TableZone::reorganizeCards() qreal childX = actualX - j * STACKED_CARD_OFFSET_X; qreal childY = y + 5; attachedCard->setPos(childX, childY); - attachedCard->setRealZValue((childY + CARD_HEIGHT) * 100000 + (childX + 1) * 100); + attachedCard->setRealZValue(ZValues::tableCardZValue(childX, childY)); } } diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index 3ea4eb119..0ad3b10f1 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -7,6 +7,7 @@ #include "../game_scene.h" #include "../player/player.h" #include "../player/player_actions.h" +#include "../z_values.h" #include "view_zone.h" #include @@ -47,7 +48,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, { setAcceptHoverEvents(true); setAttribute(Qt::WA_DeleteOnClose); - setZValue(2000000006); + setZValue(ZValues::ZONE_VIEW_WIDGET); setFlag(ItemIgnoresTransformations); QGraphicsLinearLayout *vbox = new QGraphicsLinearLayout(Qt::Vertical); @@ -71,7 +72,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, QGraphicsProxyWidget *searchEditProxy = new QGraphicsProxyWidget; searchEditProxy->setWidget(&searchEdit); - searchEditProxy->setZValue(2000000007); + searchEditProxy->setZValue(ZValues::DRAG_ITEM); vbox->addItem(searchEditProxy); // top row @@ -80,13 +81,13 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, // groupBy options QGraphicsProxyWidget *groupBySelectorProxy = new QGraphicsProxyWidget; groupBySelectorProxy->setWidget(&groupBySelector); - groupBySelectorProxy->setZValue(2000000008); + groupBySelectorProxy->setZValue(ZValues::TOP_UI); hTopRow->addItem(groupBySelectorProxy); // sortBy options QGraphicsProxyWidget *sortBySelectorProxy = new QGraphicsProxyWidget; sortBySelectorProxy->setWidget(&sortBySelector); - sortBySelectorProxy->setZValue(2000000007); + sortBySelectorProxy->setZValue(ZValues::DRAG_ITEM); hTopRow->addItem(sortBySelectorProxy); vbox->addItem(hTopRow); From e39bbd2b316753f254be8ae8b60c5c8808160b6b Mon Sep 17 00:00:00 2001 From: ebbit1q Date: Fri, 6 Mar 2026 00:19:10 +0100 Subject: [PATCH 11/28] take default theme name from startup instead of using empty (#6663) --- cockatrice/src/interface/theme_manager.cpp | 4 +++- cockatrice/src/interface/theme_manager.h | 1 + cockatrice/src/interface/widgets/general/home_widget.cpp | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/interface/theme_manager.cpp b/cockatrice/src/interface/theme_manager.cpp index abacb2d68..1dc61fdb7 100644 --- a/cockatrice/src/interface/theme_manager.cpp +++ b/cockatrice/src/interface/theme_manager.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -89,6 +90,7 @@ struct PaletteColorInfo ThemeManager::ThemeManager(QObject *parent) : QObject(parent) { + defaultStyleName = qApp->style()->objectName(); ensureThemeDirectoryExists(); #if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, &ThemeManager::themeChangedSlot); @@ -346,7 +348,7 @@ void ThemeManager::themeChangedSlot() qApp->setStyle(QStyleFactory::create("Fusion")); qApp->setPalette(createDarkGreenFusionPalette()); } else { - qApp->setStyle(""); + qApp->setStyle(defaultStyleName); // setting the style also sets the palette } if (dirPath.isEmpty()) { diff --git a/cockatrice/src/interface/theme_manager.h b/cockatrice/src/interface/theme_manager.h index 594ede2bf..d942f0fef 100644 --- a/cockatrice/src/interface/theme_manager.h +++ b/cockatrice/src/interface/theme_manager.h @@ -40,6 +40,7 @@ public: }; private: + QString defaultStyleName; std::array brushes; QStringMap availableThemes; /* diff --git a/cockatrice/src/interface/widgets/general/home_widget.cpp b/cockatrice/src/interface/widgets/general/home_widget.cpp index 750ddd000..ea20ef6a0 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.cpp +++ b/cockatrice/src/interface/widgets/general/home_widget.cpp @@ -46,6 +46,8 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor) &HomeWidget::onBackgroundShuffleFrequencyChanged); // Lambda is cleaner to read than overloading this connect(&SettingsCache::instance(), &SettingsCache::homeTabDisplayCardNameChanged, this, [this] { repaint(); }); + connect(&SettingsCache::instance(), &SettingsCache::themeChanged, this, + &HomeWidget::updateButtonsToBackgroundColor); } void HomeWidget::initializeBackgroundFromSource() From 14f1925edc8a47045e771e71746e07290fef3679 Mon Sep 17 00:00:00 2001 From: tooomm Date: Fri, 6 Mar 2026 01:50:15 +0100 Subject: [PATCH 12/28] Add icon to exe (#6655) --- cmake/NSIS.template.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/NSIS.template.in b/cmake/NSIS.template.in index a0d51fa2b..2fdc61fb9 100644 --- a/cmake/NSIS.template.in +++ b/cmake/NSIS.template.in @@ -26,6 +26,7 @@ Var PortableMode !define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of Cockatrice.$\r$\n$\r$\nClick Next to continue." !define MUI_FINISHPAGE_RUN "$INSTDIR/cockatrice.exe" !define MUI_FINISHPAGE_RUN_TEXT "Run 'Cockatrice' now" +!define MUI_ICON "${NSIS_SOURCE_PATH}\cockatrice\resources\appicon.ico" !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "${NSIS_SOURCE_PATH}\LICENSE" From bd5cbb89d47c2cdfac9eb59e550e737d51321775 Mon Sep 17 00:00:00 2001 From: DawnFire42 Date: Thu, 5 Mar 2026 22:13:58 -0500 Subject: [PATCH 13/28] refactor: extract CARD_HEIGHT to shared CardDimensions header (#6668) * refactor: extract CARD_HEIGHT to shared CardDimensions header Move duplicated CARD_WIDTH/CARD_HEIGHT constants to card_dimensions.h. Fixed documentation in z_value_layer_manager.h. * WIDTH_F used directly instead of casting * Improved consistency and added missing newlines at end of files --- .../game/board/abstract_card_drag_item.cpp | 12 ++++---- .../src/game/board/abstract_card_drag_item.h | 2 +- .../src/game/board/abstract_card_item.cpp | 10 +++---- .../src/game/board/abstract_card_item.h | 4 +-- cockatrice/src/game/board/card_item.cpp | 9 +++--- cockatrice/src/game/board/card_item.h | 2 -- cockatrice/src/game/card_dimensions.h | 29 +++++++++++++++++++ cockatrice/src/game/deckview/deck_view.cpp | 22 +++++++------- .../src/game/player/player_graphics_item.cpp | 22 +++++++------- cockatrice/src/game/z_value_layer_manager.h | 8 ++--- cockatrice/src/game/z_values.h | 4 +-- cockatrice/src/game/zones/hand_zone.cpp | 2 +- cockatrice/src/game/zones/pile_zone.cpp | 12 ++++---- cockatrice/src/game/zones/table_zone.cpp | 22 +++++++------- cockatrice/src/game/zones/table_zone.h | 4 +-- cockatrice/src/game/zones/view_zone.cpp | 16 +++++----- .../src/game/zones/view_zone_widget.cpp | 4 +-- .../cards/card_info_display_widget.cpp | 2 +- 18 files changed, 108 insertions(+), 78 deletions(-) create mode 100644 cockatrice/src/game/card_dimensions.h diff --git a/cockatrice/src/game/board/abstract_card_drag_item.cpp b/cockatrice/src/game/board/abstract_card_drag_item.cpp index c9a964048..8e3def4ca 100644 --- a/cockatrice/src/game/board/abstract_card_drag_item.cpp +++ b/cockatrice/src/game/board/abstract_card_drag_item.cpp @@ -8,8 +8,6 @@ #include #include -static const float CARD_WIDTH_HALF = CARD_WIDTH / 2; -static const float CARD_HEIGHT_HALF = CARD_HEIGHT / 2; const QColor GHOST_MASK = QColor(255, 255, 255, 50); AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, @@ -22,16 +20,16 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, setZValue(ZValues::childDragZValue(hotSpot.x(), hotSpot.y())); connect(parentDrag, &QObject::destroyed, this, &AbstractCardDragItem::deleteLater); } else { - hotSpot = QPointF{qBound(0.0, hotSpot.x(), static_cast(CARD_WIDTH - 1)), - qBound(0.0, hotSpot.y(), static_cast(CARD_HEIGHT - 1))}; + hotSpot = QPointF{qBound(0.0, hotSpot.x(), CardDimensions::WIDTH_F - 1), + qBound(0.0, hotSpot.y(), CardDimensions::HEIGHT_F - 1)}; setCursor(Qt::ClosedHandCursor); setZValue(ZValues::DRAG_ITEM); } if (item->getTapped()) setTransform(QTransform() - .translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF) + .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F) .rotate(90) - .translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF)); + .translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F)); setCacheMode(DeviceCoordinateCache); @@ -48,7 +46,7 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, QPainterPath AbstractCardDragItem::shape() const { QPainterPath shape; - qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CARD_WIDTH : 0.0; + qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CardDimensions::WIDTH_F : 0.0; shape.addRoundedRect(boundingRect(), cardCornerRadius, cardCornerRadius); return shape; } diff --git a/cockatrice/src/game/board/abstract_card_drag_item.h b/cockatrice/src/game/board/abstract_card_drag_item.h index 1d741eded..fe3b87983 100644 --- a/cockatrice/src/game/board/abstract_card_drag_item.h +++ b/cockatrice/src/game/board/abstract_card_drag_item.h @@ -34,7 +34,7 @@ public: AbstractCardDragItem(AbstractCardItem *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag = 0); [[nodiscard]] QRectF boundingRect() const override { - return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT); + return QRectF(0, 0, CardDimensions::WIDTH_F, CardDimensions::HEIGHT_F); } [[nodiscard]] QPainterPath shape() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; diff --git a/cockatrice/src/game/board/abstract_card_item.cpp b/cockatrice/src/game/board/abstract_card_item.cpp index f8365c3d6..9ec6ada9a 100644 --- a/cockatrice/src/game/board/abstract_card_item.cpp +++ b/cockatrice/src/game/board/abstract_card_item.cpp @@ -39,13 +39,13 @@ AbstractCardItem::~AbstractCardItem() QRectF AbstractCardItem::boundingRect() const { - return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT); + return QRectF(0, 0, CardDimensions::WIDTH_F, CardDimensions::HEIGHT_F); } QPainterPath AbstractCardItem::shape() const { QPainterPath shape; - qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CARD_WIDTH : 0.0; + qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CardDimensions::WIDTH_F : 0.0; shape.addRoundedRect(boundingRect(), cardCornerRadius, cardCornerRadius); return shape; } @@ -218,7 +218,7 @@ void AbstractCardItem::setHovered(bool _hovered) isHovered = _hovered; setZValue(_hovered ? ZValues::HOVERED_CARD : realZValue); setScale(_hovered && SettingsCache::instance().getScaleCards() ? 1.1 : 1); - setTransformOriginPoint(_hovered ? CARD_WIDTH / 2 : 0, _hovered ? CARD_HEIGHT / 2 : 0); + setTransformOriginPoint(_hovered ? CardDimensions::WIDTH_HALF_F : 0, _hovered ? CardDimensions::HEIGHT_HALF_F : 0); update(); } @@ -274,9 +274,9 @@ void AbstractCardItem::setTapped(bool _tapped, bool canAnimate) else { tapAngle = tapped ? 90 : 0; setTransform(QTransform() - .translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2) + .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F) .rotate(tapAngle) - .translate((float)-CARD_WIDTH / 2, (float)-CARD_HEIGHT / 2)); + .translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F)); update(); } } diff --git a/cockatrice/src/game/board/abstract_card_item.h b/cockatrice/src/game/board/abstract_card_item.h index dad73dc14..7d2c29cae 100644 --- a/cockatrice/src/game/board/abstract_card_item.h +++ b/cockatrice/src/game/board/abstract_card_item.h @@ -8,6 +8,7 @@ #define ABSTRACTCARDITEM_H #include "../../game_graphics/board/graphics_item_type.h" +#include "../card_dimensions.h" #include "arrow_target.h" #include @@ -15,9 +16,6 @@ class Player; -const int CARD_WIDTH = 72; -const int CARD_HEIGHT = 102; - class AbstractCardItem : public ArrowTarget { Q_OBJECT diff --git a/cockatrice/src/game/board/card_item.cpp b/cockatrice/src/game/board/card_item.cpp index 8b9549f07..62de4a02e 100644 --- a/cockatrice/src/game/board/card_item.cpp +++ b/cockatrice/src/game/board/card_item.cpp @@ -367,8 +367,9 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) if (zone->getHasCardAttr()) childPos = card->pos() - pos(); else - childPos = QPointF(childIndex * CARD_WIDTH / 2, 0); - CardDragItem *drag = new CardDragItem(card, card->getId(), childPos, forceFaceDown, dragItem); + childPos = QPointF(childIndex * CardDimensions::WIDTH_HALF_F, 0); + CardDragItem *drag = + new CardDragItem(card, card->getId(), childPos, card->getFaceDown() || forceFaceDown, dragItem); drag->setPos(dragItem->pos() + childPos); scene()->addItem(drag); } @@ -475,9 +476,9 @@ bool CardItem::animationEvent() } setTransform(QTransform() - .translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF) + .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F) .rotate(tapAngle) - .translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF)); + .translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F)); setHovered(false); update(); diff --git a/cockatrice/src/game/board/card_item.h b/cockatrice/src/game/board/card_item.h index 875c3f4d0..da2097a2c 100644 --- a/cockatrice/src/game/board/card_item.h +++ b/cockatrice/src/game/board/card_item.h @@ -21,8 +21,6 @@ class QAction; class QColor; const int MAX_COUNTERS_ON_CARD = 999; -const float CARD_WIDTH_HALF = CARD_WIDTH / 2; -const float CARD_HEIGHT_HALF = CARD_HEIGHT / 2; const int ROTATION_DEGREES_PER_FRAME = 10; class CardItem : public AbstractCardItem diff --git a/cockatrice/src/game/card_dimensions.h b/cockatrice/src/game/card_dimensions.h new file mode 100644 index 000000000..255d0bc04 --- /dev/null +++ b/cockatrice/src/game/card_dimensions.h @@ -0,0 +1,29 @@ +#ifndef CARD_DIMENSIONS_H +#define CARD_DIMENSIONS_H + +#include + +/** + * @file card_dimensions.h + * @brief Canonical card dimension constants for layout and Z-value calculations. + * + * These values represent the logical pixel dimensions of a standard card graphic. + * They are used throughout the game scene for layout, rendering, and Z-value computation. + */ +namespace CardDimensions +{ +/// Card width in pixels +constexpr int WIDTH = 72; +/// Card height in pixels +constexpr int HEIGHT = 102; + +/// Pre-converted for floating-point contexts (Z-value calculations) +constexpr qreal WIDTH_F = static_cast(WIDTH); +constexpr qreal HEIGHT_F = static_cast(HEIGHT); + +/// Half-dimensions for centering and rotation transforms +constexpr qreal WIDTH_HALF_F = WIDTH_F / 2; +constexpr qreal HEIGHT_HALF_F = HEIGHT_F / 2; +} // namespace CardDimensions + +#endif // CARD_DIMENSIONS_H diff --git a/cockatrice/src/game/deckview/deck_view.cpp b/cockatrice/src/game/deckview/deck_view.cpp index 383545cf3..620dfaa5f 100644 --- a/cockatrice/src/game/deckview/deck_view.cpp +++ b/cockatrice/src/game/deckview/deck_view.cpp @@ -95,8 +95,9 @@ void DeckViewCard::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti pen.setJoinStyle(Qt::MiterJoin); pen.setColor(originZone == DECK_ZONE_MAIN ? Qt::green : Qt::red); painter->setPen(pen); - qreal cardRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * (CARD_WIDTH - 3) : 0.0; - painter->drawRoundedRect(QRectF(1.5, 1.5, CARD_WIDTH - 3., CARD_HEIGHT - 3.), cardRadius, cardRadius); + qreal cardRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * (CardDimensions::WIDTH_F - 3) : 0.0; + painter->drawRoundedRect(QRectF(1.5, 1.5, CardDimensions::WIDTH_F - 3, CardDimensions::HEIGHT_F - 3), cardRadius, + cardRadius); painter->restore(); } @@ -122,7 +123,7 @@ void DeckViewCard::mouseMoveEvent(QGraphicsSceneMouseEvent *event) if (c == this) continue; ++j; - auto childPos = QPointF(j * CARD_WIDTH / 2, 0); + auto childPos = QPointF(j * CardDimensions::WIDTH_HALF_F, 0); auto *drag = new DeckViewCardDragItem(c, childPos, dragItem); drag->setPos(dragItem->pos() + childPos); scene()->addItem(drag); @@ -204,7 +205,7 @@ void DeckViewCardContainer::paint(QPainter *painter, const QStyleOptionGraphicsI painter->setPen(QColor(255, 255, 255, 100)); painter->drawLine(QPointF(0, yUntilNow - paddingY / 2), QPointF(width, yUntilNow - paddingY / 2)); } - qreal thisRowHeight = CARD_HEIGHT * currentRowsAndCols[i].first; + qreal thisRowHeight = CardDimensions::HEIGHT_F * currentRowsAndCols[i].first; QRectF textRect(0, yUntilNow, totalTextWidth, thisRowHeight); yUntilNow += thisRowHeight + paddingY; @@ -260,9 +261,9 @@ QSizeF DeckViewCardContainer::calculateBoundingRect(const QList> // Calculate space needed for cards for (int i = 0; i < rowsAndCols.size(); ++i) { - totalHeight += CARD_HEIGHT * rowsAndCols[i].first + paddingY; - if (CARD_WIDTH * rowsAndCols[i].second > totalWidth) - totalWidth = CARD_WIDTH * rowsAndCols[i].second; + totalHeight += CardDimensions::HEIGHT_F * rowsAndCols[i].first + paddingY; + if (CardDimensions::WIDTH_F * rowsAndCols[i].second > totalWidth) + totalWidth = CardDimensions::WIDTH_F * rowsAndCols[i].second; } return QSizeF(getCardTypeTextWidth() + totalWidth, totalHeight + separatorY + paddingY); @@ -289,9 +290,10 @@ void DeckViewCardContainer::rearrangeItems(const QList> &rowsAnd std::sort(row.begin(), row.end(), DeckViewCardContainer::sortCardsByName); for (int j = 0; j < row.size(); ++j) { DeckViewCard *card = row[j]; - card->setPos(x + (j % tempCols) * CARD_WIDTH, yUntilNow + (j / tempCols) * CARD_HEIGHT); + card->setPos(x + (j % tempCols) * CardDimensions::WIDTH_F, + yUntilNow + (j / tempCols) * CardDimensions::HEIGHT_F); } - yUntilNow += tempRows * CARD_HEIGHT + paddingY; + yUntilNow += tempRows * CardDimensions::HEIGHT_F + paddingY; } prepareGeometryChange(); @@ -392,7 +394,7 @@ void DeckViewScene::applySideboardPlan(const QList &plan) void DeckViewScene::rearrangeItems() { - const int spacing = CARD_HEIGHT / 3; + const int spacing = CardDimensions::HEIGHT / 3; QList contList = cardContainers.values(); // Initialize space requirements diff --git a/cockatrice/src/game/player/player_graphics_item.cpp b/cockatrice/src/game/player/player_graphics_item.cpp index 30648c926..bcc4b7f72 100644 --- a/cockatrice/src/game/player/player_graphics_item.cpp +++ b/cockatrice/src/game/player/player_graphics_item.cpp @@ -19,7 +19,8 @@ PlayerGraphicsItem::PlayerGraphicsItem(Player *_player) : player(_player) playerArea = new PlayerArea(this); playerTarget = new PlayerTarget(player, playerArea); - qreal avatarMargin = (counterAreaWidth + CARD_HEIGHT + 15 - playerTarget->boundingRect().width()) / 2.0; + qreal avatarMargin = + (counterAreaWidth + CardDimensions::HEIGHT_F + 15 - playerTarget->boundingRect().width()) / 2.0; playerTarget->setPos(QPointF(avatarMargin, avatarMargin)); initializeZones(); @@ -55,8 +56,9 @@ void PlayerGraphicsItem::onPlayerActiveChanged(bool _active) void PlayerGraphicsItem::initializeZones() { deckZoneGraphicsItem = new PileZone(player->getDeckZone(), this); - auto base = QPointF(counterAreaWidth + (CARD_HEIGHT - CARD_WIDTH + 15) / 2.0, - 10 + playerTarget->boundingRect().height() + 5 - (CARD_HEIGHT - CARD_WIDTH) / 2.0); + auto base = QPointF(counterAreaWidth + (CardDimensions::HEIGHT_F - CardDimensions::WIDTH_F + 15) / 2.0, + 10 + playerTarget->boundingRect().height() + 5 - + (CardDimensions::HEIGHT_F - CardDimensions::WIDTH_F) / 2.0); deckZoneGraphicsItem->setPos(base); qreal h = deckZoneGraphicsItem->boundingRect().width() + 5; @@ -95,7 +97,7 @@ QRectF PlayerGraphicsItem::boundingRect() const qreal PlayerGraphicsItem::getMinimumWidth() const { - qreal result = tableZoneGraphicsItem->getMinimumWidth() + CARD_HEIGHT + 15 + counterAreaWidth + + qreal result = tableZoneGraphicsItem->getMinimumWidth() + CardDimensions::HEIGHT_F + 15 + counterAreaWidth + stackZoneGraphicsItem->boundingRect().width(); if (!SettingsCache::instance().getHorizontalHand()) { result += handZoneGraphicsItem->boundingRect().width(); @@ -112,8 +114,8 @@ void PlayerGraphicsItem::paint(QPainter * /*painter*/, void PlayerGraphicsItem::processSceneSizeChange(int newPlayerWidth) { // Extend table (and hand, if horizontal) to accommodate the new player width. - qreal tableWidth = - newPlayerWidth - CARD_HEIGHT - 15 - counterAreaWidth - stackZoneGraphicsItem->boundingRect().width(); + qreal tableWidth = newPlayerWidth - CardDimensions::HEIGHT_F - 15 - counterAreaWidth - + stackZoneGraphicsItem->boundingRect().width(); if (!SettingsCache::instance().getHorizontalHand()) { tableWidth -= handZoneGraphicsItem->boundingRect().width(); } @@ -152,7 +154,7 @@ void PlayerGraphicsItem::rearrangeCounters() void PlayerGraphicsItem::rearrangeZones() { - auto base = QPointF(CARD_HEIGHT + counterAreaWidth + 15, 0); + auto base = QPointF(CardDimensions::HEIGHT_F + counterAreaWidth + 15, 0); if (SettingsCache::instance().getHorizontalHand()) { if (mirrored) { if (player->getHandZone()->contentsKnown()) { @@ -203,7 +205,7 @@ void PlayerGraphicsItem::rearrangeZones() void PlayerGraphicsItem::updateBoundingRect() { prepareGeometryChange(); - qreal width = CARD_HEIGHT + 15 + counterAreaWidth + stackZoneGraphicsItem->boundingRect().width(); + qreal width = CardDimensions::HEIGHT_F + 15 + counterAreaWidth + stackZoneGraphicsItem->boundingRect().width(); if (SettingsCache::instance().getHorizontalHand()) { qreal handHeight = player->getPlayerInfo()->getHandVisible() ? handZoneGraphicsItem->boundingRect().height() : 0; @@ -214,7 +216,7 @@ void PlayerGraphicsItem::updateBoundingRect() 0, 0, width + handZoneGraphicsItem->boundingRect().width() + tableZoneGraphicsItem->boundingRect().width(), tableZoneGraphicsItem->boundingRect().height()); } - playerArea->setSize(CARD_HEIGHT + counterAreaWidth + 15, bRect.height()); + playerArea->setSize(CardDimensions::HEIGHT_F + counterAreaWidth + 15, bRect.height()); emit sizeChanged(); -} \ No newline at end of file +} diff --git a/cockatrice/src/game/z_value_layer_manager.h b/cockatrice/src/game/z_value_layer_manager.h index 303edb8a5..4eb864486 100644 --- a/cockatrice/src/game/z_value_layer_manager.h +++ b/cockatrice/src/game/z_value_layer_manager.h @@ -15,7 +15,7 @@ * - Cards within zones (1.0 base + index) * * 2. **Card Layer (1-40,000,000)**: Dynamic card rendering on the table zone - * - Cards use formula: (actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100 + * - Cards use formula: (actualY + CardDimensions::HEIGHT) * 100000 + (actualX + 1) * 100 * - Maximum card Z-value: ~40,000,000 (with 3 rows, actualY <= ~289) * * 3. **Overlay Layer (2,000,000,000+)**: UI elements that must appear above all cards @@ -77,9 +77,9 @@ enum class Layer /** * @brief Maximum Z-value a card can have on the table zone. * - * Based on table zone formula: (actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100 - * With maximum 3 rows and CARD_HEIGHT ~96, actualY <= ~289. - * Maximum: (289 + 96) * 100000 + 100 * 100 = 38,510,000 + * Based on table zone formula: (actualY + CardDimensions::HEIGHT) * 100000 + (actualX + 1) * 100 + * With maximum 3 rows and CardDimensions::HEIGHT = 102, actualY <= ~289. + * Maximum: (289 + 102) * 100000 + 100 * 100 = 39,110,000 * * We use 40,000,000 as a safe upper bound with margin. */ diff --git a/cockatrice/src/game/z_values.h b/cockatrice/src/game/z_values.h index 2c7fe9066..c6e7f2c8a 100644 --- a/cockatrice/src/game/z_values.h +++ b/cockatrice/src/game/z_values.h @@ -1,6 +1,7 @@ #ifndef Z_VALUES_H #define Z_VALUES_H +#include "card_dimensions.h" #include "z_value_layer_manager.h" /** @@ -70,8 +71,7 @@ constexpr qreal TOP_UI = ZValueLayerManager::overlayZValue(7.0); */ [[nodiscard]] constexpr qreal tableCardZValue(qreal x, qreal y) { - constexpr qreal CARD_HEIGHT_FOR_Z = 102.0; - return (y + CARD_HEIGHT_FOR_Z) * 100000.0 + (x + 1) * 100.0; + return (y + CardDimensions::HEIGHT_F) * 100000.0 + (x + 1) * 100.0; } // Card layering (general architecture, not command-zone specific) diff --git a/cockatrice/src/game/zones/hand_zone.cpp b/cockatrice/src/game/zones/hand_zone.cpp index cc3b44910..7badfcca4 100644 --- a/cockatrice/src/game/zones/hand_zone.cpp +++ b/cockatrice/src/game/zones/hand_zone.cpp @@ -56,7 +56,7 @@ void HandZone::handleDropEvent(const QList &dragItems, QRectF HandZone::boundingRect() const { if (SettingsCache::instance().getHorizontalHand()) - return QRectF(0, 0, width, CARD_HEIGHT + 10); + return QRectF(0, 0, width, CardDimensions::HEIGHT_F + 10); else return QRectF(0, 0, 100, zoneHeight); } diff --git a/cockatrice/src/game/zones/pile_zone.cpp b/cockatrice/src/game/zones/pile_zone.cpp index 87a60fc03..d85b5f4e2 100644 --- a/cockatrice/src/game/zones/pile_zone.cpp +++ b/cockatrice/src/game/zones/pile_zone.cpp @@ -19,9 +19,9 @@ PileZone::PileZone(PileZoneLogic *_logic, QGraphicsItem *parent) : CardZone(_log setCursor(Qt::OpenHandCursor); setTransform(QTransform() - .translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2) + .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F) .rotate(90) - .translate((float)-CARD_WIDTH / 2, (float)-CARD_HEIGHT / 2)); + .translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F)); connect(&SettingsCache::instance(), &SettingsCache::roundCardCornersChanged, this, [this](bool _roundCardCorners) { Q_UNUSED(_roundCardCorners); @@ -33,13 +33,13 @@ PileZone::PileZone(PileZoneLogic *_logic, QGraphicsItem *parent) : CardZone(_log QRectF PileZone::boundingRect() const { - return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT); + return QRectF(0, 0, CardDimensions::WIDTH_F, CardDimensions::HEIGHT_F); } QPainterPath PileZone::shape() const { QPainterPath shape; - qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CARD_WIDTH : 0.0; + qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CardDimensions::WIDTH_F : 0.0; shape.addRoundedRect(boundingRect(), cardCornerRadius, cardCornerRadius); return shape; } @@ -52,9 +52,9 @@ void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio getLogic()->getCards().at(0)->paintPicture(painter, getLogic()->getCards().at(0)->getTranslatedSize(painter), 90); - painter->translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2); + painter->translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F); painter->rotate(-90); - painter->translate((float)-CARD_WIDTH / 2, (float)-CARD_HEIGHT / 2); + painter->translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F); paintNumberEllipse(getLogic()->getCards().size(), 28, Qt::white, -1, -1, painter); } diff --git a/cockatrice/src/game/zones/table_zone.cpp b/cockatrice/src/game/zones/table_zone.cpp index 33c867c43..a020e4255 100644 --- a/cockatrice/src/game/zones/table_zone.cpp +++ b/cockatrice/src/game/zones/table_zone.cpp @@ -31,7 +31,7 @@ TableZone::TableZone(TableZoneLogic *_logic, QGraphicsItem *parent) : SelectZone updateBg(); - height = MARGIN_TOP + MARGIN_BOTTOM + TABLEROWS * CARD_HEIGHT + (TABLEROWS - 1) * PADDING_Y; + height = MARGIN_TOP + MARGIN_BOTTOM + TABLEROWS * CardDimensions::HEIGHT + (TABLEROWS - 1) * PADDING_Y; width = MIN_WIDTH; currentMinimumWidth = width; @@ -106,7 +106,7 @@ void TableZone::paintLandDivider(QPainter *painter) { // Place the line 2 grid heights down then back it off just enough to allow // some space between a 3-card stack and the land area. - qreal separatorY = MARGIN_TOP + 2 * (CARD_HEIGHT + PADDING_Y) - STACKED_CARD_OFFSET_Y / 2; + qreal separatorY = MARGIN_TOP + 2 * (CardDimensions::HEIGHT + PADDING_Y) - STACKED_CARD_OFFSET_Y / 2; if (isInverted()) separatorY = height - separatorY; painter->setPen(QColor(255, 255, 255, 40)); @@ -232,7 +232,7 @@ void TableZone::resizeToContents() // Minimum width is the rightmost card position plus enough room for // another card with padding, then margin. - currentMinimumWidth = xMax + (2 * CARD_WIDTH) + PADDING_X + MARGIN_RIGHT; + currentMinimumWidth = xMax + (2 * CardDimensions::WIDTH) + PADDING_X + MARGIN_RIGHT; if (currentMinimumWidth < MIN_WIDTH) currentMinimumWidth = MIN_WIDTH; @@ -282,10 +282,10 @@ void TableZone::computeCardStackWidths() const int key = getCardStackMapKey(gridPoint.x() / 3, gridPoint.y()); const int stackCount = cardStackCount.value(key, 0); if (stackCount == 1) - cardStackWidth.insert(key, CARD_WIDTH + getLogic()->getCards()[i]->getAttachedCards().size() * - STACKED_CARD_OFFSET_X); + cardStackWidth.insert(key, CardDimensions::WIDTH + getLogic()->getCards()[i]->getAttachedCards().size() * + STACKED_CARD_OFFSET_X); else - cardStackWidth.insert(key, CARD_WIDTH + (stackCount - 1) * STACKED_CARD_OFFSET_X); + cardStackWidth.insert(key, CardDimensions::WIDTH + (stackCount - 1) * STACKED_CARD_OFFSET_X); } } @@ -299,7 +299,7 @@ QPointF TableZone::mapFromGrid(QPoint gridPoint) const // Add in width of card stack plus padding for each column for (int i = 0; i < gridPoint.x() / 3; ++i) { const int key = getCardStackMapKey(i, gridPoint.y()); - x += cardStackWidth.value(key, CARD_WIDTH) + PADDING_X; + x += cardStackWidth.value(key, CardDimensions::WIDTH) + PADDING_X; } if (isInverted()) @@ -310,7 +310,7 @@ QPointF TableZone::mapFromGrid(QPoint gridPoint) const // Add in card size and padding for each row for (int i = 0; i < gridPoint.y(); ++i) - y += CARD_HEIGHT + PADDING_Y; + y += CardDimensions::HEIGHT + PADDING_Y; return QPointF(x, y); } @@ -324,7 +324,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const int y = mapPoint.y() - MARGIN_TOP; // Below calculation effectively rounds to the nearest grid point. - const int gridPointHeight = CARD_HEIGHT + PADDING_Y; + const int gridPointHeight = CardDimensions::HEIGHT + PADDING_Y; int gridPointY = (y + PADDING_Y / 2) / gridPointHeight; gridPointY = clampValidTableRow(gridPointY); @@ -340,7 +340,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const // Maximum value is a card width from the right margin, referenced to the // grid area. - const int xMax = width - MARGIN_LEFT - MARGIN_RIGHT - CARD_WIDTH; + const int xMax = width - MARGIN_LEFT - MARGIN_RIGHT - CardDimensions::WIDTH; int xStack = 0; int xNextStack = 0; @@ -348,7 +348,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const while ((xNextStack <= x) && (xNextStack <= xMax)) { xStack = xNextStack; const int key = getCardStackMapKey(nextStackCol, gridPointY); - xNextStack += cardStackWidth.value(key, CARD_WIDTH) + PADDING_X; + xNextStack += cardStackWidth.value(key, CardDimensions::WIDTH) + PADDING_X; nextStackCol++; } int stackCol = qMax(nextStackCol - 1, 0); diff --git a/cockatrice/src/game/zones/table_zone.h b/cockatrice/src/game/zones/table_zone.h index 3b353e76c..61eb48d7b 100644 --- a/cockatrice/src/game/zones/table_zone.h +++ b/cockatrice/src/game/zones/table_zone.h @@ -41,12 +41,12 @@ private: /* Minimum width of the table zone including margins. */ - static const int MIN_WIDTH = MARGIN_LEFT + (5 * CARD_WIDTH) + MARGIN_RIGHT; + static const int MIN_WIDTH = MARGIN_LEFT + (5 * CardDimensions::WIDTH) + MARGIN_RIGHT; /* Offset sizes when cards are stacked on each other in the grid */ - static const int STACKED_CARD_OFFSET_X = CARD_WIDTH / 3; + static const int STACKED_CARD_OFFSET_X = CardDimensions::WIDTH / 3; static const int STACKED_CARD_OFFSET_Y = PADDING_Y / 3; /* diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp index 348ed6e87..d2fd1e971 100644 --- a/cockatrice/src/game/zones/view_zone.cpp +++ b/cockatrice/src/game/zones/view_zone.cpp @@ -118,7 +118,9 @@ void ZoneViewZone::zoneDumpReceived(const Response &r) qobject_cast(getLogic())->updateCardIds(ZoneViewZoneLogic::INITIALIZE); reorganizeCards(); - emit getLogic()->cardCountChanged(); + // clang-format off + emit getLogic()->cardCountChanged(); // emit keyword causes spurious spacing around -> + // clang-format on } // Because of boundingRect(), this function must not be called before the zone was added to a scene. @@ -166,8 +168,8 @@ void ZoneViewZone::reorganizeCards() // determine bounding rect qreal aleft = 0; qreal atop = 0; - qreal awidth = gridSize.cols * CARD_WIDTH + (CARD_WIDTH / 2) + HORIZONTAL_PADDING; - qreal aheight = (gridSize.rows * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3; + qreal awidth = gridSize.cols * CardDimensions::WIDTH_F + CardDimensions::WIDTH_HALF_F + HORIZONTAL_PADDING; + qreal aheight = (gridSize.rows * CardDimensions::HEIGHT_F) / 3 + CardDimensions::HEIGHT_F * 1.3; optimumRect = QRectF(aleft, atop, awidth, aheight); updateGeometry(); @@ -210,8 +212,8 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca } lastColumnProp = columnProp; - qreal x = col * CARD_WIDTH; - qreal y = row * CARD_HEIGHT / 3; + qreal x = col * CardDimensions::WIDTH_F; + qreal y = row * CardDimensions::HEIGHT_F / 3; c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); c->setRealZValue(i); longestRow = qMax(row, longestRow); @@ -238,8 +240,8 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca for (int i = 0; i < cardCount; i++) { CardItem *c = cards.at(i); - qreal x = (i / rows) * CARD_WIDTH; - qreal y = (i % rows) * CARD_HEIGHT / 3; + qreal x = (i / rows) * CardDimensions::WIDTH_F; + qreal y = (i % rows) * CardDimensions::HEIGHT_F / 3; c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); c->setRealZValue(i); } diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index 0ad3b10f1..23d7d6a19 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -458,7 +458,7 @@ void ZoneViewWidget::resizeScrollbar(const qreal newZoneHeight) */ static qreal rowsToHeight(int rows) { - const qreal cardsHeight = (rows + 1) * (CARD_HEIGHT / 3); + const qreal cardsHeight = (rows + 1) * (CardDimensions::HEIGHT_F / 3); return cardsHeight + 5; // +5 padding to make the cutoff look nicer } @@ -574,4 +574,4 @@ void ZoneViewWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) if (event->pos().y() <= 0) { expandWindow(); } -} \ No newline at end of file +} diff --git a/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp b/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp index f8b581e33..4930fbbfd 100644 --- a/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp @@ -10,7 +10,7 @@ #include CardInfoDisplayWidget::CardInfoDisplayWidget(const CardRef &cardRef, QWidget *parent, Qt::WindowFlags flags) - : QFrame(parent, flags), aspectRatio((qreal)CARD_HEIGHT / (qreal)CARD_WIDTH) + : QFrame(parent, flags), aspectRatio(CardDimensions::HEIGHT_F / CardDimensions::WIDTH_F) { setContentsMargins(3, 3, 3, 3); pic = new CardInfoPictureWidget(); From dead99363909f1b010ce4d468dc311d0a84218a1 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Fri, 6 Mar 2026 10:39:04 -0800 Subject: [PATCH 14/28] [DeckList] Refactor load from plaintext to take normalizer as param (#6664) * [DeckList] Refactor load from plaintext to take normalizer as param * update usages * weaken unit test * weaken unit test more * revert unit test * move CardNameNormalizer to libcockatrice_card * update unit test * formatting --- .../parsers/interface_json_deck_parser.h | 7 +-- .../src/interface/deck_loader/deck_loader.cpp | 5 ++- .../dialogs/dlg_load_deck_from_clipboard.cpp | 3 +- .../dialogs/dlg_load_deck_from_website.cpp | 3 +- .../dialogs/dlg_load_deck_from_website.h | 1 + ...idekt_api_response_deck_display_widget.cpp | 5 +-- .../average_deck/edhrec_deck_api_response.cpp | 5 +-- libcockatrice_card/CMakeLists.txt | 2 + .../card/import/card_name_normalizer.cpp | 45 +++++++++++++++++++ .../card/import/card_name_normalizer.h | 15 +++++++ .../libcockatrice/deck_list/deck_list.cpp | 43 ++++-------------- .../libcockatrice/deck_list/deck_list.h | 6 ++- tests/loading_from_clipboard/CMakeLists.txt | 3 +- .../clipboard_testing.cpp | 3 +- 14 files changed, 94 insertions(+), 52 deletions(-) create mode 100644 libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp create mode 100644 libcockatrice_card/libcockatrice/card/import/card_name_normalizer.h diff --git a/cockatrice/src/client/network/parsers/interface_json_deck_parser.h b/cockatrice/src/client/network/parsers/interface_json_deck_parser.h index e19e4f4e4..1818aa35c 100644 --- a/cockatrice/src/client/network/parsers/interface_json_deck_parser.h +++ b/cockatrice/src/client/network/parsers/interface_json_deck_parser.h @@ -8,10 +8,11 @@ #define INTERFACE_JSON_DECK_PARSER_H #include "../../../interface/deck_loader/card_node_function.h" -#include "../../../interface/deck_loader/deck_loader.h" #include #include +#include +#include class IJsonDeckParser { @@ -49,7 +50,7 @@ public: outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n'; } - deckList.loadFromStream_Plain(outStream, false); + deckList.loadFromStream_Plain(outStream, false, CardNameNormalizer()); deckList.forEachCard(CardNodeFunction::ResolveProviderId()); return deckList; @@ -96,7 +97,7 @@ public: outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n'; } - deckList.loadFromStream_Plain(outStream, false); + deckList.loadFromStream_Plain(outStream, false, CardNameNormalizer()); deckList.forEachCard(CardNodeFunction::ResolveProviderId()); QJsonObject commandersObj = obj.value("commanders").toObject(); diff --git a/cockatrice/src/interface/deck_loader/deck_loader.cpp b/cockatrice/src/interface/deck_loader/deck_loader.cpp index 3481e245b..d71dca24a 100644 --- a/cockatrice/src/interface/deck_loader/deck_loader.cpp +++ b/cockatrice/src/interface/deck_loader/deck_loader.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -42,7 +43,7 @@ DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fmt, bo DeckList deckList; switch (fmt) { case DeckFileFormat::PlainText: - result = deckList.loadFromFile_Plain(&file); + result = deckList.loadFromFile_Plain(&file, CardNameNormalizer()); break; case DeckFileFormat::Cockatrice: { result = deckList.loadFromFile_Native(&file); @@ -50,7 +51,7 @@ DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fmt, bo qCInfo(DeckLoaderLog) << "Failed to load " << fileName << "as cockatrice format; retrying as plain format"; file.seek(0); - result = deckList.loadFromFile_Plain(&file); + result = deckList.loadFromFile_Plain(&file, CardNameNormalizer()); fmt = DeckFileFormat::PlainText; } break; diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp index b72130c81..9ceb35d78 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp @@ -14,6 +14,7 @@ #include #include #include +#include /** * Creates the main layout and connects the signals that are common to all versions of this window @@ -81,7 +82,7 @@ bool AbstractDlgDeckTextEdit::loadIntoDeck(DeckList &deckList) const QTextStream stream(&buffer); - if (deckList.loadFromStream_Plain(stream, true)) { + if (deckList.loadFromStream_Plain(stream, true, CardNameNormalizer())) { if (loadSetNameAndNumberCheckBox->isChecked()) { deckList.forEachCard(CardNodeFunction::ResolveProviderId()); } else { diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp index da4135246..ba00873d9 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include DlgLoadDeckFromWebsite::DlgLoadDeckFromWebsite(QWidget *parent) : QDialog(parent) @@ -99,7 +100,7 @@ void DlgLoadDeckFromWebsite::accept() // Parse the plain text deck here DeckList deckList; QTextStream stream(&deckText); - deckList.loadFromStream_Plain(stream, false); + deckList.loadFromStream_Plain(stream, false, CardNameNormalizer()); deckList.forEachCard(CardNodeFunction::ResolveProviderId()); deck = deckList; diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h index fe6f0a7e9..1ac98d206 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp index f40b9094f..8b17cd49e 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp @@ -2,7 +2,6 @@ #include "../../../../../deck_loader/card_node_function.h" #include "../../../../../deck_loader/deck_loader.h" -#include "../../../../cards/card_info_picture_with_text_overlay_widget.h" #include "../../../../cards/card_size_widget.h" #include "../../../../cards/deck_card_zone_display_widget.h" #include "../../../../visual_deck_editor/visual_deck_display_options_widget.h" @@ -10,7 +9,7 @@ #include "../api_response/deck/archidekt_api_response_deck.h" #include -#include +#include ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWidget *parent, ArchidektApiResponseDeck _response, @@ -80,7 +79,7 @@ ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWi connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset); auto decklist = QSharedPointer(new DeckList); - decklist->loadFromStream_Plain(deckStream, false); + decklist->loadFromStream_Plain(deckStream, false, CardNameNormalizer()); model->setDeckList(decklist); model->forEachCard(CardNodeFunction::ResolveProviderId()); diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp index a744a9b14..1cb070c30 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp @@ -1,11 +1,10 @@ #include "edhrec_deck_api_response.h" -#include "../../../../../../deck_loader/deck_loader.h" - #include #include #include #include +#include void EdhrecDeckApiResponse::fromJson(const QJsonArray &json) { @@ -15,7 +14,7 @@ void EdhrecDeckApiResponse::fromJson(const QJsonArray &json) } QTextStream stream(&deckList); - deck.loadFromStream_Plain(stream, true); + deck.loadFromStream_Plain(stream, true, CardNameNormalizer()); } void EdhrecDeckApiResponse::debugPrint() const diff --git a/libcockatrice_card/CMakeLists.txt b/libcockatrice_card/CMakeLists.txt index f516cde00..dd3799e33 100644 --- a/libcockatrice_card/CMakeLists.txt +++ b/libcockatrice_card/CMakeLists.txt @@ -12,6 +12,7 @@ set(HEADERS libcockatrice/card/database/parser/card_database_parser.h libcockatrice/card/database/parser/cockatrice_xml_3.h libcockatrice/card/database/parser/cockatrice_xml_4.h + libcockatrice/card/import/card_name_normalizer.h libcockatrice/card/printing/exact_card.h libcockatrice/card/printing/printing_info.h libcockatrice/card/set/card_set.h @@ -36,6 +37,7 @@ add_library( libcockatrice/card/database/parser/card_database_parser.cpp libcockatrice/card/database/parser/cockatrice_xml_3.cpp libcockatrice/card/database/parser/cockatrice_xml_4.cpp + libcockatrice/card/import/card_name_normalizer.cpp libcockatrice/card/printing/exact_card.cpp libcockatrice/card/printing/printing_info.cpp libcockatrice/card/relation/card_relation.cpp diff --git a/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp new file mode 100644 index 000000000..2349dc3e8 --- /dev/null +++ b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp @@ -0,0 +1,45 @@ +#include "card_name_normalizer.h" + +#include + +QString CardNameNormalizer::operator()(const QString &cardNameString) const +{ + QString cardName = cardNameString; + + // Regex for advanced card parsing + static const QRegularExpression reSplitCard(R"( ?\/\/ ?)"); + static const QRegularExpression reBrace(R"( ?[\[\{][^\]\}]*[\]\}] ?)"); // not nested + static const QRegularExpression reRoundBrace(R"(^\([^\)]*\) ?)"); // () are only matched at start of string + static const QRegularExpression reDigitBrace(R"( ?\(\d*\) ?)"); // () are matched if containing digits + static const QRegularExpression reBraceDigit( + R"( ?\([\dA-Z]+\) *\d+$)"); // () are matched if containing setcode then a number + static const QRegularExpression reDoubleFacedMarker(R"( ?\(Transform\) ?)"); + + static const QHash differences{{QRegularExpression("’"), "'"}, + {QRegularExpression("Æ"), "Ae"}, + {QRegularExpression("æ"), "ae"}, + {QRegularExpression(" ?[|/]+ ?"), " // "}}; + + // Handle advanced card types + if (cardName.contains(reSplitCard)) { + cardName = cardName.split(reSplitCard).join(" // "); + } + + if (cardName.contains(reDoubleFacedMarker)) { + QStringList faces = cardName.split(reDoubleFacedMarker); + cardName = faces.first().trimmed(); + } + + // Remove unnecessary characters + cardName.remove(reBrace); + cardName.remove(reRoundBrace); // I'll be entirely honest here, these are split to accommodate just three cards + cardName.remove(reDigitBrace); // from un-sets that have a word in between round braces at the end + cardName.remove(reBraceDigit); // very specific format with the set code in () and collectors number after + + // Normalize characters + for (auto diff = differences.constBegin(); diff != differences.constEnd(); ++diff) { + cardName.replace(diff.key(), diff.value()); + } + + return cardName; +} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.h b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.h new file mode 100644 index 000000000..716e7da80 --- /dev/null +++ b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.h @@ -0,0 +1,15 @@ +#ifndef COCKATRICE_CARD_NAME_NORMALIZER_H +#define COCKATRICE_CARD_NAME_NORMALIZER_H + +#include + +/** + * Functor that normalizes the raw card name parsed during a plaintext deck import into the card name that Cockatrice + * uses. + */ +struct CardNameNormalizer +{ + QString operator()(const QString &cardNameString) const; +}; + +#endif // COCKATRICE_CARD_NAME_NORMALIZER_H diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp index 3d1070f15..e3e7b41c0 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp @@ -199,9 +199,12 @@ bool DeckList::saveToFile_Native(QIODevice *device) const * * @param in The text to load * @param preserveMetadata If true, don't clear the existing metadata + * @param cardNameNormalizer Function that takes the parsed card name string in the text and * @return False if the input was empty, true otherwise. */ -bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) +bool DeckList::loadFromStream_Plain(QTextStream &in, + bool preserveMetadata, + const std::function &cardNameNormalizer) { const QRegularExpression reCardLine(R"(^\s*[\w\[\(\{].*$)", QRegularExpression::UseUnicodePropertiesOption); const QRegularExpression reEmpty("^\\s*$"); @@ -213,23 +216,11 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) // Regex for advanced card parsing const QRegularExpression reMultiplier(R"(^[xX\(\[]*(\d+)[xX\*\)\]]* ?(.+))"); - const QRegularExpression reSplitCard(R"( ?\/\/ ?)"); - const QRegularExpression reBrace(R"( ?[\[\{][^\]\}]*[\]\}] ?)"); // not nested - const QRegularExpression reRoundBrace(R"(^\([^\)]*\) ?)"); // () are only matched at start of string - const QRegularExpression reDigitBrace(R"( ?\(\d*\) ?)"); // () are matched if containing digits - const QRegularExpression reBraceDigit( - R"( ?\([\dA-Z]+\) *\d+$)"); // () are matched if containing setcode then a number - const QRegularExpression reDoubleFacedMarker(R"( ?\(Transform\) ?)"); // Regex for extracting set code and collector number with attached symbols const QRegularExpression reHyphenFormat(R"(\((\w{3,})\)\s+(\w{3,})-(\d+[^\w\s]*))"); const QRegularExpression reRegularFormat(R"(\((\w{3,})\)\s+(\d+[^\w\s]*))"); - const QHash differences{{QRegularExpression("’"), QString("'")}, - {QRegularExpression("Æ"), QString("Ae")}, - {QRegularExpression("æ"), QString("ae")}, - {QRegularExpression(" ?[|/]+ ?"), QString(" // ")}}; - cleanList(preserveMetadata); auto inputs = in.readAll().trimmed().split('\n'); @@ -355,26 +346,8 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) cardName = match.captured(2); } - // Handle advanced card types - if (cardName.contains(reSplitCard)) { - cardName = cardName.split(reSplitCard).join(" // "); - } - - if (cardName.contains(reDoubleFacedMarker)) { - QStringList faces = cardName.split(reDoubleFacedMarker); - cardName = faces.first().trimmed(); - } - - // Remove unnecessary characters - cardName.remove(reBrace); - cardName.remove(reRoundBrace); // I'll be entirely honest here, these are split to accommodate just three cards - cardName.remove(reDigitBrace); // from un-sets that have a word in between round braces at the end - cardName.remove(reBraceDigit); // very specific format with the set code in () and collectors number after - - // Normalize names - for (auto diff = differences.constBegin(); diff != differences.constEnd(); ++diff) { - cardName.replace(diff.key(), diff.value()); - } + // Normalize the card name + cardName = cardNameNormalizer(cardName); // Determine the zone (mainboard/sideboard) QString zoneName = sideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; @@ -387,10 +360,10 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) return true; } -bool DeckList::loadFromFile_Plain(QIODevice *device) +bool DeckList::loadFromFile_Plain(QIODevice *device, const std::function &cardNameNormalizer) { QTextStream in(device); - return loadFromStream_Plain(in, false); + return loadFromStream_Plain(in, false, cardNameNormalizer); } bool DeckList::saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards) const diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h index 91e56fc9f..a96adeb38 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h @@ -206,8 +206,10 @@ public: /// @name Serialization (Plain text) ///@{ - bool loadFromStream_Plain(QTextStream &stream, bool preserveMetadata); - bool loadFromFile_Plain(QIODevice *device); + bool loadFromStream_Plain(QTextStream &stream, + bool preserveMetadata, + const std::function &cardNameNormalizer); + bool loadFromFile_Plain(QIODevice *device, const std::function &cardNameNormalizer); bool saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards) const; bool saveToFile_Plain(QIODevice *device, bool prefixSideboardCards = true, bool slashTappedOutSplitCards = false) const; diff --git a/tests/loading_from_clipboard/CMakeLists.txt b/tests/loading_from_clipboard/CMakeLists.txt index 40e85c66e..eecaafcbb 100644 --- a/tests/loading_from_clipboard/CMakeLists.txt +++ b/tests/loading_from_clipboard/CMakeLists.txt @@ -10,6 +10,7 @@ set(TEST_QT_MODULES ${COCKATRICE_QT_VERSION_NAME}::Concurrent ${COCKATRICE_QT_VE ) target_link_libraries( - loading_from_clipboard_test libcockatrice_deck_list Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} + loading_from_clipboard_test libcockatrice_deck_list libcockatrice_card Threads::Threads ${GTEST_BOTH_LIBRARIES} + ${TEST_QT_MODULES} ) add_test(NAME loading_from_clipboard_test COMMAND loading_from_clipboard_test) diff --git a/tests/loading_from_clipboard/clipboard_testing.cpp b/tests/loading_from_clipboard/clipboard_testing.cpp index 29535c1dc..a9977b800 100644 --- a/tests/loading_from_clipboard/clipboard_testing.cpp +++ b/tests/loading_from_clipboard/clipboard_testing.cpp @@ -1,6 +1,7 @@ #include "clipboard_testing.h" #include +#include #include DeckList getDeckList(const QString &clipboard) @@ -8,7 +9,7 @@ DeckList getDeckList(const QString &clipboard) DeckList deckList; QString cp(clipboard); QTextStream stream(&cp); // text stream requires local copy - deckList.loadFromStream_Plain(stream, false); + deckList.loadFromStream_Plain(stream, false, CardNameNormalizer()); return deckList; } From 2f10634ca2478521f02a85dc4bc4cfe010940077 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Fri, 6 Mar 2026 11:48:17 -0800 Subject: [PATCH 15/28] [DeckList] Fix double-faced cards not importing correctly (#6665) * [DeckList] Fix double-faced cards not importing correctly * make tests compile --- .../card/import/card_name_normalizer.cpp | 21 +++++++++++++++++++ tests/loading_from_clipboard/CMakeLists.txt | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp index 2349dc3e8..91ebd6647 100644 --- a/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp +++ b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp @@ -1,7 +1,25 @@ #include "card_name_normalizer.h" +#include "../database/card_database_manager.h" +#include "../printing/exact_card.h" + #include +/** + * @brief Resolves the complete display name of a card. + * @param cardName Base name. + * @return Full display name, or the cardName unchanged if a display name is not found. + */ +static QString getCompleteCardName(const QString &cardName) +{ + ExactCard temp = CardDatabaseManager::query()->guessCard({cardName}); + if (temp) { + return temp.getName(); + } + + return cardName; +} + QString CardNameNormalizer::operator()(const QString &cardNameString) const { QString cardName = cardNameString; @@ -41,5 +59,8 @@ QString CardNameNormalizer::operator()(const QString &cardNameString) const cardName.replace(diff.key(), diff.value()); } + // Resolve complete card name + cardName = getCompleteCardName(cardName); + return cardName; } \ No newline at end of file diff --git a/tests/loading_from_clipboard/CMakeLists.txt b/tests/loading_from_clipboard/CMakeLists.txt index eecaafcbb..85133a8e8 100644 --- a/tests/loading_from_clipboard/CMakeLists.txt +++ b/tests/loading_from_clipboard/CMakeLists.txt @@ -1,5 +1,5 @@ add_definitions("-DCARDDB_DATADIR=\"${CMAKE_CURRENT_SOURCE_DIR}/data/\"") -add_executable(loading_from_clipboard_test clipboard_testing.cpp loading_from_clipboard_test.cpp) +add_executable(loading_from_clipboard_test ${VERSION_STRING_CPP} clipboard_testing.cpp loading_from_clipboard_test.cpp) if(NOT GTEST_FOUND) add_dependencies(loading_from_clipboard_test gtest) From f15b70e4aef3994ab2fa9e371ee4cbbcb9b41a15 Mon Sep 17 00:00:00 2001 From: tooomm Date: Sun, 8 Mar 2026 17:53:16 +0100 Subject: [PATCH 16/28] Use new attest action (#6671) --- .github/workflows/desktop-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index e95898097..2cd25fc32 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -232,7 +232,7 @@ jobs: - name: Attest binary provenance id: attestation if: steps.upload_release.outcome == 'success' - uses: actions/attest-build-provenance@v4 + uses: actions/attest@v4 with: subject-name: ${{steps.build.outputs.name}} subject-path: ${{steps.build.outputs.path}} @@ -530,7 +530,7 @@ jobs: - name: Attest binary provenance id: attestation if: steps.upload_release.outcome == 'success' - uses: actions/attest-build-provenance@v4 + uses: actions/attest@v4 with: subject-name: ${{steps.build.outputs.name}} subject-path: ${{steps.build.outputs.path}} From c375cdbb1ae84faedc142eadf11ca5a63332bdfe Mon Sep 17 00:00:00 2001 From: tooomm Date: Sun, 8 Mar 2026 18:03:01 +0100 Subject: [PATCH 17/28] CI: Upload artifact files directly (#6654) * Upload artifact files directly * match pdbs name * Update name variable --- .ci/name_build.sh | 10 +++++----- .github/workflows/desktop-build.yml | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.ci/name_build.sh b/.ci/name_build.sh index 060f8a68e..7b964e21c 100755 --- a/.ci/name_build.sh +++ b/.ci/name_build.sh @@ -35,15 +35,15 @@ if ! cd "$path"; then fi # set filename -name="${file%.*}" # remove all after last . -new_name="$name$SUFFIX." +name="${file%.*}" # remove file extension +new_name="$name$SUFFIX" if [[ $MAKE_ZIP ]]; then - filename="${new_name}zip" + filename="${new_name}.zip" echo "creating zip '$filename' from '$file'" zip "$filename" "$file" else extension="${file##*.}" # remove all before last . - filename="$new_name$extension" + filename="$new_name.$extension" echo "renaming '$file' to '$filename'" mv "$file" "$filename" fi @@ -52,4 +52,4 @@ cd "$oldpwd" relative_path="$path/$filename" ls -l "$relative_path" echo "path=$relative_path" >>"$GITHUB_OUTPUT" -echo "name=$filename" >>"$GITHUB_OUTPUT" +echo "name=$new_name" >>"$GITHUB_OUTPUT" diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 2cd25fc32..bc49db2da 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -214,8 +214,8 @@ jobs: if: matrix.package != 'skip' uses: actions/upload-artifact@v7 with: - name: ${{matrix.distro}}${{matrix.version}}-package path: ${{steps.build.outputs.path}} + archive: false if-no-files-found: error - name: Upload to release @@ -501,15 +501,15 @@ jobs: if: matrix.make_package uses: actions/upload-artifact@v7 with: - name: ${{matrix.artifact_name}} path: ${{steps.build.outputs.path}} + archive: false if-no-files-found: error - name: Upload PDBs (Program Databases) if: matrix.os == 'Windows' uses: actions/upload-artifact@v7 with: - name: Windows${{matrix.target}}-PDBs + name: ${{steps.build.outputs.name}}-PDBs path: | build/cockatrice/Release/*.pdb build/oracle/Release/*.pdb From 4606fcdbd59ed762b8cedb8472cfda771b90642b Mon Sep 17 00:00:00 2001 From: tooomm Date: Sun, 8 Mar 2026 23:27:15 +0100 Subject: [PATCH 18/28] Add description for code docs (#6679) --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index f9abf643d..02bdc148a 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -9,4 +9,4 @@ contact_links: about: For more information and guidance check our Translation FAQ on our wiki! - name: 📖 Code Documentation url: https://cockatrice.github.io/docs/ - about: + about: Helpful source focusing on developers, but there are also references for users! From 852429f24861ae6381dd4b18d6576e74621b980d Mon Sep 17 00:00:00 2001 From: tooomm Date: Sun, 8 Mar 2026 23:48:30 +0100 Subject: [PATCH 19/28] remove unused zip command (#6676) --- .ci/name_build.sh | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/.ci/name_build.sh b/.ci/name_build.sh index 7b964e21c..b111aded6 100755 --- a/.ci/name_build.sh +++ b/.ci/name_build.sh @@ -2,7 +2,6 @@ # used by the ci to rename build artifacts # renames the file to [original name][SUFFIX].[original extension] # where SUFFIX is either available in the environment or as the first arg -# if MAKE_ZIP is set instead a zip is made # expected to be run in the build directory unless BUILD_DIR is set # adds output to GITHUB_OUTPUT builddir="${BUILD_DIR:=.}" @@ -22,8 +21,8 @@ set -e # find file found="$(find "$builddir" -maxdepth 1 -type f -name "$findrx" -print -quit)" -path="${found%/*}" # remove all after last / -file="${found##*/}" # remove all before last / +path="${found%/*}" # remove all including first "/" from right side +file="${found##*/}" # remove all including last "/" from left side if [[ ! $file ]]; then echo "::error file=$0::could not find package" exit 1 @@ -35,18 +34,12 @@ if ! cd "$path"; then fi # set filename -name="${file%.*}" # remove file extension +name="${file%.*}" # remove all including first "." from right side new_name="$name$SUFFIX" -if [[ $MAKE_ZIP ]]; then - filename="${new_name}.zip" - echo "creating zip '$filename' from '$file'" - zip "$filename" "$file" -else - extension="${file##*.}" # remove all before last . - filename="$new_name.$extension" - echo "renaming '$file' to '$filename'" - mv "$file" "$filename" -fi +extension="${file##*.}" # remove all including last "." from left side +filename="$new_name.$extension" +echo "renaming '$file' to '$filename'" +mv "$file" "$filename" cd "$oldpwd" relative_path="$path/$filename" From fd293444c5a1700638fbbdb3b70d9f9677fd1821 Mon Sep 17 00:00:00 2001 From: tooomm Date: Sun, 8 Mar 2026 23:50:33 +0100 Subject: [PATCH 20/28] tweaks (#6674) --- .github/workflows/docker-release.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 1846766bf..2fe646374 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -62,5 +62,6 @@ jobs: push: ${{ github.ref_type == 'tag' }} tags: ${{ steps.metadata.outputs.tags }} labels: ${{ steps.metadata.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max + annotations: ${{ steps.metadata.outputs.annotations }} + cache-from: type=gha,scope=servatrice + cache-to: type=gha,mode=max,scope=servatrice From 15a1d5440bc8aa794a2ae157fbd0e9b624ca3de4 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 8 Mar 2026 15:50:54 -0700 Subject: [PATCH 21/28] [DeckEditor] Fix undo/redo resetting deck sorting (#6673) --- .../src/interface/widgets/deck_editor/deck_state_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp index 8afbfaaa2..8da27b63c 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp @@ -320,6 +320,7 @@ void DeckStateManager::undo(int steps) deckListModel->rebuildTree(); emit deckListModel->layoutChanged(); + emit deckReplaced(); } void DeckStateManager::redo(int steps) @@ -338,6 +339,7 @@ void DeckStateManager::redo(int steps) deckListModel->rebuildTree(); emit deckListModel->layoutChanged(); + emit deckReplaced(); } void DeckStateManager::requestHistorySave(const QString &reason) From e79bbc67b9be76d31f149b04fb7cadeb2ef6c224 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Sun, 8 Mar 2026 15:57:39 -0700 Subject: [PATCH 22/28] [DeckEditor] Fix undo/redo clearing legality (#6675) --- .../libcockatrice/models/deck_list/deck_list_model.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp index 8cbf23be4..e43c612f0 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp @@ -85,6 +85,8 @@ void DeckListModel::rebuildTree() } endResetModel(); + + refreshCardFormatLegalities(); } int DeckListModel::rowCount(const QModelIndex &parent) const @@ -649,7 +651,6 @@ void DeckListModel::setDeckList(const QSharedPointer &_deck) deckList = _deck; } rebuildTree(); - refreshCardFormatLegalities(); emit deckReplaced(); } From 413b4b637b8949e2b52c5109d02daed0f8deeab5 Mon Sep 17 00:00:00 2001 From: tooomm Date: Mon, 9 Mar 2026 21:20:38 +0100 Subject: [PATCH 23/28] sign+notarize only releases (#6678) --- .github/workflows/desktop-build.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index bc49db2da..d627906fd 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -220,7 +220,7 @@ jobs: - name: Upload to release id: upload_release - if: needs.configure.outputs.tag != null && matrix.package != 'skip' + if: matrix.package != 'skip' && needs.configure.outputs.tag != null shell: bash env: GH_TOKEN: ${{github.token}} @@ -453,7 +453,8 @@ jobs: key: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}-${{env.BRANCH_NAME}} - name: Sign app bundle - if: matrix.os == 'macOS' && matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null) + if: matrix.os == 'macOS' && matrix.make_package && needs.configure.outputs.tag != null + id: sign_macos env: MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} @@ -465,7 +466,7 @@ jobs: fi - name: Notarize app bundle - if: matrix.os == 'macOS' && matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null) + if: steps.sign_macos.outcome == 'success' env: MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} @@ -497,8 +498,8 @@ jobs: fi - name: Upload artifact - id: upload_artifact if: matrix.make_package + id: upload_artifact uses: actions/upload-artifact@v7 with: path: ${{steps.build.outputs.path}} @@ -506,7 +507,7 @@ jobs: if-no-files-found: error - name: Upload PDBs (Program Databases) - if: matrix.os == 'Windows' + if: matrix.os == 'Windows' && github.ref_type != 'tag' uses: actions/upload-artifact@v7 with: name: ${{steps.build.outputs.name}}-PDBs @@ -517,8 +518,8 @@ jobs: if-no-files-found: error - name: Upload to release + if: needs.configure.outputs.tag != null && matrix.make_package == '1' id: upload_release - if: needs.configure.outputs.tag != null shell: bash env: GH_TOKEN: ${{github.token}} @@ -528,8 +529,8 @@ jobs: run: gh release upload "$tag_name" "$asset_path#$asset_name" - name: Attest binary provenance - id: attestation if: steps.upload_release.outcome == 'success' + id: attestation uses: actions/attest@v4 with: subject-name: ${{steps.build.outputs.name}} From 8268311fab199a86e000e7b753027afac75996dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:22:26 +0100 Subject: [PATCH 24/28] Bump docker/setup-qemu-action from 3 to 4 (#6683) Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3 to 4. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 2fe646374..02a06e592 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -41,7 +41,7 @@ jobs: org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker buildx uses: docker/setup-buildx-action@v3 From 95d7e027fc8f3f4ed44b3d76b8144a07a1421058 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:30:47 +0100 Subject: [PATCH 25/28] Bump docker/setup-buildx-action from 3 to 4 (#6682) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3 to 4. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 02a06e592..65ccd1322 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -44,7 +44,7 @@ jobs: uses: docker/setup-qemu-action@v4 - name: Set up Docker buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Login to GitHub Container Registry if: github.ref_type == 'tag' From 42cec1045725a29b72b1278ace7d037c5662f9d4 Mon Sep 17 00:00:00 2001 From: tooomm Date: Mon, 9 Mar 2026 21:32:47 +0100 Subject: [PATCH 26/28] CI: Cleanup (#6677) * Cleanup vcpkg matrix * Add filename with extension as output * fix name -> fullname, cleanup --- .ci/name_build.sh | 1 + .github/workflows/desktop-build.yml | 10 ++-------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.ci/name_build.sh b/.ci/name_build.sh index b111aded6..85818bbd9 100755 --- a/.ci/name_build.sh +++ b/.ci/name_build.sh @@ -46,3 +46,4 @@ relative_path="$path/$filename" ls -l "$relative_path" echo "path=$relative_path" >>"$GITHUB_OUTPUT" echo "name=$new_name" >>"$GITHUB_OUTPUT" +echo "fullname=$filename" >>"$GITHUB_OUTPUT" diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index d627906fd..233d9e488 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -225,7 +225,7 @@ jobs: env: GH_TOKEN: ${{github.token}} tag_name: ${{needs.configure.outputs.tag}} - asset_name: ${{steps.build.outputs.name}} + asset_name: ${{steps.build.outputs.fullname}} asset_path: ${{steps.build.outputs.path}} run: gh release upload "$tag_name" "$asset_path#$asset_name" @@ -234,7 +234,6 @@ jobs: if: steps.upload_release.outcome == 'success' uses: actions/attest@v4 with: - subject-name: ${{steps.build.outputs.name}} subject-path: ${{steps.build.outputs.path}} show-summary: false @@ -259,7 +258,6 @@ jobs: override_target: 13 make_package: 1 package_suffix: "-macOS13_Intel" - artifact_name: macOS13_Intel-package qt_version: 6.10.* qt_arch: clang_64 qt_modules: qtimageformats qtmultimedia qtwebsockets @@ -274,7 +272,6 @@ jobs: type: Release make_package: 1 package_suffix: "-macOS14" - artifact_name: macOS14-package qt_version: 6.10.* qt_arch: clang_64 qt_modules: qtimageformats qtmultimedia qtwebsockets @@ -289,7 +286,6 @@ jobs: type: Release make_package: 1 package_suffix: "-macOS15" - artifact_name: macOS15-package qt_version: 6.10.* qt_arch: clang_64 qt_modules: qtimageformats qtmultimedia qtwebsockets @@ -314,7 +310,6 @@ jobs: type: Release make_package: 1 package_suffix: "-Win10" - artifact_name: Windows10-installer qt_version: 6.10.* qt_arch: win64_msvc2022_64 qt_modules: qtimageformats qtmultimedia qtwebsockets @@ -524,7 +519,7 @@ jobs: env: GH_TOKEN: ${{github.token}} tag_name: ${{needs.configure.outputs.tag}} - asset_name: ${{steps.build.outputs.name}} + asset_name: ${{steps.build.outputs.fullname}} asset_path: ${{steps.build.outputs.path}} run: gh release upload "$tag_name" "$asset_path#$asset_name" @@ -533,7 +528,6 @@ jobs: id: attestation uses: actions/attest@v4 with: - subject-name: ${{steps.build.outputs.name}} subject-path: ${{steps.build.outputs.path}} show-summary: false From 9e2276a59fa0df05b16bb4e9dbd0a2a377dd572a Mon Sep 17 00:00:00 2001 From: DawnFire42 Date: Wed, 11 Mar 2026 19:34:05 -0400 Subject: [PATCH 27/28] Refactor zone names (#6686) * Add ZoneNames constants for protocol zone identifiers. Introduce a centralized ZoneNames namespace providing constexpr constants for zone identifiers used in the client-server protocol. This establishes a single source of truth for zone names like TABLE, GRAVE, EXILE, HAND, DECK, SIDEBOARD, and STACK. The protocol values remain unchanged (e.g., EXILE maps to rfg for backwards compatibility) while providing meaningful constant names. * refactor(server): use ZoneNames constants in server game logic Replace hardcoded zone name strings with ZoneNames:: constants in: - server_player.cpp: zone setup, draw, shuffle, mulligan operations - server_abstract_player.cpp: card movement and token destruction - server_game.cpp: returning cards when players leave No functional changes - purely mechanical string literal replacement. * refactor(client): use ZoneNames constants in core player/zone logic Update the foundational player and zone classes to use ZoneNames:: constants instead of string literals. Changes include: - player.h/cpp: zone initialization and builtinZones set - card_zone_logic.cpp: zone name translation for UI display - table_zone.cpp: table zone operations No functional changes - purely mechanical string literal replacement. * refactor(client): use ZoneNames constants in player actions and events Replace zone name strings with ZoneNames:: constants in the player action and event handling code. player_actions.cpp contains the most extensive changes (~90+ replacements) covering all card movement commands. No functional changes - purely mechanical string literal replacement. * refactor(client): use ZoneNames constants in zone menu handlers Update all zone-specific menu files to use ZoneNames:: constants for QAction data values and zone targeting. This covers context menus for cards, graveyard, hand, and exile (RFG) zones. No functional changes - purely mechanical string literal replacement. * refactor(client): use ZoneNames constants in game scene components Update remaining game scene components to use ZoneNames:: constants: - arrow_item.cpp: arrow drawing between cards - game_scene.cpp: zone view positioning - message_log_widget.cpp: removes duplicate local static constants that were previously defining zone names redundantly - phases_toolbar.cpp: phase actions (untap all) Notable: message_log_widget.cpp previously had its own local constants (TABLE_ZONE_NAME, GRAVE_ZONE_NAME, etc.) which are now removed in favor of the centralized ZoneNames:: constants. * formatting fix --- cockatrice/src/game/board/arrow_item.cpp | 19 +-- cockatrice/src/game/game_scene.cpp | 5 +- .../src/game/log/message_log_widget.cpp | 43 +++--- cockatrice/src/game/phases_toolbar.cpp | 3 +- cockatrice/src/game/player/menu/card_menu.cpp | 14 +- .../src/game/player/menu/grave_menu.cpp | 9 +- cockatrice/src/game/player/menu/hand_menu.cpp | 9 +- cockatrice/src/game/player/menu/rfg_menu.cpp | 10 +- cockatrice/src/game/player/player.cpp | 22 +-- cockatrice/src/game/player/player.h | 15 +- cockatrice/src/game/player/player_actions.cpp | 137 +++++++++--------- .../src/game/player/player_event_handler.cpp | 5 +- .../src/game/zones/logic/card_zone_logic.cpp | 11 +- cockatrice/src/game/zones/table_zone.cpp | 3 +- .../remote/game/server_abstract_player.cpp | 9 +- .../server/remote/game/server_game.cpp | 5 +- .../server/remote/game/server_player.cpp | 33 +++-- libcockatrice_utility/CMakeLists.txt | 9 +- .../libcockatrice/utility/zone_names.h | 19 +++ 19 files changed, 207 insertions(+), 173 deletions(-) create mode 100644 libcockatrice_utility/libcockatrice/utility/zone_names.h diff --git a/cockatrice/src/game/board/arrow_item.cpp b/cockatrice/src/game/board/arrow_item.cpp index 257d96f8a..60585a774 100644 --- a/cockatrice/src/game/board/arrow_item.cpp +++ b/cockatrice/src/game/board/arrow_item.cpp @@ -19,6 +19,7 @@ #include #include #include +#include ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color) : QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), targetLocked(false), @@ -239,16 +240,16 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) } // if the card is in hand then we will move the card to stack or table as part of drawing the arrow - if (startZone->getName() == "hand") { + if (startZone->getName() == ZoneNames::HAND) { startCard->playCard(false); CardInfoPtr ci = startCard->getCard().getCardPtr(); bool playToStack = SettingsCache::instance().getPlayToStack(); - if (ci && - ((!playToStack && ci->getUiAttributes().tableRow == 3) || - (playToStack && ci->getUiAttributes().tableRow != 0 && startCard->getZone()->getName() != "stack"))) - cmd.set_start_zone("stack"); + if (ci && ((!playToStack && ci->getUiAttributes().tableRow == 3) || + (playToStack && ci->getUiAttributes().tableRow != 0 && + startCard->getZone()->getName() != ZoneNames::STACK))) + cmd.set_start_zone(ZoneNames::STACK); else - cmd.set_start_zone(playToStack ? "stack" : "table"); + cmd.set_start_zone(playToStack ? ZoneNames::STACK : ZoneNames::TABLE); } if (deleteInPhase != 0) { @@ -318,7 +319,7 @@ void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCard) { // do nothing if target is already attached to another card or is not in play - if (targetCard->getAttachedTo() || targetCard->getZone()->getName() != "table") { + if (targetCard->getAttachedTo() || targetCard->getZone()->getName() != ZoneNames::TABLE) { return; } @@ -326,12 +327,12 @@ void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCar CardZoneLogic *targetZone = targetCard->getZone(); // move card onto table first if attaching from some other zone - if (startZone->getName() != "table") { + if (startZone->getName() != ZoneNames::TABLE) { player->getPlayerActions()->playCardToTable(startCard, false); } Command_AttachCard cmd; - cmd.set_start_zone("table"); + cmd.set_start_zone(ZoneNames::TABLE); cmd.set_card_id(startCard->getId()); cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_target_zone(targetZone->getName().toStdString()); diff --git a/cockatrice/src/game/game_scene.cpp b/cockatrice/src/game/game_scene.cpp index 77037cd6e..5dc3b48f7 100644 --- a/cockatrice/src/game/game_scene.cpp +++ b/cockatrice/src/game/game_scene.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include /** @@ -410,9 +411,9 @@ void GameScene::toggleZoneView(Player *player, const QString &zoneName, int numb connect(item, &ZoneViewWidget::closePressed, this, &GameScene::removeZoneView); addItem(item); - if (zoneName == "grave") + if (zoneName == ZoneNames::GRAVE) item->setPos(360, 100); - else if (zoneName == "rfg") + else if (zoneName == ZoneNames::EXILE) item->setPos(380, 120); else item->setPos(340, 80); diff --git a/cockatrice/src/game/log/message_log_widget.cpp b/cockatrice/src/game/log/message_log_widget.cpp index 645974994..c38e433eb 100644 --- a/cockatrice/src/game/log/message_log_widget.cpp +++ b/cockatrice/src/game/log/message_log_widget.cpp @@ -10,16 +10,9 @@ #include <../../client/settings/card_counter_settings.h> #include #include +#include #include -static const QString TABLE_ZONE_NAME = "table"; -static const QString GRAVE_ZONE_NAME = "grave"; -static const QString EXILE_ZONE_NAME = "rfg"; -static const QString HAND_ZONE_NAME = "hand"; -static const QString DECK_ZONE_NAME = "deck"; -static const QString SIDEBOARD_ZONE_NAME = "sb"; -static const QString STACK_ZONE_NAME = "stack"; - static QString sanitizeHtml(QString dirty) { return dirty.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """); @@ -37,15 +30,15 @@ MessageLogWidget::getFromStr(CardZoneLogic *zone, QString cardName, int position QString fromStr; QString zoneName = zone->getName(); - if (zoneName == TABLE_ZONE_NAME) { + if (zoneName == ZoneNames::TABLE) { fromStr = tr(" from play"); - } else if (zoneName == GRAVE_ZONE_NAME) { + } else if (zoneName == ZoneNames::GRAVE) { fromStr = tr(" from their graveyard"); - } else if (zoneName == EXILE_ZONE_NAME) { + } else if (zoneName == ZoneNames::EXILE) { fromStr = tr(" from exile"); - } else if (zoneName == HAND_ZONE_NAME) { + } else if (zoneName == ZoneNames::HAND) { fromStr = tr(" from their hand"); - } else if (zoneName == DECK_ZONE_NAME) { + } else if (zoneName == ZoneNames::DECK) { if (position == 0) { if (cardName.isEmpty()) { if (ownerChange) { @@ -83,9 +76,9 @@ MessageLogWidget::getFromStr(CardZoneLogic *zone, QString cardName, int position fromStr = tr(" from their library"); } } - } else if (zoneName == SIDEBOARD_ZONE_NAME) { + } else if (zoneName == ZoneNames::SIDEBOARD) { fromStr = tr(" from sideboard"); - } else if (zoneName == STACK_ZONE_NAME) { + } else if (zoneName == ZoneNames::STACK) { fromStr = tr(" from the stack"); } else { fromStr = tr(" from custom zone '%1'").arg(zoneName); @@ -275,9 +268,9 @@ void MessageLogWidget::logMoveCard(Player *player, bool ownerChanged = startZone->getPlayer() != targetZone->getPlayer(); // do not log if moved within the same zone - if ((startZoneName == TABLE_ZONE_NAME && targetZoneName == TABLE_ZONE_NAME && !ownerChanged) || - (startZoneName == HAND_ZONE_NAME && targetZoneName == HAND_ZONE_NAME) || - (startZoneName == EXILE_ZONE_NAME && targetZoneName == EXILE_ZONE_NAME)) { + if ((startZoneName == ZoneNames::TABLE && targetZoneName == ZoneNames::TABLE && !ownerChanged) || + (startZoneName == ZoneNames::HAND && targetZoneName == ZoneNames::HAND) || + (startZoneName == ZoneNames::EXILE && targetZoneName == ZoneNames::EXILE)) { return; } @@ -306,28 +299,28 @@ void MessageLogWidget::logMoveCard(Player *player, QString finalStr; std::optional fourthArg; - if (targetZoneName == TABLE_ZONE_NAME) { + if (targetZoneName == ZoneNames::TABLE) { soundEngine->playSound("play_card"); if (card->getFaceDown()) { finalStr = tr("%1 puts %2 into play%3 face down."); } else { finalStr = tr("%1 puts %2 into play%3."); } - } else if (targetZoneName == GRAVE_ZONE_NAME) { + } else if (targetZoneName == ZoneNames::GRAVE) { if (card->getFaceDown()) { finalStr = tr("%1 puts %2%3 into their graveyard face down."); } else { finalStr = tr("%1 puts %2%3 into their graveyard."); } - } else if (targetZoneName == EXILE_ZONE_NAME) { + } else if (targetZoneName == ZoneNames::EXILE) { if (card->getFaceDown()) { finalStr = tr("%1 exiles %2%3 face down."); } else { finalStr = tr("%1 exiles %2%3."); } - } else if (targetZoneName == HAND_ZONE_NAME) { + } else if (targetZoneName == ZoneNames::HAND) { finalStr = tr("%1 moves %2%3 to their hand."); - } else if (targetZoneName == DECK_ZONE_NAME) { + } else if (targetZoneName == ZoneNames::DECK) { if (newX == -1) { finalStr = tr("%1 puts %2%3 into their library."); } else if (newX >= targetZone->getCards().size()) { @@ -339,9 +332,9 @@ void MessageLogWidget::logMoveCard(Player *player, fourthArg = QString::number(newX); finalStr = tr("%1 puts %2%3 into their library %4 cards from the top."); } - } else if (targetZoneName == SIDEBOARD_ZONE_NAME) { + } else if (targetZoneName == ZoneNames::SIDEBOARD) { finalStr = tr("%1 moves %2%3 to sideboard."); - } else if (targetZoneName == STACK_ZONE_NAME) { + } else if (targetZoneName == ZoneNames::STACK) { soundEngine->playSound("play_card"); if (card->getFaceDown()) { finalStr = tr("%1 plays %2%3 face down."); diff --git a/cockatrice/src/game/phases_toolbar.cpp b/cockatrice/src/game/phases_toolbar.cpp index 5106e40de..2341a1d7f 100644 --- a/cockatrice/src/game/phases_toolbar.cpp +++ b/cockatrice/src/game/phases_toolbar.cpp @@ -11,6 +11,7 @@ #include #include #include +#include PhaseButton::PhaseButton(const QString &_name, QGraphicsItem *parent, QAction *_doubleClickAction, bool _highlightable) : QObject(), QGraphicsItem(parent), name(_name), active(false), highlightable(_highlightable), @@ -259,7 +260,7 @@ void PhasesToolbar::actNextTurn() void PhasesToolbar::actUntapAll() { Command_SetCardAttr cmd; - cmd.set_zone("table"); + cmd.set_zone(ZoneNames::TABLE); cmd.set_attribute(AttrTapped); cmd.set_attr_value("0"); diff --git a/cockatrice/src/game/player/menu/card_menu.cpp b/cockatrice/src/game/player/menu/card_menu.cpp index ddf0a55f3..ef95f052e 100644 --- a/cockatrice/src/game/player/menu/card_menu.cpp +++ b/cockatrice/src/game/player/menu/card_menu.cpp @@ -12,6 +12,7 @@ #include #include +#include CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive) : player(_player), card(_card), shortcutsActive(_shortcutsActive) @@ -115,11 +116,12 @@ CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive } else if (writeableCard) { if (card->getZone()) { - if (card->getZone()->getName() == "table") { + if (card->getZone()->getName() == ZoneNames::TABLE) { createTableMenu(); - } else if (card->getZone()->getName() == "stack") { + } else if (card->getZone()->getName() == ZoneNames::STACK) { createStackMenu(); - } else if (card->getZone()->getName() == "rfg" || card->getZone()->getName() == "grave") { + } else if (card->getZone()->getName() == ZoneNames::EXILE || + card->getZone()->getName() == ZoneNames::GRAVE) { createGraveyardOrExileMenu(); } else { createHandOrCustomZoneMenu(); @@ -128,7 +130,7 @@ CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive addMenu(new MoveMenu(player)); } } else { - if (card->getZone() && card->getZone()->getName() != "hand") { + if (card->getZone() && card->getZone()->getName() != ZoneNames::HAND) { addAction(aDrawArrow); addSeparator(); addRelatedCardView(); @@ -285,7 +287,7 @@ void CardMenu::createHandOrCustomZoneMenu() addMenu(new MoveMenu(player)); // actions that are really wonky when done from deck or sideboard - if (card->getZone()->getName() == "hand") { + if (card->getZone()->getName() == ZoneNames::HAND) { addSeparator(); addAction(aAttach); addAction(aDrawArrow); @@ -298,7 +300,7 @@ void CardMenu::createHandOrCustomZoneMenu() } addRelatedCardView(); - if (card->getZone()->getName() == "hand") { + if (card->getZone()->getName() == ZoneNames::HAND) { addRelatedCardActions(); } } diff --git a/cockatrice/src/game/player/menu/grave_menu.cpp b/cockatrice/src/game/player/menu/grave_menu.cpp index d4faca42b..2af62c08a 100644 --- a/cockatrice/src/game/player/menu/grave_menu.cpp +++ b/cockatrice/src/game/player/menu/grave_menu.cpp @@ -6,6 +6,7 @@ #include #include +#include GraveyardMenu::GraveyardMenu(Player *_player, QWidget *parent) : TearOffMenu(parent), player(_player) { @@ -39,16 +40,16 @@ void GraveyardMenu::createMoveActions() if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { aMoveGraveToTopLibrary = new QAction(this); - aMoveGraveToTopLibrary->setData(QList() << "deck" << 0); + aMoveGraveToTopLibrary->setData(QList() << ZoneNames::DECK << 0); aMoveGraveToBottomLibrary = new QAction(this); - aMoveGraveToBottomLibrary->setData(QList() << "deck" << -1); + aMoveGraveToBottomLibrary->setData(QList() << ZoneNames::DECK << -1); aMoveGraveToHand = new QAction(this); - aMoveGraveToHand->setData(QList() << "hand" << 0); + aMoveGraveToHand->setData(QList() << ZoneNames::HAND << 0); aMoveGraveToRfg = new QAction(this); - aMoveGraveToRfg->setData(QList() << "rfg" << 0); + aMoveGraveToRfg->setData(QList() << ZoneNames::EXILE << 0); connect(aMoveGraveToTopLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone); connect(aMoveGraveToBottomLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone); diff --git a/cockatrice/src/game/player/menu/hand_menu.cpp b/cockatrice/src/game/player/menu/hand_menu.cpp index 019ab925c..d65c136bf 100644 --- a/cockatrice/src/game/player/menu/hand_menu.cpp +++ b/cockatrice/src/game/player/menu/hand_menu.cpp @@ -9,6 +9,7 @@ #include #include +#include HandMenu::HandMenu(Player *_player, PlayerActions *actions, QWidget *parent) : TearOffMenu(parent), player(_player) { @@ -76,13 +77,13 @@ HandMenu::HandMenu(Player *_player, PlayerActions *actions, QWidget *parent) : T if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { aMoveHandToTopLibrary = new QAction(this); - aMoveHandToTopLibrary->setData(QList() << "deck" << 0); + aMoveHandToTopLibrary->setData(QList() << ZoneNames::DECK << 0); aMoveHandToBottomLibrary = new QAction(this); - aMoveHandToBottomLibrary->setData(QList() << "deck" << -1); + aMoveHandToBottomLibrary->setData(QList() << ZoneNames::DECK << -1); aMoveHandToGrave = new QAction(this); - aMoveHandToGrave->setData(QList() << "grave" << 0); + aMoveHandToGrave->setData(QList() << ZoneNames::GRAVE << 0); aMoveHandToRfg = new QAction(this); - aMoveHandToRfg->setData(QList() << "rfg" << 0); + aMoveHandToRfg->setData(QList() << ZoneNames::EXILE << 0); auto hand = player->getHandZone(); diff --git a/cockatrice/src/game/player/menu/rfg_menu.cpp b/cockatrice/src/game/player/menu/rfg_menu.cpp index 25e162581..8a101c04c 100644 --- a/cockatrice/src/game/player/menu/rfg_menu.cpp +++ b/cockatrice/src/game/player/menu/rfg_menu.cpp @@ -3,6 +3,8 @@ #include "../player.h" #include "../player_actions.h" +#include + RfgMenu::RfgMenu(Player *_player, QWidget *parent) : TearOffMenu(parent), player(_player) { createMoveActions(); @@ -30,13 +32,13 @@ void RfgMenu::createMoveActions() auto rfg = player->getRfgZone(); aMoveRfgToTopLibrary = new QAction(this); - aMoveRfgToTopLibrary->setData(QList() << "deck" << 0); + aMoveRfgToTopLibrary->setData(QList() << ZoneNames::DECK << 0); aMoveRfgToBottomLibrary = new QAction(this); - aMoveRfgToBottomLibrary->setData(QList() << "deck" << -1); + aMoveRfgToBottomLibrary->setData(QList() << ZoneNames::DECK << -1); aMoveRfgToHand = new QAction(this); - aMoveRfgToHand->setData(QList() << "hand" << 0); + aMoveRfgToHand->setData(QList() << ZoneNames::HAND << 0); aMoveRfgToGrave = new QAction(this); - aMoveRfgToGrave->setData(QList() << "grave" << 0); + aMoveRfgToGrave->setData(QList() << ZoneNames::GRAVE << 0); connect(aMoveRfgToTopLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone); connect(aMoveRfgToBottomLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone); diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp index 0723ae6bc..ac4149f0e 100644 --- a/cockatrice/src/game/player/player.cpp +++ b/cockatrice/src/game/player/player.cpp @@ -61,15 +61,15 @@ void Player::forwardActionSignalsToEventHandler() void Player::initializeZones() { - addZone(new PileZoneLogic(this, "deck", false, true, false, this)); - addZone(new PileZoneLogic(this, "grave", false, false, true, this)); - addZone(new PileZoneLogic(this, "rfg", false, false, true, this)); - addZone(new PileZoneLogic(this, "sb", false, false, false, this)); - addZone(new TableZoneLogic(this, "table", true, false, true, this)); - addZone(new StackZoneLogic(this, "stack", true, false, true, this)); + addZone(new PileZoneLogic(this, ZoneNames::DECK, false, true, false, this)); + addZone(new PileZoneLogic(this, ZoneNames::GRAVE, false, false, true, this)); + addZone(new PileZoneLogic(this, ZoneNames::EXILE, false, false, true, this)); + addZone(new PileZoneLogic(this, ZoneNames::SIDEBOARD, false, false, false, this)); + addZone(new TableZoneLogic(this, ZoneNames::TABLE, true, false, true, this)); + addZone(new StackZoneLogic(this, ZoneNames::STACK, true, false, true, this)); bool visibleHand = playerInfo->getLocalOrJudge() || (game->getPlayerManager()->isSpectator() && game->getGameMetaInfo()->spectatorsOmniscient()); - addZone(new HandZoneLogic(this, "hand", false, false, visibleHand, this)); + addZone(new HandZoneLogic(this, ZoneNames::HAND, false, false, visibleHand, this)); } Player::~Player() @@ -119,13 +119,13 @@ void Player::setZoneId(int _zoneId) void Player::processPlayerInfo(const ServerInfo_Player &info) { static QSet builtinZones{/* PileZones */ - "deck", "grave", "rfg", "sb", + ZoneNames::DECK, ZoneNames::GRAVE, ZoneNames::EXILE, ZoneNames::SIDEBOARD, /* TableZone */ - "table", + ZoneNames::TABLE, /* StackZone */ - "stack", + ZoneNames::STACK, /* HandZone */ - "hand"}; + ZoneNames::HAND}; clearCounters(); clearArrows(); diff --git a/cockatrice/src/game/player/player.h b/cockatrice/src/game/player/player.h index 0a03b3abe..e9c008821 100644 --- a/cockatrice/src/game/player/player.h +++ b/cockatrice/src/game/player/player.h @@ -27,6 +27,7 @@ #include #include #include +#include inline Q_LOGGING_CATEGORY(PlayerLog, "player"); @@ -155,37 +156,37 @@ public: PileZoneLogic *getDeckZone() { - return qobject_cast(zones.value("deck")); + return qobject_cast(zones.value(ZoneNames::DECK)); } PileZoneLogic *getGraveZone() { - return qobject_cast(zones.value("grave")); + return qobject_cast(zones.value(ZoneNames::GRAVE)); } PileZoneLogic *getRfgZone() { - return qobject_cast(zones.value("rfg")); + return qobject_cast(zones.value(ZoneNames::EXILE)); } PileZoneLogic *getSideboardZone() { - return qobject_cast(zones.value("sb")); + return qobject_cast(zones.value(ZoneNames::SIDEBOARD)); } TableZoneLogic *getTableZone() { - return qobject_cast(zones.value("table")); + return qobject_cast(zones.value(ZoneNames::TABLE)); } StackZoneLogic *getStackZone() { - return qobject_cast(zones.value("stack")); + return qobject_cast(zones.value(ZoneNames::STACK)); } HandZoneLogic *getHandZone() { - return qobject_cast(zones.value("hand")); + return qobject_cast(zones.value(ZoneNames::HAND)); } AbstractCounter *addCounter(const ServerInfo_Counter &counter); diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index 514ac2e67..20e526727 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -28,6 +28,7 @@ #include #include #include +#include // milliseconds in between triggers of the move top cards until action static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100; @@ -63,13 +64,13 @@ void PlayerActions::playCard(CardItem *card, bool faceDown) int tableRow = info.getUiAttributes().tableRow; bool playToStack = SettingsCache::instance().getPlayToStack(); QString currentZone = card->getZone()->getName(); - if (currentZone == "stack" && tableRow == 3) { - cmd.set_target_zone("grave"); + if (currentZone == ZoneNames::STACK && tableRow == 3) { + cmd.set_target_zone(ZoneNames::GRAVE); cmd.set_x(0); cmd.set_y(0); - } else if (!faceDown && - ((!playToStack && tableRow == 3) || ((playToStack && tableRow != 0) && currentZone != "stack"))) { - cmd.set_target_zone("stack"); + } else if (!faceDown && ((!playToStack && tableRow == 3) || + ((playToStack && tableRow != 0) && currentZone != ZoneNames::STACK))) { + cmd.set_target_zone(ZoneNames::STACK); cmd.set_x(-1); cmd.set_y(0); } else { @@ -81,7 +82,7 @@ void PlayerActions::playCard(CardItem *card, bool faceDown) } cardToMove->set_tapped(!faceDown && info.getUiAttributes().cipt); if (tableRow != 3) - cmd.set_target_zone("table"); + cmd.set_target_zone(ZoneNames::TABLE); cmd.set_x(gridPoint.x()); cmd.set_y(gridPoint.y()); } @@ -124,7 +125,7 @@ void PlayerActions::playCardToTable(const CardItem *card, bool faceDown) cardToMove->set_pt(info.getPowTough().toStdString()); } cardToMove->set_tapped(!faceDown && info.getUiAttributes().cipt); - cmd.set_target_zone("table"); + cmd.set_target_zone(ZoneNames::TABLE); cmd.set_x(gridPoint.x()); cmd.set_y(gridPoint.y()); sendGameCommand(cmd); @@ -132,12 +133,12 @@ void PlayerActions::playCardToTable(const CardItem *card, bool faceDown) void PlayerActions::actViewLibrary() { - player->getGameScene()->toggleZoneView(player, "deck", -1); + player->getGameScene()->toggleZoneView(player, ZoneNames::DECK, -1); } void PlayerActions::actViewHand() { - player->getGameScene()->toggleZoneView(player, "hand", -1); + player->getGameScene()->toggleZoneView(player, ZoneNames::HAND, -1); } /** @@ -181,7 +182,7 @@ void PlayerActions::actViewTopCards() deckSize, 1, &ok); if (ok) { defaultNumberTopCards = number; - player->getGameScene()->toggleZoneView(player, "deck", number); + player->getGameScene()->toggleZoneView(player, ZoneNames::DECK, number); } } @@ -194,14 +195,14 @@ void PlayerActions::actViewBottomCards() deckSize, 1, &ok); if (ok) { defaultNumberBottomCards = number; - player->getGameScene()->toggleZoneView(player, "deck", number, true); + player->getGameScene()->toggleZoneView(player, ZoneNames::DECK, number, true); } } void PlayerActions::actAlwaysRevealTopCard() { Command_ChangeZoneProperties cmd; - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); cmd.set_always_reveal_top_card(player->getPlayerMenu()->getLibraryMenu()->isAlwaysRevealTopCardChecked()); sendGameCommand(cmd); @@ -210,7 +211,7 @@ void PlayerActions::actAlwaysRevealTopCard() void PlayerActions::actAlwaysLookAtTopCard() { Command_ChangeZoneProperties cmd; - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); cmd.set_always_look_at_top_card(player->getPlayerMenu()->getLibraryMenu()->isAlwaysLookAtTopCardChecked()); sendGameCommand(cmd); @@ -223,17 +224,17 @@ void PlayerActions::actOpenDeckInDeckEditor() void PlayerActions::actViewGraveyard() { - player->getGameScene()->toggleZoneView(player, "grave", -1); + player->getGameScene()->toggleZoneView(player, ZoneNames::GRAVE, -1); } void PlayerActions::actViewRfg() { - player->getGameScene()->toggleZoneView(player, "rfg", -1); + player->getGameScene()->toggleZoneView(player, ZoneNames::EXILE, -1); } void PlayerActions::actViewSideboard() { - player->getGameScene()->toggleZoneView(player, "sb", -1); + player->getGameScene()->toggleZoneView(player, ZoneNames::SIDEBOARD, -1); } void PlayerActions::actShuffle() @@ -263,7 +264,7 @@ void PlayerActions::actShuffleTop() defaultNumberTopCards = number; Command_Shuffle cmd; - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); cmd.set_start(0); cmd.set_end(number - 1); // inclusive, the indexed card at end will be shuffled @@ -292,7 +293,7 @@ void PlayerActions::actShuffleBottom() defaultNumberBottomCards = number; Command_Shuffle cmd; - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); cmd.set_start(-number); cmd.set_end(-1); @@ -376,7 +377,7 @@ void PlayerActions::actUndoDraw() void PlayerActions::cmdSetTopCard(Command_MoveCard &cmd) { - cmd.set_start_zone("deck"); + cmd.set_start_zone(ZoneNames::DECK); auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); cardToMove->set_card_id(0); cmd.set_target_player_id(player->getPlayerInfo()->getId()); @@ -386,7 +387,7 @@ void PlayerActions::cmdSetBottomCard(Command_MoveCard &cmd) { CardZoneLogic *zone = player->getDeckZone(); int lastCard = zone->getCards().size() - 1; - cmd.set_start_zone("deck"); + cmd.set_start_zone(ZoneNames::DECK); auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); cardToMove->set_card_id(lastCard); cmd.set_target_player_id(player->getPlayerInfo()->getId()); @@ -400,7 +401,7 @@ void PlayerActions::actMoveTopCardToGrave() Command_MoveCard cmd; cmdSetTopCard(cmd); - cmd.set_target_zone("grave"); + cmd.set_target_zone(ZoneNames::GRAVE); cmd.set_x(0); cmd.set_y(0); @@ -415,7 +416,7 @@ void PlayerActions::actMoveTopCardToExile() Command_MoveCard cmd; cmdSetTopCard(cmd); - cmd.set_target_zone("rfg"); + cmd.set_target_zone(ZoneNames::EXILE); cmd.set_x(0); cmd.set_y(0); @@ -424,22 +425,22 @@ void PlayerActions::actMoveTopCardToExile() void PlayerActions::actMoveTopCardsToGrave() { - moveTopCardsTo("grave", tr("grave"), false); + moveTopCardsTo(ZoneNames::GRAVE, tr("grave"), false); } void PlayerActions::actMoveTopCardsToGraveFaceDown() { - moveTopCardsTo("grave", tr("grave"), true); + moveTopCardsTo(ZoneNames::GRAVE, tr("grave"), true); } void PlayerActions::actMoveTopCardsToExile() { - moveTopCardsTo("rfg", tr("exile"), false); + moveTopCardsTo(ZoneNames::EXILE, tr("exile"), false); } void PlayerActions::actMoveTopCardsToExileFaceDown() { - moveTopCardsTo("rfg", tr("exile"), true); + moveTopCardsTo(ZoneNames::EXILE, tr("exile"), true); } void PlayerActions::moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown) @@ -463,7 +464,7 @@ void PlayerActions::moveTopCardsTo(const QString &targetZone, const QString &zon defaultNumberTopCards = number; Command_MoveCard cmd; - cmd.set_start_zone("deck"); + cmd.set_start_zone(ZoneNames::DECK); cmd.set_target_player_id(player->getPlayerInfo()->getId()); cmd.set_target_zone(targetZone.toStdString()); cmd.set_x(0); @@ -549,7 +550,7 @@ void PlayerActions::actMoveTopCardToBottom() Command_MoveCard cmd; cmdSetTopCard(cmd); - cmd.set_target_zone("deck"); + cmd.set_target_zone(ZoneNames::DECK); cmd.set_x(-1); // bottom of deck cmd.set_y(0); @@ -564,7 +565,7 @@ void PlayerActions::actMoveTopCardToPlay() Command_MoveCard cmd; cmdSetTopCard(cmd); - cmd.set_target_zone("stack"); + cmd.set_target_zone(ZoneNames::STACK); cmd.set_x(-1); cmd.set_y(0); @@ -578,12 +579,12 @@ void PlayerActions::actMoveTopCardToPlayFaceDown() } Command_MoveCard cmd; - cmd.set_start_zone("deck"); + cmd.set_start_zone(ZoneNames::DECK); CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card(); cardToMove->set_card_id(0); cardToMove->set_face_down(true); cmd.set_target_player_id(player->getPlayerInfo()->getId()); - cmd.set_target_zone("table"); + cmd.set_target_zone(ZoneNames::TABLE); cmd.set_x(-1); cmd.set_y(0); @@ -598,7 +599,7 @@ void PlayerActions::actMoveBottomCardToGrave() Command_MoveCard cmd; cmdSetBottomCard(cmd); - cmd.set_target_zone("grave"); + cmd.set_target_zone(ZoneNames::GRAVE); cmd.set_x(0); cmd.set_y(0); @@ -613,7 +614,7 @@ void PlayerActions::actMoveBottomCardToExile() Command_MoveCard cmd; cmdSetBottomCard(cmd); - cmd.set_target_zone("rfg"); + cmd.set_target_zone(ZoneNames::EXILE); cmd.set_x(0); cmd.set_y(0); @@ -622,22 +623,22 @@ void PlayerActions::actMoveBottomCardToExile() void PlayerActions::actMoveBottomCardsToGrave() { - moveBottomCardsTo("grave", tr("grave"), false); + moveBottomCardsTo(ZoneNames::GRAVE, tr("grave"), false); } void PlayerActions::actMoveBottomCardsToGraveFaceDown() { - moveBottomCardsTo("grave", tr("grave"), true); + moveBottomCardsTo(ZoneNames::GRAVE, tr("grave"), true); } void PlayerActions::actMoveBottomCardsToExile() { - moveBottomCardsTo("rfg", tr("exile"), false); + moveBottomCardsTo(ZoneNames::EXILE, tr("exile"), false); } void PlayerActions::actMoveBottomCardsToExileFaceDown() { - moveBottomCardsTo("rfg", tr("exile"), true); + moveBottomCardsTo(ZoneNames::EXILE, tr("exile"), true); } void PlayerActions::moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown) @@ -661,7 +662,7 @@ void PlayerActions::moveBottomCardsTo(const QString &targetZone, const QString & defaultNumberBottomCards = number; Command_MoveCard cmd; - cmd.set_start_zone("deck"); + cmd.set_start_zone(ZoneNames::DECK); cmd.set_target_player_id(player->getPlayerInfo()->getId()); cmd.set_target_zone(targetZone.toStdString()); cmd.set_x(0); @@ -686,7 +687,7 @@ void PlayerActions::actMoveBottomCardToTop() Command_MoveCard cmd; cmdSetBottomCard(cmd); - cmd.set_target_zone("deck"); + cmd.set_target_zone(ZoneNames::DECK); cmd.set_x(0); // top of deck cmd.set_y(0); @@ -756,7 +757,7 @@ void PlayerActions::actDrawBottomCard() Command_MoveCard cmd; cmdSetBottomCard(cmd); - cmd.set_target_zone("hand"); + cmd.set_target_zone(ZoneNames::HAND); cmd.set_x(0); cmd.set_y(0); @@ -782,9 +783,9 @@ void PlayerActions::actDrawBottomCards() defaultNumberBottomCards = number; Command_MoveCard cmd; - cmd.set_start_zone("deck"); + cmd.set_start_zone(ZoneNames::DECK); cmd.set_target_player_id(player->getPlayerInfo()->getId()); - cmd.set_target_zone("hand"); + cmd.set_target_zone(ZoneNames::HAND); cmd.set_x(0); cmd.set_y(0); @@ -803,7 +804,7 @@ void PlayerActions::actMoveBottomCardToPlay() Command_MoveCard cmd; cmdSetBottomCard(cmd); - cmd.set_target_zone("stack"); + cmd.set_target_zone(ZoneNames::STACK); cmd.set_x(-1); cmd.set_y(0); @@ -820,13 +821,13 @@ void PlayerActions::actMoveBottomCardToPlayFaceDown() int lastCard = zone->getCards().size() - 1; Command_MoveCard cmd; - cmd.set_start_zone("deck"); + cmd.set_start_zone(ZoneNames::DECK); auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); cardToMove->set_card_id(lastCard); cardToMove->set_face_down(true); cmd.set_target_player_id(player->getPlayerInfo()->getId()); - cmd.set_target_zone("table"); + cmd.set_target_zone(ZoneNames::TABLE); cmd.set_x(-1); cmd.set_y(0); @@ -836,7 +837,7 @@ void PlayerActions::actMoveBottomCardToPlayFaceDown() void PlayerActions::actUntapAll() { Command_SetCardAttr cmd; - cmd.set_zone("table"); + cmd.set_zone(ZoneNames::TABLE); cmd.set_attribute(AttrTapped); cmd.set_attr_value("0"); @@ -886,7 +887,7 @@ void PlayerActions::actCreateAnotherToken() } Command_CreateToken cmd; - cmd.set_zone("table"); + cmd.set_zone(ZoneNames::TABLE); cmd.set_card_name(lastTokenInfo.name.toStdString()); cmd.set_card_provider_id(lastTokenInfo.providerId.toStdString()); cmd.set_color(lastTokenInfo.color.toStdString()); @@ -1067,7 +1068,7 @@ bool PlayerActions::createRelatedFromRelation(const CardItem *sourceCard, const // move card onto table first if attaching from some other zone // we only do this for AttachTo because cross-zone TransformInto is already handled server-side - if (attachType == CardRelationType::AttachTo && sourceCard->getZone()->getName() != "table") { + if (attachType == CardRelationType::AttachTo && sourceCard->getZone()->getName() != ZoneNames::TABLE) { playCardToTable(sourceCard, false); } @@ -1093,7 +1094,7 @@ void PlayerActions::createCard(const CardItem *sourceCard, // create the token for the related card Command_CreateToken cmd; - cmd.set_zone("table"); + cmd.set_zone(ZoneNames::TABLE); cmd.set_card_name(cardInfo->getName().toStdString()); switch (cardInfo->getColors().size()) { case 0: @@ -1122,12 +1123,12 @@ void PlayerActions::createCard(const CardItem *sourceCard, switch (attachType) { case CardRelationType::DoesNotAttach: - cmd.set_target_zone("table"); + cmd.set_target_zone(ZoneNames::TABLE); cmd.set_card_provider_id(relatedCard.getPrinting().getUuid().toStdString()); break; case CardRelationType::AttachTo: - cmd.set_target_zone("table"); // We currently only support creating tokens on the table + cmd.set_target_zone(ZoneNames::TABLE); // We currently only support creating tokens on the table cmd.set_card_provider_id(relatedCard.getPrinting().getUuid().toStdString()); cmd.set_target_card_id(sourceCard->getId()); cmd.set_target_mode(Command_CreateToken::ATTACH_TO); @@ -1135,7 +1136,7 @@ void PlayerActions::createCard(const CardItem *sourceCard, case CardRelationType::TransformInto: // allow cards to directly transform on stack - cmd.set_zone(sourceCard->getZone()->getName() == "stack" ? "stack" : "table"); + cmd.set_zone(sourceCard->getZone()->getName() == ZoneNames::STACK ? ZoneNames::STACK : ZoneNames::TABLE); // Transform card zone changes are handled server-side cmd.set_target_zone(sourceCard->getZone()->getName().toStdString()); cmd.set_target_card_id(sourceCard->getId()); @@ -1250,7 +1251,7 @@ void PlayerActions::actMoveCardXCardsFromTop() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->set_target_player_id(player->getPlayerInfo()->getId()); - cmd->set_target_zone("deck"); + cmd->set_target_zone(ZoneNames::DECK); cmd->set_x(number); cmd->set_y(0); commandList.append(cmd); @@ -1639,7 +1640,7 @@ void PlayerActions::playSelectedCards(const bool faceDown) [](const auto &card1, const auto &card2) { return card1->getId() > card2->getId(); }); for (auto &card : selectedCards) { - if (card && !isUnwritableRevealZone(card->getZone()) && card->getZone()->getName() != "table") { + if (card && !isUnwritableRevealZone(card->getZone()) && card->getZone()->getName() != ZoneNames::TABLE) { playCard(card, faceDown); } } @@ -1692,7 +1693,7 @@ void PlayerActions::actRevealHand(int revealToPlayerId) if (revealToPlayerId != -1) { cmd.set_player_id(revealToPlayerId); } - cmd.set_zone_name("hand"); + cmd.set_zone_name(ZoneNames::HAND); sendGameCommand(cmd); } @@ -1703,7 +1704,7 @@ void PlayerActions::actRevealRandomHandCard(int revealToPlayerId) if (revealToPlayerId != -1) { cmd.set_player_id(revealToPlayerId); } - cmd.set_zone_name("hand"); + cmd.set_zone_name(ZoneNames::HAND); cmd.add_card_id(RANDOM_CARD_FROM_ZONE); sendGameCommand(cmd); @@ -1715,7 +1716,7 @@ void PlayerActions::actRevealLibrary(int revealToPlayerId) if (revealToPlayerId != -1) { cmd.set_player_id(revealToPlayerId); } - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); sendGameCommand(cmd); } @@ -1726,7 +1727,7 @@ void PlayerActions::actLendLibrary(int lendToPlayerId) if (lendToPlayerId != -1) { cmd.set_player_id(lendToPlayerId); } - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); cmd.set_grant_write_access(true); sendGameCommand(cmd); @@ -1739,7 +1740,7 @@ void PlayerActions::actRevealTopCards(int revealToPlayerId, int amount) cmd.set_player_id(revealToPlayerId); } - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); cmd.set_top_cards(amount); // backward compatibility: servers before #1051 only permits to reveal the first card cmd.add_card_id(0); @@ -1753,7 +1754,7 @@ void PlayerActions::actRevealRandomGraveyardCard(int revealToPlayerId) if (revealToPlayerId != -1) { cmd.set_player_id(revealToPlayerId); } - cmd.set_zone_name("grave"); + cmd.set_zone_name(ZoneNames::GRAVE); cmd.add_card_id(RANDOM_CARD_FROM_ZONE); sendGameCommand(cmd); } @@ -1816,7 +1817,7 @@ void PlayerActions::cardMenuAction() } case cmClone: { auto *cmd = new Command_CreateToken; - cmd->set_zone("table"); + cmd->set_zone(ZoneNames::TABLE); cmd->set_card_name(card->getName().toStdString()); cmd->set_card_provider_id(card->getProviderId().toStdString()); cmd->set_color(card->getColor().toStdString()); @@ -1858,13 +1859,13 @@ void PlayerActions::cardMenuAction() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->set_target_player_id(player->getPlayerInfo()->getId()); - cmd->set_target_zone("deck"); + cmd->set_target_zone(ZoneNames::DECK); cmd->set_x(0); cmd->set_y(0); if (idList.card_size() > 1) { auto *scmd = new Command_Shuffle; - scmd->set_zone_name("deck"); + scmd->set_zone_name(ZoneNames::DECK); scmd->set_start(0); scmd->set_end(idList.card_size() - 1); // inclusive, the indexed card at end will be shuffled // Server process events backwards, so... @@ -1880,13 +1881,13 @@ void PlayerActions::cardMenuAction() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->set_target_player_id(player->getPlayerInfo()->getId()); - cmd->set_target_zone("deck"); + cmd->set_target_zone(ZoneNames::DECK); cmd->set_x(-1); cmd->set_y(0); if (idList.card_size() > 1) { auto *scmd = new Command_Shuffle; - scmd->set_zone_name("deck"); + scmd->set_zone_name(ZoneNames::DECK); scmd->set_start(-idList.card_size()); scmd->set_end(-1); // Server process events backwards, so... @@ -1902,7 +1903,7 @@ void PlayerActions::cardMenuAction() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->set_target_player_id(player->getPlayerInfo()->getId()); - cmd->set_target_zone("hand"); + cmd->set_target_zone(ZoneNames::HAND); cmd->set_x(0); cmd->set_y(0); commandList.append(cmd); @@ -1914,7 +1915,7 @@ void PlayerActions::cardMenuAction() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->set_target_player_id(player->getPlayerInfo()->getId()); - cmd->set_target_zone("grave"); + cmd->set_target_zone(ZoneNames::GRAVE); cmd->set_x(0); cmd->set_y(0); commandList.append(cmd); @@ -1926,7 +1927,7 @@ void PlayerActions::cardMenuAction() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->set_target_player_id(player->getPlayerInfo()->getId()); - cmd->set_target_zone("rfg"); + cmd->set_target_zone(ZoneNames::EXILE); cmd->set_x(0); cmd->set_y(0); commandList.append(cmd); diff --git a/cockatrice/src/game/player/player_event_handler.cpp b/cockatrice/src/game/player/player_event_handler.cpp index 331605918..f4c3840e0 100644 --- a/cockatrice/src/game/player/player_event_handler.cpp +++ b/cockatrice/src/game/player/player_event_handler.cpp @@ -30,6 +30,7 @@ #include #include #include +#include PlayerEventHandler::PlayerEventHandler(Player *_player) : player(_player) { @@ -321,8 +322,8 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv } player->getPlayerMenu()->updateCardMenu(card); - if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == "deck" && - targetZone->getName() == "stack") { + if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == ZoneNames::DECK && + targetZone->getName() == ZoneNames::STACK) { player->getPlayerActions()->moveOneCardUntil(card); } } diff --git a/cockatrice/src/game/zones/logic/card_zone_logic.cpp b/cockatrice/src/game/zones/logic/card_zone_logic.cpp index 1872ba95e..e917e4ad7 100644 --- a/cockatrice/src/game/zones/logic/card_zone_logic.cpp +++ b/cockatrice/src/game/zones/logic/card_zone_logic.cpp @@ -10,6 +10,7 @@ #include #include #include +#include /** * @param _player the player that the zone belongs to @@ -174,9 +175,9 @@ void CardZoneLogic::clearContents() QString CardZoneLogic::getTranslatedName(bool theirOwn, GrammaticalCase gc) const { QString ownerName = player->getPlayerInfo()->getName(); - if (name == "hand") + if (name == ZoneNames::HAND) return (theirOwn ? tr("their hand", "nominative") : tr("%1's hand", "nominative").arg(ownerName)); - else if (name == "deck") + else if (name == ZoneNames::DECK) switch (gc) { case CaseLookAtZone: return (theirOwn ? tr("their library", "look at zone") @@ -192,11 +193,11 @@ QString CardZoneLogic::getTranslatedName(bool theirOwn, GrammaticalCase gc) cons default: return (theirOwn ? tr("their library", "nominative") : tr("%1's library", "nominative").arg(ownerName)); } - else if (name == "grave") + else if (name == ZoneNames::GRAVE) return (theirOwn ? tr("their graveyard", "nominative") : tr("%1's graveyard", "nominative").arg(ownerName)); - else if (name == "rfg") + else if (name == ZoneNames::EXILE) return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName)); - else if (name == "sb") + else if (name == ZoneNames::SIDEBOARD) switch (gc) { case CaseLookAtZone: return (theirOwn ? tr("their sideboard", "look at zone") diff --git a/cockatrice/src/game/zones/table_zone.cpp b/cockatrice/src/game/zones/table_zone.cpp index a020e4255..b6ac2150b 100644 --- a/cockatrice/src/game/zones/table_zone.cpp +++ b/cockatrice/src/game/zones/table_zone.cpp @@ -15,6 +15,7 @@ #include #include #include +#include const QColor TableZone::BACKGROUND_COLOR = QColor(100, 100, 100); const QColor TableZone::FADE_MASK = QColor(0, 0, 0, 80); @@ -195,7 +196,7 @@ void TableZone::toggleTapped() auto isCardOnTable = [](const QGraphicsItem *item) { if (auto card = qgraphicsitem_cast(item)) { - return card->getZone()->getName() == "table"; + return card->getZone()->getName() == ZoneNames::TABLE; } return false; }; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp index 00561bef2..f04bcc849 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp @@ -48,6 +48,7 @@ #include #include #include +#include Server_AbstractPlayer::Server_AbstractPlayer(Server_Game *_game, int _playerId, @@ -190,8 +191,8 @@ shouldDestroyOnMove(const Server_Card *card, const Server_CardZone *startZone, c } // Allow tokens on the stack - if ((startZone->getName() == "table" || startZone->getName() == "stack") && - (targetZone->getName() == "table" || targetZone->getName() == "stack")) { + if ((startZone->getName() == ZoneNames::TABLE || startZone->getName() == ZoneNames::STACK) && + (targetZone->getName() == ZoneNames::TABLE || targetZone->getName() == ZoneNames::STACK)) { return false; } @@ -264,7 +265,7 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges, } // do not allow attached cards to move around on the table - if (card->getParentCard() && targetzone->getName() == "table") { + if (card->getParentCard() && targetzone->getName() == ZoneNames::TABLE) { continue; } @@ -347,7 +348,7 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges, newX = targetzone->getFreeGridColumn(newX, yCoord, card->getName(), faceDown); } else { yCoord = 0; - card->resetState(targetzone->getName() == "stack"); + card->resetState(targetzone->getName() == ZoneNames::STACK); } targetzone->insertCard(card, newX, yCoord); diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp index eae23dacf..2224ddb13 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp @@ -51,6 +51,7 @@ #include #include #include +#include Server_Game::Server_Game(const ServerInfo_User &_creatorInfo, int _gameId, @@ -832,7 +833,7 @@ void Server_Game::returnCardsFromPlayer(GameEventStorage &ges, Server_AbstractPl QMutexLocker locker(&gameMutex); // Return cards to their rightful owners before conceding the game static const QRegularExpression ownerRegex{"Owner: ?([^\n]+)"}; - const auto &playerTable = player->getZones().value("table"); + const auto &playerTable = player->getZones().value(ZoneNames::TABLE); for (const auto &card : playerTable->getCards()) { if (card == nullptr) { continue; @@ -858,7 +859,7 @@ void Server_Game::returnCardsFromPlayer(GameEventStorage &ges, Server_AbstractPl continue; } - const auto &targetZone = otherPlayer->getZones().value("table"); + const auto &targetZone = otherPlayer->getZones().value(ZoneNames::TABLE); if (playerTable == nullptr || targetZone == nullptr) { continue; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp index e62f861a9..1175e4b57 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp @@ -47,6 +47,7 @@ #include #include #include +#include Server_Player::Server_Player(Server_Game *_game, int _playerId, @@ -80,15 +81,15 @@ void Server_Player::setupZones() // ------------------------------------------------------------------ // Create zones - auto *deckZone = new Server_CardZone(this, "deck", false, ServerInfo_Zone::HiddenZone); + auto *deckZone = new Server_CardZone(this, ZoneNames::DECK, false, ServerInfo_Zone::HiddenZone); addZone(deckZone); - auto *sbZone = new Server_CardZone(this, "sb", false, ServerInfo_Zone::HiddenZone); + auto *sbZone = new Server_CardZone(this, ZoneNames::SIDEBOARD, false, ServerInfo_Zone::HiddenZone); addZone(sbZone); - addZone(new Server_CardZone(this, "table", true, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, "hand", false, ServerInfo_Zone::PrivateZone)); - addZone(new Server_CardZone(this, "stack", false, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, "grave", false, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, "rfg", false, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, ZoneNames::TABLE, true, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, ZoneNames::HAND, false, ServerInfo_Zone::PrivateZone)); + addZone(new Server_CardZone(this, ZoneNames::STACK, false, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, ZoneNames::GRAVE, false, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, ZoneNames::EXILE, false, ServerInfo_Zone::PublicZone)); addCounter(new Server_Counter(0, "life", makeColor(255, 255, 255), 25, game->getStartingLifeTotal())); addCounter(new Server_Counter(1, "w", makeColor(255, 255, 150), 20, 0)); @@ -164,8 +165,8 @@ void Server_Player::addCounter(Server_Counter *counter) Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int number) { - Server_CardZone *deckZone = zones.value("deck"); - Server_CardZone *handZone = zones.value("hand"); + Server_CardZone *deckZone = zones.value(ZoneNames::DECK); + Server_CardZone *handZone = zones.value(ZoneNames::HAND); if (deckZone->getCards().size() < number) { number = deckZone->getCards().size(); } @@ -210,7 +211,7 @@ void Server_Player::onCardBeingMoved(GameEventStorage &ges, // "Undo draw" should only remain valid if the just-drawn card stays within the user's hand (e.g., they only // reorder their hand). If a just-drawn card leaves the hand then remove cards before it from the list // (Ignore the case where the card is currently being un-drawn.) - if (startzone->getName() == "hand" && targetzone->getName() != "hand" && !undoingDraw) { + if (startzone->getName() == ZoneNames::HAND && targetzone->getName() != ZoneNames::HAND && !undoingDraw) { int index = lastDrawList.lastIndexOf(card->getId()); if (index != -1) { lastDrawList.erase(lastDrawList.begin(), lastDrawList.begin() + index); @@ -326,11 +327,11 @@ Server_Player::cmdShuffle(const Command_Shuffle &cmd, ResponseContainer & /*rc*/ return Response::RespContextError; } - if (cmd.has_zone_name() && cmd.zone_name() != "deck") { + if (cmd.has_zone_name() && cmd.zone_name() != ZoneNames::DECK) { return Response::RespFunctionNotAllowed; } - Server_CardZone *zone = zones.value("deck"); + Server_CardZone *zone = zones.value(ZoneNames::DECK); if (!zone) { return Response::RespNameNotFound; } @@ -357,8 +358,8 @@ Server_Player::cmdMulligan(const Command_Mulligan &cmd, ResponseContainer & /*rc return Response::RespContextError; } - Server_CardZone *hand = zones.value("hand"); - Server_CardZone *_deck = zones.value("deck"); + Server_CardZone *hand = zones.value(ZoneNames::HAND); + Server_CardZone *_deck = zones.value(ZoneNames::DECK); int number = cmd.number(); if (!hand->getCards().isEmpty()) { @@ -414,8 +415,8 @@ Server_Player::cmdUndoDraw(const Command_UndoDraw & /*cmd*/, ResponseContainer & Response::ResponseCode retVal; auto *cardToMove = new CardToMove; cardToMove->set_card_id(lastDrawList.takeLast()); - retVal = moveCard(ges, zones.value("hand"), QList() << cardToMove, zones.value("deck"), 0, 0, - false, true); + retVal = moveCard(ges, zones.value(ZoneNames::HAND), QList() << cardToMove, + zones.value(ZoneNames::DECK), 0, 0, false, true); delete cardToMove; return retVal; diff --git a/libcockatrice_utility/CMakeLists.txt b/libcockatrice_utility/CMakeLists.txt index 0575c260f..c0c7d8cc9 100644 --- a/libcockatrice_utility/CMakeLists.txt +++ b/libcockatrice_utility/CMakeLists.txt @@ -10,8 +10,13 @@ set(UTILITY_SOURCES libcockatrice/utility/expression.cpp libcockatrice/utility/l ) set(UTILITY_HEADERS - libcockatrice/utility/color.h libcockatrice/utility/expression.h libcockatrice/utility/levenshtein.h - libcockatrice/utility/macros.h libcockatrice/utility/passwordhasher.h libcockatrice/utility/trice_limits.h + libcockatrice/utility/color.h + libcockatrice/utility/expression.h + libcockatrice/utility/levenshtein.h + libcockatrice/utility/macros.h + libcockatrice/utility/passwordhasher.h + libcockatrice/utility/trice_limits.h + libcockatrice/utility/zone_names.h ) add_library(libcockatrice_utility STATIC ${UTILITY_SOURCES} ${UTILITY_HEADERS}) diff --git a/libcockatrice_utility/libcockatrice/utility/zone_names.h b/libcockatrice_utility/libcockatrice/utility/zone_names.h new file mode 100644 index 000000000..d1463de6a --- /dev/null +++ b/libcockatrice_utility/libcockatrice/utility/zone_names.h @@ -0,0 +1,19 @@ +#ifndef ZONE_NAMES_H +#define ZONE_NAMES_H + +namespace ZoneNames +{ +// Protocol-level zone identifiers shared between client and server. +// These must match exactly across all components. + +constexpr const char *TABLE = "table"; +constexpr const char *GRAVE = "grave"; +constexpr const char *EXILE = "rfg"; // "removed from game" +constexpr const char *HAND = "hand"; +constexpr const char *DECK = "deck"; +constexpr const char *SIDEBOARD = "sb"; +constexpr const char *STACK = "stack"; + +} // namespace ZoneNames + +#endif // ZONE_NAMES_H From 20ad9af989ffd2caace45170fc01af46fd43d818 Mon Sep 17 00:00:00 2001 From: RickyRister <42636155+RickyRister@users.noreply.github.com> Date: Thu, 12 Mar 2026 11:00:32 -0700 Subject: [PATCH 28/28] [CacheSettings] Refactor country list creation (#6687) --- .../src/client/settings/cache_settings.cpp | 266 +----------------- 1 file changed, 15 insertions(+), 251 deletions(-) diff --git a/cockatrice/src/client/settings/cache_settings.cpp b/cockatrice/src/client/settings/cache_settings.cpp index 30093fd52..e7a5d114e 100644 --- a/cockatrice/src/client/settings/cache_settings.cpp +++ b/cockatrice/src/client/settings/cache_settings.cpp @@ -1115,257 +1115,21 @@ void SettingsCache::setClientVersion(const QString &_clientVersion) QStringList SettingsCache::getCountries() const { - static QStringList countries = QStringList() << "ad" - << "ae" - << "af" - << "ag" - << "ai" - << "al" - << "am" - << "ao" - << "aq" - << "ar" - << "as" - << "at" - << "au" - << "aw" - << "ax" - << "az" - << "ba" - << "bb" - << "bd" - << "be" - << "bf" - << "bg" - << "bh" - << "bi" - << "bj" - << "bl" - << "bm" - << "bn" - << "bo" - << "bq" - << "br" - << "bs" - << "bt" - << "bv" - << "bw" - << "by" - << "bz" - << "ca" - << "cc" - << "cd" - << "cf" - << "cg" - << "ch" - << "ci" - << "ck" - << "cl" - << "cm" - << "cn" - << "co" - << "cr" - << "cu" - << "cv" - << "cw" - << "cx" - << "cy" - << "cz" - << "de" - << "dj" - << "dk" - << "dm" - << "do" - << "dz" - << "ec" - << "ee" - << "eg" - << "eh" - << "er" - << "es" - << "et" - << "eu" - << "fi" - << "fj" - << "fk" - << "fm" - << "fo" - << "fr" - << "ga" - << "gb" - << "gd" - << "ge" - << "gf" - << "gg" - << "gh" - << "gi" - << "gl" - << "gm" - << "gn" - << "gp" - << "gq" - << "gr" - << "gs" - << "gt" - << "gu" - << "gw" - << "gy" - << "hk" - << "hm" - << "hn" - << "hr" - << "ht" - << "hu" - << "id" - << "ie" - << "il" - << "im" - << "in" - << "io" - << "iq" - << "ir" - << "is" - << "it" - << "je" - << "jm" - << "jo" - << "jp" - << "ke" - << "kg" - << "kh" - << "ki" - << "km" - << "kn" - << "kp" - << "kr" - << "kw" - << "ky" - << "kz" - << "la" - << "lb" - << "lc" - << "li" - << "lk" - << "lr" - << "ls" - << "lt" - << "lu" - << "lv" - << "ly" - << "ma" - << "mc" - << "md" - << "me" - << "mf" - << "mg" - << "mh" - << "mk" - << "ml" - << "mm" - << "mn" - << "mo" - << "mp" - << "mq" - << "mr" - << "ms" - << "mt" - << "mu" - << "mv" - << "mw" - << "mx" - << "my" - << "mz" - << "na" - << "nc" - << "ne" - << "nf" - << "ng" - << "ni" - << "nl" - << "no" - << "np" - << "nr" - << "nu" - << "nz" - << "om" - << "pa" - << "pe" - << "pf" - << "pg" - << "ph" - << "pk" - << "pl" - << "pm" - << "pn" - << "pr" - << "ps" - << "pt" - << "pw" - << "py" - << "qa" - << "re" - << "ro" - << "rs" - << "ru" - << "rw" - << "sa" - << "sb" - << "sc" - << "sd" - << "se" - << "sg" - << "sh" - << "si" - << "sj" - << "sk" - << "sl" - << "sm" - << "sn" - << "so" - << "sr" - << "ss" - << "st" - << "sv" - << "sx" - << "sy" - << "sz" - << "tc" - << "td" - << "tf" - << "tg" - << "th" - << "tj" - << "tk" - << "tl" - << "tm" - << "tn" - << "to" - << "tr" - << "tt" - << "tv" - << "tw" - << "tz" - << "ua" - << "ug" - << "um" - << "us" - << "uy" - << "uz" - << "va" - << "vc" - << "ve" - << "vg" - << "vi" - << "vn" - << "vu" - << "wf" - << "ws" - << "xk" - << "ye" - << "yt" - << "za" - << "zm" - << "zw"; + static const QStringList countries = { + "ad", "ae", "af", "ag", "ai", "al", "am", "ao", "aq", "ar", "as", "at", "au", "aw", "ax", "az", "ba", "bb", + "bd", "be", "bf", "bg", "bh", "bi", "bj", "bl", "bm", "bn", "bo", "bq", "br", "bs", "bt", "bv", "bw", "by", + "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr", "cu", "cv", "cw", "cx", + "cy", "cz", "de", "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er", "es", "et", "eu", "fi", "fj", + "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq", "gr", + "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr", "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", + "ir", "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", "ky", "kz", + "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc", "md", "me", "mf", "mg", "mh", + "mk", "ml", "mm", "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na", "nc", + "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu", "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", + "pm", "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "rs", "ru", "rw", "sa", "sb", "sc", "sd", "se", + "sg", "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "ss", "st", "sv", "sx", "sy", "sz", "tc", "td", + "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "um", "us", + "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu", "wf", "ws", "xk", "ye", "yt", "za", "zm", "zw"}; return countries; }