mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
Compare commits
13 commits
2026-04-18
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1fe4c85d3 | ||
|
|
a20f3c0fb4 | ||
|
|
9226bc9ddd | ||
|
|
501c4b96d4 | ||
|
|
6ab947418c | ||
|
|
6765831b92 | ||
|
|
98c4e829f8 | ||
|
|
ccb9901b28 | ||
|
|
58a8c7d3df | ||
|
|
655a8e52a1 | ||
|
|
db235ecae8 | ||
|
|
682ac4ed0c | ||
|
|
77978c7178 |
40 changed files with 807 additions and 224 deletions
2
.github/workflows/desktop-build.yml
vendored
2
.github/workflows/desktop-build.yml
vendored
|
|
@ -46,7 +46,7 @@ concurrency:
|
||||||
jobs:
|
jobs:
|
||||||
configure:
|
configure:
|
||||||
name: Configure
|
name: Configure
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
outputs:
|
outputs:
|
||||||
tag: ${{steps.configure.outputs.tag}}
|
tag: ${{steps.configure.outputs.tag}}
|
||||||
sha: ${{steps.configure.outputs.sha}}
|
sha: ${{steps.configure.outputs.sha}}
|
||||||
|
|
|
||||||
4
.github/workflows/desktop-lint.yml
vendored
4
.github/workflows/desktop-lint.yml
vendored
|
|
@ -20,13 +20,13 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
format:
|
format:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-slim
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 20 # should be enough to find merge base
|
fetch-depth: 20 # should be enough to find merge base
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
|
||||||
2
.github/workflows/translations-pull.yml
vendored
2
.github/workflows/translations-pull.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
||||||
if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice'
|
if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice'
|
||||||
|
|
||||||
name: Pull languages
|
name: Pull languages
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
|
|
|
||||||
4
.github/workflows/translations-push.yml
vendored
4
.github/workflows/translations-push.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
||||||
if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice'
|
if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice'
|
||||||
|
|
||||||
name: Push strings
|
name: Push strings
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
|
|
@ -46,7 +46,7 @@ jobs:
|
||||||
|
|
||||||
- name: Render template
|
- name: Render template
|
||||||
id: template
|
id: template
|
||||||
uses: chuhlomin/render-template@v1
|
uses: chuhlomin/render-template/binary@v1
|
||||||
with:
|
with:
|
||||||
template: .ci/update_translation_source_strings_template.md
|
template: .ci/update_translation_source_strings_template.md
|
||||||
vars: |
|
vars: |
|
||||||
|
|
|
||||||
2
.github/workflows/web-lint.yml
vendored
2
.github/workflows/web-lint.yml
vendored
|
|
@ -10,7 +10,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ESLint:
|
ESLint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|
|
||||||
|
|
@ -74,11 +74,11 @@ endif()
|
||||||
|
|
||||||
# A project name is needed for CPack
|
# A project name is needed for CPack
|
||||||
# Version can be overriden by git tags, see cmake/getversion.cmake
|
# Version can be overriden by git tags, see cmake/getversion.cmake
|
||||||
project("Cockatrice" VERSION 2.11.0)
|
project("Cockatrice" VERSION 3.0.0)
|
||||||
|
|
||||||
# Set release name if not provided via env/cmake var
|
# Set release name if not provided via env/cmake var
|
||||||
if(NOT DEFINED GIT_TAG_RELEASENAME)
|
if(NOT DEFINED GIT_TAG_RELEASENAME)
|
||||||
set(GIT_TAG_RELEASENAME "Omenpath")
|
set(GIT_TAG_RELEASENAME "Graduation Day")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Use c++20 for all targets
|
# Use c++20 for all targets
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ SetCompressor LZMA
|
||||||
Var NormalDestDir
|
Var NormalDestDir
|
||||||
Var PortableDestDir
|
Var PortableDestDir
|
||||||
Var PortableMode
|
Var PortableMode
|
||||||
|
Var ReinstallMode
|
||||||
|
|
||||||
!include LogicLib.nsh
|
!include LogicLib.nsh
|
||||||
!include FileFunc.nsh
|
!include FileFunc.nsh
|
||||||
|
|
@ -28,13 +29,23 @@ Var PortableMode
|
||||||
!define MUI_FINISHPAGE_RUN_TEXT "Run 'Cockatrice' now"
|
!define MUI_FINISHPAGE_RUN_TEXT "Run 'Cockatrice' now"
|
||||||
!define MUI_ICON "${NSIS_SOURCE_PATH}\cockatrice\resources\appicon.ico"
|
!define MUI_ICON "${NSIS_SOURCE_PATH}\cockatrice\resources\appicon.ico"
|
||||||
|
|
||||||
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfReinstall
|
||||||
!insertmacro MUI_PAGE_WELCOME
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
|
|
||||||
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfReinstall
|
||||||
!insertmacro MUI_PAGE_LICENSE "${NSIS_SOURCE_PATH}\LICENSE"
|
!insertmacro MUI_PAGE_LICENSE "${NSIS_SOURCE_PATH}\LICENSE"
|
||||||
|
|
||||||
Page Custom PortableModePageCreate PortableModePageLeave
|
Page Custom PortableModePageCreate PortableModePageLeave
|
||||||
!define MUI_PAGE_CUSTOMFUNCTION_PRE componentsPagePre
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE componentsPagePre
|
||||||
!insertmacro MUI_PAGE_COMPONENTS
|
!insertmacro MUI_PAGE_COMPONENTS
|
||||||
|
|
||||||
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfReinstall
|
||||||
!insertmacro MUI_PAGE_DIRECTORY
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
|
|
||||||
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfReinstall
|
||||||
!insertmacro MUI_PAGE_INSTFILES
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
|
|
||||||
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfReinstall
|
||||||
!insertmacro MUI_PAGE_FINISH
|
!insertmacro MUI_PAGE_FINISH
|
||||||
|
|
||||||
!insertmacro MUI_UNPAGE_CONFIRM
|
!insertmacro MUI_UNPAGE_CONFIRM
|
||||||
|
|
@ -73,6 +84,7 @@ ${IfNot} ${Errors}
|
||||||
MessageBox MB_ICONINFORMATION|MB_SETFOREGROUND "\
|
MessageBox MB_ICONINFORMATION|MB_SETFOREGROUND "\
|
||||||
/PORTABLE : Install in portable mode$\n\
|
/PORTABLE : Install in portable mode$\n\
|
||||||
/S : Silent install$\n\
|
/S : Silent install$\n\
|
||||||
|
/R : Silent upgrade$\n\
|
||||||
/D=%directory% : Specify destination directory$\n"
|
/D=%directory% : Specify destination directory$\n"
|
||||||
Quit
|
Quit
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
@ -90,6 +102,16 @@ ${Else}
|
||||||
${EndIf}
|
${EndIf}
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
|
ClearErrors
|
||||||
|
${GetOptions} $9 "/R" $8
|
||||||
|
${IfNot} ${Errors}
|
||||||
|
StrCpy $ReinstallMode 1
|
||||||
|
SetSilent silent
|
||||||
|
SetAutoClose true
|
||||||
|
${Else}
|
||||||
|
StrCpy $ReinstallMode 0
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
${If} $InstDir == ""
|
${If} $InstDir == ""
|
||||||
; User did not use /D to specify a directory,
|
; User did not use /D to specify a directory,
|
||||||
; we need to set a default based on the install mode
|
; we need to set a default based on the install mode
|
||||||
|
|
@ -97,6 +119,22 @@ ${If} $InstDir == ""
|
||||||
${EndIf}
|
${EndIf}
|
||||||
Call SetModeDestinationFromInstdir
|
Call SetModeDestinationFromInstdir
|
||||||
|
|
||||||
|
; --- Detect portable install when using /R ---
|
||||||
|
${If} $ReinstallMode = 1
|
||||||
|
IfFileExists "$InstDir\portable.dat" 0 not_portable
|
||||||
|
StrCpy $PortableMode 1
|
||||||
|
Goto portable_done
|
||||||
|
|
||||||
|
not_portable:
|
||||||
|
StrCpy $PortableMode 0
|
||||||
|
|
||||||
|
portable_done:
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
${If} $ReinstallMode = 1
|
||||||
|
Call AutoUninstallIfNeeded
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
Function un.onInit
|
Function un.onInit
|
||||||
|
|
@ -126,8 +164,46 @@ ${Else}
|
||||||
${EndIf}
|
${EndIf}
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
|
Function SkipIfReinstall
|
||||||
|
${If} $ReinstallMode = 1
|
||||||
|
Abort
|
||||||
|
${EndIf}
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
Function AutoUninstallIfNeeded
|
||||||
|
|
||||||
|
SetShellVarContext all
|
||||||
|
|
||||||
|
; --- 32-bit uninstall ---
|
||||||
|
SetRegView 32
|
||||||
|
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString"
|
||||||
|
|
||||||
|
StrCmp $R0 "" done32
|
||||||
|
DetailPrint "Removing previous version (32-bit)..."
|
||||||
|
ExecWait '$R0'
|
||||||
|
|
||||||
|
done32:
|
||||||
|
|
||||||
|
; --- 64-bit uninstall ---
|
||||||
|
${If} ${RunningX64}
|
||||||
|
SetRegView 64
|
||||||
|
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString"
|
||||||
|
|
||||||
|
StrCmp $R0 "" done64
|
||||||
|
DetailPrint "Removing previous version (64-bit)..."
|
||||||
|
ExecWait '$R0'
|
||||||
|
|
||||||
|
done64:
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
Function PortableModePageCreate
|
Function PortableModePageCreate
|
||||||
|
|
||||||
|
${If} $ReinstallMode = 1
|
||||||
|
Abort
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
Call SetModeDestinationFromInstdir ; If the user clicks BACK on the directory page we will remember their mode specific directory
|
Call SetModeDestinationFromInstdir ; If the user clicks BACK on the directory page we will remember their mode specific directory
|
||||||
!insertmacro MUI_HEADER_TEXT "Install Mode" "Choose how you want to install Cockatrice."
|
!insertmacro MUI_HEADER_TEXT "Install Mode" "Choose how you want to install Cockatrice."
|
||||||
nsDialogs::Create 1018
|
nsDialogs::Create 1018
|
||||||
|
|
@ -159,6 +235,11 @@ ${EndIf}
|
||||||
FunctionEnd
|
FunctionEnd
|
||||||
|
|
||||||
Function componentsPagePre
|
Function componentsPagePre
|
||||||
|
|
||||||
|
${If} $ReinstallMode = 1
|
||||||
|
Return
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
${If} $PortableMode = 0
|
${If} $PortableMode = 0
|
||||||
SetShellVarContext all
|
SetShellVarContext all
|
||||||
|
|
||||||
|
|
@ -168,8 +249,12 @@ ${If} $PortableMode = 0
|
||||||
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString"
|
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString"
|
||||||
StrCmp $R0 "" done32
|
StrCmp $R0 "" done32
|
||||||
|
|
||||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst32
|
${If} $ReinstallMode = 0
|
||||||
Abort
|
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst32
|
||||||
|
Abort
|
||||||
|
${Else}
|
||||||
|
Goto uninst32
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
uninst32:
|
uninst32:
|
||||||
ClearErrors
|
ClearErrors
|
||||||
|
|
@ -184,8 +269,12 @@ ${If} $PortableMode = 0
|
||||||
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString"
|
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString"
|
||||||
StrCmp $R0 "" done64
|
StrCmp $R0 "" done64
|
||||||
|
|
||||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst64
|
${If} $ReinstallMode = 0
|
||||||
Abort
|
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst64
|
||||||
|
Abort
|
||||||
|
${Else}
|
||||||
|
Goto uninst64
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
uninst64:
|
uninst64:
|
||||||
ClearErrors
|
ClearErrors
|
||||||
|
|
@ -277,6 +366,12 @@ ${Else}
|
||||||
FileWrite $0 "PORTABLE"
|
FileWrite $0 "PORTABLE"
|
||||||
FileClose $0
|
FileClose $0
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
|
${If} $ReinstallMode = 1
|
||||||
|
IfFileExists "$INSTDIR\cockatrice.exe" 0 +2
|
||||||
|
Exec '"$INSTDIR\cockatrice.exe"'
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
Section "Start menu item" SecStartMenu
|
Section "Start menu item" SecStartMenu
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,8 @@ void TappedOutInterface::analyzeDeck(const DeckList &deck)
|
||||||
QNetworkRequest request(QUrl("https://tappedout.net/mtg-decks/paste/"));
|
QNetworkRequest request(QUrl("https://tappedout.net/mtg-decks/paste/"));
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||||
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
|
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
|
||||||
|
// we interpret the redirect and open it in the browser instead, do not follow redirects
|
||||||
|
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
|
||||||
|
|
||||||
manager->post(request, data);
|
manager->post(request, data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ MessageLogWidget::getFromStr(CardZoneLogic *zone, QString cardName, int position
|
||||||
fromStr = tr(" from the top of their library");
|
fromStr = tr(" from the top of their library");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (position >= zone->getCards().size() - 1) {
|
} else if (position == zone->getCards().size()) {
|
||||||
if (cardName.isEmpty()) {
|
if (cardName.isEmpty()) {
|
||||||
if (ownerChange) {
|
if (ownerChange) {
|
||||||
cardName = tr("the bottom card of %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName());
|
cardName = tr("the bottom card of %1's library").arg(zone->getPlayer()->getPlayerInfo()->getName());
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ void CardInfoTextWidget::setCard(const ExactCard &exactCard)
|
||||||
text += QString("<tr><td>%1</td><td width=\"5\"></td><td>%2</td></tr>")
|
text += QString("<tr><td>%1</td><td width=\"5\"></td><td>%2</td></tr>")
|
||||||
.arg(tr("Name:"), card->getName().toHtmlEscaped());
|
.arg(tr("Name:"), card->getName().toHtmlEscaped());
|
||||||
|
|
||||||
if (exactCard.getPrinting() != PrintingInfo()) {
|
if (!exactCard.getPrinting().isEmpty()) {
|
||||||
QString setShort = exactCard.getPrinting().getSet()->getShortName().toHtmlEscaped();
|
QString setShort = exactCard.getPrinting().getSet()->getShortName().toHtmlEscaped();
|
||||||
QString cardNum = exactCard.getPrinting().getProperty("num").toHtmlEscaped();
|
QString cardNum = exactCard.getPrinting().getProperty("num").toHtmlEscaped();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,14 @@ DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListStatisticsAnal
|
||||||
removeButton = new QPushButton(this);
|
removeButton = new QPushButton(this);
|
||||||
saveButton = new QPushButton(this);
|
saveButton = new QPushButton(this);
|
||||||
loadButton = new QPushButton(this);
|
loadButton = new QPushButton(this);
|
||||||
|
includeSideboardCheckBox = new QCheckBox(this);
|
||||||
|
includeSideboardCheckBox->setChecked(false);
|
||||||
|
|
||||||
controlLayout->addWidget(addButton);
|
controlLayout->addWidget(addButton);
|
||||||
controlLayout->addWidget(removeButton);
|
controlLayout->addWidget(removeButton);
|
||||||
controlLayout->addWidget(saveButton);
|
controlLayout->addWidget(saveButton);
|
||||||
controlLayout->addWidget(loadButton);
|
controlLayout->addWidget(loadButton);
|
||||||
|
controlLayout->addWidget(includeSideboardCheckBox);
|
||||||
|
|
||||||
layout->addWidget(controlContainer);
|
layout->addWidget(controlContainer);
|
||||||
|
|
||||||
|
|
@ -40,6 +44,7 @@ DeckAnalyticsWidget::DeckAnalyticsWidget(QWidget *parent, DeckListStatisticsAnal
|
||||||
connect(removeButton, &QPushButton::clicked, this, &DeckAnalyticsWidget::onRemoveSelected);
|
connect(removeButton, &QPushButton::clicked, this, &DeckAnalyticsWidget::onRemoveSelected);
|
||||||
connect(saveButton, &QPushButton::clicked, this, &DeckAnalyticsWidget::saveLayout);
|
connect(saveButton, &QPushButton::clicked, this, &DeckAnalyticsWidget::saveLayout);
|
||||||
connect(loadButton, &QPushButton::clicked, this, &DeckAnalyticsWidget::loadLayout);
|
connect(loadButton, &QPushButton::clicked, this, &DeckAnalyticsWidget::loadLayout);
|
||||||
|
connect(includeSideboardCheckBox, &QCheckBox::clicked, this, &DeckAnalyticsWidget::includeSideboardChanged);
|
||||||
|
|
||||||
// Scroll area and container
|
// Scroll area and container
|
||||||
scrollArea = new QScrollArea(this);
|
scrollArea = new QScrollArea(this);
|
||||||
|
|
@ -66,6 +71,13 @@ void DeckAnalyticsWidget::retranslateUi()
|
||||||
removeButton->setText(tr("Remove Panel"));
|
removeButton->setText(tr("Remove Panel"));
|
||||||
saveButton->setText(tr("Save Layout"));
|
saveButton->setText(tr("Save Layout"));
|
||||||
loadButton->setText(tr("Load Layout"));
|
loadButton->setText(tr("Load Layout"));
|
||||||
|
includeSideboardCheckBox->setText(tr("Include Sideboard"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeckAnalyticsWidget::includeSideboardChanged(bool checked)
|
||||||
|
{
|
||||||
|
statsAnalyzer->getConfig().includeSideboard = checked;
|
||||||
|
updateDisplays();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeckAnalyticsWidget::updateDisplays()
|
void DeckAnalyticsWidget::updateDisplays()
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include "deck_list_statistics_analyzer.h"
|
#include "deck_list_statistics_analyzer.h"
|
||||||
#include "resizable_panel.h"
|
#include "resizable_panel.h"
|
||||||
|
|
||||||
|
#include <QCheckBox>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QScrollArea>
|
#include <QScrollArea>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
@ -29,6 +30,7 @@ public slots:
|
||||||
public:
|
public:
|
||||||
explicit DeckAnalyticsWidget(QWidget *parent, DeckListStatisticsAnalyzer *analyzer);
|
explicit DeckAnalyticsWidget(QWidget *parent, DeckListStatisticsAnalyzer *analyzer);
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
void includeSideboardChanged(bool checked);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onAddPanel();
|
void onAddPanel();
|
||||||
|
|
@ -57,6 +59,8 @@ private:
|
||||||
QPushButton *saveButton;
|
QPushButton *saveButton;
|
||||||
QPushButton *loadButton;
|
QPushButton *loadButton;
|
||||||
|
|
||||||
|
QCheckBox *includeSideboardCheckBox;
|
||||||
|
|
||||||
QScrollArea *scrollArea;
|
QScrollArea *scrollArea;
|
||||||
QWidget *panelContainer;
|
QWidget *panelContainer;
|
||||||
QVBoxLayout *panelLayout;
|
QVBoxLayout *panelLayout;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,13 @@ void DeckListStatisticsAnalyzer::analyze()
|
||||||
{
|
{
|
||||||
clearData();
|
clearData();
|
||||||
|
|
||||||
QList<const DecklistCardNode *> nodes = model->getCardNodes();
|
QList<const DecklistCardNode *> nodes;
|
||||||
|
|
||||||
|
if (config.includeSideboard) {
|
||||||
|
nodes = model->getCardNodes();
|
||||||
|
} else {
|
||||||
|
nodes = model->getCardNodesForZone(DECK_ZONE_MAIN);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto node : nodes) {
|
for (auto node : nodes) {
|
||||||
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(node->getName());
|
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(node->getName());
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ struct DeckListStatisticsAnalyzerConfig
|
||||||
bool computeCategories = true;
|
bool computeCategories = true;
|
||||||
bool computeCurveBreakdowns = true;
|
bool computeCurveBreakdowns = true;
|
||||||
bool computeProbabilities = true;
|
bool computeProbabilities = true;
|
||||||
|
bool includeSideboard = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeckListStatisticsAnalyzer : public QObject
|
class DeckListStatisticsAnalyzer : public QObject
|
||||||
|
|
@ -133,6 +134,11 @@ public:
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeckListStatisticsAnalyzerConfig &getConfig()
|
||||||
|
{
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void statsUpdated();
|
void statsUpdated();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,9 @@ void DlgUpdate::downloadSuccessful(const QUrl &filepath)
|
||||||
{
|
{
|
||||||
setLabel(tr("Installing..."));
|
setLabel(tr("Installing..."));
|
||||||
// Try to open the installer. If it opens, quit Cockatrice
|
// Try to open the installer. If it opens, quit Cockatrice
|
||||||
if (QDesktopServices::openUrl(filepath)) {
|
if (QProcess::startDetached(filepath.toLocalFile(),
|
||||||
|
QStringList()
|
||||||
|
<< "/R" << QString("/D=%1").arg(QCoreApplication::applicationDirPath()))) {
|
||||||
QMetaObject::invokeMethod(static_cast<MainWindow *>(parent()), "close", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(static_cast<MainWindow *>(parent()), "close", Qt::QueuedConnection);
|
||||||
qCInfo(DlgUpdateLog) << "Opened downloaded update file successfully - closing Cockatrice";
|
qCInfo(DlgUpdateLog) << "Opened downloaded update file successfully - closing Cockatrice";
|
||||||
close();
|
close();
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ GameSelector::GameSelector(AbstractClient *_client,
|
||||||
bool filtersSetToDefault = showFilters && gameListProxyModel->areFilterParametersSetToDefaults();
|
bool filtersSetToDefault = showFilters && gameListProxyModel->areFilterParametersSetToDefaults();
|
||||||
clearFilterButton->setEnabled(!filtersSetToDefault);
|
clearFilterButton->setEnabled(!filtersSetToDefault);
|
||||||
connect(clearFilterButton, &QPushButton::clicked, this, &GameSelector::actClearFilter);
|
connect(clearFilterButton, &QPushButton::clicked, this, &GameSelector::actClearFilter);
|
||||||
|
connect(gameListProxyModel, &GamesProxyModel::filtersChanged, this, &GameSelector::checkClearFilterButtonState);
|
||||||
|
|
||||||
if (room) {
|
if (room) {
|
||||||
createButton = new QPushButton;
|
createButton = new QPushButton;
|
||||||
|
|
@ -188,15 +189,16 @@ void GameSelector::actSetFilter()
|
||||||
dlg.getShowOnlyIfSpectatorsCanChat(), dlg.getShowOnlyIfSpectatorsCanSeeHands());
|
dlg.getShowOnlyIfSpectatorsCanChat(), dlg.getShowOnlyIfSpectatorsCanSeeHands());
|
||||||
gameListProxyModel->saveFilterParameters(gameTypeMap);
|
gameListProxyModel->saveFilterParameters(gameTypeMap);
|
||||||
|
|
||||||
clearFilterButton->setEnabled(!gameListProxyModel->areFilterParametersSetToDefaults());
|
|
||||||
|
|
||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameSelector::checkClearFilterButtonState()
|
||||||
|
{
|
||||||
|
clearFilterButton->setEnabled(!gameListProxyModel->areFilterParametersSetToDefaults());
|
||||||
|
}
|
||||||
|
|
||||||
void GameSelector::actClearFilter()
|
void GameSelector::actClearFilter()
|
||||||
{
|
{
|
||||||
clearFilterButton->setEnabled(false);
|
|
||||||
|
|
||||||
gameListProxyModel->resetFilterParameters();
|
gameListProxyModel->resetFilterParameters();
|
||||||
gameListProxyModel->saveFilterParameters(gameTypeMap);
|
gameListProxyModel->saveFilterParameters(gameTypeMap);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ private slots:
|
||||||
* Updates the proxy model with selected filter parameters and refreshes the displayed game list.
|
* Updates the proxy model with selected filter parameters and refreshes the displayed game list.
|
||||||
*/
|
*/
|
||||||
void actSetFilter();
|
void actSetFilter();
|
||||||
|
void checkClearFilterButtonState();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clears all filters applied to the game list.
|
* @brief Clears all filters applied to the game list.
|
||||||
|
|
|
||||||
|
|
@ -19,32 +19,46 @@ GameSelectorQuickFilterToolBar::GameSelectorQuickFilterToolBar(QWidget *parent,
|
||||||
mainLayout->setSpacing(5);
|
mainLayout->setSpacing(5);
|
||||||
|
|
||||||
searchBar = new QLineEdit(this);
|
searchBar = new QLineEdit(this);
|
||||||
searchBar->setText(model->getCreatorNameFilters().join(", "));
|
searchBar->setText(model->getGameNameFilter());
|
||||||
connect(searchBar, &QLineEdit::textChanged, this, [this](const QString &text) { model->setGameNameFilter(text); });
|
connect(searchBar, &QLineEdit::textChanged, this, [this](const QString &text) {
|
||||||
|
applyFilters([&](auto &, auto &, auto &, auto &, auto &, auto &, auto &, QString &gameNameFilter, auto &,
|
||||||
|
auto &, auto &, auto &, auto &, auto &, auto &, auto &, auto &) { gameNameFilter = text; });
|
||||||
|
});
|
||||||
|
|
||||||
hideGamesNotCreatedByBuddiesCheckBox = new QCheckBox(this);
|
hideGamesNotCreatedByBuddiesCheckBox = new QCheckBox(this);
|
||||||
hideGamesNotCreatedByBuddiesCheckBox->setChecked(model->getHideBuddiesOnlyGames());
|
hideGamesNotCreatedByBuddiesCheckBox->setChecked(model->getHideNotBuddyCreatedGames());
|
||||||
connect(hideGamesNotCreatedByBuddiesCheckBox, &QCheckBox::toggled, this, [this](bool checked) {
|
connect(hideGamesNotCreatedByBuddiesCheckBox, &QCheckBox::toggled, this, [this](bool checked) {
|
||||||
if (checked) {
|
applyFilters([&](auto &, auto &, auto &, auto &, auto &, bool &hideNotBuddyCreatedGames, auto &, auto &,
|
||||||
QStringList buddyNames;
|
QStringList &creatorNameFilters, auto &, auto &, auto &, auto &, auto &, auto &, auto &,
|
||||||
for (auto buddy : tabSupervisor->getUserListManager()->getBuddyList().values()) {
|
auto &) {
|
||||||
buddyNames << QString::fromStdString(buddy.name());
|
hideNotBuddyCreatedGames = checked;
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
|
QStringList buddyNames;
|
||||||
|
for (auto buddy : tabSupervisor->getUserListManager()->getBuddyList().values()) {
|
||||||
|
buddyNames << QString::fromStdString(buddy.name());
|
||||||
|
}
|
||||||
|
creatorNameFilters = buddyNames;
|
||||||
|
} else {
|
||||||
|
creatorNameFilters.clear();
|
||||||
}
|
}
|
||||||
model->setCreatorNameFilters(buddyNames);
|
});
|
||||||
} else {
|
|
||||||
model->setCreatorNameFilters({});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
hideFullGamesCheckBox = new QCheckBox(this);
|
hideFullGamesCheckBox = new QCheckBox(this);
|
||||||
hideFullGamesCheckBox->setChecked(model->getHideFullGames());
|
hideFullGamesCheckBox->setChecked(model->getHideFullGames());
|
||||||
connect(hideFullGamesCheckBox, &QCheckBox::toggled, this,
|
connect(hideFullGamesCheckBox, &QCheckBox::toggled, this, [this](bool checked) {
|
||||||
[this](bool checked) { model->setHideFullGames(checked); });
|
applyFilters([&](auto &, auto &, bool &hideFullGames, auto &, auto &, auto &, auto &, auto &, auto &, auto &,
|
||||||
|
auto &, auto &, auto &, auto &, auto &, auto &, auto &) { hideFullGames = checked; });
|
||||||
|
});
|
||||||
|
|
||||||
hideStartedGamesCheckBox = new QCheckBox(this);
|
hideStartedGamesCheckBox = new QCheckBox(this);
|
||||||
hideStartedGamesCheckBox->setChecked(model->getHideGamesThatStarted());
|
hideStartedGamesCheckBox->setChecked(model->getHideGamesThatStarted());
|
||||||
connect(hideStartedGamesCheckBox, &QCheckBox::toggled, this,
|
connect(hideStartedGamesCheckBox, &QCheckBox::toggled, this, [this](bool checked) {
|
||||||
[this](bool checked) { model->setHideGamesThatStarted(checked); });
|
applyFilters([&](auto &, auto &, auto &, bool &hideGamesThatStarted, auto &, auto &, auto &, auto &, auto &,
|
||||||
|
auto &, auto &, auto &, auto &, auto &, auto &, auto &,
|
||||||
|
auto &) { hideGamesThatStarted = checked; });
|
||||||
|
});
|
||||||
|
|
||||||
filterToFormatComboBox = new QComboBox(this);
|
filterToFormatComboBox = new QComboBox(this);
|
||||||
|
|
||||||
|
|
@ -69,13 +83,15 @@ GameSelectorQuickFilterToolBar::GameSelectorQuickFilterToolBar(QWidget *parent,
|
||||||
|
|
||||||
// Update proxy model on selection change
|
// Update proxy model on selection change
|
||||||
connect(filterToFormatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
|
connect(filterToFormatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
|
||||||
QVariant data = filterToFormatComboBox->itemData(index);
|
applyFilters([&](auto &, auto &, auto &, auto &, auto &, auto &, auto &, auto &, auto &,
|
||||||
if (!data.isValid()) {
|
QSet<int> &gameTypeFilter, auto &, auto &, auto &, auto &, auto &, auto &, auto &) {
|
||||||
model->setGameTypeFilter({}); // empty = no filter
|
QVariant data = filterToFormatComboBox->itemData(index);
|
||||||
} else {
|
if (!data.isValid()) {
|
||||||
int typeId = data.toInt();
|
gameTypeFilter.clear();
|
||||||
model->setGameTypeFilter({typeId});
|
} else {
|
||||||
}
|
gameTypeFilter = {data.toInt()};
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
hideGamesNotCreatedByBuddiesCheckBox->setMinimumSize(20, 20);
|
hideGamesNotCreatedByBuddiesCheckBox->setMinimumSize(20, 20);
|
||||||
|
|
@ -96,9 +112,87 @@ GameSelectorQuickFilterToolBar::GameSelectorQuickFilterToolBar(QWidget *parent,
|
||||||
|
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
|
syncFromModel();
|
||||||
|
|
||||||
|
connect(model, &GamesProxyModel::filtersChanged, this, &GameSelectorQuickFilterToolBar::syncFromModel);
|
||||||
|
|
||||||
retranslateUi();
|
retranslateUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameSelectorQuickFilterToolBar::syncFromModel()
|
||||||
|
{
|
||||||
|
QSignalBlocker b1(searchBar);
|
||||||
|
QSignalBlocker b2(filterToFormatComboBox);
|
||||||
|
QSignalBlocker b3(hideGamesNotCreatedByBuddiesCheckBox);
|
||||||
|
QSignalBlocker b4(hideFullGamesCheckBox);
|
||||||
|
QSignalBlocker b5(hideStartedGamesCheckBox);
|
||||||
|
|
||||||
|
searchBar->setText(model->getGameNameFilter());
|
||||||
|
|
||||||
|
hideGamesNotCreatedByBuddiesCheckBox->setChecked(model->getHideNotBuddyCreatedGames());
|
||||||
|
hideFullGamesCheckBox->setChecked(model->getHideFullGames());
|
||||||
|
hideStartedGamesCheckBox->setChecked(model->getHideGamesThatStarted());
|
||||||
|
|
||||||
|
QSet<int> types = model->getGameTypeFilter();
|
||||||
|
if (types.size() == 1) {
|
||||||
|
int idx = filterToFormatComboBox->findData(*types.begin());
|
||||||
|
filterToFormatComboBox->setCurrentIndex(idx >= 0 ? idx : 0);
|
||||||
|
} else {
|
||||||
|
filterToFormatComboBox->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameSelectorQuickFilterToolBar::applyFilters(std::function<void(bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
QString &,
|
||||||
|
QStringList &,
|
||||||
|
QSet<int> &,
|
||||||
|
int &,
|
||||||
|
int &,
|
||||||
|
QTime &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &)> mutator)
|
||||||
|
{
|
||||||
|
bool hideBuddiesOnlyGames = model->getHideBuddiesOnlyGames();
|
||||||
|
bool hideIgnoredUserGames = model->getHideIgnoredUserGames();
|
||||||
|
bool hideFullGames = model->getHideFullGames();
|
||||||
|
bool hideGamesThatStarted = model->getHideGamesThatStarted();
|
||||||
|
bool hidePasswordProtectedGames = model->getHidePasswordProtectedGames();
|
||||||
|
bool hideNotBuddyCreatedGames = model->getHideNotBuddyCreatedGames();
|
||||||
|
bool hideOpenDecklistGames = model->getHideOpenDecklistGames();
|
||||||
|
|
||||||
|
QString gameNameFilter = model->getGameNameFilter();
|
||||||
|
QStringList creatorNameFilters = model->getCreatorNameFilters();
|
||||||
|
QSet<int> gameTypeFilter = model->getGameTypeFilter();
|
||||||
|
|
||||||
|
int minPlayers = model->getMaxPlayersFilterMin();
|
||||||
|
int maxPlayers = model->getMaxPlayersFilterMax();
|
||||||
|
QTime maxGameAge = model->getMaxGameAge();
|
||||||
|
|
||||||
|
bool showOnlyIfSpectatorsCanWatch = model->getShowOnlyIfSpectatorsCanWatch();
|
||||||
|
bool showSpectatorPasswordProtected = model->getShowSpectatorPasswordProtected();
|
||||||
|
bool showOnlyIfSpectatorsCanChat = model->getShowOnlyIfSpectatorsCanChat();
|
||||||
|
bool showOnlyIfSpectatorsCanSeeHands = model->getShowOnlyIfSpectatorsCanSeeHands();
|
||||||
|
|
||||||
|
mutator(hideBuddiesOnlyGames, hideIgnoredUserGames, hideFullGames, hideGamesThatStarted, hidePasswordProtectedGames,
|
||||||
|
hideNotBuddyCreatedGames, hideOpenDecklistGames, gameNameFilter, creatorNameFilters, gameTypeFilter,
|
||||||
|
minPlayers, maxPlayers, maxGameAge, showOnlyIfSpectatorsCanWatch, showSpectatorPasswordProtected,
|
||||||
|
showOnlyIfSpectatorsCanChat, showOnlyIfSpectatorsCanSeeHands);
|
||||||
|
|
||||||
|
model->setGameFilters(hideBuddiesOnlyGames, hideIgnoredUserGames, hideFullGames, hideGamesThatStarted,
|
||||||
|
hidePasswordProtectedGames, hideNotBuddyCreatedGames, hideOpenDecklistGames, gameNameFilter,
|
||||||
|
creatorNameFilters, gameTypeFilter, minPlayers, maxPlayers, maxGameAge,
|
||||||
|
showOnlyIfSpectatorsCanWatch, showSpectatorPasswordProtected, showOnlyIfSpectatorsCanChat,
|
||||||
|
showOnlyIfSpectatorsCanSeeHands);
|
||||||
|
}
|
||||||
|
|
||||||
void GameSelectorQuickFilterToolBar::retranslateUi()
|
void GameSelectorQuickFilterToolBar::retranslateUi()
|
||||||
{
|
{
|
||||||
searchBar->setPlaceholderText(tr("Filter by game name..."));
|
searchBar->setPlaceholderText(tr("Filter by game name..."));
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,24 @@ public:
|
||||||
TabSupervisor *tabSupervisor,
|
TabSupervisor *tabSupervisor,
|
||||||
GamesProxyModel *model,
|
GamesProxyModel *model,
|
||||||
const QMap<int, QString> &allGameTypes);
|
const QMap<int, QString> &allGameTypes);
|
||||||
|
void syncFromModel();
|
||||||
|
void applyFilters(std::function<void(bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
QString &,
|
||||||
|
QStringList &,
|
||||||
|
QSet<int> &,
|
||||||
|
int &,
|
||||||
|
int &,
|
||||||
|
QTime &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &,
|
||||||
|
bool &)> mutator);
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -326,6 +326,7 @@ void GamesProxyModel::setGameFilters(bool _hideBuddiesOnlyGames,
|
||||||
#else
|
#else
|
||||||
invalidateFilter();
|
invalidateFilter();
|
||||||
#endif
|
#endif
|
||||||
|
emit filtersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
int GamesProxyModel::getNumFilteredGames() const
|
int GamesProxyModel::getNumFilteredGames() const
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,9 @@ private:
|
||||||
bool showOnlyIfSpectatorsCanChat;
|
bool showOnlyIfSpectatorsCanChat;
|
||||||
bool showOnlyIfSpectatorsCanSeeHands;
|
bool showOnlyIfSpectatorsCanSeeHands;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void filtersChanged();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Constructs a GamesProxyModel.
|
* @brief Constructs a GamesProxyModel.
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,29 @@ void ArchidektApiResponseCardEntry::fromJson(const QJsonObject &json)
|
||||||
{
|
{
|
||||||
id = json.value("id").toInt();
|
id = json.value("id").toInt();
|
||||||
|
|
||||||
|
categories.clear();
|
||||||
|
|
||||||
auto categoriesJson = json.value("categories").toArray();
|
auto categoriesJson = json.value("categories").toArray();
|
||||||
|
|
||||||
for (auto category : categoriesJson) {
|
for (const auto &categoryValue : categoriesJson) {
|
||||||
categories.append(category.toString());
|
Category cat;
|
||||||
|
|
||||||
|
if (categoryValue.isObject()) {
|
||||||
|
QJsonObject obj = categoryValue.toObject();
|
||||||
|
|
||||||
|
cat.id = obj.value("id").toInt();
|
||||||
|
cat.name = obj.value("name").toString();
|
||||||
|
cat.isPremier = obj.value("isPremier").toBool();
|
||||||
|
cat.includedInDeck = obj.value("includedInDeck").toBool();
|
||||||
|
cat.includedInPrice = obj.value("includedInPrice").toBool();
|
||||||
|
} else if (categoryValue.isString()) {
|
||||||
|
cat.name = categoryValue.toString();
|
||||||
|
|
||||||
|
// assume mainboard unless known otherwise
|
||||||
|
cat.includedInDeck = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
categories.append(cat);
|
||||||
}
|
}
|
||||||
|
|
||||||
companion = json.value("companion").toBool();
|
companion = json.value("companion").toBool();
|
||||||
|
|
@ -27,7 +46,13 @@ void ArchidektApiResponseCardEntry::fromJson(const QJsonObject &json)
|
||||||
void ArchidektApiResponseCardEntry::debugPrint() const
|
void ArchidektApiResponseCardEntry::debugPrint() const
|
||||||
{
|
{
|
||||||
qDebug() << "Id:" << id;
|
qDebug() << "Id:" << id;
|
||||||
qDebug() << "Categories:" << categories;
|
for (auto category : categories) {
|
||||||
|
qDebug() << "Category ID:" << category.id;
|
||||||
|
qDebug() << "Category Name:" << category.name;
|
||||||
|
qDebug() << "Category Premier:" << category.isPremier;
|
||||||
|
qDebug() << "Category Included in Deck:" << category.includedInDeck;
|
||||||
|
qDebug() << "Category Included in Price:" << category.includedInPrice;
|
||||||
|
}
|
||||||
qDebug() << "Companion:" << companion;
|
qDebug() << "Companion:" << companion;
|
||||||
qDebug() << "FlippedDefault:" << flippedDefault;
|
qDebug() << "FlippedDefault:" << flippedDefault;
|
||||||
qDebug() << "Label:" << label;
|
qDebug() << "Label:" << label;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,15 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
|
struct Category
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
QString name;
|
||||||
|
bool isPremier;
|
||||||
|
bool includedInDeck;
|
||||||
|
bool includedInPrice;
|
||||||
|
};
|
||||||
|
|
||||||
class ArchidektApiResponseCardEntry
|
class ArchidektApiResponseCardEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -26,7 +35,7 @@ public:
|
||||||
return card;
|
return card;
|
||||||
};
|
};
|
||||||
|
|
||||||
QStringList getCategories() const
|
QList<Category> getCategories() const
|
||||||
{
|
{
|
||||||
return categories;
|
return categories;
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +47,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int id;
|
int id;
|
||||||
QStringList categories;
|
QList<Category> categories;
|
||||||
bool companion;
|
bool companion;
|
||||||
bool flippedDefault;
|
bool flippedDefault;
|
||||||
QString label;
|
QString label;
|
||||||
|
|
|
||||||
|
|
@ -63,16 +63,60 @@ ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWi
|
||||||
QString tempDeck;
|
QString tempDeck;
|
||||||
QTextStream deckStream(&tempDeck);
|
QTextStream deckStream(&tempDeck);
|
||||||
|
|
||||||
for (auto card : response.getCards()) {
|
QString mainboardText;
|
||||||
|
QString sideboardText;
|
||||||
|
|
||||||
|
QTextStream mainStream(&mainboardText);
|
||||||
|
QTextStream sideStream(&sideboardText);
|
||||||
|
|
||||||
|
for (const auto &card : response.getCards()) {
|
||||||
QString fullName = card.getCard().getOracleCard().value("name").toString();
|
QString fullName = card.getCard().getOracleCard().value("name").toString();
|
||||||
// We don't really care about the second card, the card database already has it as a relation
|
// We don't really care about the second card, the card database already has it as a relation
|
||||||
QString cleanName = fullName.split("//").first().trimmed();
|
QString cleanName = fullName.split("//").first().trimmed();
|
||||||
|
|
||||||
tempDeck += QString("%1 %2 (%3) %4\n")
|
QString line = QString("%1 %2 (%3) %4\n")
|
||||||
.arg(card.getQuantity())
|
.arg(card.getQuantity())
|
||||||
.arg(cleanName)
|
.arg(cleanName)
|
||||||
.arg(card.getCard().getEdition().getEditionCode().toUpper())
|
.arg(card.getCard().getEdition().getEditionCode().toUpper())
|
||||||
.arg(card.getCard().getCollectorNumber());
|
.arg(card.getCard().getCollectorNumber());
|
||||||
|
|
||||||
|
bool isCommander = false;
|
||||||
|
bool isSideboardCategory = false;
|
||||||
|
bool includedInDeck = false;
|
||||||
|
|
||||||
|
for (const auto &cat : card.getCategories()) {
|
||||||
|
|
||||||
|
if (cat.name.compare("Commander", Qt::CaseInsensitive) == 0) {
|
||||||
|
isCommander = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cat.name.compare("Sideboard", Qt::CaseInsensitive) == 0 ||
|
||||||
|
cat.name.compare("Maybeboard", Qt::CaseInsensitive) == 0) {
|
||||||
|
isSideboardCategory = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cat.includedInDeck) {
|
||||||
|
includedInDeck = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString target;
|
||||||
|
|
||||||
|
if (isCommander || isSideboardCategory) {
|
||||||
|
sideStream << line;
|
||||||
|
} else if (includedInDeck) {
|
||||||
|
mainStream << line;
|
||||||
|
} else {
|
||||||
|
sideStream << line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine with blank line separator
|
||||||
|
tempDeck = mainboardText;
|
||||||
|
|
||||||
|
if (!sideboardText.isEmpty()) {
|
||||||
|
tempDeck += "\n";
|
||||||
|
tempDeck += sideboardText;
|
||||||
}
|
}
|
||||||
|
|
||||||
model = new DeckListModel(this);
|
model = new DeckListModel(this);
|
||||||
|
|
|
||||||
|
|
@ -259,6 +259,9 @@ TabGame::~TabGame()
|
||||||
if (replayManager) {
|
if (replayManager) {
|
||||||
delete replayManager->replay;
|
delete replayManager->replay;
|
||||||
}
|
}
|
||||||
|
for (auto &player : game->getPlayerManager()->getPlayers()) {
|
||||||
|
player->clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabGame::updatePlayerListDockTitle()
|
void TabGame::updatePlayerListDockTitle()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef COCKATRICE_VISUAL_DATABASE_DISPLAY_FILTER_BUTTON_H
|
||||||
|
#define COCKATRICE_VISUAL_DATABASE_DISPLAY_FILTER_BUTTON_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
const QString visualDatabaseDisplayFilterButtonStyle = QString(R"(
|
||||||
|
QPushButton {
|
||||||
|
background-color: palette(button);
|
||||||
|
color: palette(button-text);
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid palette(dark);
|
||||||
|
}
|
||||||
|
QPushButton:checked {
|
||||||
|
background-color: palette(highlight);
|
||||||
|
color: palette(highlighted-text);
|
||||||
|
border: 1px solid palette(shadow);
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
#endif // COCKATRICE_VISUAL_DATABASE_DISPLAY_FILTER_BUTTON_H
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "visual_database_display_format_legality_filter_widget.h"
|
#include "visual_database_display_format_legality_filter_widget.h"
|
||||||
|
|
||||||
#include "../../../filters/filter_tree_model.h"
|
#include "../../../filters/filter_tree_model.h"
|
||||||
|
#include "visual_database_display_filter_button.h"
|
||||||
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
@ -80,8 +81,7 @@ void VisualDatabaseDisplayFormatLegalityFilterWidget::createFormatButtons()
|
||||||
for (auto it = allFormatsWithCount.begin(); it != allFormatsWithCount.end(); ++it) {
|
for (auto it = allFormatsWithCount.begin(); it != allFormatsWithCount.end(); ++it) {
|
||||||
auto *button = new QPushButton(it.key(), flowWidget);
|
auto *button = new QPushButton(it.key(), flowWidget);
|
||||||
button->setCheckable(true);
|
button->setCheckable(true);
|
||||||
button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }"
|
button->setStyleSheet(visualDatabaseDisplayFilterButtonStyle);
|
||||||
"QPushButton:checked { background-color: green; color: white; }");
|
|
||||||
|
|
||||||
flowWidget->addWidget(button);
|
flowWidget->addWidget(button);
|
||||||
formatButtons[it.key()] = button;
|
formatButtons[it.key()] = button;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "visual_database_display_main_type_filter_widget.h"
|
#include "visual_database_display_main_type_filter_widget.h"
|
||||||
|
|
||||||
#include "../../../filters/filter_tree_model.h"
|
#include "../../../filters/filter_tree_model.h"
|
||||||
|
#include "visual_database_display_filter_button.h"
|
||||||
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
@ -75,8 +76,8 @@ void VisualDatabaseDisplayMainTypeFilterWidget::createMainTypeButtons()
|
||||||
for (auto it = allMainCardTypesWithCount.begin(); it != allMainCardTypesWithCount.end(); ++it) {
|
for (auto it = allMainCardTypesWithCount.begin(); it != allMainCardTypesWithCount.end(); ++it) {
|
||||||
auto *button = new QPushButton(it.key(), flowWidget);
|
auto *button = new QPushButton(it.key(), flowWidget);
|
||||||
button->setCheckable(true);
|
button->setCheckable(true);
|
||||||
button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }"
|
|
||||||
"QPushButton:checked { background-color: green; color: white; }");
|
button->setStyleSheet(visualDatabaseDisplayFilterButtonStyle);
|
||||||
|
|
||||||
flowWidget->addWidget(button);
|
flowWidget->addWidget(button);
|
||||||
typeButtons[it.key()] = button;
|
typeButtons[it.key()] = button;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "../../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h"
|
#include "../../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h"
|
||||||
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
|
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
|
||||||
#include "../deck_editor/deck_state_manager.h"
|
#include "../deck_editor/deck_state_manager.h"
|
||||||
|
#include "visual_database_display_filter_button.h"
|
||||||
|
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
|
|
@ -95,8 +96,8 @@ void VisualDatabaseDisplayNameFilterWidget::createNameFilter(const QString &name
|
||||||
|
|
||||||
// Create a button for the filter
|
// Create a button for the filter
|
||||||
auto *button = new QPushButton(name, flowWidget);
|
auto *button = new QPushButton(name, flowWidget);
|
||||||
button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }"
|
|
||||||
"QPushButton:hover { background-color: red; color: white; }");
|
button->setStyleSheet(visualDatabaseDisplayFilterButtonStyle);
|
||||||
|
|
||||||
connect(button, &QPushButton::clicked, this, [this, name]() {
|
connect(button, &QPushButton::clicked, this, [this, name]() {
|
||||||
removeNameFilter(name);
|
removeNameFilter(name);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "../../../client/settings/cache_settings.h"
|
#include "../../../client/settings/cache_settings.h"
|
||||||
#include "../../../filters/filter_tree_model.h"
|
#include "../../../filters/filter_tree_model.h"
|
||||||
|
#include "visual_database_display_filter_button.h"
|
||||||
|
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
@ -101,8 +102,8 @@ void VisualDatabaseDisplaySetFilterWidget::createSetButtons()
|
||||||
|
|
||||||
auto *button = new QPushButton(longName + " (" + shortName + ")", flowWidget);
|
auto *button = new QPushButton(longName + " (" + shortName + ")", flowWidget);
|
||||||
button->setCheckable(true);
|
button->setCheckable(true);
|
||||||
button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }"
|
|
||||||
"QPushButton:checked { background-color: green; color: white; }");
|
button->setStyleSheet(visualDatabaseDisplayFilterButtonStyle);
|
||||||
|
|
||||||
flowWidget->addWidget(button);
|
flowWidget->addWidget(button);
|
||||||
setButtons[shortName] = button;
|
setButtons[shortName] = button;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "visual_database_display_sub_type_filter_widget.h"
|
#include "visual_database_display_sub_type_filter_widget.h"
|
||||||
|
|
||||||
#include "../../../filters/filter_tree_model.h"
|
#include "../../../filters/filter_tree_model.h"
|
||||||
|
#include "visual_database_display_filter_button.h"
|
||||||
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
|
|
@ -80,8 +81,8 @@ void VisualDatabaseDisplaySubTypeFilterWidget::createSubTypeButtons()
|
||||||
for (auto it = allSubCardTypesWithCount.begin(); it != allSubCardTypesWithCount.end(); ++it) {
|
for (auto it = allSubCardTypesWithCount.begin(); it != allSubCardTypesWithCount.end(); ++it) {
|
||||||
auto *button = new QPushButton(it.key(), flowWidget);
|
auto *button = new QPushButton(it.key(), flowWidget);
|
||||||
button->setCheckable(true);
|
button->setCheckable(true);
|
||||||
button->setStyleSheet("QPushButton { background-color: lightgray; border: 1px solid gray; padding: 5px; }"
|
|
||||||
"QPushButton:checked { background-color: green; color: white; }");
|
button->setStyleSheet(visualDatabaseDisplayFilterButtonStyle);
|
||||||
|
|
||||||
flowWidget->addWidget(button);
|
flowWidget->addWidget(button);
|
||||||
typeButtons[it.key()] = button;
|
typeButtons[it.key()] = button;
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ ExactCard CardDatabaseQuerier::getRandomCard() const
|
||||||
ExactCard CardDatabaseQuerier::getCardFromSameSet(const QString &cardName, const PrintingInfo &otherPrinting) const
|
ExactCard CardDatabaseQuerier::getCardFromSameSet(const QString &cardName, const PrintingInfo &otherPrinting) const
|
||||||
{
|
{
|
||||||
// The source card does not have a printing defined, which means we can't get a card from the same set.
|
// The source card does not have a printing defined, which means we can't get a card from the same set.
|
||||||
if (otherPrinting == PrintingInfo()) {
|
if (otherPrinting.isEmpty()) {
|
||||||
return getCard({cardName});
|
return getCard({cardName});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,4 +360,4 @@ QMap<QString, int> CardDatabaseQuerier::getAllFormatsWithCount() const
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatCounts;
|
return formatCounts;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,16 @@ public:
|
||||||
return this->set == other.set && this->properties == other.properties;
|
return this->set == other.set && this->properties == other.properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief check if the info is empty, as if default constructed.
|
||||||
|
*
|
||||||
|
* @return True if both set and properties are empty, otherwise false.
|
||||||
|
*/
|
||||||
|
bool isEmpty() const
|
||||||
|
{
|
||||||
|
return set == nullptr && properties.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CardSetPtr set; ///< The set this variation belongs to.
|
CardSetPtr set; ///< The set this variation belongs to.
|
||||||
QVariantHash properties; ///< Key-value store for variation-specific attributes.
|
QVariantHash properties; ///< Key-value store for variation-specific attributes.
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@
|
||||||
#include <libcockatrice/rng/rng_abstract.h>
|
#include <libcockatrice/rng/rng_abstract.h>
|
||||||
#include <libcockatrice/utility/trice_limits.h>
|
#include <libcockatrice/utility/trice_limits.h>
|
||||||
#include <libcockatrice/utility/zone_names.h>
|
#include <libcockatrice/utility/zone_names.h>
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
Server_AbstractPlayer::Server_AbstractPlayer(Server_Game *_game,
|
Server_AbstractPlayer::Server_AbstractPlayer(Server_Game *_game,
|
||||||
int _playerId,
|
int _playerId,
|
||||||
|
|
@ -228,6 +229,37 @@ shouldBeFaceDown(const MoveCardStruct &cardStruct, const Server_CardZone *startZ
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Determines whether a set of moved cards is from the bottom of the deck
|
||||||
|
*/
|
||||||
|
static bool shouldBeFromTheBottom(const Server_CardZone *startZone, const std::set<MoveCardStruct> &cardsToMove)
|
||||||
|
{
|
||||||
|
if (!startZone) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startZone->getName() != ZoneNames::DECK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int movedCount = static_cast<int>(cardsToMove.size());
|
||||||
|
int tailStart = startZone->getCards().size() - movedCount;
|
||||||
|
if (tailStart <= 0) { // if the entire deck is moved it should not be considered from the bottom
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the move is a contiguous block at the end of the deck, fail fast when not
|
||||||
|
int expectedPosition = tailStart;
|
||||||
|
for (const auto &card : cardsToMove) {
|
||||||
|
if (card.position != expectedPosition) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++expectedPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges,
|
Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges,
|
||||||
Server_CardZone *startzone,
|
Server_CardZone *startzone,
|
||||||
const QList<const CardToMove *> &_cards,
|
const QList<const CardToMove *> &_cards,
|
||||||
|
|
@ -244,8 +276,11 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges,
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetzone->hasCoords() && (xCoord <= -1)) {
|
if (!targetzone->hasCoords()) {
|
||||||
xCoord = targetzone->getCards().size();
|
yCoord = 0;
|
||||||
|
if (xCoord <= -1) {
|
||||||
|
xCoord = targetzone->getCards().size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<MoveCardStruct> cardsToMove;
|
std::set<MoveCardStruct> cardsToMove;
|
||||||
|
|
@ -285,164 +320,21 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges,
|
||||||
bool revealTopStart = false;
|
bool revealTopStart = false;
|
||||||
bool revealTopTarget = false;
|
bool revealTopTarget = false;
|
||||||
|
|
||||||
for (auto cardStruct : cardsToMove) {
|
bool isFromBottom = shouldBeFromTheBottom(startzone, cardsToMove);
|
||||||
Server_Card *card = cardStruct.card;
|
|
||||||
int originalPosition = cardStruct.position;
|
|
||||||
|
|
||||||
bool sourceBeingLookedAt;
|
if (isFromBottom) {
|
||||||
int position = startzone->removeCard(card, sourceBeingLookedAt);
|
std::ranges::reverse_view reversedCardsToMove{cardsToMove};
|
||||||
|
for (auto card : reversedCardsToMove) {
|
||||||
// Attachment relationships can be retained when moving a card onto the opponent's table
|
processMoveCard(ges, startzone, targetzone, card, xCoord, yCoord, xIndex, revealTopStart, revealTopTarget,
|
||||||
if (startzone->getName() != targetzone->getName()) {
|
isReversed, undoingDraw);
|
||||||
// Delete all attachment relationships
|
|
||||||
if (card->getParentCard()) {
|
|
||||||
card->setParentCard(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a copy of the list because the original one gets modified during the loop
|
|
||||||
QList<Server_Card *> attachedCards = card->getAttachedCards();
|
|
||||||
for (auto &attachedCard : attachedCards) {
|
|
||||||
attachedCard->getZone()->getPlayer()->unattachCard(ges, attachedCard);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if (startzone != targetzone) {
|
for (auto card : cardsToMove) {
|
||||||
// Delete all arrows from and to the card
|
processMoveCard(ges, startzone, targetzone, card, xCoord, yCoord, xIndex, revealTopStart, revealTopTarget,
|
||||||
for (auto *player : game->getPlayers().values()) {
|
isReversed, undoingDraw);
|
||||||
QList<int> arrowsToDelete;
|
|
||||||
for (Server_Arrow *arrow : player->getArrows()) {
|
|
||||||
if ((arrow->getStartCard() == card) || (arrow->getTargetItem() == card))
|
|
||||||
arrowsToDelete.append(arrow->getId());
|
|
||||||
}
|
|
||||||
for (int j : arrowsToDelete) {
|
|
||||||
player->deleteArrow(j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldDestroyOnMove(card, startzone, targetzone)) {
|
|
||||||
Event_DestroyCard event;
|
|
||||||
event.set_zone_name(startzone->getName().toStdString());
|
|
||||||
event.set_card_id(static_cast<google::protobuf::uint32>(card->getId()));
|
|
||||||
ges.enqueueGameEvent(event, playerId);
|
|
||||||
|
|
||||||
if (Server_Card *stashedCard = card->takeStashedCard()) {
|
|
||||||
stashedCard->setId(newCardId());
|
|
||||||
ges.enqueueGameEvent(makeCreateTokenEvent(startzone, stashedCard, card->getX(), card->getY()),
|
|
||||||
playerId);
|
|
||||||
card->deleteLater();
|
|
||||||
card = stashedCard;
|
|
||||||
} else {
|
|
||||||
card->deleteLater();
|
|
||||||
card = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (card) {
|
|
||||||
++xIndex;
|
|
||||||
int newX = isReversed ? targetzone->getCards().size() - xCoord + xIndex : xCoord + xIndex;
|
|
||||||
|
|
||||||
bool faceDown = shouldBeFaceDown(cardStruct, startzone, targetzone);
|
|
||||||
|
|
||||||
if (targetzone->hasCoords()) {
|
|
||||||
newX = targetzone->getFreeGridColumn(newX, yCoord, card->getName(), faceDown);
|
|
||||||
} else {
|
|
||||||
yCoord = 0;
|
|
||||||
card->resetState(targetzone->getName() == ZoneNames::STACK);
|
|
||||||
}
|
|
||||||
|
|
||||||
targetzone->insertCard(card, newX, yCoord);
|
|
||||||
int targetLookedCards = targetzone->getCardsBeingLookedAt();
|
|
||||||
bool sourceKnownToPlayer = isReversed || (sourceBeingLookedAt && !card->getFaceDown());
|
|
||||||
if (targetzone->getType() == ServerInfo_Zone::HiddenZone && targetLookedCards >= newX) {
|
|
||||||
if (sourceKnownToPlayer) {
|
|
||||||
targetLookedCards += 1;
|
|
||||||
} else {
|
|
||||||
targetLookedCards = newX;
|
|
||||||
}
|
|
||||||
targetzone->setCardsBeingLookedAt(targetLookedCards);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool targetHiddenToOthers = faceDown || (targetzone->getType() != ServerInfo_Zone::PublicZone);
|
|
||||||
bool sourceHiddenToOthers = card->getFaceDown() || (startzone->getType() != ServerInfo_Zone::PublicZone);
|
|
||||||
|
|
||||||
int oldCardId = card->getId();
|
|
||||||
if ((faceDown && (startzone != targetzone)) || (targetzone->getPlayer() != startzone->getPlayer())) {
|
|
||||||
card->setId(targetzone->getPlayer()->newCardId());
|
|
||||||
}
|
|
||||||
card->setFaceDown(faceDown);
|
|
||||||
|
|
||||||
Event_MoveCard eventOthers;
|
|
||||||
eventOthers.set_start_player_id(startzone->getPlayer()->getPlayerId());
|
|
||||||
eventOthers.set_start_zone(startzone->getName().toStdString());
|
|
||||||
eventOthers.set_target_player_id(targetzone->getPlayer()->getPlayerId());
|
|
||||||
if (startzone != targetzone) {
|
|
||||||
eventOthers.set_target_zone(targetzone->getName().toStdString());
|
|
||||||
}
|
|
||||||
eventOthers.set_y(yCoord);
|
|
||||||
eventOthers.set_face_down(faceDown);
|
|
||||||
|
|
||||||
Event_MoveCard eventPrivate(eventOthers);
|
|
||||||
if (sourceBeingLookedAt || targetzone->getType() != ServerInfo_Zone::HiddenZone ||
|
|
||||||
startzone->getType() != ServerInfo_Zone::HiddenZone) {
|
|
||||||
eventPrivate.set_card_id(oldCardId);
|
|
||||||
eventPrivate.set_new_card_id(card->getId());
|
|
||||||
} else {
|
|
||||||
eventPrivate.set_card_id(-1);
|
|
||||||
eventPrivate.set_new_card_id(-1);
|
|
||||||
}
|
|
||||||
if (sourceKnownToPlayer || !(faceDown || targetzone->getType() == ServerInfo_Zone::HiddenZone)) {
|
|
||||||
QString privateCardName = card->getName();
|
|
||||||
eventPrivate.set_card_name(privateCardName.toStdString());
|
|
||||||
eventPrivate.set_new_card_provider_id(card->getProviderId().toStdString());
|
|
||||||
}
|
|
||||||
if (startzone->getType() == ServerInfo_Zone::HiddenZone) {
|
|
||||||
eventPrivate.set_position(position);
|
|
||||||
} else {
|
|
||||||
eventPrivate.set_position(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
eventPrivate.set_x(newX);
|
|
||||||
|
|
||||||
if (
|
|
||||||
// cards from public zones have their id known, their previous position is already known, the event does
|
|
||||||
// not accomodate for previous locations in zones with coordinates (which are always public)
|
|
||||||
(startzone->getType() != ServerInfo_Zone::PublicZone) &&
|
|
||||||
// other players are not allowed to be able to track which card is which in private zones like the hand
|
|
||||||
(startzone->getType() != ServerInfo_Zone::PrivateZone)) {
|
|
||||||
eventOthers.set_position(position);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
// other players are not allowed to be able to track which card is which in private zones like the hand
|
|
||||||
(targetzone->getType() != ServerInfo_Zone::PrivateZone)) {
|
|
||||||
eventOthers.set_x(newX);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((startzone->getType() == ServerInfo_Zone::PublicZone) ||
|
|
||||||
(targetzone->getType() == ServerInfo_Zone::PublicZone)) {
|
|
||||||
eventOthers.set_card_id(oldCardId);
|
|
||||||
if (!(sourceHiddenToOthers && targetHiddenToOthers)) {
|
|
||||||
QString publicCardName = card->getName();
|
|
||||||
eventOthers.set_card_name(publicCardName.toStdString());
|
|
||||||
eventOthers.set_new_card_provider_id(card->getProviderId().toStdString());
|
|
||||||
}
|
|
||||||
eventOthers.set_new_card_id(card->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId);
|
|
||||||
ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
|
|
||||||
|
|
||||||
if (originalPosition == 0) {
|
|
||||||
revealTopStart = true;
|
|
||||||
}
|
|
||||||
if (newX == 0) {
|
|
||||||
revealTopTarget = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle side effects for this card
|
|
||||||
onCardBeingMoved(ges, cardStruct, startzone, targetzone, undoingDraw);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (revealTopStart) {
|
if (revealTopStart) {
|
||||||
revealTopCardIfNeeded(startzone, ges);
|
revealTopCardIfNeeded(startzone, ges);
|
||||||
}
|
}
|
||||||
|
|
@ -462,6 +354,174 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges,
|
||||||
return Response::RespOk;
|
return Response::RespOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Server_AbstractPlayer::processMoveCard(GameEventStorage &ges,
|
||||||
|
Server_CardZone *startzone,
|
||||||
|
Server_CardZone *targetzone,
|
||||||
|
MoveCardStruct cardStruct,
|
||||||
|
int xCoord,
|
||||||
|
int yCoord,
|
||||||
|
int &xIndex,
|
||||||
|
bool &revealTopStart,
|
||||||
|
bool &revealTopTarget,
|
||||||
|
bool isReversed,
|
||||||
|
bool undoingDraw)
|
||||||
|
{
|
||||||
|
Server_Card *card = cardStruct.card;
|
||||||
|
int originalPosition = cardStruct.position;
|
||||||
|
|
||||||
|
bool sourceBeingLookedAt;
|
||||||
|
int position = startzone->removeCard(card, sourceBeingLookedAt);
|
||||||
|
|
||||||
|
// Attachment relationships can be retained when moving a card onto the opponent's table
|
||||||
|
if (startzone->getName() != targetzone->getName()) {
|
||||||
|
// Delete all attachment relationships
|
||||||
|
if (card->getParentCard()) {
|
||||||
|
card->setParentCard(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a copy of the list because the original one gets modified during the loop
|
||||||
|
QList<Server_Card *> attachedCards = card->getAttachedCards();
|
||||||
|
for (auto &attachedCard : attachedCards) {
|
||||||
|
attachedCard->getZone()->getPlayer()->unattachCard(ges, attachedCard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startzone != targetzone) {
|
||||||
|
// Delete all arrows from and to the card
|
||||||
|
for (auto *player : game->getPlayers().values()) {
|
||||||
|
QList<int> arrowsToDelete;
|
||||||
|
for (Server_Arrow *arrow : player->getArrows()) {
|
||||||
|
if ((arrow->getStartCard() == card) || (arrow->getTargetItem() == card))
|
||||||
|
arrowsToDelete.append(arrow->getId());
|
||||||
|
}
|
||||||
|
for (int j : arrowsToDelete) {
|
||||||
|
player->deleteArrow(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldDestroyOnMove(card, startzone, targetzone)) {
|
||||||
|
Event_DestroyCard event;
|
||||||
|
event.set_zone_name(startzone->getName().toStdString());
|
||||||
|
event.set_card_id(static_cast<google::protobuf::uint32>(card->getId()));
|
||||||
|
ges.enqueueGameEvent(event, playerId);
|
||||||
|
|
||||||
|
if (Server_Card *stashedCard = card->takeStashedCard()) {
|
||||||
|
stashedCard->setId(newCardId());
|
||||||
|
ges.enqueueGameEvent(makeCreateTokenEvent(startzone, stashedCard, card->getX(), card->getY()), playerId);
|
||||||
|
card->deleteLater();
|
||||||
|
card = stashedCard;
|
||||||
|
} else {
|
||||||
|
card->deleteLater();
|
||||||
|
card = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card) {
|
||||||
|
++xIndex;
|
||||||
|
int newX = isReversed ? targetzone->getCards().size() - xCoord + xIndex : xCoord + xIndex;
|
||||||
|
|
||||||
|
bool faceDown = shouldBeFaceDown(cardStruct, startzone, targetzone);
|
||||||
|
|
||||||
|
if (targetzone->hasCoords()) {
|
||||||
|
newX = targetzone->getFreeGridColumn(newX, yCoord, card->getName(), faceDown);
|
||||||
|
} else {
|
||||||
|
card->resetState(targetzone->getName() == ZoneNames::STACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
targetzone->insertCard(card, newX, yCoord);
|
||||||
|
int targetLookedCards = targetzone->getCardsBeingLookedAt();
|
||||||
|
bool sourceKnownToPlayer = isReversed || (sourceBeingLookedAt && !card->getFaceDown());
|
||||||
|
if (targetzone->getType() == ServerInfo_Zone::HiddenZone && targetLookedCards >= newX) {
|
||||||
|
if (sourceKnownToPlayer) {
|
||||||
|
targetLookedCards += 1;
|
||||||
|
} else {
|
||||||
|
targetLookedCards = newX;
|
||||||
|
}
|
||||||
|
targetzone->setCardsBeingLookedAt(targetLookedCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool targetHiddenToOthers = faceDown || (targetzone->getType() != ServerInfo_Zone::PublicZone);
|
||||||
|
bool sourceHiddenToOthers = card->getFaceDown() || (startzone->getType() != ServerInfo_Zone::PublicZone);
|
||||||
|
|
||||||
|
int oldCardId = card->getId();
|
||||||
|
if ((faceDown && (startzone != targetzone)) || (targetzone->getPlayer() != startzone->getPlayer())) {
|
||||||
|
card->setId(targetzone->getPlayer()->newCardId());
|
||||||
|
}
|
||||||
|
card->setFaceDown(faceDown);
|
||||||
|
|
||||||
|
Event_MoveCard eventOthers;
|
||||||
|
eventOthers.set_start_player_id(startzone->getPlayer()->getPlayerId());
|
||||||
|
eventOthers.set_start_zone(startzone->getName().toStdString());
|
||||||
|
eventOthers.set_target_player_id(targetzone->getPlayer()->getPlayerId());
|
||||||
|
if (startzone != targetzone) {
|
||||||
|
eventOthers.set_target_zone(targetzone->getName().toStdString());
|
||||||
|
}
|
||||||
|
eventOthers.set_y(yCoord);
|
||||||
|
eventOthers.set_face_down(faceDown);
|
||||||
|
|
||||||
|
Event_MoveCard eventPrivate(eventOthers);
|
||||||
|
if (sourceBeingLookedAt || targetzone->getType() != ServerInfo_Zone::HiddenZone ||
|
||||||
|
startzone->getType() != ServerInfo_Zone::HiddenZone) {
|
||||||
|
eventPrivate.set_card_id(oldCardId);
|
||||||
|
eventPrivate.set_new_card_id(card->getId());
|
||||||
|
} else {
|
||||||
|
eventPrivate.set_card_id(-1);
|
||||||
|
eventPrivate.set_new_card_id(-1);
|
||||||
|
}
|
||||||
|
if (sourceKnownToPlayer || !(faceDown || targetzone->getType() == ServerInfo_Zone::HiddenZone)) {
|
||||||
|
QString privateCardName = card->getName();
|
||||||
|
eventPrivate.set_card_name(privateCardName.toStdString());
|
||||||
|
eventPrivate.set_new_card_provider_id(card->getProviderId().toStdString());
|
||||||
|
}
|
||||||
|
if (startzone->getType() == ServerInfo_Zone::HiddenZone) {
|
||||||
|
eventPrivate.set_position(position);
|
||||||
|
} else {
|
||||||
|
eventPrivate.set_position(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
eventPrivate.set_x(newX);
|
||||||
|
|
||||||
|
if (
|
||||||
|
// cards from public zones have their id known, their previous position is already known, the event does
|
||||||
|
// not accomodate for previous locations in zones with coordinates (which are always public)
|
||||||
|
(startzone->getType() != ServerInfo_Zone::PublicZone) &&
|
||||||
|
// other players are not allowed to be able to track which card is which in private zones like the hand
|
||||||
|
(startzone->getType() != ServerInfo_Zone::PrivateZone)) {
|
||||||
|
eventOthers.set_position(position);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
// other players are not allowed to be able to track which card is which in private zones like the hand
|
||||||
|
(targetzone->getType() != ServerInfo_Zone::PrivateZone)) {
|
||||||
|
eventOthers.set_x(newX);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((startzone->getType() == ServerInfo_Zone::PublicZone) ||
|
||||||
|
(targetzone->getType() == ServerInfo_Zone::PublicZone)) {
|
||||||
|
eventOthers.set_card_id(oldCardId);
|
||||||
|
if (!(sourceHiddenToOthers && targetHiddenToOthers)) {
|
||||||
|
QString publicCardName = card->getName();
|
||||||
|
eventOthers.set_card_name(publicCardName.toStdString());
|
||||||
|
eventOthers.set_new_card_provider_id(card->getProviderId().toStdString());
|
||||||
|
}
|
||||||
|
eventOthers.set_new_card_id(card->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId);
|
||||||
|
ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
|
||||||
|
|
||||||
|
if (originalPosition == 0) {
|
||||||
|
revealTopStart = true;
|
||||||
|
}
|
||||||
|
if (newX == 0) {
|
||||||
|
revealTopTarget = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle side effects for this card
|
||||||
|
onCardBeingMoved(ges, cardStruct, startzone, targetzone, undoingDraw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Server_AbstractPlayer::onCardBeingMoved(GameEventStorage &ges,
|
void Server_AbstractPlayer::onCardBeingMoved(GameEventStorage &ges,
|
||||||
const MoveCardStruct &cardStruct,
|
const MoveCardStruct &cardStruct,
|
||||||
Server_CardZone *startzone,
|
Server_CardZone *startzone,
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,19 @@ public:
|
||||||
bool fixFreeSpaces = true,
|
bool fixFreeSpaces = true,
|
||||||
bool undoingDraw = false,
|
bool undoingDraw = false,
|
||||||
bool isReversed = false);
|
bool isReversed = false);
|
||||||
|
|
||||||
|
void processMoveCard(GameEventStorage &ges,
|
||||||
|
Server_CardZone *startzone,
|
||||||
|
Server_CardZone *targetzone,
|
||||||
|
MoveCardStruct cardStruct,
|
||||||
|
int xCoord,
|
||||||
|
int yCoord,
|
||||||
|
int &xIndex,
|
||||||
|
bool &revealTopStart,
|
||||||
|
bool &revealTopTarget,
|
||||||
|
bool isReversed,
|
||||||
|
bool undoingDraw);
|
||||||
|
|
||||||
virtual void onCardBeingMoved(GameEventStorage &ges,
|
virtual void onCardBeingMoved(GameEventStorage &ges,
|
||||||
const MoveCardStruct &cardStruct,
|
const MoveCardStruct &cardStruct,
|
||||||
Server_CardZone *startzone,
|
Server_CardZone *startzone,
|
||||||
|
|
|
||||||
|
|
@ -366,8 +366,6 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList
|
||||||
auto found_iter = splitCards.find(name + numProperty);
|
auto found_iter = splitCards.find(name + numProperty);
|
||||||
if (found_iter == splitCards.end()) {
|
if (found_iter == splitCards.end()) {
|
||||||
splitCards.insert(name + numProperty, {{split}, name});
|
splitCards.insert(name + numProperty, {{split}, name});
|
||||||
} else if (layout == "adventure" || layout == "prepare") {
|
|
||||||
found_iter->first.insert(0, split);
|
|
||||||
} else {
|
} else {
|
||||||
found_iter->first.append(split);
|
found_iter->first.append(split);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,4 +65,5 @@ target_link_libraries(
|
||||||
add_subdirectory(card_zone_algorithms)
|
add_subdirectory(card_zone_algorithms)
|
||||||
add_subdirectory(carddatabase)
|
add_subdirectory(carddatabase)
|
||||||
add_subdirectory(loading_from_clipboard)
|
add_subdirectory(loading_from_clipboard)
|
||||||
|
add_subdirectory(movecard_tests)
|
||||||
add_subdirectory(oracle)
|
add_subdirectory(oracle)
|
||||||
|
|
|
||||||
16
tests/movecard_tests/CMakeLists.txt
Executable file
16
tests/movecard_tests/CMakeLists.txt
Executable file
|
|
@ -0,0 +1,16 @@
|
||||||
|
add_executable(reverse_card_move_test reverse_card_move_test.cpp)
|
||||||
|
|
||||||
|
if(NOT GTEST_FOUND)
|
||||||
|
add_dependencies(reverse_card_move_test gtest)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
reverse_card_move_test
|
||||||
|
PRIVATE libcockatrice_network_server_remote
|
||||||
|
PRIVATE libcockatrice_rng
|
||||||
|
PRIVATE Threads::Threads
|
||||||
|
PRIVATE ${GTEST_BOTH_LIBRARIES}
|
||||||
|
PRIVATE ${TEST_QT_MODULES}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(NAME reverse_card_move_test COMMAND reverse_card_move_test)
|
||||||
91
tests/movecard_tests/reverse_card_move_test.cpp
Normal file
91
tests/movecard_tests/reverse_card_move_test.cpp
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
#include "game/server_abstract_player.h"
|
||||||
|
#include "game/server_card.h"
|
||||||
|
#include "game/server_cardzone.h"
|
||||||
|
#include "game/server_game.h"
|
||||||
|
#include "server_response_containers.h"
|
||||||
|
#include "server_room.h"
|
||||||
|
#include "server_test_helpers.h"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||||
|
#include <libcockatrice/rng/rng_abstract.h>
|
||||||
|
#include <libcockatrice/utility/zone_names.h>
|
||||||
|
|
||||||
|
RNG_Abstract *rng = nullptr; // this needs to be defined due to other functions in server
|
||||||
|
|
||||||
|
TEST(ReverseCardMoveTest, MoveCardFromBottomTest)
|
||||||
|
{
|
||||||
|
ServerInfo_User user;
|
||||||
|
user.set_name("test-user");
|
||||||
|
|
||||||
|
// instantiate a fake server instance
|
||||||
|
FakeServer server;
|
||||||
|
Server_Room room(0, 0, "", "", "", "", false, "", {}, &server);
|
||||||
|
Server_Game game(user, 1, "", "", 2, QList<int>(), false, false, false, false, false, false, 20, false, &room);
|
||||||
|
Server_AbstractPlayer player(&game, 1, user, false, nullptr);
|
||||||
|
Server_CardZone deckZone(&player, ZoneNames::DECK, true, ServerInfo_Zone::PublicZone);
|
||||||
|
Server_CardZone exileZone(&player, ZoneNames::EXILE, true, ServerInfo_Zone::PublicZone);
|
||||||
|
|
||||||
|
// setup the deck with 20 useless cards
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
auto *cardUseless = new Server_Card({"Card Useless", "card-Useless"}, player.newCardId(), i, 0);
|
||||||
|
deckZone.insertCard(cardUseless, i, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add 4 cards to the end of it
|
||||||
|
auto *cardA = new Server_Card({"Card A", "card-a"}, player.newCardId(), 20, 0);
|
||||||
|
auto *cardB = new Server_Card({"Card B", "card-b"}, player.newCardId(), 21, 0);
|
||||||
|
auto *cardC = new Server_Card({"Card C", "card-c"}, player.newCardId(), 22, 0);
|
||||||
|
auto *cardD = new Server_Card({"Card D", "card-d"}, player.newCardId(), 23, 0);
|
||||||
|
|
||||||
|
deckZone.insertCard(cardA, 20, 0);
|
||||||
|
deckZone.insertCard(cardB, 21, 0);
|
||||||
|
deckZone.insertCard(cardC, 22, 0);
|
||||||
|
deckZone.insertCard(cardD, 23, 0);
|
||||||
|
|
||||||
|
// try to move them, with the expected client given order (n-3, n-2, n-1, n)
|
||||||
|
CardToMove moveA;
|
||||||
|
moveA.set_card_id(cardA->getId());
|
||||||
|
CardToMove moveB;
|
||||||
|
moveB.set_card_id(cardB->getId());
|
||||||
|
CardToMove moveC;
|
||||||
|
moveC.set_card_id(cardC->getId());
|
||||||
|
CardToMove moveD;
|
||||||
|
moveD.set_card_id(cardD->getId());
|
||||||
|
|
||||||
|
QList<const CardToMove *> cardsToMove = {&moveA, &moveB, &moveC, &moveD};
|
||||||
|
GameEventStorage ges;
|
||||||
|
|
||||||
|
const auto response = player.moveCard(ges, &deckZone, cardsToMove, &exileZone, 0, 0, false, false, false);
|
||||||
|
|
||||||
|
EXPECT_EQ(response, Response::RespOk);
|
||||||
|
|
||||||
|
int positionA;
|
||||||
|
int positionB;
|
||||||
|
int positionC;
|
||||||
|
int positionD;
|
||||||
|
// find the cards in the destination zone and check they are the right card
|
||||||
|
EXPECT_EQ(exileZone.getCard(cardA->getId(), &positionA), cardA);
|
||||||
|
EXPECT_EQ(exileZone.getCard(cardB->getId(), &positionB), cardB);
|
||||||
|
EXPECT_EQ(exileZone.getCard(cardC->getId(), &positionC), cardC);
|
||||||
|
EXPECT_EQ(exileZone.getCard(cardD->getId(), &positionD), cardD);
|
||||||
|
|
||||||
|
// check that they are at the expected index
|
||||||
|
EXPECT_EQ(cardA->getX(), 3);
|
||||||
|
EXPECT_EQ(cardB->getX(), 2);
|
||||||
|
EXPECT_EQ(cardC->getX(), 1);
|
||||||
|
EXPECT_EQ(cardD->getX(), 0);
|
||||||
|
|
||||||
|
// also check if the given positions are correct
|
||||||
|
EXPECT_EQ(positionA, 3);
|
||||||
|
EXPECT_EQ(positionB, 2);
|
||||||
|
EXPECT_EQ(positionC, 1);
|
||||||
|
EXPECT_EQ(positionD, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
42
tests/movecard_tests/server_test_helpers.h
Normal file
42
tests/movecard_tests/server_test_helpers.h
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#include "server.h"
|
||||||
|
#include "server_database_interface.h"
|
||||||
|
|
||||||
|
class MockDatabaseInterface : public Server_DatabaseInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AuthenticationResult checkUserPassword(Server_ProtocolHandler *,
|
||||||
|
const QString &,
|
||||||
|
const QString &,
|
||||||
|
const QString &,
|
||||||
|
QString &,
|
||||||
|
int &,
|
||||||
|
bool) override
|
||||||
|
{
|
||||||
|
return NotLoggedIn;
|
||||||
|
}
|
||||||
|
ServerInfo_User getUserData(const QString &, bool) override
|
||||||
|
{
|
||||||
|
return ServerInfo_User();
|
||||||
|
}
|
||||||
|
int getNextGameId() override
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int getNextReplayId() override
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int getActiveUserCount(QString) override
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FakeServer : public Server
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FakeServer()
|
||||||
|
{
|
||||||
|
setDatabaseInterface(new MockDatabaseInterface());
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue