Deck format legality checker (#6166)

* Deck legality checker.

Took 51 seconds

Took 1 minute

Took 1 minute

Took 5 minutes

Took 3 minutes

* Adjust format parsing.

Took 8 minutes


Took 3 seconds

* toString() the xmlName

Took 4 minutes

* more toStrings()

Took 5 minutes

* Comments

Took 3 minutes

* Layout

Took 2 minutes

* Layout part 2: Electric boogaloo

Took 59 seconds

* Update cockatrice/src/interface/widgets/visual_database_display/visual_database_display_format_legality_filter_widget.cpp

Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>

* Move layout.

Took 4 minutes


Took 10 seconds

* Emit deckModified

Took 6 minutes

* Fix qOverloads

Took 4 minutes

* Fix qOverloads

Took 12 seconds

* Consider text and name in a special way.

Took 11 minutes

* Adjust "Any number of" oracle text

Took 5 minutes

* Store allowedCounts by format

Took 15 minutes

Took 6 seconds

* Only restrict vintage.

Took 2 minutes

* Adjust for DBConverter.

Took 6 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
This commit is contained in:
BruebachL 2025-12-13 15:17:55 +01:00 committed by GitHub
parent 2e2682aad4
commit ccdda39e78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 987 additions and 35 deletions

View file

@ -168,6 +168,10 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const
return card->depth();
}
case DeckRoles::IsLegalRole: {
return card->getFormatLegality();
}
default: {
return {};
}
@ -268,6 +272,7 @@ bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, con
switch (index.column()) {
case DeckListModelColumns::CARD_AMOUNT:
node->setNumber(value.toInt());
refreshCardFormatLegalities();
break;
case DeckListModelColumns::CARD_NAME:
node->setName(value.toString());
@ -414,8 +419,9 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam
// Determine the correct index
int insertRow = findSortedInsertRow(groupNode, cardInfo);
auto *decklistCard = deckList->addCard(cardInfo->getName(), zoneName, insertRow, cardSetName,
printingInfo.getProperty("num"), printingInfo.getProperty("uuid"));
auto *decklistCard =
deckList->addCard(cardInfo->getName(), zoneName, insertRow, cardSetName, printingInfo.getProperty("num"),
printingInfo.getProperty("uuid"), isCardLegalForCurrentFormat(cardInfo));
beginInsertRows(parentIndex, insertRow, insertRow);
cardNode = new DecklistModelCardNode(decklistCard, groupNode, insertRow);
@ -532,6 +538,13 @@ void DeckListModel::setActiveGroupCriteria(DeckListModelGroupCriteria::Type newC
rebuildTree();
}
void DeckListModel::setActiveFormat(const QString &_format)
{
deckList->setGameFormat(_format);
refreshCardFormatLegalities();
emitBackgroundUpdates(QModelIndex()); // start from root
}
void DeckListModel::cleanList()
{
setDeckList(new DeckList);
@ -595,4 +608,90 @@ QList<QString> DeckListModel::getZones() const
[](auto zoneNode) { return zoneNode->getName(); });
return zones;
}
}
bool DeckListModel::isCardLegalForCurrentFormat(const CardInfoPtr cardInfo)
{
if (!deckList->getGameFormat().isEmpty()) {
if (cardInfo->getProperties().contains("format-" + deckList->getGameFormat())) {
QString formatLegality = cardInfo->getProperty("format-" + deckList->getGameFormat());
return formatLegality == "legal" || formatLegality == "restricted";
}
return false;
}
return true;
}
int maxAllowedForLegality(const FormatRules &format, const QString &legality)
{
for (const AllowedCount &c : format.allowedCounts) {
if (c.label == legality) {
return c.max;
}
}
return -1; // unknown legality → treat as illegal
}
bool DeckListModel::isCardQuantityLegalForCurrentFormat(const CardInfoPtr cardInfo, int quantity)
{
auto formatRules = CardDatabaseManager::query()->getFormat(deckList->getGameFormat());
if (!formatRules) {
return true;
}
// Exceptions always win
if (cardHasAnyException(*cardInfo, *formatRules)) {
return true;
}
const QString legalityProp = "format-" + deckList->getGameFormat();
if (!cardInfo->getProperties().contains(legalityProp)) {
return false;
}
const QString legality = cardInfo->getProperty(legalityProp);
int maxAllowed = maxAllowedForLegality(*formatRules, legality);
if (maxAllowed == -1) {
return false;
}
if (maxAllowed < 0) { // unlimited
return true;
}
return quantity <= maxAllowed;
}
void DeckListModel::refreshCardFormatLegalities()
{
InnerDecklistNode *listRoot = deckList->getRoot();
for (int i = 0; i < listRoot->size(); i++) {
auto *currentZone = static_cast<InnerDecklistNode *>(listRoot->at(i));
for (int j = 0; j < currentZone->size(); j++) {
auto *currentCard = static_cast<DecklistCardNode *>(currentZone->at(j));
// TODO: better sanity checking
if (currentCard == nullptr) {
continue;
}
ExactCard exactCard = CardDatabaseManager::query()->getCard(currentCard->toCardRef());
if (!exactCard) {
continue;
}
bool legal = isCardLegalForCurrentFormat(exactCard.getCardPtr());
if (legal) {
legal = isCardQuantityLegalForCurrentFormat(exactCard.getCardPtr(), currentCard->getNumber());
}
currentCard->setFormatLegality(legal);
}
}
}

View file

@ -164,6 +164,14 @@ public:
{
dataNode->setCardCollectorNumber(_cardSetNumber);
}
bool getFormatLegality() const override
{
return dataNode->getFormatLegality();
}
void setFormatLegality(const bool _formatLegal) override
{
dataNode->setFormatLegality(_formatLegal);
}
/**
* @brief Returns the underlying data node.
@ -209,6 +217,9 @@ public slots:
*/
void rebuildTree();
public slots:
void setActiveFormat(const QString &_format);
signals:
/**
* @brief Emitted whenever the deck hash changes due to modifications in the model.
@ -301,6 +312,9 @@ public:
[[nodiscard]] QList<ExactCard> getCards() const;
[[nodiscard]] QList<ExactCard> getCardsForZone(const QString &zoneName) const;
[[nodiscard]] QList<QString> getZones() const;
bool isCardLegalForCurrentFormat(CardInfoPtr cardInfo);
bool isCardQuantityLegalForCurrentFormat(CardInfoPtr cardInfo, int quantity);
void refreshCardFormatLegalities();
/**
* @brief Sets the criteria used to group cards in the model.