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