diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 2b9a10113..0650ae9f4 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -229,6 +229,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/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp index 502662c14..306558b5c 100644 --- a/cockatrice/src/dialogs/dlg_settings.cpp +++ b/cockatrice/src/dialogs/dlg_settings.cpp @@ -11,13 +11,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 @@ -41,7 +45,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" @@ -441,6 +445,55 @@ 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, pushButton]() { + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + + auto newColor = QColorDialog::getColor(cardCounterSettings.color(index), pushButton); + 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); + + cardCountersNameWarning = new QLabel; + cardCountersNameWarning->setWordWrap(true); + cardCountersLayout->addWidget(cardCountersNameWarning); + + cardCountersGroupBox = new QGroupBox; + cardCountersGroupBox->setLayout(cardCountersLayout); + // Hand layout horizontalHandCheckBox.setChecked(settings.getHorizontalHand()); connect(&horizontalHandCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setHorizontalHand); @@ -488,6 +541,7 @@ AppearanceSettingsPage::AppearanceSettingsPage() mainLayout->addWidget(themeGroupBox); mainLayout->addWidget(menuGroupBox); mainLayout->addWidget(cardsGroupBox); + mainLayout->addWidget(cardCountersGroupBox); mainLayout->addWidget(handGroupBox); mainLayout->addWidget(tableGroupBox); mainLayout->addStretch(); @@ -576,6 +630,15 @@ void AppearanceSettingsPage::retranslateUi() cardViewExpandedRowsMaxLabel.setText(tr("Maximum expanded height for card view window:")); cardViewExpandedRowsMaxBox.setSuffix(tr(" rows")); + cardCountersGroupBox->setTitle(tr("Card counters")); + cardCountersNameWarning->setText( + tr("Note: The names can't be changed and might not match the actual colors.")); + + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); + for (int index = 0; index < cardCounterNames.size(); ++index) { + cardCounterNames[index]->setText(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 6c9b8b062..e72d817cd 100644 --- a/cockatrice/src/dialogs/dlg_settings.h +++ b/cockatrice/src/dialogs/dlg_settings.h @@ -125,6 +125,9 @@ private: QGroupBox *cardsGroupBox; QGroupBox *handGroupBox; QGroupBox *tableGroupBox; + QGroupBox *cardCountersGroupBox; + QLabel *cardCountersNameWarning; + 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/player/player.cpp b/cockatrice/src/game/player/player.cpp index 160d8d336..f1fa4c72c 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" @@ -534,7 +535,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); @@ -883,19 +884,16 @@ 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(); 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")); @@ -951,24 +949,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)); } @@ -3875,15 +3882,19 @@ void Player::updateCardMenu(const CardItem *card) cardMenu->addAction(aSelectAll); cardMenu->addAction(aSelectRow); + cardMenu->addSeparator(); + auto *cardCountersMenu = new QMenu(tr("Other counters")); for (int i = 0; i < aAddCounter.size(); ++i) { - cardMenu->addSeparator(); - cardMenu->addAction(aAddCounter[i]); + auto *targetMenu = i < 3 ? cardMenu : cardCountersMenu; + targetMenu->addSeparator(); + targetMenu->addAction(aAddCounter[i]); if (card->getCounters().contains(i)) { - cardMenu->addAction(aRemoveCounter[i]); + targetMenu->addAction(aRemoveCounter[i]); } - cardMenu->addAction(aSetCounter[i]); + targetMenu->addAction(aSetCounter[i]); } cardMenu->addSeparator(); + cardMenu->addMenu(cardCountersMenu); } else if (card->getZone()->getName() == "stack") { // Card is on the stack if (canModifyCard) { diff --git a/cockatrice/src/server/message_log_widget.cpp b/cockatrice/src/server/message_log_widget.cpp index 0d21be045..37f6e29e6 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" @@ -608,28 +609,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 %n %2 counter(s) on %3 (now %4).", "", 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 %n %2 counter(s) from %3 (now %4).", "", delta); } + QLocale locale; + auto &cardCounterSettings = SettingsCache::instance().cardCounters(); appendHtmlServerMessage(finalStr.arg(sanitizeHtml(player->getName())) - .arg("" + QString::number(delta) + "") - .arg(colorStr) + .arg(locale.toLower(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 ba25590f3..cb5025540 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(); @@ -1401,3 +1405,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 78993ce4a..4555681e0 100644 --- a/cockatrice/src/settings/cache_settings.h +++ b/cockatrice/src/settings/cache_settings.h @@ -43,6 +43,7 @@ constexpr int NETWORK_CACHE_SIZE_MAX = 1024 * 1024; // 1 TB #define DEFAULT_FONT_SIZE 12 class QSettings; +class CardCounterSettings; class SettingsCache : public QObject { @@ -103,6 +104,7 @@ private: RecentsSettings *recentsSettings; CardOverrideSettings *cardOverrideSettings; DebugSettings *debugSettings; + CardCounterSettings *cardCounterSettings; QByteArray mainWindowGeometry; QByteArray tokenDialogGeometry; @@ -762,6 +764,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..027844f24 --- /dev/null +++ b/cockatrice/src/settings/card_counter_settings.cpp @@ -0,0 +1,61 @@ +#include "card_counter_settings.h" + +#include +#include + +CardCounterSettings::CardCounterSettings(const QString &settingsPath, QObject *parent) + : QObject(parent), m_settings(new QSettings(settingsPath + "global.ini", QSettings::IniFormat)) +{ +} + +void CardCounterSettings::setColor(int counterId, const QColor &color) +{ + QString key = QString("cards/counters/%1/color").arg(counterId); + + if (m_settings->value(key).value() == color) + return; + + m_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 * sin((counterId * 97) * 0.1); // 64-192 + int v = 196 + 32 * sin((counterId * 101) * 0.07); // 164-228 + + defaultColor = QColor::fromHsv(h, s, v); + } + + return m_settings->value(QString("cards/counters/%1/color").arg(counterId), defaultColor).value(); +} + +QString CardCounterSettings::displayName(int counterId) const +{ + // These are hardcoded for now. They should become configurable at some + // point. + switch (counterId) { + case 0: + return tr("Red"); + case 1: + return tr("Yellow"); + case 2: + return tr("Green"); + case 3: + return tr("Cyan"); + case 4: + return tr("Purple"); + case 5: + return tr("Magenta"); + default: + return QString(tr("Custom %1")).arg(counterId - 5); + } +} diff --git a/cockatrice/src/settings/card_counter_settings.h b/cockatrice/src/settings/card_counter_settings.h new file mode 100644 index 000000000..545cfc95b --- /dev/null +++ b/cockatrice/src/settings/card_counter_settings.h @@ -0,0 +1,30 @@ +#ifndef CARD_COUNTER_SETTINGS_H +#define CARD_COUNTER_SETTINGS_H + +#include + +class QSettings; +class QColor; + +class CardCounterSettings : public QObject +{ + 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); + +private: + QSettings *m_settings; +}; + +#endif // CARD_COUNTER_SETTINGS_H diff --git a/cockatrice/src/settings/shortcuts_settings.h b/cockatrice/src/settings/shortcuts_settings.h index 595932623..f1d8defc5 100644 --- a/cockatrice/src/settings/shortcuts_settings.h +++ b/cockatrice/src/settings/shortcuts_settings.h @@ -274,6 +274,33 @@ private: ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Toggle Sideboard Lock"), parseSequenceString("Ctrl+Shift+B"), ShortcutGroup::Game_Lobby)}, + {"Player/aCCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Magenta Counter"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aRCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Magenta Counter"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aSCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Magenta Counters..."), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aCCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Purple Counter"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aRCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Purple Counter"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aSCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Purple Counters..."), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aCCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Cyan Counter"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aRCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Cyan Counter"), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, + {"Player/aSCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Cyan Counters..."), + parseSequenceString(""), + ShortcutGroup::Card_Counters)}, {"Player/aCCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Green Counter"), parseSequenceString("Ctrl+>"), ShortcutGroup::Card_Counters)}, diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 130dfdaab..2d79ed5d6 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -28,6 +28,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