From f73196841aa2d3470dd883b4c4a34aa6ebc84ea6 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 19 Nov 2024 03:56:44 +0100 Subject: [PATCH] Multiple Printings per Deck (#5171) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor CardInfo Widgets to reside in their appropriate folder and to have a clearer naming structure. * Added Zach's work on storing printing information in the DeckList (#1) * Change CardInfo's PixmapCacheKey to be the UUID of the preferred set after database loading has finished. Otherwise, and if no UUID of a preferred set is available, default to the card name. * Refactor CardDatabase *db global variable to singleton CardDatabaseManager. This commit refactors the global variable CardDatabase *db into a singleton encapsulated by the DatabaseManager class, accessible via DatabaseManager::getInstance(). This change centralizes access to the database instance, improving code modularity and encapsulation, resolving dependencies on main.h for code that requires access to the database instance. - Added DatabaseManager class with getInstance() method returning a pointer to the singleton CardDatabase. - Removed global db variable and updated references across the codebase. - Thread-safe static initialization for the singleton. Impact: This refactor should have no functional impact on the application, as it maintains the same interface for accessing the CardDatabase instance. However, the codebase now benefits from improved encapsulation, lifetime management, and thread-safety. * fixed db issue an renamed sets to set in picture loader * canibalized zach work and added it to the decklist builder --------- Co-authored-by: Lukas Brübach * Reintroduce some changes lost in the merge. * Introduce UUID attribute to abstract_card_item, card_item, deck_view_card, server_card and serverinfo_card. * Have various game events respect the new UUID attribute on instantiation. * Correct some calls to default to preferred printing. * DeckList now tries to assign reasonable defaults for UUID and collectorNumber if none are found in loaded DeckLists. Rename overloaded DeckListModel findChild() function to findCardChildByNameAndUUID() for clarity. * canibalized zach work and added it to the decklist builder * Change getPreferredPrintingForCard to getPreferredSetForCard to reflect refactor. * Properly update and set the DeckEditor's CardFrame to fetch by name and UUID if a card was selected from the decklist. * Mainboard/Sideboard swaps should respect the UUID from the old zone instead of just blindly adding preferredPrinting. * If the card info is null, there's no point in trying to look for the sets. * Don't define methods twice. * Convenience method to fetch a specific CardInfoPerSet instance for a cardName and a UUID. * Check if the uuid starts with card_ when comparing. * Address pull request comments (nullptr checks and additional comments, mostly.) * Reformat code so the linter will stop yelling at me. * DeckList no longer pre-populates uuids. * Update Event_MoveCard to include the card UUID. * Update Player::MoveCard to include the card UUID. * Set the uuid when we set the cardName, in terms of hidden zones. * [TEST/RevertMe] Set the uuid everywhere to test. * Don't inline setUUID and mimic setName for AbstractCardItem. * Revert blindly setting uuid for testing. * Address PR comments (AbstractCardItem). * Combine if-statement. * Re-order uuid to visually align with its field number. * Remove unnecessary new uuid field from event_move_card. * Remove unused imports. * Include cardName in the PixmapCacheKey in order to not break double-faced cards. * Refactor setCode to cardUUID and introduce new cardSetShortName field. * Override * Refactor UUID to be providerId and change QString comparisons with empty string to isEmpty(). * Update translations. * Change parent to be the first argument. * Pull Parent argument up for CardItem. * Pull Parent argument up for CardItem. * Linter. --------- Co-authored-by: Lukas Brübach Co-authored-by: LunaticCat <39006478+LunyaticCat@users.noreply.github.com> Co-authored-by: luna Co-authored-by: ZeldaZach --- .../src/client/tabs/tab_deck_editor.cpp | 15 +- cockatrice/src/client/tabs/tab_game.cpp | 1 + cockatrice/src/client/ui/picture_loader.cpp | 9 +- .../widgets/cards/card_info_frame_widget.cpp | 5 + .../ui/widgets/cards/card_info_frame_widget.h | 1 + cockatrice/src/deck/deck_list_model.cpp | 111 +++-- cockatrice/src/deck/deck_list_model.h | 34 +- cockatrice/src/deck/deck_view.cpp | 10 +- cockatrice/src/deck/deck_view.h | 7 +- .../src/game/cards/abstract_card_item.cpp | 28 +- .../src/game/cards/abstract_card_item.h | 13 +- cockatrice/src/game/cards/card_database.cpp | 51 ++- cockatrice/src/game/cards/card_database.h | 9 +- cockatrice/src/game/cards/card_item.cpp | 9 +- cockatrice/src/game/cards/card_item.h | 3 +- cockatrice/src/game/player/player.cpp | 6 +- cockatrice/src/game/zones/card_zone.cpp | 3 +- cockatrice/src/game/zones/view_zone.cpp | 12 +- common/decklist.cpp | 43 +- common/decklist.h | 96 ++++- common/pb/event_move_card.proto | 1 + common/pb/serverinfo_card.proto | 1 + common/server_card.cpp | 14 +- common/server_card.h | 12 +- common/server_player.cpp | 11 +- webclient/src/i18n-default.json | 383 +----------------- 26 files changed, 397 insertions(+), 491 deletions(-) diff --git a/cockatrice/src/client/tabs/tab_deck_editor.cpp b/cockatrice/src/client/tabs/tab_deck_editor.cpp index 79f82ebc2..0c9edbb0b 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.cpp +++ b/cockatrice/src/client/tabs/tab_deck_editor.cpp @@ -724,8 +724,10 @@ void TabDeckEditor::updateCardInfoRight(const QModelIndex ¤t, const QModel { if (!current.isValid()) return; - if (!current.model()->hasChildren(current.sibling(current.row(), 0))) - cardInfo->setCard(current.sibling(current.row(), 1).data().toString()); + if (!current.model()->hasChildren(current.sibling(current.row(), 0))) { + cardInfo->setCard(current.sibling(current.row(), 1).data().toString(), + current.sibling(current.row(), 4).data().toString()); + } } void TabDeckEditor::updateSearch(const QString &search) @@ -1014,7 +1016,7 @@ void TabDeckEditor::addCardHelper(QString zoneName) if (info->getIsToken()) zoneName = DECK_ZONE_TOKENS; - QModelIndex newCardIndex = deckModel->addCard(info->getName(), zoneName); + QModelIndex newCardIndex = deckModel->addPreferredPrintingCard(info->getName(), zoneName, false); recursiveExpand(newCardIndex); deckView->setCurrentIndex(newCardIndex); setModified(true); @@ -1027,6 +1029,7 @@ void TabDeckEditor::actSwapCard() if (!currentIndex.isValid()) return; const QString cardName = currentIndex.sibling(currentIndex.row(), 1).data().toString(); + const QString cardProviderID = currentIndex.sibling(currentIndex.row(), 2).data().toString(); const QModelIndex gparent = currentIndex.parent().parent(); if (!gparent.isValid()) @@ -1036,8 +1039,10 @@ void TabDeckEditor::actSwapCard() actDecrement(); const QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; - // Third argument (true) says create the card no mater what, even if not in DB - QModelIndex newCardIndex = deckModel->addCard(cardName, otherZoneName, true); + // Third argument (true) says create the card no matter what, even if not in DB + QModelIndex newCardIndex = deckModel->addCard( + cardName, CardDatabaseManager::getInstance()->getSpecificSetForCard(cardName, cardProviderID), otherZoneName, + true); recursiveExpand(newCardIndex); setModified(true); diff --git a/cockatrice/src/client/tabs/tab_game.cpp b/cockatrice/src/client/tabs/tab_game.cpp index f27384274..68c60ff15 100644 --- a/cockatrice/src/client/tabs/tab_game.cpp +++ b/cockatrice/src/client/tabs/tab_game.cpp @@ -326,6 +326,7 @@ void DeckViewContainer::deckSelectFinished(const Response &r) { const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext); DeckLoader newDeck(QString::fromStdString(resp.deck())); + // TODO CHANGE THIS TO BE SELECTED BY UUID PictureLoader::cacheCardPixmaps(CardDatabaseManager::getInstance()->getCards(newDeck.getCardList())); setDeck(newDeck); } diff --git a/cockatrice/src/client/ui/picture_loader.cpp b/cockatrice/src/client/ui/picture_loader.cpp index e30e5fc58..ad18a9071 100644 --- a/cockatrice/src/client/ui/picture_loader.cpp +++ b/cockatrice/src/client/ui/picture_loader.cpp @@ -45,10 +45,11 @@ PictureToLoad::PictureToLoad(CardInfoPtr _card) std::sort(sortedSets.begin(), sortedSets.end(), SetDownloadPriorityComparator()); // If the pixmapCacheKey corresponds to a specific set, we have to try to load it first. for (const auto &set : card->getSets()) { - if (QLatin1String("card_") + QString(set.getProperty("uuid")) == card->getPixmapCacheKey()) { + if (QLatin1String("card_") + card->getName() + QString("_") + QString(set.getProperty("uuid")) == + card->getPixmapCacheKey()) { long long setIndex = sortedSets.indexOf(set.getPtr()); - CardSetPtr setForCardUUID = sortedSets.takeAt(setIndex); - sortedSets.prepend(setForCardUUID); + CardSetPtr setForCardProviderID = sortedSets.takeAt(setIndex); + sortedSets.prepend(setForCardProviderID); } } // The first time called, nextSet will also populate the Urls for the first set. @@ -178,7 +179,7 @@ void PictureLoaderWorker::processLoadQueue() qDebug().nospace() << "PictureLoader: [card: " << cardName << " set: " << setName << "]: Trying to load picture"; - if (CardDatabaseManager::getInstance()->isUuidForPreferredPrinting( + if (CardDatabaseManager::getInstance()->isProviderIdForPreferredPrinting( cardName, cardBeingLoaded.getCard()->getPixmapCacheKey())) { if (cardImageExistsOnDisk(setName, correctedCardName)) { continue; diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.cpp index 620543db0..fd9c84750 100644 --- a/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.cpp +++ b/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.cpp @@ -111,6 +111,11 @@ void CardInfoFrameWidget::setCard(const QString &cardName) setCard(CardDatabaseManager::getInstance()->guessCard(cardName)); } +void CardInfoFrameWidget::setCard(const QString &cardName, const QString &providerId) +{ + setCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, providerId)); +} + void CardInfoFrameWidget::setCard(AbstractCardItem *card) { if (card) { diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h b/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h index 2c2c4a3c8..a9a4a0597 100644 --- a/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h +++ b/cockatrice/src/client/ui/widgets/cards/card_info_frame_widget.h @@ -36,6 +36,7 @@ public: public slots: void setCard(CardInfoPtr card); void setCard(const QString &cardName); + void setCard(const QString &cardName, const QString &providerId); void setCard(AbstractCardItem *card); void clearCard(); void setViewMode(int mode); diff --git a/cockatrice/src/deck/deck_list_model.cpp b/cockatrice/src/deck/deck_list_model.cpp index f2ef81307..a33e72c7e 100644 --- a/cockatrice/src/deck/deck_list_model.cpp +++ b/cockatrice/src/deck/deck_list_model.cpp @@ -79,24 +79,24 @@ int DeckListModel::rowCount(const QModelIndex &parent) const int DeckListModel::columnCount(const QModelIndex & /*parent*/) const { - return 2; + return 5; } QVariant DeckListModel::data(const QModelIndex &index, int role) const { // debugIndexInfo("data", index); if (!index.isValid()) { - return QVariant(); + return {}; } if (index.column() >= columnCount()) { - return QVariant(); + return {}; } auto *temp = static_cast(index.internalPointer()); auto *card = dynamic_cast(temp); if (card == nullptr) { - auto *node = dynamic_cast(temp); + const auto *node = dynamic_cast(temp); switch (role) { case Qt::FontRole: { QFont f; @@ -108,13 +108,22 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const switch (index.column()) { case 0: return node->recursiveCount(true); - case 1: + case 1: { if (role == Qt::DisplayRole) return node->getVisibleName(); - else - return node->getName(); + return node->getName(); + } + case 2: { + return node->getCardSetShortName(); + } + case 3: { + return node->getCardCollectorNumber(); + } + case 4: { + return node->getCardProviderId(); + } default: - return QVariant(); + return {}; } } case Qt::BackgroundRole: { @@ -125,7 +134,7 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const return QBrush(QColor(0, 0, 0)); } default: - return QVariant(); + return {}; } } else { switch (role) { @@ -136,8 +145,14 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const return card->getNumber(); case 1: return card->getName(); + case 2: + return card->getCardSetShortName(); + case 3: + return card->getCardCollectorNumber(); + case 4: + return card->getCardProviderId(); default: - return QVariant(); + return {}; } } case Qt::BackgroundRole: { @@ -148,28 +163,34 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const return QBrush(QColor(0, 0, 0)); } default: - return QVariant(); + return {}; } } } -QVariant DeckListModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant DeckListModel::headerData(const int section, const Qt::Orientation orientation, const int role) const { if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal)) { - return QVariant(); + return {}; } if (section >= columnCount()) { - return QVariant(); + return {}; } switch (section) { case 0: - return tr("Number"); + return tr("Count"); case 1: return tr("Card"); + case 2: + return tr("Set"); + case 3: + return tr("Number"); + case 4: + return tr("Provider ID"); default: - return QVariant(); + return {}; } } @@ -215,7 +236,7 @@ void DeckListModel::emitRecursiveUpdates(const QModelIndex &index) emitRecursiveUpdates(index.parent()); } -bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, const int role) { auto *node = getNode(index); if (!node || (role != Qt::EditRole)) { @@ -229,6 +250,15 @@ bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, int case 1: node->setName(value.toString()); break; + case 2: + node->setCardSetShortName(value.toString()); + break; + case 3: + node->setCardCollectorNumber(value.toString()); + break; + case 4: + node->setCardProviderId(value.toString()); + break; default: return false; } @@ -279,7 +309,8 @@ InnerDecklistNode *DeckListModel::createNodeIfNeeded(const QString &name, InnerD return newNode; } -DecklistModelCardNode *DeckListModel::findCardNode(const QString &cardName, const QString &zoneName) const +DecklistModelCardNode * +DeckListModel::findCardNode(const QString &cardName, const QString &zoneName, const QString &providerId) const { InnerDecklistNode *zoneNode, *typeNode; CardInfoPtr info; @@ -301,14 +332,17 @@ DecklistModelCardNode *DeckListModel::findCardNode(const QString &cardName, cons return nullptr; } - return dynamic_cast(typeNode->findChild(cardName)); + if (providerId.isEmpty()) { + return dynamic_cast(typeNode->findChild(cardName)); + } + return dynamic_cast(typeNode->findCardChildByNameAndProviderId(cardName, providerId)); } -QModelIndex DeckListModel::findCard(const QString &cardName, const QString &zoneName) const +QModelIndex DeckListModel::findCard(const QString &cardName, const QString &zoneName, const QString &providerId) const { DecklistModelCardNode *cardNode; - cardNode = findCardNode(cardName, zoneName); + cardNode = findCardNode(cardName, zoneName, providerId); if (!cardNode) { return {}; } @@ -316,16 +350,27 @@ QModelIndex DeckListModel::findCard(const QString &cardName, const QString &zone return nodeToIndex(cardNode); } -QModelIndex DeckListModel::addCard(const QString &cardName, const QString &zoneName, bool abAddAnyway) +QModelIndex DeckListModel::addPreferredPrintingCard(const QString &cardName, const QString &zoneName, bool abAddAnyway) { - CardInfoPtr info = CardDatabaseManager::getInstance()->getCard(cardName); - if (info == nullptr) { + return addCard(cardName, CardDatabaseManager::getInstance()->getPreferredSetForCard(cardName), zoneName, + abAddAnyway); +} + +QModelIndex DeckListModel::addCard(const QString &cardName, + const CardInfoPerSet cardInfoSet, + const QString &zoneName, + bool abAddAnyway) +{ + CardInfoPtr cardInfo = + CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardInfoSet.getProperty("uuid")); + + if (cardInfo == nullptr) { if (abAddAnyway) { // We need to keep this card added no matter what // This is usually called from tab_deck_editor // So we'll create a new CardInfo with the name // and default values for all fields - info = CardInfo::newInstance(cardName); + cardInfo = CardInfo::newInstance(cardName); } else { return {}; } @@ -333,18 +378,24 @@ QModelIndex DeckListModel::addCard(const QString &cardName, const QString &zoneN InnerDecklistNode *zoneNode = createNodeIfNeeded(zoneName, root); - QString cardType = info->getMainCardType(); + const QString cardType = cardInfo->getMainCardType(); InnerDecklistNode *cardTypeNode = createNodeIfNeeded(cardType, zoneNode); - QModelIndex parentIndex = nodeToIndex(cardTypeNode); - auto *cardNode = dynamic_cast(cardTypeNode->findChild(cardName)); + const QModelIndex parentIndex = nodeToIndex(cardTypeNode); + auto *cardNode = dynamic_cast( + cardTypeNode->findCardChildByNameAndProviderId(cardName, cardInfoSet.getProperty("uuid"))); if (!cardNode) { - DecklistCardNode *decklistCard = deckList->addCard(cardName, zoneName); - beginInsertRows(parentIndex, cardTypeNode->size(), cardTypeNode->size()); + auto *decklistCard = + deckList->addCard(cardInfo->getName(), zoneName, cardInfoSet.getPtr()->getCorrectedShortName(), + cardInfoSet.getProperty("num"), cardInfoSet.getProperty("uuid")); + beginInsertRows(parentIndex, static_cast(cardTypeNode->size()), static_cast(cardTypeNode->size())); cardNode = new DecklistModelCardNode(decklistCard, cardTypeNode); endInsertRows(); } else { cardNode->setNumber(cardNode->getNumber() + 1); + cardNode->setCardSetShortName(cardInfoSet.getPtr()->getCorrectedShortName()); + cardNode->setCardCollectorNumber(cardInfoSet.getProperty("num")); + cardNode->setCardProviderId(cardInfoSet.getProperty("uuid")); deckList->updateDeckHash(); } sort(lastKnownColumn, lastKnownOrder); diff --git a/cockatrice/src/deck/deck_list_model.h b/cockatrice/src/deck/deck_list_model.h index 01cf61780..24eaea612 100644 --- a/cockatrice/src/deck/deck_list_model.h +++ b/cockatrice/src/deck/deck_list_model.h @@ -1,6 +1,7 @@ #ifndef DECKLISTMODEL_H #define DECKLISTMODEL_H +#include "../game/cards/card_database.h" #include "decklist.h" #include @@ -37,6 +38,30 @@ public: { dataNode->setName(_name); } + QString getCardProviderId() const override + { + return dataNode->getCardProviderId(); + } + void setCardProviderId(const QString &_cardProviderId) override + { + dataNode->setCardProviderId(_cardProviderId); + } + QString getCardSetShortName() const override + { + return dataNode->getCardSetShortName(); + } + void setCardSetShortName(const QString &_cardSetShortName) override + { + dataNode->setCardSetShortName(_cardSetShortName); + } + QString getCardCollectorNumber() const override + { + return dataNode->getCardCollectorNumber(); + } + void setCardCollectorNumber(const QString &_cardSetNumber) override + { + dataNode->setCardCollectorNumber(_cardSetNumber); + } DecklistCardNode *getDataNode() const { return dataNode; @@ -65,8 +90,10 @@ public: Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; bool removeRows(int row, int count, const QModelIndex &parent) override; - QModelIndex findCard(const QString &cardName, const QString &zoneName) const; - QModelIndex addCard(const QString &cardName, const QString &zoneName, bool abAddAnyway = false); + QModelIndex findCard(const QString &cardName, const QString &zoneName, const QString &providerId = "") const; + QModelIndex addPreferredPrintingCard(const QString &cardName, const QString &zoneName, bool abAddAnyway); + QModelIndex + addCard(const ::QString &cardName, CardInfoPerSet cardInfoSet, const QString &zoneName, bool abAddAnyway = false); void sort(int column, Qt::SortOrder order) override; void cleanList(); DeckLoader *getDeckList() const @@ -82,7 +109,8 @@ private: Qt::SortOrder lastKnownOrder; InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent); QModelIndex nodeToIndex(AbstractDecklistNode *node) const; - DecklistModelCardNode *findCardNode(const QString &cardName, const QString &zoneName) const; + DecklistModelCardNode * + findCardNode(const QString &cardName, const QString &zoneName, const QString &providerId = "") const; void emitRecursiveUpdates(const QModelIndex &index); void sortHelper(InnerDecklistNode *node, Qt::SortOrder order); diff --git a/cockatrice/src/deck/deck_view.cpp b/cockatrice/src/deck/deck_view.cpp index ff58fc882..9b7d77c5c 100644 --- a/cockatrice/src/deck/deck_view.cpp +++ b/cockatrice/src/deck/deck_view.cpp @@ -68,8 +68,11 @@ void DeckViewCardDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) event->accept(); } -DeckViewCard::DeckViewCard(const QString &_name, const QString &_originZone, QGraphicsItem *parent) - : AbstractCardItem(_name, 0, -1, parent), originZone(_originZone), dragItem(0) +DeckViewCard::DeckViewCard(QGraphicsItem *parent, + const QString &_name, + const QString &_providerId, + const QString &_originZone) + : AbstractCardItem(parent, _name, _providerId, 0, -1), originZone(_originZone), dragItem(0) { setAcceptHoverEvents(true); } @@ -354,7 +357,8 @@ void DeckViewScene::rebuildTree() continue; for (int k = 0; k < currentCard->getNumber(); ++k) { - DeckViewCard *newCard = new DeckViewCard(currentCard->getName(), currentZone->getName(), container); + DeckViewCard *newCard = new DeckViewCard(container, currentCard->getName(), + currentCard->getCardProviderId(), currentZone->getName()); container->addCard(newCard); emit newCardAdded(newCard); } diff --git a/cockatrice/src/deck/deck_view.h b/cockatrice/src/deck/deck_view.h index 11068d97b..88bbfdf39 100644 --- a/cockatrice/src/deck/deck_view.h +++ b/cockatrice/src/deck/deck_view.h @@ -24,9 +24,10 @@ private: DeckViewCardDragItem *dragItem; public: - DeckViewCard(const QString &_name = QString(), - const QString &_originZone = QString(), - QGraphicsItem *parent = nullptr); + DeckViewCard(QGraphicsItem *parent = nullptr, + const QString &_name = QString(), + const QString &_providerId = QString(), + const QString &_originZone = QString()); ~DeckViewCard(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); const QString &getOriginZone() const diff --git a/cockatrice/src/game/cards/abstract_card_item.cpp b/cockatrice/src/game/cards/abstract_card_item.cpp index 139ca6a08..80da703a7 100644 --- a/cockatrice/src/game/cards/abstract_card_item.cpp +++ b/cockatrice/src/game/cards/abstract_card_item.cpp @@ -1,7 +1,6 @@ #include "abstract_card_item.h" #include "../../client/ui/picture_loader.h" -#include "../../main.h" #include "../../settings/cache_settings.h" #include "../game_scene.h" #include "card_database.h" @@ -13,9 +12,13 @@ #include #include -AbstractCardItem::AbstractCardItem(const QString &_name, Player *_owner, int _id, QGraphicsItem *parent) - : ArrowTarget(_owner, parent), id(_id), name(_name), tapped(false), facedown(false), tapAngle(0), - bgColor(Qt::transparent), isHovered(false), realZValue(0) +AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, + const QString &_name, + const QString &_providerId, + Player *_owner, + int _id) + : ArrowTarget(_owner, parent), id(_id), name(_name), providerId(_providerId), tapped(false), facedown(false), + tapAngle(0), bgColor(Qt::transparent), isHovered(false), realZValue(0) { setCursor(Qt::OpenHandCursor); setFlag(ItemIsSelectable); @@ -50,7 +53,7 @@ void AbstractCardItem::pixmapUpdated() void AbstractCardItem::cardInfoUpdated() { - info = CardDatabaseManager::getInstance()->getCard(name); + info = CardDatabaseManager::getInstance()->getCardByNameAndProviderId(name, providerId); if (!info && !name.isEmpty()) { QVariantHash properties = QVariantHash(); @@ -185,6 +188,21 @@ void AbstractCardItem::setName(const QString &_name) cardInfoUpdated(); } +void AbstractCardItem::setProviderId(const QString &_providerId) +{ + if (providerId == _providerId) { + return; + } + + emit deleteCardInfoPopup(name); + if (info) { + disconnect(info.data(), nullptr, this, nullptr); + } + providerId = _providerId; + + cardInfoUpdated(); +} + void AbstractCardItem::setHovered(bool _hovered) { if (isHovered == _hovered) diff --git a/cockatrice/src/game/cards/abstract_card_item.h b/cockatrice/src/game/cards/abstract_card_item.h index 864783bbc..c7ecd0615 100644 --- a/cockatrice/src/game/cards/abstract_card_item.h +++ b/cockatrice/src/game/cards/abstract_card_item.h @@ -16,6 +16,7 @@ protected: CardInfoPtr info; int id; QString name; + QString providerId; bool tapped; bool facedown; int tapAngle; @@ -48,10 +49,11 @@ public: { return Type; } - AbstractCardItem(const QString &_name = QString(), + AbstractCardItem(QGraphicsItem *parent = nullptr, + const QString &_name = QString(), + const QString &_providerId = QString(), Player *_owner = nullptr, - int _id = -1, - QGraphicsItem *parent = nullptr); + int _id = -1); ~AbstractCardItem(); QRectF boundingRect() const override; QPainterPath shape() const override; @@ -75,6 +77,11 @@ public: return name; } void setName(const QString &_name = QString()); + QString getProviderId() const + { + return providerId; + } + void setProviderId(const QString &_providerId = QString()); qreal getRealZValue() const { return realZValue; diff --git a/cockatrice/src/game/cards/card_database.cpp b/cockatrice/src/game/cards/card_database.cpp index 9cff3d199..cd101d37f 100644 --- a/cockatrice/src/game/cards/card_database.cpp +++ b/cockatrice/src/game/cards/card_database.cpp @@ -441,13 +441,18 @@ QList CardDatabase::getCards(const QStringList &cardNames) const return cardInfos; } -CardInfoPtr CardDatabase::getCardByNameAndUUID(const QString &cardName, const QString &uuid) const +CardInfoPtr CardDatabase::getCardByNameAndProviderId(const QString &cardName, const QString &providerId) const { auto info = getCard(cardName); + if (providerId.isNull() || providerId.isEmpty() || info.isNull()) { + return info; + } + for (const auto &set : info->getSets()) { - if (set.getProperty("uuid") == uuid) { + if (set.getProperty("uuid") == providerId) { CardInfoPtr cardFromSpecificSet = info->clone(); - cardFromSpecificSet->setPixmapCacheKey(QLatin1String("card_") + QString(set.getProperty("uuid"))); + cardFromSpecificSet->setPixmapCacheKey(QLatin1String("card_") + QString(info->getName()) + QString("_") + + QString(set.getProperty("uuid"))); return cardFromSpecificSet; } } @@ -596,7 +601,8 @@ LoadStatus CardDatabase::loadCardDatabases() void CardDatabase::refreshPreferredPrintings() { for (const CardInfoPtr &card : cards) { - card->setPixmapCacheKey(QLatin1String("card_") + QString(getPreferredPrintingUUIDForCard(card->getName()))); + card->setPixmapCacheKey(QLatin1String("card_") + QString(card->getName()) + QString("_") + + QString(getPreferredPrintingProviderIdForCard(card->getName()))); } } @@ -631,23 +637,48 @@ CardInfoPerSet CardDatabase::getPreferredSetForCard(const QString &cardName) return CardInfoPerSet(nullptr); } -QString CardDatabase::getPreferredPrintingUUIDForCard(const QString &cardName) +CardInfoPerSet CardDatabase::getSpecificSetForCard(const QString &cardName, const QString &providerId) const +{ + CardInfoPtr cardInfo = getCard(cardName); + if (!cardInfo) { + return CardInfoPerSet(nullptr); + } + + CardInfoPerSetMap setMap = cardInfo->getSets(); + if (setMap.empty()) { + return CardInfoPerSet(nullptr); + } + + for (auto &cardInfoForSet : setMap) { + if (cardInfoForSet.getProperty("uuid") == providerId) { + return cardInfoForSet; + } + } + + return CardInfoPerSet(nullptr); +} + +QString CardDatabase::getPreferredPrintingProviderIdForCard(const QString &cardName) { CardInfoPerSet preferredSetCardInfo = getPreferredSetForCard(cardName); - QString preferredPrintingUUID = preferredSetCardInfo.getProperty(QString("uuid")); - if (preferredPrintingUUID.isEmpty()) { + QString preferredPrintingProviderId = preferredSetCardInfo.getProperty(QString("uuid")); + if (preferredPrintingProviderId.isEmpty()) { CardInfoPtr defaultCardInfo = getCard(cardName); if (defaultCardInfo.isNull()) { return cardName; } return defaultCardInfo->getName(); } - return preferredPrintingUUID; + return preferredPrintingProviderId; } -bool CardDatabase::isUuidForPreferredPrinting(const QString &cardName, const QString &uuid) +bool CardDatabase::isProviderIdForPreferredPrinting(const QString &cardName, const QString &providerId) { - return uuid == getPreferredPrintingUUIDForCard(cardName); + if (providerId.startsWith("card_")) { + return providerId == + QLatin1String("card_") + cardName + QString("_") + getPreferredPrintingProviderIdForCard(cardName); + } + return providerId == getPreferredPrintingProviderIdForCard(cardName); } void CardDatabase::refreshCachedReverseRelatedCards() diff --git a/cockatrice/src/game/cards/card_database.h b/cockatrice/src/game/cards/card_database.h index 5d360d39d..3322a5bb6 100644 --- a/cockatrice/src/game/cards/card_database.h +++ b/cockatrice/src/game/cards/card_database.h @@ -414,8 +414,6 @@ protected: private: CardInfoPtr getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const; void checkUnknownSets(); - CardInfoPerSet getPreferredSetForCard(const QString &cardName); - QString getPreferredPrintingUUIDForCard(const QString &cardName); void refreshCachedReverseRelatedCards(); QBasicMutex *reloadDatabaseMutex = new QBasicMutex(), *clearDatabaseMutex = new QBasicMutex(), @@ -431,7 +429,10 @@ public: void removeCard(CardInfoPtr card); CardInfoPtr getCard(const QString &cardName) const; QList getCards(const QStringList &cardNames) const; - CardInfoPtr getCardByNameAndUUID(const QString &cardName, const QString &uuid) const; + CardInfoPtr getCardByNameAndProviderId(const QString &cardName, const QString &providerId) const; + CardInfoPerSet getPreferredSetForCard(const QString &cardName); + CardInfoPerSet getSpecificSetForCard(const QString &cardName, const QString &providerId) const; + QString getPreferredPrintingProviderIdForCard(const QString &cardName); CardInfoPtr guessCard(const QString &cardName) const; /* @@ -441,7 +442,7 @@ public: CardInfoPtr getCardBySimpleName(const QString &cardName) const; CardSetPtr getSet(const QString &setName); - bool isUuidForPreferredPrinting(const QString &cardName, const QString &uuid); + bool isProviderIdForPreferredPrinting(const QString &cardName, const QString &providerId); QList getCardList() const { return cards.values(); diff --git a/cockatrice/src/game/cards/card_item.cpp b/cockatrice/src/game/cards/card_item.cpp index dd78aee04..0378c1909 100644 --- a/cockatrice/src/game/cards/card_item.cpp +++ b/cockatrice/src/game/cards/card_item.cpp @@ -1,7 +1,6 @@ #include "card_item.h" #include "../../client/tabs/tab_game.h" -#include "../../main.h" #include "../../settings/cache_settings.h" #include "../board/arrow_item.h" #include "../game_scene.h" @@ -19,13 +18,14 @@ #include CardItem::CardItem(Player *_owner, + QGraphicsItem *parent, const QString &_name, + const QString &_providerId, int _cardid, bool _revealedCard, - QGraphicsItem *parent, CardZone *_zone) - : AbstractCardItem(_name, _owner, _cardid, parent), zone(_zone), revealedCard(_revealedCard), attacking(false), - destroyOnZoneChange(false), doesntUntap(false), dragItem(nullptr), attachedTo(nullptr) + : AbstractCardItem(parent, _name, _providerId, _owner, _cardid), zone(_zone), revealedCard(_revealedCard), + attacking(false), destroyOnZoneChange(false), doesntUntap(false), dragItem(nullptr), attachedTo(nullptr) { owner->addCard(this); @@ -243,6 +243,7 @@ void CardItem::processCardInfo(const ServerInfo_Card &_info) } setId(_info.id()); + setProviderId(QString::fromStdString(_info.provider_id())); setName(QString::fromStdString(_info.name())); setAttacking(_info.attacking()); setFaceDown(_info.face_down()); diff --git a/cockatrice/src/game/cards/card_item.h b/cockatrice/src/game/cards/card_item.h index 21224878f..bb037ead9 100644 --- a/cockatrice/src/game/cards/card_item.h +++ b/cockatrice/src/game/cards/card_item.h @@ -50,10 +50,11 @@ public: return Type; } CardItem(Player *_owner, + QGraphicsItem *parent = nullptr, const QString &_name = QString(), + const QString &_providerId = QString(), int _cardid = -1, bool revealedCard = false, - QGraphicsItem *parent = nullptr, CardZone *_zone = nullptr); ~CardItem(); void retranslateUi(); diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp index f51006d4f..dd98305ff 100644 --- a/cockatrice/src/game/player/player.cpp +++ b/cockatrice/src/game/player/player.cpp @@ -2031,7 +2031,7 @@ void Player::eventCreateToken(const Event_CreateToken &event) return; } - CardItem *card = new CardItem(this, QString::fromStdString(event.card_name()), event.card_id()); + CardItem *card = new CardItem(this, nullptr, QString::fromStdString(event.card_name()), QString(), event.card_id()); // use db PT if not provided in event if (!QString::fromStdString(event.pt()).isEmpty()) { card->setPT(QString::fromStdString(event.pt())); @@ -2168,6 +2168,9 @@ void Player::eventMoveCard(const Event_MoveCard &event, const GameEventContext & if (event.has_card_name()) { card->setName(QString::fromStdString(event.card_name())); } + if (event.has_new_card_provider_id()) { + card->setProviderId(QString::fromStdString(event.new_card_provider_id())); + } if (card->getAttachedTo() && (startZone != targetZone)) { CardItem *parentCard = card->getAttachedTo(); @@ -2326,6 +2329,7 @@ void Player::eventDrawCards(const Event_DrawCards &event) for (int i = 0; i < listSize; ++i) { const ServerInfo_Card &cardInfo = event.cards(i); CardItem *card = _deck->takeCard(0, cardInfo.id()); + card->setProviderId(QString::fromStdString(cardInfo.provider_id())); card->setName(QString::fromStdString(cardInfo.name())); _hand->addCard(card, false, -1); } diff --git a/cockatrice/src/game/zones/card_zone.cpp b/cockatrice/src/game/zones/card_zone.cpp index b0e06f323..13732f960 100644 --- a/cockatrice/src/game/zones/card_zone.cpp +++ b/cockatrice/src/game/zones/card_zone.cpp @@ -126,7 +126,8 @@ void CardZone::addCard(CardItem *card, bool reorganize, int x, int y) { for (auto *view : views) { if ((x <= view->getCards().size()) || (view->getNumberCards() == -1)) { - view->addCard(new CardItem(player, card->getName(), card->getId()), reorganize, x, y); + view->addCard(new CardItem(player, nullptr, card->getName(), card->getProviderId(), card->getId()), + reorganize, x, y); } } diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp index 6cf6ad52e..6d4908179 100644 --- a/cockatrice/src/game/zones/view_zone.cpp +++ b/cockatrice/src/game/zones/view_zone.cpp @@ -56,9 +56,9 @@ void ZoneViewZone::initializeCards(const QList &cardLis { if (!cardList.isEmpty()) { for (int i = 0; i < cardList.size(); ++i) - addCard( - new CardItem(player, QString::fromStdString(cardList[i]->name()), cardList[i]->id(), revealZone, this), - false, i); + addCard(new CardItem(player, this, QString::fromStdString(cardList[i]->name()), + QString::fromStdString(cardList[i]->provider_id()), cardList[i]->id(), revealZone), + false, i); reorganizeCards(); } else if (!origZone->contentsKnown()) { Command_DumpZone cmd; @@ -75,7 +75,8 @@ 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); - addCard(new CardItem(player, card->getName(), card->getId(), revealZone, this), false, i); + addCard(new CardItem(player, this, card->getName(), card->getProviderId(), card->getId(), revealZone), + false, i); } reorganizeCards(); } @@ -88,7 +89,8 @@ void ZoneViewZone::zoneDumpReceived(const Response &r) for (int i = 0; i < respCardListSize; ++i) { const ServerInfo_Card &cardInfo = resp.zone_info().card_list(i); auto cardName = QString::fromStdString(cardInfo.name()); - auto *card = new CardItem(player, cardName, cardInfo.id(), revealZone, this, this); + auto cardProviderId = QString::fromStdString(cardInfo.provider_id()); + auto *card = new CardItem(player, this, cardName, cardProviderId, cardInfo.id(), revealZone, this); cards.insert(i, card); } reorganizeCards(); diff --git a/common/decklist.cpp b/common/decklist.cpp index 1e118d22c..011c9e0f3 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -88,7 +88,8 @@ int AbstractDecklistNode::depth() const } InnerDecklistNode::InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent) - : AbstractDecklistNode(_parent), name(other->getName()) + : AbstractDecklistNode(_parent), name(other->getName()), cardSetShortName(other->getCardSetShortName()), + cardCollectorNumber(other->getCardCollectorNumber()), cardProviderId(other->getCardProviderId()) { for (int i = 0; i < other->size(); ++i) { auto *inner = dynamic_cast(other->at(i)); @@ -139,7 +140,9 @@ void InnerDecklistNode::clearTree() } DecklistCardNode::DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent) - : AbstractDecklistCardNode(_parent), name(other->getName()), number(other->getNumber()) + : AbstractDecklistCardNode(_parent), name(other->getName()), number(other->getNumber()), + cardSetShortName(other->getCardSetShortName()), cardSetNumber(other->getCardCollectorNumber()), + cardProviderId(other->getCardProviderId()) { } @@ -153,6 +156,17 @@ AbstractDecklistNode *InnerDecklistNode::findChild(const QString &_name) return nullptr; } +AbstractDecklistNode *InnerDecklistNode::findCardChildByNameAndProviderId(const QString &_name, + const QString &_providerId) +{ + for (int i = 0; i < size(); i++) { + if (at(i) != nullptr && at(i)->getName() == _name && at(i)->getCardProviderId() == _providerId) { + return at(i); + } + } + return nullptr; +} + int InnerDecklistNode::height() const { return at(0)->height() + 1; @@ -268,9 +282,10 @@ bool InnerDecklistNode::readElement(QXmlStreamReader *xml) InnerDecklistNode *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), this); newZone->readElement(xml); } else if (childName == "card") { - DecklistCardNode *newCard = - new DecklistCardNode(xml->attributes().value("name").toString(), - xml->attributes().value("number").toString().toInt(), this); + DecklistCardNode *newCard = new DecklistCardNode( + xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(), + this, xml->attributes().value("setShortName").toString(), + xml->attributes().value("collectorNumber").toString(), xml->attributes().value("uuid").toString()); newCard->readElement(xml); } } else if (xml->isEndElement() && (childName == "zone")) @@ -303,6 +318,16 @@ void AbstractDecklistCardNode::writeElement(QXmlStreamWriter *xml) xml->writeEmptyElement("card"); xml->writeAttribute("number", QString::number(getNumber())); xml->writeAttribute("name", getName()); + + if (getCardSetShortName().isEmpty()) { + xml->writeAttribute("setShortName", getCardSetShortName()); + } + if (getCardCollectorNumber().isEmpty()) { + xml->writeAttribute("collectorNumber", getCardCollectorNumber()); + } + if (getCardProviderId().isEmpty()) { + xml->writeAttribute("uuid", getCardProviderId()); + } } QVector> InnerDecklistNode::sort(Qt::SortOrder order) @@ -740,14 +765,18 @@ int DeckList::getSideboardSize() const return size; } -DecklistCardNode *DeckList::addCard(const QString &cardName, const QString &zoneName) +DecklistCardNode *DeckList::addCard(const QString &cardName, + const QString &zoneName, + const QString &cardSetName, + const QString &cardSetCollectorNumber, + const QString &cardProviderId) { auto *zoneNode = dynamic_cast(root->findChild(zoneName)); if (zoneNode == nullptr) { zoneNode = new InnerDecklistNode(zoneName, root); } - auto *node = new DecklistCardNode(cardName, 1, zoneNode); + auto *node = new DecklistCardNode(cardName, 1, zoneNode, cardSetName, cardSetCollectorNumber, cardProviderId); updateDeckHash(); return node; diff --git a/common/decklist.h b/common/decklist.h index bacba0ce6..ad5f59b22 100644 --- a/common/decklist.h +++ b/common/decklist.h @@ -1,12 +1,7 @@ #ifndef DECKLIST_H #define DECKLIST_H -#include #include -#include -#include -#include -#include #include // Required on Mac. Forward declaration doesn't work. Don't ask why. @@ -68,6 +63,9 @@ public: sortMethod = method; } virtual QString getName() const = 0; + virtual QString getCardProviderId() const = 0; + virtual QString getCardSetShortName() const = 0; + virtual QString getCardCollectorNumber() const = 0; InnerDecklistNode *getParent() const { return parent; @@ -82,8 +80,10 @@ public: class InnerDecklistNode : public AbstractDecklistNode, public QList { -private: QString name; + QString cardSetShortName; + QString cardCollectorNumber; + QString cardProviderId; class compareFunctor; public: @@ -94,7 +94,7 @@ public: explicit InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent = nullptr); ~InnerDecklistNode() override; void setSortMethod(DeckSortMethod method) override; - QString getName() const override + [[nodiscard]] QString getName() const override { return name; } @@ -103,9 +103,35 @@ public: name = _name; } static QString visibleNameFromName(const QString &_name); - virtual QString getVisibleName() const; + [[nodiscard]] virtual QString getVisibleName() const; + [[nodiscard]] QString getCardProviderId() const override + { + return cardProviderId; + } + void setCardProviderId(const QString &_cardProviderId) + { + cardProviderId = _cardProviderId; + } + [[nodiscard]] QString getCardSetShortName() const override + { + return cardSetShortName; + } + void setCardSetShortName(const QString &_cardSetShortName) + { + cardSetShortName = _cardSetShortName; + } + [[nodiscard]] QString getCardCollectorNumber() const override + { + return cardCollectorNumber; + } + void setCardCollectorNumber(const QString &_cardCollectorNumber) + { + cardCollectorNumber = _cardCollectorNumber; + } + void clearTree(); AbstractDecklistNode *findChild(const QString &_name); + AbstractDecklistNode *findCardChildByNameAndProviderId(const QString &_name, const QString &_providerId); int height() const override; int recursiveCount(bool countTotalCards = false) const; bool compare(AbstractDecklistNode *other) const override; @@ -127,6 +153,12 @@ public: virtual void setNumber(int _number) = 0; QString getName() const override = 0; virtual void setName(const QString &_name) = 0; + virtual QString getCardProviderId() const override = 0; + virtual void setCardProviderId(const QString &_cardProviderId) = 0; + virtual QString getCardSetShortName() const override = 0; + virtual void setCardSetShortName(const QString &_cardSetShortName) = 0; + virtual QString getCardCollectorNumber() const override = 0; + virtual void setCardCollectorNumber(const QString &_cardSetNumber) = 0; int height() const override { return 0; @@ -141,13 +173,22 @@ public: class DecklistCardNode : public AbstractDecklistCardNode { -private: QString name; int number; + QString cardSetShortName; + QString cardSetNumber; + QString cardProviderId; public: - explicit DecklistCardNode(QString _name = QString(), int _number = 1, InnerDecklistNode *_parent = nullptr) - : AbstractDecklistCardNode(_parent), name(std::move(_name)), number(_number) + explicit DecklistCardNode(QString _name = QString(), + int _number = 1, + InnerDecklistNode *_parent = nullptr, + QString _cardSetShortName = QString(), + QString _cardSetNumber = QString(), + QString _cardProviderId = QString()) + : AbstractDecklistCardNode(_parent), name(std::move(_name)), number(_number), + cardSetShortName(std::move(_cardSetShortName)), cardSetNumber(std::move(_cardSetNumber)), + cardProviderId(std::move(_cardProviderId)) { } explicit DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent); @@ -167,6 +208,31 @@ public: { name = _name; } + QString getCardProviderId() const override + { + return cardProviderId; + } + void setCardProviderId(const QString &_providerId) override + { + cardProviderId = _providerId; + } + + QString getCardSetShortName() const override + { + return cardSetShortName; + } + void setCardSetShortName(const QString &_cardSetShortName) override + { + cardSetShortName = _cardSetShortName; + } + QString getCardCollectorNumber() const override + { + return cardSetNumber; + } + void setCardCollectorNumber(const QString &_cardSetNumber) override + { + cardSetNumber = _cardSetNumber; + } }; class DeckList : public QObject @@ -255,7 +321,11 @@ public: { return root; } - DecklistCardNode *addCard(const QString &cardName, const QString &zoneName); + DecklistCardNode *addCard(const QString &cardName, + const QString &zoneName, + const QString &cardSetName = QString(), + const QString &cardSetCollectorNumber = QString(), + const QString &cardProviderId = QString()); bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr); /** @@ -277,4 +347,4 @@ public: } }; -#endif +#endif \ No newline at end of file diff --git a/common/pb/event_move_card.proto b/common/pb/event_move_card.proto index f6d309f36..33363344e 100644 --- a/common/pb/event_move_card.proto +++ b/common/pb/event_move_card.proto @@ -16,4 +16,5 @@ message Event_MoveCard { optional sint32 y = 9 [default = -1]; optional sint32 new_card_id = 10 [default = -1]; optional bool face_down = 11; + optional string new_card_provider_id = 12; } diff --git a/common/pb/serverinfo_card.proto b/common/pb/serverinfo_card.proto index 4d9a585cd..5d1cd9db4 100644 --- a/common/pb/serverinfo_card.proto +++ b/common/pb/serverinfo_card.proto @@ -18,4 +18,5 @@ message ServerInfo_Card { optional sint32 attach_player_id = 14 [default = -1]; optional string attach_zone = 15; optional sint32 attach_card_id = 16 [default = -1]; + optional string provider_id = 17; } diff --git a/common/server_card.cpp b/common/server_card.cpp index 3bab40132..c195695e2 100644 --- a/common/server_card.cpp +++ b/common/server_card.cpp @@ -27,10 +27,15 @@ #include -Server_Card::Server_Card(QString _name, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone) - : zone(_zone), id(_id), coord_x(_coord_x), coord_y(_coord_y), name(_name), tapped(false), attacking(false), - facedown(false), color(), ptString(), annotation(), destroyOnZoneChange(false), doesntUntap(false), parentCard(0), - stashedCard(nullptr) +Server_Card::Server_Card(QString _name, + QString _provider_id, + int _id, + int _coord_x, + int _coord_y, + Server_CardZone *_zone) + : zone(_zone), id(_id), coord_x(_coord_x), coord_y(_coord_y), name(_name), provider_id(_provider_id), tapped(false), + attacking(false), facedown(false), color(), ptString(), annotation(), destroyOnZoneChange(false), + doesntUntap(false), parentCard(0), stashedCard(nullptr) { } @@ -130,6 +135,7 @@ void Server_Card::getInfo(ServerInfo_Card *info) QString displayedName = facedown ? QString() : name; info->set_id(id); + info->set_provider_id(provider_id.toStdString()); info->set_name(displayedName.toStdString()); info->set_x(coord_x); info->set_y(coord_y); diff --git a/common/server_card.h b/common/server_card.h index 58ef20255..bdf82dab8 100644 --- a/common/server_card.h +++ b/common/server_card.h @@ -39,6 +39,7 @@ private: int id; int coord_x, coord_y; QString name; + QString provider_id; QMap counters; bool tapped; bool attacking; @@ -54,7 +55,12 @@ private: Server_Card *stashedCard; public: - Server_Card(QString _name, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone = nullptr); + Server_Card(QString _name, + QString _provider_id, + int _id, + int _coord_x, + int _coord_y, + Server_CardZone *_zone = nullptr); ~Server_Card() override; Server_CardZone *getZone() const @@ -70,6 +76,10 @@ public: { return id; } + QString getProviderId() const + { + return provider_id; + } int getX() const { return coord_x; diff --git a/common/server_player.cpp b/common/server_player.cpp index 862705a7d..adf1a66e0 100644 --- a/common/server_player.cpp +++ b/common/server_player.cpp @@ -209,7 +209,9 @@ void Server_Player::setupZones() continue; } for (int k = 0; k < currentCard->getNumber(); ++k) { - z->insertCard(new Server_Card(currentCard->getName(), nextCardId++, 0, 0, z), -1, 0); + z->insertCard( + new Server_Card(currentCard->getName(), currentCard->getCardProviderId(), nextCardId++, 0, 0, z), + -1, 0); } } } @@ -338,6 +340,7 @@ Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int numbe ServerInfo_Card *cardInfo = eventPrivate.add_cards(); cardInfo->set_id(card->getId()); cardInfo->set_name(card->getName().toStdString()); + cardInfo->set_provider_id(card->getProviderId().toStdString()); } ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId); @@ -585,6 +588,7 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, if (sourceKnownToPlayer || !(faceDown || targetzone->getType() == ServerInfo_Zone::HiddenZone)) { QString privateCardName = card->getName(); eventPrivate.set_card_name(privateCardName.toStdString()); + eventPrivate.set_new_card_provider_id(card->getProviderId().toStdString()); } if (startzone->getType() == ServerInfo_Zone::HiddenZone) { eventPrivate.set_position(position); @@ -617,6 +621,7 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, if (!(sourceHiddenToOthers && targetHiddenToOthers)) { QString publicCardName = card->getName(); eventOthers.set_card_name(publicCardName.toStdString()); + eventOthers.set_new_card_provider_id(card->getProviderId().toStdString()); } eventOthers.set_new_card_id(card->getId()); } @@ -1406,7 +1411,7 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer yCoord = 0; } - auto *card = new Server_Card(cardName, newCardId(), xCoord, yCoord); + auto *card = new Server_Card(cardName, QString(), newCardId(), xCoord, yCoord); card->moveToThread(thread()); card->setPT(nameFromStdString(cmd.pt())); card->setColor(nameFromStdString(cmd.color())); @@ -1941,6 +1946,7 @@ Server_Player::cmdDumpZone(const Command_DumpZone &cmd, ResponseContainer &rc, G Server_Card *card = cards[i]; QString displayedName = card->getFaceDown() ? QString() : card->getName(); ServerInfo_Card *cardInfo = zoneInfo->add_card_list(); + cardInfo->set_provider_id(card->getProviderId().toStdString()); cardInfo->set_name(displayedName.toStdString()); if (zone->getType() == ServerInfo_Zone::HiddenZone) { cardInfo->set_id(i); @@ -2058,6 +2064,7 @@ Server_Player::cmdRevealCards(const Command_RevealCards &cmd, ResponseContainer ServerInfo_Card *cardInfo = eventPrivate.add_cards(); cardInfo->set_id(card->getId()); + cardInfo->set_provider_id(card->getProviderId().toStdString()); cardInfo->set_name(card->getName().toStdString()); cardInfo->set_x(card->getX()); cardInfo->set_y(card->getY()); diff --git a/webclient/src/i18n-default.json b/webclient/src/i18n-default.json index 3aabe58f6..36abca615 100644 --- a/webclient/src/i18n-default.json +++ b/webclient/src/i18n-default.json @@ -1,382 +1 @@ -{ - "Common": { - "language": "English", - "disconnect": "Disconnect", - "label": { - "confirmPassword": "Confirm Password", - "confirmSure": "Are you sure?", - "country": "Country", - "delete": "Delete", - "email": "Email", - "hostName": "Host Name", - "hostAddress": "Host Address", - "password": "Password", - "passwordAgain": "Password Again", - "port": "Port", - "realName": "Real Name", - "saveChanges": "Save Changes", - "token": "Token", - "username": "Username" - }, - "validation": { - "minChars": "Minimum of {count} {count, plural, one {character} other {characters}} required", - "passwordsMustMatch": "Passwords don't match", - "required": "Required" - }, - "countries": { - "AD": "Andorra", - "AE": "United Arab Emirates", - "AF": "Afghanistan", - "AG": "Antigua and Barbuda", - "AI": "Anguilla", - "AL": "Albania", - "AM": "Armenia", - "AO": "Angola", - "AQ": "Antarctica", - "AR": "Argentina", - "AS": "American Samoa", - "AT": "Austria", - "AU": "Australia", - "AW": "Aruba", - "AX": "Åland Islands", - "AZ": "Azerbaijan", - "BA": "Bosnia and Herzegovina", - "BB": "Barbados", - "BD": "Bangladesh", - "BE": "Belgium", - "BF": "Burkina Faso", - "BG": "Bulgaria", - "BH": "Bahrain", - "BI": "Burundi", - "BJ": "Benin", - "BL": "Saint Barthélemy", - "BM": "Bermuda", - "BN": "Brunei Darussalam", - "BO": "Bolivia", - "BQ": "Bonaire, Sint Eustatius and Saba", - "BR": "Brazil", - "BS": "Bahamas", - "BT": "Bhutan", - "BV": "Bouvet Island", - "BW": "Botswana", - "BY": "Belarus", - "BZ": "Belize", - "CA": "Canada", - "CC": "Cocos (Keeling) Islands", - "CD": "DR Congo", - "CF": "Central African Republic", - "CG": "Republic of the Congo", - "CH": "Switzerland", - "CI": "Ivory Coast", - "CK": "Cook Islands", - "CL": "Chile", - "CM": "Cameroon", - "CN": "China", - "CO": "Colombia", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Cape Verde", - "CW": "Curaçao", - "CX": "Christmas Island", - "CY": "Cyprus", - "CZ": "Czechia", - "DE": "Germany", - "DJ": "Djibouti", - "DK": "Denmark", - "DM": "Dominica", - "DO": "Dominican Republic", - "DZ": "Algeria", - "EC": "Ecuador", - "EE": "Estonia", - "EG": "Egypt", - "EH": "Western Sahara", - "ER": "Eritrea", - "ES": "Spain", - "ET": "Ethiopia", - "FI": "Finland", - "FJ": "Fiji", - "FK": "Falkland Islands", - "FM": "Micronesia", - "FO": "Faroe Islands", - "FR": "France", - "GA": "Gabon", - "GB": "United Kingdom", - "GD": "Grenada", - "GE": "Georgia", - "GF": "French Guiana", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Greenland", - "GM": "Gambia", - "GN": "Guinea", - "GP": "Guadeloupe", - "GQ": "Equatorial Guinea", - "GR": "Greece", - "GS": "South Georgia and the South Sandwich Islands", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Heard Island and McDonald Islands", - "HN": "Honduras", - "HR": "Croatia", - "HT": "Haiti", - "HU": "Hungary", - "ID": "Indonesia", - "IE": "Ireland", - "IL": "Israel", - "IM": "Isle of Man", - "IN": "India", - "IO": "British Indian Ocean Territory", - "IQ": "Iraq", - "IR": "Iran", - "IS": "Iceland", - "IT": "Italy", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Jordan", - "JP": "Japan", - "KE": "Kenya", - "KG": "Kyrgyzstan", - "KH": "Cambodia", - "KI": "Kiribati", - "KM": "Comoros", - "KN": "Saint Kitts and Nevis", - "KP": "North Korea", - "KR": "South Korea", - "KW": "Kuwait", - "KY": "Cayman Islands", - "KZ": "Kazakhstan", - "LA": "Laos", - "LB": "Lebanon", - "LC": "Saint Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Lithuania", - "LU": "Luxembourg", - "LV": "Latvia", - "LY": "Libya", - "MA": "Morocco", - "MC": "Monaco", - "MD": "Moldova", - "ME": "Montenegro", - "MF": "Saint Martin (French part)", - "MG": "Madagascar", - "MH": "Marshall Islands", - "MK": "North Macedonia", - "ML": "Mali", - "MM": "Myanmar", - "MN": "Mongolia", - "MO": "Macao", - "MP": "Northern Mariana Islands", - "MQ": "Martinique", - "MR": "Mauritania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Maldives", - "MW": "Malawi", - "MX": "Mexico", - "MY": "Malaysia", - "MZ": "Mozambique", - "NA": "Namibia", - "NC": "New Caledonia", - "NE": "Niger", - "NF": "Norfolk Island", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Netherlands", - "NO": "Norway", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "New Zealand", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "French Polynesia", - "PG": "Papua New Guinea", - "PH": "Philippines", - "PK": "Pakistan", - "PL": "Poland", - "PM": "Saint Pierre and Miquelon", - "PN": "Pitcairn", - "PR": "Puerto Rico", - "PS": "Palestine", - "PT": "Portugal", - "PW": "Palau", - "PY": "Paraguay", - "QA": "Qatar", - "RE": "Réunion", - "RO": "Romania", - "RS": "Serbia", - "RU": "Russia", - "RW": "Rwanda", - "SA": "Saudi Arabia", - "SB": "Solomon Islands", - "SC": "Seychelles", - "SD": "Sudan", - "SE": "Sweden", - "SG": "Singapore", - "SH": "Saint Helena, Ascension and Tristan da Cunha", - "SI": "Slovenia", - "SJ": "Svalbard and Jan Mayen", - "SK": "Slovakia", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Suriname", - "SS": "South Sudan", - "ST": "Sao Tome and Principe", - "SV": "El Salvador", - "SX": "Sint Maarten (Dutch part)", - "SY": "Syria", - "SZ": "Eswatini", - "TC": "Turks and Caicos Islands", - "TD": "Chad", - "TF": "TAAF", - "TG": "Togo", - "TH": "Thailand", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turkmenistan", - "TN": "Tunisia", - "TO": "Tonga", - "TR": "Turkey", - "TT": "Trinidad and Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tanzania", - "UA": "Ukraine", - "UG": "Uganda", - "UM": "United States Minor Outlying Islands", - "US": "United States", - "UY": "Uruguay", - "UZ": "Uzbekistan", - "VA": "Holy See", - "VC": "Saint Vincent and the Grenadines", - "VE": "Venezuela", - "VG": "British Virgin Islands", - "VI": "U.S. Virgin Islands", - "VN": "Viet Nam", - "VU": "Vanuatu", - "WF": "Wallis and Futuna", - "WS": "Samoa", - "YE": "Yemen", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "South Africa", - "ZM": "Zambia", - "ZW": "Zimbabwe", - "EU": "European Union" - }, - "languages": { - "en-US": "English - US", - "fr": "French", - "nl": "Dutch", - "pt_BR": "Portuguese - Brazil" - } - }, - "KnownHosts": { - "label": "Host", - "add": "Add new host", - "toast": "Host successfully {mode, select, created {created} deleted {deleted} other {edited}}." - }, - "InitializeContainer": { - "title": "DID YOU KNOW", - "subtitle": "<1>Cockatrice is run by volunteers<1>that love card games!" - }, - "LoginContainer": { - "header": { - "title": "Login", - "subtitle": "A cross-platform virtual tabletop for multiplayer card games." - }, - "footer": { - "registerPrompt": "Not registered yet?", - "registerAction": "Create an account", - "credit": "Cockatrice is an open source project", - "version": "Version" - }, - "content": { - "subtitle1": "Play multiplayer card games online.", - "subtitle2": "Cross-platform virtual tabletop for multiplayer card games. Forever free." - }, - "toasts": { - "passwordResetSuccessToast": "Password Reset Successfully", - "accountActivationSuccess": "Account Activated Successfully" - } - }, - "UnsupportedContainer": { - "title": "Unsupported Browser", - "subtitle1": "Please update your browser and/or check your permissions.", - "subtitle2": "Note: Private browsing causes some browsers to disable certain permissions or features." - }, - "AccountActivationDialog": { - "title": "Account Activation", - "subtitle1": "Your account has not been activated yet.", - "subtitle2": "You need to provide the activation token received in the activation email." - }, - "KnownHostDialog": { - "title": "{mode, select, edit {Edit} other {Add}} Known Host", - "subtitle": "Adding a new host allows you to connect to different servers. Enter the details below to your host list." - }, - "RegistrationDialog": { - "title": "Create New Account" - }, - "RequestPasswordResetDialog": { - "title": "Request Password Reset" - }, - "ResetPasswordDialog": { - "title": "Reset Password" - }, - "AccountActivationForm": { - "error": { - "failed": "Account activation failed" - }, - "label": { - "activate": "Activate Account" - } - }, - "KnownHostForm": { - "help": "Need help adding a new host?", - "label": { - "add": "Add Host", - "find": "Find Host" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Auto Connect", - "forgot": "Forgot Password", - "login": "Login", - "savePassword": "Save Password", - "savedPassword": "Saved Password" - } - }, - "RegisterForm": { - "label": { - "register": "Register" - }, - "toast": { - "registerSuccess": "Registration Successful!" - } - }, - "RequestPasswordResetForm": { - "error": "Request password reset failed", - "mfaEnabled": "Server has multi-factor authentication enabled", - "request": "Request Reset Token", - "skipRequest": "I already have a reset token" - }, - "ResetPasswordForm": { - "error": "Password reset failed", - "label": { - "reset": "Reset Password" - } - } -} +{"Common":{"language":"English","disconnect":"Disconnect","label":{"confirmPassword":"Confirm Password","confirmSure":"Are you sure?","country":"Country","delete":"Delete","email":"Email","hostName":"Host Name","hostAddress":"Host Address","password":"Password","passwordAgain":"Password Again","port":"Port","realName":"Real Name","saveChanges":"Save Changes","token":"Token","username":"Username"},"validation":{"minChars":"Minimum of {count} {count, plural, one {character} other {characters}} required","passwordsMustMatch":"Passwords don't match","required":"Required"},"countries":{"AD":"Andorra","AE":"United Arab Emirates","AF":"Afghanistan","AG":"Antigua and Barbuda","AI":"Anguilla","AL":"Albania","AM":"Armenia","AO":"Angola","AQ":"Antarctica","AR":"Argentina","AS":"American Samoa","AT":"Austria","AU":"Australia","AW":"Aruba","AX":"Åland Islands","AZ":"Azerbaijan","BA":"Bosnia and Herzegovina","BB":"Barbados","BD":"Bangladesh","BE":"Belgium","BF":"Burkina Faso","BG":"Bulgaria","BH":"Bahrain","BI":"Burundi","BJ":"Benin","BL":"Saint Barthélemy","BM":"Bermuda","BN":"Brunei Darussalam","BO":"Bolivia","BQ":"Bonaire, Sint Eustatius and Saba","BR":"Brazil","BS":"Bahamas","BT":"Bhutan","BV":"Bouvet Island","BW":"Botswana","BY":"Belarus","BZ":"Belize","CA":"Canada","CC":"Cocos (Keeling) Islands","CD":"DR Congo","CF":"Central African Republic","CG":"Republic of the Congo","CH":"Switzerland","CI":"Ivory Coast","CK":"Cook Islands","CL":"Chile","CM":"Cameroon","CN":"China","CO":"Colombia","CR":"Costa Rica","CU":"Cuba","CV":"Cape Verde","CW":"Curaçao","CX":"Christmas Island","CY":"Cyprus","CZ":"Czechia","DE":"Germany","DJ":"Djibouti","DK":"Denmark","DM":"Dominica","DO":"Dominican Republic","DZ":"Algeria","EC":"Ecuador","EE":"Estonia","EG":"Egypt","EH":"Western Sahara","ER":"Eritrea","ES":"Spain","ET":"Ethiopia","FI":"Finland","FJ":"Fiji","FK":"Falkland Islands","FM":"Micronesia","FO":"Faroe Islands","FR":"France","GA":"Gabon","GB":"United Kingdom","GD":"Grenada","GE":"Georgia","GF":"French Guiana","GG":"Guernsey","GH":"Ghana","GI":"Gibraltar","GL":"Greenland","GM":"Gambia","GN":"Guinea","GP":"Guadeloupe","GQ":"Equatorial Guinea","GR":"Greece","GS":"South Georgia and the South Sandwich Islands","GT":"Guatemala","GU":"Guam","GW":"Guinea-Bissau","GY":"Guyana","HK":"Hong Kong","HM":"Heard Island and McDonald Islands","HN":"Honduras","HR":"Croatia","HT":"Haiti","HU":"Hungary","ID":"Indonesia","IE":"Ireland","IL":"Israel","IM":"Isle of Man","IN":"India","IO":"British Indian Ocean Territory","IQ":"Iraq","IR":"Iran","IS":"Iceland","IT":"Italy","JE":"Jersey","JM":"Jamaica","JO":"Jordan","JP":"Japan","KE":"Kenya","KG":"Kyrgyzstan","KH":"Cambodia","KI":"Kiribati","KM":"Comoros","KN":"Saint Kitts and Nevis","KP":"North Korea","KR":"South Korea","KW":"Kuwait","KY":"Cayman Islands","KZ":"Kazakhstan","LA":"Laos","LB":"Lebanon","LC":"Saint Lucia","LI":"Liechtenstein","LK":"Sri Lanka","LR":"Liberia","LS":"Lesotho","LT":"Lithuania","LU":"Luxembourg","LV":"Latvia","LY":"Libya","MA":"Morocco","MC":"Monaco","MD":"Moldova","ME":"Montenegro","MF":"Saint Martin (French part)","MG":"Madagascar","MH":"Marshall Islands","MK":"North Macedonia","ML":"Mali","MM":"Myanmar","MN":"Mongolia","MO":"Macao","MP":"Northern Mariana Islands","MQ":"Martinique","MR":"Mauritania","MS":"Montserrat","MT":"Malta","MU":"Mauritius","MV":"Maldives","MW":"Malawi","MX":"Mexico","MY":"Malaysia","MZ":"Mozambique","NA":"Namibia","NC":"New Caledonia","NE":"Niger","NF":"Norfolk Island","NG":"Nigeria","NI":"Nicaragua","NL":"Netherlands","NO":"Norway","NP":"Nepal","NR":"Nauru","NU":"Niue","NZ":"New Zealand","OM":"Oman","PA":"Panama","PE":"Peru","PF":"French Polynesia","PG":"Papua New Guinea","PH":"Philippines","PK":"Pakistan","PL":"Poland","PM":"Saint Pierre and Miquelon","PN":"Pitcairn","PR":"Puerto Rico","PS":"Palestine","PT":"Portugal","PW":"Palau","PY":"Paraguay","QA":"Qatar","RE":"Réunion","RO":"Romania","RS":"Serbia","RU":"Russia","RW":"Rwanda","SA":"Saudi Arabia","SB":"Solomon Islands","SC":"Seychelles","SD":"Sudan","SE":"Sweden","SG":"Singapore","SH":"Saint Helena, Ascension and Tristan da Cunha","SI":"Slovenia","SJ":"Svalbard and Jan Mayen","SK":"Slovakia","SL":"Sierra Leone","SM":"San Marino","SN":"Senegal","SO":"Somalia","SR":"Suriname","SS":"South Sudan","ST":"Sao Tome and Principe","SV":"El Salvador","SX":"Sint Maarten (Dutch part)","SY":"Syria","SZ":"Eswatini","TC":"Turks and Caicos Islands","TD":"Chad","TF":"TAAF","TG":"Togo","TH":"Thailand","TJ":"Tajikistan","TK":"Tokelau","TL":"Timor-Leste","TM":"Turkmenistan","TN":"Tunisia","TO":"Tonga","TR":"Turkey","TT":"Trinidad and Tobago","TV":"Tuvalu","TW":"Taiwan","TZ":"Tanzania","UA":"Ukraine","UG":"Uganda","UM":"United States Minor Outlying Islands","US":"United States","UY":"Uruguay","UZ":"Uzbekistan","VA":"Holy See","VC":"Saint Vincent and the Grenadines","VE":"Venezuela","VG":"British Virgin Islands","VI":"U.S. Virgin Islands","VN":"Viet Nam","VU":"Vanuatu","WF":"Wallis and Futuna","WS":"Samoa","YE":"Yemen","YT":"Mayotte","XK":"Kosovo","ZA":"South Africa","ZM":"Zambia","ZW":"Zimbabwe","EU":"European Union"},"languages":{"en-US":"English - US","fr":"French","nl":"Dutch","pt_BR":"Portuguese - Brazil"}},"KnownHosts":{"label":"Host","add":"Add new host","toast":"Host successfully {mode, select, created {created} deleted {deleted} other {edited}}."},"InitializeContainer":{"title":"DID YOU KNOW","subtitle":"<1>Cockatrice is run by volunteers<1>that love card games!"},"LoginContainer":{"header":{"title":"Login","subtitle":"A cross-platform virtual tabletop for multiplayer card games."},"footer":{"registerPrompt":"Not registered yet?","registerAction":"Create an account","credit":"Cockatrice is an open source project","version":"Version"},"content":{"subtitle1":"Play multiplayer card games online.","subtitle2":"Cross-platform virtual tabletop for multiplayer card games. Forever free."},"toasts":{"passwordResetSuccessToast":"Password Reset Successfully","accountActivationSuccess":"Account Activated Successfully"}},"UnsupportedContainer":{"title":"Unsupported Browser","subtitle1":"Please update your browser and/or check your permissions.","subtitle2":"Note: Private browsing causes some browsers to disable certain permissions or features."},"AccountActivationDialog":{"title":"Account Activation","subtitle1":"Your account has not been activated yet.","subtitle2":"You need to provide the activation token received in the activation email."},"KnownHostDialog":{"title":"{mode, select, edit {Edit} other {Add}} Known Host","subtitle":"Adding a new host allows you to connect to different servers. Enter the details below to your host list."},"RegistrationDialog":{"title":"Create New Account"},"RequestPasswordResetDialog":{"title":"Request Password Reset"},"ResetPasswordDialog":{"title":"Reset Password"},"AccountActivationForm":{"error":{"failed":"Account activation failed"},"label":{"activate":"Activate Account"}},"KnownHostForm":{"help":"Need help adding a new host?","label":{"add":"Add Host","find":"Find Host"}},"LoginForm":{"label":{"autoConnect":"Auto Connect","forgot":"Forgot Password","login":"Login","savePassword":"Save Password","savedPassword":"Saved Password"}},"RegisterForm":{"label":{"register":"Register"},"toast":{"registerSuccess":"Registration Successful!"}},"RequestPasswordResetForm":{"error":"Request password reset failed","mfaEnabled":"Server has multi-factor authentication enabled","request":"Request Reset Token","skipRequest":"I already have a reset token"},"ResetPasswordForm":{"error":"Password reset failed","label":{"reset":"Reset Password"}}} \ No newline at end of file