mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-25 16:13:54 -07:00
Merge 32258bdfd7 into 80426d77bc
This commit is contained in:
commit
ba9623aa66
33 changed files with 1664 additions and 7 deletions
|
|
@ -298,6 +298,13 @@ set(cockatrice_SOURCES
|
||||||
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp
|
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_entry_display_widget.cpp
|
||||||
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp
|
src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_listings_display_widget.cpp
|
||||||
src/interface/widgets/tabs/api/archidekt/display/archidekt_deck_preview_image_display_widget.cpp
|
src/interface/widgets/tabs/api/archidekt/display/archidekt_deck_preview_image_display_widget.cpp
|
||||||
|
src/interface/widgets/tabs/api/commander_spellbook/api_response/card_in_deck_request.cpp
|
||||||
|
src/interface/widgets/tabs/api/commander_spellbook/api_response/commander_spellbook_deck_request.cpp
|
||||||
|
src/interface/widgets/tabs/api/commander_spellbook/api_response/commander_spellbook_card_result.cpp
|
||||||
|
src/interface/widgets/tabs/api/commander_spellbook/api_response/commander_spellbook_variant_result.cpp
|
||||||
|
src/interface/widgets/tabs/api/commander_spellbook/api_response/commander_spellbook_estimate_bracket_result.cpp
|
||||||
|
src/interface/widgets/tabs/api/commander_spellbook/commander_spellbook_bracket_explainer.cpp
|
||||||
|
src/interface/widgets/tabs/api/commander_spellbook/commander_spellbook_api_accessor.cpp
|
||||||
src/interface/widgets/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/archidekt_links/edhrec_api_response_archidekt_links.cpp
|
||||||
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_average_deck_api_response.cpp
|
||||||
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
|
src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp
|
||||||
|
|
@ -343,6 +350,9 @@ set(cockatrice_SOURCES
|
||||||
src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
|
src/interface/widgets/tabs/visual_deck_storage/tab_deck_storage_visual.cpp
|
||||||
src/interface/key_signals.cpp
|
src/interface/key_signals.cpp
|
||||||
src/interface/logger.cpp
|
src/interface/logger.cpp
|
||||||
|
src/interface/widgets/tabs/api/commander_spellbook/commander_bracket_service.cpp
|
||||||
|
src/interface/widgets/tabs/api/commander_spellbook/commander_bracket_definitions.cpp
|
||||||
|
src/interface/widgets/tabs/api/commander_spellbook/handle_commander_brackets.cpp
|
||||||
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp
|
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.cpp
|
||||||
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h
|
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_bracket_navigation_widget.h
|
||||||
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp
|
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.cpp
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "../../interface/card_picture_loader/card_picture_loader_cache_method.h"
|
#include "../../interface/card_picture_loader/card_picture_loader_cache_method.h"
|
||||||
#include "../../interface/card_picture_loader/card_picture_loader_local_schemes.h"
|
#include "../../interface/card_picture_loader/card_picture_loader_local_schemes.h"
|
||||||
|
#include "../../interface/widgets/dialogs/dlg_settings.h"
|
||||||
|
#include "../../interface/widgets/settings_page/user_interface_settings_page.h"
|
||||||
#include "../network/update/client/release_channel.h"
|
#include "../network/update/client/release_channel.h"
|
||||||
#include "card_counter_settings.h"
|
#include "card_counter_settings.h"
|
||||||
#include "version_string.h"
|
#include "version_string.h"
|
||||||
|
|
@ -191,6 +193,7 @@ SettingsCache::SettingsCache()
|
||||||
shortcutsSettings = new ShortcutsSettings(settingsPath, this);
|
shortcutsSettings = new ShortcutsSettings(settingsPath, this);
|
||||||
cardDatabaseSettings = new CardDatabaseSettings(settingsPath, this);
|
cardDatabaseSettings = new CardDatabaseSettings(settingsPath, this);
|
||||||
serversSettings = new ServersSettings(settingsPath, this);
|
serversSettings = new ServersSettings(settingsPath, this);
|
||||||
|
commanderBracketSettings = new CommanderBracketSettings(settingsPath, this);
|
||||||
messageSettings = new MessageSettings(settingsPath, this);
|
messageSettings = new MessageSettings(settingsPath, this);
|
||||||
gameFiltersSettings = new GameFiltersSettings(settingsPath, this);
|
gameFiltersSettings = new GameFiltersSettings(settingsPath, this);
|
||||||
layoutsSettings = new LayoutsSettings(settingsPath, this);
|
layoutsSettings = new LayoutsSettings(settingsPath, this);
|
||||||
|
|
@ -329,6 +332,22 @@ SettingsCache::SettingsCache()
|
||||||
deckEditorBannerCardComboBoxVisible =
|
deckEditorBannerCardComboBoxVisible =
|
||||||
settings->value("interface/deckeditorbannercardcomboboxvisible", true).toBool();
|
settings->value("interface/deckeditorbannercardcomboboxvisible", true).toBool();
|
||||||
deckEditorTagsWidgetVisible = settings->value("interface/deckeditortagswidgetvisible", true).toBool();
|
deckEditorTagsWidgetVisible = settings->value("interface/deckeditortagswidgetvisible", true).toBool();
|
||||||
|
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabled =
|
||||||
|
settings
|
||||||
|
->value("interface/deck_editor/commander_spellbook_integration/enabled",
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexUnprompted)
|
||||||
|
.toInt();
|
||||||
|
deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames =
|
||||||
|
settings->value("interface/deck_editor/commander_spellbook_integration/use_official_bracket_names", false)
|
||||||
|
.toBool();
|
||||||
|
|
||||||
|
auto definitions = commanderBracketSettings->loadDefinitions();
|
||||||
|
if (definitions.isEmpty()) {
|
||||||
|
definitions = CommanderBracketSettings::defaultDefinitions();
|
||||||
|
}
|
||||||
|
reloadBracketDefinitions(definitions);
|
||||||
|
|
||||||
visualDeckStorageCardSize = settings->value("interface/visualdeckstoragecardsize", 100).toInt();
|
visualDeckStorageCardSize = settings->value("interface/visualdeckstoragecardsize", 100).toInt();
|
||||||
visualDeckStorageSortingOrder = settings->value("interface/visualdeckstoragesortingorder", 0).toInt();
|
visualDeckStorageSortingOrder = settings->value("interface/visualdeckstoragesortingorder", 0).toInt();
|
||||||
visualDeckStorageShowFolders = settings->value("interface/visualdeckstorageshowfolders", true).toBool();
|
visualDeckStorageShowFolders = settings->value("interface/visualdeckstorageshowfolders", true).toBool();
|
||||||
|
|
@ -427,6 +446,22 @@ SettingsCache::SettingsCache()
|
||||||
clientVersion = settings->value("personal/clientversion", CLIENT_INFO_NOT_SET).toString();
|
clientVersion = settings->value("personal/clientversion", CLIENT_INFO_NOT_SET).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCache::reloadBracketDefinitions(const QVariantList &definitions)
|
||||||
|
{
|
||||||
|
bracketDefinitions.clear();
|
||||||
|
for (const auto &entry : definitions) {
|
||||||
|
const auto map = entry.toMap();
|
||||||
|
CommanderBracketDefinition def;
|
||||||
|
def.tag = map.value("tag").toString();
|
||||||
|
def.officialName = map.value("officialName").toString();
|
||||||
|
def.displayName = map.value("displayName").toString();
|
||||||
|
def.explanation = map.value("explanation").toString();
|
||||||
|
if (!def.tag.isEmpty()) {
|
||||||
|
bracketDefinitions.addDefinition(def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setUseTearOffMenus(bool _useTearOffMenus)
|
void SettingsCache::setUseTearOffMenus(bool _useTearOffMenus)
|
||||||
{
|
{
|
||||||
useTearOffMenus = _useTearOffMenus;
|
useTearOffMenus = _useTearOffMenus;
|
||||||
|
|
@ -834,6 +869,26 @@ void SettingsCache::setDeckEditorTagsWidgetVisible(QT_STATE_CHANGED_T _deckEdito
|
||||||
emit deckEditorTagsWidgetVisibleChanged(deckEditorTagsWidgetVisible);
|
emit deckEditorTagsWidgetVisibleChanged(deckEditorTagsWidgetVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setDeckEditorCommanderSpellbookIntegrationEnabled(
|
||||||
|
int _deckEditorCommanderSpellbookIntegrationEnabled)
|
||||||
|
{
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabled = _deckEditorCommanderSpellbookIntegrationEnabled;
|
||||||
|
settings->setValue("interface/deck_editor/commander_spellbook_integration/enabled",
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabled);
|
||||||
|
emit deckEditorCommanderSpellbookIntegrationEnabledChanged(deckEditorCommanderSpellbookIntegrationEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsCache::setDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames(
|
||||||
|
bool _deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames)
|
||||||
|
{
|
||||||
|
deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames =
|
||||||
|
_deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames;
|
||||||
|
settings->setValue("interface/deck_editor/commander_spellbook_integration/use_official_bracket_names",
|
||||||
|
deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames);
|
||||||
|
emit deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesChanged(
|
||||||
|
deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames);
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsCache::setVisualDeckStorageSortingOrder(int _visualDeckStorageSortingOrder)
|
void SettingsCache::setVisualDeckStorageSortingOrder(int _visualDeckStorageSortingOrder)
|
||||||
{
|
{
|
||||||
visualDeckStorageSortingOrder = _visualDeckStorageSortingOrder;
|
visualDeckStorageSortingOrder = _visualDeckStorageSortingOrder;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "../../interface/card_picture_loader/card_picture_loader_cache_method.h"
|
#include "../../interface/card_picture_loader/card_picture_loader_cache_method.h"
|
||||||
#include "../../interface/card_picture_loader/card_picture_loader_local_schemes.h"
|
#include "../../interface/card_picture_loader/card_picture_loader_local_schemes.h"
|
||||||
|
#include "../../interface/widgets/tabs/api/commander_spellbook/commander_bracket_definitions.h"
|
||||||
#include "shortcuts_settings.h"
|
#include "shortcuts_settings.h"
|
||||||
|
|
||||||
#include <QDate>
|
#include <QDate>
|
||||||
|
|
@ -19,6 +20,7 @@
|
||||||
#include <libcockatrice/interfaces/interface_network_settings_provider.h>
|
#include <libcockatrice/interfaces/interface_network_settings_provider.h>
|
||||||
#include <libcockatrice/settings/card_database_settings.h>
|
#include <libcockatrice/settings/card_database_settings.h>
|
||||||
#include <libcockatrice/settings/card_override_settings.h>
|
#include <libcockatrice/settings/card_override_settings.h>
|
||||||
|
#include <libcockatrice/settings/commander_bracket_settings.h>
|
||||||
#include <libcockatrice/settings/debug_settings.h>
|
#include <libcockatrice/settings/debug_settings.h>
|
||||||
#include <libcockatrice/settings/download_settings.h>
|
#include <libcockatrice/settings/download_settings.h>
|
||||||
#include <libcockatrice/settings/game_filters_settings.h>
|
#include <libcockatrice/settings/game_filters_settings.h>
|
||||||
|
|
@ -158,6 +160,8 @@ signals:
|
||||||
void printingSelectorNavigationButtonsVisibleChanged();
|
void printingSelectorNavigationButtonsVisibleChanged();
|
||||||
void deckEditorBannerCardComboBoxVisibleChanged(bool _visible);
|
void deckEditorBannerCardComboBoxVisibleChanged(bool _visible);
|
||||||
void deckEditorTagsWidgetVisibleChanged(bool _visible);
|
void deckEditorTagsWidgetVisibleChanged(bool _visible);
|
||||||
|
void deckEditorCommanderSpellbookIntegrationEnabledChanged(int _enabled);
|
||||||
|
void deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesChanged(bool _useOfficialBracketNames);
|
||||||
void visualDeckStorageShowTagFilterChanged(bool _visible);
|
void visualDeckStorageShowTagFilterChanged(bool _visible);
|
||||||
void visualDeckStorageDefaultTagsListChanged();
|
void visualDeckStorageDefaultTagsListChanged();
|
||||||
void visualDeckStorageShowColorIdentityChanged(bool _visible);
|
void visualDeckStorageShowColorIdentityChanged(bool _visible);
|
||||||
|
|
@ -202,6 +206,7 @@ private:
|
||||||
ShortcutsSettings *shortcutsSettings;
|
ShortcutsSettings *shortcutsSettings;
|
||||||
CardDatabaseSettings *cardDatabaseSettings;
|
CardDatabaseSettings *cardDatabaseSettings;
|
||||||
ServersSettings *serversSettings;
|
ServersSettings *serversSettings;
|
||||||
|
CommanderBracketSettings *commanderBracketSettings;
|
||||||
MessageSettings *messageSettings;
|
MessageSettings *messageSettings;
|
||||||
GameFiltersSettings *gameFiltersSettings;
|
GameFiltersSettings *gameFiltersSettings;
|
||||||
LayoutsSettings *layoutsSettings;
|
LayoutsSettings *layoutsSettings;
|
||||||
|
|
@ -211,6 +216,8 @@ private:
|
||||||
DebugSettings *debugSettings;
|
DebugSettings *debugSettings;
|
||||||
CardCounterSettings *cardCounterSettings;
|
CardCounterSettings *cardCounterSettings;
|
||||||
|
|
||||||
|
CommanderBracketDefinitions bracketDefinitions;
|
||||||
|
|
||||||
QString lang;
|
QString lang;
|
||||||
QString deckPath, filtersPath, replaysPath, picsPath, redirectCachePath, customPicsPath, cardDatabasePath,
|
QString deckPath, filtersPath, replaysPath, picsPath, redirectCachePath, customPicsPath, cardDatabasePath,
|
||||||
customCardDatabasePath, themesPath, spoilerDatabasePath, tokenDatabasePath, themeName, homeTabBackgroundSource;
|
customCardDatabasePath, themesPath, spoilerDatabasePath, tokenDatabasePath, themeName, homeTabBackgroundSource;
|
||||||
|
|
@ -253,6 +260,8 @@ private:
|
||||||
bool printingSelectorNavigationButtonsVisible;
|
bool printingSelectorNavigationButtonsVisible;
|
||||||
bool deckEditorBannerCardComboBoxVisible;
|
bool deckEditorBannerCardComboBoxVisible;
|
||||||
bool deckEditorTagsWidgetVisible;
|
bool deckEditorTagsWidgetVisible;
|
||||||
|
int deckEditorCommanderSpellbookIntegrationEnabled;
|
||||||
|
bool deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames;
|
||||||
int visualDeckStorageSortingOrder;
|
int visualDeckStorageSortingOrder;
|
||||||
bool visualDeckStorageShowFolders;
|
bool visualDeckStorageShowFolders;
|
||||||
bool visualDeckStorageShowColorIdentity;
|
bool visualDeckStorageShowColorIdentity;
|
||||||
|
|
@ -734,6 +743,14 @@ public:
|
||||||
{
|
{
|
||||||
return openDeckInNewTab;
|
return openDeckInNewTab;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] int getDeckEditorCommanderSpellbookIntegrationEnabled() const
|
||||||
|
{
|
||||||
|
return deckEditorCommanderSpellbookIntegrationEnabled;
|
||||||
|
}
|
||||||
|
[[nodiscard]] bool getDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames() const
|
||||||
|
{
|
||||||
|
return deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames;
|
||||||
|
}
|
||||||
[[nodiscard]] int getRewindBufferingMs() const
|
[[nodiscard]] int getRewindBufferingMs() const
|
||||||
{
|
{
|
||||||
return rewindBufferingMs;
|
return rewindBufferingMs;
|
||||||
|
|
@ -986,6 +1003,15 @@ public:
|
||||||
{
|
{
|
||||||
return *serversSettings;
|
return *serversSettings;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] CommanderBracketSettings &commanderBrackets() const
|
||||||
|
{
|
||||||
|
return *commanderBracketSettings;
|
||||||
|
}
|
||||||
|
void reloadBracketDefinitions(const QVariantList &definitions);
|
||||||
|
CommanderBracketDefinitions &commanderBracketDefs()
|
||||||
|
{
|
||||||
|
return bracketDefinitions;
|
||||||
|
}
|
||||||
[[nodiscard]] MessageSettings &messages() const
|
[[nodiscard]] MessageSettings &messages() const
|
||||||
{
|
{
|
||||||
return *messageSettings;
|
return *messageSettings;
|
||||||
|
|
@ -1082,6 +1108,9 @@ public slots:
|
||||||
void setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED_T _navigationButtonsVisible);
|
void setPrintingSelectorNavigationButtonsVisible(QT_STATE_CHANGED_T _navigationButtonsVisible);
|
||||||
void setDeckEditorBannerCardComboBoxVisible(QT_STATE_CHANGED_T _deckEditorBannerCardComboBoxVisible);
|
void setDeckEditorBannerCardComboBoxVisible(QT_STATE_CHANGED_T _deckEditorBannerCardComboBoxVisible);
|
||||||
void setDeckEditorTagsWidgetVisible(QT_STATE_CHANGED_T _deckEditorTagsWidgetVisible);
|
void setDeckEditorTagsWidgetVisible(QT_STATE_CHANGED_T _deckEditorTagsWidgetVisible);
|
||||||
|
void setDeckEditorCommanderSpellbookIntegrationEnabled(int _deckEditorCommanderSpellbookIntegrationEnabled);
|
||||||
|
void setDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames(
|
||||||
|
bool _deckEditorCommanderSpellbookIntegrationUseOfficialBracketNames);
|
||||||
void setVisualDeckStorageSortingOrder(int _visualDeckStorageSortingOrder);
|
void setVisualDeckStorageSortingOrder(int _visualDeckStorageSortingOrder);
|
||||||
void setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T value);
|
void setVisualDeckStorageShowFolders(QT_STATE_CHANGED_T value);
|
||||||
void setVisualDeckStorageShowTagFilter(QT_STATE_CHANGED_T _showTags);
|
void setVisualDeckStorageShowTagFilter(QT_STATE_CHANGED_T _showTags);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,20 @@
|
||||||
#include "deck_editor_deck_dock_widget.h"
|
#include "deck_editor_deck_dock_widget.h"
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
|
#include "../settings_page/user_interface_settings_page.h"
|
||||||
|
#include "../tabs/api/commander_spellbook/commander_bracket_service.h"
|
||||||
|
#include "../tabs/api/commander_spellbook/commander_spellbook_api_accessor.h"
|
||||||
|
#include "../tabs/api/commander_spellbook/commander_spellbook_bracket_explainer.h"
|
||||||
#include "deck_list_style_proxy.h"
|
#include "deck_list_style_proxy.h"
|
||||||
#include "deck_state_manager.h"
|
#include "deck_state_manager.h"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
|
#include <QFormLayout>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QMessageBox>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <libcockatrice/card/database/card_database_manager.h>
|
#include <libcockatrice/card/database/card_database_manager.h>
|
||||||
|
|
@ -131,6 +138,40 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
||||||
formatComboBox->addItem(tr("Loading Database..."));
|
formatComboBox->addItem(tr("Loading Database..."));
|
||||||
formatComboBox->setEnabled(false); // Disable until loaded
|
formatComboBox->setEnabled(false); // Disable until loaded
|
||||||
|
|
||||||
|
// --- Commander bracket row (hidden, unless format is 'commander') ---
|
||||||
|
bracketLabel = new QLabel(tr("Bracket:"), this);
|
||||||
|
|
||||||
|
bracketValueLabel = new QLabel(this);
|
||||||
|
bracketValueLabel->setText("-");
|
||||||
|
bracketValueLabel->setObjectName("bracketValueLabel");
|
||||||
|
|
||||||
|
bracketInfoButton = new QToolButton(this);
|
||||||
|
bracketInfoButton->setText("?");
|
||||||
|
bracketInfoButton->setAutoRaise(true);
|
||||||
|
bracketInfoButton->setEnabled(false);
|
||||||
|
|
||||||
|
bracketRefreshButton = new QToolButton(this);
|
||||||
|
bracketRefreshButton->setIcon(QPixmap("theme:icons/reload"));
|
||||||
|
bracketRefreshButton->setAutoRaise(true);
|
||||||
|
|
||||||
|
connect(bracketRefreshButton, &QToolButton::clicked, this, &DeckEditorDeckDockWidget::requestBracketEstimate);
|
||||||
|
if (SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationEnabled() !=
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexUnprompted) {
|
||||||
|
connect(&SettingsCache::instance(), &SettingsCache::deckEditorCommanderSpellbookIntegrationEnabledChanged, this,
|
||||||
|
&DeckEditorDeckDockWidget::maybeAutoEstimateBracket);
|
||||||
|
connect(&SettingsCache::instance(),
|
||||||
|
&SettingsCache::deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesChanged, this,
|
||||||
|
&DeckEditorDeckDockWidget::maybeAutoEstimateBracket);
|
||||||
|
}
|
||||||
|
|
||||||
|
bracketLabel->setVisible(false);
|
||||||
|
bracketValueLabel->setVisible(false);
|
||||||
|
bracketInfoButton->setVisible(false);
|
||||||
|
bracketRefreshButton->setVisible(false);
|
||||||
|
|
||||||
|
connect(&CommanderBracketService::instance(), &CommanderBracketService::estimateFinished, this,
|
||||||
|
&DeckEditorDeckDockWidget::onEstimateBracketFinished);
|
||||||
|
|
||||||
commentsLabel = new QLabel();
|
commentsLabel = new QLabel();
|
||||||
commentsLabel->setObjectName("commentsLabel");
|
commentsLabel->setObjectName("commentsLabel");
|
||||||
commentsEdit = new QTextEdit;
|
commentsEdit = new QTextEdit;
|
||||||
|
|
@ -216,13 +257,23 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
||||||
upperLayout->addWidget(formatLabel, 2, 0);
|
upperLayout->addWidget(formatLabel, 2, 0);
|
||||||
upperLayout->addWidget(formatComboBox, 2, 1);
|
upperLayout->addWidget(formatComboBox, 2, 1);
|
||||||
|
|
||||||
upperLayout->addWidget(bannerCardLabel, 3, 0);
|
upperLayout->addWidget(bracketLabel, 3, 0);
|
||||||
upperLayout->addWidget(bannerCardComboBox, 3, 1);
|
|
||||||
|
|
||||||
upperLayout->addWidget(deckTagsDisplayWidget, 4, 1);
|
auto *bracketRow = new QHBoxLayout;
|
||||||
|
bracketRow->addWidget(bracketValueLabel);
|
||||||
|
bracketRow->addWidget(bracketInfoButton);
|
||||||
|
bracketRow->addWidget(bracketRefreshButton);
|
||||||
|
bracketRow->addStretch();
|
||||||
|
|
||||||
upperLayout->addWidget(activeGroupCriteriaLabel, 5, 0);
|
upperLayout->addLayout(bracketRow, 3, 1);
|
||||||
upperLayout->addWidget(activeGroupCriteriaComboBox, 5, 1);
|
|
||||||
|
upperLayout->addWidget(bannerCardLabel, 4, 0);
|
||||||
|
upperLayout->addWidget(bannerCardComboBox, 4, 1);
|
||||||
|
|
||||||
|
upperLayout->addWidget(deckTagsDisplayWidget, 5, 1);
|
||||||
|
|
||||||
|
upperLayout->addWidget(activeGroupCriteriaLabel, 6, 0);
|
||||||
|
upperLayout->addWidget(activeGroupCriteriaComboBox, 6, 1);
|
||||||
|
|
||||||
hashLabel1 = new QLabel();
|
hashLabel1 = new QLabel();
|
||||||
hashLabel1->setObjectName("hashLabel1");
|
hashLabel1->setObjectName("hashLabel1");
|
||||||
|
|
@ -280,6 +331,147 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeckEditorDeckDockWidget::promptCommanderSpellbookIntegration()
|
||||||
|
{
|
||||||
|
QDialog dialog(this);
|
||||||
|
dialog.setWindowTitle(tr("CommanderSpellbook integration"));
|
||||||
|
|
||||||
|
auto *mainLayout = new QVBoxLayout(&dialog);
|
||||||
|
|
||||||
|
// Main text
|
||||||
|
auto *label = new QLabel(tr("CommanderSpellbook can analyze your deck and estimate its Commander bracket.\n\n"
|
||||||
|
"This sends your deck list to an external service.\n\n"
|
||||||
|
"CommanderSpellbook uses its own bracket naming system based on their own algorithm. "
|
||||||
|
"These names can be mapped to the official Commander brackets, but the mapping "
|
||||||
|
"is only an approximation."));
|
||||||
|
label->setWordWrap(true);
|
||||||
|
mainLayout->addWidget(label);
|
||||||
|
|
||||||
|
// Naming selector
|
||||||
|
auto *formLayout = new QFormLayout;
|
||||||
|
auto *namingCombo = new QComboBox(&dialog);
|
||||||
|
namingCombo->addItem(tr("CommanderSpellbook bracket names"));
|
||||||
|
namingCombo->addItem(tr("Official Commander bracket names (approximate)"));
|
||||||
|
namingCombo->setCurrentIndex(
|
||||||
|
SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames() ? 1 : 0);
|
||||||
|
|
||||||
|
// Create label + explainer button
|
||||||
|
auto *labelWidget = new QWidget(&dialog);
|
||||||
|
auto *labelLayout = new QHBoxLayout(labelWidget);
|
||||||
|
labelLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
auto *namingLabel = new QLabel(tr("Bracket naming:"), labelWidget);
|
||||||
|
auto *explainerButton = new QToolButton(labelWidget);
|
||||||
|
explainerButton->setText("?");
|
||||||
|
explainerButton->setAutoRaise(true);
|
||||||
|
explainerButton->setEnabled(false);
|
||||||
|
explainerButton->setToolTip(CommanderBracketNames::Explainer);
|
||||||
|
|
||||||
|
labelLayout->addWidget(namingLabel);
|
||||||
|
labelLayout->addWidget(explainerButton);
|
||||||
|
labelLayout->addStretch(); // push the button next to label, combo stays aligned
|
||||||
|
|
||||||
|
// Add row with the custom label widget
|
||||||
|
formLayout->addRow(labelWidget, namingCombo);
|
||||||
|
mainLayout->addLayout(formLayout);
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
auto *buttonBox = new QDialogButtonBox(&dialog);
|
||||||
|
auto *enableBtn = buttonBox->addButton(tr("Enable"), QDialogButtonBox::AcceptRole);
|
||||||
|
auto *automaticBtn = buttonBox->addButton(tr("Automatic"), QDialogButtonBox::ApplyRole);
|
||||||
|
auto *disableBtn = buttonBox->addButton(tr("Disable"), QDialogButtonBox::RejectRole);
|
||||||
|
mainLayout->addWidget(buttonBox);
|
||||||
|
|
||||||
|
// Track which button was clicked
|
||||||
|
QAbstractButton *clickedButton = nullptr;
|
||||||
|
QObject::connect(buttonBox, &QDialogButtonBox::clicked, &dialog, [&](QAbstractButton *btn) {
|
||||||
|
clickedButton = btn;
|
||||||
|
dialog.accept();
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.exec();
|
||||||
|
|
||||||
|
// Persist naming choice (if not disabled)
|
||||||
|
if (clickedButton != disableBtn) {
|
||||||
|
bool useOfficial = namingCombo->currentIndex() == 1;
|
||||||
|
SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames(useOfficial);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(&SettingsCache::instance(), &SettingsCache::deckEditorCommanderSpellbookIntegrationEnabledChanged, this,
|
||||||
|
&DeckEditorDeckDockWidget::maybeAutoEstimateBracket);
|
||||||
|
connect(&SettingsCache::instance(),
|
||||||
|
&SettingsCache::deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesChanged, this,
|
||||||
|
&DeckEditorDeckDockWidget::maybeAutoEstimateBracket);
|
||||||
|
|
||||||
|
// Persist integration mode
|
||||||
|
if (clickedButton == disableBtn) {
|
||||||
|
SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationEnabled(
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (clickedButton == enableBtn) {
|
||||||
|
SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationEnabled(
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexEnabled);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (clickedButton == automaticBtn) {
|
||||||
|
SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationEnabled(
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexAutomatic);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeckEditorDeckDockWidget::updateBracketVisibility(bool visible)
|
||||||
|
{
|
||||||
|
bracketLabel->setVisible(visible);
|
||||||
|
bracketValueLabel->setVisible(visible);
|
||||||
|
bracketInfoButton->setVisible(visible);
|
||||||
|
bracketRefreshButton->setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeckEditorDeckDockWidget::requestBracketEstimate()
|
||||||
|
{
|
||||||
|
bracketRefreshButton->setEnabled(false);
|
||||||
|
bracketInfoButton->setEnabled(false);
|
||||||
|
bracketValueLabel->setText(tr("Calculating…"));
|
||||||
|
|
||||||
|
requestId = CommanderBracketService::instance().estimateBracket(deckStateManager->getDeckList(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeckEditorDeckDockWidget::onEstimateBracketFinished(quint64 id,
|
||||||
|
QObject *requester,
|
||||||
|
const CommanderBracketEstimate &result)
|
||||||
|
{
|
||||||
|
if (requester != this || id != requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BracketExplainer explainer;
|
||||||
|
lastBracketExplanation = explainer.explain(result.rawResult);
|
||||||
|
|
||||||
|
// Display bracket
|
||||||
|
bracketValueLabel->setText(
|
||||||
|
SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames()
|
||||||
|
? result.officialName
|
||||||
|
: result.displayName);
|
||||||
|
bracketRefreshButton->setEnabled(true);
|
||||||
|
|
||||||
|
// Build tooltip
|
||||||
|
QString tooltip;
|
||||||
|
for (const auto §ion : lastBracketExplanation.sections) {
|
||||||
|
tooltip += "<b>" + section.title + "</b><br>";
|
||||||
|
for (const auto &line : section.bulletPoints) {
|
||||||
|
tooltip += "• " + line + "<br>";
|
||||||
|
}
|
||||||
|
tooltip += "<br>";
|
||||||
|
}
|
||||||
|
|
||||||
|
bracketInfoButton->setToolTip(tooltip);
|
||||||
|
bracketInfoButton->setEnabled(!tooltip.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
void DeckEditorDeckDockWidget::initializeFormats()
|
void DeckEditorDeckDockWidget::initializeFormats()
|
||||||
{
|
{
|
||||||
QStringList allFormats = CardDatabaseManager::query()->getAllFormatsWithCount().keys();
|
QStringList allFormats = CardDatabaseManager::query()->getAllFormatsWithCount().keys();
|
||||||
|
|
@ -300,15 +492,70 @@ void DeckEditorDeckDockWidget::initializeFormats()
|
||||||
// Ensure no selection is visible initially
|
// Ensure no selection is visible initially
|
||||||
formatComboBox->setCurrentIndex(-1);
|
formatComboBox->setCurrentIndex(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(formatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
|
connect(formatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
|
||||||
|
QString formatKey;
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
QString formatKey = formatComboBox->itemData(index).toString();
|
formatKey = formatComboBox->itemData(index).toString();
|
||||||
deckStateManager->setFormat(formatKey);
|
deckStateManager->setFormat(formatKey);
|
||||||
} else {
|
} else {
|
||||||
deckStateManager->setFormat(""); // clear format if deselected
|
deckStateManager->setFormat(""); // clear format if deselected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool isCommander = (formatKey.compare("commander", Qt::CaseInsensitive) == 0);
|
||||||
|
const bool commanderSpellbookIntegrationEnabled =
|
||||||
|
SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationEnabled() !=
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled;
|
||||||
|
|
||||||
|
const bool bracketVisible = isCommander && commanderSpellbookIntegrationEnabled;
|
||||||
|
|
||||||
|
updateBracketVisibility(bracketVisible);
|
||||||
|
|
||||||
|
if (!isCommander) {
|
||||||
|
bracketValueLabel->setText("-");
|
||||||
|
bracketInfoButton->setToolTip({});
|
||||||
|
bracketInfoButton->setEnabled(false);
|
||||||
|
bracketRefreshButton->setEnabled(false);
|
||||||
|
} else {
|
||||||
|
bracketRefreshButton->setEnabled(true);
|
||||||
|
maybeAutoEstimateBracket();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
maybeAutoEstimateBracket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeckEditorDeckDockWidget::maybeAutoEstimateBracket()
|
||||||
|
{
|
||||||
|
const QString formatKey = deckStateManager->getDeckList().getGameFormat();
|
||||||
|
|
||||||
|
const bool isCommander = (formatKey.compare("commander", Qt::CaseInsensitive) == 0);
|
||||||
|
|
||||||
|
int mode = SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationEnabled();
|
||||||
|
|
||||||
|
if (!isCommander || mode == deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled) {
|
||||||
|
updateBracketVisibility(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == deckEditorCommanderSpellbookIntegrationEnabledIndexUnprompted) {
|
||||||
|
if (!promptCommanderSpellbookIntegration()) {
|
||||||
|
updateBracketVisibility(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateBracketVisibility(true);
|
||||||
|
mode = SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationEnabled();
|
||||||
|
if (mode != deckEditorCommanderSpellbookIntegrationEnabledIndexAutomatic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid firing if we already have a result or a request in flight
|
||||||
|
if (!bracketRefreshButton->isEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer to avoid races during init / model rebuild
|
||||||
|
QTimer::singleShot(0, this, &DeckEditorDeckDockWidget::requestBracketEstimate);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExactCard DeckEditorDeckDockWidget::getCurrentCard()
|
ExactCard DeckEditorDeckDockWidget::getCurrentCard()
|
||||||
|
|
@ -743,6 +990,8 @@ void DeckEditorDeckDockWidget::retranslateUi()
|
||||||
commentsLabel->setText(tr("&Comments:"));
|
commentsLabel->setText(tr("&Comments:"));
|
||||||
activeGroupCriteriaLabel->setText(tr("Group by:"));
|
activeGroupCriteriaLabel->setText(tr("Group by:"));
|
||||||
formatLabel->setText(tr("Format:"));
|
formatLabel->setText(tr("Format:"));
|
||||||
|
bracketInfoButton->setToolTip(tr("Why this bracket?"));
|
||||||
|
bracketRefreshButton->setToolTip(tr("Recalculate bracket"));
|
||||||
|
|
||||||
hashLabel1->setText(tr("Hash:"));
|
hashLabel1->setText(tr("Hash:"));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
|
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
|
||||||
#include "../../key_signals.h"
|
#include "../../key_signals.h"
|
||||||
|
#include "../tabs/api/commander_spellbook/commander_spellbook_bracket_explainer.h"
|
||||||
#include "../utility/custom_line_edit.h"
|
#include "../utility/custom_line_edit.h"
|
||||||
#include "../visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
|
#include "../visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h"
|
||||||
#include "deck_list_history_manager_widget.h"
|
#include "deck_list_history_manager_widget.h"
|
||||||
|
|
@ -21,6 +22,7 @@
|
||||||
#include <QTreeView>
|
#include <QTreeView>
|
||||||
#include <libcockatrice/card/card_info.h>
|
#include <libcockatrice/card/card_info.h>
|
||||||
|
|
||||||
|
struct CommanderBracketEstimate;
|
||||||
class DeckListModel;
|
class DeckListModel;
|
||||||
class AbstractTabDeckEditor;
|
class AbstractTabDeckEditor;
|
||||||
class DeckEditorDeckDockWidget : public QDockWidget
|
class DeckEditorDeckDockWidget : public QDockWidget
|
||||||
|
|
@ -33,6 +35,9 @@ public:
|
||||||
QTreeView *deckView;
|
QTreeView *deckView;
|
||||||
QComboBox *bannerCardComboBox;
|
QComboBox *bannerCardComboBox;
|
||||||
void createDeckDock();
|
void createDeckDock();
|
||||||
|
bool promptCommanderSpellbookIntegration();
|
||||||
|
void updateBracketVisibility(bool visible);
|
||||||
|
void requestBracketEstimate();
|
||||||
ExactCard getCurrentCard();
|
ExactCard getCurrentCard();
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
|
||||||
|
|
@ -59,6 +64,8 @@ public slots:
|
||||||
void actSwapSelection();
|
void actSwapSelection();
|
||||||
void actRemoveCard();
|
void actRemoveCard();
|
||||||
void initializeFormats();
|
void initializeFormats();
|
||||||
|
void maybeAutoEstimateBracket();
|
||||||
|
void onEstimateBracketFinished(quint64 id, QObject *requester, const CommanderBracketEstimate &result);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void selectedCardChanged(const ExactCard &card);
|
void selectedCardChanged(const ExactCard &card);
|
||||||
|
|
@ -89,6 +96,15 @@ private:
|
||||||
|
|
||||||
QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard;
|
QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard;
|
||||||
|
|
||||||
|
QLabel *bracketLabel;
|
||||||
|
QLabel *bracketValueLabel;
|
||||||
|
QToolButton *bracketInfoButton;
|
||||||
|
QToolButton *bracketRefreshButton;
|
||||||
|
|
||||||
|
BracketExplanation lastBracketExplanation;
|
||||||
|
|
||||||
|
quint64 requestId;
|
||||||
|
|
||||||
DeckListModel *getModel() const;
|
DeckListModel *getModel() const;
|
||||||
[[nodiscard]] QModelIndexList getSelectedCardNodeSourceIndices() const;
|
[[nodiscard]] QModelIndexList getSelectedCardNodeSourceIndices() const;
|
||||||
void offsetCountAtIndex(const QModelIndex &idx, bool isIncrement);
|
void offsetCountAtIndex(const QModelIndex &idx, bool isIncrement);
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,56 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
|
||||||
connect(&defaultDeckEditorTypeSelector, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
connect(&defaultDeckEditorTypeSelector, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||||
&SettingsCache::instance(), &SettingsCache::setDefaultDeckEditorType);
|
&SettingsCache::instance(), &SettingsCache::setDefaultDeckEditorType);
|
||||||
|
|
||||||
|
deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setText("?");
|
||||||
|
deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setAutoRaise(true);
|
||||||
|
deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setEnabled(false);
|
||||||
|
|
||||||
|
// Add items with userData = internal enum
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledSelector.addItem(
|
||||||
|
tr("Disabled"), deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled);
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledSelector.addItem(
|
||||||
|
tr("Enabled"), deckEditorCommanderSpellbookIntegrationEnabledIndexEnabled);
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledSelector.addItem(
|
||||||
|
tr("Automatic"), deckEditorCommanderSpellbookIntegrationEnabledIndexAutomatic);
|
||||||
|
|
||||||
|
int storedMode = SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationEnabled();
|
||||||
|
for (int i = 0; i < deckEditorCommanderSpellbookIntegrationEnabledSelector.count(); ++i) {
|
||||||
|
if (deckEditorCommanderSpellbookIntegrationEnabledSelector.itemData(i).toInt() == storedMode) {
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledSelector.setCurrentIndex(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(&deckEditorCommanderSpellbookIntegrationEnabledSelector,
|
||||||
|
QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
|
||||||
|
int mode = deckEditorCommanderSpellbookIntegrationEnabledSelector.itemData(index).toInt();
|
||||||
|
SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationEnabled(mode);
|
||||||
|
updateCommanderSpellbookUiState();
|
||||||
|
});
|
||||||
|
|
||||||
|
deckEditorCommanderSpellbookIntegrationBracketNamingSelector.addItem(
|
||||||
|
tr("CommanderSpellbook bracket names")); // index 0 = false
|
||||||
|
deckEditorCommanderSpellbookIntegrationBracketNamingSelector.addItem(
|
||||||
|
tr("Official Commander bracket names (approximate)")); // index 1 = true
|
||||||
|
|
||||||
|
deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setCurrentIndex(
|
||||||
|
SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames() ? 1 : 0);
|
||||||
|
|
||||||
|
connect(&deckEditorCommanderSpellbookIntegrationBracketNamingSelector,
|
||||||
|
QOverload<int>::of(&QComboBox::currentIndexChanged), &SettingsCache::instance(), [](int index) {
|
||||||
|
SettingsCache::instance().setDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames(index == 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
updateCommanderSpellbookUiState();
|
||||||
|
|
||||||
|
auto *labelLayout = new QHBoxLayout;
|
||||||
|
labelLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
labelLayout->addWidget(&deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesLabel);
|
||||||
|
labelLayout->addWidget(&deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer);
|
||||||
|
|
||||||
|
auto *labelWidget = new QWidget;
|
||||||
|
labelWidget->setLayout(labelLayout);
|
||||||
|
|
||||||
auto *deckEditorGrid = new QGridLayout;
|
auto *deckEditorGrid = new QGridLayout;
|
||||||
deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0);
|
deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0);
|
||||||
deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0);
|
deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0);
|
||||||
|
|
@ -157,6 +207,10 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
|
||||||
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1);
|
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1);
|
||||||
deckEditorGrid->addWidget(&defaultDeckEditorTypeLabel, 4, 0);
|
deckEditorGrid->addWidget(&defaultDeckEditorTypeLabel, 4, 0);
|
||||||
deckEditorGrid->addWidget(&defaultDeckEditorTypeSelector, 4, 1);
|
deckEditorGrid->addWidget(&defaultDeckEditorTypeSelector, 4, 1);
|
||||||
|
deckEditorGrid->addWidget(&deckEditorCommanderSpellbookIntegrationEnabledLabel, 5, 0);
|
||||||
|
deckEditorGrid->addWidget(&deckEditorCommanderSpellbookIntegrationEnabledSelector, 5, 1);
|
||||||
|
deckEditorGrid->addWidget(labelWidget, 6, 0);
|
||||||
|
deckEditorGrid->addWidget(&deckEditorCommanderSpellbookIntegrationBracketNamingSelector);
|
||||||
|
|
||||||
deckEditorGroupBox = new QGroupBox;
|
deckEditorGroupBox = new QGroupBox;
|
||||||
deckEditorGroupBox->setLayout(deckEditorGrid);
|
deckEditorGroupBox->setLayout(deckEditorGrid);
|
||||||
|
|
@ -199,6 +253,27 @@ void UserInterfaceSettingsPage::setNotificationEnabled(QT_STATE_CHANGED_T i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UserInterfaceSettingsPage::updateCommanderSpellbookUiState()
|
||||||
|
{
|
||||||
|
const int mode = SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationEnabled();
|
||||||
|
|
||||||
|
const bool enabled = mode != deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled &&
|
||||||
|
mode != deckEditorCommanderSpellbookIntegrationEnabledIndexUnprompted;
|
||||||
|
|
||||||
|
deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setEnabled(enabled);
|
||||||
|
deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setEnabled(enabled);
|
||||||
|
deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesLabel.setVisible(enabled);
|
||||||
|
deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setVisible(enabled);
|
||||||
|
deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setVisible(enabled);
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
// Sync selector with the current stored bool
|
||||||
|
const bool useOfficial =
|
||||||
|
SettingsCache::instance().getDeckEditorCommanderSpellbookIntegrationUseOfficialBracketNames();
|
||||||
|
deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setCurrentIndex(useOfficial ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void UserInterfaceSettingsPage::retranslateUi()
|
void UserInterfaceSettingsPage::retranslateUi()
|
||||||
{
|
{
|
||||||
generalGroupBox->setTitle(tr("General interface settings"));
|
generalGroupBox->setTitle(tr("General interface settings"));
|
||||||
|
|
@ -236,6 +311,23 @@ void UserInterfaceSettingsPage::retranslateUi()
|
||||||
defaultDeckEditorTypeLabel.setText(tr("Default deck editor type"));
|
defaultDeckEditorTypeLabel.setText(tr("Default deck editor type"));
|
||||||
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::ClassicDeckEditor, tr("Classic Deck Editor"));
|
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::ClassicDeckEditor, tr("Classic Deck Editor"));
|
||||||
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::VisualDeckEditor, tr("Visual Deck Editor"));
|
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::VisualDeckEditor, tr("Visual Deck Editor"));
|
||||||
|
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledLabel.setText(
|
||||||
|
tr("CommanderSpellbook integration to estimate commander bracket"));
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledSelector.setItemText(
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled, tr("Disabled"));
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledSelector.setItemText(
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexEnabled, tr("Enabled"));
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledSelector.setItemText(
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexAutomatic, tr("Automatic"));
|
||||||
|
deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesLabel.setText(tr("Bracket naming"));
|
||||||
|
deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setItemText(
|
||||||
|
0, CommanderBracketNames::CommanderSpellbookBracketNames);
|
||||||
|
deckEditorCommanderSpellbookIntegrationBracketNamingSelector.setItemText(
|
||||||
|
1, CommanderBracketNames::OfficialCommanderBracketNames);
|
||||||
|
|
||||||
|
deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer.setToolTip(
|
||||||
|
CommanderBracketNames::Explainer);
|
||||||
replayGroupBox->setTitle(tr("Replay settings"));
|
replayGroupBox->setTitle(tr("Replay settings"));
|
||||||
rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:"));
|
rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:"));
|
||||||
rewindBufferingMsBox.setSuffix(" ms");
|
rewindBufferingMsBox.setSuffix(" ms");
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,38 @@
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
|
#include <QToolButton>
|
||||||
#include <libcockatrice/utility/macros.h>
|
#include <libcockatrice/utility/macros.h>
|
||||||
|
|
||||||
|
enum deckEditorCommanderSpellbookIntegrationEnabledIndex
|
||||||
|
{
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexDisabled,
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexEnabled,
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexAutomatic,
|
||||||
|
deckEditorCommanderSpellbookIntegrationEnabledIndexUnprompted,
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace CommanderBracketNames
|
||||||
|
{
|
||||||
|
inline const char *CommanderSpellbookBracketNames = QT_TR_NOOP("CommanderSpellbook");
|
||||||
|
inline const char *OfficialCommanderBracketNames = QT_TR_NOOP("Official (approximate)");
|
||||||
|
inline const char *Explainer = QT_TR_NOOP(
|
||||||
|
"The bracket system combines both objective data, as well as subjective play experience to estimate a "
|
||||||
|
"bracket for a deck.\nCommanderSpellbook's estimation is algorithmical, which means that it can only operate "
|
||||||
|
"on the objective data, not the subjective intent. \nThey have chosen to represent this by defining their "
|
||||||
|
"own bracket system which matches their algorithm.\n"
|
||||||
|
"This custom bracket system maps loosely to the standard system. \nYou may choose to use these mapped "
|
||||||
|
"standardized names if these are more familiar to you, however, you should keep in mind that these are just "
|
||||||
|
"rough estimations.\n\nAlways consider the subjective factors of the bracket system when determing a deck's "
|
||||||
|
"final bracket!");
|
||||||
|
} // namespace CommanderBracketNames
|
||||||
|
|
||||||
class UserInterfaceSettingsPage : public AbstractSettingsPage
|
class UserInterfaceSettingsPage : public AbstractSettingsPage
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private slots:
|
private slots:
|
||||||
void setNotificationEnabled(QT_STATE_CHANGED_T);
|
void setNotificationEnabled(QT_STATE_CHANGED_T);
|
||||||
|
void updateCommanderSpellbookUiState();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QCheckBox notificationsEnabledCheckBox;
|
QCheckBox notificationsEnabledCheckBox;
|
||||||
|
|
@ -39,6 +64,11 @@ private:
|
||||||
QCheckBox visualDeckStorageSelectionAnimationCheckBox;
|
QCheckBox visualDeckStorageSelectionAnimationCheckBox;
|
||||||
QLabel defaultDeckEditorTypeLabel;
|
QLabel defaultDeckEditorTypeLabel;
|
||||||
QComboBox defaultDeckEditorTypeSelector;
|
QComboBox defaultDeckEditorTypeSelector;
|
||||||
|
QLabel deckEditorCommanderSpellbookIntegrationEnabledLabel;
|
||||||
|
QComboBox deckEditorCommanderSpellbookIntegrationEnabledSelector;
|
||||||
|
QLabel deckEditorCommanderSpellbookIntegrationUseOfficialBracketNamesLabel;
|
||||||
|
QToolButton deckEditorCommanderSpellBookIntegrationUseOfficialBracketNamesExplainer;
|
||||||
|
QComboBox deckEditorCommanderSpellbookIntegrationBracketNamingSelector;
|
||||||
QLabel rewindBufferingMsLabel;
|
QLabel rewindBufferingMsLabel;
|
||||||
QSpinBox rewindBufferingMsBox;
|
QSpinBox rewindBufferingMsBox;
|
||||||
QGroupBox *generalGroupBox;
|
QGroupBox *generalGroupBox;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include "card_in_deck_request.h"
|
||||||
|
|
||||||
|
void CardInDeckRequest::fromJson(const QJsonObject &json)
|
||||||
|
{
|
||||||
|
card = json.value("card").toString();
|
||||||
|
quantity = json.value("quantity").toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject CardInDeckRequest::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject json;
|
||||||
|
json.insert("card", card);
|
||||||
|
json.insert("quantity", quantity);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardInDeckRequest::debugPrint() const
|
||||||
|
{
|
||||||
|
qDebug() << "Card:" << card;
|
||||||
|
qDebug() << "Quantity:" << quantity;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef COCKATRICE_CARD_IN_DECK_REQUEST_H
|
||||||
|
#define COCKATRICE_CARD_IN_DECK_REQUEST_H
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
class CardInDeckRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Constructor
|
||||||
|
CardInDeckRequest() = default;
|
||||||
|
|
||||||
|
// Parse deck-related data from JSON
|
||||||
|
void fromJson(const QJsonObject &json);
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
|
||||||
|
// Debug method for logging
|
||||||
|
void debugPrint() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString card;
|
||||||
|
int quantity;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_CARD_IN_DECK_REQUEST_H
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include "commander_spellbook_card_result.h"
|
||||||
|
|
||||||
|
void CommanderSpellbookCardResult::fromJson(const QJsonObject &json)
|
||||||
|
{
|
||||||
|
id = json.value("id").toString();
|
||||||
|
name = json.value("name").toString();
|
||||||
|
oracleId = json.value("oracleId").toString();
|
||||||
|
spoiler = json.value("spoiler").toBool();
|
||||||
|
typeLine = json.value("typeLine").toString();
|
||||||
|
|
||||||
|
imageUriFrontPng = json.value("imageUriFrontPng").toString();
|
||||||
|
imageUriFrontLarge = json.value("imageUriFrontLarge").toString();
|
||||||
|
imageUriFrontNormal = json.value("imageUriFrontNormal").toString();
|
||||||
|
imageUriFrontSmall = json.value("imageUriFrontSmall").toString();
|
||||||
|
imageUriFrontArtCrop = json.value("imageUriFrontArtCrop").toString();
|
||||||
|
|
||||||
|
imageUriBackPng = json.value("imageUriBackPng").toString();
|
||||||
|
imageUriBackLarge = json.value("imageUriBackLarge").toString();
|
||||||
|
imageUriBackNormal = json.value("imageUriBackNormal").toString();
|
||||||
|
imageUriBackSmall = json.value("imageUriBackSmall").toString();
|
||||||
|
imageUriBackArtCrop = json.value("imageUriBackArtCrop").toString();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef COCKATRICE_COMMANDER_SPELLBOOK_CARD_RESULT_H
|
||||||
|
#define COCKATRICE_COMMANDER_SPELLBOOK_CARD_RESULT_H
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class CommanderSpellbookCardResult
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void fromJson(const QJsonObject &json);
|
||||||
|
|
||||||
|
QString id;
|
||||||
|
QString name;
|
||||||
|
QString oracleId;
|
||||||
|
bool spoiler = false;
|
||||||
|
QString typeLine;
|
||||||
|
|
||||||
|
QString imageUriFrontPng;
|
||||||
|
QString imageUriFrontLarge;
|
||||||
|
QString imageUriFrontNormal;
|
||||||
|
QString imageUriFrontSmall;
|
||||||
|
QString imageUriFrontArtCrop;
|
||||||
|
|
||||||
|
QString imageUriBackPng;
|
||||||
|
QString imageUriBackLarge;
|
||||||
|
QString imageUriBackNormal;
|
||||||
|
QString imageUriBackSmall;
|
||||||
|
QString imageUriBackArtCrop;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_COMMANDER_SPELLBOOK_CARD_RESULT_H
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
#include "commander_spellbook_deck_request.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <libcockatrice/deck_list/deck_list.h>
|
||||||
|
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
|
||||||
|
|
||||||
|
void CommanderSpellbookDeckRequest::fromJson(const QJsonObject &json)
|
||||||
|
{
|
||||||
|
mainDeck.clear();
|
||||||
|
commanderDeck.clear();
|
||||||
|
|
||||||
|
// Main deck
|
||||||
|
const QJsonArray mainArray = json.value("main").toArray();
|
||||||
|
for (const QJsonValue &value : mainArray) {
|
||||||
|
if (!value.isObject()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInDeckRequest card;
|
||||||
|
card.fromJson(value.toObject());
|
||||||
|
mainDeck.append(card);
|
||||||
|
|
||||||
|
// Max size allowed by commanderspellbook
|
||||||
|
if (mainDeck.size() >= 600) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commanders
|
||||||
|
const QJsonArray commanderArray = json.value("commanders").toArray();
|
||||||
|
for (const QJsonValue &value : commanderArray) {
|
||||||
|
if (!value.isObject()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInDeckRequest card;
|
||||||
|
card.fromJson(value.toObject());
|
||||||
|
commanderDeck.append(card);
|
||||||
|
|
||||||
|
// Max size allowed by commanderspellbook
|
||||||
|
if (commanderDeck.size() >= 12) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject CommanderSpellbookDeckRequest::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject json;
|
||||||
|
|
||||||
|
QJsonArray mainArray;
|
||||||
|
for (const CardInDeckRequest &card : mainDeck) {
|
||||||
|
mainArray.append(card.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray commanderArray;
|
||||||
|
for (const CardInDeckRequest &card : commanderDeck) {
|
||||||
|
commanderArray.append(card.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
json.insert("main", mainArray);
|
||||||
|
json.insert("commanders", commanderArray);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommanderSpellbookDeckRequest::fromDeckList(const DeckList &deck)
|
||||||
|
{
|
||||||
|
mainDeck.clear();
|
||||||
|
commanderDeck.clear();
|
||||||
|
|
||||||
|
// --- Mainboard ---
|
||||||
|
const auto mainCards = deck.getCardNodes({DECK_ZONE_MAIN});
|
||||||
|
for (const DecklistCardNode *node : mainCards) {
|
||||||
|
if (!node) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInDeckRequest req;
|
||||||
|
QJsonObject json;
|
||||||
|
json.insert("card", node->getName());
|
||||||
|
json.insert("quantity", node->getNumber());
|
||||||
|
req.fromJson(json);
|
||||||
|
|
||||||
|
mainDeck.append(req);
|
||||||
|
|
||||||
|
// Max size allowed by commanderspellbook
|
||||||
|
if (mainDeck.size() >= 600) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Commander (bannerCard) ---
|
||||||
|
const auto &metadata = deck.getMetadata();
|
||||||
|
if (!metadata.bannerCard.name.isEmpty()) {
|
||||||
|
CardInDeckRequest commander;
|
||||||
|
QJsonObject json;
|
||||||
|
json.insert("card", metadata.bannerCard.name);
|
||||||
|
json.insert("quantity", 1);
|
||||||
|
commander.fromJson(json);
|
||||||
|
|
||||||
|
commanderDeck.append(commander);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommanderSpellbookDeckRequest::debugPrint() const
|
||||||
|
{
|
||||||
|
qDebug() << "Main deck:";
|
||||||
|
for (const CardInDeckRequest &card : mainDeck) {
|
||||||
|
card.debugPrint();
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Commanders:";
|
||||||
|
for (const CardInDeckRequest &card : commanderDeck) {
|
||||||
|
card.debugPrint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef COCKATRICE_COMMANDER_SPELLBOOK_DECK_REQUEST_H
|
||||||
|
#define COCKATRICE_COMMANDER_SPELLBOOK_DECK_REQUEST_H
|
||||||
|
#include "card_in_deck_request.h"
|
||||||
|
#include "libcockatrice/deck_list/deck_list.h"
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
class CommanderSpellbookDeckRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CommanderSpellbookDeckRequest() = default;
|
||||||
|
|
||||||
|
void fromJson(const QJsonObject &json);
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
void fromDeckList(const DeckList &deck);
|
||||||
|
|
||||||
|
void debugPrint() const;
|
||||||
|
|
||||||
|
const QVector<CardInDeckRequest> &main() const
|
||||||
|
{
|
||||||
|
return mainDeck;
|
||||||
|
}
|
||||||
|
const QVector<CardInDeckRequest> &commanders() const
|
||||||
|
{
|
||||||
|
return commanderDeck;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<CardInDeckRequest> mainDeck; // maxItems: 600
|
||||||
|
QVector<CardInDeckRequest> commanderDeck; // maxItems: 12
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_COMMANDER_SPELLBOOK_DECK_REQUEST_H
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
#include "commander_spellbook_estimate_bracket_result.h"
|
||||||
|
|
||||||
|
void EstimateBracketResult::fromJson(const QJsonObject &json)
|
||||||
|
{
|
||||||
|
bracketTag = json.value("bracketTag").toString();
|
||||||
|
|
||||||
|
gameChangerCards.clear();
|
||||||
|
massLandDenialCards.clear();
|
||||||
|
extraTurnCards.clear();
|
||||||
|
|
||||||
|
massLandDenialTemplates.clear();
|
||||||
|
extraTurnTemplates.clear();
|
||||||
|
|
||||||
|
massLandDenialCombos.clear();
|
||||||
|
extraTurnCombos.clear();
|
||||||
|
lockCombos.clear();
|
||||||
|
skipTurnsCombos.clear();
|
||||||
|
|
||||||
|
definitelyTwoCardCombos.clear();
|
||||||
|
arguablyTwoCardCombos.clear();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Cards
|
||||||
|
//
|
||||||
|
for (const auto &value : json.value("cards").toArray()) {
|
||||||
|
if (!value.isObject()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject obj = value.toObject();
|
||||||
|
|
||||||
|
CommanderSpellbookCardResult card;
|
||||||
|
card.fromJson(obj.value("card").toObject());
|
||||||
|
|
||||||
|
if (obj.value("gameChanger").toBool()) {
|
||||||
|
gameChangerCards.append(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.value("massLandDenial").toBool()) {
|
||||||
|
massLandDenialCards.append(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.value("extraTurn").toBool()) {
|
||||||
|
extraTurnCards.append(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Templates
|
||||||
|
//
|
||||||
|
for (const auto &value : json.value("templates").toArray()) {
|
||||||
|
if (!value.isObject()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject obj = value.toObject();
|
||||||
|
|
||||||
|
CommanderSpellbookVariantResult variant;
|
||||||
|
variant.fromJson(obj);
|
||||||
|
|
||||||
|
if (obj.value("massLandDenial").toBool()) {
|
||||||
|
massLandDenialTemplates.append(variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.value("extraTurn").toBool()) {
|
||||||
|
extraTurnTemplates.append(variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Combos
|
||||||
|
//
|
||||||
|
for (const auto &value : json.value("combos").toArray()) {
|
||||||
|
if (!value.isObject()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject obj = value.toObject();
|
||||||
|
|
||||||
|
CommanderSpellbookVariantResult combo;
|
||||||
|
combo.fromJson(obj);
|
||||||
|
|
||||||
|
if (obj.value("massLandDenial").toBool()) {
|
||||||
|
massLandDenialCombos.append(combo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.value("extraTurn").toBool()) {
|
||||||
|
extraTurnCombos.append(combo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.value("lock").toBool()) {
|
||||||
|
lockCombos.append(combo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.value("skipTurns").toBool()) {
|
||||||
|
skipTurnsCombos.append(combo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.value("definitelyTwoCard").toBool()) {
|
||||||
|
definitelyTwoCardCombos.append(combo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.value("arguablyTwoCard").toBool()) {
|
||||||
|
arguablyTwoCardCombos.append(combo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef COCKATRICE_COMMANDER_SPELLBOOK_ESTIMATE_BRACKET_RESULT_H
|
||||||
|
#define COCKATRICE_COMMANDER_SPELLBOOK_ESTIMATE_BRACKET_RESULT_H
|
||||||
|
|
||||||
|
#include "commander_spellbook_card_result.h"
|
||||||
|
#include "commander_spellbook_variant_result.h"
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
class EstimateBracketResult
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void fromJson(const QJsonObject &json);
|
||||||
|
|
||||||
|
QString bracketTag;
|
||||||
|
|
||||||
|
QVector<CommanderSpellbookCardResult> gameChangerCards;
|
||||||
|
QVector<CommanderSpellbookCardResult> massLandDenialCards;
|
||||||
|
QVector<CommanderSpellbookCardResult> extraTurnCards;
|
||||||
|
|
||||||
|
QVector<CommanderSpellbookVariantResult> massLandDenialTemplates;
|
||||||
|
QVector<CommanderSpellbookVariantResult> extraTurnTemplates;
|
||||||
|
|
||||||
|
QVector<CommanderSpellbookVariantResult> massLandDenialCombos;
|
||||||
|
QVector<CommanderSpellbookVariantResult> extraTurnCombos;
|
||||||
|
QVector<CommanderSpellbookVariantResult> lockCombos;
|
||||||
|
QVector<CommanderSpellbookVariantResult> skipTurnsCombos;
|
||||||
|
|
||||||
|
QVector<CommanderSpellbookVariantResult> definitelyTwoCardCombos;
|
||||||
|
QVector<CommanderSpellbookVariantResult> arguablyTwoCardCombos;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
#include "commander_spellbook_variant_result.h"
|
||||||
|
|
||||||
|
void CommanderSpellbookVariantResult::fromJson(const QJsonObject &json)
|
||||||
|
{
|
||||||
|
id = json.value("id").toString();
|
||||||
|
status = json.value("status").toString();
|
||||||
|
|
||||||
|
uses = json.value("uses").toArray();
|
||||||
|
cardRequires = json.value("requires").toArray();
|
||||||
|
produces = json.value("produces").toArray();
|
||||||
|
of = json.value("of").toArray();
|
||||||
|
includes = json.value("includes").toArray();
|
||||||
|
|
||||||
|
manaNeeded = json.value("manaNeeded").toArray();
|
||||||
|
manaValueNeeded = json.value("manaValueNeeded").toArray();
|
||||||
|
|
||||||
|
easyPrerequisites = json.value("easyPrerequisites").toArray();
|
||||||
|
notablePrerequisites = json.value("notablePrerequisites").toArray();
|
||||||
|
|
||||||
|
description = json.value("description").toString();
|
||||||
|
notes = json.value("notes").toString();
|
||||||
|
popularity = json.value("popularity").toDouble();
|
||||||
|
|
||||||
|
spoiler = json.value("spoiler").toBool();
|
||||||
|
bracketTag = json.value("bracketTag").toString();
|
||||||
|
|
||||||
|
legalities = json.value("legalities").toObject();
|
||||||
|
prices = json.value("prices").toObject();
|
||||||
|
|
||||||
|
variantCount = json.value("variantCount").toInt();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef COCKATRICE_COMMANDER_SPELLBOOK_VARIANT_RESULT_H
|
||||||
|
#define COCKATRICE_COMMANDER_SPELLBOOK_VARIANT_RESULT_H
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
class CommanderSpellbookVariantResult
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void fromJson(const QJsonObject &json);
|
||||||
|
|
||||||
|
QString id;
|
||||||
|
QString status;
|
||||||
|
|
||||||
|
QJsonArray uses;
|
||||||
|
QJsonArray cardRequires;
|
||||||
|
QJsonArray produces;
|
||||||
|
QJsonArray of;
|
||||||
|
QJsonArray includes;
|
||||||
|
|
||||||
|
QJsonArray manaNeeded;
|
||||||
|
QJsonArray manaValueNeeded;
|
||||||
|
|
||||||
|
QJsonArray easyPrerequisites;
|
||||||
|
QJsonArray notablePrerequisites;
|
||||||
|
|
||||||
|
QString description;
|
||||||
|
QString notes;
|
||||||
|
double popularity = 0.0;
|
||||||
|
|
||||||
|
bool spoiler = false;
|
||||||
|
QString bracketTag;
|
||||||
|
|
||||||
|
QJsonObject legalities;
|
||||||
|
QJsonObject prices;
|
||||||
|
|
||||||
|
int variantCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_COMMANDER_SPELLBOOK_VARIANT_RESULT_H
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
#include "commander_bracket_definitions.h"
|
||||||
|
|
||||||
|
void CommanderBracketDefinitions::clear()
|
||||||
|
{
|
||||||
|
definitions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommanderBracketDefinitions::addDefinition(const CommanderBracketDefinition &definition)
|
||||||
|
{
|
||||||
|
definitions.insert(definition.tag, definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommanderBracketDefinitions::officialName(const QString &tag) const
|
||||||
|
{
|
||||||
|
auto it = definitions.find(tag);
|
||||||
|
|
||||||
|
if (it == definitions.end()) {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->officialName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommanderBracketDefinitions::displayName(const QString &tag) const
|
||||||
|
{
|
||||||
|
auto it = definitions.find(tag);
|
||||||
|
|
||||||
|
if (it == definitions.end()) {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommanderBracketDefinitions::explanation(const QString &tag) const
|
||||||
|
{
|
||||||
|
auto it = definitions.find(tag);
|
||||||
|
|
||||||
|
if (it == definitions.end()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->explanation;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CommanderBracketDefinitions::contains(const QString &tag) const
|
||||||
|
{
|
||||||
|
return definitions.contains(tag);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef COCKATRICE_COMMANDER_BRACKET_DEFINITIONS_H
|
||||||
|
#define COCKATRICE_COMMANDER_BRACKET_DEFINITIONS_H
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
struct CommanderBracketDefinition
|
||||||
|
{
|
||||||
|
QString tag;
|
||||||
|
|
||||||
|
QString officialName;
|
||||||
|
QString displayName;
|
||||||
|
|
||||||
|
QString explanation;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CommanderBracketDefinitions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
void addDefinition(const CommanderBracketDefinition &definition);
|
||||||
|
|
||||||
|
QString officialName(const QString &tag) const;
|
||||||
|
QString displayName(const QString &tag) const;
|
||||||
|
QString explanation(const QString &tag) const;
|
||||||
|
|
||||||
|
bool contains(const QString &tag) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHash<QString, CommanderBracketDefinition> definitions;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_COMMANDER_BRACKET_DEFINITIONS_H
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
#include "commander_bracket_service.h"
|
||||||
|
|
||||||
|
#include "../../../../../client/settings/cache_settings.h"
|
||||||
|
|
||||||
|
CommanderBracketService &CommanderBracketService::instance()
|
||||||
|
{
|
||||||
|
static CommanderBracketService service;
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommanderBracketService::CommanderBracketService(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
connect(&CommanderSpellbookApiAccessor::instance(), &CommanderSpellbookApiAccessor::estimateBracketFinished, this,
|
||||||
|
&CommanderBracketService::onEstimateBracketFinished);
|
||||||
|
|
||||||
|
connect(&CommanderSpellbookApiAccessor::instance(), &CommanderSpellbookApiAccessor::estimateBracketError, this,
|
||||||
|
&CommanderBracketService::onEstimateBracketError);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 CommanderBracketService::estimateBracket(const DeckList &deck, QObject *requester)
|
||||||
|
{
|
||||||
|
return CommanderSpellbookApiAccessor::instance().estimateBracket(deck, requester);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommanderBracketService::onEstimateBracketFinished(CommanderSpellbookApiAccessor::RequestId id,
|
||||||
|
QObject *requester,
|
||||||
|
const EstimateBracketResult &result)
|
||||||
|
{
|
||||||
|
CommanderBracketEstimate estimate;
|
||||||
|
|
||||||
|
estimate.bracketTag = result.bracketTag;
|
||||||
|
|
||||||
|
estimate.rawResult = result;
|
||||||
|
|
||||||
|
auto &definitions = SettingsCache::instance().commanderBracketDefs();
|
||||||
|
|
||||||
|
estimate.officialName = definitions.officialName(result.bracketTag);
|
||||||
|
|
||||||
|
estimate.displayName = definitions.displayName(result.bracketTag);
|
||||||
|
|
||||||
|
estimate.explanation = definitions.explanation(result.bracketTag);
|
||||||
|
|
||||||
|
emit estimateFinished(id, requester, estimate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommanderBracketService::onEstimateBracketError(CommanderSpellbookApiAccessor::RequestId id,
|
||||||
|
QObject *requester,
|
||||||
|
const QString &error)
|
||||||
|
{
|
||||||
|
emit estimateError(id, requester, error);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef COCKATRICE_COMMANDER_BRACKET_SERVICE_H
|
||||||
|
#define COCKATRICE_COMMANDER_BRACKET_SERVICE_H
|
||||||
|
|
||||||
|
#include "commander_spellbook_api_accessor.h"
|
||||||
|
#include "libcockatrice/deck_list/deck_list.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
struct CommanderBracketEstimate
|
||||||
|
{
|
||||||
|
QString bracketTag;
|
||||||
|
|
||||||
|
QString officialName;
|
||||||
|
QString displayName;
|
||||||
|
QString explanation;
|
||||||
|
|
||||||
|
EstimateBracketResult rawResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CommanderBracketService : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static CommanderBracketService &instance();
|
||||||
|
|
||||||
|
quint64 estimateBracket(const DeckList &deck, QObject *requester);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void estimateFinished(quint64 requestId, QObject *requester, const CommanderBracketEstimate &estimate);
|
||||||
|
|
||||||
|
void estimateError(quint64 requestId, QObject *requester, const QString &error);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onEstimateBracketFinished(CommanderSpellbookApiAccessor::RequestId id,
|
||||||
|
QObject *requester,
|
||||||
|
const EstimateBracketResult &result);
|
||||||
|
|
||||||
|
void onEstimateBracketError(CommanderSpellbookApiAccessor::RequestId id, QObject *requester, const QString &error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit CommanderBracketService(QObject *parent = nullptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
#include "commander_spellbook_api_accessor.h"
|
||||||
|
|
||||||
|
#include "api_response/commander_spellbook_deck_request.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <version_string.h>
|
||||||
|
|
||||||
|
static const QUrl ESTIMATE_BRACKET_URL(QStringLiteral("https://backend.commanderspellbook.com/estimate-bracket"));
|
||||||
|
|
||||||
|
CommanderSpellbookApiAccessor &CommanderSpellbookApiAccessor::instance()
|
||||||
|
{
|
||||||
|
static CommanderSpellbookApiAccessor instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommanderSpellbookApiAccessor::CommanderSpellbookApiAccessor(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CommanderSpellbookApiAccessor::RequestId CommanderSpellbookApiAccessor::estimateBracket(const DeckList &deck,
|
||||||
|
QObject *requester)
|
||||||
|
{
|
||||||
|
CommanderSpellbookDeckRequest deckRequest;
|
||||||
|
deckRequest.fromDeckList(deck);
|
||||||
|
|
||||||
|
QJsonDocument doc(deckRequest.toJson());
|
||||||
|
QByteArray body = doc.toJson(QJsonDocument::Compact);
|
||||||
|
|
||||||
|
QNetworkRequest req(ESTIMATE_BRACKET_URL);
|
||||||
|
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
req.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
|
||||||
|
|
||||||
|
QNetworkReply *reply = network.post(req, body);
|
||||||
|
|
||||||
|
const RequestId id = nextRequestId++;
|
||||||
|
|
||||||
|
reply->setProperty("requestId", QVariant::fromValue(id));
|
||||||
|
reply->setProperty("requester", QVariant::fromValue(requester));
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() { onEstimateReplyFinished(reply); });
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommanderSpellbookApiAccessor::onEstimateReplyFinished(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
const RequestId id = reply->property("requestId").toULongLong();
|
||||||
|
QObject *requester = reply->property("requester").value<QObject *>();
|
||||||
|
|
||||||
|
if (!requester) {
|
||||||
|
// Requester died — silently drop
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
emit estimateBracketError(id, requester, reply->errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonParseError err;
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &err);
|
||||||
|
|
||||||
|
if (err.error != QJsonParseError::NoError || !doc.isObject()) {
|
||||||
|
emit estimateBracketError(id, requester, QStringLiteral("Invalid JSON response"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EstimateBracketResult result;
|
||||||
|
result.fromJson(doc.object());
|
||||||
|
|
||||||
|
emit estimateBracketFinished(id, requester, result);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef COCKATRICE_COMMANDER_SPELLBOOK_API_ACCESSOR_H
|
||||||
|
#define COCKATRICE_COMMANDER_SPELLBOOK_API_ACCESSOR_H
|
||||||
|
|
||||||
|
#include "api_response/commander_spellbook_estimate_bracket_result.h"
|
||||||
|
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QObject>
|
||||||
|
#include <libcockatrice/deck_list/deck_list.h>
|
||||||
|
|
||||||
|
class CommanderSpellbookApiAccessor final : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static CommanderSpellbookApiAccessor &instance();
|
||||||
|
|
||||||
|
using RequestId = quint64;
|
||||||
|
|
||||||
|
RequestId estimateBracket(const DeckList &deck, QObject *requester);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void estimateBracketFinished(RequestId id, QObject *requester, const EstimateBracketResult &result);
|
||||||
|
|
||||||
|
void estimateBracketError(RequestId id, QObject *requester, const QString &errorMessage);
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit CommanderSpellbookApiAccessor(QObject *parent = nullptr);
|
||||||
|
Q_DISABLE_COPY_MOVE(CommanderSpellbookApiAccessor)
|
||||||
|
|
||||||
|
void onEstimateReplyFinished(QNetworkReply *reply);
|
||||||
|
|
||||||
|
QNetworkAccessManager network;
|
||||||
|
RequestId nextRequestId = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_COMMANDER_SPELLBOOK_API_ACCESSOR_H
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
#include "commander_spellbook_bracket_explainer.h"
|
||||||
|
|
||||||
|
static QString cardList(const QVector<CommanderSpellbookCardResult> &cards, int max = 5)
|
||||||
|
{
|
||||||
|
QStringList names;
|
||||||
|
for (int i = 0; i < cards.size() && i < max; ++i) {
|
||||||
|
names << cards[i].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cards.size() > max) {
|
||||||
|
names << QString("and %1 more").arg(cards.size() - max);
|
||||||
|
}
|
||||||
|
|
||||||
|
return names.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString comboCount(const QVector<CommanderSpellbookVariantResult> &variants)
|
||||||
|
{
|
||||||
|
return QString::number(variants.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
BracketExplanation BracketExplainer::explain(const EstimateBracketResult &r)
|
||||||
|
{
|
||||||
|
BracketExplanation out;
|
||||||
|
out.bracket = r.bracketTag;
|
||||||
|
|
||||||
|
if (!r.gameChangerCards.isEmpty()) {
|
||||||
|
BracketExplanationSection s;
|
||||||
|
s.title = "Game-changing cards";
|
||||||
|
|
||||||
|
s.bulletPoints << QString("Your deck contains %1 game-changing cards, such as %2.")
|
||||||
|
.arg(r.gameChangerCards.size())
|
||||||
|
.arg(cardList(r.gameChangerCards));
|
||||||
|
|
||||||
|
out.sections << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.extraTurnCards.isEmpty() || !r.extraTurnTemplates.isEmpty() || !r.extraTurnCombos.isEmpty()) {
|
||||||
|
|
||||||
|
BracketExplanationSection s;
|
||||||
|
s.title = "Extra turns";
|
||||||
|
|
||||||
|
if (!r.extraTurnCards.isEmpty()) {
|
||||||
|
s.bulletPoints << QString("The deck contains %1 extra-turn cards (%2).")
|
||||||
|
.arg(r.extraTurnCards.size())
|
||||||
|
.arg(cardList(r.extraTurnCards));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.extraTurnTemplates.isEmpty()) {
|
||||||
|
s.bulletPoints << QString("%1 extra-turn templates were identified.").arg(comboCount(r.extraTurnTemplates));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.extraTurnCombos.isEmpty()) {
|
||||||
|
s.bulletPoints
|
||||||
|
<< QString("%1 extra-turn combo variants were identified.").arg(comboCount(r.extraTurnCombos));
|
||||||
|
}
|
||||||
|
|
||||||
|
out.sections << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.massLandDenialCards.isEmpty() || !r.massLandDenialTemplates.isEmpty() || !r.massLandDenialCombos.isEmpty()) {
|
||||||
|
|
||||||
|
BracketExplanationSection s;
|
||||||
|
s.title = "Mass land denial";
|
||||||
|
|
||||||
|
if (!r.massLandDenialCards.isEmpty()) {
|
||||||
|
s.bulletPoints << QString("The deck contains %1 mass land denial cards (%2).")
|
||||||
|
.arg(r.massLandDenialCards.size())
|
||||||
|
.arg(cardList(r.massLandDenialCards));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.massLandDenialTemplates.isEmpty()) {
|
||||||
|
s.bulletPoints
|
||||||
|
<< QString("%1 mass land denial templates were identified.").arg(comboCount(r.massLandDenialTemplates));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.massLandDenialCombos.isEmpty()) {
|
||||||
|
s.bulletPoints << QString("%1 mass land denial combo variants were identified.")
|
||||||
|
.arg(comboCount(r.massLandDenialCombos));
|
||||||
|
}
|
||||||
|
|
||||||
|
out.sections << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.lockCombos.isEmpty() || !r.skipTurnsCombos.isEmpty()) {
|
||||||
|
|
||||||
|
BracketExplanationSection s;
|
||||||
|
s.title = "Lock pieces";
|
||||||
|
|
||||||
|
if (!r.lockCombos.isEmpty()) {
|
||||||
|
s.bulletPoints << QString("%1 lock combo variants were detected.").arg(comboCount(r.lockCombos));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.skipTurnsCombos.isEmpty()) {
|
||||||
|
s.bulletPoints << QString("%1 skip-turn combo variants were detected.").arg(comboCount(r.skipTurnsCombos));
|
||||||
|
}
|
||||||
|
|
||||||
|
out.sections << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.definitelyTwoCardCombos.isEmpty() || !r.arguablyTwoCardCombos.isEmpty()) {
|
||||||
|
|
||||||
|
BracketExplanationSection s;
|
||||||
|
s.title = "Two-card combos";
|
||||||
|
|
||||||
|
if (!r.definitelyTwoCardCombos.isEmpty()) {
|
||||||
|
s.bulletPoints << QString("%1 definite two-card combo variants were identified.")
|
||||||
|
.arg(comboCount(r.definitelyTwoCardCombos));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.arguablyTwoCardCombos.isEmpty()) {
|
||||||
|
s.bulletPoints << QString("%1 arguable two-card combo variants were identified.")
|
||||||
|
.arg(comboCount(r.arguablyTwoCardCombos));
|
||||||
|
}
|
||||||
|
|
||||||
|
out.sections << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef COCKATRICE_COMMANDER_SPELLBOOK_BRACKET_EXPLAINER_H
|
||||||
|
#define COCKATRICE_COMMANDER_SPELLBOOK_BRACKET_EXPLAINER_H
|
||||||
|
#include "api_response/commander_spellbook_estimate_bracket_result.h"
|
||||||
|
|
||||||
|
struct BracketExplanationSection
|
||||||
|
{
|
||||||
|
QString title;
|
||||||
|
QStringList bulletPoints;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BracketExplanation
|
||||||
|
{
|
||||||
|
QString bracket;
|
||||||
|
QList<BracketExplanationSection> sections;
|
||||||
|
|
||||||
|
bool isEmpty() const
|
||||||
|
{
|
||||||
|
return sections.isEmpty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BracketExplainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static BracketExplanation explain(const EstimateBracketResult &result);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_COMMANDER_SPELLBOOK_BRACKET_EXPLAINER_H
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
#include "handle_commander_brackets.h"
|
||||||
|
|
||||||
|
#include "../../../../../client/settings/cache_settings.h"
|
||||||
|
#include "commander_bracket_definitions.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#define COMMANDER_BRACKET_JSON "https://cockatrice.github.io/commander-brackets.json"
|
||||||
|
|
||||||
|
HandleCommanderBrackets::HandleCommanderBrackets(QObject *parent)
|
||||||
|
: QObject(parent), nam(new QNetworkAccessManager(this)), reply(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleCommanderBrackets::downloadBracketDefinitions()
|
||||||
|
{
|
||||||
|
reply = nam->get(QNetworkRequest(QUrl(COMMANDER_BRACKET_JSON)));
|
||||||
|
|
||||||
|
connect(reply, &QNetworkReply::finished, this, &HandleCommanderBrackets::actFinishParsingDownloadedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleCommanderBrackets::actFinishParsingDownloadedData()
|
||||||
|
{
|
||||||
|
reply = qobject_cast<QNetworkReply *>(sender());
|
||||||
|
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
emit sigBracketDefinitionsDownloadFailed(reply->error());
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonParseError parseError;
|
||||||
|
|
||||||
|
auto document = QJsonDocument::fromJson(reply->readAll(), &parseError);
|
||||||
|
|
||||||
|
if (parseError.error != QJsonParseError::NoError) {
|
||||||
|
emit sigBracketDefinitionsDownloadFailed(QNetworkReply::UnknownContentError);
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBracketDefinitions(document.toVariant().toMap());
|
||||||
|
|
||||||
|
emit sigBracketDefinitionsDownloaded();
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleCommanderBrackets::updateBracketDefinitions(const QVariantMap &jsonMap)
|
||||||
|
{
|
||||||
|
const auto bracketList = jsonMap.value("brackets").toList();
|
||||||
|
SettingsCache::instance().commanderBrackets().saveDefinitions(bracketList);
|
||||||
|
SettingsCache::instance().reloadBracketDefinitions(bracketList);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef COCKATRICE_HANDLE_COMMANDER_BRACKETS_H
|
||||||
|
#define COCKATRICE_HANDLE_COMMANDER_BRACKETS_H
|
||||||
|
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class HandleCommanderBrackets : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit HandleCommanderBrackets(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void downloadBracketDefinitions();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sigBracketDefinitionsDownloaded();
|
||||||
|
void sigBracketDefinitionsDownloadFailed(QNetworkReply::NetworkError error);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void actFinishParsingDownloadedData();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateBracketDefinitions(const QVariantMap &jsonMap);
|
||||||
|
|
||||||
|
QNetworkAccessManager *nam;
|
||||||
|
QNetworkReply *reply;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_HANDLE_COMMANDER_BRACKETS_H
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
#include "version_string.h"
|
#include "version_string.h"
|
||||||
#include "widgets/dialogs/dlg_connect.h"
|
#include "widgets/dialogs/dlg_connect.h"
|
||||||
#include "widgets/server/handle_public_servers.h"
|
#include "widgets/server/handle_public_servers.h"
|
||||||
|
#include "widgets/tabs/api/commander_spellbook/handle_commander_brackets.h"
|
||||||
#include "widgets/utility/get_text_with_max.h"
|
#include "widgets/utility/get_text_with_max.h"
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
|
@ -640,6 +641,7 @@ void MainWindow::alertForcedOracleRun(const QString &version, bool isUpdate)
|
||||||
|
|
||||||
actCheckCardUpdates();
|
actCheckCardUpdates();
|
||||||
actCheckServerUpdates();
|
actCheckServerUpdates();
|
||||||
|
actCheckCommanderBracketDefinitionUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
|
|
@ -984,6 +986,16 @@ void MainWindow::checkClientUpdatesFinished(bool needToUpdate, bool /* isCompati
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::actCheckCommanderBracketDefinitionUpdates()
|
||||||
|
{
|
||||||
|
auto *handler = new HandleCommanderBrackets(this);
|
||||||
|
|
||||||
|
connect(handler, &HandleCommanderBrackets::sigBracketDefinitionsDownloaded, this,
|
||||||
|
[]() { qDebug() << "Bracket definitions loaded"; });
|
||||||
|
|
||||||
|
handler->downloadBracketDefinitions();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::refreshShortcuts()
|
void MainWindow::refreshShortcuts()
|
||||||
{
|
{
|
||||||
ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();
|
ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ private slots:
|
||||||
void cardDatabaseAllNewSetsEnabled();
|
void cardDatabaseAllNewSetsEnabled();
|
||||||
|
|
||||||
void checkClientUpdatesFinished(bool needToUpdate, bool isCompatible, Release *release);
|
void checkClientUpdatesFinished(bool needToUpdate, bool isCompatible, Release *release);
|
||||||
|
void actCheckCommanderBracketDefinitionUpdates();
|
||||||
|
|
||||||
void actOpenCustomFolder();
|
void actOpenCustomFolder();
|
||||||
void actOpenCustomsetsFolder();
|
void actOpenCustomsetsFolder();
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ set(CMAKE_AUTORCC ON)
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
libcockatrice/settings/card_database_settings.h
|
libcockatrice/settings/card_database_settings.h
|
||||||
libcockatrice/settings/card_override_settings.h
|
libcockatrice/settings/card_override_settings.h
|
||||||
|
libcockatrice/settings/commander_bracket_settings.h
|
||||||
libcockatrice/settings/debug_settings.h
|
libcockatrice/settings/debug_settings.h
|
||||||
libcockatrice/settings/download_settings.h
|
libcockatrice/settings/download_settings.h
|
||||||
libcockatrice/settings/game_filters_settings.h
|
libcockatrice/settings/game_filters_settings.h
|
||||||
|
|
@ -26,6 +27,7 @@ add_library(
|
||||||
${MOC_SOURCES}
|
${MOC_SOURCES}
|
||||||
libcockatrice/settings/card_database_settings.cpp
|
libcockatrice/settings/card_database_settings.cpp
|
||||||
libcockatrice/settings/card_override_settings.cpp
|
libcockatrice/settings/card_override_settings.cpp
|
||||||
|
libcockatrice/settings/commander_bracket_settings.cpp
|
||||||
libcockatrice/settings/debug_settings.cpp
|
libcockatrice/settings/debug_settings.cpp
|
||||||
libcockatrice/settings/download_settings.cpp
|
libcockatrice/settings/download_settings.cpp
|
||||||
libcockatrice/settings/game_filters_settings.cpp
|
libcockatrice/settings/game_filters_settings.cpp
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
#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", "PA"},
|
||||||
|
{"officialName", "[1] Exhibition"},
|
||||||
|
{"displayName", "Precon Appropriate"},
|
||||||
|
{"explanation", "Lightly upgraded preconstructed decks or very casual builds."}},
|
||||||
|
QVariantMap{{"tag", "C"},
|
||||||
|
{"officialName", "[1] Casual"},
|
||||||
|
{"displayName", "Casual"},
|
||||||
|
{"explanation", "Relaxed decks with no strict optimization goals."}},
|
||||||
|
QVariantMap{{"tag", "U"},
|
||||||
|
{"officialName", "Unknown"},
|
||||||
|
{"displayName", "Unknown"},
|
||||||
|
{"explanation", "Unclassified or missing bracket definition."}},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef COMMANDER_BRACKET_SETTINGS_H
|
||||||
|
#define COMMANDER_BRACKET_SETTINGS_H
|
||||||
|
|
||||||
|
#include "settings_manager.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVariantList>
|
||||||
|
|
||||||
|
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 setSchemaVersion(int version);
|
||||||
|
int getSchemaVersion() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit CommanderBracketSettings(const QString &settingPath, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
CommanderBracketSettings(const CommanderBracketSettings &) = delete;
|
||||||
|
CommanderBracketSettings &operator=(const CommanderBracketSettings &) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COMMANDER_BRACKET_SETTINGS_H
|
||||||
|
|
@ -32,6 +32,7 @@ set(oracle_SOURCES
|
||||||
../cockatrice/src/interface/theme_manager.cpp
|
../cockatrice/src/interface/theme_manager.cpp
|
||||||
../cockatrice/src/interface/widgets/quick_settings/settings_button_widget.cpp
|
../cockatrice/src/interface/widgets/quick_settings/settings_button_widget.cpp
|
||||||
../cockatrice/src/interface/widgets/quick_settings/settings_popup_widget.cpp
|
../cockatrice/src/interface/widgets/quick_settings/settings_popup_widget.cpp
|
||||||
|
../cockatrice/src/interface/widgets/tabs/api/commander_spellbook/commander_bracket_definitions.cpp
|
||||||
${VERSION_STRING_CPP}
|
${VERSION_STRING_CPP}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue