mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-12 17:14:52 -07:00
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:
parent
2e2682aad4
commit
ccdda39e78
37 changed files with 987 additions and 35 deletions
|
|
@ -202,6 +202,7 @@ set(cockatrice_SOURCES
|
||||||
src/interface/widgets/utility/sequence_edit.cpp
|
src/interface/widgets/utility/sequence_edit.cpp
|
||||||
src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp
|
src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp
|
||||||
src/interface/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
|
src/interface/widgets/visual_database_display/visual_database_display_filter_save_load_widget.cpp
|
||||||
|
src/interface/widgets/visual_database_display/visual_database_display_format_legality_filter_widget.cpp
|
||||||
src/interface/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
|
src/interface/widgets/visual_database_display/visual_database_display_main_type_filter_widget.cpp
|
||||||
src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
|
src/interface/widgets/visual_database_display/visual_database_display_name_filter_widget.cpp
|
||||||
src/interface/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp
|
src/interface/widgets/visual_database_display/visual_database_display_set_filter_widget.cpp
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,12 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
||||||
quickSettingsWidget->addSettingsWidget(showBannerCardCheckBox);
|
quickSettingsWidget->addSettingsWidget(showBannerCardCheckBox);
|
||||||
quickSettingsWidget->addSettingsWidget(showTagsWidgetCheckBox);
|
quickSettingsWidget->addSettingsWidget(showTagsWidgetCheckBox);
|
||||||
|
|
||||||
|
formatLabel = new QLabel(this);
|
||||||
|
|
||||||
|
formatComboBox = new QComboBox(this);
|
||||||
|
formatComboBox->addItem(tr("Loading Database..."));
|
||||||
|
formatComboBox->setEnabled(false); // Disable until loaded
|
||||||
|
|
||||||
commentsLabel = new QLabel();
|
commentsLabel = new QLabel();
|
||||||
commentsLabel->setObjectName("commentsLabel");
|
commentsLabel->setObjectName("commentsLabel");
|
||||||
commentsEdit = new QTextEdit;
|
commentsEdit = new QTextEdit;
|
||||||
|
|
@ -208,13 +214,16 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
||||||
upperLayout->addWidget(commentsLabel, 1, 0);
|
upperLayout->addWidget(commentsLabel, 1, 0);
|
||||||
upperLayout->addWidget(commentsEdit, 1, 1);
|
upperLayout->addWidget(commentsEdit, 1, 1);
|
||||||
|
|
||||||
upperLayout->addWidget(bannerCardLabel, 2, 0);
|
upperLayout->addWidget(formatLabel, 2, 0);
|
||||||
upperLayout->addWidget(bannerCardComboBox, 2, 1);
|
upperLayout->addWidget(formatComboBox, 2, 1);
|
||||||
|
|
||||||
upperLayout->addWidget(deckTagsDisplayWidget, 3, 1);
|
upperLayout->addWidget(bannerCardLabel, 3, 0);
|
||||||
|
upperLayout->addWidget(bannerCardComboBox, 3, 1);
|
||||||
|
|
||||||
upperLayout->addWidget(activeGroupCriteriaLabel, 4, 0);
|
upperLayout->addWidget(deckTagsDisplayWidget, 4, 1);
|
||||||
upperLayout->addWidget(activeGroupCriteriaComboBox, 4, 1);
|
|
||||||
|
upperLayout->addWidget(activeGroupCriteriaLabel, 5, 0);
|
||||||
|
upperLayout->addWidget(activeGroupCriteriaComboBox, 5, 1);
|
||||||
|
|
||||||
hashLabel1 = new QLabel();
|
hashLabel1 = new QLabel();
|
||||||
hashLabel1->setObjectName("hashLabel1");
|
hashLabel1->setObjectName("hashLabel1");
|
||||||
|
|
@ -263,6 +272,46 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
||||||
|
|
||||||
refreshShortcuts();
|
refreshShortcuts();
|
||||||
retranslateUi();
|
retranslateUi();
|
||||||
|
|
||||||
|
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
|
||||||
|
&DeckEditorDeckDockWidget::initializeFormats);
|
||||||
|
|
||||||
|
if (CardDatabaseManager::getInstance()->getLoadStatus() == LoadStatus::Ok) {
|
||||||
|
initializeFormats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeckEditorDeckDockWidget::initializeFormats()
|
||||||
|
{
|
||||||
|
QMap<QString, int> allFormats = CardDatabaseManager::query()->getAllFormatsWithCount();
|
||||||
|
|
||||||
|
formatComboBox->clear(); // Remove "Loading Database..."
|
||||||
|
formatComboBox->setEnabled(true);
|
||||||
|
|
||||||
|
// Populate with formats
|
||||||
|
formatComboBox->addItem("", "");
|
||||||
|
for (auto it = allFormats.constBegin(); it != allFormats.constEnd(); ++it) {
|
||||||
|
QString displayText = QString("%1").arg(it.key());
|
||||||
|
formatComboBox->addItem(displayText, it.key()); // store the raw key in itemData
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deckModel->getDeckList()->getGameFormat().isEmpty()) {
|
||||||
|
deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat());
|
||||||
|
formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat()));
|
||||||
|
} else {
|
||||||
|
// Ensure no selection is visible initially
|
||||||
|
formatComboBox->setCurrentIndex(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(formatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
|
||||||
|
if (index >= 0) {
|
||||||
|
QString formatKey = formatComboBox->itemData(index).toString();
|
||||||
|
deckModel->setActiveFormat(formatKey);
|
||||||
|
} else {
|
||||||
|
deckModel->setActiveFormat(QString()); // clear format if deselected
|
||||||
|
}
|
||||||
|
emit deckModified();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ExactCard DeckEditorDeckDockWidget::getCurrentCard()
|
ExactCard DeckEditorDeckDockWidget::getCurrentCard()
|
||||||
|
|
@ -466,6 +515,12 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
|
||||||
void DeckEditorDeckDockWidget::sortDeckModelToDeckView()
|
void DeckEditorDeckDockWidget::sortDeckModelToDeckView()
|
||||||
{
|
{
|
||||||
deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
|
deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
|
||||||
|
deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat());
|
||||||
|
formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat()));
|
||||||
|
deckView->expandAll();
|
||||||
|
deckView->expandAll();
|
||||||
|
|
||||||
|
emit deckChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckLoader *DeckEditorDeckDockWidget::getDeckLoader()
|
DeckLoader *DeckEditorDeckDockWidget::getDeckLoader()
|
||||||
|
|
@ -724,6 +779,8 @@ void DeckEditorDeckDockWidget::retranslateUi()
|
||||||
showTagsWidgetCheckBox->setText(tr("Show tags selection menu"));
|
showTagsWidgetCheckBox->setText(tr("Show tags selection menu"));
|
||||||
commentsLabel->setText(tr("&Comments:"));
|
commentsLabel->setText(tr("&Comments:"));
|
||||||
activeGroupCriteriaLabel->setText(tr("Group by:"));
|
activeGroupCriteriaLabel->setText(tr("Group by:"));
|
||||||
|
formatLabel->setText(tr("Format:"));
|
||||||
|
|
||||||
hashLabel1->setText(tr("Hash:"));
|
hashLabel1->setText(tr("Hash:"));
|
||||||
|
|
||||||
aIncrement->setText(tr("&Increment number"));
|
aIncrement->setText(tr("&Increment number"));
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ public slots:
|
||||||
void actSwapCard();
|
void actSwapCard();
|
||||||
void actRemoveCard();
|
void actRemoveCard();
|
||||||
void offsetCountAtIndex(const QModelIndex &idx, int offset);
|
void offsetCountAtIndex(const QModelIndex &idx, int offset);
|
||||||
|
void initializeFormats();
|
||||||
void expandAll();
|
void expandAll();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
@ -100,6 +101,8 @@ private:
|
||||||
LineEditUnfocusable *hashLabel;
|
LineEditUnfocusable *hashLabel;
|
||||||
QLabel *activeGroupCriteriaLabel;
|
QLabel *activeGroupCriteriaLabel;
|
||||||
QComboBox *activeGroupCriteriaComboBox;
|
QComboBox *activeGroupCriteriaComboBox;
|
||||||
|
QLabel *formatLabel;
|
||||||
|
QComboBox *formatComboBox;
|
||||||
|
|
||||||
QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard;
|
QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,7 @@ QVariant DeckListStyleProxy::data(const QModelIndex &index, int role) const
|
||||||
|
|
||||||
if (role == Qt::BackgroundRole) {
|
if (role == Qt::BackgroundRole) {
|
||||||
if (isCard) {
|
if (isCard) {
|
||||||
const bool legal =
|
const bool legal = QIdentityProxyModel::data(index, DeckRoles::IsLegalRole).toBool();
|
||||||
true; // TODO: Not implemented yet. QIdentityProxyModel::data(index, DeckRoles::IsLegalRole).toBool();
|
|
||||||
int base = 255 - (index.row() % 2) * 30;
|
int base = 255 - (index.row() % 2) * 30;
|
||||||
return legal ? QBrush(QColor(base, base, base)) : QBrush(QColor(255, base / 3, base / 3));
|
return legal ? QBrush(QColor(base, base, base)) : QBrush(QColor(255, base / 3, base / 3));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
||||||
minDeckSizeLogicCombo->addItems({"Exact", "≥", "≤"}); // Exact = unset, ≥ = GTE, ≤ = LTE
|
minDeckSizeLogicCombo->addItems({"Exact", "≥", "≤"}); // Exact = unset, ≥ = GTE, ≤ = LTE
|
||||||
minDeckSizeLogicCombo->setCurrentIndex(1); // default GTE
|
minDeckSizeLogicCombo->setCurrentIndex(1); // default GTE
|
||||||
|
|
||||||
connect(minDeckSizeSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
|
connect(minDeckSizeSpin, qOverload<int>(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
|
||||||
connect(minDeckSizeLogicCombo, &QComboBox::currentTextChanged, this, &TabArchidekt::doSearch);
|
connect(minDeckSizeLogicCombo, &QComboBox::currentTextChanged, this, &TabArchidekt::doSearch);
|
||||||
|
|
||||||
// Page number
|
// Page number
|
||||||
|
|
@ -224,7 +224,7 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
||||||
pageSpin->setRange(1, 9999);
|
pageSpin->setRange(1, 9999);
|
||||||
pageSpin->setValue(1);
|
pageSpin->setValue(1);
|
||||||
|
|
||||||
connect(pageSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
|
connect(pageSpin, qOverload<int>(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
|
||||||
|
|
||||||
// Page display
|
// Page display
|
||||||
currentPageDisplay = new QWidget(container);
|
currentPageDisplay = new QWidget(container);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,205 @@
|
||||||
|
#include "visual_database_display_format_legality_filter_widget.h"
|
||||||
|
|
||||||
|
#include "../../../filters/filter_tree_model.h"
|
||||||
|
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <libcockatrice/card/database/card_database_manager.h>
|
||||||
|
#include <libcockatrice/filters/filter_tree.h>
|
||||||
|
|
||||||
|
VisualDatabaseDisplayFormatLegalityFilterWidget::VisualDatabaseDisplayFormatLegalityFilterWidget(
|
||||||
|
QWidget *parent,
|
||||||
|
FilterTreeModel *_filterModel)
|
||||||
|
: QWidget(parent), filterModel(_filterModel)
|
||||||
|
{
|
||||||
|
allFormatsWithCount = CardDatabaseManager::query()->getAllFormatsWithCount();
|
||||||
|
|
||||||
|
setMaximumHeight(75);
|
||||||
|
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
layout = new QHBoxLayout(this);
|
||||||
|
setLayout(layout);
|
||||||
|
layout->setContentsMargins(0, 1, 0, 1);
|
||||||
|
layout->setSpacing(1);
|
||||||
|
layout->setAlignment(Qt::AlignTop);
|
||||||
|
|
||||||
|
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||||
|
layout->addWidget(flowWidget);
|
||||||
|
|
||||||
|
// Create the spinbox
|
||||||
|
spinBox = new QSpinBox(this);
|
||||||
|
spinBox->setMinimum(1);
|
||||||
|
spinBox->setMaximum(getMaxMainTypeCount()); // Set the max value dynamically
|
||||||
|
spinBox->setValue(150);
|
||||||
|
layout->addWidget(spinBox);
|
||||||
|
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||||
|
&VisualDatabaseDisplayFormatLegalityFilterWidget::updateFormatButtonsVisibility);
|
||||||
|
|
||||||
|
// Create the toggle button for Exact Match/Includes mode
|
||||||
|
toggleButton = new QPushButton(this);
|
||||||
|
toggleButton->setCheckable(true);
|
||||||
|
layout->addWidget(toggleButton);
|
||||||
|
connect(toggleButton, &QPushButton::toggled, this,
|
||||||
|
&VisualDatabaseDisplayFormatLegalityFilterWidget::updateFilterMode);
|
||||||
|
connect(filterModel, &FilterTreeModel::layoutChanged, this, [this]() {
|
||||||
|
QTimer::singleShot(100, this, &VisualDatabaseDisplayFormatLegalityFilterWidget::syncWithFilterModel);
|
||||||
|
});
|
||||||
|
|
||||||
|
createFormatButtons(); // Populate buttons initially
|
||||||
|
updateFilterMode(false); // Initialize toggle button text
|
||||||
|
|
||||||
|
retranslateUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VisualDatabaseDisplayFormatLegalityFilterWidget::retranslateUi()
|
||||||
|
{
|
||||||
|
spinBox->setToolTip(tr("Do not display formats with less than this amount of cards in the database"));
|
||||||
|
toggleButton->setToolTip(tr("Filter mode (AND/OR/NOT conjunctions of filters)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VisualDatabaseDisplayFormatLegalityFilterWidget::createFormatButtons()
|
||||||
|
{
|
||||||
|
// Iterate through main types and create buttons
|
||||||
|
for (auto it = allFormatsWithCount.begin(); it != allFormatsWithCount.end(); ++it) {
|
||||||
|
auto *button = new QPushButton(it.key(), flowWidget);
|
||||||
|
button->setCheckable(true);
|
||||||
|
button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }"
|
||||||
|
"QPushButton:checked { background-color: green; color: white; }");
|
||||||
|
|
||||||
|
flowWidget->addWidget(button);
|
||||||
|
formatButtons[it.key()] = button;
|
||||||
|
|
||||||
|
// Connect toggle signal
|
||||||
|
connect(button, &QPushButton::toggled, this,
|
||||||
|
[this, mainType = it.key()](bool checked) { handleFormatToggled(mainType, checked); });
|
||||||
|
}
|
||||||
|
updateFormatButtonsVisibility(); // Ensure visibility is updated initially
|
||||||
|
}
|
||||||
|
|
||||||
|
void VisualDatabaseDisplayFormatLegalityFilterWidget::updateFormatButtonsVisibility()
|
||||||
|
{
|
||||||
|
int threshold = spinBox->value(); // Get the current spinbox value
|
||||||
|
|
||||||
|
// Iterate through buttons and hide/disable those below the threshold
|
||||||
|
for (auto it = formatButtons.begin(); it != formatButtons.end(); ++it) {
|
||||||
|
bool visible = allFormatsWithCount[it.key()] >= threshold;
|
||||||
|
it.value()->setVisible(visible);
|
||||||
|
it.value()->setEnabled(visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int VisualDatabaseDisplayFormatLegalityFilterWidget::getMaxMainTypeCount() const
|
||||||
|
{
|
||||||
|
int maxCount = 1;
|
||||||
|
for (auto it = allFormatsWithCount.begin(); it != allFormatsWithCount.end(); ++it) {
|
||||||
|
maxCount = qMax(maxCount, it.value());
|
||||||
|
}
|
||||||
|
return maxCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VisualDatabaseDisplayFormatLegalityFilterWidget::handleFormatToggled(const QString &format, bool active)
|
||||||
|
{
|
||||||
|
activeFormats[format] = active;
|
||||||
|
|
||||||
|
if (formatButtons.contains(format)) {
|
||||||
|
formatButtons[format]->setChecked(active);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFormatFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VisualDatabaseDisplayFormatLegalityFilterWidget::updateFormatFilter()
|
||||||
|
{
|
||||||
|
// Clear existing filters related to main type
|
||||||
|
filterModel->blockSignals(true);
|
||||||
|
filterModel->filterTree()->blockSignals(true);
|
||||||
|
filterModel->clearFiltersOfType(CardFilter::Attr::AttrFormat);
|
||||||
|
|
||||||
|
if (exactMatchMode) {
|
||||||
|
// Exact Match: Only selected main types are allowed
|
||||||
|
QSet<QString> selectedTypes;
|
||||||
|
for (const auto &type : activeFormats.keys()) {
|
||||||
|
if (activeFormats[type]) {
|
||||||
|
selectedTypes.insert(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectedTypes.isEmpty()) {
|
||||||
|
// Require all selected types (TypeAnd)
|
||||||
|
for (const auto &type : selectedTypes) {
|
||||||
|
QString typeString = type;
|
||||||
|
filterModel->addFilter(
|
||||||
|
new CardFilter(typeString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude any other types (TypeAndNot)
|
||||||
|
for (const auto &type : formatButtons.keys()) {
|
||||||
|
if (!selectedTypes.contains(type)) {
|
||||||
|
QString typeString = type;
|
||||||
|
filterModel->addFilter(
|
||||||
|
new CardFilter(typeString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrFormat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Default Includes Mode (TypeOr) - match any selected main types
|
||||||
|
for (const auto &type : activeFormats.keys()) {
|
||||||
|
if (activeFormats[type]) {
|
||||||
|
QString typeString = type;
|
||||||
|
filterModel->addFilter(
|
||||||
|
new CardFilter(typeString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrFormat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filterModel->blockSignals(false);
|
||||||
|
filterModel->filterTree()->blockSignals(false);
|
||||||
|
|
||||||
|
emit filterModel->filterTree()->changed();
|
||||||
|
emit filterModel->layoutChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VisualDatabaseDisplayFormatLegalityFilterWidget::updateFilterMode(bool checked)
|
||||||
|
{
|
||||||
|
exactMatchMode = checked;
|
||||||
|
toggleButton->setText(exactMatchMode ? tr("Mode: Exact Match") : tr("Mode: Includes"));
|
||||||
|
updateFormatFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VisualDatabaseDisplayFormatLegalityFilterWidget::syncWithFilterModel()
|
||||||
|
{
|
||||||
|
// Temporarily block signals for each button to prevent toggling while updating button states
|
||||||
|
for (auto it = formatButtons.begin(); it != formatButtons.end(); ++it) {
|
||||||
|
it.value()->blockSignals(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncheck all buttons
|
||||||
|
for (auto it = formatButtons.begin(); it != formatButtons.end(); ++it) {
|
||||||
|
it.value()->setChecked(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get active filters for main types
|
||||||
|
QSet<QString> activeTypes;
|
||||||
|
for (const auto &filter : filterModel->getFiltersOfType(CardFilter::AttrFormat)) {
|
||||||
|
if (filter->type() == CardFilter::Type::TypeAnd) {
|
||||||
|
activeTypes.insert(filter->term());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the buttons for active types
|
||||||
|
for (const auto &type : activeTypes) {
|
||||||
|
activeFormats[type] = true;
|
||||||
|
if (formatButtons.contains(type)) {
|
||||||
|
formatButtons[type]->setChecked(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-enable signal emissions for each button
|
||||||
|
for (auto it = formatButtons.begin(); it != formatButtons.end(); ++it) {
|
||||||
|
it.value()->blockSignals(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the visibility of buttons
|
||||||
|
updateFormatButtonsVisibility();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef COCKATRICE_VISUAL_DATABASE_DISPLAY_FORMAT_LEGALITY_FILTER_WIDGET_H
|
||||||
|
#define COCKATRICE_VISUAL_DATABASE_DISPLAY_FORMAT_LEGALITY_FILTER_WIDGET_H
|
||||||
|
|
||||||
|
#include "../../../filters/filter_tree_model.h"
|
||||||
|
#include "../general/layout_containers/flow_widget.h"
|
||||||
|
|
||||||
|
#include <QMap>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class VisualDatabaseDisplayFormatLegalityFilterWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit VisualDatabaseDisplayFormatLegalityFilterWidget(QWidget *parent, FilterTreeModel *filterModel);
|
||||||
|
void retranslateUi();
|
||||||
|
void createFormatButtons();
|
||||||
|
void updateFormatButtonsVisibility();
|
||||||
|
int getMaxMainTypeCount() const;
|
||||||
|
|
||||||
|
void handleFormatToggled(const QString &format, bool active);
|
||||||
|
void updateFormatFilter();
|
||||||
|
void updateFilterMode(bool checked);
|
||||||
|
void syncWithFilterModel();
|
||||||
|
|
||||||
|
private:
|
||||||
|
FilterTreeModel *filterModel;
|
||||||
|
QMap<QString, int> allFormatsWithCount;
|
||||||
|
QSpinBox *spinBox;
|
||||||
|
QHBoxLayout *layout;
|
||||||
|
FlowWidget *flowWidget;
|
||||||
|
QPushButton *toggleButton; // Mode switch button
|
||||||
|
|
||||||
|
QMap<QString, bool> activeFormats; // Track active filters
|
||||||
|
QMap<QString, QPushButton *> formatButtons; // Store toggle buttons
|
||||||
|
|
||||||
|
bool exactMatchMode = false; // Toggle between "Exact Match" and "Includes"
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_VISUAL_DATABASE_DISPLAY_FORMAT_LEGALITY_FILTER_WIDGET_H
|
||||||
|
|
@ -33,7 +33,7 @@ VisualDatabaseDisplayMainTypeFilterWidget::VisualDatabaseDisplayMainTypeFilterWi
|
||||||
spinBox->setMaximum(getMaxMainTypeCount()); // Set the max value dynamically
|
spinBox->setMaximum(getMaxMainTypeCount()); // Set the max value dynamically
|
||||||
spinBox->setValue(150);
|
spinBox->setValue(150);
|
||||||
layout->addWidget(spinBox);
|
layout->addWidget(spinBox);
|
||||||
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||||
&VisualDatabaseDisplayMainTypeFilterWidget::updateMainTypeButtonsVisibility);
|
&VisualDatabaseDisplayMainTypeFilterWidget::updateMainTypeButtonsVisibility);
|
||||||
|
|
||||||
// Create the toggle button for Exact Match/Includes mode
|
// Create the toggle button for Exact Match/Includes mode
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ VisualDatabaseDisplayRecentSetFilterSettingsWidget::VisualDatabaseDisplayRecentS
|
||||||
filterToMostRecentSetsAmount->setMaximum(100);
|
filterToMostRecentSetsAmount->setMaximum(100);
|
||||||
filterToMostRecentSetsAmount->setValue(
|
filterToMostRecentSetsAmount->setValue(
|
||||||
SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsAmount());
|
SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsAmount());
|
||||||
connect(filterToMostRecentSetsAmount, QOverload<int>::of(&QSpinBox::valueChanged), &SettingsCache::instance(),
|
connect(filterToMostRecentSetsAmount, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
|
||||||
&SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsAmount);
|
&SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsAmount);
|
||||||
|
|
||||||
layout->addWidget(filterToMostRecentSetsCheckBox);
|
layout->addWidget(filterToMostRecentSetsCheckBox);
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ VisualDatabaseDisplaySubTypeFilterWidget::VisualDatabaseDisplaySubTypeFilterWidg
|
||||||
spinBox->setMaximum(getMaxSubTypeCount());
|
spinBox->setMaximum(getMaxSubTypeCount());
|
||||||
spinBox->setValue(150);
|
spinBox->setValue(150);
|
||||||
layout->addWidget(spinBox);
|
layout->addWidget(spinBox);
|
||||||
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||||
&VisualDatabaseDisplaySubTypeFilterWidget::updateSubTypeButtonsVisibility);
|
&VisualDatabaseDisplaySubTypeFilterWidget::updateSubTypeButtonsVisibility);
|
||||||
|
|
||||||
// Create search box
|
// Create search box
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,7 @@ void VisualDatabaseDisplayWidget::initialize()
|
||||||
saveLoadWidget = new VisualDatabaseDisplayFilterSaveLoadWidget(this, filterModel);
|
saveLoadWidget = new VisualDatabaseDisplayFilterSaveLoadWidget(this, filterModel);
|
||||||
nameFilterWidget = new VisualDatabaseDisplayNameFilterWidget(this, deckEditor, filterModel);
|
nameFilterWidget = new VisualDatabaseDisplayNameFilterWidget(this, deckEditor, filterModel);
|
||||||
mainTypeFilterWidget = new VisualDatabaseDisplayMainTypeFilterWidget(this, filterModel);
|
mainTypeFilterWidget = new VisualDatabaseDisplayMainTypeFilterWidget(this, filterModel);
|
||||||
|
formatLegalityWidget = new VisualDatabaseDisplayFormatLegalityFilterWidget(this, filterModel);
|
||||||
subTypeFilterWidget = new VisualDatabaseDisplaySubTypeFilterWidget(this, filterModel);
|
subTypeFilterWidget = new VisualDatabaseDisplaySubTypeFilterWidget(this, filterModel);
|
||||||
setFilterWidget = new VisualDatabaseDisplaySetFilterWidget(this, filterModel);
|
setFilterWidget = new VisualDatabaseDisplaySetFilterWidget(this, filterModel);
|
||||||
|
|
||||||
|
|
@ -223,6 +224,7 @@ void VisualDatabaseDisplayWidget::initialize()
|
||||||
filterContainerLayout->addWidget(quickFilterSubTypeWidget);
|
filterContainerLayout->addWidget(quickFilterSubTypeWidget);
|
||||||
filterContainerLayout->addWidget(quickFilterSetWidget);
|
filterContainerLayout->addWidget(quickFilterSetWidget);
|
||||||
filterContainerLayout->addWidget(mainTypeFilterWidget);
|
filterContainerLayout->addWidget(mainTypeFilterWidget);
|
||||||
|
filterContainerLayout->addWidget(formatLegalityWidget);
|
||||||
|
|
||||||
searchLayout->addWidget(colorFilterWidget);
|
searchLayout->addWidget(colorFilterWidget);
|
||||||
searchLayout->addWidget(clearFilterWidget);
|
searchLayout->addWidget(clearFilterWidget);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#include "../utility/custom_line_edit.h"
|
#include "../utility/custom_line_edit.h"
|
||||||
#include "visual_database_display_color_filter_widget.h"
|
#include "visual_database_display_color_filter_widget.h"
|
||||||
#include "visual_database_display_filter_save_load_widget.h"
|
#include "visual_database_display_filter_save_load_widget.h"
|
||||||
|
#include "visual_database_display_format_legality_filter_widget.h"
|
||||||
#include "visual_database_display_main_type_filter_widget.h"
|
#include "visual_database_display_main_type_filter_widget.h"
|
||||||
#include "visual_database_display_name_filter_widget.h"
|
#include "visual_database_display_name_filter_widget.h"
|
||||||
#include "visual_database_display_set_filter_widget.h"
|
#include "visual_database_display_set_filter_widget.h"
|
||||||
|
|
@ -91,6 +92,7 @@ private:
|
||||||
SettingsButtonWidget *quickFilterNameWidget;
|
SettingsButtonWidget *quickFilterNameWidget;
|
||||||
VisualDatabaseDisplayNameFilterWidget *nameFilterWidget;
|
VisualDatabaseDisplayNameFilterWidget *nameFilterWidget;
|
||||||
VisualDatabaseDisplayMainTypeFilterWidget *mainTypeFilterWidget;
|
VisualDatabaseDisplayMainTypeFilterWidget *mainTypeFilterWidget;
|
||||||
|
VisualDatabaseDisplayFormatLegalityFilterWidget *formatLegalityWidget;
|
||||||
SettingsButtonWidget *quickFilterSubTypeWidget;
|
SettingsButtonWidget *quickFilterSubTypeWidget;
|
||||||
VisualDatabaseDisplaySubTypeFilterWidget *subTypeFilterWidget;
|
VisualDatabaseDisplaySubTypeFilterWidget *subTypeFilterWidget;
|
||||||
SettingsButtonWidget *quickFilterSetWidget;
|
SettingsButtonWidget *quickFilterSetWidget;
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ VisualDeckEditorSampleHandWidget::VisualDeckEditorSampleHandWidget(QWidget *pare
|
||||||
handSizeSpinBox = new QSpinBox(this);
|
handSizeSpinBox = new QSpinBox(this);
|
||||||
handSizeSpinBox->setValue(SettingsCache::instance().getVisualDeckEditorSampleHandSize());
|
handSizeSpinBox->setValue(SettingsCache::instance().getVisualDeckEditorSampleHandSize());
|
||||||
handSizeSpinBox->setMinimum(1);
|
handSizeSpinBox->setMinimum(1);
|
||||||
connect(handSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), &SettingsCache::instance(),
|
connect(handSizeSpinBox, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
|
||||||
&SettingsCache::setVisualDeckEditorSampleHandSize);
|
&SettingsCache::setVisualDeckEditorSampleHandSize);
|
||||||
connect(handSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
connect(handSizeSpinBox, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||||
&VisualDeckEditorSampleHandWidget::updateDisplay);
|
&VisualDeckEditorSampleHandWidget::updateDisplay);
|
||||||
resetAndHandSizeLayout->addWidget(handSizeSpinBox);
|
resetAndHandSizeLayout->addWidget(handSizeSpinBox);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,10 @@ VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidg
|
||||||
unusedColorIdentitiesOpacitySpinBox->setMaximum(100);
|
unusedColorIdentitiesOpacitySpinBox->setMaximum(100);
|
||||||
unusedColorIdentitiesOpacitySpinBox->setValue(
|
unusedColorIdentitiesOpacitySpinBox->setValue(
|
||||||
SettingsCache::instance().getVisualDeckStorageUnusedColorIdentitiesOpacity());
|
SettingsCache::instance().getVisualDeckStorageUnusedColorIdentitiesOpacity());
|
||||||
connect(unusedColorIdentitiesOpacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
connect(unusedColorIdentitiesOpacitySpinBox, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||||
&VisualDeckStorageQuickSettingsWidget::unusedColorIdentitiesOpacityChanged);
|
&VisualDeckStorageQuickSettingsWidget::unusedColorIdentitiesOpacityChanged);
|
||||||
connect(unusedColorIdentitiesOpacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
|
connect(unusedColorIdentitiesOpacitySpinBox, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
|
||||||
&SettingsCache::instance(), &SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity);
|
&SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity);
|
||||||
|
|
||||||
unusedColorIdentitiesOpacityLabel->setBuddy(unusedColorIdentitiesOpacitySpinBox);
|
unusedColorIdentitiesOpacityLabel->setBuddy(unusedColorIdentitiesOpacitySpinBox);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@
|
||||||
#include <libcockatrice/card/database/parser/cockatrice_xml_4.h>
|
#include <libcockatrice/card/database/parser/cockatrice_xml_4.h>
|
||||||
#include <libcockatrice/interfaces/noop_card_preference_provider.h>
|
#include <libcockatrice/interfaces/noop_card_preference_provider.h>
|
||||||
|
|
||||||
|
static const QList<AllowedCount> kConstructedCounts = {{4, "legal"}, {0, "banned"}};
|
||||||
|
|
||||||
|
static const QList<AllowedCount> kSingletonCounts = {{1, "legal"}, {0, "banned"}};
|
||||||
|
|
||||||
class CardDatabaseConverter : public CardDatabase
|
class CardDatabaseConverter : public CardDatabase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -23,7 +27,73 @@ public:
|
||||||
bool saveCardDatabase(const QString &fileName)
|
bool saveCardDatabase(const QString &fileName)
|
||||||
{
|
{
|
||||||
CockatriceXml4Parser parser(new NoopCardPreferenceProvider());
|
CockatriceXml4Parser parser(new NoopCardPreferenceProvider());
|
||||||
return parser.saveToFile(sets, cards, fileName);
|
|
||||||
|
return parser.saveToFile(createDefaultMagicFormats(), sets, cards, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
FormatRulesNameMap createDefaultMagicFormats()
|
||||||
|
{
|
||||||
|
// Predefined common exceptions
|
||||||
|
CardCondition superTypeIsBasic;
|
||||||
|
superTypeIsBasic.field = "type";
|
||||||
|
superTypeIsBasic.matchType = "contains";
|
||||||
|
superTypeIsBasic.value = "Basic Land";
|
||||||
|
|
||||||
|
ExceptionRule basicLands;
|
||||||
|
basicLands.conditions.append(superTypeIsBasic);
|
||||||
|
|
||||||
|
CardCondition anyNumberAllowed;
|
||||||
|
anyNumberAllowed.field = "text";
|
||||||
|
anyNumberAllowed.matchType = "contains";
|
||||||
|
anyNumberAllowed.value = "A deck can have any number of";
|
||||||
|
|
||||||
|
ExceptionRule mayContainAnyNumber;
|
||||||
|
mayContainAnyNumber.conditions.append(anyNumberAllowed);
|
||||||
|
|
||||||
|
// Map to store default rules
|
||||||
|
FormatRulesNameMap defaultFormatRulesNameMap;
|
||||||
|
|
||||||
|
// ----------------- Helper lambda to create format -----------------
|
||||||
|
auto makeFormat = [&](const QString &name, int minDeck = 60, int maxDeck = -1, int maxSideboardSize = 15,
|
||||||
|
const QList<AllowedCount> &allowedCounts = kConstructedCounts) -> FormatRulesPtr {
|
||||||
|
FormatRulesPtr f(new FormatRules);
|
||||||
|
f->formatName = name;
|
||||||
|
f->allowedCounts = allowedCounts;
|
||||||
|
f->minDeckSize = minDeck;
|
||||||
|
f->maxDeckSize = maxDeck;
|
||||||
|
f->maxSideboardSize = maxSideboardSize;
|
||||||
|
f->exceptions.append(basicLands);
|
||||||
|
f->exceptions.append(mayContainAnyNumber);
|
||||||
|
defaultFormatRulesNameMap.insert(name.toLower(), f);
|
||||||
|
return f;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------- Standard formats -----------------
|
||||||
|
makeFormat("Standard");
|
||||||
|
makeFormat("Modern");
|
||||||
|
makeFormat("Legacy");
|
||||||
|
makeFormat("Pioneer");
|
||||||
|
makeFormat("Historic");
|
||||||
|
makeFormat("Timeless");
|
||||||
|
makeFormat("Future");
|
||||||
|
makeFormat("OldSchool");
|
||||||
|
makeFormat("Premodern");
|
||||||
|
makeFormat("Pauper");
|
||||||
|
makeFormat("Penny");
|
||||||
|
|
||||||
|
// ----------------- Singleton formats -----------------
|
||||||
|
makeFormat("Commander", 100, 100, 15, kSingletonCounts);
|
||||||
|
makeFormat("Duel", 100, 100, 15, kSingletonCounts);
|
||||||
|
makeFormat("Brawl", 60, 60, 15, kSingletonCounts);
|
||||||
|
makeFormat("StandardBrawl", 60, 60, 15, kSingletonCounts);
|
||||||
|
makeFormat("Oathbreaker", 60, 60, 15, kSingletonCounts);
|
||||||
|
makeFormat("PauperCommander", 100, 100, 15, kSingletonCounts);
|
||||||
|
makeFormat("Predh", 100, 100, 15, kSingletonCounts);
|
||||||
|
|
||||||
|
// ----------------- Restricted formats -----------------
|
||||||
|
makeFormat("Vintage", 60, -1, 15, {{4, "legal"}, {1, "restricted"}, {0, "banned"}});
|
||||||
|
|
||||||
|
return defaultFormatRulesNameMap;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ add_library(
|
||||||
libcockatrice/card/relation/card_relation.cpp
|
libcockatrice/card/relation/card_relation.cpp
|
||||||
libcockatrice/card/set/card_set.cpp
|
libcockatrice/card/set/card_set.cpp
|
||||||
libcockatrice/card/set/card_set_list.cpp
|
libcockatrice/card/set/card_set_list.cpp
|
||||||
|
libcockatrice/card/format/format_legality_rules.cpp
|
||||||
|
libcockatrice/card/format/format_legality_rules.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef CARD_INFO_H
|
#ifndef CARD_INFO_H
|
||||||
#define CARD_INFO_H
|
#define CARD_INFO_H
|
||||||
|
|
||||||
|
#include "format/format_legality_rules.h"
|
||||||
#include "printing/printing_info.h"
|
#include "printing/printing_info.h"
|
||||||
|
|
||||||
#include <QDate>
|
#include <QDate>
|
||||||
|
|
@ -22,10 +23,12 @@ class ICardDatabaseParser;
|
||||||
|
|
||||||
typedef QSharedPointer<CardInfo> CardInfoPtr;
|
typedef QSharedPointer<CardInfo> CardInfoPtr;
|
||||||
typedef QSharedPointer<CardSet> CardSetPtr;
|
typedef QSharedPointer<CardSet> CardSetPtr;
|
||||||
|
typedef QSharedPointer<FormatRules> FormatRulesPtr;
|
||||||
typedef QMap<QString, QList<PrintingInfo>> SetToPrintingsMap;
|
typedef QMap<QString, QList<PrintingInfo>> SetToPrintingsMap;
|
||||||
|
|
||||||
typedef QHash<QString, CardInfoPtr> CardNameMap;
|
typedef QHash<QString, CardInfoPtr> CardNameMap;
|
||||||
typedef QHash<QString, CardSetPtr> SetNameMap;
|
typedef QHash<QString, CardSetPtr> SetNameMap;
|
||||||
|
typedef QHash<QString, FormatRulesPtr> FormatRulesNameMap;
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(CardInfoPtr)
|
Q_DECLARE_METATYPE(CardInfoPtr)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -199,3 +199,8 @@ void CardDatabase::notifyEnabledSetsChanged()
|
||||||
// inform the carddatabasemodels that they need to re-check their list of cards
|
// inform the carddatabasemodels that they need to re-check their list of cards
|
||||||
emit cardDatabaseEnabledSetsChanged();
|
emit cardDatabaseEnabledSetsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CardDatabase::addFormat(FormatRulesPtr format)
|
||||||
|
{
|
||||||
|
formats.insert(format->formatName.toLower(), format);
|
||||||
|
}
|
||||||
|
|
@ -42,6 +42,8 @@ protected:
|
||||||
/// Sets indexed by short name
|
/// Sets indexed by short name
|
||||||
SetNameMap sets;
|
SetNameMap sets;
|
||||||
|
|
||||||
|
FormatRulesNameMap formats;
|
||||||
|
|
||||||
/// Loader responsible for file discovery and parsing
|
/// Loader responsible for file discovery and parsing
|
||||||
CardDatabaseLoader *loader;
|
CardDatabaseLoader *loader;
|
||||||
|
|
||||||
|
|
@ -141,6 +143,8 @@ public slots:
|
||||||
*/
|
*/
|
||||||
void addSet(CardSetPtr set);
|
void addSet(CardSetPtr set);
|
||||||
|
|
||||||
|
void addFormat(FormatRulesPtr format);
|
||||||
|
|
||||||
/** @brief Loads card databases from configured paths. */
|
/** @brief Loads card databases from configured paths. */
|
||||||
void loadCardDatabases();
|
void loadCardDatabases();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ CardDatabaseLoader::CardDatabaseLoader(QObject *parent,
|
||||||
// connect parser outputs to the database adders
|
// connect parser outputs to the database adders
|
||||||
connect(p, &ICardDatabaseParser::addCard, database, &CardDatabase::addCard, Qt::DirectConnection);
|
connect(p, &ICardDatabaseParser::addCard, database, &CardDatabase::addCard, Qt::DirectConnection);
|
||||||
connect(p, &ICardDatabaseParser::addSet, database, &CardDatabase::addSet, Qt::DirectConnection);
|
connect(p, &ICardDatabaseParser::addSet, database, &CardDatabase::addSet, Qt::DirectConnection);
|
||||||
|
connect(p, &ICardDatabaseParser::addFormat, database, &CardDatabase::addFormat, Qt::DirectConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// when SettingsCache's path changes, trigger reloads
|
// when SettingsCache's path changes, trigger reloads
|
||||||
|
|
@ -149,6 +150,6 @@ bool CardDatabaseLoader::saveCustomTokensToFile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
availableParsers.first()->saveToFile(tmpSets, tmpCards, fileName);
|
availableParsers.first()->saveToFile(FormatRulesNameMap(), tmpSets, tmpCards, fileName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -342,3 +342,26 @@ QMap<QString, int> CardDatabaseQuerier::getAllSubCardTypesWithCount() const
|
||||||
|
|
||||||
return typeCounts;
|
return typeCounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FormatRulesPtr CardDatabaseQuerier::getFormat(const QString &formatName) const
|
||||||
|
{
|
||||||
|
return db->formats.value(formatName.toLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, int> CardDatabaseQuerier::getAllFormatsWithCount() const
|
||||||
|
{
|
||||||
|
QMap<QString, int> formatCounts;
|
||||||
|
|
||||||
|
for (const auto &card : db->cards.values()) {
|
||||||
|
QStringList allProps = card->getProperties();
|
||||||
|
|
||||||
|
for (const QString &prop : allProps) {
|
||||||
|
if (prop.startsWith("format-")) {
|
||||||
|
QString formatName = prop.mid(QStringLiteral("format-").size());
|
||||||
|
formatCounts[formatName]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatCounts;
|
||||||
|
}
|
||||||
|
|
@ -214,6 +214,8 @@ public:
|
||||||
* @return Map of subtype string to count.
|
* @return Map of subtype string to count.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] QMap<QString, int> getAllSubCardTypesWithCount() const;
|
[[nodiscard]] QMap<QString, int> getAllSubCardTypesWithCount() const;
|
||||||
|
FormatRulesPtr getFormat(const QString &formatName) const;
|
||||||
|
QMap<QString, int> getAllFormatsWithCount() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const CardDatabase *db; //!< Card database used for all lookups.
|
const CardDatabase *db; //!< Card database used for all lookups.
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Saves card and set data to a file.
|
* @brief Saves card and set data to a file.
|
||||||
|
* @param _formats
|
||||||
* @param sets Map of sets to save.
|
* @param sets Map of sets to save.
|
||||||
* @param cards Map of cards to save.
|
* @param cards Map of cards to save.
|
||||||
* @param fileName Target file path.
|
* @param fileName Target file path.
|
||||||
|
|
@ -45,7 +46,8 @@ public:
|
||||||
* @param sourceVersion Optional version string of the source.
|
* @param sourceVersion Optional version string of the source.
|
||||||
* @return true if save succeeded.
|
* @return true if save succeeded.
|
||||||
*/
|
*/
|
||||||
virtual bool saveToFile(SetNameMap sets,
|
virtual bool saveToFile(FormatRulesNameMap _formats,
|
||||||
|
SetNameMap sets,
|
||||||
CardNameMap cards,
|
CardNameMap cards,
|
||||||
const QString &fileName,
|
const QString &fileName,
|
||||||
const QString &sourceUrl = "unknown",
|
const QString &sourceUrl = "unknown",
|
||||||
|
|
@ -79,6 +81,8 @@ signals:
|
||||||
|
|
||||||
/** Emitted when a set is loaded from the database. */
|
/** Emitted when a set is loaded from the database. */
|
||||||
void addSet(CardSetPtr set);
|
void addSet(CardSetPtr set);
|
||||||
|
|
||||||
|
void addFormat(FormatRulesPtr format);
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_INTERFACE(ICardDatabaseParser, "ICardDatabaseParser")
|
Q_DECLARE_INTERFACE(ICardDatabaseParser, "ICardDatabaseParser")
|
||||||
|
|
|
||||||
|
|
@ -438,12 +438,15 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CockatriceXml3Parser::saveToFile(SetNameMap _sets,
|
bool CockatriceXml3Parser::saveToFile(FormatRulesNameMap _formats,
|
||||||
|
SetNameMap _sets,
|
||||||
CardNameMap cards,
|
CardNameMap cards,
|
||||||
const QString &fileName,
|
const QString &fileName,
|
||||||
const QString &sourceUrl,
|
const QString &sourceUrl,
|
||||||
const QString &sourceVersion)
|
const QString &sourceVersion)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(_formats);
|
||||||
|
|
||||||
QFile file(fileName);
|
QFile file(fileName);
|
||||||
if (!file.open(QIODevice::WriteOnly)) {
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,8 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Save sets and cards back to an XML3 file.
|
* @brief Save sets and cards back to an XML3 file.
|
||||||
*/
|
*/
|
||||||
bool saveToFile(SetNameMap _sets,
|
bool saveToFile(FormatRulesNameMap _formats,
|
||||||
|
SetNameMap _sets,
|
||||||
CardNameMap cards,
|
CardNameMap cards,
|
||||||
const QString &fileName,
|
const QString &fileName,
|
||||||
const QString &sourceUrl = "unknown",
|
const QString &sourceUrl = "unknown",
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
|
#include <libcockatrice/card/format/format_legality_rules.h>
|
||||||
#include <version_string.h>
|
#include <version_string.h>
|
||||||
|
|
||||||
#define COCKATRICE_XML4_TAGNAME "cockatrice_carddatabase"
|
#define COCKATRICE_XML4_TAGNAME "cockatrice_carddatabase"
|
||||||
|
|
@ -60,7 +61,9 @@ void CockatriceXml4Parser::parseFile(QIODevice &device)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto xmlName = xml.name().toString();
|
auto xmlName = xml.name().toString();
|
||||||
if (xmlName == "sets") {
|
if (xmlName == "formats") {
|
||||||
|
loadFormats(xml);
|
||||||
|
} else if (xmlName == "sets") {
|
||||||
loadSetsFromXml(xml);
|
loadSetsFromXml(xml);
|
||||||
} else if (xmlName == "cards") {
|
} else if (xmlName == "cards") {
|
||||||
loadCardsFromXml(xml);
|
loadCardsFromXml(xml);
|
||||||
|
|
@ -78,6 +81,116 @@ void CockatriceXml4Parser::parseFile(QIODevice &device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QSharedPointer<FormatRules> parseFormat(QXmlStreamReader &xml)
|
||||||
|
{
|
||||||
|
auto rulesPtr = FormatRulesPtr(new FormatRules());
|
||||||
|
|
||||||
|
if (xml.attributes().hasAttribute("formatName")) {
|
||||||
|
rulesPtr->formatName = xml.attributes().value("formatName").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!xml.atEnd()) {
|
||||||
|
auto token = xml.readNext();
|
||||||
|
|
||||||
|
if (token == QXmlStreamReader::EndElement && xml.name().toString() == "format") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token != QXmlStreamReader::StartElement) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString xmlName = xml.name().toString();
|
||||||
|
|
||||||
|
if (xmlName == "minDeckSize") {
|
||||||
|
rulesPtr->minDeckSize = xml.readElementText().toInt();
|
||||||
|
} else if (xmlName == "maxDeckSize") {
|
||||||
|
QString text = xml.readElementText();
|
||||||
|
rulesPtr->maxDeckSize = text.toInt();
|
||||||
|
} else if (xmlName == "maxSideboardSize") {
|
||||||
|
rulesPtr->maxSideboardSize = xml.readElementText().toInt();
|
||||||
|
} else if (xmlName == "allowedCounts") {
|
||||||
|
while (!xml.atEnd()) {
|
||||||
|
token = xml.readNext();
|
||||||
|
|
||||||
|
if (token == QXmlStreamReader::EndElement && xml.name().toString() == "allowedCounts") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token == QXmlStreamReader::StartElement && xml.name().toString() == "count") {
|
||||||
|
|
||||||
|
AllowedCount c;
|
||||||
|
|
||||||
|
QString maxAttr = xml.attributes().value("max").toString();
|
||||||
|
c.max = (maxAttr == "unlimited") ? -1 : maxAttr.toInt();
|
||||||
|
|
||||||
|
c.label = xml.readElementText().trimmed();
|
||||||
|
|
||||||
|
rulesPtr->allowedCounts.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (xmlName == "exceptions") {
|
||||||
|
while (!xml.atEnd()) {
|
||||||
|
token = xml.readNext();
|
||||||
|
|
||||||
|
if (token == QXmlStreamReader::EndElement && xml.name().toString() == "exceptions") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token == QXmlStreamReader::StartElement && xml.name().toString() == "exception") {
|
||||||
|
ExceptionRule ex;
|
||||||
|
|
||||||
|
while (!xml.atEnd()) {
|
||||||
|
token = xml.readNext();
|
||||||
|
|
||||||
|
if (token == QXmlStreamReader::EndElement && xml.name().toString() == "exception") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token == QXmlStreamReader::StartElement) {
|
||||||
|
QString ename = xml.name().toString();
|
||||||
|
|
||||||
|
if (ename == "maxCopies") {
|
||||||
|
QString text = xml.readElementText();
|
||||||
|
ex.maxCopies = (text == "unlimited") ? -1 : text.toInt();
|
||||||
|
} else if (ename == "cardCondition") {
|
||||||
|
CardCondition cond;
|
||||||
|
cond.field = xml.attributes().value("field").toString();
|
||||||
|
cond.matchType = xml.attributes().value("match").toString();
|
||||||
|
cond.value = xml.attributes().value("value").toString();
|
||||||
|
ex.conditions.append(cond);
|
||||||
|
xml.skipCurrentElement();
|
||||||
|
} else {
|
||||||
|
xml.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rulesPtr->exceptions.append(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xml.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rulesPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CockatriceXml4Parser::loadFormats(QXmlStreamReader &xml)
|
||||||
|
{
|
||||||
|
while (!xml.atEnd()) {
|
||||||
|
if (xml.readNext() == QXmlStreamReader::EndElement) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xml.name().toString() == "format") {
|
||||||
|
auto rulesPtr = parseFormat(xml);
|
||||||
|
emit addFormat(rulesPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CockatriceXml4Parser::loadSetsFromXml(QXmlStreamReader &xml)
|
void CockatriceXml4Parser::loadSetsFromXml(QXmlStreamReader &xml)
|
||||||
{
|
{
|
||||||
while (!xml.atEnd()) {
|
while (!xml.atEnd()) {
|
||||||
|
|
@ -273,6 +386,59 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const QSharedPointer<FormatRules> &rulesPtr)
|
||||||
|
{
|
||||||
|
if (rulesPtr.isNull()) {
|
||||||
|
qCWarning(CockatriceXml4Log) << "&operator<< FormatRules is nullptr";
|
||||||
|
return xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormatRules &rules = *rulesPtr;
|
||||||
|
|
||||||
|
xml.writeStartElement("format");
|
||||||
|
if (!rules.formatName.isEmpty()) {
|
||||||
|
xml.writeAttribute("formatName", rules.formatName);
|
||||||
|
}
|
||||||
|
|
||||||
|
xml.writeTextElement("minDeckSize", QString::number(rules.minDeckSize));
|
||||||
|
xml.writeTextElement("maxDeckSize", rules.maxDeckSize >= 0 ? QString::number(rules.maxDeckSize) : "0");
|
||||||
|
xml.writeTextElement("maxSideboardSize", QString::number(rules.maxSideboardSize));
|
||||||
|
if (!rules.allowedCounts.isEmpty()) {
|
||||||
|
xml.writeStartElement("allowedCounts");
|
||||||
|
|
||||||
|
for (const AllowedCount &c : rules.allowedCounts) {
|
||||||
|
xml.writeStartElement("count");
|
||||||
|
xml.writeAttribute("max", c.max == -1 ? "unlimited" : QString::number(c.max));
|
||||||
|
xml.writeCharacters(c.label);
|
||||||
|
xml.writeEndElement(); // count
|
||||||
|
}
|
||||||
|
|
||||||
|
xml.writeEndElement(); // allowedCounts
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rules.exceptions.isEmpty()) {
|
||||||
|
xml.writeStartElement("exceptions");
|
||||||
|
for (const ExceptionRule &ex : rules.exceptions) {
|
||||||
|
xml.writeStartElement("exception");
|
||||||
|
xml.writeTextElement("maxCopies", ex.maxCopies == -1 ? "unlimited" : QString::number(ex.maxCopies));
|
||||||
|
|
||||||
|
for (const CardCondition &cond : ex.conditions) {
|
||||||
|
xml.writeStartElement("cardCondition");
|
||||||
|
xml.writeAttribute("field", cond.field);
|
||||||
|
xml.writeAttribute("match", cond.matchType);
|
||||||
|
xml.writeAttribute("value", cond.value);
|
||||||
|
xml.writeEndElement(); // cardCondition
|
||||||
|
}
|
||||||
|
|
||||||
|
xml.writeEndElement(); // exception
|
||||||
|
}
|
||||||
|
xml.writeEndElement(); // exceptions
|
||||||
|
}
|
||||||
|
|
||||||
|
xml.writeEndElement(); // format
|
||||||
|
return xml;
|
||||||
|
}
|
||||||
|
|
||||||
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSetPtr &set)
|
static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSetPtr &set)
|
||||||
{
|
{
|
||||||
if (set.isNull()) {
|
if (set.isNull()) {
|
||||||
|
|
@ -399,7 +565,8 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CockatriceXml4Parser::saveToFile(SetNameMap _sets,
|
bool CockatriceXml4Parser::saveToFile(FormatRulesNameMap _formats,
|
||||||
|
SetNameMap _sets,
|
||||||
CardNameMap cards,
|
CardNameMap cards,
|
||||||
const QString &fileName,
|
const QString &fileName,
|
||||||
const QString &sourceUrl,
|
const QString &sourceUrl,
|
||||||
|
|
@ -426,6 +593,14 @@ bool CockatriceXml4Parser::saveToFile(SetNameMap _sets,
|
||||||
xml.writeTextElement("sourceVersion", sourceVersion);
|
xml.writeTextElement("sourceVersion", sourceVersion);
|
||||||
xml.writeEndElement();
|
xml.writeEndElement();
|
||||||
|
|
||||||
|
if (_formats.count() > 0) {
|
||||||
|
xml.writeStartElement("formats");
|
||||||
|
for (FormatRulesPtr format : _formats) {
|
||||||
|
xml << format;
|
||||||
|
}
|
||||||
|
xml.writeEndElement();
|
||||||
|
}
|
||||||
|
|
||||||
if (_sets.count() > 0) {
|
if (_sets.count() > 0) {
|
||||||
xml.writeStartElement("sets");
|
xml.writeStartElement("sets");
|
||||||
for (CardSetPtr set : _sets) {
|
for (CardSetPtr set : _sets) {
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,8 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Save sets and cards back to an XML4 file.
|
* @brief Save sets and cards back to an XML4 file.
|
||||||
*/
|
*/
|
||||||
bool saveToFile(SetNameMap _sets,
|
bool saveToFile(FormatRulesNameMap _formats,
|
||||||
|
SetNameMap _sets,
|
||||||
CardNameMap cards,
|
CardNameMap cards,
|
||||||
const QString &fileName,
|
const QString &fileName,
|
||||||
const QString &sourceUrl = "unknown",
|
const QString &sourceUrl = "unknown",
|
||||||
|
|
@ -72,6 +73,7 @@ private:
|
||||||
*/
|
*/
|
||||||
void loadCardsFromXml(QXmlStreamReader &xml);
|
void loadCardsFromXml(QXmlStreamReader &xml);
|
||||||
|
|
||||||
|
void loadFormats(QXmlStreamReader &xml);
|
||||||
/**
|
/**
|
||||||
* @brief Load all <set> elements from the XML stream.
|
* @brief Load all <set> elements from the XML stream.
|
||||||
* @param xml The open QXmlStreamReader positioned at the <sets> element.
|
* @param xml The open QXmlStreamReader positioned at the <sets> element.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "format_legality_rules.h"
|
||||||
|
|
||||||
|
#include <libcockatrice/card/card_info.h>
|
||||||
|
|
||||||
|
bool cardMatchesCondition(const CardInfo &card, const CardCondition &cond)
|
||||||
|
{
|
||||||
|
CardMatchType type = matchTypeFromString(cond.matchType);
|
||||||
|
QString fieldValue;
|
||||||
|
if (cond.field == "name") {
|
||||||
|
fieldValue = card.getName();
|
||||||
|
} else if (cond.field == "text") {
|
||||||
|
fieldValue = card.getText();
|
||||||
|
} else {
|
||||||
|
fieldValue = card.getProperty(cond.field);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case CardMatchType::Equals:
|
||||||
|
return fieldValue == cond.value;
|
||||||
|
case CardMatchType::NotEquals:
|
||||||
|
return fieldValue != cond.value;
|
||||||
|
case CardMatchType::Contains:
|
||||||
|
return fieldValue.contains(cond.value, Qt::CaseInsensitive);
|
||||||
|
case CardMatchType::NotContains:
|
||||||
|
return !fieldValue.contains(cond.value, Qt::CaseInsensitive);
|
||||||
|
case CardMatchType::Regex: {
|
||||||
|
QRegularExpression re(cond.value, QRegularExpression::CaseInsensitiveOption);
|
||||||
|
return re.match(fieldValue).hasMatch();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool exceptionAppliesToCard(const CardInfo &card, const ExceptionRule &rule)
|
||||||
|
{
|
||||||
|
for (const CardCondition &cond : rule.conditions) {
|
||||||
|
if (!cardMatchesCondition(card, cond)) {
|
||||||
|
return false; // all conditions must match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cardHasAnyException(const CardInfo &card, const FormatRules &format)
|
||||||
|
{
|
||||||
|
for (const ExceptionRule &rule : format.exceptions) {
|
||||||
|
if (exceptionAppliesToCard(card, rule)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
#ifndef COCKATRICE_FORMAT_LEGALITY_RULES_H
|
||||||
|
#define COCKATRICE_FORMAT_LEGALITY_RULES_H
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class CardInfo;
|
||||||
|
using CardInfoPtr = QSharedPointer<CardInfo>;
|
||||||
|
|
||||||
|
struct CardCondition
|
||||||
|
{
|
||||||
|
QString field; // e.g. "type", "maintype", "text"
|
||||||
|
QString matchType; // "contains", "equals", "regex", "notContains", etc.
|
||||||
|
QString value; // e.g. "Basic Land"
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AllowedCount
|
||||||
|
{
|
||||||
|
int max = 0; // 4, 1, 0, or -1 for unlimited
|
||||||
|
QString label; // "legal", "restricted", "banned"
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExceptionRule
|
||||||
|
{
|
||||||
|
QList<CardCondition> conditions; // All must match
|
||||||
|
int maxCopies = -1; // -1 = unlimited
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FormatRules
|
||||||
|
{
|
||||||
|
QString formatName;
|
||||||
|
int minDeckSize = 60;
|
||||||
|
int maxDeckSize = -1; // -1 = unlimited
|
||||||
|
int maxSideboardSize = 15;
|
||||||
|
|
||||||
|
QList<AllowedCount> allowedCounts;
|
||||||
|
|
||||||
|
QList<ExceptionRule> exceptions; // Cards allowed to break maxCopies
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CardMatchType
|
||||||
|
{
|
||||||
|
Equals,
|
||||||
|
NotEquals,
|
||||||
|
Contains,
|
||||||
|
NotContains,
|
||||||
|
Regex
|
||||||
|
};
|
||||||
|
|
||||||
|
// convert string to enum
|
||||||
|
inline CardMatchType matchTypeFromString(const QString &str)
|
||||||
|
{
|
||||||
|
if (str == "equals")
|
||||||
|
return CardMatchType::Equals;
|
||||||
|
if (str == "notEquals")
|
||||||
|
return CardMatchType::NotEquals;
|
||||||
|
if (str == "contains")
|
||||||
|
return CardMatchType::Contains;
|
||||||
|
if (str == "notContains")
|
||||||
|
return CardMatchType::NotContains;
|
||||||
|
if (str == "regex")
|
||||||
|
return CardMatchType::Regex;
|
||||||
|
return CardMatchType::Equals; // fallback default
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cardMatchesCondition(const CardInfo &card, const CardCondition &cond);
|
||||||
|
|
||||||
|
bool exceptionAppliesToCard(const CardInfo &card, const ExceptionRule &rule);
|
||||||
|
|
||||||
|
bool cardHasAnyException(const CardInfo &card, const FormatRules &format);
|
||||||
|
|
||||||
|
#endif // COCKATRICE_FORMAT_LEGALITY_RULES_H
|
||||||
|
|
@ -136,6 +136,8 @@ bool DeckList::readElement(QXmlStreamReader *xml)
|
||||||
metadata.lastLoadedTimestamp = xml->readElementText();
|
metadata.lastLoadedTimestamp = xml->readElementText();
|
||||||
} else if (childName == "deckname") {
|
} else if (childName == "deckname") {
|
||||||
metadata.name = xml->readElementText();
|
metadata.name = xml->readElementText();
|
||||||
|
} else if (childName == "format") {
|
||||||
|
metadata.gameFormat = xml->readElementText();
|
||||||
} else if (childName == "comments") {
|
} else if (childName == "comments") {
|
||||||
metadata.comments = xml->readElementText();
|
metadata.comments = xml->readElementText();
|
||||||
} else if (childName == "bannerCard") {
|
} else if (childName == "bannerCard") {
|
||||||
|
|
@ -170,6 +172,7 @@ void writeMetadata(QXmlStreamWriter *xml, const DeckList::Metadata &metadata)
|
||||||
{
|
{
|
||||||
xml->writeTextElement("lastLoadedTimestamp", metadata.lastLoadedTimestamp);
|
xml->writeTextElement("lastLoadedTimestamp", metadata.lastLoadedTimestamp);
|
||||||
xml->writeTextElement("deckname", metadata.name);
|
xml->writeTextElement("deckname", metadata.name);
|
||||||
|
xml->writeTextElement("format", metadata.gameFormat);
|
||||||
xml->writeStartElement("bannerCard");
|
xml->writeStartElement("bannerCard");
|
||||||
xml->writeAttribute("providerId", metadata.bannerCard.providerId);
|
xml->writeAttribute("providerId", metadata.bannerCard.providerId);
|
||||||
xml->writeCharacters(metadata.bannerCard.name);
|
xml->writeCharacters(metadata.bannerCard.name);
|
||||||
|
|
@ -594,15 +597,16 @@ DecklistCardNode *DeckList::addCard(const QString &cardName,
|
||||||
const int position,
|
const int position,
|
||||||
const QString &cardSetName,
|
const QString &cardSetName,
|
||||||
const QString &cardSetCollectorNumber,
|
const QString &cardSetCollectorNumber,
|
||||||
const QString &cardProviderId)
|
const QString &cardProviderId,
|
||||||
|
const bool formatLegal)
|
||||||
{
|
{
|
||||||
auto *zoneNode = dynamic_cast<InnerDecklistNode *>(root->findChild(zoneName));
|
auto *zoneNode = dynamic_cast<InnerDecklistNode *>(root->findChild(zoneName));
|
||||||
if (zoneNode == nullptr) {
|
if (zoneNode == nullptr) {
|
||||||
zoneNode = new InnerDecklistNode(zoneName, root);
|
zoneNode = new InnerDecklistNode(zoneName, root);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *node =
|
auto *node = new DecklistCardNode(cardName, 1, zoneNode, position, cardSetName, cardSetCollectorNumber,
|
||||||
new DecklistCardNode(cardName, 1, zoneNode, position, cardSetName, cardSetCollectorNumber, cardProviderId);
|
cardProviderId, formatLegal);
|
||||||
refreshDeckHash();
|
refreshDeckHash();
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ public:
|
||||||
{
|
{
|
||||||
QString name; ///< User-defined deck name.
|
QString name; ///< User-defined deck name.
|
||||||
QString comments; ///< Free-form comments or notes.
|
QString comments; ///< Free-form comments or notes.
|
||||||
|
QString gameFormat; ///< The name of the game format this deck contains legal cards for
|
||||||
CardRef bannerCard; ///< Optional representative card for the deck.
|
CardRef bannerCard; ///< Optional representative card for the deck.
|
||||||
QStringList tags; ///< User-defined tags for deck classification.
|
QStringList tags; ///< User-defined tags for deck classification.
|
||||||
QString lastLoadedTimestamp; ///< Timestamp string of last load.
|
QString lastLoadedTimestamp; ///< Timestamp string of last load.
|
||||||
|
|
@ -181,6 +182,10 @@ public:
|
||||||
{
|
{
|
||||||
metadata.lastLoadedTimestamp = _lastLoadedTimestamp;
|
metadata.lastLoadedTimestamp = _lastLoadedTimestamp;
|
||||||
}
|
}
|
||||||
|
void setGameFormat(const QString &_gameFormat = QString())
|
||||||
|
{
|
||||||
|
metadata.gameFormat = _gameFormat;
|
||||||
|
}
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
/// @brief Construct an empty deck.
|
/// @brief Construct an empty deck.
|
||||||
|
|
@ -219,6 +224,10 @@ public:
|
||||||
{
|
{
|
||||||
return metadata.lastLoadedTimestamp;
|
return metadata.lastLoadedTimestamp;
|
||||||
}
|
}
|
||||||
|
QString getGameFormat() const
|
||||||
|
{
|
||||||
|
return metadata.gameFormat;
|
||||||
|
}
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
bool isBlankDeck() const
|
bool isBlankDeck() const
|
||||||
|
|
@ -277,7 +286,8 @@ public:
|
||||||
int position,
|
int position,
|
||||||
const QString &cardSetName = QString(),
|
const QString &cardSetName = QString(),
|
||||||
const QString &cardSetCollectorNumber = QString(),
|
const QString &cardSetCollectorNumber = QString(),
|
||||||
const QString &cardProviderId = QString());
|
const QString &cardProviderId = QString(),
|
||||||
|
const bool formatLegal = true);
|
||||||
bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr);
|
bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr);
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,12 @@ public:
|
||||||
/// @param _cardSetNumber Set the collector number.
|
/// @param _cardSetNumber Set the collector number.
|
||||||
virtual void setCardCollectorNumber(const QString &_cardSetNumber) = 0;
|
virtual void setCardCollectorNumber(const QString &_cardSetNumber) = 0;
|
||||||
|
|
||||||
|
/// @return The format legality of the card
|
||||||
|
virtual bool getFormatLegality() const = 0;
|
||||||
|
|
||||||
|
/// @param _formatLegal If the card is considered legal
|
||||||
|
virtual void setFormatLegality(const bool _formatLegal) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the height of this node in the tree.
|
* @brief Get the height of this node in the tree.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ class DecklistCardNode : public AbstractDecklistCardNode
|
||||||
QString cardSetShortName; ///< Short set code (e.g., "NEO").
|
QString cardSetShortName; ///< Short set code (e.g., "NEO").
|
||||||
QString cardSetNumber; ///< Collector number within the set.
|
QString cardSetNumber; ///< Collector number within the set.
|
||||||
QString cardProviderId; ///< External provider identifier (e.g., UUID).
|
QString cardProviderId; ///< External provider identifier (e.g., UUID).
|
||||||
|
bool formatLegal; ///< Format legality
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|
@ -63,6 +64,7 @@ public:
|
||||||
* @param _cardSetShortName Short set code (e.g., "NEO").
|
* @param _cardSetShortName Short set code (e.g., "NEO").
|
||||||
* @param _cardSetNumber Collector number within the set.
|
* @param _cardSetNumber Collector number within the set.
|
||||||
* @param _cardProviderId External provider ID (e.g., UUID).
|
* @param _cardProviderId External provider ID (e.g., UUID).
|
||||||
|
* @param _formatLegality If the card is legal in the format
|
||||||
*
|
*
|
||||||
* On construction, if a parent is provided, this node is inserted into
|
* On construction, if a parent is provided, this node is inserted into
|
||||||
* the parent’s children list automatically.
|
* the parent’s children list automatically.
|
||||||
|
|
@ -73,10 +75,11 @@ public:
|
||||||
int position = -1,
|
int position = -1,
|
||||||
QString _cardSetShortName = QString(),
|
QString _cardSetShortName = QString(),
|
||||||
QString _cardSetNumber = QString(),
|
QString _cardSetNumber = QString(),
|
||||||
QString _cardProviderId = QString())
|
QString _cardProviderId = QString(),
|
||||||
|
bool _formatLegality = true)
|
||||||
: AbstractDecklistCardNode(_parent, position), name(std::move(_name)), number(_number),
|
: AbstractDecklistCardNode(_parent, position), name(std::move(_name)), number(_number),
|
||||||
cardSetShortName(std::move(_cardSetShortName)), cardSetNumber(std::move(_cardSetNumber)),
|
cardSetShortName(std::move(_cardSetShortName)), cardSetNumber(std::move(_cardSetNumber)),
|
||||||
cardProviderId(std::move(_cardProviderId))
|
cardProviderId(std::move(_cardProviderId)), formatLegal(_formatLegality)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,6 +153,18 @@ public:
|
||||||
cardSetNumber = _cardSetNumber;
|
cardSetNumber = _cardSetNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @return The format legality of the card
|
||||||
|
[[nodiscard]] bool getFormatLegality() const override
|
||||||
|
{
|
||||||
|
return formatLegal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @param _formatLegal If the card is considered legal
|
||||||
|
void setFormatLegality(const bool _formatLegal) override
|
||||||
|
{
|
||||||
|
formatLegal = _formatLegal;
|
||||||
|
}
|
||||||
|
|
||||||
/// @return Always false; card nodes are not deck headers.
|
/// @return Always false; card nodes are not deck headers.
|
||||||
[[nodiscard]] bool isDeckHeader() const override
|
[[nodiscard]] bool isDeckHeader() const override
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,10 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const
|
||||||
return card->depth();
|
return card->depth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case DeckRoles::IsLegalRole: {
|
||||||
|
return card->getFormatLegality();
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -268,6 +272,7 @@ bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, con
|
||||||
switch (index.column()) {
|
switch (index.column()) {
|
||||||
case DeckListModelColumns::CARD_AMOUNT:
|
case DeckListModelColumns::CARD_AMOUNT:
|
||||||
node->setNumber(value.toInt());
|
node->setNumber(value.toInt());
|
||||||
|
refreshCardFormatLegalities();
|
||||||
break;
|
break;
|
||||||
case DeckListModelColumns::CARD_NAME:
|
case DeckListModelColumns::CARD_NAME:
|
||||||
node->setName(value.toString());
|
node->setName(value.toString());
|
||||||
|
|
@ -414,8 +419,9 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam
|
||||||
// Determine the correct index
|
// Determine the correct index
|
||||||
int insertRow = findSortedInsertRow(groupNode, cardInfo);
|
int insertRow = findSortedInsertRow(groupNode, cardInfo);
|
||||||
|
|
||||||
auto *decklistCard = deckList->addCard(cardInfo->getName(), zoneName, insertRow, cardSetName,
|
auto *decklistCard =
|
||||||
printingInfo.getProperty("num"), printingInfo.getProperty("uuid"));
|
deckList->addCard(cardInfo->getName(), zoneName, insertRow, cardSetName, printingInfo.getProperty("num"),
|
||||||
|
printingInfo.getProperty("uuid"), isCardLegalForCurrentFormat(cardInfo));
|
||||||
|
|
||||||
beginInsertRows(parentIndex, insertRow, insertRow);
|
beginInsertRows(parentIndex, insertRow, insertRow);
|
||||||
cardNode = new DecklistModelCardNode(decklistCard, groupNode, insertRow);
|
cardNode = new DecklistModelCardNode(decklistCard, groupNode, insertRow);
|
||||||
|
|
@ -532,6 +538,13 @@ void DeckListModel::setActiveGroupCriteria(DeckListModelGroupCriteria::Type newC
|
||||||
rebuildTree();
|
rebuildTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeckListModel::setActiveFormat(const QString &_format)
|
||||||
|
{
|
||||||
|
deckList->setGameFormat(_format);
|
||||||
|
refreshCardFormatLegalities();
|
||||||
|
emitBackgroundUpdates(QModelIndex()); // start from root
|
||||||
|
}
|
||||||
|
|
||||||
void DeckListModel::cleanList()
|
void DeckListModel::cleanList()
|
||||||
{
|
{
|
||||||
setDeckList(new DeckList);
|
setDeckList(new DeckList);
|
||||||
|
|
@ -596,3 +609,89 @@ QList<QString> DeckListModel::getZones() const
|
||||||
|
|
||||||
return zones;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,14 @@ public:
|
||||||
{
|
{
|
||||||
dataNode->setCardCollectorNumber(_cardSetNumber);
|
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.
|
* @brief Returns the underlying data node.
|
||||||
|
|
@ -209,6 +217,9 @@ public slots:
|
||||||
*/
|
*/
|
||||||
void rebuildTree();
|
void rebuildTree();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setActiveFormat(const QString &_format);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
* @brief Emitted whenever the deck hash changes due to modifications in the model.
|
* @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> getCards() const;
|
||||||
[[nodiscard]] QList<ExactCard> getCardsForZone(const QString &zoneName) const;
|
[[nodiscard]] QList<ExactCard> getCardsForZone(const QString &zoneName) const;
|
||||||
[[nodiscard]] QList<QString> getZones() 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.
|
* @brief Sets the criteria used to group cards in the model.
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,10 @@
|
||||||
#include <libcockatrice/card/database/parser/cockatrice_xml_4.h>
|
#include <libcockatrice/card/database/parser/cockatrice_xml_4.h>
|
||||||
#include <libcockatrice/card/relation/card_relation.h>
|
#include <libcockatrice/card/relation/card_relation.h>
|
||||||
|
|
||||||
|
static const QList<AllowedCount> kConstructedCounts = {{4, "legal"}, {0, "banned"}};
|
||||||
|
|
||||||
|
static const QList<AllowedCount> kSingletonCounts = {{1, "legal"}, {0, "banned"}};
|
||||||
|
|
||||||
SplitCardPart::SplitCardPart(const QString &_name,
|
SplitCardPart::SplitCardPart(const QString &_name,
|
||||||
const QString &_text,
|
const QString &_text,
|
||||||
const QVariantHash &_properties,
|
const QVariantHash &_properties,
|
||||||
|
|
@ -463,6 +467,71 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList
|
||||||
return numCards;
|
return numCards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FormatRulesNameMap OracleImporter::createDefaultMagicFormats()
|
||||||
|
{
|
||||||
|
// Predefined common exceptions
|
||||||
|
CardCondition superTypeIsBasic;
|
||||||
|
superTypeIsBasic.field = "type";
|
||||||
|
superTypeIsBasic.matchType = "contains";
|
||||||
|
superTypeIsBasic.value = "Basic Land";
|
||||||
|
|
||||||
|
ExceptionRule basicLands;
|
||||||
|
basicLands.conditions.append(superTypeIsBasic);
|
||||||
|
|
||||||
|
CardCondition anyNumberAllowed;
|
||||||
|
anyNumberAllowed.field = "text";
|
||||||
|
anyNumberAllowed.matchType = "contains";
|
||||||
|
anyNumberAllowed.value = "A deck can have any number of";
|
||||||
|
|
||||||
|
ExceptionRule mayContainAnyNumber;
|
||||||
|
mayContainAnyNumber.conditions.append(anyNumberAllowed);
|
||||||
|
|
||||||
|
// Map to store default rules
|
||||||
|
FormatRulesNameMap defaultFormatRulesNameMap;
|
||||||
|
|
||||||
|
// ----------------- Helper lambda to create format -----------------
|
||||||
|
auto makeFormat = [&](const QString &name, int minDeck = 60, int maxDeck = -1, int maxSideboardSize = 15,
|
||||||
|
const QList<AllowedCount> &allowedCounts = kConstructedCounts) -> FormatRulesPtr {
|
||||||
|
FormatRulesPtr f(new FormatRules);
|
||||||
|
f->formatName = name;
|
||||||
|
f->allowedCounts = allowedCounts;
|
||||||
|
f->minDeckSize = minDeck;
|
||||||
|
f->maxDeckSize = maxDeck;
|
||||||
|
f->maxSideboardSize = maxSideboardSize;
|
||||||
|
f->exceptions.append(basicLands);
|
||||||
|
f->exceptions.append(mayContainAnyNumber);
|
||||||
|
defaultFormatRulesNameMap.insert(name.toLower(), f);
|
||||||
|
return f;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------- Standard formats -----------------
|
||||||
|
makeFormat("Standard");
|
||||||
|
makeFormat("Modern");
|
||||||
|
makeFormat("Legacy");
|
||||||
|
makeFormat("Pioneer");
|
||||||
|
makeFormat("Historic");
|
||||||
|
makeFormat("Timeless");
|
||||||
|
makeFormat("Future");
|
||||||
|
makeFormat("OldSchool");
|
||||||
|
makeFormat("Premodern");
|
||||||
|
makeFormat("Pauper");
|
||||||
|
makeFormat("Penny");
|
||||||
|
|
||||||
|
// ----------------- Singleton formats -----------------
|
||||||
|
makeFormat("Commander", 100, 100, 15, kSingletonCounts);
|
||||||
|
makeFormat("Duel", 100, 100, 15, kSingletonCounts);
|
||||||
|
makeFormat("Brawl", 60, 60, 15, kSingletonCounts);
|
||||||
|
makeFormat("StandardBrawl", 60, 60, 15, kSingletonCounts);
|
||||||
|
makeFormat("Oathbreaker", 60, 60, 15, kSingletonCounts);
|
||||||
|
makeFormat("PauperCommander", 100, 100, 15, kSingletonCounts);
|
||||||
|
makeFormat("Predh", 100, 100, 15, kSingletonCounts);
|
||||||
|
|
||||||
|
// ----------------- Restricted formats -----------------
|
||||||
|
makeFormat("Vintage", 60, -1, 15, {{4, "legal"}, {1, "restricted"}, {0, "banned"}});
|
||||||
|
|
||||||
|
return defaultFormatRulesNameMap;
|
||||||
|
}
|
||||||
|
|
||||||
int OracleImporter::startImport()
|
int OracleImporter::startImport()
|
||||||
{
|
{
|
||||||
static ICardSetPriorityController *noOpController = new NoopCardSetPriorityController();
|
static ICardSetPriorityController *noOpController = new NoopCardSetPriorityController();
|
||||||
|
|
@ -497,7 +566,8 @@ int OracleImporter::startImport()
|
||||||
bool OracleImporter::saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion)
|
bool OracleImporter::saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion)
|
||||||
{
|
{
|
||||||
CockatriceXml4Parser parser(new NoopCardPreferenceProvider());
|
CockatriceXml4Parser parser(new NoopCardPreferenceProvider());
|
||||||
return parser.saveToFile(sets, cards, fileName, sourceUrl, sourceVersion);
|
|
||||||
|
return parser.saveToFile(createDefaultMagicFormats(), sets, cards, fileName, sourceUrl, sourceVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OracleImporter::clear()
|
void OracleImporter::clear()
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,7 @@ public:
|
||||||
int startImport();
|
int startImport();
|
||||||
bool saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion);
|
bool saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion);
|
||||||
int importCardsFromSet(const CardSetPtr ¤tSet, const QList<QVariant> &cardsList);
|
int importCardsFromSet(const CardSetPtr ¤tSet, const QList<QVariant> &cardsList);
|
||||||
|
FormatRulesNameMap createDefaultMagicFormats();
|
||||||
const CardNameMap &getCardList() const
|
const CardNameMap &getCardList() const
|
||||||
{
|
{
|
||||||
return cards;
|
return cards;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue