mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-28 00:42:19 -07:00
Change things about resolution, address comments.
Took 45 minutes Took 12 minutes
This commit is contained in:
parent
cb8fe8305e
commit
80d44da729
36 changed files with 393 additions and 413 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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{};
|
||||
|
|
|
|||
|
|
@ -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 <QMessageBox>
|
||||
#include <libcockatrice/card/database/card_database.h>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/playmat_resolver.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_select.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_ready_start.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_playmat.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_sideboard_lock.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_sideboard_plan.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_deck_download.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/settings/visual_deck_storage_settings.h>
|
||||
#include <libcockatrice/utility/string_limits.h>
|
||||
|
||||
|
|
@ -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<PlaymatFallbackMode>(settings.getPlaymatFallbackBehavior());
|
||||
|
||||
PlaymatResolution resolved;
|
||||
QList<PlaymatResolution> 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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
///@}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "playmat_settings_dialog.h"
|
||||
#include "playmat_settings_utils.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
|
|
@ -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)"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#define COCKATRICE_PLAYMAT_COLLECTION_DIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <libcockatrice/interfaces/interface_interface_settings_provider.h>
|
||||
#include <libcockatrice/utility/playmat_params.h>
|
||||
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
|
|
@ -39,9 +39,8 @@ private:
|
|||
void setupUi();
|
||||
void retranslateUi();
|
||||
int currentRow() const;
|
||||
void appendEntry(const StoredPlaymat &entry);
|
||||
|
||||
QList<StoredPlaymat> playmats; ///< Working copy edited by the dialog.
|
||||
QList<PlaymatResolution> playmats; ///< Working copy edited by the dialog.
|
||||
QListWidget *playmatList;
|
||||
QComboBox *modeCombo;
|
||||
QLabel *modeLabel;
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
#include "playmat_settings_utils.h"
|
||||
|
||||
#include <libcockatrice/interfaces/interface_interface_settings_provider.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
|
||||
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<PlaymatResolution> fallbackList;
|
||||
const QList<StoredPlaymat> 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<PlaymatFallbackMode>(settings.getPlaymatFallbackMode()), rotationIndex);
|
||||
|
||||
DeckList effectiveDeck = deck;
|
||||
effectiveDeck.setPlaymatCard(resolved.card);
|
||||
effectiveDeck.setPlaymatParams(resolved.params);
|
||||
return effectiveDeck;
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
#ifndef COCKATRICE_PLAYMAT_SETTINGS_UTILS_H
|
||||
#define COCKATRICE_PLAYMAT_SETTINGS_UTILS_H
|
||||
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/playmat_resolver.h>
|
||||
#include <libcockatrice/interfaces/interface_interface_settings_provider.h>
|
||||
|
||||
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
|
||||
|
|
@ -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 <QApplication>
|
||||
#include <QColorDialog>
|
||||
|
|
@ -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<int>(&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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -911,6 +911,7 @@ void TabGame::stopGame()
|
|||
QMapIterator<int, TabbedDeckViewContainer *> i(deckViewContainers);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
i.value()->playerDeckView->advancePlaymatRotation();
|
||||
i.value()->show();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#include <QMap>
|
||||
#include <QVector>
|
||||
#include <QtCore/QXmlStreamReader>
|
||||
#include <libcockatrice/utility/card_ref.h>
|
||||
#include <libcockatrice/utility/playmat_params.h>
|
||||
|
||||
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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,14 +6,15 @@ PlaymatResolution resolveEffectivePlaymat(const DeckList &deck,
|
|||
const PlaymatResolution &force,
|
||||
const QList<PlaymatResolution> &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()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<PlaymatResolution> &fallbackList,
|
||||
PlaymatFallbackMode fallbackMode,
|
||||
int &rotationIndex);
|
||||
int rotationIndex);
|
||||
|
||||
#endif // COCKATRICE_PLAYMAT_RESOLVER_H
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -4,26 +4,7 @@
|
|||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
/**
|
||||
* @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 <libcockatrice/utility/playmat_params.h>
|
||||
|
||||
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<StoredPlaymat> 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<PlaymatResolution> 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
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_card_counter.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_counter.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_playmat.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_sideboard_lock.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_sideboard_plan.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_shuffle.pb.h>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <libcockatrice/protocol/pb/command_mulligan.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_active_phase.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_counter.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_playmat.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_sideboard_lock.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_sideboard_plan.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_shuffle.pb.h>
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<StoredPlaymat> InterfaceSettings::getPlaymatFallbackList() const
|
||||
QList<PlaymatResolution> InterfaceSettings::getPlaymatFallbackList() const
|
||||
{
|
||||
const QStringList entries = getValue("playmatFallbackList", QString(), QString(), QStringList()).toStringList();
|
||||
QList<StoredPlaymat> result;
|
||||
QList<PlaymatResolution> 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<StoredPlaymat> &_fallbackList)
|
||||
void InterfaceSettings::setPlaymatFallbackList(const QList<PlaymatResolution> &_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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ public:
|
|||
[[nodiscard]] bool getShowShortcuts() const override;
|
||||
[[nodiscard]] bool getShowGameSelectorFilterToolbar() const override;
|
||||
[[nodiscard]] int getPlaymatVisibility() const override;
|
||||
[[nodiscard]] QList<StoredPlaymat> getPlaymatFallbackList() const override;
|
||||
[[nodiscard]] StoredPlaymat getPlaymatOverride() const override;
|
||||
[[nodiscard]] int getPlaymatFallbackMode() const override;
|
||||
[[nodiscard]] QList<PlaymatResolution> 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<StoredPlaymat> &_fallbackList);
|
||||
void setPlaymatOverride(const StoredPlaymat &_override);
|
||||
void setPlaymatFallbackMode(int _fallbackMode);
|
||||
void setPlaymatFallbackList(const QList<PlaymatResolution> &_fallbackList);
|
||||
void setPlaymatMode(int _mode);
|
||||
void setPlaymatFallbackBehavior(int _behavior);
|
||||
void setLifeCounterAnimationsEnabled(bool _lifeCounterAnimationsEnabled);
|
||||
void setBattlefieldFlashEnabled(bool _battlefieldFlashEnabled);
|
||||
void setUserListExpandedSections(const QStringList §ions);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
52
libcockatrice_utility/libcockatrice/utility/playmat_params.h
Normal file
52
libcockatrice_utility/libcockatrice/utility/playmat_params.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef COCKATRICE_PLAYMAT_PARAMS_H
|
||||
#define COCKATRICE_PLAYMAT_PARAMS_H
|
||||
|
||||
#include "card_ref.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
|
@ -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<PlaymatResolution> 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<PlaymatResolution> 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<PlaymatResolution> 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<PlaymatResolution> 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<PlaymatResolution> 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"));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue