Merge branch 'master' into custom-zones

This commit is contained in:
Basile Clement 2025-05-07 01:46:58 +02:00 committed by GitHub
commit d90e4a99ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 411 additions and 72 deletions

View file

@ -1,4 +1,4 @@
FROM fedora:40 FROM fedora:42
RUN dnf install -y \ RUN dnf install -y \
ccache \ ccache \

View file

@ -23,8 +23,8 @@ Available pre-compiled binaries for installation:
<kbd>Ubuntu 20.04 LTS</kbd> <sub><i>Focal Fossa</i></sub> <kbd>Ubuntu 20.04 LTS</kbd> <sub><i>Focal Fossa</i></sub>
<kbd>Debian 12</kbd> <sub><i>Bookworm</i></sub> <kbd>Debian 12</kbd> <sub><i>Bookworm</i></sub>
<kbd>Debian 11</kbd> <sub><i>Bullseye</i></sub> <kbd>Debian 11</kbd> <sub><i>Bullseye</i></sub>
<kbd>Fedora 42</kbd>
<kbd>Fedora 41</kbd> <kbd>Fedora 41</kbd>
<kbd>Fedora 40</kbd>
<sub>We are also packaged in <kbd>Arch Linux</kbd>'s <a href="https://archlinux.org/packages/extra/x86_64/cockatrice">official extra repository</a>, courtesy of @FFY00.</sub> <sub>We are also packaged in <kbd>Arch Linux</kbd>'s <a href="https://archlinux.org/packages/extra/x86_64/cockatrice">official extra repository</a>, courtesy of @FFY00.</sub>
<sub>General Linux support is available via a <kbd>flatpak</kbd> package at <a href="https://flathub.org/apps/io.github.Cockatrice.cockatrice">Flathub</a>!</sub> <sub>General Linux support is available via a <kbd>flatpak</kbd> package at <a href="https://flathub.org/apps/io.github.Cockatrice.cockatrice">Flathub</a>!</sub>

View file

@ -9,6 +9,7 @@ on:
- 'webclient/**' - 'webclient/**'
- '.github/workflows/web-*.yml' - '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml' - '.github/workflows/translations-*.yml'
- '.github/workflows/docker-release.yml'
tags: tags:
- '*' - '*'
pull_request: pull_request:
@ -101,12 +102,12 @@ jobs:
package: DEB package: DEB
- distro: Fedora - distro: Fedora
version: 40 version: 41
package: RPM package: RPM
test: skip # Running tests on all distros is superfluous test: skip # Running tests on all distros is superfluous
- distro: Fedora - distro: Fedora
version: 41 version: 42
package: RPM package: RPM
- distro: Ubuntu - distro: Ubuntu
@ -380,13 +381,15 @@ jobs:
uses: jurplel/install-qt-action@v4 uses: jurplel/install-qt-action@v4
with: with:
cache: true cache: true
setup-python: false setup-python: true
version: ${{matrix.qt_version}} version: ${{matrix.qt_version}}
arch: win64_${{matrix.qt_arch}} arch: win64_${{matrix.qt_arch}}
tools: ${{matrix.qt_tools}} tools: ${{matrix.qt_tools}}
modules: ${{matrix.qt_modules}} modules: ${{matrix.qt_modules}}
- name: Run vcpkg # TODO: re-enable when https://github.com/lukka/run-vcpkg/issues/243 is fixed
- if: false
name: Run vcpkg (disabled)
uses: lukka/run-vcpkg@v11 uses: lukka/run-vcpkg@v11
with: with:
runVcpkgInstall: true runVcpkgInstall: true

View file

@ -7,6 +7,7 @@ on:
- 'webclient/**' - 'webclient/**'
- '.github/workflows/web-*.yml' - '.github/workflows/web-*.yml'
- '.github/workflows/translations-*.yml' - '.github/workflows/translations-*.yml'
- '.github/workflows/docker-release.yml'
jobs: jobs:
format: format:

71
.github/workflows/docker-release.yml vendored Normal file
View file

@ -0,0 +1,71 @@
name: Build Docker Container
on:
push:
tags:
- '*Release*'
branches:
- master
pull_request:
branches:
- master
paths:
- '.github/workflows/docker-release.yml'
- 'CMakeLists.txt'
- 'Dockerfile'
- 'servatrice/**'
- 'common/**'
- 'cmake/**'
- '!**.md'
jobs:
docker:
name: amd64 & arm64
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Docker metadata
id: metadata
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/cockatrice/servatrice
labels: |
org.opencontainers.image.title=Servatrice
org.opencontainers.image.url=https://cockatrice.github.io/
org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games
annotations: |
org.opencontainers.image.title=Servatrice
org.opencontainers.image.url=https://cockatrice.github.io/
org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: github.ref_type == 'tag'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Build and push Docker Image
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.ref_type == 'tag' }}
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View file

@ -16,7 +16,11 @@ RUN apt-get update && apt-get install -y\
qt6-tools-dev \ qt6-tools-dev \
qt6-tools-dev-tools qt6-tools-dev-tools
COPY . /home/servatrice/code/ COPY ./CMakeLists.txt ./LICENSE ./README.md /home/servatrice/code/
COPY ./cmake /home/servatrice/code/cmake
COPY ./common /home/servatrice/code/common
COPY ./servatrice /home/servatrice/code/servatrice
WORKDIR /home/servatrice/code WORKDIR /home/servatrice/code
WORKDIR build WORKDIR build

View file

@ -143,7 +143,6 @@ The following flags (with their non-default values) can be passed to `cmake`:
# Run # Run
<kbd>Cockatrice</kbd> is the game client<br> <kbd>Cockatrice</kbd> is the game client<br>
<kbd>Oracle</kbd> fetches card data<br> <kbd>Oracle</kbd> fetches card data<br>
<kbd>Servatrice</kbd> is the server<br> <kbd>Servatrice</kbd> is the server<br>

View file

@ -356,7 +356,7 @@ void TabEdhRecMain::processAverageDeckResponse(QJsonObject reply)
{ {
EdhrecAverageDeckApiResponse deckData; EdhrecAverageDeckApiResponse deckData;
deckData.fromJson(reply); deckData.fromJson(reply);
tabSupervisor->addVisualDeckEditorTab(deckData.deck.deckLoader); tabSupervisor->openDeckInNewTab(deckData.deck.deckLoader);
} }
void TabEdhRecMain::prettyPrintJson(const QJsonValue &value, int indentLevel) void TabEdhRecMain::prettyPrintJson(const QJsonValue &value, int indentLevel)

View file

@ -590,7 +590,7 @@ void TabSupervisor::actTabDeckStorage(bool checked)
void TabSupervisor::openTabDeckStorage() void TabSupervisor::openTabDeckStorage()
{ {
tabDeckStorage = new TabDeckStorage(this, client, userInfo); tabDeckStorage = new TabDeckStorage(this, client, userInfo);
connect(tabDeckStorage, &TabDeckStorage::openDeckEditor, this, &TabSupervisor::addDeckEditorTab); connect(tabDeckStorage, &TabDeckStorage::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tabDeckStorage, aTabDeckStorage); myAddTab(tabDeckStorage, aTabDeckStorage);
connect(tabDeckStorage, &Tab::closed, this, [this] { connect(tabDeckStorage, &Tab::closed, this, [this] {
tabDeckStorage = nullptr; tabDeckStorage = nullptr;
@ -691,7 +691,7 @@ void TabSupervisor::gameJoined(const Event_GameJoined &event)
auto *tab = new TabGame(this, QList<AbstractClient *>() << client, event, roomGameTypes); auto *tab = new TabGame(this, QList<AbstractClient *>() << client, event, roomGameTypes);
connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft); connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft);
connect(tab, &TabGame::openMessageDialog, this, &TabSupervisor::addMessageTab); connect(tab, &TabGame::openMessageDialog, this, &TabSupervisor::addMessageTab);
connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::addDeckEditorTab); connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tab); myAddTab(tab);
gameTabs.insert(event.game_info().game_id(), tab); gameTabs.insert(event.game_info().game_id(), tab);
setCurrentWidget(tab); setCurrentWidget(tab);
@ -701,7 +701,7 @@ void TabSupervisor::localGameJoined(const Event_GameJoined &event)
{ {
auto *tab = new TabGame(this, localClients, event, QMap<int, QString>()); auto *tab = new TabGame(this, localClients, event, QMap<int, QString>());
connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft); connect(tab, &TabGame::gameClosing, this, &TabSupervisor::gameLeft);
connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::addDeckEditorTab); connect(tab, &TabGame::openDeckEditor, this, &TabSupervisor::openDeckInNewTab);
myAddTab(tab); myAddTab(tab);
gameTabs.insert(event.game_info().game_id(), tab); gameTabs.insert(event.game_info().game_id(), tab);
setCurrentWidget(tab); setCurrentWidget(tab);
@ -807,6 +807,29 @@ void TabSupervisor::talkLeft(TabMessage *tab)
removeTab(indexOf(tab)); removeTab(indexOf(tab));
} }
/**
* Creates a new deck editor tab and loads the deck into it.
* Creates either a classic or visual deck editor tab depending on settings
* @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance.
*/
void TabSupervisor::openDeckInNewTab(const DeckLoader *deckToOpen)
{
int type = SettingsCache::instance().getDefaultDeckEditorType();
switch (type) {
case ClassicDeckEditor:
addDeckEditorTab(deckToOpen);
break;
case VisualDeckEditor:
addVisualDeckEditorTab(deckToOpen);
break;
default:
qCWarning(TabSupervisorLog) << "Unknown DeckEditorType [" << type
<< "]; opening ClassicDeckEditor as fallback";
addDeckEditorTab(deckToOpen);
break;
}
}
/** /**
* Creates a new deck editor tab * Creates a new deck editor tab
* @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance. * @param deckToOpen The deck to open in the tab. Creates a copy of the DeckLoader instance.

View file

@ -75,6 +75,14 @@ protected:
class TabSupervisor : public QTabWidget class TabSupervisor : public QTabWidget
{ {
Q_OBJECT Q_OBJECT
public:
enum DeckEditorType
{
ClassicDeckEditor,
VisualDeckEditor
};
private: private:
ServerInfo_User *userInfo; ServerInfo_User *userInfo;
AbstractClient *client; AbstractClient *client;
@ -152,6 +160,7 @@ signals:
void showWindowIfHidden(); void showWindowIfHidden();
public slots: public slots:
void openDeckInNewTab(const DeckLoader *deckToOpen);
TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen); TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen);
TabDeckEditorVisual *addVisualDeckEditorTab(const DeckLoader *deckToOpen); TabDeckEditorVisual *addVisualDeckEditorTab(const DeckLoader *deckToOpen);
TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab(); TabVisualDatabaseDisplay *addVisualDatabaseDisplayTab();

View file

@ -12,7 +12,7 @@
TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor) TabDeckStorageVisual::TabDeckStorageVisual(TabSupervisor *_tabSupervisor)
: Tab(_tabSupervisor), visualDeckStorageWidget(new VisualDeckStorageWidget(this)) : Tab(_tabSupervisor), visualDeckStorageWidget(new VisualDeckStorageWidget(this))
{ {
connect(this, &TabDeckStorageVisual::openDeckEditor, tabSupervisor, &TabSupervisor::addVisualDeckEditorTab); connect(this, &TabDeckStorageVisual::openDeckEditor, tabSupervisor, &TabSupervisor::openDeckInNewTab);
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::deckLoadRequested, this, connect(visualDeckStorageWidget, &VisualDeckStorageWidget::deckLoadRequested, this,
&TabDeckStorageVisual::actOpenLocalDeck); &TabDeckStorageVisual::actOpenLocalDeck);
connect(visualDeckStorageWidget, &VisualDeckStorageWidget::openDeckEditor, this, connect(visualDeckStorageWidget, &VisualDeckStorageWidget::openDeckEditor, this,

View file

@ -66,7 +66,7 @@ void ManaSymbolWidget::loadManaIcon()
QString filename = "theme:icons/mana/"; QString filename = "theme:icons/mana/";
if (symbol == "W" || symbol == "U" || symbol == "B" || symbol == "R" || symbol == "G") { if (symbol == "W" || symbol == "U" || symbol == "B" || symbol == "R" || symbol == "G") {
filename += symbol + ".svg"; filename += symbol;
} }
manaIcon = QPixmap(filename); manaIcon = QPixmap(filename);

View file

@ -64,6 +64,12 @@ CardInfoPictureWidget::CardInfoPictureWidget(QWidget *parent, const bool _hoverT
}); });
} }
CardInfoPictureWidget::~CardInfoPictureWidget()
{
enlargedPixmapWidget->hide();
enlargedPixmapWidget->deleteLater();
}
/** /**
* @brief Sets the card to be displayed and updates the pixmap. * @brief Sets the card to be displayed and updates the pixmap.
* @param card A shared pointer to the card information (CardInfoPtr). * @param card A shared pointer to the card information (CardInfoPtr).
@ -341,6 +347,12 @@ void CardInfoPictureWidget::mousePressEvent(QMouseEvent *event)
emit cardClicked(); emit cardClicked();
} }
void CardInfoPictureWidget::hideEvent(QHideEvent *event)
{
enlargedPixmapWidget->hide();
QWidget::hideEvent(event);
}
QMenu *CardInfoPictureWidget::createRightClickMenu() QMenu *CardInfoPictureWidget::createRightClickMenu()
{ {
auto *cardMenu = new QMenu(this); auto *cardMenu = new QMenu(this);

View file

@ -21,6 +21,7 @@ public:
explicit CardInfoPictureWidget(QWidget *parent = nullptr, explicit CardInfoPictureWidget(QWidget *parent = nullptr,
bool hoverToZoomEnabled = false, bool hoverToZoomEnabled = false,
bool raiseOnEnter = false); bool raiseOnEnter = false);
~CardInfoPictureWidget();
CardInfoPtr getInfo() CardInfoPtr getInfo()
{ {
return info; return info;
@ -52,6 +53,7 @@ protected:
void moveEvent(QMoveEvent *event) override; void moveEvent(QMoveEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
void hideEvent(QHideEvent *event) override;
void loadPixmap(); void loadPixmap();
[[nodiscard]] const QPixmap &getResizedPixmap() const [[nodiscard]] const QPixmap &getResizedPixmap() const
{ {

View file

@ -63,6 +63,9 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
destroyCheckBox = new QCheckBox(tr("&Destroy token when it leaves the table")); destroyCheckBox = new QCheckBox(tr("&Destroy token when it leaves the table"));
destroyCheckBox->setChecked(true); destroyCheckBox->setChecked(true);
faceDownCheckBox = new QCheckBox(tr("Create face-down (Only hides name)"));
connect(faceDownCheckBox, &QCheckBox::toggled, this, &DlgCreateToken::faceDownCheckBoxToggled);
QGridLayout *grid = new QGridLayout; QGridLayout *grid = new QGridLayout;
grid->addWidget(nameLabel, 0, 0); grid->addWidget(nameLabel, 0, 0);
grid->addWidget(nameEdit, 0, 1); grid->addWidget(nameEdit, 0, 1);
@ -73,6 +76,7 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
grid->addWidget(annotationLabel, 3, 0); grid->addWidget(annotationLabel, 3, 0);
grid->addWidget(annotationEdit, 3, 1); grid->addWidget(annotationEdit, 3, 1);
grid->addWidget(destroyCheckBox, 4, 0, 1, 2); grid->addWidget(destroyCheckBox, 4, 0, 1, 2);
grid->addWidget(faceDownCheckBox, 5, 0, 1, 2);
QGroupBox *tokenDataGroupBox = new QGroupBox(tr("Token data")); QGroupBox *tokenDataGroupBox = new QGroupBox(tr("Token data"));
tokenDataGroupBox->setLayout(grid); tokenDataGroupBox->setLayout(grid);
@ -155,6 +159,21 @@ void DlgCreateToken::closeEvent(QCloseEvent *event)
SettingsCache::instance().setTokenDialogGeometry(saveGeometry()); SettingsCache::instance().setTokenDialogGeometry(saveGeometry());
} }
void DlgCreateToken::faceDownCheckBoxToggled(bool checked)
{
if (checked) {
colorEdit->setCurrentIndex(6);
colorEdit->setEnabled(false);
ptEdit->clear();
ptEdit->clearFocus();
ptEdit->setEnabled(false);
} else {
colorEdit->setEnabled(true);
ptEdit->setEnabled(true);
annotationEdit->setEnabled(true);
}
}
void DlgCreateToken::tokenSelectionChanged(const QModelIndex &current, const QModelIndex & /*previous*/) void DlgCreateToken::tokenSelectionChanged(const QModelIndex &current, const QModelIndex & /*previous*/)
{ {
const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current); const QModelIndex realIndex = cardDatabaseDisplayModel->mapToSource(current);
@ -230,5 +249,6 @@ TokenInfo DlgCreateToken::getTokenInfo() const
.color = colorEdit->itemData(colorEdit->currentIndex()).toString(), .color = colorEdit->itemData(colorEdit->currentIndex()).toString(),
.pt = ptEdit->text(), .pt = ptEdit->text(),
.annotation = annotationEdit->text(), .annotation = annotationEdit->text(),
.destroy = destroyCheckBox->isChecked()}; .destroy = destroyCheckBox->isChecked(),
.faceDown = faceDownCheckBox->isChecked()};
} }

View file

@ -24,6 +24,7 @@ struct TokenInfo
QString pt; QString pt;
QString annotation; QString annotation;
bool destroy = true; bool destroy = true;
bool faceDown = false;
}; };
class DlgCreateToken : public QDialog class DlgCreateToken : public QDialog
@ -36,6 +37,7 @@ public:
protected: protected:
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
private slots: private slots:
void faceDownCheckBoxToggled(bool checked);
void tokenSelectionChanged(const QModelIndex &current, const QModelIndex &previous); void tokenSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
void updateSearch(const QString &search); void updateSearch(const QString &search);
void actChooseTokenFromAll(bool checked); void actChooseTokenFromAll(bool checked);
@ -51,6 +53,7 @@ private:
QComboBox *colorEdit; QComboBox *colorEdit;
QLineEdit *nameEdit, *ptEdit, *annotationEdit; QLineEdit *nameEdit, *ptEdit, *annotationEdit;
QCheckBox *destroyCheckBox; QCheckBox *destroyCheckBox;
QCheckBox *faceDownCheckBox;
QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton; QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton;
CardInfoPictureWidget *pic; CardInfoPictureWidget *pic;
QTreeView *chooseTokenView; QTreeView *chooseTokenView;

View file

@ -4,6 +4,7 @@
#include "../client/network/release_channel.h" #include "../client/network/release_channel.h"
#include "../client/network/spoiler_background_updater.h" #include "../client/network/spoiler_background_updater.h"
#include "../client/sound_engine.h" #include "../client/sound_engine.h"
#include "../client/tabs/tab_supervisor.h"
#include "../client/ui/picture_loader/picture_loader.h" #include "../client/ui/picture_loader/picture_loader.h"
#include "../client/ui/theme_manager.h" #include "../client/ui/theme_manager.h"
#include "../deck/custom_line_edit.h" #include "../deck/custom_line_edit.h"
@ -629,6 +630,10 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
connect(&closeEmptyCardViewCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), connect(&closeEmptyCardViewCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setCloseEmptyCardView); &SettingsCache::setCloseEmptyCardView);
focusCardViewSearchBarCheckBox.setChecked(SettingsCache::instance().getFocusCardViewSearchBar());
connect(&focusCardViewSearchBarCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setFocusCardViewSearchBar);
annotateTokensCheckBox.setChecked(SettingsCache::instance().getAnnotateTokens()); annotateTokensCheckBox.setChecked(SettingsCache::instance().getAnnotateTokens());
connect(&annotateTokensCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), connect(&annotateTokensCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setAnnotateTokens); &SettingsCache::setAnnotateTokens);
@ -642,8 +647,9 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0); generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0);
generalGrid->addWidget(&playToStackCheckBox, 2, 0); generalGrid->addWidget(&playToStackCheckBox, 2, 0);
generalGrid->addWidget(&closeEmptyCardViewCheckBox, 3, 0); generalGrid->addWidget(&closeEmptyCardViewCheckBox, 3, 0);
generalGrid->addWidget(&annotateTokensCheckBox, 4, 0); generalGrid->addWidget(&focusCardViewSearchBarCheckBox, 4, 0);
generalGrid->addWidget(&useTearOffMenusCheckBox, 5, 0); generalGrid->addWidget(&annotateTokensCheckBox, 5, 0);
generalGrid->addWidget(&useTearOffMenusCheckBox, 6, 0);
generalGroupBox = new QGroupBox; generalGroupBox = new QGroupBox;
generalGroupBox->setLayout(generalGrid); generalGroupBox->setLayout(generalGrid);
@ -699,12 +705,20 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
index == visualDeckStoragePromptForConversionIndexAlways); index == visualDeckStoragePromptForConversionIndexAlways);
}); });
defaultDeckEditorTypeSelector.addItem(""); // these will be set in retranslateUI
defaultDeckEditorTypeSelector.addItem("");
defaultDeckEditorTypeSelector.setCurrentIndex(SettingsCache::instance().getDefaultDeckEditorType());
connect(&defaultDeckEditorTypeSelector, QOverload<int>::of(&QComboBox::currentIndexChanged),
&SettingsCache::instance(), &SettingsCache::setDefaultDeckEditorType);
auto *deckEditorGrid = new QGridLayout; auto *deckEditorGrid = new QGridLayout;
deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0); deckEditorGrid->addWidget(&openDeckInNewTabCheckBox, 0, 0);
deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0); deckEditorGrid->addWidget(&visualDeckStorageInGameCheckBox, 1, 0);
deckEditorGrid->addWidget(&visualDeckStorageSelectionAnimationCheckBox, 2, 0); deckEditorGrid->addWidget(&visualDeckStorageSelectionAnimationCheckBox, 2, 0);
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionLabel, 3, 0); deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionLabel, 3, 0);
deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1); deckEditorGrid->addWidget(&visualDeckStoragePromptForConversionSelector, 3, 1);
deckEditorGrid->addWidget(&defaultDeckEditorTypeLabel, 4, 0);
deckEditorGrid->addWidget(&defaultDeckEditorTypeSelector, 4, 1);
deckEditorGroupBox = new QGroupBox; deckEditorGroupBox = new QGroupBox;
deckEditorGroupBox->setLayout(deckEditorGrid); deckEditorGroupBox->setLayout(deckEditorGrid);
@ -754,6 +768,7 @@ void UserInterfaceSettingsPage::retranslateUi()
clickPlaysAllSelectedCheckBox.setText(tr("&Clicking plays all selected cards (instead of just the clicked card)")); clickPlaysAllSelectedCheckBox.setText(tr("&Clicking plays all selected cards (instead of just the clicked card)"));
playToStackCheckBox.setText(tr("&Play all nonlands onto the stack (not the battlefield) by default")); playToStackCheckBox.setText(tr("&Play all nonlands onto the stack (not the battlefield) by default"));
closeEmptyCardViewCheckBox.setText(tr("Close card view window when last card is removed")); closeEmptyCardViewCheckBox.setText(tr("Close card view window when last card is removed"));
focusCardViewSearchBarCheckBox.setText(tr("Auto focus search bar when card view window is opened"));
annotateTokensCheckBox.setText(tr("Annotate card text on tokens")); annotateTokensCheckBox.setText(tr("Annotate card text on tokens"));
useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen")); useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen"));
notificationsGroupBox->setTitle(tr("Notifications settings")); notificationsGroupBox->setTitle(tr("Notifications settings"));
@ -774,6 +789,9 @@ void UserInterfaceSettingsPage::retranslateUi()
tr("ask to convert to .cod")); tr("ask to convert to .cod"));
visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexAlways, visualDeckStoragePromptForConversionSelector.setItemText(visualDeckStoragePromptForConversionIndexAlways,
tr("always convert to .cod")); tr("always convert to .cod"));
defaultDeckEditorTypeLabel.setText(tr("Default deck editor type"));
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::ClassicDeckEditor, tr("Classic Deck Editor"));
defaultDeckEditorTypeSelector.setItemText(TabSupervisor::VisualDeckEditor, tr("Visual Deck Editor"));
replayGroupBox->setTitle(tr("Replay settings")); replayGroupBox->setTitle(tr("Replay settings"));
rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:")); rewindBufferingMsLabel.setText(tr("Buffer time for backwards skip via shortcut:"));
rewindBufferingMsBox.setSuffix(" ms"); rewindBufferingMsBox.setSuffix(" ms");

View file

