[DeckEditor] Use CommanderSpellbook.com to estimate bracket if format is 'commander' (#6415)

* [DeckEditor] Use CommanderSpellbook.com to estimate bracket if format is 'commander'

Took 2 minutes

Took 16 minutes

* Convert json data holder to structs, rename variables, extract widget

- Extract bracket estimation UI from DeckEditorDeckDockWidget into a
  new CommanderBracketWidget
- Move CommanderSpellbook integration settings from CardsDisplaySettings
  to DeckEditorSettings (matching the settings refactor on master)
- Rename CommanderSpellbook integration variables to drop the redundant
  'DeckEditor' prefix

Took 4 minutes

# Commit time for manual adjustment:
# Took 6 minutes

# Commit time for manual adjustment:
# Took 8 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-08-09 00:46:17 +02:00 committed by GitHub
parent e9eab328a4
commit 827e44a4d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 1642 additions and 8 deletions

View file

@ -0,0 +1,194 @@
#include "commander_bracket_settings.h"
#include <QSettings>
QVariantList CommanderBracketSettings::defaultDefinitions()
{
return {
QVariantMap{{"tag", "R"},
{"officialName", "[5] cEDH"},
{"displayName", "Ruthless"},
{"explanation",
"Top-tier competitive decks with maximum optimization, fast combos, and minimal variance."}},
QVariantMap{{"tag", "S"},
{"officialName", "[4] Optimized"},
{"displayName", "Spicy"},
{"explanation", "Highly tuned decks with strong synergy and occasional combo finishes."}},
QVariantMap{{"tag", "P"},
{"officialName", "[3] Upgraded"},
{"displayName", "Powerful"},
{"explanation", "Focused decks with clear win conditions and solid consistency."}},
QVariantMap{{"tag", "O"},
{"officialName", "[2] Core"},
{"displayName", "Oddball"},
{"explanation", "Unconventional or thematic decks with some structure but non-standard choices."}},
QVariantMap{{"tag", "C"},
{"officialName", "[2] Core"},
{"displayName", "Core"},
{"explanation", "Preconstructed or precon-level decks with straightforward strategies."}},
QVariantMap{{"tag", "E"},
{"officialName", "[1] Exhibition"},
{"displayName", "Exhibition"},
{"explanation", "Ultra-casual, theme-focused decks with minimal optimization."}},
QVariantMap{{"tag", "B"},
{"officialName", "Banned"},
{"displayName", "Banned"},
{"explanation", "The deck contains one or more cards banned in Commander."}},
};
}
CommanderBracketSettings::CommanderBracketSettings(const QString &settingPath, QObject *parent)
: SettingsManager(settingPath + "commander_brackets.ini", "commander_brackets", QString(), parent)
{
}
void CommanderBracketSettings::setSchemaVersion(int version)
{
setValue(version, "schemaVersion");
}
int CommanderBracketSettings::getSchemaVersion() const
{
QVariant value = getValue("schemaVersion");
return value.isValid() ? value.toInt() : 0;
}
void CommanderBracketSettings::clearDefinitions()
{
auto settings = getSettings();
settings.beginGroup("commander_brackets");
settings.remove("");
settings.endGroup();
settings.sync();
}
void CommanderBracketSettings::saveDefinitions(const QVariantList &definitions)
{
auto settings = getSettings();
settings.beginGroup("commander_brackets");
settings.remove("");
settings.setValue("schemaVersion", CurrentSchemaVersion);
for (const auto &entry : definitions) {
QVariantMap map = entry.toMap();
QString tag = map.value("tag").toString();
if (tag.isEmpty()) {
continue;
}
settings.beginGroup(tag);
settings.setValue("officialName", map.value("officialName"));
settings.setValue("displayName", map.value("displayName"));
settings.setValue("explanation", map.value("explanation"));
settings.endGroup();
}
settings.endGroup();
settings.sync();
}
QVariantList CommanderBracketSettings::loadDefinitions() const
{
QVariantList result;
auto settings = getSettings();
settings.beginGroup("commander_brackets");
int version = settings.value("schemaVersion", 0).toInt();
if (version != CurrentSchemaVersion) {
settings.endGroup();
return result;
}
QStringList groups = settings.childGroups();
for (const QString &tag : groups) {
settings.beginGroup(tag);
QVariantMap map;
map["tag"] = tag;
map["officialName"] = settings.value("officialName");
map["displayName"] = settings.value("displayName");
map["explanation"] = settings.value("explanation");
result.append(map);
settings.endGroup();
}
settings.endGroup();
return result;
}
void CommanderBracketSettings::reloadDefinitions(const QVariantList &definitionsList)
{
definitions.clear();
for (const auto &entry : definitionsList) {
const auto map = entry.toMap();
CommanderBracketDefinition definition;
definition.tag = map.value("tag").toString();
definition.officialName = map.value("officialName").toString();
definition.displayName = map.value("displayName").toString();
definition.explanation = map.value("explanation").toString();
if (!definition.tag.isEmpty()) {
definitions.insert(definition.tag, definition);
}
}
}
QString CommanderBracketSettings::officialName(const QString &tag) const
{
auto it = definitions.find(tag);
if (it == definitions.end()) {
return tag;
}
return it->officialName;
}
QString CommanderBracketSettings::displayName(const QString &tag) const
{
auto it = definitions.find(tag);
if (it == definitions.end()) {
return tag;
}
return it->displayName;
}
QString CommanderBracketSettings::explanation(const QString &tag) const
{
auto it = definitions.find(tag);
if (it == definitions.end()) {
return {};
}
return it->explanation;
}
bool CommanderBracketSettings::contains(const QString &tag) const
{
return definitions.contains(tag);
}

View file

@ -0,0 +1,56 @@
#ifndef COMMANDER_BRACKET_SETTINGS_H
#define COMMANDER_BRACKET_SETTINGS_H
#include "settings_manager.h"
#include <QHash>
#include <QObject>
#include <QString>
#include <QVariantList>
struct CommanderBracketDefinition
{
QString tag;
QString officialName;
QString displayName;
QString explanation;
};
class CommanderBracketSettings : public SettingsManager
{
Q_OBJECT
friend class SettingsCache;
public:
static constexpr int CurrentSchemaVersion = 1;
static QVariantList defaultDefinitions();
void clearDefinitions();
void saveDefinitions(const QVariantList &definitions);
QVariantList loadDefinitions() const;
void reloadDefinitions(const QVariantList &definitions);
QString officialName(const QString &tag) const;
QString displayName(const QString &tag) const;
QString explanation(const QString &tag) const;
bool contains(const QString &tag) const;
void setSchemaVersion(int version);
int getSchemaVersion() const;
private:
explicit CommanderBracketSettings(const QString &settingPath, QObject *parent = nullptr);
CommanderBracketSettings(const CommanderBracketSettings &) = delete;
CommanderBracketSettings &operator=(const CommanderBracketSettings &) = delete;
QHash<QString, CommanderBracketDefinition> definitions;
};
#endif // COMMANDER_BRACKET_SETTINGS_H

View file

@ -46,3 +46,27 @@ void DeckEditorSettings::setDefaultDeckEditorType(int _defaultDeckEditorType)
{
setValue(_defaultDeckEditorType, "defaultDeckEditorType");
}
int DeckEditorSettings::getCommanderSpellbookIntegrationEnabled() const
{
return getValue("commanderspellbookintegrationenabled", QString(), QString(),
commanderSpellbookIntegrationEnabledIndexUnprompted)
.toInt();
}
bool DeckEditorSettings::getCommanderSpellbookIntegrationUseOfficialBracketNames() const
{
return getValue("commanderspellbookintegrationuseofficialbracketnames", QString(), QString(), false).toBool();
}
void DeckEditorSettings::setCommanderSpellbookIntegrationEnabled(int _commanderSpellbookIntegrationEnabled)
{
setValue(_commanderSpellbookIntegrationEnabled, "commanderspellbookintegrationenabled");
emit commanderSpellbookIntegrationEnabledChanged(_commanderSpellbookIntegrationEnabled);
}
void DeckEditorSettings::setCommanderSpellbookIntegrationUseOfficialBracketNames(bool _useOfficialBracketNames)
{
setValue(_useOfficialBracketNames, "commanderspellbookintegrationuseofficialbracketnames");
emit commanderSpellbookIntegrationUseOfficialBracketNamesChanged(_useOfficialBracketNames);
}

View file

@ -5,6 +5,14 @@
#include <libcockatrice/interfaces/interface_deck_editor_settings_provider.h>
enum commanderSpellbookIntegrationEnabledIndex
{
commanderSpellbookIntegrationEnabledIndexDisabled,
commanderSpellbookIntegrationEnabledIndexEnabled,
commanderSpellbookIntegrationEnabledIndexAutomatic,
commanderSpellbookIntegrationEnabledIndexUnprompted,
};
class DeckEditorSettings : public SettingsManager, public IDeckEditorSettingsProvider
{
Q_OBJECT
@ -15,15 +23,21 @@ public:
[[nodiscard]] bool getBannerCardComboBoxVisible() const override;
[[nodiscard]] bool getTagsWidgetVisible() const override;
[[nodiscard]] int getDefaultDeckEditorType() const override;
[[nodiscard]] int getCommanderSpellbookIntegrationEnabled() const;
[[nodiscard]] bool getCommanderSpellbookIntegrationUseOfficialBracketNames() const;
void setOpenDeckInNewTab(bool _openDeckInNewTab);
void setBannerCardComboBoxVisible(bool _bannerCardComboBoxVisible);
void setTagsWidgetVisible(bool _tagsWidgetVisible);
void setDefaultDeckEditorType(int _defaultDeckEditorType);
void setCommanderSpellbookIntegrationEnabled(int _commanderSpellbookIntegrationEnabled);
void setCommanderSpellbookIntegrationUseOfficialBracketNames(bool _useOfficialBracketNames);
signals:
void bannerCardComboBoxVisibleChanged(bool visible);
void tagsWidgetVisibleChanged(bool visible);
void commanderSpellbookIntegrationEnabledChanged(int enabled);
void commanderSpellbookIntegrationUseOfficialBracketNamesChanged(bool useOfficialBracketNames);
public:
explicit DeckEditorSettings(const QString &settingPath, QObject *parent = nullptr);