Add local game options (#6669)
Some checks are pending
Build Desktop / Configure (push) Waiting to run
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Debian, DEB, 11) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Debian, DEB, 13) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Debian, DEB, skip, 12) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Fedora, RPM, 43) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Fedora, RPM, skip, 42) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Servatrice_Debian, DEB, yes, skip, 11) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Ubuntu, DEB, 22.04) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (Ubuntu, DEB, 24.04) (push) Blocked by required conditions
Build Desktop / ${{matrix.distro}} ${{matrix.version}} (yes, Arch, skip) (push) Blocked by required conditions
Build Desktop / ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (Ninja, 1, macOS, -macOS14, clang_64, qtimageformats qtmultimedia qtwebsockets, 6.10.*, macos-14, Apple, 14, Release, 1, 15.4) (push) Blocked by required conditions
Build Desktop / ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (Ninja, 1, macOS, -macOS15, clang_64, qtimageformats qtmultimedia qtwebsockets, 6.10.*, macos-15, Apple, 15, Release, 1, 16.4) (push) Blocked by required conditions
Build Desktop / ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (Ninja, 1, macOS, 13, -macOS13_Intel, clang_64, qtimageformats qtmultimedia qtwebsockets, 6.10.*, macos-15-intel, Intel, 13, … (push) Blocked by required conditions
Build Desktop / ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (Ninja, macOS, clang_64, qtimageformats qtmultimedia qtwebsockets, 6.10.*, macos-15, Apple, 15, Debug, 1, 16.4) (push) Blocked by required conditions
Build Desktop / ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }} (Visual Studio 17 2022, x64, 1, Windows, -Win10, win64_msvc2022_64, qtimageformats qtmultimedia qtwebsockets, 6.10.*, windows… (push) Blocked by required conditions
Build Docker Image / amd64 & arm64 (push) Waiting to run

* Add local game options dialog. Introduces LocalGameOptions struct and DlgLocalGameOptions dialog to replace the previous QInputDialog for starting local games. Encapsulates game configuration with a simple interface that prevents parameter explosion as options are added. The dialog provides UI with settings persistence via SettingsCache

* integrate local game options into main window. Replaces QInputDialog with DlgLocalGameOptions in actSinglePlayer(). The startLocalGame() function now accepts LocalGameOptions, enabling configuration of starting life total and spectator visibility in addition to player count. Also adds user documentation for the local game options flow.

* Removed superfluous documentation file

* removed spectator option and moved structure definition

* Now remember settings separately and & shortcuts removed

* re-run checks
This commit is contained in:
DawnFire42 2026-03-12 17:30:01 -04:00 committed by GitHub
parent 20ad9af989
commit aa4592dc9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 199 additions and 12 deletions

View File

@ -39,6 +39,7 @@ set(cockatrice_SOURCES
src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp
src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp
src/interface/widgets/dialogs/dlg_load_remote_deck.cpp src/interface/widgets/dialogs/dlg_load_remote_deck.cpp
src/interface/widgets/dialogs/dlg_local_game_options.cpp
src/interface/widgets/dialogs/dlg_manage_sets.cpp src/interface/widgets/dialogs/dlg_manage_sets.cpp
src/interface/widgets/dialogs/dlg_register.cpp src/interface/widgets/dialogs/dlg_register.cpp
src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp

View File

@ -385,6 +385,13 @@ SettingsCache::SettingsCache()
defaultStartingLifeTotal = settings->value("game/defaultstartinglifetotal", 20).toInt(); defaultStartingLifeTotal = settings->value("game/defaultstartinglifetotal", 20).toInt();
shareDecklistsOnLoad = settings->value("game/sharedecklistsonload", false).toBool(); shareDecklistsOnLoad = settings->value("game/sharedecklistsonload", false).toBool();
rememberGameSettings = settings->value("game/remembergamesettings", true).toBool(); rememberGameSettings = settings->value("game/remembergamesettings", true).toBool();
// Local game settings use "localgameoptions/" prefix to keep them separate
// from server game settings which use "game/" prefix
localGameRememberSettings = settings->value("localgameoptions/remembersettings", false).toBool();
localGameMaxPlayers = settings->value("localgameoptions/maxplayers", 1).toInt();
localGameStartingLifeTotal = settings->value("localgameoptions/startinglifetotal", 20).toInt();
clientID = settings->value("personal/clientid", CLIENT_INFO_NOT_SET).toString(); clientID = settings->value("personal/clientid", CLIENT_INFO_NOT_SET).toString();
clientVersion = settings->value("personal/clientversion", CLIENT_INFO_NOT_SET).toString(); clientVersion = settings->value("personal/clientversion", CLIENT_INFO_NOT_SET).toString();
} }
@ -1242,6 +1249,24 @@ void SettingsCache::setRememberGameSettings(const bool _rememberGameSettings)
settings->setValue("game/remembergamesettings", rememberGameSettings); settings->setValue("game/remembergamesettings", rememberGameSettings);
} }
void SettingsCache::setLocalGameRememberSettings(bool value)
{
localGameRememberSettings = value;
settings->setValue("localgameoptions/remembersettings", value);
}
void SettingsCache::setLocalGameMaxPlayers(int value)
{
localGameMaxPlayers = value;
settings->setValue("localgameoptions/maxplayers", value);
}
void SettingsCache::setLocalGameStartingLifeTotal(int value)
{
localGameStartingLifeTotal = value;
settings->setValue("localgameoptions/startinglifetotal", value);
}
void SettingsCache::setNotifyAboutUpdate(QT_STATE_CHANGED_T _notifyaboutupdate) void SettingsCache::setNotifyAboutUpdate(QT_STATE_CHANGED_T _notifyaboutupdate)
{ {
notifyAboutUpdates = static_cast<bool>(_notifyaboutupdate); notifyAboutUpdates = static_cast<bool>(_notifyaboutupdate);

View File

@ -330,6 +330,12 @@ private:
[[nodiscard]] QString getSafeConfigFilePath(QString configEntry, QString defaultPath) const; [[nodiscard]] QString getSafeConfigFilePath(QString configEntry, QString defaultPath) const;
void loadPaths(); void loadPaths();
bool rememberGameSettings; bool rememberGameSettings;
// Local game settings (separate from server game settings in game/*)
bool localGameRememberSettings;
int localGameMaxPlayers;
int localGameStartingLifeTotal;
QList<ReleaseChannel *> releaseChannels; QList<ReleaseChannel *> releaseChannels;
bool isPortableBuild; bool isPortableBuild;
bool roundCardCorners; bool roundCardCorners;
@ -862,6 +868,18 @@ public:
{ {
return rememberGameSettings; return rememberGameSettings;
} }
[[nodiscard]] bool getLocalGameRememberSettings() const
{
return localGameRememberSettings;
}
[[nodiscard]] int getLocalGameMaxPlayers() const
{
return localGameMaxPlayers;
}
[[nodiscard]] int getLocalGameStartingLifeTotal() const
{
return localGameStartingLifeTotal;
}
[[nodiscard]] int getKeepAlive() const override [[nodiscard]] int getKeepAlive() const override
{ {
return keepalive; return keepalive;
@ -1089,6 +1107,9 @@ public slots:
void setDefaultStartingLifeTotal(const int _defaultStartingLifeTotal); void setDefaultStartingLifeTotal(const int _defaultStartingLifeTotal);
void setShareDecklistsOnLoad(const bool _shareDecklistsOnLoad); void setShareDecklistsOnLoad(const bool _shareDecklistsOnLoad);
void setRememberGameSettings(const bool _rememberGameSettings); void setRememberGameSettings(const bool _rememberGameSettings);
void setLocalGameRememberSettings(bool value);
void setLocalGameMaxPlayers(int value);
void setLocalGameStartingLifeTotal(int value);
void setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value); void setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value);
void setStartupCardUpdateCheckPromptForUpdate(bool value); void setStartupCardUpdateCheckPromptForUpdate(bool value);
void setStartupCardUpdateCheckAlwaysUpdate(bool value); void setStartupCardUpdateCheckAlwaysUpdate(bool value);

View File

@ -0,0 +1,83 @@
#include "dlg_local_game_options.h"
#include "../../../client/settings/cache_settings.h"
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QSpinBox>
#include <QVBoxLayout>
DlgLocalGameOptions::DlgLocalGameOptions(QWidget *parent) : QDialog(parent)
{
numberPlayersLabel = new QLabel(tr("Players:"), this);
numberPlayersEdit = new QSpinBox(this);
numberPlayersEdit->setMinimum(1);
numberPlayersEdit->setMaximum(8);
numberPlayersEdit->setValue(1);
numberPlayersLabel->setBuddy(numberPlayersEdit);
auto *generalGrid = new QGridLayout;
generalGrid->addWidget(numberPlayersLabel, 0, 0);
generalGrid->addWidget(numberPlayersEdit, 0, 1);
generalGroupBox = new QGroupBox(tr("General"), this);
generalGroupBox->setLayout(generalGrid);
startingLifeTotalLabel = new QLabel(tr("Starting life total:"), this);
startingLifeTotalEdit = new QSpinBox(this);
startingLifeTotalEdit->setMinimum(1);
startingLifeTotalEdit->setMaximum(99999);
startingLifeTotalEdit->setValue(20);
startingLifeTotalLabel->setBuddy(startingLifeTotalEdit);
auto *gameSetupGrid = new QGridLayout;
gameSetupGrid->addWidget(startingLifeTotalLabel, 0, 0);
gameSetupGrid->addWidget(startingLifeTotalEdit, 0, 1);
gameSetupOptionsGroupBox = new QGroupBox(tr("Game setup options"), this);
gameSetupOptionsGroupBox->setLayout(gameSetupGrid);
rememberSettingsCheckBox = new QCheckBox(tr("Remember settings"), this);
buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgLocalGameOptions::actOK);
connect(buttonBox, &QDialogButtonBox::rejected, this, &DlgLocalGameOptions::reject);
auto *mainLayout = new QVBoxLayout;
mainLayout->addWidget(generalGroupBox);
mainLayout->addWidget(gameSetupOptionsGroupBox);
mainLayout->addWidget(rememberSettingsCheckBox);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
rememberSettingsCheckBox->setChecked(SettingsCache::instance().getLocalGameRememberSettings());
if (rememberSettingsCheckBox->isChecked()) {
numberPlayersEdit->setValue(SettingsCache::instance().getLocalGameMaxPlayers());
startingLifeTotalEdit->setValue(SettingsCache::instance().getLocalGameStartingLifeTotal());
}
setWindowTitle(tr("Local game options"));
setFixedHeight(sizeHint().height());
numberPlayersEdit->setFocus();
}
void DlgLocalGameOptions::actOK()
{
SettingsCache::instance().setLocalGameRememberSettings(rememberSettingsCheckBox->isChecked());
if (rememberSettingsCheckBox->isChecked()) {
SettingsCache::instance().setLocalGameMaxPlayers(numberPlayersEdit->value());
SettingsCache::instance().setLocalGameStartingLifeTotal(startingLifeTotalEdit->value());
}
accept();
}
LocalGameOptions DlgLocalGameOptions::getOptions() const
{
return LocalGameOptions{
.numberPlayers = numberPlayersEdit->value(),
.startingLifeTotal = startingLifeTotalEdit->value(),
};
}

View File

@ -0,0 +1,53 @@
/**
* @file dlg_local_game_options.h
* @ingroup RoomDialogs
* @brief Dialog for configuring local game options.
*
* Provides a user interface for setting up local games with configurable
* number of players and starting life total.
*/
#ifndef DLG_LOCAL_GAME_OPTIONS_H
#define DLG_LOCAL_GAME_OPTIONS_H
#include <QDialog>
struct LocalGameOptions
{
int numberPlayers = 1;
int startingLifeTotal = 20;
};
class QCheckBox;
class QDialogButtonBox;
class QGroupBox;
class QLabel;
class QSpinBox;
class DlgLocalGameOptions : public QDialog
{
Q_OBJECT
public:
explicit DlgLocalGameOptions(QWidget *parent = nullptr);
[[nodiscard]] LocalGameOptions getOptions() const;
private slots:
void actOK();
private:
QGroupBox *generalGroupBox;
QGroupBox *gameSetupOptionsGroupBox;
QLabel *numberPlayersLabel;
QSpinBox *numberPlayersEdit;
QLabel *startingLifeTotalLabel;
QSpinBox *startingLifeTotalEdit;
QCheckBox *rememberSettingsCheckBox;
QDialogButtonBox *buttonBox;
};
#endif // DLG_LOCAL_GAME_OPTIONS_H

View File

@ -27,6 +27,7 @@
#include "../interface/widgets/dialogs/dlg_forgot_password_challenge.h" #include "../interface/widgets/dialogs/dlg_forgot_password_challenge.h"
#include "../interface/widgets/dialogs/dlg_forgot_password_request.h" #include "../interface/widgets/dialogs/dlg_forgot_password_request.h"
#include "../interface/widgets/dialogs/dlg_forgot_password_reset.h" #include "../interface/widgets/dialogs/dlg_forgot_password_reset.h"
#include "../interface/widgets/dialogs/dlg_local_game_options.h"
#include "../interface/widgets/dialogs/dlg_manage_sets.h" #include "../interface/widgets/dialogs/dlg_manage_sets.h"
#include "../interface/widgets/dialogs/dlg_register.h" #include "../interface/widgets/dialogs/dlg_register.h"
#include "../interface/widgets/dialogs/dlg_settings.h" #include "../interface/widgets/dialogs/dlg_settings.h"
@ -49,7 +50,6 @@
#include <QDesktopServices> #include <QDesktopServices>
#include <QFile> #include <QFile>
#include <QFileDialog> #include <QFileDialog>
#include <QInputDialog>
#include <QMenu> #include <QMenu>
#include <QMenuBar> #include <QMenuBar>
#include <QMessageBox> #include <QMessageBox>
@ -225,16 +225,15 @@ void MainWindow::actDisconnect()
void MainWindow::actSinglePlayer() void MainWindow::actSinglePlayer()
{ {
bool ok; DlgLocalGameOptions dlg(this);
int numberPlayers = if (dlg.exec() != QDialog::Accepted) {
QInputDialog::getInt(this, tr("Number of players"), tr("Please enter the number of players."), 1, 1, 8, 1, &ok);
if (!ok)
return; return;
}
startLocalGame(numberPlayers); startLocalGame(dlg.getOptions());
} }
void MainWindow::startLocalGame(int numberPlayers) void MainWindow::startLocalGame(const LocalGameOptions &options)
{ {
aConnect->setEnabled(false); aConnect->setEnabled(false);
aRegister->setEnabled(false); aRegister->setEnabled(false);
@ -248,7 +247,7 @@ void MainWindow::startLocalGame(int numberPlayers)
QList<AbstractClient *> localClients; QList<AbstractClient *> localClients;
localClients.append(mainClient); localClients.append(mainClient);
for (int i = 0; i < numberPlayers - 1; ++i) { for (int i = 0; i < options.numberPlayers - 1; ++i) {
LocalServerInterface *slaveLsi = localServer->newConnection(); LocalServerInterface *slaveLsi = localServer->newConnection();
LocalClient *slaveClient = LocalClient *slaveClient =
new LocalClient(slaveLsi, tr("Player %1").arg(i + 2), SettingsCache::instance().getClientID(), this); new LocalClient(slaveLsi, tr("Player %1").arg(i + 2), SettingsCache::instance().getClientID(), this);
@ -257,7 +256,8 @@ void MainWindow::startLocalGame(int numberPlayers)
tabSupervisor->startLocal(localClients); tabSupervisor->startLocal(localClients);
Command_CreateGame createCommand; Command_CreateGame createCommand;
createCommand.set_max_players(static_cast<google::protobuf::uint32>(numberPlayers)); createCommand.set_max_players(static_cast<google::protobuf::uint32>(options.numberPlayers));
createCommand.set_starting_life_total(options.startingLifeTotal);
mainClient->sendCommand(LocalClient::prepareRoomCommand(createCommand, 0)); mainClient->sendCommand(LocalClient::prepareRoomCommand(createCommand, 0));
} }
@ -913,7 +913,9 @@ MainWindow::MainWindow(QWidget *parent)
void MainWindow::startupConfigCheck() void MainWindow::startupConfigCheck()
{ {
if (SettingsCache::instance().debug().getLocalGameOnStartup()) { if (SettingsCache::instance().debug().getLocalGameOnStartup()) {
startLocalGame(SettingsCache::instance().debug().getLocalGamePlayerCount()); LocalGameOptions options;
options.numberPlayers = SettingsCache::instance().debug().getLocalGamePlayerCount();
startLocalGame(options);
} }
if (SettingsCache::instance().getCheckUpdatesOnStartup()) { if (SettingsCache::instance().getCheckUpdatesOnStartup()) {

View File

@ -25,6 +25,8 @@
#ifndef WINDOW_H #ifndef WINDOW_H
#define WINDOW_H #define WINDOW_H
#include "widgets/dialogs/dlg_local_game_options.h"
#include <QList> #include <QList>
#include <QMainWindow> #include <QMainWindow>
#include <QMessageBox> #include <QMessageBox>
@ -137,7 +139,7 @@ private:
void createCardUpdateProcess(bool background = false); void createCardUpdateProcess(bool background = false);
void exitCardDatabaseUpdate(); void exitCardDatabaseUpdate();
void startLocalGame(int numberPlayers); void startLocalGame(const LocalGameOptions &options);
QList<QMenu *> tabMenus; QList<QMenu *> tabMenus;
QMenu *cockatriceMenu, *dbMenu, *tabsMenu, *helpMenu, *trayIconMenu; QMenu *cockatriceMenu, *dbMenu, *tabsMenu, *helpMenu, *trayIconMenu;

View File

@ -1,5 +1,5 @@
@page user_reference User Reference @page user_reference User Reference
## Deck Management ## Deck Management
- @subpage creating_decks - @subpage creating_decks