@ -147,6 +147,7 @@ private:
QCheckBox clickPlaysAllSelectedCheckBox; QCheckBox clickPlaysAllSelectedCheckBox;
QCheckBox playToStackCheckBox; QCheckBox playToStackCheckBox;
QCheckBox closeEmptyCardViewCheckBox; QCheckBox closeEmptyCardViewCheckBox;
QCheckBox focusCardViewSearchBarCheckBox;
QCheckBox annotateTokensCheckBox; QCheckBox annotateTokensCheckBox;
QCheckBox useTearOffMenusCheckBox; QCheckBox useTearOffMenusCheckBox;
QCheckBox tapAnimationCheckBox; QCheckBox tapAnimationCheckBox;
@ -155,6 +156,8 @@ private:
QComboBox visualDeckStoragePromptForConversionSelector; QComboBox visualDeckStoragePromptForConversionSelector;
QCheckBox visualDeckStorageInGameCheckBox; QCheckBox visualDeckStorageInGameCheckBox;
QCheckBox visualDeckStorageSelectionAnimationCheckBox; QCheckBox visualDeckStorageSelectionAnimationCheckBox;
QLabel defaultDeckEditorTypeLabel;
QComboBox defaultDeckEditorTypeSelector;
QLabel rewindBufferingMsLabel; QLabel rewindBufferingMsLabel;
QSpinBox rewindBufferingMsBox; QSpinBox rewindBufferingMsBox;
QGroupBox *generalGroupBox; QGroupBox *generalGroupBox;

View file

@ -3,23 +3,40 @@
#include "../settings/cache_settings.h" #include "../settings/cache_settings.h"
#include "../utility/logger.h" #include "../utility/logger.h"
#include <QClipboard>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QPushButton>
#include <QRegularExpression>
#include <QVBoxLayout> #include <QVBoxLayout>
DlgViewLog::DlgViewLog(QWidget *parent) : QDialog(parent) DlgViewLog::DlgViewLog(QWidget *parent) : QDialog(parent)
{ {
logArea = new QPlainTextEdit; logArea = new QPlainTextEdit;
logArea->setReadOnly(true); logArea->setReadOnly(true);
auto *mainLayout = new QVBoxLayout; auto *mainLayout = new QVBoxLayout;
mainLayout->setSpacing(3);
mainLayout->setContentsMargins(20, 20, 20, 6);
mainLayout->addWidget(logArea); mainLayout->addWidget(logArea);
auto *bottomLayout = new QHBoxLayout;
coClearLog = new QCheckBox; coClearLog = new QCheckBox;
coClearLog->setText(tr("Clear log when closing")); coClearLog->setText(tr("Clear log when closing"));
coClearLog->setChecked(SettingsCache::instance().servers().getClearDebugLogStatus(false)); coClearLog->setChecked(SettingsCache::instance().servers().getClearDebugLogStatus(false));
connect(coClearLog, &QCheckBox::toggled, this, &DlgViewLog::actCheckBoxChanged); connect(coClearLog, &QCheckBox::toggled, this, &DlgViewLog::actCheckBoxChanged);
mainLayout->addWidget(coClearLog);
copyToClipboardButton = new QPushButton;
copyToClipboardButton->setText(tr("Copy to clipboard"));
copyToClipboardButton->setAutoDefault(false);
connect(copyToClipboardButton, &QPushButton::clicked, this, &DlgViewLog::actCopyToClipboard);
bottomLayout->addWidget(coClearLog);
bottomLayout->addStretch();
bottomLayout->addWidget(copyToClipboardButton);
mainLayout->addLayout(bottomLayout);
setLayout(mainLayout); setLayout(mainLayout);
@ -27,7 +44,7 @@ DlgViewLog::DlgViewLog(QWidget *parent) : QDialog(parent)
resize(800, 500); resize(800, 500);
loadInitialLogBuffer(); loadInitialLogBuffer();
connect(&Logger::getInstance(), &Logger::logEntryAdded, this, &DlgViewLog::logEntryAdded); connect(&Logger::getInstance(), &Logger::logEntryAdded, this, &DlgViewLog::appendLogEntry);
} }
void DlgViewLog::actCheckBoxChanged(bool abNewValue) void DlgViewLog::actCheckBoxChanged(bool abNewValue)
@ -35,16 +52,26 @@ void DlgViewLog::actCheckBoxChanged(bool abNewValue)
SettingsCache::instance().servers().setClearDebugLogStatus(abNewValue); SettingsCache::instance().servers().setClearDebugLogStatus(abNewValue);
} }
void DlgViewLog::actCopyToClipboard()
{
QApplication::clipboard()->setText(logArea->toPlainText());
}
void DlgViewLog::loadInitialLogBuffer() void DlgViewLog::loadInitialLogBuffer()
{ {
QList<QString> logBuffer = Logger::getInstance().getLogBuffer(); QList<QString> logBuffer = Logger::getInstance().getLogBuffer();
for (const QString &message : logBuffer) for (const QString &message : logBuffer)
logEntryAdded(message); appendLogEntry(message);
} }
void DlgViewLog::logEntryAdded(QString message) void DlgViewLog::appendLogEntry(const QString &message)
{ {
logArea->appendPlainText(message); static auto colorEscapeCodePattern = QRegularExpression("\033\\[\\d+m");
QString sanitizedMessage = message;
sanitizedMessage.replace(colorEscapeCodePattern, "");
logArea->appendPlainText(sanitizedMessage);
} }
void DlgViewLog::closeEvent(QCloseEvent * /* event */) void DlgViewLog::closeEvent(QCloseEvent * /* event */)

View file

@ -19,11 +19,13 @@ protected:
private: private:
QPlainTextEdit *logArea; QPlainTextEdit *logArea;
QCheckBox *coClearLog; QCheckBox *coClearLog;
QPushButton *copyToClipboardButton;
void loadInitialLogBuffer(); void loadInitialLogBuffer();
private slots: private slots:
void logEntryAdded(QString message); void appendLogEntry(const QString &message);
void actCheckBoxChanged(bool abNewValue); void actCheckBoxChanged(bool abNewValue);
void actCopyToClipboard();
}; };
#endif #endif

View file

@ -39,6 +39,8 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item,
prepareGeometryChange(); prepareGeometryChange();
update(); update();
}); });
connect(item, &QObject::destroyed, this, &AbstractCardDragItem::deleteLater);
} }
AbstractCardDragItem::~AbstractCardDragItem() AbstractCardDragItem::~AbstractCardDragItem()

View file

@ -47,10 +47,25 @@ void CardDragItem::updatePosition(const QPointF &cursorScenePos)
cursorZone = zoneViewZone; cursorZone = zoneViewZone;
else if (cardZone) else if (cardZone)
cursorZone = cardZone; cursorZone = cardZone;
if (!cursorZone)
return; // Always update the current zone, even if its null, to cancel the drag
// instead of dropping cards into an non-intuitive location.
currentZone = cursorZone; currentZone = cursorZone;
if (!cursorZone) {
// Avoid the cards getting stuck visually when not over
// any zone.
QPointF newPos = cursorScenePos - hotSpot;
if (newPos != pos()) {
for (int i = 0; i < childDrags.size(); i++)
childDrags[i]->setPos(newPos + childDrags[i]->getHotSpot());
setPos(newPos);
}
return;
}
QPointF zonePos = currentZone->scenePos(); QPointF zonePos = currentZone->scenePos();
QPointF cursorPosInZone = cursorScenePos - zonePos; QPointF cursorPosInZone = cursorScenePos - zonePos;

View file

