mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -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/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_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_name_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(showTagsWidgetCheckBox);
|
||||
|
||||
formatLabel = new QLabel(this);
|
||||
|
||||
formatComboBox = new QComboBox(this);
|
||||
formatComboBox->addItem(tr("Loading Database..."));
|
||||
formatComboBox->setEnabled(false); // Disable until loaded
|
||||
|
||||
commentsLabel = new QLabel();
|
||||
commentsLabel->setObjectName("commentsLabel");
|
||||
commentsEdit = new QTextEdit;
|
||||
|
|
@ -208,13 +214,16 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
|||
upperLayout->addWidget(commentsLabel, 1, 0);
|
||||
upperLayout->addWidget(commentsEdit, 1, 1);
|
||||
|
||||
upperLayout->addWidget(bannerCardLabel, 2, 0);
|
||||
upperLayout->addWidget(bannerCardComboBox, 2, 1);
|
||||
upperLayout->addWidget(formatLabel, 2, 0);
|
||||
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(activeGroupCriteriaComboBox, 4, 1);
|
||||
upperLayout->addWidget(deckTagsDisplayWidget, 4, 1);
|
||||
|
||||
upperLayout->addWidget(activeGroupCriteriaLabel, 5, 0);
|
||||
upperLayout->addWidget(activeGroupCriteriaComboBox, 5, 1);
|
||||
|
||||
hashLabel1 = new QLabel();
|
||||
hashLabel1->setObjectName("hashLabel1");
|
||||
|
|
@ -263,6 +272,46 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
|||
|
||||
refreshShortcuts();
|
||||
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()
|
||||
|
|
@ -466,6 +515,12 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
|
|||
void DeckEditorDeckDockWidget::sortDeckModelToDeckView()
|
||||
{
|
||||
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()
|
||||
|
|
@ -724,6 +779,8 @@ void DeckEditorDeckDockWidget::retranslateUi()
|
|||
showTagsWidgetCheckBox->setText(tr("Show tags selection menu"));
|
||||
commentsLabel->setText(tr("&Comments:"));
|
||||
activeGroupCriteriaLabel->setText(tr("Group by:"));
|
||||
formatLabel->setText(tr("Format:"));
|
||||
|
||||
hashLabel1->setText(tr("Hash:"));
|
||||
|
||||
aIncrement->setText(tr("&Increment number"));
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ public slots:
|
|||
void actSwapCard();
|
||||
void actRemoveCard();
|
||||
void offsetCountAtIndex(const QModelIndex &idx, int offset);
|
||||
void initializeFormats();
|
||||
void expandAll();
|
||||
|
||||
signals:
|
||||
|
|
@ -100,6 +101,8 @@ private:
|
|||
LineEditUnfocusable *hashLabel;
|
||||
QLabel *activeGroupCriteriaLabel;
|
||||
QComboBox *activeGroupCriteriaComboBox;
|
||||
QLabel *formatLabel;
|
||||
QComboBox *formatComboBox;
|
||||
|
||||
QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ QVariant DeckListStyleProxy::data(const QModelIndex &index, int role) const
|
|||
|
||||
if (role == Qt::BackgroundRole) {
|
||||
if (isCard) {
|
||||
const bool legal =
|
||||
true; // TODO: Not implemented yet. QIdentityProxyModel::data(index, DeckRoles::IsLegalRole).toBool();
|
||||
const bool legal = QIdentityProxyModel::data(index, DeckRoles::IsLegalRole).toBool();
|
||||
int base = 255 - (index.row() % 2) * 30;
|
||||
return legal ? QBrush(QColor(base, base, base)) : QBrush(QColor(255, base / 3, base / 3));
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
|||
minDeckSizeLogicCombo->addItems({"Exact", "≥", "≤"}); // Exact = unset, ≥ = GTE, ≤ = LTE
|
||||
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);
|
||||
|
||||
// Page number
|
||||
|
|
@ -224,7 +224,7 @@ TabArchidekt::TabArchidekt(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
|
|||
pageSpin->setRange(1, 9999);
|
||||
pageSpin->setValue(1);
|
||||
|
||||
connect(pageSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
|
||||
connect(pageSpin, qOverload<int>(&QSpinBox::valueChanged), this, &TabArchidekt::doSearch);
|
||||
|
||||
// Page display
|
||||
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->setValue(150);
|
||||
layout->addWidget(spinBox);
|
||||
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||
&VisualDatabaseDisplayMainTypeFilterWidget::updateMainTypeButtonsVisibility);
|
||||
|
||||
// Create the toggle button for Exact Match/Includes mode
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ VisualDatabaseDisplayRecentSetFilterSettingsWidget::VisualDatabaseDisplayRecentS
|
|||
filterToMostRecentSetsAmount->setMaximum(100);
|
||||
filterToMostRecentSetsAmount->setValue(
|
||||
SettingsCache::instance().getVisualDatabaseDisplayFilterToMostRecentSetsAmount());
|
||||
connect(filterToMostRecentSetsAmount, QOverload<int>::of(&QSpinBox::valueChanged), &SettingsCache::instance(),
|
||||
connect(filterToMostRecentSetsAmount, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
|
||||
&SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsAmount);
|
||||
|
||||
layout->addWidget(filterToMostRecentSetsCheckBox);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ VisualDatabaseDisplaySubTypeFilterWidget::VisualDatabaseDisplaySubTypeFilterWidg
|
|||
spinBox->setMaximum(getMaxSubTypeCount());
|
||||
spinBox->setValue(150);
|
||||
layout->addWidget(spinBox);
|
||||
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||
&VisualDatabaseDisplaySubTypeFilterWidget::updateSubTypeButtonsVisibility);
|
||||
|
||||
// Create search box
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@ void VisualDatabaseDisplayWidget::initialize()
|
|||
saveLoadWidget = new VisualDatabaseDisplayFilterSaveLoadWidget(this, filterModel);
|
||||
nameFilterWidget = new VisualDatabaseDisplayNameFilterWidget(this, deckEditor, filterModel);
|
||||
mainTypeFilterWidget = new VisualDatabaseDisplayMainTypeFilterWidget(this, filterModel);
|
||||
formatLegalityWidget = new VisualDatabaseDisplayFormatLegalityFilterWidget(this, filterModel);
|
||||
subTypeFilterWidget = new VisualDatabaseDisplaySubTypeFilterWidget(this, filterModel);
|
||||
setFilterWidget = new VisualDatabaseDisplaySetFilterWidget(this, filterModel);
|
||||
|
||||
|
|
@ -223,6 +224,7 @@ void VisualDatabaseDisplayWidget::initialize()
|
|||
filterContainerLayout->addWidget(quickFilterSubTypeWidget);
|
||||
filterContainerLayout->addWidget(quickFilterSetWidget);
|
||||
filterContainerLayout->addWidget(mainTypeFilterWidget);
|
||||
filterContainerLayout->addWidget(formatLegalityWidget);
|
||||
|
||||
searchLayout->addWidget(colorFilterWidget);
|
||||
searchLayout->addWidget(clearFilterWidget);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "../utility/custom_line_edit.h"
|
||||
#include "visual_database_display_color_filter_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_name_filter_widget.h"
|
||||
#include "visual_database_display_set_filter_widget.h"
|
||||
|
|
@ -91,6 +92,7 @@ private:
|
|||
SettingsButtonWidget *quickFilterNameWidget;
|
||||
VisualDatabaseDisplayNameFilterWidget *nameFilterWidget;
|
||||
VisualDatabaseDisplayMainTypeFilterWidget *mainTypeFilterWidget;
|
||||
VisualDatabaseDisplayFormatLegalityFilterWidget *formatLegalityWidget;
|
||||
SettingsButtonWidget *quickFilterSubTypeWidget;
|
||||
VisualDatabaseDisplaySubTypeFilterWidget *subTypeFilterWidget;
|
||||
SettingsButtonWidget *quickFilterSetWidget;
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ VisualDeckEditorSampleHandWidget::VisualDeckEditorSampleHandWidget(QWidget *pare
|
|||
handSizeSpinBox = new QSpinBox(this);
|
||||
handSizeSpinBox->setValue(SettingsCache::instance().getVisualDeckEditorSampleHandSize());
|
||||
handSizeSpinBox->setMinimum(1);
|
||||
connect(handSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), &SettingsCache::instance(),
|
||||
connect(handSizeSpinBox, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
|
||||
&SettingsCache::setVisualDeckEditorSampleHandSize);
|
||||
connect(handSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
connect(handSizeSpinBox, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||
&VisualDeckEditorSampleHandWidget::updateDisplay);
|
||||
resetAndHandSizeLayout->addWidget(handSizeSpinBox);
|
||||
|
||||
|
|
|
|||
|
|
@ -61,10 +61,10 @@ VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidg
|
|||
unusedColorIdentitiesOpacitySpinBox->setMaximum(100);
|
||||
unusedColorIdentitiesOpacitySpinBox->setValue(
|
||||
SettingsCache::instance().getVisualDeckStorageUnusedColorIdentitiesOpacity());
|
||||
connect(unusedColorIdentitiesOpacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
connect(unusedColorIdentitiesOpacitySpinBox, qOverload<int>(&QSpinBox::valueChanged), this,
|
||||
&VisualDeckStorageQuickSettingsWidget::unusedColorIdentitiesOpacityChanged);
|
||||
connect(unusedColorIdentitiesOpacitySpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
|
||||
&SettingsCache::instance(), &SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity);
|
||||
connect(unusedColorIdentitiesOpacitySpinBox, qOverload<int>(&QSpinBox::valueChanged), &SettingsCache::instance(),
|
||||
&SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity);
|
||||
|
||||
unusedColorIdentitiesOpacityLabel->setBuddy(unusedColorIdentitiesOpacitySpinBox);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
#include <libcockatrice/card/database/parser/cockatrice_xml_4.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
|
||||
{
|
||||
public:
|
||||
|
|
@ -23,7 +27,73 @@ public:
|
|||
bool saveCardDatabase(const QString &fileName)
|
||||
{
|
||||
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/set/card_set.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(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef CARD_INFO_H
|
||||
#define CARD_INFO_H
|
||||
|
||||
#include "format/format_legality_rules.h"
|
||||
#include "printing/printing_info.h"
|
||||
|
||||
#include <QDate>
|
||||
|
|
@ -22,10 +23,12 @@ class ICardDatabaseParser;
|
|||
|
||||
typedef QSharedPointer<CardInfo> CardInfoPtr;
|
||||
typedef QSharedPointer<CardSet> CardSetPtr;
|
||||
typedef QSharedPointer<FormatRules> FormatRulesPtr;
|
||||
typedef QMap<QString, QList<PrintingInfo>> SetToPrintingsMap;
|
||||
|
||||
typedef QHash<QString, CardInfoPtr> CardNameMap;
|
||||
typedef QHash<QString, CardSetPtr> SetNameMap;
|
||||
typedef QHash<QString, FormatRulesPtr> FormatRulesNameMap;
|
||||
|
||||
Q_DECLARE_METATYPE(CardInfoPtr)
|
||||
|
||||
|
|
|
|||
|
|
@ -199,3 +199,8 @@ void CardDatabase::notifyEnabledSetsChanged()
|
|||
// inform the carddatabasemodels that they need to re-check their list of cards
|
||||
emit cardDatabaseEnabledSetsChanged();
|
||||
}
|
||||
|
||||
void CardDatabase::addFormat(FormatRulesPtr format)
|
||||
{
|
||||
formats.insert(format->formatName.toLower(), format);
|
||||
}
|
||||
|
|
@ -42,6 +42,8 @@ protected:
|
|||
/// Sets indexed by short name
|
||||
SetNameMap sets;
|
||||
|
||||
FormatRulesNameMap formats;
|
||||
|
||||
/// Loader responsible for file discovery and parsing
|
||||
CardDatabaseLoader *loader;
|
||||
|
||||
|
|
@ -141,6 +143,8 @@ public slots:
|
|||
*/
|
||||
void addSet(CardSetPtr set);
|
||||
|
||||
void addFormat(FormatRulesPtr format);
|
||||
|
||||
/** @brief Loads card databases from configured paths. */
|
||||
void loadCardDatabases();
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ CardDatabaseLoader::CardDatabaseLoader(QObject *parent,
|
|||
// connect parser outputs to the database adders
|
||||
connect(p, &ICardDatabaseParser::addCard, database, &CardDatabase::addCard, 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
|
||||
|
|
@ -149,6 +150,6 @@ bool CardDatabaseLoader::saveCustomTokensToFile()
|
|||
}
|
||||
}
|
||||
|
||||
availableParsers.first()->saveToFile(tmpSets, tmpCards, fileName);
|
||||
availableParsers.first()->saveToFile(FormatRulesNameMap(), tmpSets, tmpCards, fileName);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -341,4 +341,27 @@ QMap<QString, int> CardDatabaseQuerier::getAllSubCardTypesWithCount() const
|
|||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
[[nodiscard]] QMap<QString, int> getAllSubCardTypesWithCount() const;
|
||||
FormatRulesPtr getFormat(const QString &formatName) const;
|
||||
QMap<QString, int> getAllFormatsWithCount() const;
|
||||
|
||||
private:
|
||||
const CardDatabase *db; //!< Card database used for all lookups.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ public:
|
|||
|
||||
/**
|
||||
* @brief Saves card and set data to a file.
|
||||
* @param _formats
|
||||
* @param sets Map of sets to save.
|
||||
* @param cards Map of cards to save.
|
||||
* @param fileName Target file path.
|
||||
|
|
@ -45,7 +46,8 @@ public:
|
|||
* @param sourceVersion Optional version string of the source.
|
||||
* @return true if save succeeded.
|
||||
*/
|
||||
virtual bool saveToFile(SetNameMap sets,
|
||||
virtual bool saveToFile(FormatRulesNameMap _formats,
|
||||
SetNameMap sets,
|
||||
CardNameMap cards,
|
||||
const QString &fileName,
|
||||
const QString &sourceUrl = "unknown",
|
||||
|
|
@ -79,6 +81,8 @@ signals:
|
|||
|
||||
/** Emitted when a set is loaded from the database. */
|
||||
void addSet(CardSetPtr set);
|
||||
|
||||
void addFormat(FormatRulesPtr format);
|
||||
};
|
||||
|
||||
Q_DECLARE_INTERFACE(ICardDatabaseParser, "ICardDatabaseParser")
|
||||
|
|
|
|||
|
|
@ -438,12 +438,15 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
|
|||
return xml;
|
||||
}
|
||||
|
||||
bool CockatriceXml3Parser::saveToFile(SetNameMap _sets,
|
||||
bool CockatriceXml3Parser::saveToFile(FormatRulesNameMap _formats,
|
||||
SetNameMap _sets,
|
||||
CardNameMap cards,
|
||||
const QString &fileName,
|
||||
const QString &sourceUrl,
|
||||
const QString &sourceVersion)
|
||||
{
|
||||
Q_UNUSED(_formats);
|
||||
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ public:
|
|||
/**
|
||||
* @brief Save sets and cards back to an XML3 file.
|
||||
*/
|
||||
bool saveToFile(SetNameMap _sets,
|
||||
bool saveToFile(FormatRulesNameMap _formats,
|
||||
SetNameMap _sets,
|
||||
CardNameMap cards,
|
||||
const QString &fileName,
|
||||
const QString &sourceUrl = "unknown",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QXmlStreamReader>
|
||||
#include <libcockatrice/card/format/format_legality_rules.h>
|
||||
#include <version_string.h>
|
||||
|
||||
#define COCKATRICE_XML4_TAGNAME "cockatrice_carddatabase"
|
||||
|
|
@ -60,7 +61,9 @@ void CockatriceXml4Parser::parseFile(QIODevice &device)
|
|||
}
|
||||
|
||||
auto xmlName = xml.name().toString();
|
||||
if (xmlName == "sets") {
|
||||
if (xmlName == "formats") {
|
||||
loadFormats(xml);
|
||||
} else if (xmlName == "sets") {
|
||||
loadSetsFromXml(xml);
|
||||
} else if (xmlName == "cards") {
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (set.isNull()) {
|
||||
|
|
@ -399,7 +565,8 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in
|
|||
return xml;
|
||||
}
|
||||
|
||||
bool CockatriceXml4Parser::saveToFile(SetNameMap _sets,
|
||||
bool CockatriceXml4Parser::saveToFile(FormatRulesNameMap _formats,
|
||||
SetNameMap _sets,
|
||||
CardNameMap cards,
|
||||
const QString &fileName,
|
||||
const QString &sourceUrl,
|
||||
|
|
@ -426,6 +593,14 @@ bool CockatriceXml4Parser::saveToFile(SetNameMap _sets,
|
|||
xml.writeTextElement("sourceVersion", sourceVersion);
|
||||
xml.writeEndElement();
|
||||
|
||||
if (_formats.count() > 0) {
|
||||
xml.writeStartElement("formats");
|
||||
for (FormatRulesPtr format : _formats) {
|
||||
xml << format;
|
||||
}
|
||||
xml.writeEndElement();
|
||||
}
|
||||
|
||||
if (_sets.count() > 0) {
|
||||
xml.writeStartElement("sets");
|
||||
for (CardSetPtr set : _sets) {
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ public:
|
|||
/**
|
||||
* @brief Save sets and cards back to an XML4 file.
|
||||
*/
|
||||
bool saveToFile(SetNameMap _sets,
|
||||
bool saveToFile(FormatRulesNameMap _formats,
|
||||
SetNameMap _sets,
|
||||
CardNameMap cards,
|
||||
const QString &fileName,
|
||||
const QString &sourceUrl = "unknown",
|
||||
|
|
@ -72,6 +73,7 @@ private:
|
|||
*/
|
||||
void loadCardsFromXml(QXmlStreamReader &xml);
|
||||
|
||||
void loadFormats(QXmlStreamReader &xml);
|
||||
/**
|
||||
* @brief Load all <set> elements from the XML stream.
|
||||
* @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();
|
||||
} else if (childName == "deckname") {
|
||||
metadata.name = xml->readElementText();
|
||||
} else if (childName == "format") {
|
||||
metadata.gameFormat = xml->readElementText();
|
||||
} else if (childName == "comments") {
|
||||
metadata.comments = xml->readElementText();
|
||||
} else if (childName == "bannerCard") {
|
||||
|
|
@ -170,6 +172,7 @@ void writeMetadata(QXmlStreamWriter *xml, const DeckList::Metadata &metadata)
|
|||
{
|
||||
xml->writeTextElement("lastLoadedTimestamp", metadata.lastLoadedTimestamp);
|
||||
xml->writeTextElement("deckname", metadata.name);
|
||||
xml->writeTextElement("format", metadata.gameFormat);
|
||||
xml->writeStartElement("bannerCard");
|
||||
xml->writeAttribute("providerId", metadata.bannerCard.providerId);
|
||||
xml->writeCharacters(metadata.bannerCard.name);
|
||||
|
|
@ -594,15 +597,16 @@ DecklistCardNode *DeckList::addCard(const QString &cardName,
|
|||
const int position,
|
||||
const QString &cardSetName,
|
||||
const QString &cardSetCollectorNumber,
|
||||
const QString &cardProviderId)
|
||||
const QString &cardProviderId,
|
||||
const bool formatLegal)
|
||||
{
|
||||
auto *zoneNode = dynamic_cast<InnerDecklistNode *>(root->findChild(zoneName));
|
||||
if (zoneNode == nullptr) {
|
||||
zoneNode = new InnerDecklistNode(zoneName, root);
|
||||
}
|
||||
|
||||
auto *node =
|
||||
new DecklistCardNode(cardName, 1, zoneNode, position, cardSetName, cardSetCollectorNumber, cardProviderId);
|
||||
auto *node = new DecklistCardNode(cardName, 1, zoneNode, position, cardSetName, cardSetCollectorNumber,
|
||||
cardProviderId, formatLegal);
|
||||
refreshDeckHash();
|
||||
|
||||
return node;
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ public:
|
|||
{
|
||||
QString name; ///< User-defined deck name.
|
||||
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.
|
||||
QStringList tags; ///< User-defined tags for deck classification.
|
||||
QString lastLoadedTimestamp; ///< Timestamp string of last load.
|
||||
|
|
@ -181,6 +182,10 @@ public:
|
|||
{
|
||||
metadata.lastLoadedTimestamp = _lastLoadedTimestamp;
|
||||
}
|
||||
void setGameFormat(const QString &_gameFormat = QString())
|
||||
{
|
||||
metadata.gameFormat = _gameFormat;
|
||||
}
|
||||
///@}
|
||||
|
||||
/// @brief Construct an empty deck.
|
||||
|
|
@ -219,6 +224,10 @@ public:
|
|||
{
|
||||
return metadata.lastLoadedTimestamp;
|
||||
}
|
||||
QString getGameFormat() const
|
||||
{
|
||||
return metadata.gameFormat;
|
||||
}
|
||||
///@}
|
||||
|
||||
bool isBlankDeck() const
|
||||
|
|
@ -277,7 +286,8 @@ public:
|
|||
int position,
|
||||
const QString &cardSetName = QString(),
|
||||
const QString &cardSetCollectorNumber = QString(),
|
||||
const QString &cardProviderId = QString());
|
||||
const QString &cardProviderId = QString(),
|
||||
const bool formatLegal = true);
|
||||
bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr);
|
||||
///@}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,12 @@ public:
|
|||
/// @param _cardSetNumber Set the collector number.
|
||||
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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ class DecklistCardNode : public AbstractDecklistCardNode
|
|||
QString cardSetShortName; ///< Short set code (e.g., "NEO").
|
||||
QString cardSetNumber; ///< Collector number within the set.
|
||||
QString cardProviderId; ///< External provider identifier (e.g., UUID).
|
||||
bool formatLegal; ///< Format legality
|
||||
|
||||
public:
|
||||
/**
|
||||
|
|
@ -63,6 +64,7 @@ public:
|
|||
* @param _cardSetShortName Short set code (e.g., "NEO").
|
||||
* @param _cardSetNumber Collector number within the set.
|
||||
* @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
|
||||
* the parent’s children list automatically.
|
||||
|
|
@ -73,10 +75,11 @@ public:
|
|||
int position = -1,
|
||||
QString _cardSetShortName = QString(),
|
||||
QString _cardSetNumber = QString(),
|
||||
QString _cardProviderId = QString())
|
||||
QString _cardProviderId = QString(),
|
||||
bool _formatLegality = true)
|
||||
: AbstractDecklistCardNode(_parent, position), name(std::move(_name)), number(_number),
|
||||
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;
|
||||
}
|
||||
|
||||
/// @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.
|
||||
[[nodiscard]] bool isDeckHeader() const override
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@
|
|||
#include <libcockatrice/card/database/parser/cockatrice_xml_4.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,
|
||||
const QString &_text,
|
||||
const QVariantHash &_properties,
|
||||
|
|
@ -463,6 +467,71 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList
|
|||
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()
|
||||
{
|
||||
static ICardSetPriorityController *noOpController = new NoopCardSetPriorityController();
|
||||
|
|
@ -497,7 +566,8 @@ int OracleImporter::startImport()
|
|||
bool OracleImporter::saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion)
|
||||
{
|
||||
CockatriceXml4Parser parser(new NoopCardPreferenceProvider());
|
||||
return parser.saveToFile(sets, cards, fileName, sourceUrl, sourceVersion);
|
||||
|
||||
return parser.saveToFile(createDefaultMagicFormats(), sets, cards, fileName, sourceUrl, sourceVersion);
|
||||
}
|
||||
|
||||
void OracleImporter::clear()
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ public:
|
|||
int startImport();
|
||||
bool saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion);
|
||||
int importCardsFromSet(const CardSetPtr ¤tSet, const QList<QVariant> &cardsList);
|
||||
FormatRulesNameMap createDefaultMagicFormats();
|
||||
const CardNameMap &getCardList() const
|
||||
{
|
||||
return cards;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue