From 17a1f8cf783e3aa922cd3ad79ef81886577bef55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Tue, 4 Aug 2026 22:51:22 +0200 Subject: [PATCH] [Client] Add shuffle players option and turn order dialog Adds the 'Shuffle players at start' checkbox to game creation and a host-only 'Set turn order...' action that opens a dialog to reorder or randomize the seats before the game starts. Seating changes are shown in the game's message log. --- cockatrice/CMakeLists.txt | 1 + .../game_graphics/log/message_log_widget.cpp | 14 +++ .../game_graphics/log/message_log_widget.h | 1 + .../widgets/dialogs/dlg_create_game.cpp | 9 ++ .../widgets/dialogs/dlg_create_game.h | 1 + .../widgets/dialogs/dlg_turn_order.cpp | 114 ++++++++++++++++++ .../widgets/dialogs/dlg_turn_order.h | 46 +++++++ .../src/interface/widgets/tabs/tab_game.cpp | 41 +++++++ .../src/interface/widgets/tabs/tab_game.h | 4 +- .../libcockatrice/game/game_event_handler.cpp | 48 ++++++++ .../libcockatrice/game/game_event_handler.h | 16 +++ .../game/player/player_manager.h | 5 + .../interface_game_settings_provider.h | 1 + .../libcockatrice/settings/game_settings.cpp | 10 ++ .../libcockatrice/settings/game_settings.h | 2 + 15 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 cockatrice/src/interface/widgets/dialogs/dlg_turn_order.cpp create mode 100644 cockatrice/src/interface/widgets/dialogs/dlg_turn_order.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 642d998a0..21bf841f0 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -56,6 +56,7 @@ set(cockatrice_SOURCES src/interface/widgets/dialogs/dlg_settings.cpp src/interface/widgets/dialogs/dlg_startup_card_check.cpp src/interface/widgets/dialogs/dlg_tip_of_the_day.cpp + src/interface/widgets/dialogs/dlg_turn_order.cpp src/interface/widgets/dialogs/dlg_update.cpp src/interface/widgets/dialogs/dlg_view_log.cpp src/interface/widgets/dialogs/override_printing_warning.cpp diff --git a/cockatrice/src/game_graphics/log/message_log_widget.cpp b/cockatrice/src/game_graphics/log/message_log_widget.cpp index 046a1ae90..8b351e022 100644 --- a/cockatrice/src/game_graphics/log/message_log_widget.cpp +++ b/cockatrice/src/game_graphics/log/message_log_widget.cpp @@ -605,6 +605,20 @@ void MessageLogWidget::logReverseTurn(PlayerLogic *player, bool reversed) .arg(reversed ? tr("reversed") : tr("normal"))); } +void MessageLogWidget::logTurnOrderChanged(QStringList playerNames, bool randomized) +{ + QStringList sanitizedNames; + for (const QString &name : playerNames) { + sanitizedNames.append(sanitizeHtml(name)); + } + const QString order = sanitizedNames.join(" -> "); + if (randomized) { + appendHtmlServerMessage(tr("Randomized turn order: %1.").arg(order)); + } else { + appendHtmlServerMessage(tr("New turn order set: %1.").arg(order)); + } +} + void MessageLogWidget::logRollDie(PlayerLogic *player, int sides, const QList &rolls) { if (rolls.length() == 1) { diff --git a/cockatrice/src/game_graphics/log/message_log_widget.h b/cockatrice/src/game_graphics/log/message_log_widget.h index 5642a76e9..5632f5af8 100644 --- a/cockatrice/src/game_graphics/log/message_log_widget.h +++ b/cockatrice/src/game_graphics/log/message_log_widget.h @@ -94,6 +94,7 @@ public slots: int amount, bool isLentToAnotherPlayer); void logReverseTurn(PlayerLogic *player, bool reversed); + void logTurnOrderChanged(QStringList playerNames, bool randomized); void logRollDie(PlayerLogic *player, int sides, const QList &rolls); void logSay(PlayerLogic *player, QString message); void logSetActivePhase(int phase); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_create_game.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_create_game.cpp index 104b695e6..c730ad21c 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_create_game.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_create_game.cpp @@ -116,12 +116,15 @@ void DlgCreateGame::sharedCtor() shareDecklistsOnLoadCheckBox = new QCheckBox(tr("Open decklists in lobby")); + shufflePlayersCheckBox = new QCheckBox(tr("Shuffle players at start")); + createGameAsJudgeCheckBox = new QCheckBox(tr("Create game as judge")); auto *gameSetupOptionsLayout = new QGridLayout; gameSetupOptionsLayout->addWidget(startingLifeTotalLabel, 0, 0); gameSetupOptionsLayout->addWidget(startingLifeTotalEdit, 0, 1); gameSetupOptionsLayout->addWidget(shareDecklistsOnLoadCheckBox, 1, 0); + gameSetupOptionsLayout->addWidget(shufflePlayersCheckBox, 1, 1); if (room && room->getUserInfo()->user_level() & ServerInfo_User::IsJudge) { gameSetupOptionsLayout->addWidget(createGameAsJudgeCheckBox, 2, 0); } else { @@ -184,6 +187,7 @@ DlgCreateGame::DlgCreateGame(TabRoom *_room, const QMap &_gameType createGameAsSpectatorCheckBox->setChecked(SettingsCache::instance().game().getCreateGameAsSpectator()); startingLifeTotalEdit->setValue(SettingsCache::instance().game().getDefaultStartingLifeTotal()); shareDecklistsOnLoadCheckBox->setChecked(SettingsCache::instance().game().getShareDecklistsOnLoad()); + shufflePlayersCheckBox->setChecked(SettingsCache::instance().game().getShufflePlayers()); if (!rememberGameSettings->isChecked()) { actReset(); @@ -217,6 +221,7 @@ DlgCreateGame::DlgCreateGame(const ServerInfo_Game &gameInfo, const QMapsetEnabled(false); startingLifeTotalEdit->setEnabled(false); shareDecklistsOnLoadCheckBox->setEnabled(false); + shufflePlayersCheckBox->setEnabled(false); descriptionEdit->setText(QString::fromStdString(gameInfo.description())); maxPlayersEdit->setValue(gameInfo.max_players()); @@ -227,6 +232,7 @@ DlgCreateGame::DlgCreateGame(const ServerInfo_Game &gameInfo, const QMapsetChecked(gameInfo.spectators_can_chat()); spectatorsSeeEverythingCheckBox->setChecked(gameInfo.spectators_omniscient()); shareDecklistsOnLoadCheckBox->setChecked(gameInfo.share_decklists_on_load()); + shufflePlayersCheckBox->setChecked(gameInfo.shuffle_players()); QSet types; for (int i = 0; i < gameInfo.game_types_size(); ++i) { @@ -264,6 +270,7 @@ void DlgCreateGame::actReset() startingLifeTotalEdit->setValue(20); shareDecklistsOnLoadCheckBox->setChecked(false); + shufflePlayersCheckBox->setChecked(false); createGameAsJudgeCheckBox->setChecked(false); QMapIterator gameTypeCheckBoxIterator(gameTypeCheckBoxes); @@ -294,6 +301,7 @@ void DlgCreateGame::actOK() cmd.set_join_as_spectator(createGameAsSpectatorCheckBox->isChecked()); cmd.set_starting_life_total(startingLifeTotalEdit->value()); cmd.set_share_decklists_on_load(shareDecklistsOnLoadCheckBox->isChecked()); + cmd.set_shuffle_players(shufflePlayersCheckBox->isChecked()); auto _gameTypes = QString(); QMapIterator gameTypeCheckBoxIterator(gameTypeCheckBoxes); @@ -318,6 +326,7 @@ void DlgCreateGame::actOK() SettingsCache::instance().game().setCreateGameAsSpectator(createGameAsSpectatorCheckBox->isChecked()); SettingsCache::instance().game().setDefaultStartingLifeTotal(startingLifeTotalEdit->value()); SettingsCache::instance().game().setShareDecklistsOnLoad(shareDecklistsOnLoadCheckBox->isChecked()); + SettingsCache::instance().game().setShufflePlayers(shufflePlayersCheckBox->isChecked()); SettingsCache::instance().game().setGameTypes(_gameTypes); } PendingCommand *pend = room->prepareRoomCommand(cmd); diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_create_game.h b/cockatrice/src/interface/widgets/dialogs/dlg_create_game.h index 71b09997c..58356c9b6 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_create_game.h +++ b/cockatrice/src/interface/widgets/dialogs/dlg_create_game.h @@ -51,6 +51,7 @@ private: QCheckBox *spectatorsAllowedCheckBox, *spectatorsNeedPasswordCheckBox, *spectatorsCanTalkCheckBox, *spectatorsSeeEverythingCheckBox, *createGameAsJudgeCheckBox, *createGameAsSpectatorCheckBox; QCheckBox *shareDecklistsOnLoadCheckBox; + QCheckBox *shufflePlayersCheckBox; QDialogButtonBox *buttonBox; QPushButton *clearButton; QCheckBox *rememberGameSettings; diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_turn_order.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_turn_order.cpp new file mode 100644 index 000000000..af0a264d7 --- /dev/null +++ b/cockatrice/src/interface/widgets/dialogs/dlg_turn_order.cpp @@ -0,0 +1,114 @@ +#include "dlg_turn_order.h" + +#include +#include +#include +#include +#include +#include +#include + +DlgTurnOrder::DlgTurnOrder(const QStringList &_playerNames, QWidget *parent) + : QDialog(parent), randomizeRequested(false) +{ + listWidget = new QListWidget(this); + listWidget->addItems(_playerNames); + listWidget->setCurrentRow(0); + connect(listWidget, &QListWidget::currentRowChanged, this, &DlgTurnOrder::selectionChanged); + + moveUpButton = new QPushButton(tr("Move up"), this); + connect(moveUpButton, &QPushButton::clicked, this, &DlgTurnOrder::actMoveUp); + moveDownButton = new QPushButton(tr("Move down"), this); + connect(moveDownButton, &QPushButton::clicked, this, &DlgTurnOrder::actMoveDown); + randomizeButton = new QPushButton(tr("Randomize"), this); + connect(randomizeButton, &QPushButton::clicked, this, &DlgTurnOrder::actRandomize); + + auto *buttonColumn = new QVBoxLayout; + buttonColumn->addWidget(moveUpButton); + buttonColumn->addWidget(moveDownButton); + buttonColumn->addWidget(randomizeButton); + buttonColumn->addStretch(); + + auto *listLayout = new QHBoxLayout; + listLayout->addWidget(listWidget); + listLayout->addLayout(buttonColumn); + + auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgTurnOrder::actOk); + connect(buttonBox, &QDialogButtonBox::rejected, this, &DlgTurnOrder::reject); + + auto *mainLayout = new QVBoxLayout; + mainLayout->addLayout(listLayout); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + updateButtons(); + setWindowTitle(tr("Set turn order")); + setMinimumWidth(300); +} + +QStringList DlgTurnOrder::order() const +{ + QStringList playerNames; + for (int i = 0; i < listWidget->count(); ++i) { + playerNames.append(listWidget->item(i)->text()); + } + return playerNames; +} + +void DlgTurnOrder::moveSelected(int delta) +{ + const int row = listWidget->currentRow(); + const int newRow = row + delta; + if (row < 0 || newRow < 0 || newRow >= listWidget->count()) { + return; + } + + QListWidgetItem *item = listWidget->takeItem(row); + listWidget->insertItem(newRow, item); + listWidget->setCurrentRow(newRow); + randomizeRequested = false; + updateButtons(); +} + +void DlgTurnOrder::updateButtons() +{ + const int row = listWidget->currentRow(); + const int count = listWidget->count(); + moveUpButton->setEnabled(row > 0); + moveDownButton->setEnabled(row >= 0 && row < count - 1); +} + +void DlgTurnOrder::actMoveUp() +{ + moveSelected(-1); +} + +void DlgTurnOrder::actMoveDown() +{ + moveSelected(1); +} + +void DlgTurnOrder::actRandomize() +{ + QList items; + while (listWidget->count() > 0) { + items.append(listWidget->takeItem(0)); + } + while (!items.isEmpty()) { + const int index = QRandomGenerator::global()->bounded(items.size()); + listWidget->addItem(items.takeAt(index)); + } + randomizeRequested = true; + updateButtons(); +} + +void DlgTurnOrder::actOk() +{ + accept(); +} + +void DlgTurnOrder::selectionChanged() +{ + updateButtons(); +} diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_turn_order.h b/cockatrice/src/interface/widgets/dialogs/dlg_turn_order.h new file mode 100644 index 000000000..1dc66316c --- /dev/null +++ b/cockatrice/src/interface/widgets/dialogs/dlg_turn_order.h @@ -0,0 +1,46 @@ +/** + * @file dlg_turn_order.h + * @ingroup GameDialogs + */ +//! \todo Document this file. + +#ifndef DLG_TURN_ORDER_H +#define DLG_TURN_ORDER_H + +#include +#include + +class QListWidget; +class QPushButton; + +class DlgTurnOrder : public QDialog +{ + Q_OBJECT +private: + QListWidget *listWidget; + QPushButton *moveUpButton; + QPushButton *moveDownButton; + QPushButton *randomizeButton; + bool randomizeRequested; + + void moveSelected(int delta); + void updateButtons(); + +private slots: + void actMoveUp(); + void actMoveDown(); + void actRandomize(); + void actOk(); + void selectionChanged(); + +public: + explicit DlgTurnOrder(const QStringList &_playerNames, QWidget *parent = nullptr); + + [[nodiscard]] bool randomize() const + { + return randomizeRequested; + } + [[nodiscard]] QStringList order() const; +}; + +#endif diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.cpp b/cockatrice/src/interface/widgets/tabs/tab_game.cpp index 9e724727f..855ca84dd 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_game.cpp @@ -20,6 +20,7 @@ #include "../interface/widgets/cards/card_info_frame_widget.h" #include "../interface/widgets/dialogs/dlg_create_game.h" #include "../interface/widgets/dialogs/dlg_invite_to_game.h" +#include "../interface/widgets/dialogs/dlg_turn_order.h" #include "../interface/widgets/server/game_link.h" #include "../interface/widgets/server/user/user_list_manager.h" #include "../interface/widgets/utility/completer_utils.h" @@ -259,6 +260,8 @@ void TabGame::connectMessageLogToGameEventHandler() connect(game->getGameEventHandler(), &GameEventHandler::logTurnReversed, messageLog, &MessageLogWidget::logReverseTurn); + connect(game->getGameEventHandler(), &GameEventHandler::logTurnOrderChanged, messageLog, + &MessageLogWidget::logTurnOrderChanged); connect(game->getGameEventHandler(), &GameEventHandler::logConcede, messageLog, &MessageLogWidget::logConcede); connect(game->getGameEventHandler(), &GameEventHandler::logUnconcede, messageLog, &MessageLogWidget::logUnconcede); @@ -369,6 +372,9 @@ void TabGame::retranslateUi() if (aReverseTurn) { aReverseTurn->setText(tr("Reverse turn order")); } + if (aTurnOrder) { + aTurnOrder->setText(tr("&Set turn order...")); + } if (aRemoveLocalArrows) { aRemoveLocalArrows->setText(tr("&Remove all local arrows")); } @@ -625,6 +631,25 @@ void TabGame::actConcede() } } +void TabGame::actTurnOrder() +{ + if (!game->isHost() || game->getGameMetaInfo()->started()) { + return; + } + + QStringList playerNames; + for (PlayerLogic *player : game->getPlayerManager()->getPlayers()) { + playerNames.append(player->getPlayerInfo()->getName()); + } + + DlgTurnOrder dlg(playerNames, this); + if (dlg.exec() != QDialog::Accepted) { + return; + } + + game->getGameEventHandler()->handleSetPlayerOrder(dlg.order(), dlg.randomize()); +} + /** * Confirms the leave game and sends the leave game command, if applicable. * @@ -836,6 +861,15 @@ void TabGame::processPlayerLeave(PlayerLogic *leavingPlayer) } } + // A shuffled local player has their seat changed, so the deck view container + // keyed by their previous id must be cleaned up alongside the leaving player. + int leavingPlayerId = leavingPlayer->getPlayerInfo()->getId(); + if (deckViewContainers.contains(leavingPlayerId)) { + TabbedDeckViewContainer *deckView = deckViewContainers.take(leavingPlayerId); + deckViewContainerLayout->removeWidget(deckView); + deckView->deleteLater(); + } + scene->removePlayer(leavingPlayer); } @@ -1090,6 +1124,11 @@ void TabGame::createMenuItems() connect(aNextTurn, &QAction::triggered, game->getGameEventHandler(), &GameEventHandler::handleNextTurn); aReverseTurn = new QAction(this); connect(aReverseTurn, &QAction::triggered, game->getGameEventHandler(), &GameEventHandler::handleReverseTurn); + aTurnOrder = new QAction(this); + connect(aTurnOrder, &QAction::triggered, this, &TabGame::actTurnOrder); + if (!game->isHost() || game->getGameMetaInfo()->started()) { + aTurnOrder->setVisible(false); + } aRemoveLocalArrows = new QAction(this); connect(aRemoveLocalArrows, &QAction::triggered, this, &TabGame::actRemoveLocalArrows); aRotateViewCW = new QAction(this); @@ -1135,6 +1174,7 @@ void TabGame::createMenuItems() gameMenu->addMenu(phasesMenu); gameMenu->addAction(aNextTurn); gameMenu->addAction(aReverseTurn); + gameMenu->addAction(aTurnOrder); gameMenu->addSeparator(); gameMenu->addAction(aRemoveLocalArrows); gameMenu->addAction(aRotateViewCW); @@ -1163,6 +1203,7 @@ void TabGame::createReplayMenuItems() aNextPhaseAction = nullptr; aNextTurn = nullptr; aReverseTurn = nullptr; + aTurnOrder = nullptr; aRemoveLocalArrows = nullptr; aRotateViewCW = nullptr; aRotateViewCCW = nullptr; diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.h b/cockatrice/src/interface/widgets/tabs/tab_game.h index 7bc52202f..7d044ec28 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.h +++ b/cockatrice/src/interface/widgets/tabs/tab_game.h @@ -94,7 +94,8 @@ private: QMenu *gameMenu, *viewMenu; TearOffMenu *phasesMenu; QAction *aGameInfo, *aConcede, *aCopyGameLink, *aLeaveGame, *aNextPhase, *aNextPhaseAction, *aNextTurn, - *aReverseTurn, *aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW, *aResetLayout, *aResetReplayLayout; + *aReverseTurn, *aTurnOrder, *aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW, *aResetLayout, + *aResetReplayLayout; QAction *aFocusChat; QAction *aInviteToGame = nullptr; QList phaseActions; @@ -162,6 +163,7 @@ private slots: void actInviteToGame(); void actConcede(); void actCopyGameLink(); + void actTurnOrder(); void actRemoveLocalArrows(); void actRotateViewCW(); void actRotateViewCCW(); diff --git a/libcockatrice_game/libcockatrice/game/game_event_handler.cpp b/libcockatrice_game/libcockatrice/game/game_event_handler.cpp index 856475dfa..4d45a1f21 100644 --- a/libcockatrice_game/libcockatrice/game/game_event_handler.cpp +++ b/libcockatrice_game/libcockatrice/game/game_event_handler.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -189,6 +192,9 @@ void GameEventHandler::processGameEventContainer(const GameEventContainer &cont, case GameEvent::REVERSE_TURN: eventReverseTurn(event.GetExtension(Event_ReverseTurn::ext), playerId, context); break; + case GameEvent::PLAYER_ORDER_CHANGED: + eventPlayerOrderChanged(event.GetExtension(Event_PlayerOrderChanged::ext), playerId, context); + break; default: { PlayerLogic *player = game->getPlayerManager()->getPlayers().value(playerId, 0); @@ -215,6 +221,16 @@ void GameEventHandler::handleReverseTurn() sendGameCommand(Command_ReverseTurn()); } +void GameEventHandler::handleSetPlayerOrder(const QList &playerNames, bool randomize) +{ + Command_SetPlayerOrder command; + command.set_randomize(randomize); + for (const QString &name : playerNames) { + command.add_player_names(name.toStdString()); + } + sendGameCommand(command); +} + void GameEventHandler::handleActiveLocalPlayerConceded() { sendGameCommand(Command_Concede()); @@ -377,6 +393,27 @@ void GameEventHandler::eventGameStateChanged(const Event_GameStateChanged &event { const int playerListSize = event.player_list_size(); + if (event.has_local_player_id()) { + game->getPlayerManager()->setLocalPlayerId(event.local_player_id()); + } + + QSet authoritativePlayerIds; + for (int i = 0; i < playerListSize; ++i) { + const ServerInfo_PlayerProperties &prop = event.player_list(i).properties(); + if (!prop.spectator()) { + authoritativePlayerIds.insert(prop.player_id()); + } + } + + // Players whose seats were shuffled away from the previous game are no longer + // part of the authoritative list and must be removed. + for (int stalePlayerId : game->getPlayerManager()->getPlayers().keys()) { + if (!authoritativePlayerIds.contains(stalePlayerId)) { + game->getPlayerManager()->removePlayer(stalePlayerId); + emit playerLeft(stalePlayerId); + } + } + QVector>> opponentDecksToDisplay; for (int i = 0; i < playerListSize; ++i) { @@ -625,6 +662,17 @@ void GameEventHandler::eventReverseTurn(const Event_ReverseTurn &event, emit logTurnReversed(player, event.reversed()); } +void GameEventHandler::eventPlayerOrderChanged(const Event_PlayerOrderChanged &event, + int /*eventPlayerId*/, + const GameEventContext & /*context*/) +{ + QStringList playerNames; + for (const auto &name : event.player_names()) { + playerNames.append(QString::fromStdString(name)); + } + emit logTurnOrderChanged(playerNames, event.randomized()); +} + void GameEventHandler::eventGameHostChanged(const Event_GameHostChanged & /*event*/, int eventPlayerId, const GameEventContext & /*context*/) diff --git a/libcockatrice_game/libcockatrice/game/game_event_handler.h b/libcockatrice_game/libcockatrice/game/game_event_handler.h index f7b11780b..bfcf355b0 100644 --- a/libcockatrice_game/libcockatrice/game/game_event_handler.h +++ b/libcockatrice_game/libcockatrice/game/game_event_handler.h @@ -51,6 +51,7 @@ class Event_GameSay; class Event_Kicked; class Event_ReverseTurn; class Event_Ping; +class Event_PlayerOrderChanged; class ServerInfo_PlayerProperties; namespace google { @@ -113,6 +114,16 @@ public: /** @brief Request reversing the current turn order. */ void handleReverseTurn(); + /** + * @brief Request a new seating / turn order. + * + * Only the game host (or a moderator) may do this before the game starts. + * + * @param playerNames Ordered list of player names. Ignored when randomize is true. + * @param randomize Ask the server to shuffle the seats instead. + */ + void handleSetPlayerOrder(const QList &playerNames, bool randomize); + /** @brief Concede the game for the currently active local player. */ void handleActiveLocalPlayerConceded(); @@ -316,6 +327,10 @@ public: /** @brief Handle a turn reversal event. */ void eventReverseTurn(const Event_ReverseTurn &event, int eventPlayerId, const GameEventContext &context); + /** @brief Handle a change of the seating / turn order. */ + void + eventPlayerOrderChanged(const Event_PlayerOrderChanged &event, int eventPlayerId, const GameEventContext &context); + /** @brief Handle ping / latency updates. */ void eventPing(const Event_Ping &event, int eventPlayerId, const GameEventContext &context); @@ -443,6 +458,7 @@ signals: void logLeave(PlayerLogic *player, QString reason); void logKicked(); void logTurnReversed(PlayerLogic *player, bool reversed); + void logTurnOrderChanged(QStringList playerNames, bool randomized); void logGameClosed(); void logActivePlayer(PlayerLogic *activePlayer); void logActivePhaseChanged(int activePhase); diff --git a/libcockatrice_game/libcockatrice/game/player/player_manager.h b/libcockatrice_game/libcockatrice/game/player/player_manager.h index c95bbbb96..950574112 100644 --- a/libcockatrice_game/libcockatrice/game/player/player_manager.h +++ b/libcockatrice_game/libcockatrice/game/player/player_manager.h @@ -57,6 +57,11 @@ public: return localPlayerId; } + void setLocalPlayerId(int _localPlayerId) + { + localPlayerId = _localPlayerId; + } + [[nodiscard]] const QMap &getPlayers() const { return players; diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_game_settings_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_game_settings_provider.h index 2b1ddf8ec..00763d031 100644 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_game_settings_provider.h +++ b/libcockatrice_interfaces/libcockatrice/interfaces/interface_game_settings_provider.h @@ -20,6 +20,7 @@ public: [[nodiscard]] virtual bool getCreateGameAsSpectator() const = 0; [[nodiscard]] virtual int getDefaultStartingLifeTotal() const = 0; [[nodiscard]] virtual bool getShareDecklistsOnLoad() const = 0; + [[nodiscard]] virtual bool getShufflePlayers() const = 0; [[nodiscard]] virtual bool getRememberGameSettings() const = 0; [[nodiscard]] virtual bool getLocalGameRememberSettings() const = 0; [[nodiscard]] virtual int getLocalGameMaxPlayers() const = 0; diff --git a/libcockatrice_settings/libcockatrice/settings/game_settings.cpp b/libcockatrice_settings/libcockatrice/settings/game_settings.cpp index c8c6463e3..89fe7b85e 100644 --- a/libcockatrice_settings/libcockatrice/settings/game_settings.cpp +++ b/libcockatrice_settings/libcockatrice/settings/game_settings.cpp @@ -71,6 +71,11 @@ bool GameSettings::getShareDecklistsOnLoad() const return getValue("shareDecklistsOnLoad", "game").toBool(); } +bool GameSettings::getShufflePlayers() const +{ + return getValue("shuffleplayers", "game").toBool(); +} + bool GameSettings::getRememberGameSettings() const { return getValue("rememberGameSettings", "game", QString(), true).toBool(); @@ -151,6 +156,11 @@ void GameSettings::setShareDecklistsOnLoad(bool _shareDecklistsOnLoad) setValue(_shareDecklistsOnLoad, "shareDecklistsOnLoad", "game"); } +void GameSettings::setShufflePlayers(bool _shufflePlayers) +{ + setValue(_shufflePlayers, "shuffleplayers", "game"); +} + void GameSettings::setRememberGameSettings(bool _rememberGameSettings) { setValue(_rememberGameSettings, "rememberGameSettings", "game"); diff --git a/libcockatrice_settings/libcockatrice/settings/game_settings.h b/libcockatrice_settings/libcockatrice/settings/game_settings.h index 3e64234fc..45ec8554b 100644 --- a/libcockatrice_settings/libcockatrice/settings/game_settings.h +++ b/libcockatrice_settings/libcockatrice/settings/game_settings.h @@ -27,6 +27,7 @@ public: [[nodiscard]] bool getCreateGameAsSpectator() const override; [[nodiscard]] int getDefaultStartingLifeTotal() const override; [[nodiscard]] bool getShareDecklistsOnLoad() const override; + [[nodiscard]] bool getShufflePlayers() const override; [[nodiscard]] bool getRememberGameSettings() const override; [[nodiscard]] bool getLocalGameRememberSettings() const override; [[nodiscard]] int getLocalGameMaxPlayers() const override; @@ -44,6 +45,7 @@ public: void setCreateGameAsSpectator(bool _createGameAsSpectator); void setDefaultStartingLifeTotal(int _defaultStartingLifeTotal); void setShareDecklistsOnLoad(bool _shareDecklistsOnLoad); + void setShufflePlayers(bool _shufflePlayers); void setRememberGameSettings(bool _rememberGameSettings); void setLocalGameRememberSettings(bool value); void setLocalGameMaxPlayers(int value);