@ -1864,6 +1864,7 @@ void Player::actCreateAnotherToken()
cmd.set_pt(lastTokenInfo.pt.toStdString()); cmd.set_pt(lastTokenInfo.pt.toStdString());
cmd.set_annotation(lastTokenInfo.annotation.toStdString()); cmd.set_annotation(lastTokenInfo.annotation.toStdString());
cmd.set_destroy_on_zone_change(lastTokenInfo.destroy); cmd.set_destroy_on_zone_change(lastTokenInfo.destroy);
cmd.set_face_down(lastTokenInfo.faceDown);
cmd.set_x(-1); cmd.set_x(-1);
cmd.set_y(lastTokenTableRow); cmd.set_y(lastTokenTableRow);
@ -2075,6 +2076,8 @@ void Player::createCard(const CardItem *sourceCard,
break; break;
case CardRelation::TransformInto: case CardRelation::TransformInto:
// allow cards to directly transform on stack
cmd.set_zone(sourceCard->getZone()->getName() == "stack" ? "stack" : "table");
// Transform card zone changes are handled server-side // Transform card zone changes are handled server-side
cmd.set_target_zone(sourceCard->getZone()->getName().toStdString()); cmd.set_target_zone(sourceCard->getZone()->getName().toStdString());
cmd.set_target_card_id(sourceCard->getId()); cmd.set_target_card_id(sourceCard->getId());
@ -2240,10 +2243,10 @@ void Player::eventCreateToken(const Event_CreateToken &event)
CardItem *card = new CardItem(this, nullptr, QString::fromStdString(event.card_name()), CardItem *card = new CardItem(this, nullptr, QString::fromStdString(event.card_name()),
QString::fromStdString(event.card_provider_id()), event.card_id()); QString::fromStdString(event.card_provider_id()), event.card_id());
// use db PT if not provided in event // use db PT if not provided in event and not face-down
if (!QString::fromStdString(event.pt()).isEmpty()) { if (!QString::fromStdString(event.pt()).isEmpty()) {
card->setPT(QString::fromStdString(event.pt())); card->setPT(QString::fromStdString(event.pt()));
} else { } else if (!event.face_down()) {
CardInfoPtr dbCard = card->getInfo(); CardInfoPtr dbCard = card->getInfo();
if (dbCard) { if (dbCard) {
card->setPT(dbCard->getPowTough()); card->setPT(dbCard->getPowTough());
@ -2252,8 +2255,9 @@ void Player::eventCreateToken(const Event_CreateToken &event)
card->setColor(QString::fromStdString(event.color())); card->setColor(QString::fromStdString(event.color()));
card->setAnnotation(QString::fromStdString(event.annotation())); card->setAnnotation(QString::fromStdString(event.annotation()));
card->setDestroyOnZoneChange(event.destroy_on_zone_change()); card->setDestroyOnZoneChange(event.destroy_on_zone_change());
card->setFaceDown(event.face_down());
emit logCreateToken(this, card->getName(), card->getPT()); emit logCreateToken(this, card->getName(), card->getPT(), card->getFaceDown());
zone->addCard(card, true, event.x(), event.y()); zone->addCard(card, true, event.x(), event.y());
} }

View file

@ -128,7 +128,7 @@ signals:
Player *targetPlayer, Player *targetPlayer,
QString targetCard, QString targetCard,
bool _playerTarget); bool _playerTarget);
void logCreateToken(Player *player, QString cardName, QString pt); void logCreateToken(Player *player, QString cardName, QString pt, bool faceDown);
void logDrawCards(Player *player, int number, bool deckIsEmpty); void logDrawCards(Player *player, int number, bool deckIsEmpty);
void logUndoDraw(Player *player, QString cardName); void logUndoDraw(Player *player, QString cardName);
void logMoveCard(Player *player, CardItem *card, CardZone *startZone, int oldX, CardZone *targetZone, int newX); void logMoveCard(Player *player, CardItem *card, CardZone *startZone, int oldX, CardZone *targetZone, int newX);

View file

@ -56,6 +56,11 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
connect(help, &QAction::triggered, this, [this] { createSearchSyntaxHelpWindow(&searchEdit); }); connect(help, &QAction::triggered, this, [this] { createSearchSyntaxHelpWindow(&searchEdit); });
if (SettingsCache::instance().getFocusCardViewSearchBar()) {
this->setActive(true);
searchEdit.setFocus();
}
QGraphicsProxyWidget *searchEditProxy = new QGraphicsProxyWidget; QGraphicsProxyWidget *searchEditProxy = new QGraphicsProxyWidget;
searchEditProxy->setWidget(&searchEdit); searchEditProxy->setWidget(&searchEdit);
searchEditProxy->setZValue(2000000007); searchEditProxy->setZValue(2000000007);

View file

@ -216,12 +216,16 @@ void MessageLogWidget::logCreateArrow(Player *player,
} }
} }
void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString pt) void MessageLogWidget::logCreateToken(Player *player, QString cardName, QString pt, bool faceDown)
{ {
if (faceDown) {
appendHtmlServerMessage(tr("%1 creates a face down token.").arg(sanitizeHtml(player->getName())));
} else {
appendHtmlServerMessage(tr("%1 creates token: %2%3.") appendHtmlServerMessage(tr("%1 creates token: %2%3.")
.arg(sanitizeHtml(player->getName())) .arg(sanitizeHtml(player->getName()))
.arg(cardLink(std::move(cardName))) .arg(cardLink(std::move(cardName)))
.arg(pt.isEmpty() ? QString() : QString(" (%1)").arg(sanitizeHtml(pt)))); .arg(pt.isEmpty() ? QString() : QString(" (%1)").arg(sanitizeHtml(pt))));
}
} }
void MessageLogWidget::logDeckSelect(Player *player, QString deckHash, int sideboardSize) void MessageLogWidget::logDeckSelect(Player *player, QString deckHash, int sideboardSize)

View file

@ -41,7 +41,7 @@ public slots:
Player *targetPlayer, Player *targetPlayer,
QString targetCard, QString targetCard,
bool playerTarget); bool playerTarget);
void logCreateToken(Player *player, QString cardName, QString pt); void logCreateToken(Player *player, QString cardName, QString pt, bool faceDown);
void logDeckSelect(Player *player, QString deckHash, int sideboardSize); void logDeckSelect(Player *player, QString deckHash, int sideboardSize);
void logDestroyCard(Player *player, QString cardName); void logDestroyCard(Player *player, QString cardName);
void logDrawCards(Player *player, int number, bool deckIsEmpty); void logDrawCards(Player *player, int number, bool deckIsEmpty);

View file

@ -251,6 +251,7 @@ SettingsCache::SettingsCache()
cardViewInitialRowsMax = settings->value("interface/cardViewInitialRowsMax", 14).toInt(); cardViewInitialRowsMax = settings->value("interface/cardViewInitialRowsMax", 14).toInt();
cardViewExpandedRowsMax = settings->value("interface/cardViewExpandedRowsMax", 20).toInt(); cardViewExpandedRowsMax = settings->value("interface/cardViewExpandedRowsMax", 20).toInt();
closeEmptyCardView = settings->value("interface/closeEmptyCardView", true).toBool(); closeEmptyCardView = settings->value("interface/closeEmptyCardView", true).toBool();
focusCardViewSearchBar = settings->value("interface/focusCardViewSearchBar", true).toBool();
showShortcuts = settings->value("menu/showshortcuts", true).toBool(); showShortcuts = settings->value("menu/showshortcuts", true).toBool();
displayCardNames = settings->value("cards/displaycardnames", true).toBool(); displayCardNames = settings->value("cards/displaycardnames", true).toBool();
@ -285,6 +286,7 @@ SettingsCache::SettingsCache()
visualDeckStorageInGame = settings->value("interface/visualdeckstorageingame", true).toBool(); visualDeckStorageInGame = settings->value("interface/visualdeckstorageingame", true).toBool();
visualDeckStorageSelectionAnimation = visualDeckStorageSelectionAnimation =
settings->value("interface/visualdeckstorageselectionanimation", true).toBool(); settings->value("interface/visualdeckstorageselectionanimation", true).toBool();
defaultDeckEditorType = settings->value("interface/defaultDeckEditorType", 1).toInt();
visualDatabaseDisplayFilterToMostRecentSetsEnabled = visualDatabaseDisplayFilterToMostRecentSetsEnabled =
settings->value("interface/visualdatabasedisplayfiltertomostrecentsetsenabled", true).toBool(); settings->value("interface/visualdatabasedisplayfiltertomostrecentsetsenabled", true).toBool();
visualDatabaseDisplayFilterToMostRecentSetsAmount = visualDatabaseDisplayFilterToMostRecentSetsAmount =
@ -369,6 +371,12 @@ void SettingsCache::setCloseEmptyCardView(QT_STATE_CHANGED_T value)
settings->setValue("interface/closeEmptyCardView", closeEmptyCardView); settings->setValue("interface/closeEmptyCardView", closeEmptyCardView);
} }
void SettingsCache::setFocusCardViewSearchBar(QT_STATE_CHANGED_T value)
{
focusCardViewSearchBar = value;
settings->setValue("interface/focusCardViewSearchBar", focusCardViewSearchBar);
}
void SettingsCache::setKnownMissingFeatures(const QString &_knownMissingFeatures) void SettingsCache::setKnownMissingFeatures(const QString &_knownMissingFeatures)
{ {
knownMissingFeatures = _knownMissingFeatures; knownMissingFeatures = _knownMissingFeatures;
@ -791,6 +799,12 @@ void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T va
emit visualDeckStorageSelectionAnimationChanged(visualDeckStorageSelectionAnimation); emit visualDeckStorageSelectionAnimationChanged(visualDeckStorageSelectionAnimation);
} }
void SettingsCache::setDefaultDeckEditorType(int value)
{
defaultDeckEditorType = value;
settings->setValue("interface/defaultDeckEditorType", defaultDeckEditorType);
}
void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T _enabled) void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T _enabled)
{ {
visualDatabaseDisplayFilterToMostRecentSetsEnabled = _enabled; visualDatabaseDisplayFilterToMostRecentSetsEnabled = _enabled;

View file

@ -153,6 +153,7 @@ private:
bool visualDeckStorageAlwaysConvert; bool visualDeckStorageAlwaysConvert;
bool visualDeckStorageInGame; bool visualDeckStorageInGame;
bool visualDeckStorageSelectionAnimation; bool visualDeckStorageSelectionAnimation;
int defaultDeckEditorType;
bool visualDatabaseDisplayFilterToMostRecentSetsEnabled; bool visualDatabaseDisplayFilterToMostRecentSetsEnabled;
int visualDatabaseDisplayFilterToMostRecentSetsAmount; int visualDatabaseDisplayFilterToMostRecentSetsAmount;
bool horizontalHand; bool horizontalHand;
@ -183,6 +184,7 @@ private:
int cardViewInitialRowsMax; int cardViewInitialRowsMax;
int cardViewExpandedRowsMax; int cardViewExpandedRowsMax;
bool closeEmptyCardView; bool closeEmptyCardView;
bool focusCardViewSearchBar;
int pixmapCacheSize; int pixmapCacheSize;
int networkCacheSize; int networkCacheSize;
int redirectCacheTtl; int redirectCacheTtl;
@ -488,6 +490,10 @@ public:
{ {
return visualDeckStorageSelectionAnimation; return visualDeckStorageSelectionAnimation;
} }
int getDefaultDeckEditorType() const
{
return defaultDeckEditorType;
}
bool getVisualDatabaseDisplayFilterToMostRecentSetsEnabled() const bool getVisualDatabaseDisplayFilterToMostRecentSetsEnabled() const
{ {
return visualDatabaseDisplayFilterToMostRecentSetsEnabled; return visualDatabaseDisplayFilterToMostRecentSetsEnabled;
@ -694,6 +700,7 @@ public:
void setCardViewInitialRowsMax(int _cardViewInitialRowsMax); void setCardViewInitialRowsMax(int _cardViewInitialRowsMax);
void setCardViewExpandedRowsMax(int value); void setCardViewExpandedRowsMax(int value);
void setCloseEmptyCardView(QT_STATE_CHANGED_T value); void setCloseEmptyCardView(QT_STATE_CHANGED_T value);
void setFocusCardViewSearchBar(QT_STATE_CHANGED_T value);
QString getClientID() QString getClientID()
{ {
return clientID; return clientID;
@ -722,6 +729,10 @@ public:
{ {
return closeEmptyCardView; return closeEmptyCardView;
} }
bool getFocusCardViewSearchBar() const
{
return focusCardViewSearchBar;
}
ShortcutsSettings &shortcuts() const ShortcutsSettings &shortcuts() const
{ {
return *shortcutsSettings; return *shortcutsSettings;
@ -839,6 +850,7 @@ public slots:
void setVisualDeckStorageAlwaysConvert(bool _visualDeckStorageAlwaysConvert); void setVisualDeckStorageAlwaysConvert(bool _visualDeckStorageAlwaysConvert);
void setVisualDeckStorageInGame(QT_STATE_CHANGED_T value); void setVisualDeckStorageInGame(QT_STATE_CHANGED_T value);
void setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T value); void setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T value);
void setDefaultDeckEditorType(int value);
void setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T _enabled); void setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T _enabled);
void setVisualDatabaseDisplayFilterToMostRecentSetsAmount(int _amount); void setVisualDatabaseDisplayFilterToMostRecentSetsAmount(int _amount);
void setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand); void setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand);

View file

@ -81,12 +81,12 @@ void Logger::closeLogfileSession()
fileHandle.close(); fileHandle.close();
} }
void Logger::log(QtMsgType /* type */, const QMessageLogContext & /* ctx */, const QString message) void Logger::log(QtMsgType /* type */, const QMessageLogContext & /* ctx */, const QString &message)
{ {
QMetaObject::invokeMethod(this, "internalLog", Qt::QueuedConnection, Q_ARG(const QString &, message)); QMetaObject::invokeMethod(this, "internalLog", Qt::QueuedConnection, Q_ARG(const QString &, message));
} }
void Logger::internalLog(QString message) void Logger::internalLog(const QString &message)
{ {
QMutexLocker locker(&mutex); QMutexLocker locker(&mutex);

View file

@ -28,7 +28,7 @@ public:
} }
void logToFile(bool enabled); void logToFile(bool enabled);
void log(QtMsgType type, const QMessageLogContext &ctx, QString message); void log(QtMsgType type, const QMessageLogContext &ctx, const QString &message);
QString getClientVersion(); QString getClientVersion();
QString getClientOperatingSystem(); QString getClientOperatingSystem();
QString getSystemArchitecture(); QString getSystemArchitecture();
@ -57,10 +57,10 @@ protected:
void closeLogfileSession(); void closeLogfileSession();
protected slots: protected slots:
void internalLog(QString message); void internalLog(const QString &message);
signals: signals:
void logEntryAdded(QString message); void logEntryAdded(const QString &message);
}; };
#endif #endif

View file

@ -203,7 +203,7 @@ Controlla se la cartella è valida e prova ancora.</translation>
<message> <message>
<location filename="src/dialogs/dlg_settings.cpp" line="550"/> <location filename="src/dialogs/dlg_settings.cpp" line="550"/>
<source>Minimum overlap percentage of cards on the stack and in vertical hand</source> <source>Minimum overlap percentage of cards on the stack and in vertical hand</source>
<translation>Sovrapposizione % minima delle carte in pila e nella mano verticale</translation> <translation>Sovrapposizione % minima delle carte in pila e nella mano verticale:</translation>
</message> </message>
<message> <message>
<location filename="src/dialogs/dlg_settings.cpp" line="551"/> <location filename="src/dialogs/dlg_settings.cpp" line="551"/>
@ -2770,13 +2770,13 @@ Per favore, visitate la pagina di download per aggiornare manualmente.</translat
<location filename="src/dialogs/dlg_update.cpp" line="154"/> <location filename="src/dialogs/dlg_update.cpp" line="154"/>
<location filename="src/dialogs/dlg_update.cpp" line="165"/> <location filename="src/dialogs/dlg_update.cpp" line="165"/>
<source>Released</source> <source>Released</source>
<translation>Rilasciato</translation> <translation>Data di rilascio</translation>
</message> </message>
<message> <message>
<location filename="src/dialogs/dlg_update.cpp" line="155"/> <location filename="src/dialogs/dlg_update.cpp" line="155"/>
<location filename="src/dialogs/dlg_update.cpp" line="166"/> <location filename="src/dialogs/dlg_update.cpp" line="166"/>
<source>Changelog</source> <source>Changelog</source>
<translation>Cambiamenti della versione</translation> <translation>Changelog</translation>
</message> </message>
<message> <message>
<location filename="src/dialogs/dlg_update.cpp" line="156"/> <location filename="src/dialogs/dlg_update.cpp" line="156"/>
@ -2789,13 +2789,14 @@ Per favore, visitate la pagina di download per aggiornare manualmente.</translat
You may have to manually download the new version.</source> You may have to manually download the new version.</source>
<oldsource>Unfortunately there are no download packages available for your operating system. <oldsource>Unfortunately there are no download packages available for your operating system.
You may have to build from source yourself.</oldsource> You may have to build from source yourself.</oldsource>
<translation type="unfinished"/> <translation>Purtroppo l&apos;aggiornamento automatico non è riuscito a trovare una versione compatibile.
Dovrai scaricare la nuova versione manualmente.</translation>
</message> </message>
<message> <message>
<location filename="src/dialogs/dlg_update.cpp" line="171"/> <location filename="src/dialogs/dlg_update.cpp" line="171"/>
<source>Please check the &lt;a href=&quot;%1&quot;&gt;releases page&lt;/a&gt; on our Github and download the build for your system.</source> <source>Please check the &lt;a href=&quot;%1&quot;&gt;releases page&lt;/a&gt; on our Github and download the build for your system.</source>
<oldsource>Please check the download page manually and visit the wiki for instructions on compiling.</oldsource> <oldsource>Please check the download page manually and visit the wiki for instructions on compiling.</oldsource>
<translation type="unfinished"/> <translation>Per favore, controlla la &lt;a href=&quot;%1&quot;&gt;pagina delle release&lt;/a&gt; sul nostro Github e scarica la versione giusta per il tuo sistema.</translation>
</message> </message>
<message> <message>
<location filename="src/dialogs/dlg_update.cpp" line="208"/> <location filename="src/dialogs/dlg_update.cpp" line="208"/>
@ -3668,7 +3669,7 @@ La tua versione è la %1, la versione online è la %2.</translation>
<message> <message>
<location filename="src/client/ui/window_main.cpp" line="660"/> <location filename="src/client/ui/window_main.cpp" line="660"/>
<source>Start &amp;local game...</source> <source>Start &amp;local game...</source>
<translation>Inizia &amp;partita in locale...</translation> <translation>Inizia partita in &amp;locale...</translation>
</message> </message>
<message> <message>
<location filename="src/client/ui/window_main.cpp" line="661"/> <location filename="src/client/ui/window_main.cpp" line="661"/>
@ -3688,7 +3689,7 @@ La tua versione è la %1, la versione online è la %2.</translation>
<message> <message>
<location filename="src/client/ui/window_main.cpp" line="664"/> <location filename="src/client/ui/window_main.cpp" line="664"/>
<source>&amp;Restore password...</source> <source>&amp;Restore password...</source>
<translation>&amp;Recupera password...</translation> <translation>Recupera &amp;password...</translation>
</message> </message>
<message> <message>
<location filename="src/client/ui/window_main.cpp" line="665"/> <location filename="src/client/ui/window_main.cpp" line="665"/>
@ -3713,7 +3714,7 @@ La tua versione è la %1, la versione online è la %2.</translation>
<message> <message>
<location filename="src/client/ui/window_main.cpp" line="675"/> <location filename="src/client/ui/window_main.cpp" line="675"/>
<source>C&amp;ard Database</source> <source>C&amp;ard Database</source>
<translation>&amp;Database delle carte</translation> <translation>&amp;Database carte</translation>
</message> </message>
<message> <message>
<location filename="src/client/ui/window_main.cpp" line="676"/> <location filename="src/client/ui/window_main.cpp" line="676"/>
@ -3916,7 +3917,7 @@ Scopri metodi alternativi per visualizzare i set o disabilitare set ed effetti n
<message> <message>
<location filename="src/client/ui/window_main.cpp" line="1226"/> <location filename="src/client/ui/window_main.cpp" line="1226"/>
<source>An error occurred when attempting to write to the process. For example, the process may not be running, or it may have closed its input channel.</source> <source>An error occurred when attempting to write to the process. For example, the process may not be running, or it may have closed its input channel.</source>
<translation type="unfinished"/> <translation>Si è verificato un errore nel cercare di scrivere al processo. Ad esempio, il processo potrebbe non essere in esecuzione, o potrebbe aver chiuso il suo canale di input.</translation>
</message> </message>
<message> <message>
<location filename="src/client/ui/window_main.cpp" line="1230"/> <location filename="src/client/ui/window_main.cpp" line="1230"/>
@ -5850,7 +5851,7 @@ Il database delle carte verrà ricaricato.</translation>
<message> <message>
<location filename="src/server/remote/remote_replay_list_tree_widget.cpp" line="157"/> <location filename="src/server/remote/remote_replay_list_tree_widget.cpp" line="157"/>
<source>Time started</source> <source>Time started</source>
<translation>Inizio tempo</translation> <translation>Inizio</translation>
</message> </message>
<message> <message>
<location filename="src/server/remote/remote_replay_list_tree_widget.cpp" line="159"/> <location filename="src/server/remote/remote_replay_list_tree_widget.cpp" line="159"/>
@ -7122,7 +7123,7 @@ Più informazioni inserisci, più specifici saranno i risultati.</translation>
<message> <message>
<location filename="src/client/tabs/tab_supervisor.cpp" line="178"/> <location filename="src/client/tabs/tab_supervisor.cpp" line="178"/>
<source>Deck Editor</source> <source>Deck Editor</source>
<translation>Editor dei mazzi</translation> <translation>&amp;Editor dei mazzi</translation>
</message> </message>
<message> <message>
<location filename="src/client/tabs/tab_supervisor.cpp" line="179"/> <location filename="src/client/tabs/tab_supervisor.cpp" line="179"/>
@ -7142,12 +7143,12 @@ Più informazioni inserisci, più specifici saranno i risultati.</translation>
<message> <message>
<location filename="src/client/tabs/tab_supervisor.cpp" line="182"/> <location filename="src/client/tabs/tab_supervisor.cpp" line="182"/>
<source>Deck Storage</source> <source>Deck Storage</source>
<translation>Archivio mazzi</translation> <translation>&amp;Archivio mazzi</translation>
</message> </message>
<message> <message>
<location filename="src/client/tabs/tab_supervisor.cpp" line="183"/> <location filename="src/client/tabs/tab_supervisor.cpp" line="183"/>
<source>Game Replays</source> <source>Game Replays</source>
<translation>Replay partite</translation> <translation>&amp;Replay partite</translation>
</message> </message>
<message> <message>
<location filename="src/client/tabs/tab_supervisor.cpp" line="184"/> <location filename="src/client/tabs/tab_supervisor.cpp" line="184"/>
@ -8299,12 +8300,12 @@ Se pregato di evitare di continuare questa attività o potrebbero venire presi u
<location filename="src/settings/shortcuts_settings.h" line="176"/> <location filename="src/settings/shortcuts_settings.h" line="176"/>
<source>Analyze Deck (deckstats.net)</source> <source>Analyze Deck (deckstats.net)</source>
<oldsource>Analyze Deck</oldsource> <oldsource>Analyze Deck</oldsource>
<translation>Analizza Mazzo (deckstats.net)</translation> <translation>Analizza mazzo (deckstats.net)</translation>
</message> </message>
<message> <message>
<location filename="src/settings/shortcuts_settings.h" line="180"/> <location filename="src/settings/shortcuts_settings.h" line="180"/>
<source>Analyze Deck (tappedout.net)</source> <source>Analyze Deck (tappedout.net)</source>
<translation>Analizza Mazzo (tappedout.net)</translation> <translation>Analizza mazzo (tappedout.net)</translation>
</message> </message>
<message> <message>
<location filename="src/settings/shortcuts_settings.h" line="183"/> <location filename="src/settings/shortcuts_settings.h" line="183"/>
@ -8496,7 +8497,7 @@ Se pregato di evitare di continuare questa attività o potrebbero venire presi u
<location filename="src/settings/shortcuts_settings.h" line="301"/> <location filename="src/settings/shortcuts_settings.h" line="301"/>
<location filename="src/settings/shortcuts_settings.h" line="346"/> <location filename="src/settings/shortcuts_settings.h" line="346"/>
<source>Set Red Counters...</source> <source>Set Red Counters...</source>
<translation>Imposta Contatore Rosso...</translation> <translation>Imposta Segnalino Rosso...</translation>
</message> </message>
<message> <message>
<location filename="src/settings/shortcuts_settings.h" line="304"/> <location filename="src/settings/shortcuts_settings.h" line="304"/>

View file

@ -27,4 +27,5 @@ message Command_CreateToken {
optional TargetMode target_mode = 11; optional TargetMode target_mode = 11;
optional string card_provider_id = 12; optional string card_provider_id = 12;
optional bool face_down = 13;
} }

View file

@ -15,4 +15,5 @@ message Event_CreateToken {
optional sint32 x = 8; optional sint32 x = 8;
optional sint32 y = 9; optional sint32 y = 9;
optional string card_provider_id = 10; optional string card_provider_id = 10;
optional bool face_down = 11;
} }

View file

@ -386,13 +386,23 @@ void Server_Player::revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorag
} }
} }
static Event_CreateToken makeCreateTokenEvent(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord) /**
* Creates the create token event.
* By default, will set event's name and color fields to empty if the token is face-down
*/
static Event_CreateToken
makeCreateTokenEvent(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord, bool revealFacedownInfo = false)
{ {
Event_CreateToken event; Event_CreateToken event;
event.set_zone_name(zone->getName().toStdString()); event.set_zone_name(zone->getName().toStdString());
event.set_card_id(card->getId()); event.set_card_id(card->getId());
event.set_face_down(card->getFaceDown());
if (!card->getFaceDown() || revealFacedownInfo) {
event.set_card_name(card->getName().toStdString()); event.set_card_name(card->getName().toStdString());
event.set_card_provider_id(card->getProviderId().toStdString()); event.set_card_provider_id(card->getProviderId().toStdString());
}
event.set_color(card->getColor().toStdString()); event.set_color(card->getColor().toStdString());
event.set_pt(card->getPT().toStdString()); event.set_pt(card->getPT().toStdString());
event.set_annotation(card->getAnnotation().toStdString()); event.set_annotation(card->getAnnotation().toStdString());
@ -401,7 +411,6 @@ static Event_CreateToken makeCreateTokenEvent(Server_CardZone *zone, Server_Card
event.set_y(yCoord); event.set_y(yCoord);
return event; return event;
} }
static Event_AttachCard makeAttachCardEvent(Server_Card *attachedCard, Server_Card *parentCard = nullptr) static Event_AttachCard makeAttachCardEvent(Server_Card *attachedCard, Server_Card *parentCard = nullptr)
{ {
Event_AttachCard event; Event_AttachCard event;
@ -417,6 +426,29 @@ static Event_AttachCard makeAttachCardEvent(Server_Card *attachedCard, Server_Ca
return event; return event;
} }
/**
* Determines whether moving the card from startZone to targetZone should cause the card to be destroyed.
*/
static bool
shouldDestroyOnMove(const Server_Card *card, const Server_CardZone *startZone, const Server_CardZone *targetZone)
{
if (!card->getDestroyOnZoneChange()) {
return false;
}
if (startZone->getName() == targetZone->getName()) {
return false;
}
// Allow tokens on the stack
if ((startZone->getName() == "table" || startZone->getName() == "stack") &&
(targetZone->getName() == "table" || targetZone->getName() == "stack")) {
return false;
}
return true;
}
Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges,
Server_CardZone *startzone, Server_CardZone *startzone,
const QList<const CardToMove *> &_cards, const QList<const CardToMove *> &_cards,
@ -523,7 +555,7 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges,
} }
} }
if (card->getDestroyOnZoneChange() && (startzone->getName() != targetzone->getName())) { if (shouldDestroyOnMove(card, startzone, targetzone)) {
Event_DestroyCard event; Event_DestroyCard event;
event.set_zone_name(startzone->getName().toStdString()); event.set_zone_name(startzone->getName().toStdString());
event.set_card_id(static_cast<google::protobuf::uint32>(card->getId())); event.set_card_id(static_cast<google::protobuf::uint32>(card->getId()));
@ -1471,7 +1503,8 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer
const QString cardName = nameFromStdString(cmd.card_name()); const QString cardName = nameFromStdString(cmd.card_name());
const QString cardProviderId = nameFromStdString(cmd.card_provider_id()); const QString cardProviderId = nameFromStdString(cmd.card_provider_id());
if (zone->hasCoords()) { if (zone->hasCoords()) {
xCoord = zone->getFreeGridColumn(xCoord, yCoord, cardName, false); bool dontStackSameName = cmd.face_down();
xCoord = zone->getFreeGridColumn(xCoord, yCoord, cardName, dontStackSameName);
} }
if (xCoord < 0) { if (xCoord < 0) {
xCoord = 0; xCoord = 0;
@ -1482,13 +1515,17 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer
auto *card = new Server_Card(cardName, cardProviderId, newCardId(), xCoord, yCoord); auto *card = new Server_Card(cardName, cardProviderId, newCardId(), xCoord, yCoord);
card->moveToThread(thread()); card->moveToThread(thread());
card->setPT(nameFromStdString(cmd.pt())); // Client should already prevent face-down tokens from having attributes; this just an extra server-side check
if (!cmd.face_down()) {
card->setColor(nameFromStdString(cmd.color())); card->setColor(nameFromStdString(cmd.color()));
card->setPT(nameFromStdString(cmd.pt()));
}
card->setAnnotation(nameFromStdString(cmd.annotation())); card->setAnnotation(nameFromStdString(cmd.annotation()));
card->setDestroyOnZoneChange(cmd.destroy_on_zone_change()); card->setDestroyOnZoneChange(cmd.destroy_on_zone_change());
card->setFaceDown(cmd.face_down());
zone->insertCard(card, xCoord, yCoord); zone->insertCard(card, xCoord, yCoord);
ges.enqueueGameEvent(makeCreateTokenEvent(zone, card, xCoord, yCoord), playerId); sendCreateTokenEvents(zone, card, xCoord, yCoord, ges);
// check if the token is a replacement for an existing card // check if the token is a replacement for an existing card
if (!targetCard) { if (!targetCard) {
@ -1619,6 +1656,38 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer
return Response::RespOk; return Response::RespOk;
} }
/**
* Creates and sends the events required to properly communicate the given token creation.
* Primarily written to handle creating face-down tokens.
*/
void Server_Player::sendCreateTokenEvents(Server_CardZone *zone,
Server_Card *card,
int xCoord,
int yCoord,
GameEventStorage &ges)
{
// Token is not face-down; things are easy
if (!card->getFaceDown()) {
ges.enqueueGameEvent(makeCreateTokenEvent(zone, card, xCoord, yCoord), playerId);
return;
}
// Token is face-down. We have to send different info to each player
auto eventOthers = makeCreateTokenEvent(zone, card, xCoord, yCoord, false);
ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
auto eventPrivate = makeCreateTokenEvent(zone, card, xCoord, yCoord, true);
ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId);
// Event_CreateToken didn't use to have face_down field; send attribute event afterward for backwards compatibility
Event_SetCardAttr event;
event.set_zone_name(zone->getName().toStdString());
event.set_card_id(card->getId());
event.set_attribute(AttrFaceDown);
event.set_attr_value("1");
ges.enqueueGameEvent(event, playerId);
}
Response::ResponseCode Response::ResponseCode
Server_Player::cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) Server_Player::cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
{ {

View file

@ -84,6 +84,7 @@ private:
bool conceded; bool conceded;
bool sideboardLocked; bool sideboardLocked;
void revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorage &ges); void revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorage &ges);
void sendCreateTokenEvents(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord, GameEventStorage &ges);
public: public:
mutable QMutex playerMutex; mutable QMutex playerMutex;

View file

@ -64,6 +64,9 @@ void SettingsCache::setCardViewExpandedRowsMax(int /* value */)
void SettingsCache::setCloseEmptyCardView(const QT_STATE_CHANGED_T /* value */) void SettingsCache::setCloseEmptyCardView(const QT_STATE_CHANGED_T /* value */)
{ {
} }
void SettingsCache::setFocusCardViewSearchBar(QT_STATE_CHANGED_T /* value */)
{
}
void SettingsCache::setKnownMissingFeatures(const QString & /* _knownMissingFeatures */) void SettingsCache::setKnownMissingFeatures(const QString & /* _knownMissingFeatures */)
{ {
} }
@ -256,6 +259,9 @@ void SettingsCache::setVisualDeckStorageInGame(QT_STATE_CHANGED_T /* value */)
void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T /* value */) void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T /* value */)
{ {
} }
void SettingsCache::setDefaultDeckEditorType(int /* value */)
{
}
void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T /* _enabled */) void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T /* _enabled */)
{ {
} }

View file

@ -68,6 +68,9 @@ void SettingsCache::setCardViewExpandedRowsMax(int /* value */)
void SettingsCache::setCloseEmptyCardView(QT_STATE_CHANGED_T /* value */) void SettingsCache::setCloseEmptyCardView(QT_STATE_CHANGED_T /* value */)
{ {
} }
void SettingsCache::setFocusCardViewSearchBar(QT_STATE_CHANGED_T /* value */)
{
}
void SettingsCache::setKnownMissingFeatures(const QString & /* _knownMissingFeatures */) void SettingsCache::setKnownMissingFeatures(const QString & /* _knownMissingFeatures */)
{ {
} }
@ -260,6 +263,9 @@ void SettingsCache::setVisualDeckStorageInGame(QT_STATE_CHANGED_T /* value */)
void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T /* value */) void SettingsCache::setVisualDeckStorageSelectionAnimation(QT_STATE_CHANGED_T /* value */)
{ {
} }
void SettingsCache::setDefaultDeckEditorType(int /* value */)
{
}
void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T /* _enabled */) void SettingsCache::setVisualDatabaseDisplayFilterToMostRecentSetsEnabled(QT_STATE_CHANGED_T /* _enabled */)
{ {
} }

View file

@ -17962,9 +17962,10 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}, },
"node_modules/serialize-javascript": { "node_modules/serialize-javascript": {
"version": "6.0.0", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"randombytes": "^2.1.0" "randombytes": "^2.1.0"
} }
@ -32825,9 +32826,9 @@
} }
}, },
"serialize-javascript": { "serialize-javascript": {
"version": "6.0.0", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"requires": { "requires": {
"randombytes": "^2.1.0" "randombytes": "^2.1.0"
} }