diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 15a924471..fbb779701 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -235,6 +235,7 @@ set(cockatrice_SOURCES src/server/user/user_list_manager.cpp src/server/user/user_list_widget.cpp src/settings/cache_settings.cpp + src/settings/card_counter_settings.cpp src/settings/card_database_settings.cpp src/settings/card_override_settings.cpp src/settings/debug_settings.cpp diff --git a/cockatrice/resources/help/deck_search.md b/cockatrice/resources/help/deck_search.md index e2fa4fe37..95c237b16 100644 --- a/cockatrice/resources/help/deck_search.md +++ b/cockatrice/resources/help/deck_search.md @@ -8,6 +8,20 @@ searches are case insensitive.
[red deck wins](#red deck wins) (Any deck with a display name containing the words red, deck, and wins)
["red deck wins"](#%22red deck wins%22) (Any deck with a display name containing the exact phrase "red deck wins")
+
Deck Name:
+
[n:aggro](#n:aggro) (Any deck with a name containing the word aggro)
+
[n:red n:deck n:wins](#n:red n:deck n:wins) (Any deck with a name containing the words red, deck, and wins)
+
[n:"red deck wins"](#n:%22red deck wins%22) (Any deck with a name containing the exact phrase "red deck wins")
+ +
File Name:
+
[f:aggro](#f:aggro) (Any deck with a filename containing the word aggro)
+
[f:red f:deck f:wins](#f:red f:deck f:wins) (Any deck with a filename containing the words red, deck, and wins)
+
[f:"red deck wins"](#f:%22red deck wins%22) (Any deck with a filename containing the exact phrase "red deck wins")
+ +
Relative Path (starting from the deck folder):
+
[p:aggro](#p:aggro) (Any deck that has "aggro" somewhere in its relative path)
+
[p:edh/](#p:edh/) (Any deck with "edh/" in its relative path, A.K.A. decks in the "edh" folder)
+
Deck Contents (Uses [card search expressions](#cardSearchSyntaxHelp)):
[[plains]] (Any deck that contains at least one card with "plains" in its name)
[[t:legendary]] (Any deck that contains at least one legendary)
diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp index cd085a511..385007c3e 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.cpp @@ -42,14 +42,6 @@ VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidg connect(showBannerCardComboBoxCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), &SettingsCache::setVisualDeckStorageShowBannerCardComboBox); - // search folder names checkbox - searchFolderNamesCheckBox = new QCheckBox(this); - searchFolderNamesCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageSearchFolderNames()); - connect(searchFolderNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, this, - &VisualDeckStorageQuickSettingsWidget::searchFolderNamesChanged); - connect(searchFolderNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), - &SettingsCache::setVisualDeckStorageSearchFolderNames); - // draw unused color identities checkbox drawUnusedColorIdentitiesCheckBox = new QCheckBox(this); drawUnusedColorIdentitiesCheckBox->setChecked( @@ -113,7 +105,6 @@ VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidg this->addSettingsWidget(showTagFilterCheckBox); this->addSettingsWidget(showTagsOnDeckPreviewsCheckBox); this->addSettingsWidget(showBannerCardComboBoxCheckBox); - this->addSettingsWidget(searchFolderNamesCheckBox); this->addSettingsWidget(drawUnusedColorIdentitiesCheckBox); this->addSettingsWidget(unusedColorIdentityOpacityWidget); this->addSettingsWidget(deckPreviewTooltipWidget); @@ -130,7 +121,6 @@ void VisualDeckStorageQuickSettingsWidget::retranslateUi() showTagFilterCheckBox->setText(tr("Show Tag Filter")); showTagsOnDeckPreviewsCheckBox->setText(tr("Show Tags On Deck Previews")); showBannerCardComboBoxCheckBox->setText(tr("Show Banner Card Selection Option")); - searchFolderNamesCheckBox->setText(tr("Include Folder Names in Search")); drawUnusedColorIdentitiesCheckBox->setText(tr("Draw unused Color Identities")); unusedColorIdentitiesOpacityLabel->setText(tr("Unused Color Identities Opacity")); unusedColorIdentitiesOpacitySpinBox->setSuffix("%"); @@ -165,11 +155,6 @@ bool VisualDeckStorageQuickSettingsWidget::getShowTagsOnDeckPreviews() const return showTagsOnDeckPreviewsCheckBox->isChecked(); } -bool VisualDeckStorageQuickSettingsWidget::getSearchFolderNames() const -{ - return searchFolderNamesCheckBox->isChecked(); -} - int VisualDeckStorageQuickSettingsWidget::getUnusedColorIdentitiesOpacity() const { return unusedColorIdentitiesOpacitySpinBox->value(); diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.h index d27a9053b..11c1d64e9 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_quick_settings_widget.h @@ -23,7 +23,6 @@ class VisualDeckStorageQuickSettingsWidget : public SettingsButtonWidget QCheckBox *showBannerCardComboBoxCheckBox; QCheckBox *showTagFilterCheckBox; QCheckBox *showTagsOnDeckPreviewsCheckBox; - QCheckBox *searchFolderNamesCheckBox; QLabel *unusedColorIdentitiesOpacityLabel; QSpinBox *unusedColorIdentitiesOpacitySpinBox; QLabel *deckPreviewTooltipLabel; @@ -50,7 +49,6 @@ public: bool getShowBannerCardComboBox() const; bool getShowTagFilter() const; bool getShowTagsOnDeckPreviews() const; - bool getSearchFolderNames() const; int getUnusedColorIdentitiesOpacity() const; TooltipType getDeckPreviewTooltip() const; int getCardSize() const; @@ -61,7 +59,6 @@ signals: void showBannerCardComboBoxChanged(bool enabled); void showTagFilterChanged(bool enabled); void showTagsOnDeckPreviewsChanged(bool enabled); - void searchFolderNamesChanged(bool enabled); void unusedColorIdentitiesOpacityChanged(int opacity); void deckPreviewTooltipChanged(TooltipType tooltip); void cardSizeChanged(int scale); diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp index 2998845f1..560e3c6c9 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp @@ -52,17 +52,15 @@ QString VisualDeckStorageSearchWidget::getSearchText() } /** - * Gets the filename used for the search. + * Converts the filepath into a relative filepath starting from the deck folder. + * If the file isn't in the deck folder, then this will just return the filename. * - * if includeFolderName is true, then this returns the relative filepath starting from the deck folder. - * If the file isn't in the deck folder, or includeFolderName is false, then this will just return the filename. - * - * @param filePath The filePath to convert into a search name + * @param filePath The filepath to convert into a relative filepath */ -static QString getFileSearchName(const QString &filePath, bool includeFolderName) +static QString toRelativeFilepath(const QString &filePath) { QString deckPath = SettingsCache::instance().getDeckPath(); - if (includeFolderName && filePath.startsWith(deckPath)) { + if (filePath.startsWith(deckPath)) { return filePath.mid(deckPath.length()); } @@ -71,14 +69,12 @@ static QString getFileSearchName(const QString &filePath, bool includeFolderName return fileName; } -void VisualDeckStorageSearchWidget::filterWidgets(QList widgets, - const QString &searchText, - bool includeFolderName) +void VisualDeckStorageSearchWidget::filterWidgets(QList widgets, const QString &searchText) { auto filterString = DeckFilterString(searchText); for (auto widget : widgets) { - QString fileSearchName = getFileSearchName(widget->filePath, includeFolderName); - widget->filteredBySearch = !filterString.check(widget, {fileSearchName}); + QString relativeFilePath = toRelativeFilepath(widget->filePath); + widget->filteredBySearch = !filterString.check(widget, {relativeFilePath}); } } diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.h index 444ea9a27..e8b91bc5b 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.h @@ -16,7 +16,7 @@ class VisualDeckStorageSearchWidget : public QWidget public: explicit VisualDeckStorageSearchWidget(VisualDeckStorageWidget *parent); QString getSearchText(); - void filterWidgets(QList widgets, const QString &searchText, bool includeFolderName); + void filterWidgets(QList widgets, const QString &searchText); private: QHBoxLayout *layout; diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp index e82f0c8d6..078b9337f 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/visual_deck_storage_widget.cpp @@ -46,8 +46,6 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare &VisualDeckStorageWidget::updateShowFolders); connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::showTagFilterChanged, this, &VisualDeckStorageWidget::updateTagsVisibility); - connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::searchFolderNamesChanged, this, - &VisualDeckStorageWidget::updateSearchFilter); searchAndSortLayout->addWidget(deckPreviewColorIdentityFilterWidget); searchAndSortLayout->addWidget(sortWidget); @@ -196,8 +194,7 @@ void VisualDeckStorageWidget::updateColorFilter() void VisualDeckStorageWidget::updateSearchFilter() { if (folderWidget) { - searchWidget->filterWidgets(folderWidget->findChildren(), searchWidget->getSearchText(), - quickSettingsWidget->getSearchFolderNames()); + searchWidget->filterWidgets(folderWidget->findChildren(), searchWidget->getSearchText()); folderWidget->updateVisibility(); } } diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp index 63901325f..0b0b86260 100644 --- a/cockatrice/src/dialogs/dlg_settings.cpp +++ b/cockatrice/src/dialogs/dlg_settings.cpp @@ -12,13 +12,17 @@ #include "../game/cards/card_database_manager.h" #include "../main.h" #include "../settings/cache_settings.h" +#include "../settings/card_counter_settings.h" #include "../settings/shortcut_treeview.h" #include "../utility/sequence_edit.h" +#include +#include #include #include #include #include +#include #include #include #include @@ -42,7 +46,7 @@ #include #include #include -#include +#include #define WIKI_CUSTOM_PIC_URL "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Picture-Download-URLs" #define WIKI_CUSTOM_SHORTCUTS "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Keyboard-Shortcuts" @@ -442,6 +446,51 @@ AppearanceSettingsPage::AppearanceSettingsPage() cardsGroupBox = new QGroupBox; cardsGroupBox->setLayout(cardsGrid); + // Card counter colors + + auto *cardCounterColorsLayout = new QGridLayout; + cardCounterColorsLayout->setColumnStretch(1, 1); + cardCounterColorsLayout->setColumnStretch(3, 1); + cardCounterColorsLayout->setColumnStretch(5, 1); + + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + for (int index = 0; index < 6; ++index) { + auto *pushButton = new QPushButton; + pushButton->setStyleSheet(QString("background-color: %1").arg(cardCounterSettings.color(index).name())); + + connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, pushButton, + [index, pushButton](int changedIndex, const QColor &color) { + if (index == changedIndex) { + pushButton->setStyleSheet(QString("background-color: %1").arg(color.name())); + } + }); + + connect(pushButton, &QPushButton::clicked, this, [index, this]() { + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + + auto newColor = QColorDialog::getColor(cardCounterSettings.color(index), this); + if (!newColor.isValid()) + return; + + cardCounterSettings.setColor(index, newColor); + }); + + auto *colorName = new QLabel; + cardCounterNames.append(colorName); + + int row = index / 3; + int column = 2 * (index % 3); + + cardCounterColorsLayout->addWidget(pushButton, row, column); + cardCounterColorsLayout->addWidget(colorName, row, column + 1); + } + + auto *cardCountersLayout = new QVBoxLayout; + cardCountersLayout->addLayout(cardCounterColorsLayout, 1); + + cardCountersGroupBox = new QGroupBox; + cardCountersGroupBox->setLayout(cardCountersLayout); + // Hand layout horizontalHandCheckBox.setChecked(settings.getHorizontalHand()); connect(&horizontalHandCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setHorizontalHand); @@ -489,6 +538,7 @@ AppearanceSettingsPage::AppearanceSettingsPage() mainLayout->addWidget(themeGroupBox); mainLayout->addWidget(menuGroupBox); mainLayout->addWidget(cardsGroupBox); + mainLayout->addWidget(cardCountersGroupBox); mainLayout->addWidget(handGroupBox); mainLayout->addWidget(tableGroupBox); mainLayout->addStretch(); @@ -577,6 +627,13 @@ void AppearanceSettingsPage::retranslateUi() cardViewExpandedRowsMaxLabel.setText(tr("Maximum expanded height for card view window:")); cardViewExpandedRowsMaxBox.setSuffix(tr(" rows")); + cardCountersGroupBox->setTitle(tr("Card counters")); + + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + for (int index = 0; index < cardCounterNames.size(); ++index) { + cardCounterNames[index]->setText(tr("Counter %1").arg(cardCounterSettings.displayName(index))); + } + handGroupBox->setTitle(tr("Hand layout")); horizontalHandCheckBox.setText(tr("Display hand horizontally (wastes space)")); leftJustifiedHandCheckBox.setText(tr("Enable left justification")); diff --git a/cockatrice/src/dialogs/dlg_settings.h b/cockatrice/src/dialogs/dlg_settings.h index 54b2cbcd8..f0b9c62c9 100644 --- a/cockatrice/src/dialogs/dlg_settings.h +++ b/cockatrice/src/dialogs/dlg_settings.h @@ -125,6 +125,8 @@ private: QGroupBox *cardsGroupBox; QGroupBox *handGroupBox; QGroupBox *tableGroupBox; + QGroupBox *cardCountersGroupBox; + QList cardCounterNames; QSpinBox minPlayersForMultiColumnLayoutEdit; QSpinBox maxFontSizeForCardsEdit; diff --git a/cockatrice/src/game/board/card_item.cpp b/cockatrice/src/game/board/card_item.cpp index 55df7a477..f9d702d92 100644 --- a/cockatrice/src/game/board/card_item.cpp +++ b/cockatrice/src/game/board/card_item.cpp @@ -2,6 +2,7 @@ #include "../../client/tabs/tab_game.h" #include "../../settings/cache_settings.h" +#include "../../settings/card_counter_settings.h" #include "../cards/card_info.h" #include "../game_scene.h" #include "../player/player.h" @@ -32,6 +33,11 @@ CardItem::CardItem(Player *_owner, ptMenu = new QMenu; moveMenu = new QMenu; + connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, this, [this](int counterId) { + if (counters.contains(counterId)) + update(); + }); + retranslateUi(); } @@ -84,6 +90,8 @@ void CardItem::retranslateUi() void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + painter->save(); AbstractCardItem::paint(painter, option, widget); @@ -91,8 +99,7 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QMapIterator counterIterator(counters); while (counterIterator.hasNext()) { counterIterator.next(); - QColor _color; - _color.setHsv(counterIterator.key() * 60, 150, 255); + QColor _color = cardCounterSettings.color(counterIterator.key()); paintNumberEllipse(counterIterator.value(), 14, _color, i, counters.size(), painter); ++i; diff --git a/cockatrice/src/game/filters/deck_filter_string.cpp b/cockatrice/src/game/filters/deck_filter_string.cpp index 13c39910d..4d493e5f1 100644 --- a/cockatrice/src/game/filters/deck_filter_string.cpp +++ b/cockatrice/src/game/filters/deck_filter_string.cpp @@ -12,7 +12,7 @@ QueryPartList <- ComplexQueryPart ( ws ("AND" ws)? ComplexQueryPart)* ws* ComplexQueryPart <- SomewhatComplexQueryPart ws "OR" ws ComplexQueryPart / SomewhatComplexQueryPart SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart -QueryPart <- NotQuery / DeckContentQuery / GenericQuery +QueryPart <- NotQuery / DeckContentQuery / DeckNameQuery / FileNameQuery / PathQuery / GenericQuery NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart @@ -20,6 +20,10 @@ DeckContentQuery <- CardSearch NumericExpression? CardSearch <- '[[' CardFilterString ']]' CardFilterString <- (!']]'.)* +DeckNameQuery <- ([Dd] 'eck')? [Nn] 'ame'? [:] String +FileNameQuery <- [Ff] ('ile' 'name'?)? [:] String +PathQuery <- [Pp] 'ath'? [:] String + GenericQuery <- String NonDoubleQuoteUnlessEscaped <- '\\\"'. / !["]. @@ -129,6 +133,28 @@ static void setupParserRules() return QString::fromStdString(std::string(sv.sv())); }; + search["DeckNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { + auto name = std::any_cast(sv[0]); + return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { + return deck->deckLoader->getName().contains(name, Qt::CaseInsensitive); + }; + }; + + search["FileNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { + auto name = std::any_cast(sv[0]); + return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { + auto filename = QFileInfo(deck->filePath).fileName(); + return filename.contains(name, Qt::CaseInsensitive); + }; + }; + + search["PathQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { + auto name = std::any_cast(sv[0]); + return [=](const DeckPreviewWidget *, const ExtraDeckSearchInfo &info) { + return info.relativeFilePath.contains(name, Qt::CaseInsensitive); + }; + }; + search["GenericQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter { auto name = std::any_cast(sv[0]); return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) { diff --git a/cockatrice/src/game/filters/deck_filter_string.h b/cockatrice/src/game/filters/deck_filter_string.h index dfdb1f1a8..df6eac783 100644 --- a/cockatrice/src/game/filters/deck_filter_string.h +++ b/cockatrice/src/game/filters/deck_filter_string.h @@ -17,9 +17,9 @@ inline Q_LOGGING_CATEGORY(DeckFilterStringLog, "deck_filter_string"); struct ExtraDeckSearchInfo { /** - * The filename used for filtering. Varies based on settings. + * The relative filepath starting from the deck folder */ - QString fileSearchName; + QString relativeFilePath; }; typedef std::function DeckFilter; diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp index 9fff6b3df..8ce7b0055 100644 --- a/cockatrice/src/game/player/player.cpp +++ b/cockatrice/src/game/player/player.cpp @@ -8,6 +8,7 @@ #include "../../dialogs/dlg_roll_dice.h" #include "../../main.h" #include "../../settings/cache_settings.h" +#include "../../settings/card_counter_settings.h" #include "../board/arrow_item.h" #include "../board/card_item.h" #include "../board/card_list.h" @@ -419,6 +420,8 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T createPredefinedTokenMenu = new QMenu(QString()); createPredefinedTokenMenu->setEnabled(false); + mCardCounters = new QMenu; + playerMenu->addSeparator(); countersMenu = playerMenu->addMenu(QString()); playerMenu->addSeparator(); @@ -461,6 +464,7 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T mCustomZones = nullptr; aCreateAnotherToken = nullptr; createPredefinedTokenMenu = nullptr; + mCardCounters = nullptr; } aTap = new QAction(this); @@ -538,7 +542,7 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T aPlayFacedown = new QAction(this); connect(aPlayFacedown, &QAction::triggered, this, &Player::actPlayFacedown); - for (int i = 0; i < 3; ++i) { + for (int i = 0; i < 6; ++i) { auto *tempAddCounter = new QAction(this); tempAddCounter->setData(9 + i * 1000); auto *tempRemoveCounter = new QAction(this); @@ -892,19 +896,17 @@ void Player::retranslateUi() aResetPT->setText(tr("Reset p&ower and toughness")); aSetAnnotation->setText(tr("&Set annotation...")); - QStringList counterColors; - counterColors.append(tr("Red")); - counterColors.append(tr("Yellow")); - counterColors.append(tr("Green")); + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + mCardCounters->setTitle(tr("Ca&rd counters")); for (int i = 0; i < aAddCounter.size(); ++i) { - aAddCounter[i]->setText(tr("&Add counter (%1)").arg(counterColors[i])); + aAddCounter[i]->setText(tr("&Add counter (%1)").arg(cardCounterSettings.displayName(i))); } for (int i = 0; i < aRemoveCounter.size(); ++i) { - aRemoveCounter[i]->setText(tr("&Remove counter (%1)").arg(counterColors[i])); + aRemoveCounter[i]->setText(tr("&Remove counter (%1)").arg(cardCounterSettings.displayName(i))); } for (int i = 0; i < aSetCounter.size(); ++i) { - aSetCounter[i]->setText(tr("&Set counters (%1)...").arg(counterColors[i])); + aSetCounter[i]->setText(tr("&Set counters (%1)...").arg(cardCounterSettings.displayName(i))); } aMoveToTopLibrary->setText(tr("&Top of library in random order")); @@ -960,24 +962,33 @@ void Player::setShortcutsActive() addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCRed")); addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCYellow")); addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCGreen")); + addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCCyan")); + addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCPurple")); + addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCMagenta")); QList removeCCShortCuts; removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCRed")); removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCYellow")); removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCGreen")); + removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCCyan")); + removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCPurple")); + removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCMagenta")); QList setCCShortCuts; setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCRed")); setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCYellow")); setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCGreen")); + setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCCyan")); + setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCPurple")); + setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCMagenta")); - for (int i = 0; i < aAddCounter.size(); ++i) { + for (int i = 0; i < addCCShortCuts.size(); ++i) { aAddCounter[i]->setShortcut(addCCShortCuts.at(i)); } - for (int i = 0; i < aRemoveCounter.size(); ++i) { + for (int i = 0; i < removeCCShortCuts.size(); ++i) { aRemoveCounter[i]->setShortcut(removeCCShortCuts.at(i)); } - for (int i = 0; i < aSetCounter.size(); ++i) { + for (int i = 0; i < setCCShortCuts.size(); ++i) { aSetCounter[i]->setShortcut(setCCShortCuts.at(i)); } @@ -3944,15 +3955,18 @@ void Player::updateCardMenu(const CardItem *card) cardMenu->addAction(aSelectAll); cardMenu->addAction(aSelectRow); + cardMenu->addSeparator(); + mCardCounters->clear(); for (int i = 0; i < aAddCounter.size(); ++i) { - cardMenu->addSeparator(); - cardMenu->addAction(aAddCounter[i]); + mCardCounters->addSeparator(); + mCardCounters->addAction(aAddCounter[i]); if (card->getCounters().contains(i)) { - cardMenu->addAction(aRemoveCounter[i]); + mCardCounters->addAction(aRemoveCounter[i]); } - cardMenu->addAction(aSetCounter[i]); + mCardCounters->addAction(aSetCounter[i]); } cardMenu->addSeparator(); + cardMenu->addMenu(mCardCounters); } else if (card->getZone()->getName() == "stack") { // Card is on the stack if (canModifyCard) { diff --git a/cockatrice/src/game/player/player.h b/cockatrice/src/game/player/player.h index d802ef796..9062ac5b4 100644 --- a/cockatrice/src/game/player/player.h +++ b/cockatrice/src/game/player/player.h @@ -253,7 +253,7 @@ public: private: TabGame *game; QMenu *sbMenu, *countersMenu, *sayMenu, *createPredefinedTokenMenu, *mRevealLibrary, *mLendLibrary, *mRevealTopCard, - *mRevealHand, *mRevealRandomHandCard, *mRevealRandomGraveyardCard, *mCustomZones; + *mRevealHand, *mRevealRandomHandCard, *mRevealRandomGraveyardCard, *mCustomZones, *mCardCounters; TearOffMenu *moveGraveMenu, *moveRfgMenu, *graveMenu, *moveHandMenu, *handMenu, *libraryMenu, *topLibraryMenu, *bottomLibraryMenu, *rfgMenu, *playerMenu; QList playerLists; diff --git a/cockatrice/src/server/message_log_widget.cpp b/cockatrice/src/server/message_log_widget.cpp index a61a7ba2c..ed25801f6 100644 --- a/cockatrice/src/server/message_log_widget.cpp +++ b/cockatrice/src/server/message_log_widget.cpp @@ -6,6 +6,7 @@ #include "../game/phase.h" #include "../game/player/player.h" #include "../game/zones/card_zone.h" +#include "../settings/card_counter_settings.h" #include "pb/context_move_card.pb.h" #include "pb/context_mulligan.pb.h" #include "pb/serverinfo_user.pb.h" @@ -613,28 +614,15 @@ void MessageLogWidget::logSetCardCounter(Player *player, QString cardName, int c QString finalStr; int delta = abs(oldValue - value); if (value > oldValue) { - finalStr = tr("%1 places %2 %3 on %4 (now %5)."); + finalStr = tr("%1 places %2 \"%3\" counter(s) on %4 (now %5).", "", delta); } else { - finalStr = tr("%1 removes %2 %3 from %4 (now %5)."); - } - - QString colorStr; - switch (counterId) { - case 0: - colorStr = tr("red counter(s)", "", delta); - break; - case 1: - colorStr = tr("yellow counter(s)", "", delta); - break; - case 2: - colorStr = tr("green counter(s)", "", delta); - break; - default:; + finalStr = tr("%1 removes %2 \"%3\" counter(s) from %4 (now %5).", "", delta); } + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); appendHtmlServerMessage(finalStr.arg(sanitizeHtml(player->getName())) .arg("" + QString::number(delta) + "") - .arg(colorStr) + .arg(cardCounterSettings.displayName(counterId)) .arg(cardLink(std::move(cardName))) .arg(value)); } diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp index b2bfbf9f8..df7027dd5 100644 --- a/cockatrice/src/settings/cache_settings.cpp +++ b/cockatrice/src/settings/cache_settings.cpp @@ -1,8 +1,10 @@ #include "cache_settings.h" #include "../client/network/release_channel.h" +#include "card_counter_settings.h" #include "card_override_settings.h" +#include #include #include #include @@ -179,6 +181,8 @@ SettingsCache::SettingsCache() cardOverrideSettings = new CardOverrideSettings(settingsPath, this); debugSettings = new DebugSettings(settingsPath, this); + cardCounterSettings = new CardCounterSettings(settingsPath, this); + if (!QFile(settingsPath + "global.ini").exists()) translateLegacySettings(); @@ -1439,3 +1443,8 @@ SettingsCache &SettingsCache::instance() { return *settingsCache; } + +CardCounterSettings &SettingsCache::cardCounters() const +{ + return *cardCounterSettings; +} diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h index 3fa04c094..70d2c0ca2 100644 --- a/cockatrice/src/settings/cache_settings.h +++ b/cockatrice/src/settings/cache_settings.h @@ -123,6 +123,7 @@ inline QStringList defaultTags = { }; class QSettings; +class CardCounterSettings; class SettingsCache : public QObject { @@ -185,6 +186,7 @@ private: RecentsSettings *recentsSettings; CardOverrideSettings *cardOverrideSettings; DebugSettings *debugSettings; + CardCounterSettings *cardCounterSettings; QByteArray mainWindowGeometry; QByteArray tokenDialogGeometry; @@ -870,6 +872,8 @@ public: { return *debugSettings; } + CardCounterSettings &cardCounters() const; + bool getIsPortableBuild() const { return isPortableBuild; diff --git a/cockatrice/src/settings/card_counter_settings.cpp b/cockatrice/src/settings/card_counter_settings.cpp new file mode 100644 index 000000000..d22eb239f --- /dev/null +++ b/cockatrice/src/settings/card_counter_settings.cpp @@ -0,0 +1,56 @@ +#include "card_counter_settings.h" + +#include +#include +#include + +CardCounterSettings::CardCounterSettings(const QString &settingsPath, QObject *parent) + : SettingsManager(settingsPath + "global.ini", parent) +{ +} + +void CardCounterSettings::setColor(int counterId, const QColor &color) +{ + QString key = QString("cards/counters/%1/color").arg(counterId); + + if (settings.value(key).value() == color) + return; + + settings.setValue(key, color); + emit colorChanged(counterId, color); +} + +QColor CardCounterSettings::color(int counterId) const +{ + QColor defaultColor; + + if (counterId < 6) { + // Preserve legacy colors + defaultColor = QColor::fromHsv(counterId * 60, 150, 255); + } else { + // Future-proof support for more counters with pseudo-random colors + int h = (counterId * 37) % 360; + int s = 128 + 64 * qSin((counterId * 97) * 0.1); // 64-192 + int v = 196 + 32 * qSin((counterId * 101) * 0.07); // 164-228 + + defaultColor = QColor::fromHsv(h, s, v); + } + + return settings.value(QString("cards/counters/%1/color").arg(counterId), defaultColor).value(); +} + +QString CardCounterSettings::displayName(int counterId) const +{ + // Currently, card counters name are fixed to A, B, ..., Z, AA, AB, ... + + auto nChars = 1 + counterId / 26; + QString str; + str.resize(nChars); + + for (auto it = str.rbegin(); it != str.rend(); ++it) { + *it = QChar('A' + (counterId) % 26); + counterId /= 26; + } + + return str; +} diff --git a/cockatrice/src/settings/card_counter_settings.h b/cockatrice/src/settings/card_counter_settings.h new file mode 100644 index 000000000..bb3a9bd57 --- /dev/null +++ b/cockatrice/src/settings/card_counter_settings.h @@ -0,0 +1,29 @@ +#ifndef CARD_COUNTER_SETTINGS_H +#define CARD_COUNTER_SETTINGS_H + +#include "settings_manager.h" + +#include + +class QSettings; +class QColor; + +class CardCounterSettings : public SettingsManager +{ + Q_OBJECT + +public: + CardCounterSettings(const QString &settingsPath, QObject *parent = nullptr); + + QColor color(int counterId) const; + + QString displayName(int counterId) const; + +public slots: + void setColor(int counterId, const QColor &color); + +signals: + void colorChanged(int counterId, const QColor &color); +}; + +#endif // CARD_COUNTER_SETTINGS_H diff --git a/cockatrice/src/settings/shortcuts_settings.h b/cockatrice/src/settings/shortcuts_settings.h index 596781cff..dd8a60b68 100644 --- a/cockatrice/src/settings/shortcuts_settings.h +++ b/cockatrice/src/settings/shortcuts_settings.h @@ -284,31 +284,58 @@ private: {"DeckViewContainer/forceStartGameButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Force Start"), parseSequenceString(""), ShortcutGroup::Game_Lobby)}, - {"Player/aCCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Green Counter"), + {"Player/aCCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (F)"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aRCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (F)"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aSCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (F)..."), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aCCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (E)"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aRCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (E)"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aSCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (E)..."), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aCCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter(D)"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aRCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (D)"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aSCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (D)..."), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aCCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (C)"), parseSequenceString("Ctrl+>"), ShortcutGroup::Card_Counters)}, - {"Player/aRCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Green Counter"), + {"Player/aRCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (C)"), parseSequenceString("Ctrl+<"), ShortcutGroup::Card_Counters)}, - {"Player/aSCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Green Counters..."), + {"Player/aSCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (C)..."), parseSequenceString("Ctrl+?"), ShortcutGroup::Card_Counters)}, - {"Player/aCCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Yellow Counter"), + {"Player/aCCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (B)"), parseSequenceString("Ctrl+."), ShortcutGroup::Card_Counters)}, - {"Player/aRCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Yellow Counter"), + {"Player/aRCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (B)"), parseSequenceString("Ctrl+,"), ShortcutGroup::Card_Counters)}, - {"Player/aSCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Yellow Counters..."), + {"Player/aSCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (B)..."), parseSequenceString("Ctrl+/"), ShortcutGroup::Card_Counters)}, - {"Player/aCCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Red Counter"), + {"Player/aCCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (A)"), parseSequenceString("Alt+."), ShortcutGroup::Card_Counters)}, - {"Player/aRCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Red Counter"), + {"Player/aRCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (A)"), parseSequenceString("Alt+,"), ShortcutGroup::Card_Counters)}, - {"Player/aSCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Red Counters..."), + {"Player/aSCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (A)..."), parseSequenceString("Alt+/"), ShortcutGroup::Card_Counters)}, {"Player/aInc", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Life Counter"), diff --git a/doc/carddatabase_v4/cards.xsd b/doc/carddatabase_v4/cards.xsd index 732a5b5ba..9a879302b 100644 --- a/doc/carddatabase_v4/cards.xsd +++ b/doc/carddatabase_v4/cards.xsd @@ -56,6 +56,7 @@ + diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index ba5dd7284..5015ba234 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -33,6 +33,7 @@ set(oracle_SOURCES ../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_4.cpp ../cockatrice/src/settings/cache_settings.cpp ../cockatrice/src/settings/shortcuts_settings.cpp + ../cockatrice/src/settings/card_counter_settings.cpp ../cockatrice/src/settings/card_database_settings.cpp ../cockatrice/src/settings/servers_settings.cpp ../cockatrice/src/settings/settings_manager.cpp