diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 60e3ce08f..a966ec51f 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -244,8 +244,6 @@ set(cockatrice_SOURCES src/interface/widgets/playmat/playmat_collection_dialog.h src/interface/widgets/playmat/playmat_preview_widget.cpp src/interface/widgets/playmat/playmat_settings_dialog.cpp - src/interface/widgets/playmat/playmat_settings_utils.cpp - src/interface/widgets/playmat/playmat_settings_utils.h src/interface/widgets/quick_settings/settings_button_widget.cpp src/interface/widgets/quick_settings/settings_popup_widget.cpp src/interface/widgets/replay/replay_manager.cpp diff --git a/cockatrice/src/game/game_event_handler.cpp b/cockatrice/src/game/game_event_handler.cpp index 8e8adab2c..bc68d4d7c 100644 --- a/cockatrice/src/game/game_event_handler.cpp +++ b/cockatrice/src/game/game_event_handler.cpp @@ -286,7 +286,7 @@ void GameEventHandler::eventGameStateChanged(const Event_GameStateChanged &event } player->processPlayerInfo(playerInfo); // Extract playmat from player properties for opponent display - if (prop.has_playmat_card_name()) { + if (prop.has_playmat_params()) { player->setPlaymatFromProperties(prop); } if (player->getPlayerInfo()->getLocal()) { @@ -356,7 +356,7 @@ void GameEventHandler::eventPlayerPropertiesChanged(const Event_PlayerProperties emit playerPropertiesChanged(prop, eventPlayerId); // Update playmat from player properties - if (prop.has_playmat_card_name()) { + if (prop.has_playmat_params()) { player->setPlaymatFromProperties(prop); } diff --git a/cockatrice/src/game/player/player_logic.cpp b/cockatrice/src/game/player/player_logic.cpp index eef550133..45ba09aac 100644 --- a/cockatrice/src/game/player/player_logic.cpp +++ b/cockatrice/src/game/player/player_logic.cpp @@ -252,12 +252,11 @@ void PlayerLogic::setDeck(const DeckList &_deck) void PlayerLogic::setPlaymatFromProperties(const ServerInfo_PlayerProperties &props) { - if (props.has_playmat_card_name() && !props.playmat_card_name().empty()) { - remotePlaymatCard = {QString::fromStdString(props.playmat_card_name()), - QString::fromStdString(props.playmat_card_provider_id())}; - remotePlaymatParams = { - qBound(0.0, props.playmat_margin_pct_l(), 0.95), qBound(0.0, props.playmat_margin_pct_r(), 0.95), - qBound(0.0, props.playmat_vertical_offset(), 1.0), qBound(0.1, props.playmat_zoom(), 4.0)}; + if (props.has_playmat_params() && !props.playmat_params().card_name().empty()) { + const auto &pp = props.playmat_params(); + remotePlaymatCard = {QString::fromStdString(pp.card_name()), QString::fromStdString(pp.card_provider_id())}; + remotePlaymatParams = {qBound(0.0, pp.margin_pct_l(), 0.95), qBound(0.0, pp.margin_pct_r(), 0.95), + qBound(0.0, pp.vertical_offset(), 1.0), qBound(0.1, pp.zoom(), 4.0)}; hasRemotePlaymat = true; } else { remotePlaymatCard = CardRef{}; diff --git a/cockatrice/src/game_graphics/deckview/deck_view_container.cpp b/cockatrice/src/game_graphics/deckview/deck_view_container.cpp index c8c9fd357..aae2e59a5 100644 --- a/cockatrice/src/game_graphics/deckview/deck_view_container.cpp +++ b/cockatrice/src/game_graphics/deckview/deck_view_container.cpp @@ -8,19 +8,21 @@ #include "../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h" #include "../../interface/widgets/dialogs/dlg_load_deck_from_website.h" #include "../../interface/widgets/dialogs/dlg_load_remote_deck.h" -#include "../../interface/widgets/playmat/playmat_settings_utils.h" #include "../../interface/widgets/tabs/tab_game.h" #include "deck_view.h" #include #include #include +#include #include #include +#include #include #include #include #include +#include #include #include @@ -101,6 +103,9 @@ DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent) connect(&SettingsCache::instance().visualDeckStorage(), &VisualDeckStorageSettings::visualDeckStorageInGameChanged, this, &DeckViewContainer::setVisualDeckStorageExists); + connect(&SettingsCache::instance().userInterface(), &InterfaceSettings::playmatSettingsChanged, this, + &DeckViewContainer::onPlaymatSettingsChanged); + switchToDeckSelectView(); } @@ -278,11 +283,9 @@ void DeckViewContainer::loadDeckFromFile(const QString &filePath) void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck) { - // Bake the user-level playmat resolution (override > deck > fallback list) - // into the uploaded deck so the server, local and remote players, and - // replays all derive the same effective playmat. - const DeckList effectiveDeck = applyUserPlaymatSettings(deck, SettingsCache::instance().userInterface()); - QString deckString = effectiveDeck.writeToString_Native(); + currentDeck = deck; + + QString deckString = deck.writeToString_Native(); if (deckString.length() > MAX_FILE_LENGTH) { QMessageBox::critical(this, tr("Error"), tr("Deck is greater than maximum file size.")); @@ -294,6 +297,65 @@ void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck) PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(cmd); connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished); parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId); + + resolveAndSendPlaymat(); +} + +void DeckViewContainer::resolveAndSendPlaymat() +{ + if (currentDeck.getCardRefList().isEmpty() && currentDeck.getPlaymat().card.isEmpty()) { + return; + } + + const auto &settings = SettingsCache::instance().userInterface(); + const auto fallbackBehavior = static_cast(settings.getPlaymatFallbackBehavior()); + + PlaymatResolution resolved; + QList fallbackList = settings.getPlaymatFallbackList(); + + // In random mode with 2+ entries, remove the last-resolved mat to avoid repeats. + if (fallbackBehavior == PlaymatFallbackMode::Random && fallbackList.size() > 1) { + fallbackList.removeAll(lastResolvedPlaymat); + } + + switch (settings.getPlaymatMode()) { + case 0: { // Override deck playmat — always use collection + DeckList emptyDeck; + resolved = resolveEffectivePlaymat(emptyDeck, {}, fallbackList, fallbackBehavior, playmatRotationIndex); + break; + } + case 1: // Fallback if deck has none — deck > collection > none + resolved = resolveEffectivePlaymat(currentDeck, {}, fallbackList, fallbackBehavior, playmatRotationIndex); + break; + case 2: // Deck only, ignore collection + resolved = currentDeck.getPlaymat(); + break; + default: + break; + } + + lastResolvedPlaymat = resolved; + + Command_SetPlaymat playmatCmd; + auto *pp = playmatCmd.mutable_playmat_params(); + pp->set_card_name(resolved.card.name.toStdString()); + pp->set_card_provider_id(resolved.card.providerId.toStdString()); + pp->set_margin_pct_l(resolved.params.marginPctL); + pp->set_margin_pct_r(resolved.params.marginPctR); + pp->set_vertical_offset(resolved.params.verticalOffset); + pp->set_zoom(resolved.params.zoom); + PendingCommand *playmatPend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(playmatCmd); + parentGame->getGame()->getGameEventHandler()->sendGameCommand(playmatPend, playerId); +} + +void DeckViewContainer::onPlaymatSettingsChanged() +{ + resolveAndSendPlaymat(); +} + +void DeckViewContainer::advancePlaymatRotation() +{ + playmatRotationIndex++; } void DeckViewContainer::loadRemoteDeck() @@ -384,6 +446,10 @@ void DeckViewContainer::sideboardPlanChanged() */ void DeckViewContainer::sendReadyStartCommand(bool ready) { + if (ready) { + resolveAndSendPlaymat(); + } + Command_ReadyStart cmd; cmd.set_ready(ready); parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId); @@ -421,6 +487,7 @@ void DeckViewContainer::setSideboardLocked(bool locked) void DeckViewContainer::setDeck(const DeckList &deck) { + currentDeck = deck; deckView->setDeck(deck); switchToDeckLoadedView(); } \ No newline at end of file diff --git a/cockatrice/src/game_graphics/deckview/deck_view_container.h b/cockatrice/src/game_graphics/deckview/deck_view_container.h index ec024bace..66c610a45 100644 --- a/cockatrice/src/game_graphics/deckview/deck_view_container.h +++ b/cockatrice/src/game_graphics/deckview/deck_view_container.h @@ -57,6 +57,9 @@ private: VisualDeckStorageWidget *visualDeckStorageWidget; TabGame *parentGame; int playerId; + int playmatRotationIndex = 0; ///< Per-match cursor for round-robin playmat mode. + DeckList currentDeck; ///< Cached deck for live settings re-resolution. + PlaymatResolution lastResolvedPlaymat; ///< Tracks last sent playmat to avoid repeats in random mode. void tryCreateVisualDeckStorageWidget(); void sendReadyStartCommand(bool ready); @@ -75,6 +78,7 @@ private slots: void sideboardLockButtonClicked(); void updateSideboardLockButtonText(); void refreshShortcuts(); + void onPlaymatSettingsChanged(); signals: void newCardAdded(AbstractCardItem *card); void notIdle(); @@ -87,6 +91,8 @@ public: void setSideboardLocked(bool locked); void setDeck(const DeckList &deck); void setVisualDeckStorageExists(bool exists); + void advancePlaymatRotation(); + void resolveAndSendPlaymat(); public slots: void loadDeckFromFile(const QString &filePath); diff --git a/cockatrice/src/game_graphics/player/player_graphics_item.cpp b/cockatrice/src/game_graphics/player/player_graphics_item.cpp index 0bce9750c..ac0565fa8 100644 --- a/cockatrice/src/game_graphics/player/player_graphics_item.cpp +++ b/cockatrice/src/game_graphics/player/player_graphics_item.cpp @@ -379,15 +379,18 @@ void PlayerGraphicsItem::updatePlaymat() CardRef playmatCard; PlaymatParams params; - // Check local deck first (for local player) - const DeckList &deck = player->getDeck(); - if (!deck.getPlaymatCard().isEmpty()) { - playmatCard = deck.getPlaymatCard(); - params = deck.getPlaymatParams(); - } else if (visibility == 2 && player->getHasRemotePlaymat()) { - // "Show all" — also show remote playmats from opponents + if (player->getHasRemotePlaymat()) { + // Prefer the server-confirmed playmat (updated by Command_SetPlaymat). playmatCard = player->getRemotePlaymatCard(); params = player->getRemotePlaymatParams(); + } else { + // Fall back to the locally baked-in deck playmat. + const DeckList &deck = player->getDeck(); + const PlaymatResolution &deckPlaymat = deck.getPlaymat(); + if (!deckPlaymat.card.isEmpty()) { + playmatCard = deckPlaymat.card; + params = deckPlaymat.params; + } } if (playmatCard.isEmpty()) { diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp index a06407a34..cc605fa0a 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -3,7 +3,6 @@ #include "../../../client/settings/cache_settings.h" #include "../../../client/settings/shortcuts_settings.h" #include "../playmat/playmat_settings_dialog.h" -#include "../playmat/playmat_settings_utils.h" #include "../settings_page/user_interface_settings_page.h" #include "../tabs/api/commander_spellbook/commander_bracket_widget.h" #include "deck_list_style_proxy.h" @@ -454,29 +453,25 @@ void DeckEditorDeckDockWidget::writeBannerCard(int index) void DeckEditorDeckDockWidget::openPlaymatSettings() { - CardRef currentCard = deckStateManager->getMetadata().playmatCard; - PlaymatParams currentParams = deckStateManager->getMetadata().playmatParams; + PlaymatResolution current = deckStateManager->getMetadata().playmat; - PlaymatSettingsDialog dialog(currentCard, currentParams, this); + PlaymatSettingsDialog dialog(current.card, current.params, this); if (dialog.exec() == QDialog::Accepted) { CardRef newCard = dialog.card(); PlaymatParams newParams = dialog.params(); if (newCard.isEmpty()) { - // User clicked "Remove Playmat" - deckStateManager->setPlaymatCard(CardRef{}); - deckStateManager->setPlaymatParams(PlaymatParams{}); + deckStateManager->setPlaymat(PlaymatResolution{}); } else { - deckStateManager->setPlaymatCard(newCard); - deckStateManager->setPlaymatParams(newParams); + deckStateManager->setPlaymat({newCard, newParams}); } // "Also set as my default playmat": make this the fixed user-level // fallback, so decks without a playmat of their own use it. if (dialog.useAsDefault() && !newCard.isEmpty()) { auto &interfaceSettings = SettingsCache::instance().userInterface(); - interfaceSettings.setPlaymatFallbackList({storedFromResolution({newCard, newParams})}); - interfaceSettings.setPlaymatFallbackMode(0); // PlaymatFallbackMode::Fixed + interfaceSettings.setPlaymatFallbackList({{newCard, newParams}}); + interfaceSettings.setPlaymatFallbackBehavior(0); // PlaymatFallbackMode::Fixed } updatePlaymatLabel(); } @@ -484,7 +479,7 @@ void DeckEditorDeckDockWidget::openPlaymatSettings() void DeckEditorDeckDockWidget::updatePlaymatLabel() { - CardRef playmat = deckStateManager->getMetadata().playmatCard; + CardRef playmat = deckStateManager->getMetadata().playmat.card; if (playmat.isEmpty()) { playmatSettingsButton->setText(tr("Edit Playmat...")); } else { diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp index 39636d8e2..23f305723 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp @@ -142,28 +142,15 @@ void DeckStateManager::setBannerCard(const CardRef &bannerCard) doMetadataModified(); } -void DeckStateManager::setPlaymatCard(const CardRef &playmatCard) +void DeckStateManager::setPlaymat(const PlaymatResolution &playmat) { - CardRef previous = deckList->getPlaymatCard(); - if (previous == playmatCard) { + PlaymatResolution previous = deckList->getPlaymat(); + if (previous == playmat) { return; } - requestHistorySave(tr("Set playmat card to %1 (%2)").arg(playmatCard.name).arg(playmatCard.providerId)); - deckList->setPlaymatCard(playmatCard); - - doMetadataModified(); -} - -void DeckStateManager::setPlaymatParams(const PlaymatParams &playmatParams) -{ - PlaymatParams previous = deckList->getPlaymatParams(); - if (previous == playmatParams) { - return; - } - - requestHistorySave(tr("Updated playmat positioning")); - deckList->setPlaymatParams(playmatParams); + requestHistorySave(tr("Set playmat to %1").arg(playmat.card.name)); + deckList->setPlaymat(playmat); doMetadataModified(); } diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h index 5fd72b780..af9b1e7ed 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h +++ b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.h @@ -171,8 +171,7 @@ public: void setName(const QString &name); void setComments(const QString &comments); void setBannerCard(const CardRef &bannerCard); - void setPlaymatCard(const CardRef &playmatCard); - void setPlaymatParams(const PlaymatParams &playmatParams); + void setPlaymat(const PlaymatResolution &playmat); void setTags(const QStringList &tags); void setFormat(const QString &format); ///@} diff --git a/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.cpp b/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.cpp index 7ad1308ad..483d096bf 100644 --- a/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.cpp +++ b/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.cpp @@ -2,7 +2,6 @@ #include "../../../client/settings/cache_settings.h" #include "playmat_settings_dialog.h" -#include "playmat_settings_utils.h" #include #include @@ -24,7 +23,7 @@ void PlaymatCollectionDialog::accept() { auto &interfaceSettings = SettingsCache::instance().userInterface(); interfaceSettings.setPlaymatFallbackList(playmats); - interfaceSettings.setPlaymatFallbackMode(modeCombo->currentData().toInt()); + interfaceSettings.setPlaymatFallbackBehavior(modeCombo->currentData().toInt()); QDialog::accept(); } @@ -39,8 +38,8 @@ void PlaymatCollectionDialog::setupUi() playmats = interfaceSettings.getPlaymatFallbackList(); playmatList = new QListWidget; - for (const StoredPlaymat &entry : playmats) { - playmatList->addItem(entry.name); + for (const PlaymatResolution &entry : playmats) { + playmatList->addItem(entry.card.name); } connect(playmatList, &QListWidget::itemSelectionChanged, this, &PlaymatCollectionDialog::selectionChanged); connect(playmatList, &QListWidget::itemDoubleClicked, this, [this](QListWidgetItem *) { editPlaymat(); }); @@ -73,7 +72,7 @@ void PlaymatCollectionDialog::setupUi() modeCombo->addItem(QString(), 0); modeCombo->addItem(QString(), 1); modeCombo->addItem(QString(), 2); - const int modeIndex = modeCombo->findData(interfaceSettings.getPlaymatFallbackMode()); + const int modeIndex = modeCombo->findData(interfaceSettings.getPlaymatFallbackBehavior()); if (modeIndex >= 0) { modeCombo->setCurrentIndex(modeIndex); } @@ -106,20 +105,16 @@ void PlaymatCollectionDialog::selectionChanged() moveDownButton->setEnabled(hasSelection && playmatList->currentRow() < playmatList->count() - 1); } -void PlaymatCollectionDialog::appendEntry(const StoredPlaymat &entry) -{ - playmats.append(entry); - playmatList->addItem(entry.name); - playmatList->setCurrentRow(playmatList->count() - 1); -} - void PlaymatCollectionDialog::addPlaymat() { PlaymatSettingsDialog dialog(CardRef{}, PlaymatParams{}, this); if (dialog.exec() == QDialog::Accepted) { const CardRef card = dialog.card(); if (!card.isEmpty()) { - appendEntry(storedFromResolution({card, dialog.params()})); + PlaymatResolution res = {card, dialog.params()}; + playmats.append(res); + playmatList->addItem(res.card.name); + playmatList->setCurrentRow(playmatList->count() - 1); } } } @@ -131,14 +126,14 @@ void PlaymatCollectionDialog::editPlaymat() return; } - const PlaymatResolution current = resolutionFromStoredPlaymat(playmats.at(row)); + const PlaymatResolution ¤t = playmats.at(row); PlaymatSettingsDialog dialog(current.card, current.params, this); if (dialog.exec() == QDialog::Accepted) { const CardRef card = dialog.card(); if (card.isEmpty()) { return; // Removal is handled by the Remove button } - playmats[row] = storedFromResolution({card, dialog.params()}); + playmats[row] = {card, dialog.params()}; playmatList->item(row)->setText(card.name); } } @@ -186,8 +181,8 @@ void PlaymatCollectionDialog::retranslateUi() removeButton->setText(tr("Remove")); moveUpButton->setText(tr("Move Up")); moveDownButton->setText(tr("Move Down")); - modeLabel->setText(tr("When a deck has no playmat:")); - modeCombo->setItemText(0, tr("Always use the first playmat")); - modeCombo->setItemText(1, tr("Cycle through playmats (one per game)")); - modeCombo->setItemText(2, tr("Pick a random playmat per game")); + modeLabel->setText(tr("List mode:")); + modeCombo->setItemText(0, tr("Fixed (always the first entry)")); + modeCombo->setItemText(1, tr("Round-robin (cycle through entries)")); + modeCombo->setItemText(2, tr("Random (pick one per game)")); } diff --git a/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.h b/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.h index 1a93e0d78..fb9e715a9 100644 --- a/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.h +++ b/cockatrice/src/interface/widgets/playmat/playmat_collection_dialog.h @@ -2,7 +2,7 @@ #define COCKATRICE_PLAYMAT_COLLECTION_DIALOG_H #include -#include +#include class QComboBox; class QLabel; @@ -39,9 +39,8 @@ private: void setupUi(); void retranslateUi(); int currentRow() const; - void appendEntry(const StoredPlaymat &entry); - QList playmats; ///< Working copy edited by the dialog. + QList playmats; ///< Working copy edited by the dialog. QListWidget *playmatList; QComboBox *modeCombo; QLabel *modeLabel; diff --git a/cockatrice/src/interface/widgets/playmat/playmat_settings_utils.cpp b/cockatrice/src/interface/widgets/playmat/playmat_settings_utils.cpp deleted file mode 100644 index f4d7badfe..000000000 --- a/cockatrice/src/interface/widgets/playmat/playmat_settings_utils.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "playmat_settings_utils.h" - -#include -#include - -PlaymatResolution resolutionFromStoredPlaymat(const StoredPlaymat &stored) -{ - PlaymatResolution resolution; - resolution.card = {stored.name, stored.providerId}; - resolution.params = {qBound(0.0, stored.marginPctL, 0.95), qBound(0.0, stored.marginPctR, 0.95), - qBound(0.0, stored.verticalOffset, 1.0), qBound(0.1, stored.zoom, 4.0)}; - return resolution; -} - -StoredPlaymat storedFromResolution(const PlaymatResolution &resolution) -{ - StoredPlaymat stored; - stored.name = resolution.card.name; - stored.providerId = resolution.card.providerId; - stored.marginPctL = resolution.params.marginPctL; - stored.marginPctR = resolution.params.marginPctR; - stored.verticalOffset = resolution.params.verticalOffset; - stored.zoom = resolution.params.zoom; - return stored; -} - -DeckList applyUserPlaymatSettings(const DeckList &deck, const InterfaceSettings &settings) -{ - // Per-session cursor for the round-robin mode; advancing per resolution - // (per deck upload, i.e. roughly per game) cycles deterministically. - static int rotationIndex = 0; - - QList fallbackList; - const QList storedList = settings.getPlaymatFallbackList(); - fallbackList.reserve(storedList.size()); - for (const StoredPlaymat &stored : storedList) { - fallbackList.append(resolutionFromStoredPlaymat(stored)); - } - - const PlaymatResolution resolved = - resolveEffectivePlaymat(deck, resolutionFromStoredPlaymat(settings.getPlaymatOverride()), fallbackList, - static_cast(settings.getPlaymatFallbackMode()), rotationIndex); - - DeckList effectiveDeck = deck; - effectiveDeck.setPlaymatCard(resolved.card); - effectiveDeck.setPlaymatParams(resolved.params); - return effectiveDeck; -} diff --git a/cockatrice/src/interface/widgets/playmat/playmat_settings_utils.h b/cockatrice/src/interface/widgets/playmat/playmat_settings_utils.h deleted file mode 100644 index 7b22d34dc..000000000 --- a/cockatrice/src/interface/widgets/playmat/playmat_settings_utils.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef COCKATRICE_PLAYMAT_SETTINGS_UTILS_H -#define COCKATRICE_PLAYMAT_SETTINGS_UTILS_H - -#include -#include -#include - -class InterfaceSettings; - -/** - * @brief Converts a stored (settings-level) playmat entry into resolver types, - * clamping the positioning parameters to their valid ranges. - */ -PlaymatResolution resolutionFromStoredPlaymat(const StoredPlaymat &stored); - -/** - * @brief Converts a resolved playmat into a stored (settings-level) entry. - */ -StoredPlaymat storedFromResolution(const PlaymatResolution &resolution); - -/** - * @brief Applies the user-level playmat settings (override, fallback list and - * mode) to a copy of the deck, returning the deck with the effective - * playmat baked into its metadata. - * - * The resolution chain is: override > deck-configured playmat > fallback list. - * The deck passed in is never modified; the returned copy is intended for - * upload to the server so local and remote players see the same effective mat. - */ -DeckList applyUserPlaymatSettings(const DeckList &deck, const InterfaceSettings &settings); - -#endif // COCKATRICE_PLAYMAT_SETTINGS_UTILS_H diff --git a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp index eaf42b48d..f3ce6c730 100644 --- a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp +++ b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.cpp @@ -9,7 +9,6 @@ #include "../interface/widgets/general/background_sources.h" #include "../playmat/playmat_collection_dialog.h" #include "../playmat/playmat_settings_dialog.h" -#include "../playmat/playmat_settings_utils.h" #include #include @@ -342,22 +341,28 @@ AppearanceSettingsPage::AppearanceSettingsPage() }); playmatVisibilityLabel.setBuddy(&playmatVisibilityCombo); - // User-level playmat settings: override (force all decks) and fallback collection. - connect(&playmatOverrideEditButton, &QPushButton::clicked, this, - &AppearanceSettingsPage::openOverridePlaymatSettings); - connect(&playmatOverrideClearButton, &QPushButton::clicked, this, &AppearanceSettingsPage::clearOverridePlaymat); + // Playmat mode: Override / Fallback / Deck-only + playmatModeCombo.addItem(tr("Override deck playmat"), 0); + playmatModeCombo.addItem(tr("Fallback if deck has none"), 1); + playmatModeCombo.addItem(tr("Deck only, ignore collection"), 2); + int modeIdx = playmatModeCombo.findData(settings.userInterface().getPlaymatMode()); + if (modeIdx >= 0) { + playmatModeCombo.setCurrentIndex(modeIdx); + } + connect(&playmatModeCombo, qOverload(&QComboBox::currentIndexChanged), this, [this](int index) { + SettingsCache::instance().userInterface().setPlaymatMode(playmatModeCombo.itemData(index).toInt()); + }); + playmatModeLabel.setBuddy(&playmatModeCombo); + + // User-level playmat settings: fallback collection. connect(&playmatDefaultEditButton, &QPushButton::clicked, this, &AppearanceSettingsPage::openPlaymatCollectionDialog); - auto *overrideButtons = new QHBoxLayout; - overrideButtons->addWidget(&playmatOverrideEditButton); - overrideButtons->addWidget(&playmatOverrideClearButton); - auto *playmatGrid = new QGridLayout; playmatGrid->addWidget(&playmatVisibilityLabel, 0, 0, 1, 1); playmatGrid->addWidget(&playmatVisibilityCombo, 0, 1, 1, 1); - playmatGrid->addWidget(&playmatOverrideLabel, 1, 0, 1, 1); - playmatGrid->addLayout(overrideButtons, 1, 1, 1, 1); + playmatGrid->addWidget(&playmatModeLabel, 1, 0, 1, 1); + playmatGrid->addWidget(&playmatModeCombo, 1, 1, 1, 1); playmatGrid->addWidget(&playmatDefaultLabel, 2, 0, 1, 1); playmatGrid->addWidget(&playmatDefaultEditButton, 2, 1, 1, 1); @@ -471,40 +476,10 @@ void AppearanceSettingsPage::cardViewExpandedRowsMaxChanged(int value) } } -void AppearanceSettingsPage::openOverridePlaymatSettings() -{ - auto &interfaceSettings = SettingsCache::instance().userInterface(); - const PlaymatResolution current = resolutionFromStoredPlaymat(interfaceSettings.getPlaymatOverride()); - PlaymatSettingsDialog dialog(current.card, current.params, this); - if (dialog.exec() == QDialog::Accepted) { - const CardRef card = dialog.card(); - if (card.isEmpty()) { - interfaceSettings.setPlaymatOverride(StoredPlaymat{}); - } else { - interfaceSettings.setPlaymatOverride(storedFromResolution({card, dialog.params()})); - } - updatePlaymatSummary(); - } -} - -void AppearanceSettingsPage::clearOverridePlaymat() -{ - SettingsCache::instance().userInterface().setPlaymatOverride(StoredPlaymat{}); - updatePlaymatSummary(); -} - void AppearanceSettingsPage::openPlaymatCollectionDialog() { PlaymatCollectionDialog dialog(this); dialog.exec(); - updatePlaymatSummary(); -} - -void AppearanceSettingsPage::updatePlaymatSummary() -{ - const StoredPlaymat overridePlaymat = SettingsCache::instance().userInterface().getPlaymatOverride(); - playmatOverrideEditButton.setText(overridePlaymat.isEmpty() ? tr("Set...") - : tr("Change (%1)").arg(overridePlaymat.name)); } void AppearanceSettingsPage::retranslateUi() @@ -567,9 +542,7 @@ void AppearanceSettingsPage::retranslateUi() minPlayersForMultiColumnLayoutLabel.setText(tr("Minimum player count for multi-column layout:")); playmatGroupBox->setTitle(tr("Playmat settings")); playmatVisibilityLabel.setText(tr("Playmat visibility:")); - playmatOverrideLabel.setText(tr("Force playmat for all decks:")); - playmatOverrideClearButton.setText(tr("Clear")); - playmatDefaultLabel.setText(tr("Default playmats (when a deck has none):")); + playmatModeLabel.setText(tr("Default collection behavior:")); + playmatDefaultLabel.setText(tr("Default playmat collection:")); playmatDefaultEditButton.setText(tr("Edit...")); - updatePlaymatSummary(); } diff --git a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h index 2eb8702fe..28abbd537 100644 --- a/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h +++ b/cockatrice/src/interface/widgets/settings_page/appearance_settings_page.h @@ -24,10 +24,7 @@ private slots: void cardViewInitialRowsMaxChanged(int value); void cardViewExpandedRowsMaxChanged(int value); - void openOverridePlaymatSettings(); - void clearOverridePlaymat(); void openPlaymatCollectionDialog(); - void updatePlaymatSummary(); private: QLabel themeLabel; @@ -65,9 +62,8 @@ private: QCheckBox invertVerticalCoordinateCheckBox; QLabel playmatVisibilityLabel; QComboBox playmatVisibilityCombo; - QLabel playmatOverrideLabel; - QPushButton playmatOverrideEditButton; - QPushButton playmatOverrideClearButton; + QLabel playmatModeLabel; + QComboBox playmatModeCombo; QLabel playmatDefaultLabel; QPushButton playmatDefaultEditButton; QGroupBox *themeGroupBox; diff --git a/cockatrice/src/interface/widgets/tabs/tab_game.cpp b/cockatrice/src/interface/widgets/tabs/tab_game.cpp index dbf4a5a4a..196ea4526 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_game.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_game.cpp @@ -911,6 +911,7 @@ void TabGame::stopGame() QMapIterator i(deckViewContainers); while (i.hasNext()) { i.next(); + i.value()->playerDeckView->advancePlaymatRotation(); i.value()->show(); } diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp index f9d0b18e6..55edc23a5 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp @@ -25,7 +25,7 @@ static const QString CURRENT_SIDEBOARD_PLAN_KEY = ""; bool DeckList::Metadata::isEmpty() const { - return name.isEmpty() && comments.isEmpty() && bannerCard.isEmpty() && tags.isEmpty() && playmatCard.isEmpty(); + return name.isEmpty() && comments.isEmpty() && bannerCard.isEmpty() && tags.isEmpty() && playmat.card.isEmpty(); } DeckList::DeckList() @@ -82,28 +82,28 @@ bool DeckList::readElement(QXmlStreamReader *xml) QString vOffStr = xml->attributes().value("verticalOffset").toString(); QString zoomStr = xml->attributes().value("zoom").toString(); QString cardName = xml->readElementText(); - metadata.playmatCard = {cardName, providerId}; - PlaymatParams params; + PlaymatResolution playmat; + playmat.card = {cardName, providerId}; // Clamp to the same ranges as the settings dialog and the remote // player-properties path so malformed deck files cannot produce // degenerate art rectangles (e.g. a zoom of 0 dividing by zero). - params.marginPctL = qBound(0.0, marginLStr.toDouble(&ok), 0.95); + playmat.params.marginPctL = qBound(0.0, marginLStr.toDouble(&ok), 0.95); if (!ok) { - params.marginPctL = 0.07; + playmat.params.marginPctL = 0.07; } - params.marginPctR = qBound(0.0, marginRStr.toDouble(&ok), 0.95); + playmat.params.marginPctR = qBound(0.0, marginRStr.toDouble(&ok), 0.95); if (!ok) { - params.marginPctR = 0.07; + playmat.params.marginPctR = 0.07; } - params.verticalOffset = qBound(0.0, vOffStr.toDouble(&ok), 1.0); + playmat.params.verticalOffset = qBound(0.0, vOffStr.toDouble(&ok), 1.0); if (!ok) { - params.verticalOffset = 0.33; + playmat.params.verticalOffset = 0.33; } - params.zoom = qBound(0.1, zoomStr.toDouble(&ok), 4.0); + playmat.params.zoom = qBound(0.1, zoomStr.toDouble(&ok), 4.0); if (!ok) { - params.zoom = 1.0; + playmat.params.zoom = 1.0; } - metadata.playmatParams = params; + metadata.playmat = playmat; } else if (childName == "tags") { metadata.tags.clear(); // Clear existing tags while (xml->readNextStartElement()) { @@ -134,14 +134,14 @@ static void writeMetadata(QXmlStreamWriter *xml, const DeckList::Metadata &metad xml->writeAttribute("providerId", metadata.bannerCard.providerId); xml->writeCharacters(metadata.bannerCard.name); xml->writeEndElement(); - if (!metadata.playmatCard.isEmpty()) { + if (!metadata.playmat.card.isEmpty()) { xml->writeStartElement("playmatCard"); - xml->writeAttribute("providerId", metadata.playmatCard.providerId); - xml->writeAttribute("marginPctL", QString::number(metadata.playmatParams.marginPctL, 'f', 4)); - xml->writeAttribute("marginPctR", QString::number(metadata.playmatParams.marginPctR, 'f', 4)); - xml->writeAttribute("verticalOffset", QString::number(metadata.playmatParams.verticalOffset, 'f', 4)); - xml->writeAttribute("zoom", QString::number(metadata.playmatParams.zoom, 'f', 4)); - xml->writeCharacters(metadata.playmatCard.name); + xml->writeAttribute("providerId", metadata.playmat.card.providerId); + xml->writeAttribute("marginPctL", QString::number(metadata.playmat.params.marginPctL, 'f', 4)); + xml->writeAttribute("marginPctR", QString::number(metadata.playmat.params.marginPctR, 'f', 4)); + xml->writeAttribute("verticalOffset", QString::number(metadata.playmat.params.verticalOffset, 'f', 4)); + xml->writeAttribute("zoom", QString::number(metadata.playmat.params.zoom, 'f', 4)); + xml->writeCharacters(metadata.playmat.card.name); xml->writeEndElement(); } xml->writeTextElement("comments", metadata.comments); diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h index 21af24627..c5a90f14e 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include class AbstractDecklistNode; class DecklistCardNode; @@ -27,34 +27,6 @@ class QIODevice; class QTextStream; class InnerDecklistNode; -/** - * @struct PlaymatParams - * @ingroup Decks - * @brief Positioning parameters for a playmat card image. - * - * Controls how the cropped card art is positioned within the - * combined table+stack play area. The coordinate system is - * relative to the cropped art source image. - */ -struct PlaymatParams -{ - double marginPctL = 0.07; ///< Left margin as fraction of card width (0.0–0.95). - double marginPctR = 0.07; ///< Right margin as fraction of card width (0.0–0.95). - double verticalOffset = 0.33; ///< Vertical position within card (0.0=top, 1.0=bottom). - double zoom = 1.0; ///< Scale factor (0.1–4.0). - - bool operator==(const PlaymatParams &other) const - { - return qFuzzyCompare(marginPctL, other.marginPctL) && qFuzzyCompare(marginPctR, other.marginPctR) && - qFuzzyCompare(verticalOffset, other.verticalOffset) && qFuzzyCompare(zoom, other.zoom); - } - - bool operator!=(const PlaymatParams &other) const - { - return !(*this == other); - } -}; - /** * @class DeckList * @ingroup Decks @@ -98,8 +70,7 @@ public: CardRef bannerCard; ///< Optional representative card for the deck. QStringList tags; ///< User-defined tags for deck classification. QString lastLoadedTimestamp; ///< Timestamp string of last load. - CardRef playmatCard; ///< Optional card used as playmat background for table+stack zones. - PlaymatParams playmatParams; ///< Positioning parameters for the playmat card image. + PlaymatResolution playmat; ///< Optional playmat background for table+stack zones. /** * @brief Checks if all values (except for lastLoadedTimestamp) in the metadata is empty. @@ -145,13 +116,9 @@ public: { metadata.bannerCard = _bannerCard; } - void setPlaymatCard(const CardRef &_playmatCard = {}) + void setPlaymat(const PlaymatResolution &_playmat = {}) { - metadata.playmatCard = _playmatCard; - } - void setPlaymatParams(const PlaymatParams &_playmatParams = {}) - { - metadata.playmatParams = _playmatParams; + metadata.playmat = _playmat; } void setLastLoadedTimestamp(const QString &_lastLoadedTimestamp = QString()) { @@ -208,13 +175,9 @@ public: { return metadata.bannerCard; } - CardRef getPlaymatCard() const + PlaymatResolution getPlaymat() const { - return metadata.playmatCard; - } - PlaymatParams getPlaymatParams() const - { - return metadata.playmatParams; + return metadata.playmat; } QString getLastLoadedTimestamp() const { diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.cpp index 246d05dfa..bd5e1302d 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.cpp +++ b/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.cpp @@ -6,14 +6,15 @@ PlaymatResolution resolveEffectivePlaymat(const DeckList &deck, const PlaymatResolution &force, const QList &fallbackList, PlaymatFallbackMode fallbackMode, - int &rotationIndex) + int rotationIndex) { if (!force.card.isEmpty()) { return force; } - if (!deck.getPlaymatCard().isEmpty()) { - return {deck.getPlaymatCard(), deck.getPlaymatParams()}; + const PlaymatResolution &deckPlaymat = deck.getPlaymat(); + if (!deckPlaymat.card.isEmpty()) { + return deckPlaymat; } if (fallbackList.isEmpty()) { @@ -24,7 +25,7 @@ PlaymatResolution resolveEffectivePlaymat(const DeckList &deck, case PlaymatFallbackMode::Fixed: return fallbackList.first(); case PlaymatFallbackMode::RoundRobin: - return fallbackList.at(rotationIndex++ % fallbackList.size()); + return fallbackList.at(rotationIndex % fallbackList.size()); case PlaymatFallbackMode::Random: return fallbackList.at(QRandomGenerator::global()->bounded(fallbackList.size())); } diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.h b/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.h index 66d2c3f54..c48a8bb8b 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.h +++ b/libcockatrice_deck_list/libcockatrice/deck_list/playmat_resolver.h @@ -14,22 +14,6 @@ enum class PlaymatFallbackMode Random = 2 ///< Pick a random entry per resolution. }; -/** - * @struct PlaymatResolution - * @ingroup Decks - * @brief A resolved playmat (card + positioning parameters). - */ -struct PlaymatResolution -{ - CardRef card; ///< The card whose art is used as playmat. - PlaymatParams params; ///< Positioning parameters for the playmat card image. - - bool operator==(const PlaymatResolution &other) const - { - return card == other.card && params == other.params; - } -}; - /** * @brief Resolves the effective playmat for a deck per the resolution chain: * force override > deck-configured playmat > fallback list > none. @@ -49,6 +33,6 @@ PlaymatResolution resolveEffectivePlaymat(const DeckList &deck, const PlaymatResolution &force, const QList &fallbackList, PlaymatFallbackMode fallbackMode, - int &rotationIndex); + int rotationIndex); #endif // COCKATRICE_PLAYMAT_RESOLVER_H diff --git a/libcockatrice_interfaces/CMakeLists.txt b/libcockatrice_interfaces/CMakeLists.txt index f606f6207..f53c41807 100644 --- a/libcockatrice_interfaces/CMakeLists.txt +++ b/libcockatrice_interfaces/CMakeLists.txt @@ -30,4 +30,4 @@ add_library(libcockatrice_interfaces STATIC ${MOC_SOURCES}) target_include_directories(libcockatrice_interfaces PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(libcockatrice_interfaces PUBLIC ${QT_CORE_MODULE}) +target_link_libraries(libcockatrice_interfaces PUBLIC libcockatrice_utility ${QT_CORE_MODULE}) diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_interface_settings_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_interface_settings_provider.h index 737038ebd..8f291332d 100644 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_interface_settings_provider.h +++ b/libcockatrice_interfaces/libcockatrice/interfaces/interface_interface_settings_provider.h @@ -4,26 +4,7 @@ #include #include #include - -/** - * @struct StoredPlaymat - * @brief A user-level playmat entry as stored in the settings, independent of - * any deck. Mirrors a deck-configured playmat (card + parameters). - */ -struct StoredPlaymat -{ - QString name; ///< Card name whose art is used as playmat. - QString providerId; ///< Printing provider id (uuid); may be empty. - double marginPctL = 0.07; ///< Left margin as fraction of card width (0.0-0.95). - double marginPctR = 0.07; ///< Right margin as fraction of card width (0.0-0.95). - double verticalOffset = 0.33; ///< Vertical position within card (0.0=top, 1.0=bottom). - double zoom = 1.0; ///< Scale factor (0.1-4.0). - - bool isEmpty() const - { - return name.isEmpty(); - } -}; +#include class IInterfaceSettingsProvider { @@ -66,14 +47,16 @@ public: [[nodiscard]] virtual QStringList getUserListExpandedSections() const = 0; [[nodiscard]] virtual int getPlaymatVisibility() const = 0; - /** @brief User-level fallback playmats, used when a deck has none configured. */ - [[nodiscard]] virtual QList getPlaymatFallbackList() const = 0; + /** @brief User-level playmat collection. Used either as a forced playmat + * (mode == Always) or as a fallback when a deck has none (mode == Fallback). */ + [[nodiscard]] virtual QList getPlaymatFallbackList() const = 0; - /** @brief User-level override playmat applied to all decks, or empty to disable. */ - [[nodiscard]] virtual StoredPlaymat getPlaymatOverride() const = 0; + /** @brief How the fallback list is applied: Always (0), Fallback (1), Never (2). */ + [[nodiscard]] virtual int getPlaymatMode() const = 0; - /** @brief How the fallback list is consulted (0 Fixed, 1 Round-robin, 2 Random). */ - [[nodiscard]] virtual int getPlaymatFallbackMode() const = 0; + /** @brief How the fallback list is picked from when mode is Fallback + * (0 Fixed, 1 Round-robin, 2 Random). */ + [[nodiscard]] virtual int getPlaymatFallbackBehavior() const = 0; }; #endif // COCKATRICE_INTERFACE_INTERFACE_SETTINGS_PROVIDER_H diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp index 5c0fdf944..a6280ffa1 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,13 @@ Response::ResponseCode Server_AbstractParticipant::cmdSetSideboardLock(const Com return Response::RespFunctionNotAllowed; } +Response::ResponseCode Server_AbstractParticipant::cmdSetPlaymat(const Command_SetPlaymat & /*cmd*/, + ResponseContainer & /*rc*/, + GameEventStorage & /*ges*/) +{ + return Response::RespFunctionNotAllowed; +} + Response::ResponseCode Server_AbstractParticipant::cmdConcede(const Command_Concede & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/) @@ -525,6 +533,9 @@ Server_AbstractParticipant::processGameCommand(const GameCommand &command, Respo case GameCommand::REVERSE_TURN: return cmdReverseTurn(command.GetExtension(Command_ReverseTurn::ext), rc, ges); break; + case GameCommand::SET_PLAYMAT: + return cmdSetPlaymat(command.GetExtension(Command_SetPlaymat::ext), rc, ges); + break; default: return Response::RespInvalidCommand; } diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h index a24fa5799..c78ae78c1 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h @@ -52,6 +52,7 @@ class Command_SetSideboardPlan; class Command_DeckSelect; class Command_SetSideboardLock; class Command_ChangeZoneProperties; +class Command_SetPlaymat; class Server_AbstractParticipant : public Server_ArrowTarget, public ServerInfo_User_Container { @@ -124,6 +125,8 @@ public: cmdSetSideboardPlan(const Command_SetSideboardPlan &cmd, ResponseContainer &rc, GameEventStorage &ges); virtual Response::ResponseCode cmdSetSideboardLock(const Command_SetSideboardLock &cmd, ResponseContainer &rc, GameEventStorage &ges); + virtual Response::ResponseCode + cmdSetPlaymat(const Command_SetPlaymat &cmd, ResponseContainer &rc, GameEventStorage &ges); virtual Response::ResponseCode cmdGameSay(const Command_GameSay &cmd, ResponseContainer &rc, GameEventStorage &ges); virtual Response::ResponseCode cmdShuffle(const Command_Shuffle &cmd, ResponseContainer &rc, GameEventStorage &ges); virtual Response::ResponseCode diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp index a4267b714..957a89792 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp @@ -1653,13 +1653,13 @@ void Server_AbstractPlayer::getPlayerProperties(ServerInfo_PlayerProperties &res result.set_ready_start(readyStart); if (deck) { result.set_deck_hash(deck->getDeckHash().toStdString()); - const auto &playmatCard = deck->getPlaymatCard(); - const auto ¶ms = deck->getPlaymatParams(); - result.set_playmat_card_name(playmatCard.name.toStdString()); - result.set_playmat_card_provider_id(playmatCard.providerId.toStdString()); - result.set_playmat_margin_pct_l(params.marginPctL); - result.set_playmat_margin_pct_r(params.marginPctR); - result.set_playmat_vertical_offset(params.verticalOffset); - result.set_playmat_zoom(params.zoom); + const auto &playmat = deck->getPlaymat(); + auto *playmatParams = result.mutable_playmat_params(); + playmatParams->set_card_name(playmat.card.name.toStdString()); + playmatParams->set_card_provider_id(playmat.card.providerId.toStdString()); + playmatParams->set_margin_pct_l(playmat.params.marginPctL); + playmatParams->set_margin_pct_r(playmat.params.marginPctR); + playmatParams->set_vertical_offset(playmat.params.verticalOffset); + playmatParams->set_zoom(playmat.params.zoom); } } diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp index 9b79ae587..a76f55af7 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -250,14 +251,14 @@ Server_Player::cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &r Event_PlayerPropertiesChanged event; event.mutable_player_properties()->set_sideboard_locked(true); event.mutable_player_properties()->set_deck_hash(deck->getDeckHash().toStdString()); - const auto &playmatCard = deck->getPlaymatCard(); - const auto ¶ms = deck->getPlaymatParams(); - event.mutable_player_properties()->set_playmat_card_name(playmatCard.name.toStdString()); - event.mutable_player_properties()->set_playmat_card_provider_id(playmatCard.providerId.toStdString()); - event.mutable_player_properties()->set_playmat_margin_pct_l(params.marginPctL); - event.mutable_player_properties()->set_playmat_margin_pct_r(params.marginPctR); - event.mutable_player_properties()->set_playmat_vertical_offset(params.verticalOffset); - event.mutable_player_properties()->set_playmat_zoom(params.zoom); + const auto &playmat = deck->getPlaymat(); + auto *playmatParams = event.mutable_player_properties()->mutable_playmat_params(); + playmatParams->set_card_name(playmat.card.name.toStdString()); + playmatParams->set_card_provider_id(playmat.card.providerId.toStdString()); + playmatParams->set_margin_pct_l(playmat.params.marginPctL); + playmatParams->set_margin_pct_r(playmat.params.marginPctR); + playmatParams->set_vertical_offset(playmat.params.verticalOffset); + playmatParams->set_zoom(playmat.params.zoom); ges.enqueueGameEvent(event, playerId); Context_DeckSelect context; @@ -602,6 +603,41 @@ Server_Player::cmdReverseTurn(const Command_ReverseTurn &cmd, ResponseContainer return Server_AbstractParticipant::cmdReverseTurn(cmd, rc, ges); } +Response::ResponseCode +Server_Player::cmdSetPlaymat(const Command_SetPlaymat &cmd, ResponseContainer &rc, GameEventStorage &ges) +{ + Q_UNUSED(rc); + + if (!deck) { + return Response::RespContextError; + } + + const auto &pp = cmd.playmat_params(); + PlaymatResolution playmat; + playmat.card.name = QString::fromStdString(pp.card_name()); + playmat.card.providerId = QString::fromStdString(pp.card_provider_id()); + playmat.params.marginPctL = qBound(0.0, pp.margin_pct_l(), 0.95); + playmat.params.marginPctR = qBound(0.0, pp.margin_pct_r(), 0.95); + playmat.params.verticalOffset = qBound(0.0, pp.vertical_offset(), 1.0); + playmat.params.zoom = qBound(0.1, pp.zoom(), 4.0); + deck->setPlaymat(playmat); + + Event_PlayerPropertiesChanged event; + auto *props = event.mutable_player_properties(); + props->set_sideboard_locked(sideboardLocked); + props->set_deck_hash(deck->getDeckHash().toStdString()); + auto *playmatParams = props->mutable_playmat_params(); + playmatParams->set_card_name(playmat.card.name.toStdString()); + playmatParams->set_card_provider_id(playmat.card.providerId.toStdString()); + playmatParams->set_margin_pct_l(playmat.params.marginPctL); + playmatParams->set_margin_pct_r(playmat.params.marginPctR); + playmatParams->set_vertical_offset(playmat.params.verticalOffset); + playmatParams->set_zoom(playmat.params.zoom); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; +} + void Server_Player::getInfo(ServerInfo_Player *info, Server_AbstractParticipant *recipient, bool omniscient, diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h index 5925ed3c2..c35428480 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h @@ -62,6 +62,8 @@ public: cmdSetActivePhase(const Command_SetActivePhase &cmd, ResponseContainer &rc, GameEventStorage &ges) override; Response::ResponseCode cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges) override; + Response::ResponseCode + cmdSetPlaymat(const Command_SetPlaymat &cmd, ResponseContainer &rc, GameEventStorage &ges) override; Response::ResponseCode cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges) override; diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt index 20a4cb08d..6a9e40d2d 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt @@ -46,6 +46,7 @@ set(PROTO_FILES command_set_card_attr.proto command_set_card_counter.proto command_set_counter.proto + command_set_playmat.proto command_set_sideboard_lock.proto command_set_sideboard_plan.proto command_shuffle.proto diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_playmat.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_playmat.proto new file mode 100644 index 000000000..ad9a29a44 --- /dev/null +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_playmat.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; +import "game_commands.proto"; +import "serverinfo_playerproperties.proto"; +message Command_SetPlaymat { + extend GameCommand { + optional Command_SetPlaymat ext = 1035; + } + optional ServerInfo_PlayerProperties.PlaymatParams playmat_params = 1; +} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto index 8ecf7ec9c..2e5b88978 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto @@ -175,6 +175,11 @@ message GameCommand { /// Server: Server_Player::cmdReverseTurn /// Client: reflected via subsequent turn events REVERSE_TURN = 1034; + + /// Set the player's playmat independently of the deck. + /// Server: Server_Player::cmdSetPlaymat + /// Client: reflected via player properties changed event + SET_PLAYMAT = 1035; } extensions 100 to max; diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_playerproperties.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_playerproperties.proto index 40a112aeb..ae19e4018 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_playerproperties.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_playerproperties.proto @@ -2,6 +2,15 @@ syntax = "proto2"; import "serverinfo_user.proto"; message ServerInfo_PlayerProperties { + message PlaymatParams { + optional string card_name = 1; + optional string card_provider_id = 2; + optional double margin_pct_l = 3 [default = 0.07]; + optional double margin_pct_r = 4 [default = 0.07]; + optional double vertical_offset = 5 [default = 0.33]; + optional double zoom = 6 [default = 1.0]; + } + optional sint32 player_id = 1; optional ServerInfo_User user_info = 2; optional bool spectator = 3; @@ -11,10 +20,5 @@ message ServerInfo_PlayerProperties { optional sint32 ping_seconds = 7; optional bool sideboard_locked = 8; optional bool judge = 9; - optional string playmat_card_name = 10; - optional string playmat_card_provider_id = 11; - optional double playmat_margin_pct_l = 12 [default = 0.07]; - optional double playmat_margin_pct_r = 13 [default = 0.07]; - optional double playmat_vertical_offset = 14 [default = 0.33]; - optional double playmat_zoom = 15 [default = 1.0]; + optional PlaymatParams playmat_params = 10; } diff --git a/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp b/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp index a1d3568c7..44572685d 100644 --- a/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp +++ b/libcockatrice_settings/libcockatrice/settings/interface_settings.cpp @@ -2,30 +2,31 @@ namespace { -const QChar PLAYMAT_FIELD_SEP = QChar(0x1F); ///< Separator between StoredPlaymat fields. +const QChar PLAYMAT_FIELD_SEP = QChar(0x1F); ///< Separator between PlaymatResolution fields. -QString encodeStoredPlaymat(const StoredPlaymat &stored) +QString encodePlaymatResolution(const PlaymatResolution &res) { - return stored.name + PLAYMAT_FIELD_SEP + stored.providerId + PLAYMAT_FIELD_SEP + - QString::number(stored.marginPctL, 'f', 4) + PLAYMAT_FIELD_SEP + QString::number(stored.marginPctR, 'f', 4) + - PLAYMAT_FIELD_SEP + QString::number(stored.verticalOffset, 'f', 4) + PLAYMAT_FIELD_SEP + - QString::number(stored.zoom, 'f', 4); + return res.card.name + PLAYMAT_FIELD_SEP + res.card.providerId + PLAYMAT_FIELD_SEP + + QString::number(res.params.marginPctL, 'f', 4) + PLAYMAT_FIELD_SEP + + QString::number(res.params.marginPctR, 'f', 4) + PLAYMAT_FIELD_SEP + + QString::number(res.params.verticalOffset, 'f', 4) + PLAYMAT_FIELD_SEP + + QString::number(res.params.zoom, 'f', 4); } -StoredPlaymat decodeStoredPlaymat(const QString &encoded) +PlaymatResolution decodePlaymatResolution(const QString &encoded) { const QStringList fields = encoded.split(PLAYMAT_FIELD_SEP); if (fields.size() != 6) { return {}; } - StoredPlaymat stored; - stored.name = fields.at(0); - stored.providerId = fields.at(1); - stored.marginPctL = fields.at(2).toDouble(); - stored.marginPctR = fields.at(3).toDouble(); - stored.verticalOffset = fields.at(4).toDouble(); - stored.zoom = fields.at(5).toDouble(); - return stored; + PlaymatResolution res; + res.card.name = fields.at(0); + res.card.providerId = fields.at(1); + res.params.marginPctL = fields.at(2).toDouble(); + res.params.marginPctR = fields.at(3).toDouble(); + res.params.verticalOffset = fields.at(4).toDouble(); + res.params.zoom = fields.at(5).toDouble(); + return res; } } // namespace @@ -194,28 +195,28 @@ int InterfaceSettings::getPlaymatVisibility() const return getValue("playmatvisibility", QString(), QString(), 2).toInt(); } -QList InterfaceSettings::getPlaymatFallbackList() const +QList InterfaceSettings::getPlaymatFallbackList() const { const QStringList entries = getValue("playmatFallbackList", QString(), QString(), QStringList()).toStringList(); - QList result; + QList result; result.reserve(entries.size()); for (const QString &entry : entries) { - const StoredPlaymat stored = decodeStoredPlaymat(entry); - if (!stored.isEmpty()) { - result.append(stored); + const PlaymatResolution res = decodePlaymatResolution(entry); + if (!res.card.isEmpty()) { + result.append(res); } } return result; } -StoredPlaymat InterfaceSettings::getPlaymatOverride() const +int InterfaceSettings::getPlaymatMode() const { - return decodeStoredPlaymat(getValue("playmatOverride", QString(), QString(), QString()).toString()); + return qBound(0, getValue("playmatMode", QString(), QString(), 1).toInt(), 2); } -int InterfaceSettings::getPlaymatFallbackMode() const +int InterfaceSettings::getPlaymatFallbackBehavior() const { - return qBound(0, getValue("playmatFallbackMode", QString(), QString(), 1).toInt(), 2); + return qBound(0, getValue("playmatFallbackBehavior", QString(), QString(), 0).toInt(), 2); } bool InterfaceSettings::getLifeCounterAnimationsEnabled() const @@ -410,30 +411,34 @@ void InterfaceSettings::setPlaymatVisibility(int _visibility) emit playmatVisibilityChanged(_visibility); } -void InterfaceSettings::setPlaymatFallbackList(const QList &_fallbackList) +void InterfaceSettings::setPlaymatFallbackList(const QList &_fallbackList) { QStringList entries; entries.reserve(_fallbackList.size()); - for (const StoredPlaymat &stored : _fallbackList) { - entries.append(encodeStoredPlaymat(stored)); + for (const PlaymatResolution &res : _fallbackList) { + entries.append(encodePlaymatResolution(res)); } setValue(entries, "playmatFallbackList"); emit playmatSettingsChanged(); } -void InterfaceSettings::setPlaymatOverride(const StoredPlaymat &_override) +void InterfaceSettings::setPlaymatMode(int _mode) { - setValue(_override.isEmpty() ? QString() : encodeStoredPlaymat(_override), "playmatOverride"); + const int mode = qBound(0, _mode, 2); + if (getPlaymatMode() == mode) { + return; + } + setValue(mode, "playmatMode"); emit playmatSettingsChanged(); } -void InterfaceSettings::setPlaymatFallbackMode(int _fallbackMode) +void InterfaceSettings::setPlaymatFallbackBehavior(int _behavior) { - const int mode = qBound(0, _fallbackMode, 2); - if (getPlaymatFallbackMode() == mode) { + const int behavior = qBound(0, _behavior, 2); + if (getPlaymatFallbackBehavior() == behavior) { return; } - setValue(mode, "playmatFallbackMode"); + setValue(behavior, "playmatFallbackBehavior"); emit playmatSettingsChanged(); } diff --git a/libcockatrice_settings/libcockatrice/settings/interface_settings.h b/libcockatrice_settings/libcockatrice/settings/interface_settings.h index bedc20ef0..a46213f81 100644 --- a/libcockatrice_settings/libcockatrice/settings/interface_settings.h +++ b/libcockatrice_settings/libcockatrice/settings/interface_settings.h @@ -43,9 +43,9 @@ public: [[nodiscard]] bool getShowShortcuts() const override; [[nodiscard]] bool getShowGameSelectorFilterToolbar() const override; [[nodiscard]] int getPlaymatVisibility() const override; - [[nodiscard]] QList getPlaymatFallbackList() const override; - [[nodiscard]] StoredPlaymat getPlaymatOverride() const override; - [[nodiscard]] int getPlaymatFallbackMode() const override; + [[nodiscard]] QList getPlaymatFallbackList() const override; + [[nodiscard]] int getPlaymatMode() const override; + [[nodiscard]] int getPlaymatFallbackBehavior() const override; [[nodiscard]] bool getLifeCounterAnimationsEnabled() const override; [[nodiscard]] bool getBattlefieldFlashEnabled() const override; [[nodiscard]] QStringList getUserListExpandedSections() const override; @@ -82,9 +82,9 @@ public: void setShowShortcuts(bool _showShortcuts); void setShowGameSelectorFilterToolbar(bool _showGameSelectorFilterToolbar); void setPlaymatVisibility(int _visibility); - void setPlaymatFallbackList(const QList &_fallbackList); - void setPlaymatOverride(const StoredPlaymat &_override); - void setPlaymatFallbackMode(int _fallbackMode); + void setPlaymatFallbackList(const QList &_fallbackList); + void setPlaymatMode(int _mode); + void setPlaymatFallbackBehavior(int _behavior); void setLifeCounterAnimationsEnabled(bool _lifeCounterAnimationsEnabled); void setBattlefieldFlashEnabled(bool _battlefieldFlashEnabled); void setUserListExpandedSections(const QStringList §ions); diff --git a/libcockatrice_utility/CMakeLists.txt b/libcockatrice_utility/CMakeLists.txt index 79f5a11e4..3a81f179a 100644 --- a/libcockatrice_utility/CMakeLists.txt +++ b/libcockatrice_utility/CMakeLists.txt @@ -10,11 +10,13 @@ set(UTILITY_SOURCES libcockatrice/utility/expression.cpp libcockatrice/utility/l ) set(UTILITY_HEADERS + libcockatrice/utility/card_ref.h libcockatrice/utility/color.h libcockatrice/utility/expression.h libcockatrice/utility/levenshtein.h libcockatrice/utility/macros.h libcockatrice/utility/passwordhasher.h + libcockatrice/utility/playmat_params.h libcockatrice/utility/string_limits.h libcockatrice/utility/dice_limits.h libcockatrice/utility/counter_limits.h diff --git a/libcockatrice_utility/libcockatrice/utility/playmat_params.h b/libcockatrice_utility/libcockatrice/utility/playmat_params.h new file mode 100644 index 000000000..0219b9f9d --- /dev/null +++ b/libcockatrice_utility/libcockatrice/utility/playmat_params.h @@ -0,0 +1,52 @@ +#ifndef COCKATRICE_PLAYMAT_PARAMS_H +#define COCKATRICE_PLAYMAT_PARAMS_H + +#include "card_ref.h" + +#include + +/** + * @struct PlaymatParams + * @ingroup Decks + * @brief Positioning parameters for a playmat card image. + * + * Controls how the cropped card art is positioned within the + * combined table+stack play area. The coordinate system is + * relative to the cropped art source image. + */ +struct PlaymatParams +{ + double marginPctL = 0.07; ///< Left margin as fraction of card width (0.0–0.95). + double marginPctR = 0.07; ///< Right margin as fraction of card width (0.0–0.95). + double verticalOffset = 0.33; ///< Vertical position within card (0.0=top, 1.0=bottom). + double zoom = 1.0; ///< Scale factor (0.1–4.0). + + bool operator==(const PlaymatParams &other) const + { + return qFuzzyCompare(marginPctL, other.marginPctL) && qFuzzyCompare(marginPctR, other.marginPctR) && + qFuzzyCompare(verticalOffset, other.verticalOffset) && qFuzzyCompare(zoom, other.zoom); + } + + bool operator!=(const PlaymatParams &other) const + { + return !(*this == other); + } +}; + +/** + * @struct PlaymatResolution + * @ingroup Decks + * @brief A resolved playmat (card + positioning parameters). + */ +struct PlaymatResolution +{ + CardRef card; ///< The card whose art is used as playmat. + PlaymatParams params; ///< Positioning parameters for the playmat card image. + + bool operator==(const PlaymatResolution &other) const + { + return card == other.card && params == other.params; + } +}; + +#endif // COCKATRICE_PLAYMAT_PARAMS_H diff --git a/tests/playmat_resolver_test.cpp b/tests/playmat_resolver_test.cpp index 6be3b3401..21a9f0ecd 100644 --- a/tests/playmat_resolver_test.cpp +++ b/tests/playmat_resolver_test.cpp @@ -17,22 +17,19 @@ PlaymatResolution makeResolution(const QString &name, const QString &providerId TEST(PlaymatResolverTest, EmptyChainReturnsEmpty) { DeckList deck; - int rotationIndex = 0; - const PlaymatResolution resolved = resolveEffectivePlaymat(deck, {}, {}, PlaymatFallbackMode::Fixed, rotationIndex); + const PlaymatResolution resolved = resolveEffectivePlaymat(deck, {}, {}, PlaymatFallbackMode::Fixed, 0); EXPECT_TRUE(resolved.card.isEmpty()); } TEST(PlaymatResolverTest, OverrideWinsOverDeckAndFallback) { DeckList deck; - deck.setPlaymatCard({QStringLiteral("Deck Mat"), QStringLiteral("deck-provider")}); + deck.setPlaymat({{QStringLiteral("Deck Mat"), QStringLiteral("deck-provider")}, {}}); const PlaymatResolution force = makeResolution(QStringLiteral("Force Mat"), QStringLiteral("force-provider")); const QList fallback = {makeResolution(QStringLiteral("Fallback Mat"))}; - int rotationIndex = 0; - const PlaymatResolution resolved = - resolveEffectivePlaymat(deck, force, fallback, PlaymatFallbackMode::Fixed, rotationIndex); + const PlaymatResolution resolved = resolveEffectivePlaymat(deck, force, fallback, PlaymatFallbackMode::Fixed, 0); EXPECT_EQ(resolved.card.name, QStringLiteral("Force Mat")); EXPECT_EQ(resolved.card.providerId, QStringLiteral("force-provider")); } @@ -40,14 +37,11 @@ TEST(PlaymatResolverTest, OverrideWinsOverDeckAndFallback) TEST(PlaymatResolverTest, DeckWinsOverFallback) { DeckList deck; - deck.setPlaymatCard({QStringLiteral("Deck Mat"), QStringLiteral("deck-provider")}); - deck.setPlaymatParams({0.1, 0.2, 0.3, 1.5}); + deck.setPlaymat({{QStringLiteral("Deck Mat"), QStringLiteral("deck-provider")}, {0.1, 0.2, 0.3, 1.5}}); const QList fallback = {makeResolution(QStringLiteral("Fallback Mat"))}; - int rotationIndex = 0; - const PlaymatResolution resolved = - resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Fixed, rotationIndex); + const PlaymatResolution resolved = resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Fixed, 0); EXPECT_EQ(resolved.card.name, QStringLiteral("Deck Mat")); EXPECT_EQ(resolved.card.providerId, QStringLiteral("deck-provider")); EXPECT_DOUBLE_EQ(resolved.params.marginPctL, 0.1); @@ -59,9 +53,7 @@ TEST(PlaymatResolverTest, FallbackUsedWhenDeckHasNone) DeckList deck; const QList fallback = {makeResolution(QStringLiteral("Fallback Mat"))}; - int rotationIndex = 0; - const PlaymatResolution resolved = - resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Fixed, rotationIndex); + const PlaymatResolution resolved = resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Fixed, 0); EXPECT_EQ(resolved.card.name, QStringLiteral("Fallback Mat")); } @@ -71,10 +63,8 @@ TEST(PlaymatResolverTest, FixedAlwaysUsesFirst) const QList fallback = {makeResolution(QStringLiteral("First")), makeResolution(QStringLiteral("Second"))}; - int rotationIndex = 3; // ignored in Fixed mode for (int i = 0; i < 5; ++i) { - const PlaymatResolution resolved = - resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Fixed, rotationIndex); + const PlaymatResolution resolved = resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Fixed, i); EXPECT_EQ(resolved.card.name, QStringLiteral("First")); } } @@ -86,28 +76,23 @@ TEST(PlaymatResolverTest, RoundRobinCyclesAndWraps) makeResolution(QStringLiteral("Second")), makeResolution(QStringLiteral("Third"))}; - int rotationIndex = 0; const QStringList expected = {QStringLiteral("First"), QStringLiteral("Second"), QStringLiteral("Third"), QStringLiteral("First"), QStringLiteral("Second"), QStringLiteral("Third")}; - for (const QString &name : expected) { + for (int i = 0; i < expected.size(); ++i) { const PlaymatResolution resolved = - resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::RoundRobin, rotationIndex); - EXPECT_EQ(resolved.card.name, name); + resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::RoundRobin, i); + EXPECT_EQ(resolved.card.name, expected.at(i)); } - EXPECT_EQ(rotationIndex, expected.size()); } -TEST(PlaymatResolverTest, RoundRobinRespectsExistingCursor) +TEST(PlaymatResolverTest, RoundRobinRespectsCursor) { DeckList deck; const QList fallback = {makeResolution(QStringLiteral("First")), makeResolution(QStringLiteral("Second"))}; - int rotationIndex = 5; - const PlaymatResolution resolved = - resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::RoundRobin, rotationIndex); + const PlaymatResolution resolved = resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::RoundRobin, 5); EXPECT_EQ(resolved.card.name, QStringLiteral("Second")); // 5 % 2 == 1 - EXPECT_EQ(rotationIndex, 6); } TEST(PlaymatResolverTest, RandomStaysWithinList) @@ -117,10 +102,8 @@ TEST(PlaymatResolverTest, RandomStaysWithinList) makeResolution(QStringLiteral("Second")), makeResolution(QStringLiteral("Third"))}; - int rotationIndex = 0; for (int i = 0; i < 50; ++i) { - const PlaymatResolution resolved = - resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Random, rotationIndex); + const PlaymatResolution resolved = resolveEffectivePlaymat(deck, {}, fallback, PlaymatFallbackMode::Random, i); ASSERT_FALSE(resolved.card.name.isEmpty()); EXPECT_TRUE(fallback.contains(resolved)); } @@ -129,13 +112,11 @@ TEST(PlaymatResolverTest, RandomStaysWithinList) TEST(PlaymatResolverTest, ForceWithEmptyCardIgnoresFallbackParamsButNotFallback) { DeckList deck; - deck.setPlaymatCard({QStringLiteral("Deck Mat")}); + deck.setPlaymat({{QStringLiteral("Deck Mat")}, {}}); // An empty force entry must not mask the deck-configured playmat. const PlaymatResolution emptyForce; - int rotationIndex = 0; - const PlaymatResolution resolved = - resolveEffectivePlaymat(deck, emptyForce, {}, PlaymatFallbackMode::Fixed, rotationIndex); + const PlaymatResolution resolved = resolveEffectivePlaymat(deck, emptyForce, {}, PlaymatFallbackMode::Fixed, 0); EXPECT_EQ(resolved.card.name, QStringLiteral("Deck Mat")); }