Merge branch 'master' into ci/use-ninja-win

This commit is contained in:
Bruno Alexandre Rosa 2026-03-12 15:47:24 -03:00
commit 746f910c08
72 changed files with 965 additions and 723 deletions

View file

@ -2,7 +2,6 @@
# used by the ci to rename build artifacts # used by the ci to rename build artifacts
# renames the file to [original name][SUFFIX].[original extension] # renames the file to [original name][SUFFIX].[original extension]
# where SUFFIX is either available in the environment or as the first arg # where SUFFIX is either available in the environment or as the first arg
# if MAKE_ZIP is set instead a zip is made
# expected to be run in the build directory unless BUILD_DIR is set # expected to be run in the build directory unless BUILD_DIR is set
# adds output to GITHUB_OUTPUT # adds output to GITHUB_OUTPUT
builddir="${BUILD_DIR:=.}" builddir="${BUILD_DIR:=.}"
@ -22,8 +21,8 @@ set -e
# find file # find file
found="$(find "$builddir" -maxdepth 1 -type f -name "$findrx" -print -quit)" found="$(find "$builddir" -maxdepth 1 -type f -name "$findrx" -print -quit)"
path="${found%/*}" # remove all after last / path="${found%/*}" # remove all including first "/" from right side
file="${found##*/}" # remove all before last / file="${found##*/}" # remove all including last "/" from left side
if [[ ! $file ]]; then if [[ ! $file ]]; then
echo "::error file=$0::could not find package" echo "::error file=$0::could not find package"
exit 1 exit 1
@ -35,21 +34,16 @@ if ! cd "$path"; then
fi fi
# set filename # set filename
name="${file%.*}" # remove all after last . name="${file%.*}" # remove all including first "." from right side
new_name="$name$SUFFIX." new_name="$name$SUFFIX"
if [[ $MAKE_ZIP ]]; then extension="${file##*.}" # remove all including last "." from left side
filename="${new_name}zip" filename="$new_name.$extension"
echo "creating zip '$filename' from '$file'" echo "renaming '$file' to '$filename'"
zip "$filename" "$file" mv "$file" "$filename"
else
extension="${file##*.}" # remove all before last .
filename="$new_name$extension"
echo "renaming '$file' to '$filename'"
mv "$file" "$filename"
fi
cd "$oldpwd" cd "$oldpwd"
relative_path="$path/$filename" relative_path="$path/$filename"
ls -l "$relative_path" ls -l "$relative_path"
echo "path=$relative_path" >>"$GITHUB_OUTPUT" echo "path=$relative_path" >>"$GITHUB_OUTPUT"
echo "name=$filename" >>"$GITHUB_OUTPUT" echo "name=$new_name" >>"$GITHUB_OUTPUT"
echo "fullname=$filename" >>"$GITHUB_OUTPUT"

View file

@ -9,4 +9,4 @@ contact_links:
about: For more information and guidance check our Translation FAQ on our wiki! about: For more information and guidance check our Translation FAQ on our wiki!
- name: 📖 Code Documentation - name: 📖 Code Documentation
url: https://cockatrice.github.io/docs/ url: https://cockatrice.github.io/docs/
about: about: Helpful source focusing on developers, but there are also references for users!

View file

@ -220,27 +220,26 @@ jobs:
if: matrix.package != 'skip' if: matrix.package != 'skip'
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: ${{matrix.distro}}${{matrix.version}}-package
path: ${{steps.build.outputs.path}} path: ${{steps.build.outputs.path}}
archive: false
if-no-files-found: error if-no-files-found: error
- name: Upload to release - name: Upload to release
id: upload_release id: upload_release
if: needs.configure.outputs.tag != null && matrix.package != 'skip' if: matrix.package != 'skip' && needs.configure.outputs.tag != null
shell: bash shell: bash
env: env:
GH_TOKEN: ${{github.token}} GH_TOKEN: ${{github.token}}
tag_name: ${{needs.configure.outputs.tag}} tag_name: ${{needs.configure.outputs.tag}}
asset_name: ${{steps.build.outputs.name}} asset_name: ${{steps.build.outputs.fullname}}
asset_path: ${{steps.build.outputs.path}} asset_path: ${{steps.build.outputs.path}}
run: gh release upload "$tag_name" "$asset_path#$asset_name" run: gh release upload "$tag_name" "$asset_path#$asset_name"
- name: Attest binary provenance - name: Attest binary provenance
id: attestation id: attestation
if: steps.upload_release.outcome == 'success' if: steps.upload_release.outcome == 'success'
uses: actions/attest-build-provenance@v4 uses: actions/attest@v4
with: with:
subject-name: ${{steps.build.outputs.name}}
subject-path: ${{steps.build.outputs.path}} subject-path: ${{steps.build.outputs.path}}
show-summary: false show-summary: false
@ -265,7 +264,6 @@ jobs:
override_target: 13 override_target: 13
make_package: 1 make_package: 1
package_suffix: "-macOS13_Intel" package_suffix: "-macOS13_Intel"
artifact_name: macOS13_Intel-package
qt_version: 6.10.* qt_version: 6.10.*
qt_arch: clang_64 qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets qt_modules: qtimageformats qtmultimedia qtwebsockets
@ -280,7 +278,6 @@ jobs:
type: Release type: Release
make_package: 1 make_package: 1
package_suffix: "-macOS14" package_suffix: "-macOS14"
artifact_name: macOS14-package
qt_version: 6.10.* qt_version: 6.10.*
qt_arch: clang_64 qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets qt_modules: qtimageformats qtmultimedia qtwebsockets
@ -295,7 +292,6 @@ jobs:
type: Release type: Release
make_package: 1 make_package: 1
package_suffix: "-macOS15" package_suffix: "-macOS15"
artifact_name: macOS15-package
qt_version: 6.10.* qt_version: 6.10.*
qt_arch: clang_64 qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets qt_modules: qtimageformats qtmultimedia qtwebsockets
@ -320,7 +316,6 @@ jobs:
type: Release type: Release
make_package: 1 make_package: 1
package_suffix: "-Win10" package_suffix: "-Win10"
artifact_name: Windows10-installer
qt_version: 6.10.* qt_version: 6.10.*
qt_arch: win64_msvc2022_64 qt_arch: win64_msvc2022_64
qt_modules: qtimageformats qtmultimedia qtwebsockets qt_modules: qtimageformats qtmultimedia qtwebsockets
@ -464,7 +459,8 @@ jobs:
key: ${{ steps.ccache_restore.outputs.cache-primary-key }} key: ${{ steps.ccache_restore.outputs.cache-primary-key }}
- name: Sign app bundle - name: Sign app bundle
if: matrix.os == 'macOS' && matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null) if: matrix.os == 'macOS' && matrix.make_package && needs.configure.outputs.tag != null
id: sign_macos
env: env:
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
@ -476,7 +472,7 @@ jobs:
fi fi
- name: Notarize app bundle - name: Notarize app bundle
if: matrix.os == 'macOS' && matrix.make_package && (github.ref == 'refs/heads/master' || needs.configure.outputs.tag != null) if: steps.sign_macos.outcome == 'success'
env: env:
MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}
@ -508,19 +504,19 @@ jobs:
fi fi
- name: Upload artifact - name: Upload artifact
id: upload_artifact
if: matrix.make_package if: matrix.make_package
id: upload_artifact
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: ${{matrix.artifact_name}}
path: ${{steps.build.outputs.path}} path: ${{steps.build.outputs.path}}
archive: false
if-no-files-found: error if-no-files-found: error
- name: Upload PDBs (Program Databases) - name: Upload PDBs (Program Databases)
if: matrix.os == 'Windows' if: matrix.os == 'Windows' && github.ref_type != 'tag'
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: Windows${{matrix.target}}-PDBs name: ${{steps.build.outputs.name}}-PDBs
path: | path: |
build/cockatrice/**/*.pdb build/cockatrice/**/*.pdb
build/oracle/**/*.pdb build/oracle/**/*.pdb
@ -528,22 +524,21 @@ jobs:
if-no-files-found: error if-no-files-found: error
- name: Upload to release - name: Upload to release
if: needs.configure.outputs.tag != null && matrix.make_package == '1'
id: upload_release id: upload_release
if: needs.configure.outputs.tag != null
shell: bash shell: bash
env: env:
GH_TOKEN: ${{github.token}} GH_TOKEN: ${{github.token}}
tag_name: ${{needs.configure.outputs.tag}} tag_name: ${{needs.configure.outputs.tag}}
asset_name: ${{steps.build.outputs.name}} asset_name: ${{steps.build.outputs.fullname}}
asset_path: ${{steps.build.outputs.path}} asset_path: ${{steps.build.outputs.path}}
run: gh release upload "$tag_name" "$asset_path#$asset_name" run: gh release upload "$tag_name" "$asset_path#$asset_name"
- name: Attest binary provenance - name: Attest binary provenance
id: attestation
if: steps.upload_release.outcome == 'success' if: steps.upload_release.outcome == 'success'
uses: actions/attest-build-provenance@v4 id: attestation
uses: actions/attest@v4
with: with:
subject-name: ${{steps.build.outputs.name}}
subject-path: ${{steps.build.outputs.path}} subject-path: ${{steps.build.outputs.path}}
show-summary: false show-summary: false

View file

@ -41,10 +41,10 @@ jobs:
org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
- name: Set up Docker buildx - name: Set up Docker buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
if: github.ref_type == 'tag' if: github.ref_type == 'tag'
@ -62,5 +62,6 @@ jobs:
push: ${{ github.ref_type == 'tag' }} push: ${{ github.ref_type == 'tag' }}
tags: ${{ steps.metadata.outputs.tags }} tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }} labels: ${{ steps.metadata.outputs.labels }}
cache-from: type=gha annotations: ${{ steps.metadata.outputs.annotations }}
cache-to: type=gha,mode=max cache-from: type=gha,scope=servatrice
cache-to: type=gha,mode=max,scope=servatrice

View file

@ -26,6 +26,7 @@ Var PortableMode
!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of Cockatrice.$\r$\n$\r$\nClick Next to continue." !define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of Cockatrice.$\r$\n$\r$\nClick Next to continue."
!define MUI_FINISHPAGE_RUN "$INSTDIR/cockatrice.exe" !define MUI_FINISHPAGE_RUN "$INSTDIR/cockatrice.exe"
!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"
!insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "${NSIS_SOURCE_PATH}\LICENSE" !insertmacro MUI_PAGE_LICENSE "${NSIS_SOURCE_PATH}\LICENSE"

View file

@ -8,10 +8,11 @@
#define INTERFACE_JSON_DECK_PARSER_H #define INTERFACE_JSON_DECK_PARSER_H
#include "../../../interface/deck_loader/card_node_function.h" #include "../../../interface/deck_loader/card_node_function.h"
#include "../../../interface/deck_loader/deck_loader.h"
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>
#include <libcockatrice/card/import/card_name_normalizer.h>
#include <libcockatrice/deck_list/deck_list.h>
class IJsonDeckParser class IJsonDeckParser
{ {
@ -49,7 +50,7 @@ public:
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n'; outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
} }
deckList.loadFromStream_Plain(outStream, false); deckList.loadFromStream_Plain(outStream, false, CardNameNormalizer());
deckList.forEachCard(CardNodeFunction::ResolveProviderId()); deckList.forEachCard(CardNodeFunction::ResolveProviderId());
return deckList; return deckList;
@ -96,7 +97,7 @@ public:
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n'; outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
} }
deckList.loadFromStream_Plain(outStream, false); deckList.loadFromStream_Plain(outStream, false, CardNameNormalizer());
deckList.forEachCard(CardNodeFunction::ResolveProviderId()); deckList.forEachCard(CardNodeFunction::ResolveProviderId());
QJsonObject commandersObj = obj.value("commanders").toObject(); QJsonObject commandersObj = obj.value("commanders").toObject();

View file

@ -1115,257 +1115,21 @@ void SettingsCache::setClientVersion(const QString &_clientVersion)
QStringList SettingsCache::getCountries() const QStringList SettingsCache::getCountries() const
{ {
static QStringList countries = QStringList() << "ad" static const QStringList countries = {
<< "ae" "ad", "ae", "af", "ag", "ai", "al", "am", "ao", "aq", "ar", "as", "at", "au", "aw", "ax", "az", "ba", "bb",
<< "af" "bd", "be", "bf", "bg", "bh", "bi", "bj", "bl", "bm", "bn", "bo", "bq", "br", "bs", "bt", "bv", "bw", "by",
<< "ag" "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr", "cu", "cv", "cw", "cx",
<< "ai" "cy", "cz", "de", "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er", "es", "et", "eu", "fi", "fj",
<< "al" "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq", "gr",
<< "am" "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr", "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq",
<< "ao" "ir", "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", "ky", "kz",
<< "aq" "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc", "md", "me", "mf", "mg", "mh",
<< "ar" "mk", "ml", "mm", "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na", "nc",
<< "as" "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu", "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl",
<< "at" "pm", "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "rs", "ru", "rw", "sa", "sb", "sc", "sd", "se",
<< "au" "sg", "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "ss", "st", "sv", "sx", "sy", "sz", "tc", "td",
<< "aw" "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "um", "us",
<< "ax" "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu", "wf", "ws", "xk", "ye", "yt", "za", "zm", "zw"};
<< "az"
<< "ba"
<< "bb"
<< "bd"
<< "be"
<< "bf"
<< "bg"
<< "bh"
<< "bi"
<< "bj"
<< "bl"
<< "bm"
<< "bn"
<< "bo"
<< "bq"
<< "br"
<< "bs"
<< "bt"
<< "bv"
<< "bw"
<< "by"
<< "bz"
<< "ca"
<< "cc"
<< "cd"
<< "cf"
<< "cg"
<< "ch"
<< "ci"
<< "ck"
<< "cl"
<< "cm"
<< "cn"
<< "co"
<< "cr"
<< "cu"
<< "cv"
<< "cw"
<< "cx"
<< "cy"
<< "cz"
<< "de"
<< "dj"
<< "dk"
<< "dm"
<< "do"
<< "dz"
<< "ec"
<< "ee"
<< "eg"
<< "eh"
<< "er"
<< "es"
<< "et"
<< "eu"
<< "fi"
<< "fj"
<< "fk"
<< "fm"
<< "fo"
<< "fr"
<< "ga"
<< "gb"
<< "gd"
<< "ge"
<< "gf"
<< "gg"
<< "gh"
<< "gi"
<< "gl"
<< "gm"
<< "gn"
<< "gp"
<< "gq"
<< "gr"
<< "gs"
<< "gt"
<< "gu"
<< "gw"
<< "gy"
<< "hk"
<< "hm"
<< "hn"
<< "hr"
<< "ht"
<< "hu"
<< "id"
<< "ie"
<< "il"
<< "im"
<< "in"
<< "io"
<< "iq"
<< "ir"
<< "is"
<< "it"
<< "je"
<< "jm"
<< "jo"
<< "jp"
<< "ke"
<< "kg"
<< "kh"
<< "ki"
<< "km"
<< "kn"
<< "kp"
<< "kr"
<< "kw"
<< "ky"
<< "kz"
<< "la"
<< "lb"
<< "lc"
<< "li"
<< "lk"
<< "lr"
<< "ls"
<< "lt"
<< "lu"
<< "lv"
<< "ly"
<< "ma"
<< "mc"
<< "md"
<< "me"
<< "mf"
<< "mg"
<< "mh"
<< "mk"
<< "ml"
<< "mm"
<< "mn"
<< "mo"
<< "mp"
<< "mq"
<< "mr"
<< "ms"
<< "mt"
<< "mu"
<< "mv"
<< "mw"
<< "mx"
<< "my"
<< "mz"
<< "na"
<< "nc"
<< "ne"
<< "nf"
<< "ng"
<< "ni"
<< "nl"
<< "no"
<< "np"
<< "nr"
<< "nu"
<< "nz"
<< "om"
<< "pa"
<< "pe"
<< "pf"
<< "pg"
<< "ph"
<< "pk"
<< "pl"
<< "pm"
<< "pn"
<< "pr"
<< "ps"
<< "pt"
<< "pw"
<< "py"
<< "qa"
<< "re"
<< "ro"
<< "rs"
<< "ru"
<< "rw"
<< "sa"
<< "sb"
<< "sc"
<< "sd"
<< "se"
<< "sg"
<< "sh"
<< "si"
<< "sj"
<< "sk"
<< "sl"
<< "sm"
<< "sn"
<< "so"
<< "sr"
<< "ss"
<< "st"
<< "sv"
<< "sx"
<< "sy"
<< "sz"
<< "tc"
<< "td"
<< "tf"
<< "tg"
<< "th"
<< "tj"
<< "tk"
<< "tl"
<< "tm"
<< "tn"
<< "to"
<< "tr"
<< "tt"
<< "tv"
<< "tw"
<< "tz"
<< "ua"
<< "ug"
<< "um"
<< "us"
<< "uy"
<< "uz"
<< "va"
<< "vc"
<< "ve"
<< "vg"
<< "vi"
<< "vn"
<< "vu"
<< "wf"
<< "ws"
<< "xk"
<< "ye"
<< "yt"
<< "za"
<< "zm"
<< "zw";
return countries; return countries;
} }

View file

@ -598,11 +598,19 @@ private:
{"Player/aMoveTopCardsToGraveyard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple)"), {"Player/aMoveTopCardsToGraveyard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple)"),
parseSequenceString("Alt+M"), parseSequenceString("Alt+M"),
ShortcutGroup::Move_top)}, ShortcutGroup::Move_top)},
{"Player/aMoveTopCardsToGraveyardFaceDown",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple), Face Down"),
parseSequenceString(""),
ShortcutGroup::Move_top)},
{"Player/aMoveTopCardToExile", {"Player/aMoveTopCardToExile",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile"), parseSequenceString(""), ShortcutGroup::Move_top)}, ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile"), parseSequenceString(""), ShortcutGroup::Move_top)},
{"Player/aMoveTopCardsToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple)"), {"Player/aMoveTopCardsToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple)"),
parseSequenceString(""), parseSequenceString(""),
ShortcutGroup::Move_top)}, ShortcutGroup::Move_top)},
{"Player/aMoveTopCardsToExileFaceDown",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple), Face Down"),
parseSequenceString(""),
ShortcutGroup::Move_top)},
{"Player/aMoveTopCardsUntil", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Stack Until Found"), {"Player/aMoveTopCardsUntil", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Stack Until Found"),
parseSequenceString("Ctrl+Shift+Y"), parseSequenceString("Ctrl+Shift+Y"),
ShortcutGroup::Move_top)}, ShortcutGroup::Move_top)},
@ -620,11 +628,19 @@ private:
{"Player/aMoveBottomCardsToGrave", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple)"), {"Player/aMoveBottomCardsToGrave", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple)"),
parseSequenceString(""), parseSequenceString(""),
ShortcutGroup::Move_bottom)}, ShortcutGroup::Move_bottom)},
{"Player/aMoveBottomCardsToGraveFaceDown",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple), Face Down"),
parseSequenceString(""),
ShortcutGroup::Move_bottom)},
{"Player/aMoveBottomCardToExile", {"Player/aMoveBottomCardToExile",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile"), parseSequenceString(""), ShortcutGroup::Move_bottom)}, ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile"), parseSequenceString(""), ShortcutGroup::Move_bottom)},
{"Player/aMoveBottomCardsToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple)"), {"Player/aMoveBottomCardsToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple)"),
parseSequenceString(""), parseSequenceString(""),
ShortcutGroup::Move_bottom)}, ShortcutGroup::Move_bottom)},
{"Player/aMoveBottomCardsToExileFaceDown",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple), Face Down"),
parseSequenceString(""),
ShortcutGroup::Move_bottom)},
{"Player/aMoveBottomCardToTop", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Top of Library"), {"Player/aMoveBottomCardToTop", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Top of Library"),
parseSequenceString(""), parseSequenceString(""),
ShortcutGroup::Move_bottom)}, ShortcutGroup::Move_bottom)},

View file

@ -1,14 +1,13 @@
#include "abstract_card_drag_item.h" #include "abstract_card_drag_item.h"
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../z_values.h"
#include <QCursor> #include <QCursor>
#include <QDebug> #include <QDebug>
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QPainter> #include <QPainter>
static const float CARD_WIDTH_HALF = CARD_WIDTH / 2;
static const float CARD_HEIGHT_HALF = CARD_HEIGHT / 2;
const QColor GHOST_MASK = QColor(255, 255, 255, 50); const QColor GHOST_MASK = QColor(255, 255, 255, 50);
AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item,
@ -18,19 +17,19 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item,
{ {
if (parentDrag) { if (parentDrag) {
parentDrag->addChildDrag(this); parentDrag->addChildDrag(this);
setZValue(2000000007 + hotSpot.x() * 1000000 + hotSpot.y() * 1000 + 1000); setZValue(ZValues::childDragZValue(hotSpot.x(), hotSpot.y()));
connect(parentDrag, &QObject::destroyed, this, &AbstractCardDragItem::deleteLater); connect(parentDrag, &QObject::destroyed, this, &AbstractCardDragItem::deleteLater);
} else { } else {
hotSpot = QPointF{qBound(0.0, hotSpot.x(), static_cast<qreal>(CARD_WIDTH - 1)), hotSpot = QPointF{qBound(0.0, hotSpot.x(), CardDimensions::WIDTH_F - 1),
qBound(0.0, hotSpot.y(), static_cast<qreal>(CARD_HEIGHT - 1))}; qBound(0.0, hotSpot.y(), CardDimensions::HEIGHT_F - 1)};
setCursor(Qt::ClosedHandCursor); setCursor(Qt::ClosedHandCursor);
setZValue(2000000007); setZValue(ZValues::DRAG_ITEM);
} }
if (item->getTapped()) if (item->getTapped())
setTransform(QTransform() setTransform(QTransform()
.translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF) .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F)
.rotate(90) .rotate(90)
.translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF)); .translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F));
setCacheMode(DeviceCoordinateCache); setCacheMode(DeviceCoordinateCache);
@ -47,7 +46,7 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item,
QPainterPath AbstractCardDragItem::shape() const QPainterPath AbstractCardDragItem::shape() const
{ {
QPainterPath shape; QPainterPath shape;
qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CARD_WIDTH : 0.0; qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CardDimensions::WIDTH_F : 0.0;
shape.addRoundedRect(boundingRect(), cardCornerRadius, cardCornerRadius); shape.addRoundedRect(boundingRect(), cardCornerRadius, cardCornerRadius);
return shape; return shape;
} }

View file

@ -34,7 +34,7 @@ public:
AbstractCardDragItem(AbstractCardItem *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag = 0); AbstractCardDragItem(AbstractCardItem *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag = 0);
[[nodiscard]] QRectF boundingRect() const override [[nodiscard]] QRectF boundingRect() const override
{ {
return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT); return QRectF(0, 0, CardDimensions::WIDTH_F, CardDimensions::HEIGHT_F);
} }
[[nodiscard]] QPainterPath shape() const override; [[nodiscard]] QPainterPath shape() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;

View file

@ -3,6 +3,7 @@
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../interface/card_picture_loader/card_picture_loader.h" #include "../../interface/card_picture_loader/card_picture_loader.h"
#include "../game_scene.h" #include "../game_scene.h"
#include "../z_values.h"
#include <QCursor> #include <QCursor>
#include <QGraphicsScene> #include <QGraphicsScene>
@ -38,13 +39,13 @@ AbstractCardItem::~AbstractCardItem()
QRectF AbstractCardItem::boundingRect() const QRectF AbstractCardItem::boundingRect() const
{ {
return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT); return QRectF(0, 0, CardDimensions::WIDTH_F, CardDimensions::HEIGHT_F);
} }
QPainterPath AbstractCardItem::shape() const QPainterPath AbstractCardItem::shape() const
{ {
QPainterPath shape; QPainterPath shape;
qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CARD_WIDTH : 0.0; qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CardDimensions::WIDTH_F : 0.0;
shape.addRoundedRect(boundingRect(), cardCornerRadius, cardCornerRadius); shape.addRoundedRect(boundingRect(), cardCornerRadius, cardCornerRadius);
return shape; return shape;
} }
@ -215,9 +216,9 @@ void AbstractCardItem::setHovered(bool _hovered)
if (_hovered) if (_hovered)
processHoverEvent(); processHoverEvent();
isHovered = _hovered; isHovered = _hovered;
setZValue(_hovered ? 2000000004 : realZValue); setZValue(_hovered ? ZValues::HOVERED_CARD : realZValue);
setScale(_hovered && SettingsCache::instance().getScaleCards() ? 1.1 : 1); setScale(_hovered && SettingsCache::instance().getScaleCards() ? 1.1 : 1);
setTransformOriginPoint(_hovered ? CARD_WIDTH / 2 : 0, _hovered ? CARD_HEIGHT / 2 : 0); setTransformOriginPoint(_hovered ? CardDimensions::WIDTH_HALF_F : 0, _hovered ? CardDimensions::HEIGHT_HALF_F : 0);
update(); update();
} }
@ -273,9 +274,9 @@ void AbstractCardItem::setTapped(bool _tapped, bool canAnimate)
else { else {
tapAngle = tapped ? 90 : 0; tapAngle = tapped ? 90 : 0;
setTransform(QTransform() setTransform(QTransform()
.translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2) .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F)
.rotate(tapAngle) .rotate(tapAngle)
.translate((float)-CARD_WIDTH / 2, (float)-CARD_HEIGHT / 2)); .translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F));
update(); update();
} }
} }

View file

@ -8,6 +8,7 @@
#define ABSTRACTCARDITEM_H #define ABSTRACTCARDITEM_H
#include "../../game_graphics/board/graphics_item_type.h" #include "../../game_graphics/board/graphics_item_type.h"
#include "../card_dimensions.h"
#include "arrow_target.h" #include "arrow_target.h"
#include <libcockatrice/card/printing/exact_card.h> #include <libcockatrice/card/printing/exact_card.h>
@ -15,9 +16,6 @@
class Player; class Player;
const int CARD_WIDTH = 72;
const int CARD_HEIGHT = 102;
class AbstractCardItem : public ArrowTarget class AbstractCardItem : public ArrowTarget
{ {
Q_OBJECT Q_OBJECT

View file

@ -5,6 +5,7 @@
#include "../player/player.h" #include "../player/player.h"
#include "../player/player_actions.h" #include "../player/player_actions.h"
#include "../player/player_target.h" #include "../player/player_target.h"
#include "../z_values.h"
#include "../zones/card_zone.h" #include "../zones/card_zone.h"
#include "card_item.h" #include "card_item.h"
@ -18,12 +19,13 @@
#include <libcockatrice/protocol/pb/command_create_arrow.pb.h> #include <libcockatrice/protocol/pb/command_create_arrow.pb.h>
#include <libcockatrice/protocol/pb/command_delete_arrow.pb.h> #include <libcockatrice/protocol/pb/command_delete_arrow.pb.h>
#include <libcockatrice/utility/color.h> #include <libcockatrice/utility/color.h>
#include <libcockatrice/utility/zone_names.h>
ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color) ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color)
: QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), targetLocked(false), : QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), targetLocked(false),
color(_color), fullColor(true) color(_color), fullColor(true)
{ {
setZValue(2000000005); setZValue(ZValues::ARROWS);
if (startItem) if (startItem)
startItem->addArrowFrom(this); startItem->addArrowFrom(this);
@ -238,16 +240,16 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
} }
// if the card is in hand then we will move the card to stack or table as part of drawing the arrow // if the card is in hand then we will move the card to stack or table as part of drawing the arrow
if (startZone->getName() == "hand") { if (startZone->getName() == ZoneNames::HAND) {
startCard->playCard(false); startCard->playCard(false);
CardInfoPtr ci = startCard->getCard().getCardPtr(); CardInfoPtr ci = startCard->getCard().getCardPtr();
bool playToStack = SettingsCache::instance().getPlayToStack(); bool playToStack = SettingsCache::instance().getPlayToStack();
if (ci && if (ci && ((!playToStack && ci->getUiAttributes().tableRow == 3) ||
((!playToStack && ci->getUiAttributes().tableRow == 3) || (playToStack && ci->getUiAttributes().tableRow != 0 &&
(playToStack && ci->getUiAttributes().tableRow != 0 && startCard->getZone()->getName() != "stack"))) startCard->getZone()->getName() != ZoneNames::STACK)))
cmd.set_start_zone("stack"); cmd.set_start_zone(ZoneNames::STACK);
else else
cmd.set_start_zone(playToStack ? "stack" : "table"); cmd.set_start_zone(playToStack ? ZoneNames::STACK : ZoneNames::TABLE);
} }
if (deleteInPhase != 0) { if (deleteInPhase != 0) {
@ -317,7 +319,7 @@ void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCard) void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCard)
{ {
// do nothing if target is already attached to another card or is not in play // do nothing if target is already attached to another card or is not in play
if (targetCard->getAttachedTo() || targetCard->getZone()->getName() != "table") { if (targetCard->getAttachedTo() || targetCard->getZone()->getName() != ZoneNames::TABLE) {
return; return;
} }
@ -325,12 +327,12 @@ void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCar
CardZoneLogic *targetZone = targetCard->getZone(); CardZoneLogic *targetZone = targetCard->getZone();
// move card onto table first if attaching from some other zone // move card onto table first if attaching from some other zone
if (startZone->getName() != "table") { if (startZone->getName() != ZoneNames::TABLE) {
player->getPlayerActions()->playCardToTable(startCard, false); player->getPlayerActions()->playCardToTable(startCard, false);
} }
Command_AttachCard cmd; Command_AttachCard cmd;
cmd.set_start_zone("table"); cmd.set_start_zone(ZoneNames::TABLE);
cmd.set_card_id(startCard->getId()); cmd.set_card_id(startCard->getId());
cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId());
cmd.set_target_zone(targetZone->getName().toStdString()); cmd.set_target_zone(targetZone->getName().toStdString());

View file

@ -13,9 +13,10 @@
CardDragItem::CardDragItem(CardItem *_item, CardDragItem::CardDragItem(CardItem *_item,
int _id, int _id,
const QPointF &_hotSpot, const QPointF &_hotSpot,
bool _faceDown, bool _forceFaceDown,
AbstractCardDragItem *parentDrag) AbstractCardDragItem *parentDrag)
: AbstractCardDragItem(_item, _hotSpot, parentDrag), id(_id), faceDown(_faceDown), occupied(false), currentZone(0) : AbstractCardDragItem(_item, _hotSpot, parentDrag), id(_id), forceFaceDown(_forceFaceDown), occupied(false),
currentZone(0)
{ {
} }

View file

@ -16,7 +16,7 @@ class CardDragItem : public AbstractCardDragItem
Q_OBJECT Q_OBJECT
private: private:
int id; int id;
bool faceDown; bool forceFaceDown;
bool occupied; bool occupied;
CardZone *currentZone; CardZone *currentZone;
@ -24,15 +24,15 @@ public:
CardDragItem(CardItem *_item, CardDragItem(CardItem *_item,
int _id, int _id,
const QPointF &_hotSpot, const QPointF &_hotSpot,
bool _faceDown, bool _forceFaceDown,
AbstractCardDragItem *parentDrag = 0); AbstractCardDragItem *parentDrag = 0);
int getId() const int getId() const
{ {
return id; return id;
} }
bool getFaceDown() const bool isForceFaceDown() const
{ {
return faceDown; return forceFaceDown;
} }
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
void updatePosition(const QPointF &cursorScenePos) override; void updatePosition(const QPointF &cursorScenePos) override;

View file

@ -212,10 +212,12 @@ void CardItem::setAttachedTo(CardItem *_attachedTo)
} }
} }
/**
* @brief Resets the fields that should be reset after a zone transition
*/
void CardItem::resetState(bool keepAnnotations) void CardItem::resetState(bool keepAnnotations)
{ {
attacking = false; attacking = false;
facedown = false;
counters.clear(); counters.clear();
pt.clear(); pt.clear();
if (!keepAnnotations) { if (!keepAnnotations) {
@ -251,10 +253,10 @@ void CardItem::processCardInfo(const ServerInfo_Card &_info)
setDoesntUntap(_info.doesnt_untap()); setDoesntUntap(_info.doesnt_untap());
} }
CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown) CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool forceFaceDown)
{ {
deleteDragItem(); deleteDragItem();
dragItem = new CardDragItem(this, _id, _pos, faceDown); dragItem = new CardDragItem(this, _id, _pos, forceFaceDown);
dragItem->setVisible(false); dragItem->setVisible(false);
scene()->addItem(dragItem); scene()->addItem(dragItem);
dragItem->updatePosition(_scenePos); dragItem->updatePosition(_scenePos);
@ -352,7 +354,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
// Use the buttonDownPos to align the hot spot with the position when // Use the buttonDownPos to align the hot spot with the position when
// the user originally clicked // the user originally clicked
createDragItem(id, event->buttonDownPos(Qt::LeftButton), event->scenePos(), facedown || forceFaceDown); createDragItem(id, event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown);
dragItem->grabMouse(); dragItem->grabMouse();
int childIndex = 0; int childIndex = 0;
@ -365,7 +367,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
if (zone->getHasCardAttr()) if (zone->getHasCardAttr())
childPos = card->pos() - pos(); childPos = card->pos() - pos();
else else
childPos = QPointF(childIndex * CARD_WIDTH / 2, 0); childPos = QPointF(childIndex * CardDimensions::WIDTH_HALF_F, 0);
CardDragItem *drag = CardDragItem *drag =
new CardDragItem(card, card->getId(), childPos, card->getFaceDown() || forceFaceDown, dragItem); new CardDragItem(card, card->getId(), childPos, card->getFaceDown() || forceFaceDown, dragItem);
drag->setPos(dragItem->pos() + childPos); drag->setPos(dragItem->pos() + childPos);
@ -474,9 +476,9 @@ bool CardItem::animationEvent()
} }
setTransform(QTransform() setTransform(QTransform()
.translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF) .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F)
.rotate(tapAngle) .rotate(tapAngle)
.translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF)); .translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F));
setHovered(false); setHovered(false);
update(); update();

View file

@ -21,8 +21,6 @@ class QAction;
class QColor; class QColor;
const int MAX_COUNTERS_ON_CARD = 999; const int MAX_COUNTERS_ON_CARD = 999;
const float CARD_WIDTH_HALF = CARD_WIDTH / 2;
const float CARD_HEIGHT_HALF = CARD_HEIGHT / 2;
const int ROTATION_DEGREES_PER_FRAME = 10; const int ROTATION_DEGREES_PER_FRAME = 10;
class CardItem : public AbstractCardItem class CardItem : public AbstractCardItem
@ -142,7 +140,7 @@ public:
void processCardInfo(const ServerInfo_Card &_info); void processCardInfo(const ServerInfo_Card &_info);
bool animationEvent(); bool animationEvent();
CardDragItem *createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown); CardDragItem *createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool forceFaceDown);
void deleteDragItem(); void deleteDragItem();
void drawArrow(const QColor &arrowColor); void drawArrow(const QColor &arrowColor);
void drawAttachArrow(); void drawAttachArrow();

View file

@ -0,0 +1,29 @@
#ifndef CARD_DIMENSIONS_H
#define CARD_DIMENSIONS_H
#include <QtGlobal>
/**
* @file card_dimensions.h
* @brief Canonical card dimension constants for layout and Z-value calculations.
*
* These values represent the logical pixel dimensions of a standard card graphic.
* They are used throughout the game scene for layout, rendering, and Z-value computation.
*/
namespace CardDimensions
{
/// Card width in pixels
constexpr int WIDTH = 72;
/// Card height in pixels
constexpr int HEIGHT = 102;
/// Pre-converted for floating-point contexts (Z-value calculations)
constexpr qreal WIDTH_F = static_cast<qreal>(WIDTH);
constexpr qreal HEIGHT_F = static_cast<qreal>(HEIGHT);
/// Half-dimensions for centering and rotation transforms
constexpr qreal WIDTH_HALF_F = WIDTH_F / 2;
constexpr qreal HEIGHT_HALF_F = HEIGHT_F / 2;
} // namespace CardDimensions
#endif // CARD_DIMENSIONS_H

View file

@ -95,8 +95,9 @@ void DeckViewCard::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
pen.setJoinStyle(Qt::MiterJoin); pen.setJoinStyle(Qt::MiterJoin);
pen.setColor(originZone == DECK_ZONE_MAIN ? Qt::green : Qt::red); pen.setColor(originZone == DECK_ZONE_MAIN ? Qt::green : Qt::red);
painter->setPen(pen); painter->setPen(pen);
qreal cardRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * (CARD_WIDTH - 3) : 0.0; qreal cardRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * (CardDimensions::WIDTH_F - 3) : 0.0;
painter->drawRoundedRect(QRectF(1.5, 1.5, CARD_WIDTH - 3., CARD_HEIGHT - 3.), cardRadius, cardRadius); painter->drawRoundedRect(QRectF(1.5, 1.5, CardDimensions::WIDTH_F - 3, CardDimensions::HEIGHT_F - 3), cardRadius,
cardRadius);
painter->restore(); painter->restore();
} }
@ -122,7 +123,7 @@ void DeckViewCard::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
if (c == this) if (c == this)
continue; continue;
++j; ++j;
auto childPos = QPointF(j * CARD_WIDTH / 2, 0); auto childPos = QPointF(j * CardDimensions::WIDTH_HALF_F, 0);
auto *drag = new DeckViewCardDragItem(c, childPos, dragItem); auto *drag = new DeckViewCardDragItem(c, childPos, dragItem);
drag->setPos(dragItem->pos() + childPos); drag->setPos(dragItem->pos() + childPos);
scene()->addItem(drag); scene()->addItem(drag);
@ -204,7 +205,7 @@ void DeckViewCardContainer::paint(QPainter *painter, const QStyleOptionGraphicsI
painter->setPen(QColor(255, 255, 255, 100)); painter->setPen(QColor(255, 255, 255, 100));
painter->drawLine(QPointF(0, yUntilNow - paddingY / 2), QPointF(width, yUntilNow - paddingY / 2)); painter->drawLine(QPointF(0, yUntilNow - paddingY / 2), QPointF(width, yUntilNow - paddingY / 2));
} }
qreal thisRowHeight = CARD_HEIGHT * currentRowsAndCols[i].first; qreal thisRowHeight = CardDimensions::HEIGHT_F * currentRowsAndCols[i].first;
QRectF textRect(0, yUntilNow, totalTextWidth, thisRowHeight); QRectF textRect(0, yUntilNow, totalTextWidth, thisRowHeight);
yUntilNow += thisRowHeight + paddingY; yUntilNow += thisRowHeight + paddingY;
@ -260,9 +261,9 @@ QSizeF DeckViewCardContainer::calculateBoundingRect(const QList<QPair<int, int>>
// Calculate space needed for cards // Calculate space needed for cards
for (int i = 0; i < rowsAndCols.size(); ++i) { for (int i = 0; i < rowsAndCols.size(); ++i) {
totalHeight += CARD_HEIGHT * rowsAndCols[i].first + paddingY; totalHeight += CardDimensions::HEIGHT_F * rowsAndCols[i].first + paddingY;
if (CARD_WIDTH * rowsAndCols[i].second > totalWidth) if (CardDimensions::WIDTH_F * rowsAndCols[i].second > totalWidth)
totalWidth = CARD_WIDTH * rowsAndCols[i].second; totalWidth = CardDimensions::WIDTH_F * rowsAndCols[i].second;
} }
return QSizeF(getCardTypeTextWidth() + totalWidth, totalHeight + separatorY + paddingY); return QSizeF(getCardTypeTextWidth() + totalWidth, totalHeight + separatorY + paddingY);
@ -289,9 +290,10 @@ void DeckViewCardContainer::rearrangeItems(const QList<QPair<int, int>> &rowsAnd
std::sort(row.begin(), row.end(), DeckViewCardContainer::sortCardsByName); std::sort(row.begin(), row.end(), DeckViewCardContainer::sortCardsByName);
for (int j = 0; j < row.size(); ++j) { for (int j = 0; j < row.size(); ++j) {
DeckViewCard *card = row[j]; DeckViewCard *card = row[j];
card->setPos(x + (j % tempCols) * CARD_WIDTH, yUntilNow + (j / tempCols) * CARD_HEIGHT); card->setPos(x + (j % tempCols) * CardDimensions::WIDTH_F,
yUntilNow + (j / tempCols) * CardDimensions::HEIGHT_F);
} }
yUntilNow += tempRows * CARD_HEIGHT + paddingY; yUntilNow += tempRows * CardDimensions::HEIGHT_F + paddingY;
} }
prepareGeometryChange(); prepareGeometryChange();
@ -392,7 +394,7 @@ void DeckViewScene::applySideboardPlan(const QList<MoveCard_ToZone> &plan)
void DeckViewScene::rearrangeItems() void DeckViewScene::rearrangeItems()
{ {
const int spacing = CARD_HEIGHT / 3; const int spacing = CardDimensions::HEIGHT / 3;
QList<DeckViewCardContainer *> contList = cardContainers.values(); QList<DeckViewCardContainer *> contList = cardContainers.values();
// Initialize space requirements // Initialize space requirements

View file

@ -14,6 +14,7 @@
#include <QGraphicsView> #include <QGraphicsView>
#include <QSet> #include <QSet>
#include <QtMath> #include <QtMath>
#include <libcockatrice/utility/zone_names.h>
#include <numeric> #include <numeric>
/** /**
@ -410,9 +411,9 @@ void GameScene::toggleZoneView(Player *player, const QString &zoneName, int numb
connect(item, &ZoneViewWidget::closePressed, this, &GameScene::removeZoneView); connect(item, &ZoneViewWidget::closePressed, this, &GameScene::removeZoneView);
addItem(item); addItem(item);
if (zoneName == "grave") if (zoneName == ZoneNames::GRAVE)
item->setPos(360, 100); item->setPos(360, 100);
else if (zoneName == "rfg") else if (zoneName == ZoneNames::EXILE)
item->setPos(380, 120); item->setPos(380, 120);
else else
item->setPos(340, 80); item->setPos(340, 80);

View file

@ -10,16 +10,9 @@
#include <../../client/settings/card_counter_settings.h> #include <../../client/settings/card_counter_settings.h>
#include <libcockatrice/protocol/pb/context_move_card.pb.h> #include <libcockatrice/protocol/pb/context_move_card.pb.h>
#include <libcockatrice/protocol/pb/context_mulligan.pb.h> #include <libcockatrice/protocol/pb/context_mulligan.pb.h>
#include <libcockatrice/utility/zone_names.h>
#include <utility> #include <utility>
static const QString TABLE_ZONE_NAME = "table";
static const QString GRAVE_ZONE_NAME = "grave";
static const QString EXILE_ZONE_NAME = "rfg";
static const QString HAND_ZONE_NAME = "hand";
static const QString DECK_ZONE_NAME = "deck";
static const QString SIDEBOARD_ZONE_NAME = "sb";
static const QString STACK_ZONE_NAME = "stack";
static QString sanitizeHtml(QString dirty) static QString sanitizeHtml(QString dirty)
{ {
return dirty.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;"); return dirty.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;");
@ -37,15 +30,15 @@ MessageLogWidget::getFromStr(CardZoneLogic *zone, QString cardName, int position
QString fromStr; QString fromStr;
QString zoneName = zone->getName(); QString zoneName = zone->getName();
if (zoneName == TABLE_ZONE_NAME) { if (zoneName == ZoneNames::TABLE) {
fromStr = tr(" from play"); fromStr = tr(" from play");
} else if (zoneName == GRAVE_ZONE_NAME) { } else if (zoneName == ZoneNames::GRAVE) {
fromStr = tr(" from their graveyard"); fromStr = tr(" from their graveyard");
} else if (zoneName == EXILE_ZONE_NAME) { } else if (zoneName == ZoneNames::EXILE) {
fromStr = tr(" from exile"); fromStr = tr(" from exile");
} else if (zoneName == HAND_ZONE_NAME) { } else if (zoneName == ZoneNames::HAND) {
fromStr = tr(" from their hand"); fromStr = tr(" from their hand");
} else if (zoneName == DECK_ZONE_NAME) { } else if (zoneName == ZoneNames::DECK) {
if (position == 0) { if (position == 0) {
if (cardName.isEmpty()) { if (cardName.isEmpty()) {
if (ownerChange) { if (ownerChange) {
@ -83,9 +76,9 @@ MessageLogWidget::getFromStr(CardZoneLogic *zone, QString cardName, int position
fromStr = tr(" from their library"); fromStr = tr(" from their library");
} }
} }
} else if (zoneName == SIDEBOARD_ZONE_NAME) { } else if (zoneName == ZoneNames::SIDEBOARD) {
fromStr = tr(" from sideboard"); fromStr = tr(" from sideboard");
} else if (zoneName == STACK_ZONE_NAME) { } else if (zoneName == ZoneNames::STACK) {
fromStr = tr(" from the stack"); fromStr = tr(" from the stack");
} else { } else {
fromStr = tr(" from custom zone '%1'").arg(zoneName); fromStr = tr(" from custom zone '%1'").arg(zoneName);
@ -275,9 +268,9 @@ void MessageLogWidget::logMoveCard(Player *player,
bool ownerChanged = startZone->getPlayer() != targetZone->getPlayer(); bool ownerChanged = startZone->getPlayer() != targetZone->getPlayer();
// do not log if moved within the same zone // do not log if moved within the same zone
if ((startZoneName == TABLE_ZONE_NAME && targetZoneName == TABLE_ZONE_NAME && !ownerChanged) || if ((startZoneName == ZoneNames::TABLE && targetZoneName == ZoneNames::TABLE && !ownerChanged) ||
(startZoneName == HAND_ZONE_NAME && targetZoneName == HAND_ZONE_NAME) || (startZoneName == ZoneNames::HAND && targetZoneName == ZoneNames::HAND) ||
(startZoneName == EXILE_ZONE_NAME && targetZoneName == EXILE_ZONE_NAME)) { (startZoneName == ZoneNames::EXILE && targetZoneName == ZoneNames::EXILE)) {
return; return;
} }
@ -306,20 +299,28 @@ void MessageLogWidget::logMoveCard(Player *player,
QString finalStr; QString finalStr;
std::optional<QString> fourthArg; std::optional<QString> fourthArg;
if (targetZoneName == TABLE_ZONE_NAME) { if (targetZoneName == ZoneNames::TABLE) {
soundEngine->playSound("play_card"); soundEngine->playSound("play_card");
if (card->getFaceDown()) { if (card->getFaceDown()) {
finalStr = tr("%1 puts %2 into play%3 face down."); finalStr = tr("%1 puts %2 into play%3 face down.");
} else { } else {
finalStr = tr("%1 puts %2 into play%3."); finalStr = tr("%1 puts %2 into play%3.");
} }
} else if (targetZoneName == GRAVE_ZONE_NAME) { } else if (targetZoneName == ZoneNames::GRAVE) {
finalStr = tr("%1 puts %2%3 into their graveyard."); if (card->getFaceDown()) {
} else if (targetZoneName == EXILE_ZONE_NAME) { finalStr = tr("%1 puts %2%3 into their graveyard face down.");
finalStr = tr("%1 exiles %2%3."); } else {
} else if (targetZoneName == HAND_ZONE_NAME) { finalStr = tr("%1 puts %2%3 into their graveyard.");
}
} else if (targetZoneName == ZoneNames::EXILE) {
if (card->getFaceDown()) {
finalStr = tr("%1 exiles %2%3 face down.");
} else {
finalStr = tr("%1 exiles %2%3.");
}
} else if (targetZoneName == ZoneNames::HAND) {
finalStr = tr("%1 moves %2%3 to their hand."); finalStr = tr("%1 moves %2%3 to their hand.");
} else if (targetZoneName == DECK_ZONE_NAME) { } else if (targetZoneName == ZoneNames::DECK) {
if (newX == -1) { if (newX == -1) {
finalStr = tr("%1 puts %2%3 into their library."); finalStr = tr("%1 puts %2%3 into their library.");
} else if (newX >= targetZone->getCards().size()) { } else if (newX >= targetZone->getCards().size()) {
@ -331,14 +332,22 @@ void MessageLogWidget::logMoveCard(Player *player,
fourthArg = QString::number(newX); fourthArg = QString::number(newX);
finalStr = tr("%1 puts %2%3 into their library %4 cards from the top."); finalStr = tr("%1 puts %2%3 into their library %4 cards from the top.");
} }
} else if (targetZoneName == SIDEBOARD_ZONE_NAME) { } else if (targetZoneName == ZoneNames::SIDEBOARD) {
finalStr = tr("%1 moves %2%3 to sideboard."); finalStr = tr("%1 moves %2%3 to sideboard.");
} else if (targetZoneName == STACK_ZONE_NAME) { } else if (targetZoneName == ZoneNames::STACK) {
soundEngine->playSound("play_card"); soundEngine->playSound("play_card");
finalStr = tr("%1 plays %2%3."); if (card->getFaceDown()) {
finalStr = tr("%1 plays %2%3 face down.");
} else {
finalStr = tr("%1 plays %2%3.");
}
} else { } else {
fourthArg = targetZoneName; fourthArg = targetZoneName;
finalStr = tr("%1 moves %2%3 to custom zone '%4'."); if (card->getFaceDown()) {
finalStr = tr("%1 moves %2%3 to custom zone '%4' face down.");
} else {
finalStr = tr("%1 moves %2%3 to custom zone '%4'.");
}
} }
QString message = finalStr.arg(sanitizeHtml(player->getPlayerInfo()->getName()), cardStr, nameFrom.second); QString message = finalStr.arg(sanitizeHtml(player->getPlayerInfo()->getName()), cardStr, nameFrom.second);

View file

@ -11,6 +11,7 @@
#include <libcockatrice/protocol/pb/command_next_turn.pb.h> #include <libcockatrice/protocol/pb/command_next_turn.pb.h>
#include <libcockatrice/protocol/pb/command_set_active_phase.pb.h> #include <libcockatrice/protocol/pb/command_set_active_phase.pb.h>
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h> #include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
#include <libcockatrice/utility/zone_names.h>
PhaseButton::PhaseButton(const QString &_name, QGraphicsItem *parent, QAction *_doubleClickAction, bool _highlightable) PhaseButton::PhaseButton(const QString &_name, QGraphicsItem *parent, QAction *_doubleClickAction, bool _highlightable)
: QObject(), QGraphicsItem(parent), name(_name), active(false), highlightable(_highlightable), : QObject(), QGraphicsItem(parent), name(_name), active(false), highlightable(_highlightable),
@ -259,7 +260,7 @@ void PhasesToolbar::actNextTurn()
void PhasesToolbar::actUntapAll() void PhasesToolbar::actUntapAll()
{ {
Command_SetCardAttr cmd; Command_SetCardAttr cmd;
cmd.set_zone("table"); cmd.set_zone(ZoneNames::TABLE);
cmd.set_attribute(AttrTapped); cmd.set_attribute(AttrTapped);
cmd.set_attr_value("0"); cmd.set_attr_value("0");

View file

@ -12,6 +12,7 @@
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/relation/card_relation.h> #include <libcockatrice/card/relation/card_relation.h>
#include <libcockatrice/utility/zone_names.h>
CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive) CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive)
: player(_player), card(_card), shortcutsActive(_shortcutsActive) : player(_player), card(_card), shortcutsActive(_shortcutsActive)
@ -115,11 +116,12 @@ CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive
} else if (writeableCard) { } else if (writeableCard) {
if (card->getZone()) { if (card->getZone()) {
if (card->getZone()->getName() == "table") { if (card->getZone()->getName() == ZoneNames::TABLE) {
createTableMenu(); createTableMenu();
} else if (card->getZone()->getName() == "stack") { } else if (card->getZone()->getName() == ZoneNames::STACK) {
createStackMenu(); createStackMenu();
} else if (card->getZone()->getName() == "rfg" || card->getZone()->getName() == "grave") { } else if (card->getZone()->getName() == ZoneNames::EXILE ||
card->getZone()->getName() == ZoneNames::GRAVE) {
createGraveyardOrExileMenu(); createGraveyardOrExileMenu();
} else { } else {
createHandOrCustomZoneMenu(); createHandOrCustomZoneMenu();
@ -128,7 +130,7 @@ CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive
addMenu(new MoveMenu(player)); addMenu(new MoveMenu(player));
} }
} else { } else {
if (card->getZone() && card->getZone()->getName() != "hand") { if (card->getZone() && card->getZone()->getName() != ZoneNames::HAND) {
addAction(aDrawArrow); addAction(aDrawArrow);
addSeparator(); addSeparator();
addRelatedCardView(); addRelatedCardView();
@ -285,7 +287,7 @@ void CardMenu::createHandOrCustomZoneMenu()
addMenu(new MoveMenu(player)); addMenu(new MoveMenu(player));
// actions that are really wonky when done from deck or sideboard // actions that are really wonky when done from deck or sideboard
if (card->getZone()->getName() == "hand") { if (card->getZone()->getName() == ZoneNames::HAND) {
addSeparator(); addSeparator();
addAction(aAttach); addAction(aAttach);
addAction(aDrawArrow); addAction(aDrawArrow);
@ -298,7 +300,7 @@ void CardMenu::createHandOrCustomZoneMenu()
} }
addRelatedCardView(); addRelatedCardView();
if (card->getZone()->getName() == "hand") { if (card->getZone()->getName() == ZoneNames::HAND) {
addRelatedCardActions(); addRelatedCardActions();
} }
} }

View file

@ -6,6 +6,7 @@
#include <QAction> #include <QAction>
#include <QMenu> #include <QMenu>
#include <libcockatrice/utility/zone_names.h>
GraveyardMenu::GraveyardMenu(Player *_player, QWidget *parent) : TearOffMenu(parent), player(_player) GraveyardMenu::GraveyardMenu(Player *_player, QWidget *parent) : TearOffMenu(parent), player(_player)
{ {
@ -39,16 +40,16 @@ void GraveyardMenu::createMoveActions()
if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) {
aMoveGraveToTopLibrary = new QAction(this); aMoveGraveToTopLibrary = new QAction(this);
aMoveGraveToTopLibrary->setData(QList<QVariant>() << "deck" << 0); aMoveGraveToTopLibrary->setData(QList<QVariant>() << ZoneNames::DECK << 0);
aMoveGraveToBottomLibrary = new QAction(this); aMoveGraveToBottomLibrary = new QAction(this);
aMoveGraveToBottomLibrary->setData(QList<QVariant>() << "deck" << -1); aMoveGraveToBottomLibrary->setData(QList<QVariant>() << ZoneNames::DECK << -1);
aMoveGraveToHand = new QAction(this); aMoveGraveToHand = new QAction(this);
aMoveGraveToHand->setData(QList<QVariant>() << "hand" << 0); aMoveGraveToHand->setData(QList<QVariant>() << ZoneNames::HAND << 0);
aMoveGraveToRfg = new QAction(this); aMoveGraveToRfg = new QAction(this);
aMoveGraveToRfg->setData(QList<QVariant>() << "rfg" << 0); aMoveGraveToRfg->setData(QList<QVariant>() << ZoneNames::EXILE << 0);
connect(aMoveGraveToTopLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone); connect(aMoveGraveToTopLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone);
connect(aMoveGraveToBottomLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone); connect(aMoveGraveToBottomLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone);

View file

@ -9,6 +9,7 @@
#include <QAction> #include <QAction>
#include <QMenu> #include <QMenu>
#include <libcockatrice/utility/zone_names.h>
HandMenu::HandMenu(Player *_player, PlayerActions *actions, QWidget *parent) : TearOffMenu(parent), player(_player) HandMenu::HandMenu(Player *_player, PlayerActions *actions, QWidget *parent) : TearOffMenu(parent), player(_player)
{ {
@ -76,13 +77,13 @@ HandMenu::HandMenu(Player *_player, PlayerActions *actions, QWidget *parent) : T
if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) {
aMoveHandToTopLibrary = new QAction(this); aMoveHandToTopLibrary = new QAction(this);
aMoveHandToTopLibrary->setData(QList<QVariant>() << "deck" << 0); aMoveHandToTopLibrary->setData(QList<QVariant>() << ZoneNames::DECK << 0);
aMoveHandToBottomLibrary = new QAction(this); aMoveHandToBottomLibrary = new QAction(this);
aMoveHandToBottomLibrary->setData(QList<QVariant>() << "deck" << -1); aMoveHandToBottomLibrary->setData(QList<QVariant>() << ZoneNames::DECK << -1);
aMoveHandToGrave = new QAction(this); aMoveHandToGrave = new QAction(this);
aMoveHandToGrave->setData(QList<QVariant>() << "grave" << 0); aMoveHandToGrave->setData(QList<QVariant>() << ZoneNames::GRAVE << 0);
aMoveHandToRfg = new QAction(this); aMoveHandToRfg = new QAction(this);
aMoveHandToRfg->setData(QList<QVariant>() << "rfg" << 0); aMoveHandToRfg->setData(QList<QVariant>() << ZoneNames::EXILE << 0);
auto hand = player->getHandZone(); auto hand = player->getHandZone();

View file

@ -51,8 +51,10 @@ LibraryMenu::LibraryMenu(Player *_player, QWidget *parent) : TearOffMenu(parent)
topLibraryMenu->addSeparator(); topLibraryMenu->addSeparator();
topLibraryMenu->addAction(aMoveTopCardToGraveyard); topLibraryMenu->addAction(aMoveTopCardToGraveyard);
topLibraryMenu->addAction(aMoveTopCardsToGraveyard); topLibraryMenu->addAction(aMoveTopCardsToGraveyard);
topLibraryMenu->addAction(aMoveTopCardsToGraveyardFaceDown);
topLibraryMenu->addAction(aMoveTopCardToExile); topLibraryMenu->addAction(aMoveTopCardToExile);
topLibraryMenu->addAction(aMoveTopCardsToExile); topLibraryMenu->addAction(aMoveTopCardsToExile);
topLibraryMenu->addAction(aMoveTopCardsToExileFaceDown);
topLibraryMenu->addAction(aMoveTopCardsUntil); topLibraryMenu->addAction(aMoveTopCardsUntil);
topLibraryMenu->addSeparator(); topLibraryMenu->addSeparator();
topLibraryMenu->addAction(aShuffleTopCards); topLibraryMenu->addAction(aShuffleTopCards);
@ -66,8 +68,10 @@ LibraryMenu::LibraryMenu(Player *_player, QWidget *parent) : TearOffMenu(parent)
bottomLibraryMenu->addSeparator(); bottomLibraryMenu->addSeparator();
bottomLibraryMenu->addAction(aMoveBottomCardToGraveyard); bottomLibraryMenu->addAction(aMoveBottomCardToGraveyard);
bottomLibraryMenu->addAction(aMoveBottomCardsToGraveyard); bottomLibraryMenu->addAction(aMoveBottomCardsToGraveyard);
bottomLibraryMenu->addAction(aMoveBottomCardsToGraveyardFaceDown);
bottomLibraryMenu->addAction(aMoveBottomCardToExile); bottomLibraryMenu->addAction(aMoveBottomCardToExile);
bottomLibraryMenu->addAction(aMoveBottomCardsToExile); bottomLibraryMenu->addAction(aMoveBottomCardsToExile);
bottomLibraryMenu->addAction(aMoveBottomCardsToExileFaceDown);
bottomLibraryMenu->addSeparator(); bottomLibraryMenu->addSeparator();
bottomLibraryMenu->addAction(aShuffleBottomCards); bottomLibraryMenu->addAction(aShuffleBottomCards);
@ -136,8 +140,14 @@ void LibraryMenu::createMoveActions()
connect(aMoveTopCardToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardToExile); connect(aMoveTopCardToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardToExile);
aMoveTopCardsToGraveyard = new QAction(this); aMoveTopCardsToGraveyard = new QAction(this);
connect(aMoveTopCardsToGraveyard, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsToGrave); connect(aMoveTopCardsToGraveyard, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsToGrave);
aMoveTopCardsToGraveyardFaceDown = new QAction(this);
connect(aMoveTopCardsToGraveyardFaceDown, &QAction::triggered, playerActions,
&PlayerActions::actMoveTopCardsToGraveFaceDown);
aMoveTopCardsToExile = new QAction(this); aMoveTopCardsToExile = new QAction(this);
connect(aMoveTopCardsToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsToExile); connect(aMoveTopCardsToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsToExile);
aMoveTopCardsToExileFaceDown = new QAction(this);
connect(aMoveTopCardsToExileFaceDown, &QAction::triggered, playerActions,
&PlayerActions::actMoveTopCardsToExileFaceDown);
aMoveTopCardsUntil = new QAction(this); aMoveTopCardsUntil = new QAction(this);
connect(aMoveTopCardsUntil, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsUntil); connect(aMoveTopCardsUntil, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsUntil);
aMoveTopCardToBottom = new QAction(this); aMoveTopCardToBottom = new QAction(this);
@ -156,8 +166,14 @@ void LibraryMenu::createMoveActions()
aMoveBottomCardsToGraveyard = new QAction(this); aMoveBottomCardsToGraveyard = new QAction(this);
connect(aMoveBottomCardsToGraveyard, &QAction::triggered, playerActions, connect(aMoveBottomCardsToGraveyard, &QAction::triggered, playerActions,
&PlayerActions::actMoveBottomCardsToGrave); &PlayerActions::actMoveBottomCardsToGrave);
aMoveBottomCardsToGraveyardFaceDown = new QAction(this);
connect(aMoveBottomCardsToGraveyardFaceDown, &QAction::triggered, playerActions,
&PlayerActions::actMoveBottomCardsToGraveFaceDown);
aMoveBottomCardsToExile = new QAction(this); aMoveBottomCardsToExile = new QAction(this);
connect(aMoveBottomCardsToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardsToExile); connect(aMoveBottomCardsToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardsToExile);
aMoveBottomCardsToExileFaceDown = new QAction(this);
connect(aMoveBottomCardsToExileFaceDown, &QAction::triggered, playerActions,
&PlayerActions::actMoveBottomCardsToExileFaceDown);
aMoveBottomCardToTop = new QAction(this); aMoveBottomCardToTop = new QAction(this);
connect(aMoveBottomCardToTop, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardToTop); connect(aMoveBottomCardToTop, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardToTop);
} }
@ -216,7 +232,9 @@ void LibraryMenu::retranslateUi()
aMoveTopCardToGraveyard->setText(tr("Move top card to grave&yard")); aMoveTopCardToGraveyard->setText(tr("Move top card to grave&yard"));
aMoveTopCardToExile->setText(tr("Move top card to e&xile")); aMoveTopCardToExile->setText(tr("Move top card to e&xile"));
aMoveTopCardsToGraveyard->setText(tr("Move top cards to &graveyard...")); aMoveTopCardsToGraveyard->setText(tr("Move top cards to &graveyard..."));
aMoveTopCardsToGraveyardFaceDown->setText(tr("Move top cards to graveyard face down..."));
aMoveTopCardsToExile->setText(tr("Move top cards to &exile...")); aMoveTopCardsToExile->setText(tr("Move top cards to &exile..."));
aMoveTopCardsToExileFaceDown->setText(tr("Move top cards to exile face down..."));
aMoveTopCardsUntil->setText(tr("Put top cards on stack &until...")); aMoveTopCardsUntil->setText(tr("Put top cards on stack &until..."));
aShuffleTopCards->setText(tr("Shuffle top cards...")); aShuffleTopCards->setText(tr("Shuffle top cards..."));
@ -227,7 +245,9 @@ void LibraryMenu::retranslateUi()
aMoveBottomCardToGraveyard->setText(tr("Move bottom card to grave&yard")); aMoveBottomCardToGraveyard->setText(tr("Move bottom card to grave&yard"));
aMoveBottomCardToExile->setText(tr("Move bottom card to e&xile")); aMoveBottomCardToExile->setText(tr("Move bottom card to e&xile"));
aMoveBottomCardsToGraveyard->setText(tr("Move bottom cards to &graveyard...")); aMoveBottomCardsToGraveyard->setText(tr("Move bottom cards to &graveyard..."));
aMoveBottomCardsToGraveyardFaceDown->setText(tr("Move bottom cards to graveyard face down..."));
aMoveBottomCardsToExile->setText(tr("Move bottom cards to &exile...")); aMoveBottomCardsToExile->setText(tr("Move bottom cards to &exile..."));
aMoveBottomCardsToExileFaceDown->setText(tr("Move bottom cards to exile face down..."));
aMoveBottomCardToTop->setText(tr("Put bottom card on &top")); aMoveBottomCardToTop->setText(tr("Put bottom card on &top"));
aShuffleBottomCards->setText(tr("Shuffle bottom cards...")); aShuffleBottomCards->setText(tr("Shuffle bottom cards..."));
} }
@ -335,8 +355,10 @@ void LibraryMenu::setShortcutsActive()
aMoveTopToPlayFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveTopToPlayFaceDown")); aMoveTopToPlayFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveTopToPlayFaceDown"));
aMoveTopCardToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToGraveyard")); aMoveTopCardToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToGraveyard"));
aMoveTopCardsToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToGraveyard")); aMoveTopCardsToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToGraveyard"));
aMoveTopCardsToGraveyardFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToGraveyardFaceDown"));
aMoveTopCardToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToExile")); aMoveTopCardToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToExile"));
aMoveTopCardsToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToExile")); aMoveTopCardsToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToExile"));
aMoveTopCardsToExileFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToExileFaceDown"));
aMoveTopCardsUntil->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsUntil")); aMoveTopCardsUntil->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsUntil"));
aMoveTopCardToBottom->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToBottom")); aMoveTopCardToBottom->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToBottom"));
aDrawBottomCard->setShortcuts(shortcuts.getShortcut("Player/aDrawBottomCard")); aDrawBottomCard->setShortcuts(shortcuts.getShortcut("Player/aDrawBottomCard"));
@ -345,8 +367,10 @@ void LibraryMenu::setShortcutsActive()
aMoveBottomToPlayFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomToPlayFaceDown")); aMoveBottomToPlayFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomToPlayFaceDown"));
aMoveBottomCardToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToGrave")); aMoveBottomCardToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToGrave"));
aMoveBottomCardsToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToGrave")); aMoveBottomCardsToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToGrave"));
aMoveBottomCardsToGraveyardFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToGraveFaceDown"));
aMoveBottomCardToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToExile")); aMoveBottomCardToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToExile"));
aMoveBottomCardsToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToExile")); aMoveBottomCardsToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToExile"));
aMoveBottomCardsToExileFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToExileFaceDown"));
aMoveBottomCardToTop->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToTop")); aMoveBottomCardToTop->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToTop"));
} }
@ -367,8 +391,10 @@ void LibraryMenu::setShortcutsInactive()
aMoveTopToPlayFaceDown->setShortcut(QKeySequence()); aMoveTopToPlayFaceDown->setShortcut(QKeySequence());
aMoveTopCardToGraveyard->setShortcut(QKeySequence()); aMoveTopCardToGraveyard->setShortcut(QKeySequence());
aMoveTopCardsToGraveyard->setShortcut(QKeySequence()); aMoveTopCardsToGraveyard->setShortcut(QKeySequence());
aMoveTopCardsToGraveyardFaceDown->setShortcut(QKeySequence());
aMoveTopCardToExile->setShortcut(QKeySequence()); aMoveTopCardToExile->setShortcut(QKeySequence());
aMoveTopCardsToExile->setShortcut(QKeySequence()); aMoveTopCardsToExile->setShortcut(QKeySequence());
aMoveTopCardsToExileFaceDown->setShortcut(QKeySequence());
aMoveTopCardsUntil->setShortcut(QKeySequence()); aMoveTopCardsUntil->setShortcut(QKeySequence());
aDrawBottomCard->setShortcut(QKeySequence()); aDrawBottomCard->setShortcut(QKeySequence());
aDrawBottomCards->setShortcut(QKeySequence()); aDrawBottomCards->setShortcut(QKeySequence());
@ -376,6 +402,8 @@ void LibraryMenu::setShortcutsInactive()
aMoveBottomToPlayFaceDown->setShortcut(QKeySequence()); aMoveBottomToPlayFaceDown->setShortcut(QKeySequence());
aMoveBottomCardToGraveyard->setShortcut(QKeySequence()); aMoveBottomCardToGraveyard->setShortcut(QKeySequence());
aMoveBottomCardsToGraveyard->setShortcut(QKeySequence()); aMoveBottomCardsToGraveyard->setShortcut(QKeySequence());
aMoveBottomCardsToGraveyardFaceDown->setShortcut(QKeySequence());
aMoveBottomCardToExile->setShortcut(QKeySequence()); aMoveBottomCardToExile->setShortcut(QKeySequence());
aMoveBottomCardsToExile->setShortcut(QKeySequence()); aMoveBottomCardsToExile->setShortcut(QKeySequence());
aMoveBottomCardsToExileFaceDown->setShortcut(QKeySequence());
} }

View file

@ -88,7 +88,9 @@ public:
QAction *aMoveTopCardToGraveyard = nullptr; QAction *aMoveTopCardToGraveyard = nullptr;
QAction *aMoveTopCardToExile = nullptr; QAction *aMoveTopCardToExile = nullptr;
QAction *aMoveTopCardsToGraveyard = nullptr; QAction *aMoveTopCardsToGraveyard = nullptr;
QAction *aMoveTopCardsToGraveyardFaceDown = nullptr;
QAction *aMoveTopCardsToExile = nullptr; QAction *aMoveTopCardsToExile = nullptr;
QAction *aMoveTopCardsToExileFaceDown = nullptr;
QAction *aMoveTopCardsUntil = nullptr; QAction *aMoveTopCardsUntil = nullptr;
QAction *aShuffleTopCards = nullptr; QAction *aShuffleTopCards = nullptr;
@ -100,7 +102,9 @@ public:
QAction *aMoveBottomCardToGraveyard = nullptr; QAction *aMoveBottomCardToGraveyard = nullptr;
QAction *aMoveBottomCardToExile = nullptr; QAction *aMoveBottomCardToExile = nullptr;
QAction *aMoveBottomCardsToGraveyard = nullptr; QAction *aMoveBottomCardsToGraveyard = nullptr;
QAction *aMoveBottomCardsToGraveyardFaceDown = nullptr;
QAction *aMoveBottomCardsToExile = nullptr; QAction *aMoveBottomCardsToExile = nullptr;
QAction *aMoveBottomCardsToExileFaceDown = nullptr;
QAction *aShuffleBottomCards = nullptr; QAction *aShuffleBottomCards = nullptr;
int defaultNumberTopCards = 1; int defaultNumberTopCards = 1;

View file

@ -3,6 +3,8 @@
#include "../player.h" #include "../player.h"
#include "../player_actions.h" #include "../player_actions.h"
#include <libcockatrice/utility/zone_names.h>
RfgMenu::RfgMenu(Player *_player, QWidget *parent) : TearOffMenu(parent), player(_player) RfgMenu::RfgMenu(Player *_player, QWidget *parent) : TearOffMenu(parent), player(_player)
{ {
createMoveActions(); createMoveActions();
@ -30,13 +32,13 @@ void RfgMenu::createMoveActions()
auto rfg = player->getRfgZone(); auto rfg = player->getRfgZone();
aMoveRfgToTopLibrary = new QAction(this); aMoveRfgToTopLibrary = new QAction(this);
aMoveRfgToTopLibrary->setData(QList<QVariant>() << "deck" << 0); aMoveRfgToTopLibrary->setData(QList<QVariant>() << ZoneNames::DECK << 0);
aMoveRfgToBottomLibrary = new QAction(this); aMoveRfgToBottomLibrary = new QAction(this);
aMoveRfgToBottomLibrary->setData(QList<QVariant>() << "deck" << -1); aMoveRfgToBottomLibrary->setData(QList<QVariant>() << ZoneNames::DECK << -1);
aMoveRfgToHand = new QAction(this); aMoveRfgToHand = new QAction(this);
aMoveRfgToHand->setData(QList<QVariant>() << "hand" << 0); aMoveRfgToHand->setData(QList<QVariant>() << ZoneNames::HAND << 0);
aMoveRfgToGrave = new QAction(this); aMoveRfgToGrave = new QAction(this);
aMoveRfgToGrave->setData(QList<QVariant>() << "grave" << 0); aMoveRfgToGrave->setData(QList<QVariant>() << ZoneNames::GRAVE << 0);
connect(aMoveRfgToTopLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone); connect(aMoveRfgToTopLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone);
connect(aMoveRfgToBottomLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone); connect(aMoveRfgToBottomLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone);

View file

@ -61,15 +61,15 @@ void Player::forwardActionSignalsToEventHandler()
void Player::initializeZones() void Player::initializeZones()
{ {
addZone(new PileZoneLogic(this, "deck", false, true, false, this)); addZone(new PileZoneLogic(this, ZoneNames::DECK, false, true, false, this));
addZone(new PileZoneLogic(this, "grave", false, false, true, this)); addZone(new PileZoneLogic(this, ZoneNames::GRAVE, false, false, true, this));
addZone(new PileZoneLogic(this, "rfg", false, false, true, this)); addZone(new PileZoneLogic(this, ZoneNames::EXILE, false, false, true, this));
addZone(new PileZoneLogic(this, "sb", false, false, false, this)); addZone(new PileZoneLogic(this, ZoneNames::SIDEBOARD, false, false, false, this));
addZone(new TableZoneLogic(this, "table", true, false, true, this)); addZone(new TableZoneLogic(this, ZoneNames::TABLE, true, false, true, this));
addZone(new StackZoneLogic(this, "stack", true, false, true, this)); addZone(new StackZoneLogic(this, ZoneNames::STACK, true, false, true, this));
bool visibleHand = playerInfo->getLocalOrJudge() || bool visibleHand = playerInfo->getLocalOrJudge() ||
(game->getPlayerManager()->isSpectator() && game->getGameMetaInfo()->spectatorsOmniscient()); (game->getPlayerManager()->isSpectator() && game->getGameMetaInfo()->spectatorsOmniscient());
addZone(new HandZoneLogic(this, "hand", false, false, visibleHand, this)); addZone(new HandZoneLogic(this, ZoneNames::HAND, false, false, visibleHand, this));
} }
Player::~Player() Player::~Player()
@ -119,13 +119,13 @@ void Player::setZoneId(int _zoneId)
void Player::processPlayerInfo(const ServerInfo_Player &info) void Player::processPlayerInfo(const ServerInfo_Player &info)
{ {
static QSet<QString> builtinZones{/* PileZones */ static QSet<QString> builtinZones{/* PileZones */
"deck", "grave", "rfg", "sb", ZoneNames::DECK, ZoneNames::GRAVE, ZoneNames::EXILE, ZoneNames::SIDEBOARD,
/* TableZone */ /* TableZone */
"table", ZoneNames::TABLE,
/* StackZone */ /* StackZone */
"stack", ZoneNames::STACK,
/* HandZone */ /* HandZone */
"hand"}; ZoneNames::HAND};
clearCounters(); clearCounters();
clearArrows(); clearArrows();

View file

@ -27,6 +27,7 @@
#include <libcockatrice/filters/filter_string.h> #include <libcockatrice/filters/filter_string.h>
#include <libcockatrice/protocol/pb/card_attributes.pb.h> #include <libcockatrice/protocol/pb/card_attributes.pb.h>
#include <libcockatrice/protocol/pb/game_event.pb.h> #include <libcockatrice/protocol/pb/game_event.pb.h>
#include <libcockatrice/utility/zone_names.h>
inline Q_LOGGING_CATEGORY(PlayerLog, "player"); inline Q_LOGGING_CATEGORY(PlayerLog, "player");
@ -155,37 +156,37 @@ public:
PileZoneLogic *getDeckZone() PileZoneLogic *getDeckZone()
{ {
return qobject_cast<PileZoneLogic *>(zones.value("deck")); return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::DECK));
} }
PileZoneLogic *getGraveZone() PileZoneLogic *getGraveZone()
{ {
return qobject_cast<PileZoneLogic *>(zones.value("grave")); return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::GRAVE));
} }
PileZoneLogic *getRfgZone() PileZoneLogic *getRfgZone()
{ {
return qobject_cast<PileZoneLogic *>(zones.value("rfg")); return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::EXILE));
} }
PileZoneLogic *getSideboardZone() PileZoneLogic *getSideboardZone()
{ {
return qobject_cast<PileZoneLogic *>(zones.value("sb")); return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::SIDEBOARD));
} }
TableZoneLogic *getTableZone() TableZoneLogic *getTableZone()
{ {
return qobject_cast<TableZoneLogic *>(zones.value("table")); return qobject_cast<TableZoneLogic *>(zones.value(ZoneNames::TABLE));
} }
StackZoneLogic *getStackZone() StackZoneLogic *getStackZone()
{ {
return qobject_cast<StackZoneLogic *>(zones.value("stack")); return qobject_cast<StackZoneLogic *>(zones.value(ZoneNames::STACK));
} }
HandZoneLogic *getHandZone() HandZoneLogic *getHandZone()
{ {
return qobject_cast<HandZoneLogic *>(zones.value("hand")); return qobject_cast<HandZoneLogic *>(zones.value(ZoneNames::HAND));
} }
AbstractCounter *addCounter(const ServerInfo_Counter &counter); AbstractCounter *addCounter(const ServerInfo_Counter &counter);

View file

@ -28,6 +28,7 @@
#include <libcockatrice/protocol/pb/command_undo_draw.pb.h> #include <libcockatrice/protocol/pb/command_undo_draw.pb.h>
#include <libcockatrice/protocol/pb/context_move_card.pb.h> #include <libcockatrice/protocol/pb/context_move_card.pb.h>
#include <libcockatrice/utility/trice_limits.h> #include <libcockatrice/utility/trice_limits.h>
#include <libcockatrice/utility/zone_names.h>
// milliseconds in between triggers of the move top cards until action // milliseconds in between triggers of the move top cards until action
static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100; static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100;
@ -63,13 +64,13 @@ void PlayerActions::playCard(CardItem *card, bool faceDown)
int tableRow = info.getUiAttributes().tableRow; int tableRow = info.getUiAttributes().tableRow;
bool playToStack = SettingsCache::instance().getPlayToStack(); bool playToStack = SettingsCache::instance().getPlayToStack();
QString currentZone = card->getZone()->getName(); QString currentZone = card->getZone()->getName();
if (currentZone == "stack" && tableRow == 3) { if (currentZone == ZoneNames::STACK && tableRow == 3) {
cmd.set_target_zone("grave"); cmd.set_target_zone(ZoneNames::GRAVE);
cmd.set_x(0); cmd.set_x(0);
cmd.set_y(0); cmd.set_y(0);
} else if (!faceDown && } else if (!faceDown && ((!playToStack && tableRow == 3) ||
((!playToStack && tableRow == 3) || ((playToStack && tableRow != 0) && currentZone != "stack"))) { ((playToStack && tableRow != 0) && currentZone != ZoneNames::STACK))) {
cmd.set_target_zone("stack"); cmd.set_target_zone(ZoneNames::STACK);
cmd.set_x(-1); cmd.set_x(-1);
cmd.set_y(0); cmd.set_y(0);
} else { } else {
@ -81,7 +82,7 @@ void PlayerActions::playCard(CardItem *card, bool faceDown)
} }
cardToMove->set_tapped(!faceDown && info.getUiAttributes().cipt); cardToMove->set_tapped(!faceDown && info.getUiAttributes().cipt);
if (tableRow != 3) if (tableRow != 3)
cmd.set_target_zone("table"); cmd.set_target_zone(ZoneNames::TABLE);
cmd.set_x(gridPoint.x()); cmd.set_x(gridPoint.x());
cmd.set_y(gridPoint.y()); cmd.set_y(gridPoint.y());
} }
@ -124,7 +125,7 @@ void PlayerActions::playCardToTable(const CardItem *card, bool faceDown)
cardToMove->set_pt(info.getPowTough().toStdString()); cardToMove->set_pt(info.getPowTough().toStdString());
} }
cardToMove->set_tapped(!faceDown && info.getUiAttributes().cipt); cardToMove->set_tapped(!faceDown && info.getUiAttributes().cipt);
cmd.set_target_zone("table"); cmd.set_target_zone(ZoneNames::TABLE);
cmd.set_x(gridPoint.x()); cmd.set_x(gridPoint.x());
cmd.set_y(gridPoint.y()); cmd.set_y(gridPoint.y());
sendGameCommand(cmd); sendGameCommand(cmd);
@ -132,12 +133,12 @@ void PlayerActions::playCardToTable(const CardItem *card, bool faceDown)
void PlayerActions::actViewLibrary() void PlayerActions::actViewLibrary()
{ {
player->getGameScene()->toggleZoneView(player, "deck", -1); player->getGameScene()->toggleZoneView(player, ZoneNames::DECK, -1);
} }
void PlayerActions::actViewHand() void PlayerActions::actViewHand()
{ {
player->getGameScene()->toggleZoneView(player, "hand", -1); player->getGameScene()->toggleZoneView(player, ZoneNames::HAND, -1);
} }
/** /**
@ -181,7 +182,7 @@ void PlayerActions::actViewTopCards()
deckSize, 1, &ok); deckSize, 1, &ok);
if (ok) { if (ok) {
defaultNumberTopCards = number; defaultNumberTopCards = number;
player->getGameScene()->toggleZoneView(player, "deck", number); player->getGameScene()->toggleZoneView(player, ZoneNames::DECK, number);
} }
} }
@ -194,14 +195,14 @@ void PlayerActions::actViewBottomCards()
deckSize, 1, &ok); deckSize, 1, &ok);
if (ok) { if (ok) {
defaultNumberBottomCards = number; defaultNumberBottomCards = number;
player->getGameScene()->toggleZoneView(player, "deck", number, true); player->getGameScene()->toggleZoneView(player, ZoneNames::DECK, number, true);
} }
} }
void PlayerActions::actAlwaysRevealTopCard() void PlayerActions::actAlwaysRevealTopCard()
{ {
Command_ChangeZoneProperties cmd; Command_ChangeZoneProperties cmd;
cmd.set_zone_name("deck"); cmd.set_zone_name(ZoneNames::DECK);
cmd.set_always_reveal_top_card(player->getPlayerMenu()->getLibraryMenu()->isAlwaysRevealTopCardChecked()); cmd.set_always_reveal_top_card(player->getPlayerMenu()->getLibraryMenu()->isAlwaysRevealTopCardChecked());
sendGameCommand(cmd); sendGameCommand(cmd);
@ -210,7 +211,7 @@ void PlayerActions::actAlwaysRevealTopCard()
void PlayerActions::actAlwaysLookAtTopCard() void PlayerActions::actAlwaysLookAtTopCard()
{ {
Command_ChangeZoneProperties cmd; Command_ChangeZoneProperties cmd;
cmd.set_zone_name("deck"); cmd.set_zone_name(ZoneNames::DECK);
cmd.set_always_look_at_top_card(player->getPlayerMenu()->getLibraryMenu()->isAlwaysLookAtTopCardChecked()); cmd.set_always_look_at_top_card(player->getPlayerMenu()->getLibraryMenu()->isAlwaysLookAtTopCardChecked());
sendGameCommand(cmd); sendGameCommand(cmd);
@ -223,17 +224,17 @@ void PlayerActions::actOpenDeckInDeckEditor()
void PlayerActions::actViewGraveyard() void PlayerActions::actViewGraveyard()
{ {
player->getGameScene()->toggleZoneView(player, "grave", -1); player->getGameScene()->toggleZoneView(player, ZoneNames::GRAVE, -1);
} }
void PlayerActions::actViewRfg() void PlayerActions::actViewRfg()
{ {
player->getGameScene()->toggleZoneView(player, "rfg", -1); player->getGameScene()->toggleZoneView(player, ZoneNames::EXILE, -1);
} }
void PlayerActions::actViewSideboard() void PlayerActions::actViewSideboard()
{ {
player->getGameScene()->toggleZoneView(player, "sb", -1); player->getGameScene()->toggleZoneView(player, ZoneNames::SIDEBOARD, -1);
} }
void PlayerActions::actShuffle() void PlayerActions::actShuffle()
@ -263,7 +264,7 @@ void PlayerActions::actShuffleTop()
defaultNumberTopCards = number; defaultNumberTopCards = number;
Command_Shuffle cmd; Command_Shuffle cmd;
cmd.set_zone_name("deck"); cmd.set_zone_name(ZoneNames::DECK);
cmd.set_start(0); cmd.set_start(0);
cmd.set_end(number - 1); // inclusive, the indexed card at end will be shuffled cmd.set_end(number - 1); // inclusive, the indexed card at end will be shuffled
@ -292,7 +293,7 @@ void PlayerActions::actShuffleBottom()
defaultNumberBottomCards = number; defaultNumberBottomCards = number;
Command_Shuffle cmd; Command_Shuffle cmd;
cmd.set_zone_name("deck"); cmd.set_zone_name(ZoneNames::DECK);
cmd.set_start(-number); cmd.set_start(-number);
cmd.set_end(-1); cmd.set_end(-1);
@ -376,7 +377,7 @@ void PlayerActions::actUndoDraw()
void PlayerActions::cmdSetTopCard(Command_MoveCard &cmd) void PlayerActions::cmdSetTopCard(Command_MoveCard &cmd)
{ {
cmd.set_start_zone("deck"); cmd.set_start_zone(ZoneNames::DECK);
auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); auto *cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(0); cardToMove->set_card_id(0);
cmd.set_target_player_id(player->getPlayerInfo()->getId()); cmd.set_target_player_id(player->getPlayerInfo()->getId());
@ -386,7 +387,7 @@ void PlayerActions::cmdSetBottomCard(Command_MoveCard &cmd)
{ {
CardZoneLogic *zone = player->getDeckZone(); CardZoneLogic *zone = player->getDeckZone();
int lastCard = zone->getCards().size() - 1; int lastCard = zone->getCards().size() - 1;
cmd.set_start_zone("deck"); cmd.set_start_zone(ZoneNames::DECK);
auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); auto *cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(lastCard); cardToMove->set_card_id(lastCard);
cmd.set_target_player_id(player->getPlayerInfo()->getId()); cmd.set_target_player_id(player->getPlayerInfo()->getId());
@ -400,7 +401,7 @@ void PlayerActions::actMoveTopCardToGrave()
Command_MoveCard cmd; Command_MoveCard cmd;
cmdSetTopCard(cmd); cmdSetTopCard(cmd);
cmd.set_target_zone("grave"); cmd.set_target_zone(ZoneNames::GRAVE);
cmd.set_x(0); cmd.set_x(0);
cmd.set_y(0); cmd.set_y(0);
@ -415,7 +416,7 @@ void PlayerActions::actMoveTopCardToExile()
Command_MoveCard cmd; Command_MoveCard cmd;
cmdSetTopCard(cmd); cmdSetTopCard(cmd);
cmd.set_target_zone("rfg"); cmd.set_target_zone(ZoneNames::EXILE);
cmd.set_x(0); cmd.set_x(0);
cmd.set_y(0); cmd.set_y(0);
@ -424,37 +425,25 @@ void PlayerActions::actMoveTopCardToExile()
void PlayerActions::actMoveTopCardsToGrave() void PlayerActions::actMoveTopCardsToGrave()
{ {
const int maxCards = player->getDeckZone()->getCards().size(); moveTopCardsTo(ZoneNames::GRAVE, tr("grave"), false);
if (maxCards == 0) { }
return;
}
bool ok; void PlayerActions::actMoveTopCardsToGraveFaceDown()
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move top cards to grave"), {
tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1, moveTopCardsTo(ZoneNames::GRAVE, tr("grave"), true);
maxCards, 1, &ok);
if (!ok) {
return;
} else if (number > maxCards) {
number = maxCards;
}
defaultNumberTopCards = number;
Command_MoveCard cmd;
cmd.set_start_zone("deck");
cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone("grave");
cmd.set_x(0);
cmd.set_y(0);
for (int i = number - 1; i >= 0; --i) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(i);
}
sendGameCommand(cmd);
} }
void PlayerActions::actMoveTopCardsToExile() void PlayerActions::actMoveTopCardsToExile()
{
moveTopCardsTo(ZoneNames::EXILE, tr("exile"), false);
}
void PlayerActions::actMoveTopCardsToExileFaceDown()
{
moveTopCardsTo(ZoneNames::EXILE, tr("exile"), true);
}
void PlayerActions::moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown)
{ {
const int maxCards = player->getDeckZone()->getCards().size(); const int maxCards = player->getDeckZone()->getCards().size();
if (maxCards == 0) { if (maxCards == 0) {
@ -462,25 +451,31 @@ void PlayerActions::actMoveTopCardsToExile()
} }
bool ok; bool ok;
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move top cards to exile"), int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move top cards to %1").arg(zoneDisplayName),
tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1, tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1,
maxCards, 1, &ok); maxCards, 1, &ok);
if (!ok) { if (!ok) {
return; return;
} else if (number > maxCards) { }
if (number > maxCards) {
number = maxCards; number = maxCards;
} }
defaultNumberTopCards = number; defaultNumberTopCards = number;
Command_MoveCard cmd; Command_MoveCard cmd;
cmd.set_start_zone("deck"); cmd.set_start_zone(ZoneNames::DECK);
cmd.set_target_player_id(player->getPlayerInfo()->getId()); cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone("rfg"); cmd.set_target_zone(targetZone.toStdString());
cmd.set_x(0); cmd.set_x(0);
cmd.set_y(0); cmd.set_y(0);
for (int i = number - 1; i >= 0; --i) { for (int i = number - 1; i >= 0; --i) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(i); auto card = cmd.mutable_cards_to_move()->add_card();
card->set_card_id(i);
if (faceDown) {
card->set_face_down(true);
}
} }
sendGameCommand(cmd); sendGameCommand(cmd);
@ -555,7 +550,7 @@ void PlayerActions::actMoveTopCardToBottom()
Command_MoveCard cmd; Command_MoveCard cmd;
cmdSetTopCard(cmd); cmdSetTopCard(cmd);
cmd.set_target_zone("deck"); cmd.set_target_zone(ZoneNames::DECK);
cmd.set_x(-1); // bottom of deck cmd.set_x(-1); // bottom of deck
cmd.set_y(0); cmd.set_y(0);
@ -570,7 +565,7 @@ void PlayerActions::actMoveTopCardToPlay()
Command_MoveCard cmd; Command_MoveCard cmd;
cmdSetTopCard(cmd); cmdSetTopCard(cmd);
cmd.set_target_zone("stack"); cmd.set_target_zone(ZoneNames::STACK);
cmd.set_x(-1); cmd.set_x(-1);
cmd.set_y(0); cmd.set_y(0);
@ -584,12 +579,12 @@ void PlayerActions::actMoveTopCardToPlayFaceDown()
} }
Command_MoveCard cmd; Command_MoveCard cmd;
cmd.set_start_zone("deck"); cmd.set_start_zone(ZoneNames::DECK);
CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card(); CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(0); cardToMove->set_card_id(0);
cardToMove->set_face_down(true); cardToMove->set_face_down(true);
cmd.set_target_player_id(player->getPlayerInfo()->getId()); cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone("table"); cmd.set_target_zone(ZoneNames::TABLE);
cmd.set_x(-1); cmd.set_x(-1);
cmd.set_y(0); cmd.set_y(0);
@ -604,7 +599,7 @@ void PlayerActions::actMoveBottomCardToGrave()
Command_MoveCard cmd; Command_MoveCard cmd;
cmdSetBottomCard(cmd); cmdSetBottomCard(cmd);
cmd.set_target_zone("grave"); cmd.set_target_zone(ZoneNames::GRAVE);
cmd.set_x(0); cmd.set_x(0);
cmd.set_y(0); cmd.set_y(0);
@ -619,7 +614,7 @@ void PlayerActions::actMoveBottomCardToExile()
Command_MoveCard cmd; Command_MoveCard cmd;
cmdSetBottomCard(cmd); cmdSetBottomCard(cmd);
cmd.set_target_zone("rfg"); cmd.set_target_zone(ZoneNames::EXILE);
cmd.set_x(0); cmd.set_x(0);
cmd.set_y(0); cmd.set_y(0);
@ -628,37 +623,25 @@ void PlayerActions::actMoveBottomCardToExile()
void PlayerActions::actMoveBottomCardsToGrave() void PlayerActions::actMoveBottomCardsToGrave()
{ {
const int maxCards = player->getDeckZone()->getCards().size(); moveBottomCardsTo(ZoneNames::GRAVE, tr("grave"), false);
if (maxCards == 0) { }
return;
}
bool ok; void PlayerActions::actMoveBottomCardsToGraveFaceDown()
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move bottom cards to grave"), {
tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, moveBottomCardsTo(ZoneNames::GRAVE, tr("grave"), true);
maxCards, 1, &ok);
if (!ok) {
return;
} else if (number > maxCards) {
number = maxCards;
}
defaultNumberBottomCards = number;
Command_MoveCard cmd;
cmd.set_start_zone("deck");
cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone("grave");
cmd.set_x(0);
cmd.set_y(0);
for (int i = maxCards - number; i < maxCards; ++i) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(i);
}
sendGameCommand(cmd);
} }
void PlayerActions::actMoveBottomCardsToExile() void PlayerActions::actMoveBottomCardsToExile()
{
moveBottomCardsTo(ZoneNames::EXILE, tr("exile"), false);
}
void PlayerActions::actMoveBottomCardsToExileFaceDown()
{
moveBottomCardsTo(ZoneNames::EXILE, tr("exile"), true);
}
void PlayerActions::moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown)
{ {
const int maxCards = player->getDeckZone()->getCards().size(); const int maxCards = player->getDeckZone()->getCards().size();
if (maxCards == 0) { if (maxCards == 0) {
@ -666,25 +649,31 @@ void PlayerActions::actMoveBottomCardsToExile()
} }
bool ok; bool ok;
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move bottom cards to exile"), int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move bottom cards to %1").arg(zoneDisplayName),
tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1,
maxCards, 1, &ok); maxCards, 1, &ok);
if (!ok) { if (!ok) {
return; return;
} else if (number > maxCards) { }
if (number > maxCards) {
number = maxCards; number = maxCards;
} }
defaultNumberBottomCards = number; defaultNumberBottomCards = number;
Command_MoveCard cmd; Command_MoveCard cmd;
cmd.set_start_zone("deck"); cmd.set_start_zone(ZoneNames::DECK);
cmd.set_target_player_id(player->getPlayerInfo()->getId()); cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone("rfg"); cmd.set_target_zone(targetZone.toStdString());
cmd.set_x(0); cmd.set_x(0);
cmd.set_y(0); cmd.set_y(0);
for (int i = maxCards - number; i < maxCards; ++i) { for (int i = maxCards - number; i < maxCards; ++i) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(i); auto card = cmd.mutable_cards_to_move()->add_card();
card->set_card_id(i);
if (faceDown) {
card->set_face_down(true);
}
} }
sendGameCommand(cmd); sendGameCommand(cmd);
@ -698,7 +687,7 @@ void PlayerActions::actMoveBottomCardToTop()
Command_MoveCard cmd; Command_MoveCard cmd;
cmdSetBottomCard(cmd); cmdSetBottomCard(cmd);
cmd.set_target_zone("deck"); cmd.set_target_zone(ZoneNames::DECK);
cmd.set_x(0); // top of deck cmd.set_x(0); // top of deck
cmd.set_y(0); cmd.set_y(0);
@ -768,7 +757,7 @@ void PlayerActions::actDrawBottomCard()
Command_MoveCard cmd; Command_MoveCard cmd;
cmdSetBottomCard(cmd); cmdSetBottomCard(cmd);
cmd.set_target_zone("hand"); cmd.set_target_zone(ZoneNames::HAND);
cmd.set_x(0); cmd.set_x(0);
cmd.set_y(0); cmd.set_y(0);
@ -794,9 +783,9 @@ void PlayerActions::actDrawBottomCards()
defaultNumberBottomCards = number; defaultNumberBottomCards = number;
Command_MoveCard cmd; Command_MoveCard cmd;
cmd.set_start_zone("deck"); cmd.set_start_zone(ZoneNames::DECK);
cmd.set_target_player_id(player->getPlayerInfo()->getId()); cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone("hand"); cmd.set_target_zone(ZoneNames::HAND);
cmd.set_x(0); cmd.set_x(0);
cmd.set_y(0); cmd.set_y(0);
@ -815,7 +804,7 @@ void PlayerActions::actMoveBottomCardToPlay()
Command_MoveCard cmd; Command_MoveCard cmd;
cmdSetBottomCard(cmd); cmdSetBottomCard(cmd);
cmd.set_target_zone("stack"); cmd.set_target_zone(ZoneNames::STACK);
cmd.set_x(-1); cmd.set_x(-1);
cmd.set_y(0); cmd.set_y(0);
@ -832,13 +821,13 @@ void PlayerActions::actMoveBottomCardToPlayFaceDown()
int lastCard = zone->getCards().size() - 1; int lastCard = zone->getCards().size() - 1;
Command_MoveCard cmd; Command_MoveCard cmd;
cmd.set_start_zone("deck"); cmd.set_start_zone(ZoneNames::DECK);
auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); auto *cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(lastCard); cardToMove->set_card_id(lastCard);
cardToMove->set_face_down(true); cardToMove->set_face_down(true);
cmd.set_target_player_id(player->getPlayerInfo()->getId()); cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone("table"); cmd.set_target_zone(ZoneNames::TABLE);
cmd.set_x(-1); cmd.set_x(-1);
cmd.set_y(0); cmd.set_y(0);
@ -848,7 +837,7 @@ void PlayerActions::actMoveBottomCardToPlayFaceDown()
void PlayerActions::actUntapAll() void PlayerActions::actUntapAll()
{ {
Command_SetCardAttr cmd; Command_SetCardAttr cmd;
cmd.set_zone("table"); cmd.set_zone(ZoneNames::TABLE);
cmd.set_attribute(AttrTapped); cmd.set_attribute(AttrTapped);
cmd.set_attr_value("0"); cmd.set_attr_value("0");
@ -898,7 +887,7 @@ void PlayerActions::actCreateAnotherToken()
} }
Command_CreateToken cmd; Command_CreateToken cmd;
cmd.set_zone("table"); cmd.set_zone(ZoneNames::TABLE);
cmd.set_card_name(lastTokenInfo.name.toStdString()); cmd.set_card_name(lastTokenInfo.name.toStdString());
cmd.set_card_provider_id(lastTokenInfo.providerId.toStdString()); cmd.set_card_provider_id(lastTokenInfo.providerId.toStdString());
cmd.set_color(lastTokenInfo.color.toStdString()); cmd.set_color(lastTokenInfo.color.toStdString());
@ -1079,7 +1068,7 @@ bool PlayerActions::createRelatedFromRelation(const CardItem *sourceCard, const
// move card onto table first if attaching from some other zone // move card onto table first if attaching from some other zone
// we only do this for AttachTo because cross-zone TransformInto is already handled server-side // we only do this for AttachTo because cross-zone TransformInto is already handled server-side
if (attachType == CardRelationType::AttachTo && sourceCard->getZone()->getName() != "table") { if (attachType == CardRelationType::AttachTo && sourceCard->getZone()->getName() != ZoneNames::TABLE) {
playCardToTable(sourceCard, false); playCardToTable(sourceCard, false);
} }
@ -1105,7 +1094,7 @@ void PlayerActions::createCard(const CardItem *sourceCard,
// create the token for the related card // create the token for the related card
Command_CreateToken cmd; Command_CreateToken cmd;
cmd.set_zone("table"); cmd.set_zone(ZoneNames::TABLE);
cmd.set_card_name(cardInfo->getName().toStdString()); cmd.set_card_name(cardInfo->getName().toStdString());
switch (cardInfo->getColors().size()) { switch (cardInfo->getColors().size()) {
case 0: case 0:
@ -1134,12 +1123,12 @@ void PlayerActions::createCard(const CardItem *sourceCard,
switch (attachType) { switch (attachType) {
case CardRelationType::DoesNotAttach: case CardRelationType::DoesNotAttach:
cmd.set_target_zone("table"); cmd.set_target_zone(ZoneNames::TABLE);
cmd.set_card_provider_id(relatedCard.getPrinting().getUuid().toStdString()); cmd.set_card_provider_id(relatedCard.getPrinting().getUuid().toStdString());
break; break;
case CardRelationType::AttachTo: case CardRelationType::AttachTo:
cmd.set_target_zone("table"); // We currently only support creating tokens on the table cmd.set_target_zone(ZoneNames::TABLE); // We currently only support creating tokens on the table
cmd.set_card_provider_id(relatedCard.getPrinting().getUuid().toStdString()); cmd.set_card_provider_id(relatedCard.getPrinting().getUuid().toStdString());
cmd.set_target_card_id(sourceCard->getId()); cmd.set_target_card_id(sourceCard->getId());
cmd.set_target_mode(Command_CreateToken::ATTACH_TO); cmd.set_target_mode(Command_CreateToken::ATTACH_TO);
@ -1147,7 +1136,7 @@ void PlayerActions::createCard(const CardItem *sourceCard,
case CardRelationType::TransformInto: case CardRelationType::TransformInto:
// allow cards to directly transform on stack // allow cards to directly transform on stack
cmd.set_zone(sourceCard->getZone()->getName() == "stack" ? "stack" : "table"); cmd.set_zone(sourceCard->getZone()->getName() == ZoneNames::STACK ? ZoneNames::STACK : ZoneNames::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());
@ -1262,7 +1251,7 @@ void PlayerActions::actMoveCardXCardsFromTop()
cmd->set_start_zone(startZone.toStdString()); cmd->set_start_zone(startZone.toStdString());
cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->mutable_cards_to_move()->CopyFrom(idList);
cmd->set_target_player_id(player->getPlayerInfo()->getId()); cmd->set_target_player_id(player->getPlayerInfo()->getId());
cmd->set_target_zone("deck"); cmd->set_target_zone(ZoneNames::DECK);
cmd->set_x(number); cmd->set_x(number);
cmd->set_y(0); cmd->set_y(0);
commandList.append(cmd); commandList.append(cmd);
@ -1651,7 +1640,7 @@ void PlayerActions::playSelectedCards(const bool faceDown)
[](const auto &card1, const auto &card2) { return card1->getId() > card2->getId(); }); [](const auto &card1, const auto &card2) { return card1->getId() > card2->getId(); });
for (auto &card : selectedCards) { for (auto &card : selectedCards) {
if (card && !isUnwritableRevealZone(card->getZone()) && card->getZone()->getName() != "table") { if (card && !isUnwritableRevealZone(card->getZone()) && card->getZone()->getName() != ZoneNames::TABLE) {
playCard(card, faceDown); playCard(card, faceDown);
} }
} }
@ -1704,7 +1693,7 @@ void PlayerActions::actRevealHand(int revealToPlayerId)
if (revealToPlayerId != -1) { if (revealToPlayerId != -1) {
cmd.set_player_id(revealToPlayerId); cmd.set_player_id(revealToPlayerId);
} }
cmd.set_zone_name("hand"); cmd.set_zone_name(ZoneNames::HAND);
sendGameCommand(cmd); sendGameCommand(cmd);
} }
@ -1715,7 +1704,7 @@ void PlayerActions::actRevealRandomHandCard(int revealToPlayerId)
if (revealToPlayerId != -1) { if (revealToPlayerId != -1) {
cmd.set_player_id(revealToPlayerId); cmd.set_player_id(revealToPlayerId);
} }
cmd.set_zone_name("hand"); cmd.set_zone_name(ZoneNames::HAND);
cmd.add_card_id(RANDOM_CARD_FROM_ZONE); cmd.add_card_id(RANDOM_CARD_FROM_ZONE);
sendGameCommand(cmd); sendGameCommand(cmd);
@ -1727,7 +1716,7 @@ void PlayerActions::actRevealLibrary(int revealToPlayerId)
if (revealToPlayerId != -1) { if (revealToPlayerId != -1) {
cmd.set_player_id(revealToPlayerId); cmd.set_player_id(revealToPlayerId);
} }
cmd.set_zone_name("deck"); cmd.set_zone_name(ZoneNames::DECK);
sendGameCommand(cmd); sendGameCommand(cmd);
} }
@ -1738,7 +1727,7 @@ void PlayerActions::actLendLibrary(int lendToPlayerId)
if (lendToPlayerId != -1) { if (lendToPlayerId != -1) {
cmd.set_player_id(lendToPlayerId); cmd.set_player_id(lendToPlayerId);
} }
cmd.set_zone_name("deck"); cmd.set_zone_name(ZoneNames::DECK);
cmd.set_grant_write_access(true); cmd.set_grant_write_access(true);
sendGameCommand(cmd); sendGameCommand(cmd);
@ -1751,7 +1740,7 @@ void PlayerActions::actRevealTopCards(int revealToPlayerId, int amount)
cmd.set_player_id(revealToPlayerId); cmd.set_player_id(revealToPlayerId);
} }
cmd.set_zone_name("deck"); cmd.set_zone_name(ZoneNames::DECK);
cmd.set_top_cards(amount); cmd.set_top_cards(amount);
// backward compatibility: servers before #1051 only permits to reveal the first card // backward compatibility: servers before #1051 only permits to reveal the first card
cmd.add_card_id(0); cmd.add_card_id(0);
@ -1765,7 +1754,7 @@ void PlayerActions::actRevealRandomGraveyardCard(int revealToPlayerId)
if (revealToPlayerId != -1) { if (revealToPlayerId != -1) {
cmd.set_player_id(revealToPlayerId); cmd.set_player_id(revealToPlayerId);
} }
cmd.set_zone_name("grave"); cmd.set_zone_name(ZoneNames::GRAVE);
cmd.add_card_id(RANDOM_CARD_FROM_ZONE); cmd.add_card_id(RANDOM_CARD_FROM_ZONE);
sendGameCommand(cmd); sendGameCommand(cmd);
} }
@ -1828,7 +1817,7 @@ void PlayerActions::cardMenuAction()
} }
case cmClone: { case cmClone: {
auto *cmd = new Command_CreateToken; auto *cmd = new Command_CreateToken;
cmd->set_zone("table"); cmd->set_zone(ZoneNames::TABLE);
cmd->set_card_name(card->getName().toStdString()); cmd->set_card_name(card->getName().toStdString());
cmd->set_card_provider_id(card->getProviderId().toStdString()); cmd->set_card_provider_id(card->getProviderId().toStdString());
cmd->set_color(card->getColor().toStdString()); cmd->set_color(card->getColor().toStdString());
@ -1870,13 +1859,13 @@ void PlayerActions::cardMenuAction()
cmd->set_start_zone(startZone.toStdString()); cmd->set_start_zone(startZone.toStdString());
cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->mutable_cards_to_move()->CopyFrom(idList);
cmd->set_target_player_id(player->getPlayerInfo()->getId()); cmd->set_target_player_id(player->getPlayerInfo()->getId());
cmd->set_target_zone("deck"); cmd->set_target_zone(ZoneNames::DECK);
cmd->set_x(0); cmd->set_x(0);
cmd->set_y(0); cmd->set_y(0);
if (idList.card_size() > 1) { if (idList.card_size() > 1) {
auto *scmd = new Command_Shuffle; auto *scmd = new Command_Shuffle;
scmd->set_zone_name("deck"); scmd->set_zone_name(ZoneNames::DECK);
scmd->set_start(0); scmd->set_start(0);
scmd->set_end(idList.card_size() - 1); // inclusive, the indexed card at end will be shuffled scmd->set_end(idList.card_size() - 1); // inclusive, the indexed card at end will be shuffled
// Server process events backwards, so... // Server process events backwards, so...
@ -1892,13 +1881,13 @@ void PlayerActions::cardMenuAction()
cmd->set_start_zone(startZone.toStdString()); cmd->set_start_zone(startZone.toStdString());
cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->mutable_cards_to_move()->CopyFrom(idList);
cmd->set_target_player_id(player->getPlayerInfo()->getId()); cmd->set_target_player_id(player->getPlayerInfo()->getId());
cmd->set_target_zone("deck"); cmd->set_target_zone(ZoneNames::DECK);
cmd->set_x(-1); cmd->set_x(-1);
cmd->set_y(0); cmd->set_y(0);
if (idList.card_size() > 1) { if (idList.card_size() > 1) {
auto *scmd = new Command_Shuffle; auto *scmd = new Command_Shuffle;
scmd->set_zone_name("deck"); scmd->set_zone_name(ZoneNames::DECK);
scmd->set_start(-idList.card_size()); scmd->set_start(-idList.card_size());
scmd->set_end(-1); scmd->set_end(-1);
// Server process events backwards, so... // Server process events backwards, so...
@ -1914,7 +1903,7 @@ void PlayerActions::cardMenuAction()
cmd->set_start_zone(startZone.toStdString()); cmd->set_start_zone(startZone.toStdString());
cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->mutable_cards_to_move()->CopyFrom(idList);
cmd->set_target_player_id(player->getPlayerInfo()->getId()); cmd->set_target_player_id(player->getPlayerInfo()->getId());
cmd->set_target_zone("hand"); cmd->set_target_zone(ZoneNames::HAND);
cmd->set_x(0); cmd->set_x(0);
cmd->set_y(0); cmd->set_y(0);
commandList.append(cmd); commandList.append(cmd);
@ -1926,7 +1915,7 @@ void PlayerActions::cardMenuAction()
cmd->set_start_zone(startZone.toStdString()); cmd->set_start_zone(startZone.toStdString());
cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->mutable_cards_to_move()->CopyFrom(idList);
cmd->set_target_player_id(player->getPlayerInfo()->getId()); cmd->set_target_player_id(player->getPlayerInfo()->getId());
cmd->set_target_zone("grave"); cmd->set_target_zone(ZoneNames::GRAVE);
cmd->set_x(0); cmd->set_x(0);
cmd->set_y(0); cmd->set_y(0);
commandList.append(cmd); commandList.append(cmd);
@ -1938,7 +1927,7 @@ void PlayerActions::cardMenuAction()
cmd->set_start_zone(startZone.toStdString()); cmd->set_start_zone(startZone.toStdString());
cmd->mutable_cards_to_move()->CopyFrom(idList); cmd->mutable_cards_to_move()->CopyFrom(idList);
cmd->set_target_player_id(player->getPlayerInfo()->getId()); cmd->set_target_player_id(player->getPlayerInfo()->getId());
cmd->set_target_zone("rfg"); cmd->set_target_zone(ZoneNames::EXILE);
cmd->set_x(0); cmd->set_x(0);
cmd->set_y(0); cmd->set_y(0);
commandList.append(cmd); commandList.append(cmd);

View file

@ -98,7 +98,9 @@ public slots:
void actMoveTopCardToGrave(); void actMoveTopCardToGrave();
void actMoveTopCardToExile(); void actMoveTopCardToExile();
void actMoveTopCardsToGrave(); void actMoveTopCardsToGrave();
void actMoveTopCardsToGraveFaceDown();
void actMoveTopCardsToExile(); void actMoveTopCardsToExile();
void actMoveTopCardsToExileFaceDown();
void actMoveTopCardsUntil(); void actMoveTopCardsUntil();
void actMoveTopCardToBottom(); void actMoveTopCardToBottom();
void actDrawBottomCard(); void actDrawBottomCard();
@ -108,7 +110,9 @@ public slots:
void actMoveBottomCardToGrave(); void actMoveBottomCardToGrave();
void actMoveBottomCardToExile(); void actMoveBottomCardToExile();
void actMoveBottomCardsToGrave(); void actMoveBottomCardsToGrave();
void actMoveBottomCardsToGraveFaceDown();
void actMoveBottomCardsToExile(); void actMoveBottomCardsToExile();
void actMoveBottomCardsToExileFaceDown();
void actMoveBottomCardToTop(); void actMoveBottomCardToTop();
void actSelectAll(); void actSelectAll();
@ -180,6 +184,9 @@ private:
FilterString movingCardsUntilFilter; FilterString movingCardsUntilFilter;
int movingCardsUntilCounter = 0; int movingCardsUntilCounter = 0;
void moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown);
void moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName, bool faceDown);
void createCard(const CardItem *sourceCard, void createCard(const CardItem *sourceCard,
const QString &dbCardName, const QString &dbCardName,
CardRelationType attach = CardRelationType::DoesNotAttach, CardRelationType attach = CardRelationType::DoesNotAttach,

View file

@ -30,6 +30,7 @@
#include <libcockatrice/protocol/pb/event_set_card_counter.pb.h> #include <libcockatrice/protocol/pb/event_set_card_counter.pb.h>
#include <libcockatrice/protocol/pb/event_set_counter.pb.h> #include <libcockatrice/protocol/pb/event_set_counter.pb.h>
#include <libcockatrice/protocol/pb/event_shuffle.pb.h> #include <libcockatrice/protocol/pb/event_shuffle.pb.h>
#include <libcockatrice/utility/zone_names.h>
PlayerEventHandler::PlayerEventHandler(Player *_player) : player(_player) PlayerEventHandler::PlayerEventHandler(Player *_player) : player(_player)
{ {
@ -321,8 +322,8 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv
} }
player->getPlayerMenu()->updateCardMenu(card); player->getPlayerMenu()->updateCardMenu(card);
if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == "deck" && if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == ZoneNames::DECK &&
targetZone->getName() == "stack") { targetZone->getName() == ZoneNames::STACK) {
player->getPlayerActions()->moveOneCardUntil(card); player->getPlayerActions()->moveOneCardUntil(card);
} }
} }

View file

@ -19,7 +19,8 @@ PlayerGraphicsItem::PlayerGraphicsItem(Player *_player) : player(_player)
playerArea = new PlayerArea(this); playerArea = new PlayerArea(this);
playerTarget = new PlayerTarget(player, playerArea); playerTarget = new PlayerTarget(player, playerArea);
qreal avatarMargin = (counterAreaWidth + CARD_HEIGHT + 15 - playerTarget->boundingRect().width()) / 2.0; qreal avatarMargin =
(counterAreaWidth + CardDimensions::HEIGHT_F + 15 - playerTarget->boundingRect().width()) / 2.0;
playerTarget->setPos(QPointF(avatarMargin, avatarMargin)); playerTarget->setPos(QPointF(avatarMargin, avatarMargin));
initializeZones(); initializeZones();
@ -55,8 +56,9 @@ void PlayerGraphicsItem::onPlayerActiveChanged(bool _active)
void PlayerGraphicsItem::initializeZones() void PlayerGraphicsItem::initializeZones()
{ {
deckZoneGraphicsItem = new PileZone(player->getDeckZone(), this); deckZoneGraphicsItem = new PileZone(player->getDeckZone(), this);
auto base = QPointF(counterAreaWidth + (CARD_HEIGHT - CARD_WIDTH + 15) / 2.0, auto base = QPointF(counterAreaWidth + (CardDimensions::HEIGHT_F - CardDimensions::WIDTH_F + 15) / 2.0,
10 + playerTarget->boundingRect().height() + 5 - (CARD_HEIGHT - CARD_WIDTH) / 2.0); 10 + playerTarget->boundingRect().height() + 5 -
(CardDimensions::HEIGHT_F - CardDimensions::WIDTH_F) / 2.0);
deckZoneGraphicsItem->setPos(base); deckZoneGraphicsItem->setPos(base);
qreal h = deckZoneGraphicsItem->boundingRect().width() + 5; qreal h = deckZoneGraphicsItem->boundingRect().width() + 5;
@ -95,7 +97,7 @@ QRectF PlayerGraphicsItem::boundingRect() const
qreal PlayerGraphicsItem::getMinimumWidth() const qreal PlayerGraphicsItem::getMinimumWidth() const
{ {
qreal result = tableZoneGraphicsItem->getMinimumWidth() + CARD_HEIGHT + 15 + counterAreaWidth + qreal result = tableZoneGraphicsItem->getMinimumWidth() + CardDimensions::HEIGHT_F + 15 + counterAreaWidth +
stackZoneGraphicsItem->boundingRect().width(); stackZoneGraphicsItem->boundingRect().width();
if (!SettingsCache::instance().getHorizontalHand()) { if (!SettingsCache::instance().getHorizontalHand()) {
result += handZoneGraphicsItem->boundingRect().width(); result += handZoneGraphicsItem->boundingRect().width();
@ -112,8 +114,8 @@ void PlayerGraphicsItem::paint(QPainter * /*painter*/,
void PlayerGraphicsItem::processSceneSizeChange(int newPlayerWidth) void PlayerGraphicsItem::processSceneSizeChange(int newPlayerWidth)
{ {
// Extend table (and hand, if horizontal) to accommodate the new player width. // Extend table (and hand, if horizontal) to accommodate the new player width.
qreal tableWidth = qreal tableWidth = newPlayerWidth - CardDimensions::HEIGHT_F - 15 - counterAreaWidth -
newPlayerWidth - CARD_HEIGHT - 15 - counterAreaWidth - stackZoneGraphicsItem->boundingRect().width(); stackZoneGraphicsItem->boundingRect().width();
if (!SettingsCache::instance().getHorizontalHand()) { if (!SettingsCache::instance().getHorizontalHand()) {
tableWidth -= handZoneGraphicsItem->boundingRect().width(); tableWidth -= handZoneGraphicsItem->boundingRect().width();
} }
@ -152,7 +154,7 @@ void PlayerGraphicsItem::rearrangeCounters()
void PlayerGraphicsItem::rearrangeZones() void PlayerGraphicsItem::rearrangeZones()
{ {
auto base = QPointF(CARD_HEIGHT + counterAreaWidth + 15, 0); auto base = QPointF(CardDimensions::HEIGHT_F + counterAreaWidth + 15, 0);
if (SettingsCache::instance().getHorizontalHand()) { if (SettingsCache::instance().getHorizontalHand()) {
if (mirrored) { if (mirrored) {
if (player->getHandZone()->contentsKnown()) { if (player->getHandZone()->contentsKnown()) {
@ -203,7 +205,7 @@ void PlayerGraphicsItem::rearrangeZones()
void PlayerGraphicsItem::updateBoundingRect() void PlayerGraphicsItem::updateBoundingRect()
{ {
prepareGeometryChange(); prepareGeometryChange();
qreal width = CARD_HEIGHT + 15 + counterAreaWidth + stackZoneGraphicsItem->boundingRect().width(); qreal width = CardDimensions::HEIGHT_F + 15 + counterAreaWidth + stackZoneGraphicsItem->boundingRect().width();
if (SettingsCache::instance().getHorizontalHand()) { if (SettingsCache::instance().getHorizontalHand()) {
qreal handHeight = qreal handHeight =
player->getPlayerInfo()->getHandVisible() ? handZoneGraphicsItem->boundingRect().height() : 0; player->getPlayerInfo()->getHandVisible() ? handZoneGraphicsItem->boundingRect().height() : 0;
@ -214,7 +216,7 @@ void PlayerGraphicsItem::updateBoundingRect()
0, 0, width + handZoneGraphicsItem->boundingRect().width() + tableZoneGraphicsItem->boundingRect().width(), 0, 0, width + handZoneGraphicsItem->boundingRect().width() + tableZoneGraphicsItem->boundingRect().width(),
tableZoneGraphicsItem->boundingRect().height()); tableZoneGraphicsItem->boundingRect().height());
} }
playerArea->setSize(CARD_HEIGHT + counterAreaWidth + 15, bRect.height()); playerArea->setSize(CardDimensions::HEIGHT_F + counterAreaWidth + 15, bRect.height());
emit sizeChanged(); emit sizeChanged();
} }

View file

@ -0,0 +1,136 @@
/**
* @file z_value_layer_manager.h
* @ingroup GameGraphics
* @brief Semantic Z-value layer management for game scene rendering.
*
* This file provides a structured approach to Z-value allocation in the game scene.
* Z-values in Qt determine stacking order - higher values render on top of lower values.
*
* ## Layer Architecture
*
* The game scene is organized into three conceptual layers:
*
* 1. **Zone Layer (0-999)**: Zone backgrounds, containers, and static elements
* - Zone backgrounds (0.5-1.0)
* - Cards within zones (1.0 base + index)
*
* 2. **Card Layer (1-40,000,000)**: Dynamic card rendering on the table zone
* - Cards use formula: (actualY + CardDimensions::HEIGHT) * 100000 + (actualX + 1) * 100
* - Maximum card Z-value: ~40,000,000 (with 3 rows, actualY <= ~289)
*
* 3. **Overlay Layer (2,000,000,000+)**: UI elements that must appear above all cards
* - Hovered cards (+1)
* - Arrows (+3)
* - Zone views (+4)
* - Drag items (+5, +6)
* - Top UI elements (+7)
*
* ## Design Rationale
*
* The large gap between card Z-values (max ~40M) and overlay base (2B) provides
* safety margin for future table zone expansions while ensuring overlays always
* render above cards regardless of table position.
*
* ## Usage
*
* Prefer using the semantic constants from ZValues namespace:
* @code
* card->setZValue(ZValues::HOVERED_CARD);
* arrow->setZValue(ZValues::ARROWS);
* @endcode
*
* Use validation functions to verify card Z-values during development:
* @code
* Q_ASSERT(ZValueLayerManager::isValidCardZValue(cardZ));
* @endcode
*/
#ifndef Z_VALUE_LAYER_MANAGER_H
#define Z_VALUE_LAYER_MANAGER_H
#include <QtGlobal>
/**
* @namespace ZValueLayerManager
* @brief Utilities for Z-value validation and layer management.
*/
namespace ZValueLayerManager
{
/**
* @enum Layer
* @brief Semantic layer identifiers for Z-value allocation.
*
* These represent conceptual rendering layers, not actual Z-values.
* Use the corresponding ZValues constants for actual rendering.
*/
enum class Layer
{
/// Zone-level elements like backgrounds and containers
Zone,
/// Cards rendered in zones (uses sequential Z-values)
Card,
/// Temporary UI elements like hovered cards and drag items
Overlay
};
/**
* @brief Maximum Z-value a card can have on the table zone.
*
* Based on table zone formula: (actualY + CardDimensions::HEIGHT) * 100000 + (actualX + 1) * 100
* With maximum 3 rows and CardDimensions::HEIGHT = 102, actualY <= ~289.
* Maximum: (289 + 102) * 100000 + 100 * 100 = 39,110,000
*
* We use 40,000,000 as a safe upper bound with margin.
*/
constexpr qreal CARD_Z_VALUE_MAX = 40000000.0;
/**
* @brief Base Z-value for overlay elements.
*
* Must exceed CARD_Z_VALUE_MAX to ensure overlays render above all cards.
* The 50x margin (2B vs 40M) provides safety for future expansion.
*/
constexpr qreal OVERLAY_BASE = 2000000000.0;
/**
* @brief Validates that a Z-value is within the valid card range.
*
* Cards should have Z-values between CARD_BASE (1.0) and CARD_Z_VALUE_MAX.
* Values outside this range may interfere with overlay rendering.
*
* @param zValue The Z-value to validate
* @return true if the Z-value is valid for a card
*/
[[nodiscard]] constexpr bool isValidCardZValue(qreal zValue)
{
return zValue >= 1.0 && zValue <= CARD_Z_VALUE_MAX;
}
/**
* @brief Validates that a Z-value is in the overlay layer.
*
* Overlay elements should have Z-values at or above OVERLAY_BASE.
*
* @param zValue The Z-value to validate
* @return true if the Z-value is valid for an overlay element
*/
[[nodiscard]] constexpr bool isOverlayZValue(qreal zValue)
{
return zValue >= OVERLAY_BASE;
}
/**
* @brief Returns the Z-value for a specific overlay element.
*
* @param offset Offset from OVERLAY_BASE (0-7 for current elements)
* @return The absolute Z-value for the overlay element
*/
[[nodiscard]] constexpr qreal overlayZValue(qreal offset)
{
return OVERLAY_BASE + offset;
}
} // namespace ZValueLayerManager
#endif // Z_VALUE_LAYER_MANAGER_H

View file

@ -0,0 +1,83 @@
#ifndef Z_VALUES_H
#define Z_VALUES_H
#include "card_dimensions.h"
#include "z_value_layer_manager.h"
/**
* @file z_values.h
* @ingroup GameGraphics
* @brief Centralized Z-value constants for rendering layer order.
*
* Z-values in Qt determine stacking order. Higher values render on top.
* These constants define the visual layering hierarchy for the game scene.
*
* ## Layer Architecture
*
* See z_value_layer_manager.h for detailed documentation on the three-layer
* architecture (Zone, Card, Overlay) and the rationale for Z-value choices.
*
* ## Quick Reference
*
* | Layer | Z-Value Range | Purpose |
* |----------|------------------|-----------------------------------|
* | Zone | 0.5 - 1.0 | Zone backgrounds, containers |
* | Card | 1.0 - 40,000,000 | Cards on table (position-based) |
* | Overlay | 2,000,000,000+ | UI elements above all cards |
*/
namespace ZValues
{
// Expose base for callers that need it
constexpr qreal OVERLAY_BASE = ZValueLayerManager::OVERLAY_BASE;
// Overlay layer Z-values for items that should appear above normal cards
constexpr qreal HOVERED_CARD = ZValueLayerManager::overlayZValue(1.0);
constexpr qreal ARROWS = ZValueLayerManager::overlayZValue(3.0);
constexpr qreal ZONE_VIEW_WIDGET = ZValueLayerManager::overlayZValue(4.0);
constexpr qreal DRAG_ITEM = ZValueLayerManager::overlayZValue(5.0);
constexpr qreal DRAG_ITEM_CHILD = ZValueLayerManager::overlayZValue(6.0);
constexpr qreal TOP_UI = ZValueLayerManager::overlayZValue(7.0);
/**
* @brief Compute Z-value for child drag items based on hotspot position.
*
* When dragging multiple cards together, each child card needs a unique Z-value
* to prevent Z-fighting (flickering/flashing). The Z-values are derived from
* their position when grabbed to conserve original stacking. The formula encodes
* 2D coordinates into a single value where X has higher weight, ensuring
* deterministic visual stacking.
*
* @param hotSpotX The X coordinate of the grab position
* @param hotSpotY The Y coordinate of the grab position
* @return Unique Z-value for the child drag item
*/
[[nodiscard]] constexpr qreal childDragZValue(qreal hotSpotX, qreal hotSpotY)
{
return DRAG_ITEM_CHILD + hotSpotX * 1000000 + hotSpotY * 1000 + 1000;
}
/**
* @brief Compute Z-value for cards on the table zone based on position.
*
* Cards lower on the table (higher Y) render above cards higher up,
* and cards to the right (higher X) render above cards to the left.
* This creates natural visual stacking for overlapping cards.
*
* @param x The X coordinate of the card position
* @param y The Y coordinate of the card position
* @return Z-value for the card's table position
*/
[[nodiscard]] constexpr qreal tableCardZValue(qreal x, qreal y)
{
return (y + CardDimensions::HEIGHT_F) * 100000.0 + (x + 1) * 100.0;
}
// Card layering (general architecture, not command-zone specific)
constexpr qreal CARD_BASE = 1.0;
constexpr qreal CARD_MAX = ZValueLayerManager::CARD_Z_VALUE_MAX;
} // namespace ZValues
#endif // Z_VALUES_H

View file

@ -56,7 +56,7 @@ void HandZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
QRectF HandZone::boundingRect() const QRectF HandZone::boundingRect() const
{ {
if (SettingsCache::instance().getHorizontalHand()) if (SettingsCache::instance().getHorizontalHand())
return QRectF(0, 0, width, CARD_HEIGHT + 10); return QRectF(0, 0, width, CardDimensions::HEIGHT_F + 10);
else else
return QRectF(0, 0, 100, zoneHeight); return QRectF(0, 0, 100, zoneHeight);
} }

View file

@ -10,6 +10,7 @@
#include <QDebug> #include <QDebug>
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/protocol/pb/command_move_card.pb.h> #include <libcockatrice/protocol/pb/command_move_card.pb.h>
#include <libcockatrice/utility/zone_names.h>
/** /**
* @param _player the player that the zone belongs to * @param _player the player that the zone belongs to
@ -43,8 +44,10 @@ void CardZoneLogic::addCard(CardItem *card, const bool reorganize, const int x,
for (auto *view : views) { for (auto *view : views) {
if (qobject_cast<ZoneViewZoneLogic *>(view->getLogic())->prepareAddCard(x)) { if (qobject_cast<ZoneViewZoneLogic *>(view->getLogic())->prepareAddCard(x)) {
view->getLogic()->addCard(new CardItem(player, nullptr, card->getCardRef(), card->getId()), reorganize, x, auto copy = new CardItem(player, nullptr, card->getCardRef(), card->getId());
y); copy->setFaceDown(card->getFaceDown());
view->getLogic()->addCard(copy, reorganize, x, y);
} }
} }
@ -172,9 +175,9 @@ void CardZoneLogic::clearContents()
QString CardZoneLogic::getTranslatedName(bool theirOwn, GrammaticalCase gc) const QString CardZoneLogic::getTranslatedName(bool theirOwn, GrammaticalCase gc) const
{ {
QString ownerName = player->getPlayerInfo()->getName(); QString ownerName = player->getPlayerInfo()->getName();
if (name == "hand") if (name == ZoneNames::HAND)
return (theirOwn ? tr("their hand", "nominative") : tr("%1's hand", "nominative").arg(ownerName)); return (theirOwn ? tr("their hand", "nominative") : tr("%1's hand", "nominative").arg(ownerName));
else if (name == "deck") else if (name == ZoneNames::DECK)
switch (gc) { switch (gc) {
case CaseLookAtZone: case CaseLookAtZone:
return (theirOwn ? tr("their library", "look at zone") return (theirOwn ? tr("their library", "look at zone")
@ -190,11 +193,11 @@ QString CardZoneLogic::getTranslatedName(bool theirOwn, GrammaticalCase gc) cons
default: default:
return (theirOwn ? tr("their library", "nominative") : tr("%1's library", "nominative").arg(ownerName)); return (theirOwn ? tr("their library", "nominative") : tr("%1's library", "nominative").arg(ownerName));
} }
else if (name == "grave") else if (name == ZoneNames::GRAVE)
return (theirOwn ? tr("their graveyard", "nominative") : tr("%1's graveyard", "nominative").arg(ownerName)); return (theirOwn ? tr("their graveyard", "nominative") : tr("%1's graveyard", "nominative").arg(ownerName));
else if (name == "rfg") else if (name == ZoneNames::EXILE)
return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName)); return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName));
else if (name == "sb") else if (name == ZoneNames::SIDEBOARD)
switch (gc) { switch (gc) {
case CaseLookAtZone: case CaseLookAtZone:
return (theirOwn ? tr("their sideboard", "look at zone") return (theirOwn ? tr("their sideboard", "look at zone")

View file

@ -19,9 +19,9 @@ PileZone::PileZone(PileZoneLogic *_logic, QGraphicsItem *parent) : CardZone(_log
setCursor(Qt::OpenHandCursor); setCursor(Qt::OpenHandCursor);
setTransform(QTransform() setTransform(QTransform()
.translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2) .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F)
.rotate(90) .rotate(90)
.translate((float)-CARD_WIDTH / 2, (float)-CARD_HEIGHT / 2)); .translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F));
connect(&SettingsCache::instance(), &SettingsCache::roundCardCornersChanged, this, [this](bool _roundCardCorners) { connect(&SettingsCache::instance(), &SettingsCache::roundCardCornersChanged, this, [this](bool _roundCardCorners) {
Q_UNUSED(_roundCardCorners); Q_UNUSED(_roundCardCorners);
@ -33,13 +33,13 @@ PileZone::PileZone(PileZoneLogic *_logic, QGraphicsItem *parent) : CardZone(_log
QRectF PileZone::boundingRect() const QRectF PileZone::boundingRect() const
{ {
return QRectF(0, 0, CARD_WIDTH, CARD_HEIGHT); return QRectF(0, 0, CardDimensions::WIDTH_F, CardDimensions::HEIGHT_F);
} }
QPainterPath PileZone::shape() const QPainterPath PileZone::shape() const
{ {
QPainterPath shape; QPainterPath shape;
qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CARD_WIDTH : 0.0; qreal cardCornerRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * CardDimensions::WIDTH_F : 0.0;
shape.addRoundedRect(boundingRect(), cardCornerRadius, cardCornerRadius); shape.addRoundedRect(boundingRect(), cardCornerRadius, cardCornerRadius);
return shape; return shape;
} }
@ -52,9 +52,9 @@ void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio
getLogic()->getCards().at(0)->paintPicture(painter, getLogic()->getCards().at(0)->getTranslatedSize(painter), getLogic()->getCards().at(0)->paintPicture(painter, getLogic()->getCards().at(0)->getTranslatedSize(painter),
90); 90);
painter->translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2); painter->translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F);
painter->rotate(-90); painter->rotate(-90);
painter->translate((float)-CARD_WIDTH / 2, (float)-CARD_HEIGHT / 2); painter->translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F);
paintNumberEllipse(getLogic()->getCards().size(), 28, Qt::white, -1, -1, painter); paintNumberEllipse(getLogic()->getCards().size(), 28, Qt::white, -1, -1, painter);
} }
@ -68,8 +68,13 @@ void PileZone::handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneL
cmd.set_x(0); cmd.set_x(0);
cmd.set_y(0); cmd.set_y(0);
for (int i = 0; i < dragItems.size(); ++i) for (int i = 0; i < dragItems.size(); ++i) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId()); auto cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(dragItems[i]->getId());
if (dragItems[i]->isForceFaceDown()) {
cardToMove->set_face_down(true);
}
}
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
} }
@ -101,12 +106,12 @@ void PileZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
if (getLogic()->getCards().isEmpty()) if (getLogic()->getCards().isEmpty())
return; return;
bool faceDown = event->modifiers().testFlag(Qt::ShiftModifier); bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier);
bool bottomCard = event->modifiers().testFlag(Qt::ControlModifier); bool bottomCard = event->modifiers().testFlag(Qt::ControlModifier);
CardItem *card = bottomCard ? getLogic()->getCards().last() : getLogic()->getCards().first(); CardItem *card = bottomCard ? getLogic()->getCards().last() : getLogic()->getCards().first();
const int cardid = const int cardid =
getLogic()->contentsKnown() ? card->getId() : (bottomCard ? getLogic()->getCards().size() - 1 : 0); getLogic()->contentsKnown() ? card->getId() : (bottomCard ? getLogic()->getCards().size() - 1 : 0);
CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), faceDown); CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), forceFaceDown);
drag->grabMouse(); drag->grabMouse();
setCursor(Qt::OpenHandCursor); setCursor(Qt::OpenHandCursor);
} }

View file

@ -78,7 +78,11 @@ void StackZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
for (CardDragItem *item : dragItems) { for (CardDragItem *item : dragItems) {
if (item) { if (item) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(item->getId()); auto cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(item->getId());
if (item->isForceFaceDown()) {
cardToMove->set_face_down(true);
}
} }
} }

View file

@ -7,6 +7,7 @@
#include "../board/card_item.h" #include "../board/card_item.h"
#include "../player/player.h" #include "../player/player.h"
#include "../player/player_actions.h" #include "../player/player_actions.h"
#include "../z_values.h"
#include "logic/table_zone_logic.h" #include "logic/table_zone_logic.h"
#include <QGraphicsScene> #include <QGraphicsScene>
@ -14,6 +15,7 @@
#include <libcockatrice/card/card_info.h> #include <libcockatrice/card/card_info.h>
#include <libcockatrice/protocol/pb/command_move_card.pb.h> #include <libcockatrice/protocol/pb/command_move_card.pb.h>
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h> #include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
#include <libcockatrice/utility/zone_names.h>
const QColor TableZone::BACKGROUND_COLOR = QColor(100, 100, 100); const QColor TableZone::BACKGROUND_COLOR = QColor(100, 100, 100);
const QColor TableZone::FADE_MASK = QColor(0, 0, 0, 80); const QColor TableZone::FADE_MASK = QColor(0, 0, 0, 80);
@ -30,7 +32,7 @@ TableZone::TableZone(TableZoneLogic *_logic, QGraphicsItem *parent) : SelectZone
updateBg(); updateBg();
height = MARGIN_TOP + MARGIN_BOTTOM + TABLEROWS * CARD_HEIGHT + (TABLEROWS - 1) * PADDING_Y; height = MARGIN_TOP + MARGIN_BOTTOM + TABLEROWS * CardDimensions::HEIGHT + (TABLEROWS - 1) * PADDING_Y;
width = MIN_WIDTH; width = MIN_WIDTH;
currentMinimumWidth = width; currentMinimumWidth = width;
@ -105,7 +107,7 @@ void TableZone::paintLandDivider(QPainter *painter)
{ {
// Place the line 2 grid heights down then back it off just enough to allow // Place the line 2 grid heights down then back it off just enough to allow
// some space between a 3-card stack and the land area. // some space between a 3-card stack and the land area.
qreal separatorY = MARGIN_TOP + 2 * (CARD_HEIGHT + PADDING_Y) - STACKED_CARD_OFFSET_Y / 2; qreal separatorY = MARGIN_TOP + 2 * (CardDimensions::HEIGHT + PADDING_Y) - STACKED_CARD_OFFSET_Y / 2;
if (isInverted()) if (isInverted())
separatorY = height - separatorY; separatorY = height - separatorY;
painter->setPen(QColor(255, 255, 255, 40)); painter->setPen(QColor(255, 255, 255, 40));
@ -134,8 +136,10 @@ void TableZone::handleDropEventByGrid(const QList<CardDragItem *> &dragItems,
for (const auto &item : dragItems) { for (const auto &item : dragItems) {
CardToMove *ctm = cmd.mutable_cards_to_move()->add_card(); CardToMove *ctm = cmd.mutable_cards_to_move()->add_card();
ctm->set_card_id(item->getId()); ctm->set_card_id(item->getId());
ctm->set_face_down(item->getFaceDown()); if (item->isForceFaceDown()) {
if (startZone->getName() != getLogic()->getName() && !item->getFaceDown()) { ctm->set_face_down(true);
}
if (startZone->getName() != getLogic()->getName() && !item->isForceFaceDown()) {
const auto &card = item->getItem()->getCard(); const auto &card = item->getItem()->getCard();
if (card) { if (card) {
ctm->set_pt(card.getInfo().getPowTough().toStdString()); ctm->set_pt(card.getInfo().getPowTough().toStdString());
@ -167,7 +171,7 @@ void TableZone::reorganizeCards()
actualY += 15; actualY += 15;
getLogic()->getCards()[i]->setPos(actualX, actualY); getLogic()->getCards()[i]->setPos(actualX, actualY);
getLogic()->getCards()[i]->setRealZValue((actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100); getLogic()->getCards()[i]->setRealZValue(ZValues::tableCardZValue(actualX, actualY));
QListIterator<CardItem *> attachedCardIterator(getLogic()->getCards()[i]->getAttachedCards()); QListIterator<CardItem *> attachedCardIterator(getLogic()->getCards()[i]->getAttachedCards());
int j = 0; int j = 0;
@ -177,7 +181,7 @@ void TableZone::reorganizeCards()
qreal childX = actualX - j * STACKED_CARD_OFFSET_X; qreal childX = actualX - j * STACKED_CARD_OFFSET_X;
qreal childY = y + 5; qreal childY = y + 5;
attachedCard->setPos(childX, childY); attachedCard->setPos(childX, childY);
attachedCard->setRealZValue((childY + CARD_HEIGHT) * 100000 + (childX + 1) * 100); attachedCard->setRealZValue(ZValues::tableCardZValue(childX, childY));
} }
} }
@ -192,7 +196,7 @@ void TableZone::toggleTapped()
auto isCardOnTable = [](const QGraphicsItem *item) { auto isCardOnTable = [](const QGraphicsItem *item) {
if (auto card = qgraphicsitem_cast<const CardItem *>(item)) { if (auto card = qgraphicsitem_cast<const CardItem *>(item)) {
return card->getZone()->getName() == "table"; return card->getZone()->getName() == ZoneNames::TABLE;
} }
return false; return false;
}; };
@ -229,7 +233,7 @@ void TableZone::resizeToContents()
// Minimum width is the rightmost card position plus enough room for // Minimum width is the rightmost card position plus enough room for
// another card with padding, then margin. // another card with padding, then margin.
currentMinimumWidth = xMax + (2 * CARD_WIDTH) + PADDING_X + MARGIN_RIGHT; currentMinimumWidth = xMax + (2 * CardDimensions::WIDTH) + PADDING_X + MARGIN_RIGHT;
if (currentMinimumWidth < MIN_WIDTH) if (currentMinimumWidth < MIN_WIDTH)
currentMinimumWidth = MIN_WIDTH; currentMinimumWidth = MIN_WIDTH;
@ -279,10 +283,10 @@ void TableZone::computeCardStackWidths()
const int key = getCardStackMapKey(gridPoint.x() / 3, gridPoint.y()); const int key = getCardStackMapKey(gridPoint.x() / 3, gridPoint.y());
const int stackCount = cardStackCount.value(key, 0); const int stackCount = cardStackCount.value(key, 0);
if (stackCount == 1) if (stackCount == 1)
cardStackWidth.insert(key, CARD_WIDTH + getLogic()->getCards()[i]->getAttachedCards().size() * cardStackWidth.insert(key, CardDimensions::WIDTH + getLogic()->getCards()[i]->getAttachedCards().size() *
STACKED_CARD_OFFSET_X); STACKED_CARD_OFFSET_X);
else else
cardStackWidth.insert(key, CARD_WIDTH + (stackCount - 1) * STACKED_CARD_OFFSET_X); cardStackWidth.insert(key, CardDimensions::WIDTH + (stackCount - 1) * STACKED_CARD_OFFSET_X);
} }
} }
@ -296,7 +300,7 @@ QPointF TableZone::mapFromGrid(QPoint gridPoint) const
// Add in width of card stack plus padding for each column // Add in width of card stack plus padding for each column
for (int i = 0; i < gridPoint.x() / 3; ++i) { for (int i = 0; i < gridPoint.x() / 3; ++i) {
const int key = getCardStackMapKey(i, gridPoint.y()); const int key = getCardStackMapKey(i, gridPoint.y());
x += cardStackWidth.value(key, CARD_WIDTH) + PADDING_X; x += cardStackWidth.value(key, CardDimensions::WIDTH) + PADDING_X;
} }
if (isInverted()) if (isInverted())
@ -307,7 +311,7 @@ QPointF TableZone::mapFromGrid(QPoint gridPoint) const
// Add in card size and padding for each row // Add in card size and padding for each row
for (int i = 0; i < gridPoint.y(); ++i) for (int i = 0; i < gridPoint.y(); ++i)
y += CARD_HEIGHT + PADDING_Y; y += CardDimensions::HEIGHT + PADDING_Y;
return QPointF(x, y); return QPointF(x, y);
} }
@ -321,7 +325,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const
int y = mapPoint.y() - MARGIN_TOP; int y = mapPoint.y() - MARGIN_TOP;
// Below calculation effectively rounds to the nearest grid point. // Below calculation effectively rounds to the nearest grid point.
const int gridPointHeight = CARD_HEIGHT + PADDING_Y; const int gridPointHeight = CardDimensions::HEIGHT + PADDING_Y;
int gridPointY = (y + PADDING_Y / 2) / gridPointHeight; int gridPointY = (y + PADDING_Y / 2) / gridPointHeight;
gridPointY = clampValidTableRow(gridPointY); gridPointY = clampValidTableRow(gridPointY);
@ -337,7 +341,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const
// Maximum value is a card width from the right margin, referenced to the // Maximum value is a card width from the right margin, referenced to the
// grid area. // grid area.
const int xMax = width - MARGIN_LEFT - MARGIN_RIGHT - CARD_WIDTH; const int xMax = width - MARGIN_LEFT - MARGIN_RIGHT - CardDimensions::WIDTH;
int xStack = 0; int xStack = 0;
int xNextStack = 0; int xNextStack = 0;
@ -345,7 +349,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const
while ((xNextStack <= x) && (xNextStack <= xMax)) { while ((xNextStack <= x) && (xNextStack <= xMax)) {
xStack = xNextStack; xStack = xNextStack;
const int key = getCardStackMapKey(nextStackCol, gridPointY); const int key = getCardStackMapKey(nextStackCol, gridPointY);
xNextStack += cardStackWidth.value(key, CARD_WIDTH) + PADDING_X; xNextStack += cardStackWidth.value(key, CardDimensions::WIDTH) + PADDING_X;
nextStackCol++; nextStackCol++;
} }
int stackCol = qMax(nextStackCol - 1, 0); int stackCol = qMax(nextStackCol - 1, 0);

View file

@ -41,12 +41,12 @@ private:
/* /*
Minimum width of the table zone including margins. Minimum width of the table zone including margins.
*/ */
static const int MIN_WIDTH = MARGIN_LEFT + (5 * CARD_WIDTH) + MARGIN_RIGHT; static const int MIN_WIDTH = MARGIN_LEFT + (5 * CardDimensions::WIDTH) + MARGIN_RIGHT;
/* /*
Offset sizes when cards are stacked on each other in the grid Offset sizes when cards are stacked on each other in the grid
*/ */
static const int STACKED_CARD_OFFSET_X = CARD_WIDTH / 3; static const int STACKED_CARD_OFFSET_X = CardDimensions::WIDTH / 3;
static const int STACKED_CARD_OFFSET_Y = PADDING_Y / 3; static const int STACKED_CARD_OFFSET_Y = PADDING_Y / 3;
/* /*

View file

@ -73,7 +73,10 @@ void ZoneViewZone::initializeCards(const QList<const ServerInfo_Card *> &cardLis
for (int i = 0; i < cardList.size(); ++i) { for (int i = 0; i < cardList.size(); ++i) {
auto card = cardList[i]; auto card = cardList[i];
CardRef cardRef = {QString::fromStdString(card->name()), QString::fromStdString(card->provider_id())}; CardRef cardRef = {QString::fromStdString(card->name()), QString::fromStdString(card->provider_id())};
getLogic()->addCard(new CardItem(getLogic()->getPlayer(), this, cardRef, card->id()), false, i); auto copy = new CardItem(getLogic()->getPlayer(), this, cardRef, card->id());
copy->setFaceDown(card->face_down());
getLogic()->addCard(copy, false, i);
} }
reorganizeCards(); reorganizeCards();
} else if (!qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->contentsKnown()) { } else if (!qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->contentsKnown()) {
@ -91,8 +94,10 @@ void ZoneViewZone::initializeCards(const QList<const ServerInfo_Card *> &cardLis
int number = numberCards == -1 ? c.size() : (numberCards < c.size() ? numberCards : c.size()); int number = numberCards == -1 ? c.size() : (numberCards < c.size() ? numberCards : c.size());
for (int i = 0; i < number; i++) { for (int i = 0; i < number; i++) {
CardItem *card = c.at(i); CardItem *card = c.at(i);
getLogic()->addCard(new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId()), false, auto copy = new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId());
i); copy->setFaceDown(card->getFaceDown());
getLogic()->addCard(copy, false, i);
} }
reorganizeCards(); reorganizeCards();
} }
@ -107,12 +112,15 @@ void ZoneViewZone::zoneDumpReceived(const Response &r)
auto cardName = QString::fromStdString(cardInfo.name()); auto cardName = QString::fromStdString(cardInfo.name());
auto cardProviderId = QString::fromStdString(cardInfo.provider_id()); auto cardProviderId = QString::fromStdString(cardInfo.provider_id());
auto card = new CardItem(getLogic()->getPlayer(), this, {cardName, cardProviderId}, cardInfo.id(), getLogic()); auto card = new CardItem(getLogic()->getPlayer(), this, {cardName, cardProviderId}, cardInfo.id(), getLogic());
card->setFaceDown(cardInfo.face_down());
getLogic()->rawInsertCard(card, i); getLogic()->rawInsertCard(card, i);
} }
qobject_cast<ZoneViewZoneLogic *>(getLogic())->updateCardIds(ZoneViewZoneLogic::INITIALIZE); qobject_cast<ZoneViewZoneLogic *>(getLogic())->updateCardIds(ZoneViewZoneLogic::INITIALIZE);
reorganizeCards(); reorganizeCards();
emit getLogic()->cardCountChanged(); // clang-format off
emit getLogic()->cardCountChanged(); // emit keyword causes spurious spacing around ->
// clang-format on
} }
// Because of boundingRect(), this function must not be called before the zone was added to a scene. // Because of boundingRect(), this function must not be called before the zone was added to a scene.
@ -160,8 +168,8 @@ void ZoneViewZone::reorganizeCards()
// determine bounding rect // determine bounding rect
qreal aleft = 0; qreal aleft = 0;
qreal atop = 0; qreal atop = 0;
qreal awidth = gridSize.cols * CARD_WIDTH + (CARD_WIDTH / 2) + HORIZONTAL_PADDING; qreal awidth = gridSize.cols * CardDimensions::WIDTH_F + CardDimensions::WIDTH_HALF_F + HORIZONTAL_PADDING;
qreal aheight = (gridSize.rows * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3; qreal aheight = (gridSize.rows * CardDimensions::HEIGHT_F) / 3 + CardDimensions::HEIGHT_F * 1.3;
optimumRect = QRectF(aleft, atop, awidth, aheight); optimumRect = QRectF(aleft, atop, awidth, aheight);
updateGeometry(); updateGeometry();
@ -204,8 +212,8 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca
} }
lastColumnProp = columnProp; lastColumnProp = columnProp;
qreal x = col * CARD_WIDTH; qreal x = col * CardDimensions::WIDTH_F;
qreal y = row * CARD_HEIGHT / 3; qreal y = row * CardDimensions::HEIGHT_F / 3;
c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y);
c->setRealZValue(i); c->setRealZValue(i);
longestRow = qMax(row, longestRow); longestRow = qMax(row, longestRow);
@ -232,8 +240,8 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca
for (int i = 0; i < cardCount; i++) { for (int i = 0; i < cardCount; i++) {
CardItem *c = cards.at(i); CardItem *c = cards.at(i);
qreal x = (i / rows) * CARD_WIDTH; qreal x = (i / rows) * CardDimensions::WIDTH_F;
qreal y = (i % rows) * CARD_HEIGHT / 3; qreal y = (i % rows) * CardDimensions::HEIGHT_F / 3;
c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y);
c->setRealZValue(i); c->setRealZValue(i);
} }
@ -279,8 +287,13 @@ void ZoneViewZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
cmd.set_y(0); cmd.set_y(0);
cmd.set_is_reversed(qobject_cast<ZoneViewZoneLogic *>(getLogic())->getIsReversed()); cmd.set_is_reversed(qobject_cast<ZoneViewZoneLogic *>(getLogic())->getIsReversed());
for (int i = 0; i < dragItems.size(); ++i) for (int i = 0; i < dragItems.size(); ++i) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId()); auto cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(dragItems[i]->getId());
if (dragItems[i]->isForceFaceDown()) {
cardToMove->set_face_down(true);
}
}
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
} }

View file

@ -7,6 +7,7 @@
#include "../game_scene.h" #include "../game_scene.h"
#include "../player/player.h" #include "../player/player.h"
#include "../player/player_actions.h" #include "../player/player_actions.h"
#include "../z_values.h"
#include "view_zone.h" #include "view_zone.h"
#include <QCheckBox> #include <QCheckBox>
@ -47,7 +48,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
{ {
setAcceptHoverEvents(true); setAcceptHoverEvents(true);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
setZValue(2000000006); setZValue(ZValues::ZONE_VIEW_WIDGET);
setFlag(ItemIgnoresTransformations); setFlag(ItemIgnoresTransformations);
QGraphicsLinearLayout *vbox = new QGraphicsLinearLayout(Qt::Vertical); QGraphicsLinearLayout *vbox = new QGraphicsLinearLayout(Qt::Vertical);
@ -71,7 +72,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
QGraphicsProxyWidget *searchEditProxy = new QGraphicsProxyWidget; QGraphicsProxyWidget *searchEditProxy = new QGraphicsProxyWidget;
searchEditProxy->setWidget(&searchEdit); searchEditProxy->setWidget(&searchEdit);
searchEditProxy->setZValue(2000000007); searchEditProxy->setZValue(ZValues::DRAG_ITEM);
vbox->addItem(searchEditProxy); vbox->addItem(searchEditProxy);
// top row // top row
@ -80,13 +81,13 @@ ZoneViewWidget::ZoneViewWidget(Player *_player,
// groupBy options // groupBy options
QGraphicsProxyWidget *groupBySelectorProxy = new QGraphicsProxyWidget; QGraphicsProxyWidget *groupBySelectorProxy = new QGraphicsProxyWidget;
groupBySelectorProxy->setWidget(&groupBySelector); groupBySelectorProxy->setWidget(&groupBySelector);
groupBySelectorProxy->setZValue(2000000008); groupBySelectorProxy->setZValue(ZValues::TOP_UI);
hTopRow->addItem(groupBySelectorProxy); hTopRow->addItem(groupBySelectorProxy);
// sortBy options // sortBy options
QGraphicsProxyWidget *sortBySelectorProxy = new QGraphicsProxyWidget; QGraphicsProxyWidget *sortBySelectorProxy = new QGraphicsProxyWidget;
sortBySelectorProxy->setWidget(&sortBySelector); sortBySelectorProxy->setWidget(&sortBySelector);
sortBySelectorProxy->setZValue(2000000007); sortBySelectorProxy->setZValue(ZValues::DRAG_ITEM);
hTopRow->addItem(sortBySelectorProxy); hTopRow->addItem(sortBySelectorProxy);
vbox->addItem(hTopRow); vbox->addItem(hTopRow);
@ -457,7 +458,7 @@ void ZoneViewWidget::resizeScrollbar(const qreal newZoneHeight)
*/ */
static qreal rowsToHeight(int rows) static qreal rowsToHeight(int rows)
{ {
const qreal cardsHeight = (rows + 1) * (CARD_HEIGHT / 3); const qreal cardsHeight = (rows + 1) * (CardDimensions::HEIGHT_F / 3);
return cardsHeight + 5; // +5 padding to make the cutoff look nicer return cardsHeight + 5; // +5 padding to make the cutoff look nicer
} }
@ -573,4 +574,4 @@ void ZoneViewWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
if (event->pos().y() <= 0) { if (event->pos().y() <= 0) {
expandWindow(); expandWindow();
} }
} }

View file

@ -17,6 +17,7 @@
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include <libcockatrice/card/database/card_database.h> #include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/import/card_name_normalizer.h>
#include <libcockatrice/deck_list/deck_list.h> #include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h> #include <libcockatrice/deck_list/tree/deck_list_card_node.h>
@ -42,7 +43,7 @@ DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fmt, bo
DeckList deckList; DeckList deckList;
switch (fmt) { switch (fmt) {
case DeckFileFormat::PlainText: case DeckFileFormat::PlainText:
result = deckList.loadFromFile_Plain(&file); result = deckList.loadFromFile_Plain(&file, CardNameNormalizer());
break; break;
case DeckFileFormat::Cockatrice: { case DeckFileFormat::Cockatrice: {
result = deckList.loadFromFile_Native(&file); result = deckList.loadFromFile_Native(&file);
@ -50,7 +51,7 @@ DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fmt, bo
qCInfo(DeckLoaderLog) << "Failed to load " << fileName qCInfo(DeckLoaderLog) << "Failed to load " << fileName
<< "as cockatrice format; retrying as plain format"; << "as cockatrice format; retrying as plain format";
file.seek(0); file.seek(0);
result = deckList.loadFromFile_Plain(&file); result = deckList.loadFromFile_Plain(&file, CardNameNormalizer());
fmt = DeckFileFormat::PlainText; fmt = DeckFileFormat::PlainText;
} }
break; break;

View file

@ -12,6 +12,7 @@
#include <QPixmapCache> #include <QPixmapCache>
#include <QStandardPaths> #include <QStandardPaths>
#include <QString> #include <QString>
#include <QStyle>
#include <QStyleFactory> #include <QStyleFactory>
#include <QStyleHints> #include <QStyleHints>
#include <Qt> #include <Qt>
@ -89,6 +90,7 @@ struct PaletteColorInfo
ThemeManager::ThemeManager(QObject *parent) : QObject(parent) ThemeManager::ThemeManager(QObject *parent) : QObject(parent)
{ {
defaultStyleName = qApp->style()->objectName();
ensureThemeDirectoryExists(); ensureThemeDirectoryExists();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, &ThemeManager::themeChangedSlot); connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, &ThemeManager::themeChangedSlot);
@ -346,7 +348,7 @@ void ThemeManager::themeChangedSlot()
qApp->setStyle(QStyleFactory::create("Fusion")); qApp->setStyle(QStyleFactory::create("Fusion"));
qApp->setPalette(createDarkGreenFusionPalette()); qApp->setPalette(createDarkGreenFusionPalette());
} else { } else {
qApp->setStyle(""); qApp->setStyle(defaultStyleName); // setting the style also sets the palette
} }
if (dirPath.isEmpty()) { if (dirPath.isEmpty()) {

View file

@ -40,6 +40,7 @@ public:
}; };
private: private:
QString defaultStyleName;
std::array<QBrush, Role::MaxRole + 1> brushes; std::array<QBrush, Role::MaxRole + 1> brushes;
QStringMap availableThemes; QStringMap availableThemes;
/* /*

View file

@ -10,7 +10,7 @@
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
CardInfoDisplayWidget::CardInfoDisplayWidget(const CardRef &cardRef, QWidget *parent, Qt::WindowFlags flags) CardInfoDisplayWidget::CardInfoDisplayWidget(const CardRef &cardRef, QWidget *parent, Qt::WindowFlags flags)
: QFrame(parent, flags), aspectRatio((qreal)CARD_HEIGHT / (qreal)CARD_WIDTH) : QFrame(parent, flags), aspectRatio(CardDimensions::HEIGHT_F / CardDimensions::WIDTH_F)
{ {
setContentsMargins(3, 3, 3, 3); setContentsMargins(3, 3, 3, 3);
pic = new CardInfoPictureWidget(); pic = new CardInfoPictureWidget();

View file

@ -8,7 +8,7 @@ CardInfoPictureArtCropWidget::CardInfoPictureArtCropWidget(QWidget *parent)
hide(); hide();
} }
QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &targetSize) QPixmap CardInfoPictureArtCropWidget::getBackground()
{ {
// Load the full-resolution card image, not a pre-scaled one // Load the full-resolution card image, not a pre-scaled one
QPixmap fullResPixmap; QPixmap fullResPixmap;
@ -17,10 +17,8 @@ QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &target
} else { } else {
CardPictureLoader::getCardBackPixmap(fullResPixmap, QSize(745, 1040)); CardPictureLoader::getCardBackPixmap(fullResPixmap, QSize(745, 1040));
} }
// Fail-safe if loading failed
if (fullResPixmap.isNull()) { if (fullResPixmap.isNull()) {
return QPixmap(targetSize); return QPixmap(); // return null qpixmap
} }
const QSize sz = fullResPixmap.size(); const QSize sz = fullResPixmap.size();
@ -33,9 +31,7 @@ QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &target
foilRect = foilRect.intersected(fullResPixmap.rect()); // always clamp to source bounds foilRect = foilRect.intersected(fullResPixmap.rect()); // always clamp to source bounds
// Crop first, then scale for best quality // return full resolution image crop
QPixmap cropped = fullResPixmap.copy(foilRect); QPixmap cropped = fullResPixmap.copy(foilRect);
QPixmap scaled = cropped.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); return cropped;
return scaled;
} }

View file

@ -16,8 +16,8 @@ class CardInfoPictureArtCropWidget : public CardInfoPictureWidget
public: public:
explicit CardInfoPictureArtCropWidget(QWidget *parent = nullptr); explicit CardInfoPictureArtCropWidget(QWidget *parent = nullptr);
// Returns a processed (cropped & scaled) version of the pixmap // Returns a cropped version of the pixmap
QPixmap getProcessedBackground(const QSize &targetSize); QPixmap getBackground();
}; };
#endif // CARD_INFO_PICTURE_ART_CROP_WIDGET_H #endif // CARD_INFO_PICTURE_ART_CROP_WIDGET_H

View file

@ -320,6 +320,7 @@ void DeckStateManager::undo(int steps)
deckListModel->rebuildTree(); deckListModel->rebuildTree();
emit deckListModel->layoutChanged(); emit deckListModel->layoutChanged();
emit deckReplaced();
} }
void DeckStateManager::redo(int steps) void DeckStateManager::redo(int steps)
@ -338,6 +339,7 @@ void DeckStateManager::redo(int steps)
deckListModel->rebuildTree(); deckListModel->rebuildTree();
emit deckListModel->layoutChanged(); emit deckListModel->layoutChanged();
emit deckReplaced();
} }
void DeckStateManager::requestHistorySave(const QString &reason) void DeckStateManager::requestHistorySave(const QString &reason)

View file

@ -14,6 +14,7 @@
#include <QPushButton> #include <QPushButton>
#include <QTextStream> #include <QTextStream>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <libcockatrice/card/import/card_name_normalizer.h>
/** /**
* Creates the main layout and connects the signals that are common to all versions of this window * Creates the main layout and connects the signals that are common to all versions of this window
@ -81,7 +82,7 @@ bool AbstractDlgDeckTextEdit::loadIntoDeck(DeckList &deckList) const
QTextStream stream(&buffer); QTextStream stream(&buffer);
if (deckList.loadFromStream_Plain(stream, true)) { if (deckList.loadFromStream_Plain(stream, true, CardNameNormalizer())) {
if (loadSetNameAndNumberCheckBox->isChecked()) { if (loadSetNameAndNumberCheckBox->isChecked()) {
deckList.forEachCard(CardNodeFunction::ResolveProviderId()); deckList.forEachCard(CardNodeFunction::ResolveProviderId());
} else { } else {

View file

@ -8,6 +8,7 @@
#include <QJsonObject> #include <QJsonObject>
#include <QMessageBox> #include <QMessageBox>
#include <QNetworkReply> #include <QNetworkReply>
#include <libcockatrice/card/import/card_name_normalizer.h>
#include <version_string.h> #include <version_string.h>
DlgLoadDeckFromWebsite::DlgLoadDeckFromWebsite(QWidget *parent) : QDialog(parent) DlgLoadDeckFromWebsite::DlgLoadDeckFromWebsite(QWidget *parent) : QDialog(parent)
@ -99,7 +100,7 @@ void DlgLoadDeckFromWebsite::accept()
// Parse the plain text deck here // Parse the plain text deck here
DeckList deckList; DeckList deckList;
QTextStream stream(&deckText); QTextStream stream(&deckText);
deckList.loadFromStream_Plain(stream, false); deckList.loadFromStream_Plain(stream, false, CardNameNormalizer());
deckList.forEachCard(CardNodeFunction::ResolveProviderId()); deckList.forEachCard(CardNodeFunction::ResolveProviderId());
deck = deckList; deck = deckList;

View file

@ -14,6 +14,7 @@
#include <QDialog> #include <QDialog>
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QLoggingCategory>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QVBoxLayout> #include <QVBoxLayout>

View file

@ -46,6 +46,8 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor)
&HomeWidget::onBackgroundShuffleFrequencyChanged); &HomeWidget::onBackgroundShuffleFrequencyChanged);
// Lambda is cleaner to read than overloading this // Lambda is cleaner to read than overloading this
connect(&SettingsCache::instance(), &SettingsCache::homeTabDisplayCardNameChanged, this, [this] { repaint(); }); connect(&SettingsCache::instance(), &SettingsCache::homeTabDisplayCardNameChanged, this, [this] { repaint(); });
connect(&SettingsCache::instance(), &SettingsCache::themeChanged, this,
&HomeWidget::updateButtonsToBackgroundColor);
} }
void HomeWidget::initializeBackgroundFromSource() void HomeWidget::initializeBackgroundFromSource()
@ -58,20 +60,24 @@ void HomeWidget::initializeBackgroundFromSource()
auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource()); auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
cardChangeTimer->stop();
switch (backgroundSourceType) { switch (backgroundSourceType) {
case BackgroundSources::Theme: case BackgroundSources::Theme:
cardChangeTimer->stop();
background = QPixmap("theme:backgrounds/home"); background = QPixmap("theme:backgrounds/home");
backgroundSourceDeck = DeckList();
backgroundSourceCard->setCard(ExactCard());
updateButtonsToBackgroundColor(); updateButtonsToBackgroundColor();
update(); update();
break; break;
case BackgroundSources::RandomCardArt: case BackgroundSources::RandomCardArt:
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); backgroundSourceDeck = DeckList();
updateRandomCard();
onBackgroundShuffleFrequencyChanged();
break; break;
case BackgroundSources::DeckFileArt: case BackgroundSources::DeckFileArt:
loadBackgroundSourceDeck(); loadBackgroundSourceDeck();
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); updateRandomCard();
onBackgroundShuffleFrequencyChanged();
break; break;
} }
} }
@ -83,6 +89,20 @@ void HomeWidget::loadBackgroundSourceDeck()
backgroundSourceDeck = deckOpt.has_value() ? deckOpt.value().deckList : DeckList(); backgroundSourceDeck = deckOpt.has_value() ? deckOpt.value().deckList : DeckList();
} }
void HomeWidget::setRandomCard(ExactCard &newCard)
{
static constexpr int ATTEMPTS = 10;
for (int i = 0; i < ATTEMPTS; ++i) {
ExactCard tmpCard = CardDatabaseManager::query()->getRandomCard();
if (tmpCard != backgroundSourceCard->getCard() && tmpCard.getCardPtr()->getProperty("layout") == "normal" &&
tmpCard.getPrinting().getSet() != nullptr) {
newCard = tmpCard;
return;
}
}
qWarning() << "failed to set random card image after" << ATTEMPTS << "attempts";
}
void HomeWidget::updateRandomCard() void HomeWidget::updateRandomCard()
{ {
auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource()); auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
@ -93,10 +113,7 @@ void HomeWidget::updateRandomCard()
case BackgroundSources::Theme: case BackgroundSources::Theme:
break; break;
case BackgroundSources::RandomCardArt: case BackgroundSources::RandomCardArt:
do { setRandomCard(newCard);
newCard = CardDatabaseManager::query()->getRandomCard();
} while (newCard == backgroundSourceCard->getCard() &&
newCard.getCardPtr()->getProperty("layout") != "normal");
break; break;
case BackgroundSources::DeckFileArt: case BackgroundSources::DeckFileArt:
QList<CardRef> cardRefs = backgroundSourceDeck.getCardRefList(); QList<CardRef> cardRefs = backgroundSourceDeck.getCardRefList();
@ -124,25 +141,20 @@ void HomeWidget::updateRandomCard()
connect(newCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &HomeWidget::updateBackgroundProperties); connect(newCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &HomeWidget::updateBackgroundProperties);
backgroundSourceCard->setCard(newCard); backgroundSourceCard->setCard(newCard);
background = backgroundSourceCard->getProcessedBackground(size()); background = backgroundSourceCard->getBackground();
if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() <= 0) {
cardChangeTimer->stop();
}
} }
void HomeWidget::onBackgroundShuffleFrequencyChanged() void HomeWidget::onBackgroundShuffleFrequencyChanged()
{ {
cardChangeTimer->stop(); cardChangeTimer->stop();
if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() <= 0) { if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() > 0) {
return; cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
} }
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
} }
void HomeWidget::updateBackgroundProperties() void HomeWidget::updateBackgroundProperties()
{ {
background = backgroundSourceCard->getProcessedBackground(size()); background = backgroundSourceCard->getBackground();
updateButtonsToBackgroundColor(); updateButtonsToBackgroundColor();
update(); // Triggers repaint update(); // Triggers repaint
} }
@ -300,14 +312,17 @@ void HomeWidget::paintEvent(QPaintEvent *event)
QPainter painter(this); QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::Antialiasing);
background = background.scaled(size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); if (!background.isNull()) {
QSize widgetSize = size() * devicePixelRatio();
QPixmap toDraw = background.scaled(widgetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
// Draw already-scaled background centered // Draw scaled background centered
QSize widgetSize = size(); QSize bgSize = toDraw.size();
QSize bgSize = background.size(); QPoint topLeft((widgetSize.width() - bgSize.width()) / (devicePixelRatio() * 2), // undo scaling for painter
QPoint topLeft((widgetSize.width() - bgSize.width()) / 2, (widgetSize.height() - bgSize.height()) / 2); (widgetSize.height() - bgSize.height()) / (devicePixelRatio() * 2));
painter.drawPixmap(topLeft, background); painter.drawPixmap(topLeft, toDraw);
}
// Draw translucent black overlay with rounded corners // Draw translucent black overlay with rounded corners
QRectF overlayRect(5, 5, width() - 10, height() - 10); QRectF overlayRect(5, 5, width() - 10, height() - 10);
@ -321,8 +336,11 @@ void HomeWidget::paintEvent(QPaintEvent *event)
QString cardName; QString cardName;
ExactCard card = backgroundSourceCard->getCard(); ExactCard card = backgroundSourceCard->getCard();
if (card) { if (card) {
cardName = card.getCardPtr()->getName() + " (" + card.getPrinting().getSet()->getCorrectedShortName() + ") " + cardName = card.getCardPtr()->getName();
card.getPrinting().getProperty("num"); if (card.getPrinting().getSet() != nullptr) {
cardName += " (" + card.getPrinting().getSet()->getCorrectedShortName() + ") " +
card.getPrinting().getProperty("num");
}
} }
if (!cardName.isEmpty() && SettingsCache::instance().getHomeTabDisplayCardName()) { if (!cardName.isEmpty() && SettingsCache::instance().getHomeTabDisplayCardName()) {

View file

@ -45,6 +45,7 @@ private:
QPair<QColor, QColor> gradientColors; QPair<QColor, QColor> gradientColors;
HomeStyledButton *connectButton; HomeStyledButton *connectButton;
void setRandomCard(ExactCard &newCard);
void loadBackgroundSourceDeck(); void loadBackgroundSourceDeck();
}; };

View file

@ -2,7 +2,6 @@
#include "../../../../../deck_loader/card_node_function.h" #include "../../../../../deck_loader/card_node_function.h"
#include "../../../../../deck_loader/deck_loader.h" #include "../../../../../deck_loader/deck_loader.h"
#include "../../../../cards/card_info_picture_with_text_overlay_widget.h"
#include "../../../../cards/card_size_widget.h" #include "../../../../cards/card_size_widget.h"
#include "../../../../cards/deck_card_zone_display_widget.h" #include "../../../../cards/deck_card_zone_display_widget.h"
#include "../../../../visual_deck_editor/visual_deck_display_options_widget.h" #include "../../../../visual_deck_editor/visual_deck_display_options_widget.h"
@ -10,7 +9,7 @@
#include "../api_response/deck/archidekt_api_response_deck.h" #include "../api_response/deck/archidekt_api_response_deck.h"
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/import/card_name_normalizer.h>
ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWidget *parent, ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWidget *parent,
ArchidektApiResponseDeck _response, ArchidektApiResponseDeck _response,
@ -80,7 +79,7 @@ ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWi
connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset); connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset);
auto decklist = QSharedPointer<DeckList>(new DeckList); auto decklist = QSharedPointer<DeckList>(new DeckList);
decklist->loadFromStream_Plain(deckStream, false); decklist->loadFromStream_Plain(deckStream, false, CardNameNormalizer());
model->setDeckList(decklist); model->setDeckList(decklist);
model->forEachCard(CardNodeFunction::ResolveProviderId()); model->forEachCard(CardNodeFunction::ResolveProviderId());

View file

@ -1,11 +1,10 @@
#include "edhrec_deck_api_response.h" #include "edhrec_deck_api_response.h"
#include "../../../../../../deck_loader/deck_loader.h"
#include <QApplication> #include <QApplication>
#include <QDebug> #include <QDebug>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>
#include <libcockatrice/card/import/card_name_normalizer.h>
void EdhrecDeckApiResponse::fromJson(const QJsonArray &json) void EdhrecDeckApiResponse::fromJson(const QJsonArray &json)
{ {
@ -15,7 +14,7 @@ void EdhrecDeckApiResponse::fromJson(const QJsonArray &json)
} }
QTextStream stream(&deckList); QTextStream stream(&deckList);
deck.loadFromStream_Plain(stream, true); deck.loadFromStream_Plain(stream, true, CardNameNormalizer());
} }
void EdhrecDeckApiResponse::debugPrint() const void EdhrecDeckApiResponse::debugPrint() const

View file

@ -12,6 +12,7 @@ set(HEADERS
libcockatrice/card/database/parser/card_database_parser.h libcockatrice/card/database/parser/card_database_parser.h
libcockatrice/card/database/parser/cockatrice_xml_3.h libcockatrice/card/database/parser/cockatrice_xml_3.h
libcockatrice/card/database/parser/cockatrice_xml_4.h libcockatrice/card/database/parser/cockatrice_xml_4.h
libcockatrice/card/import/card_name_normalizer.h
libcockatrice/card/printing/exact_card.h libcockatrice/card/printing/exact_card.h
libcockatrice/card/printing/printing_info.h libcockatrice/card/printing/printing_info.h
libcockatrice/card/set/card_set.h libcockatrice/card/set/card_set.h
@ -36,6 +37,7 @@ add_library(
libcockatrice/card/database/parser/card_database_parser.cpp libcockatrice/card/database/parser/card_database_parser.cpp
libcockatrice/card/database/parser/cockatrice_xml_3.cpp libcockatrice/card/database/parser/cockatrice_xml_3.cpp
libcockatrice/card/database/parser/cockatrice_xml_4.cpp libcockatrice/card/database/parser/cockatrice_xml_4.cpp
libcockatrice/card/import/card_name_normalizer.cpp
libcockatrice/card/printing/exact_card.cpp libcockatrice/card/printing/exact_card.cpp
libcockatrice/card/printing/printing_info.cpp libcockatrice/card/printing/printing_info.cpp
libcockatrice/card/relation/card_relation.cpp libcockatrice/card/relation/card_relation.cpp

View file

@ -0,0 +1,66 @@
#include "card_name_normalizer.h"
#include "../database/card_database_manager.h"
#include "../printing/exact_card.h"
#include <QRegularExpression>
/**
* @brief Resolves the complete display name of a card.
* @param cardName Base name.
* @return Full display name, or the cardName unchanged if a display name is not found.
*/
static QString getCompleteCardName(const QString &cardName)
{
ExactCard temp = CardDatabaseManager::query()->guessCard({cardName});
if (temp) {
return temp.getName();
}
return cardName;
}
QString CardNameNormalizer::operator()(const QString &cardNameString) const
{
QString cardName = cardNameString;
// Regex for advanced card parsing
static const QRegularExpression reSplitCard(R"( ?\/\/ ?)");
static const QRegularExpression reBrace(R"( ?[\[\{][^\]\}]*[\]\}] ?)"); // not nested
static const QRegularExpression reRoundBrace(R"(^\([^\)]*\) ?)"); // () are only matched at start of string
static const QRegularExpression reDigitBrace(R"( ?\(\d*\) ?)"); // () are matched if containing digits
static const QRegularExpression reBraceDigit(
R"( ?\([\dA-Z]+\) *\d+$)"); // () are matched if containing setcode then a number
static const QRegularExpression reDoubleFacedMarker(R"( ?\(Transform\) ?)");
static const QHash<QRegularExpression, QString> differences{{QRegularExpression(""), "'"},
{QRegularExpression("Æ"), "Ae"},
{QRegularExpression("æ"), "ae"},
{QRegularExpression(" ?[|/]+ ?"), " // "}};
// Handle advanced card types
if (cardName.contains(reSplitCard)) {
cardName = cardName.split(reSplitCard).join(" // ");
}
if (cardName.contains(reDoubleFacedMarker)) {
QStringList faces = cardName.split(reDoubleFacedMarker);
cardName = faces.first().trimmed();
}
// Remove unnecessary characters
cardName.remove(reBrace);
cardName.remove(reRoundBrace); // I'll be entirely honest here, these are split to accommodate just three cards
cardName.remove(reDigitBrace); // from un-sets that have a word in between round braces at the end
cardName.remove(reBraceDigit); // very specific format with the set code in () and collectors number after
// Normalize characters
for (auto diff = differences.constBegin(); diff != differences.constEnd(); ++diff) {
cardName.replace(diff.key(), diff.value());
}
// Resolve complete card name
cardName = getCompleteCardName(cardName);
return cardName;
}

View file

@ -0,0 +1,15 @@
#ifndef COCKATRICE_CARD_NAME_NORMALIZER_H
#define COCKATRICE_CARD_NAME_NORMALIZER_H
#include <QString>
/**
* Functor that normalizes the raw card name parsed during a plaintext deck import into the card name that Cockatrice
* uses.
*/
struct CardNameNormalizer
{
QString operator()(const QString &cardNameString) const;
};
#endif // COCKATRICE_CARD_NAME_NORMALIZER_H

View file

@ -199,9 +199,12 @@ bool DeckList::saveToFile_Native(QIODevice *device) const
* *
* @param in The text to load * @param in The text to load
* @param preserveMetadata If true, don't clear the existing metadata * @param preserveMetadata If true, don't clear the existing metadata
* @param cardNameNormalizer Function that takes the parsed card name string in the text and
* @return False if the input was empty, true otherwise. * @return False if the input was empty, true otherwise.
*/ */
bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) bool DeckList::loadFromStream_Plain(QTextStream &in,
bool preserveMetadata,
const std::function<QString(const QString &)> &cardNameNormalizer)
{ {
const QRegularExpression reCardLine(R"(^\s*[\w\[\(\{].*$)", QRegularExpression::UseUnicodePropertiesOption); const QRegularExpression reCardLine(R"(^\s*[\w\[\(\{].*$)", QRegularExpression::UseUnicodePropertiesOption);
const QRegularExpression reEmpty("^\\s*$"); const QRegularExpression reEmpty("^\\s*$");
@ -213,23 +216,11 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata)
// Regex for advanced card parsing // Regex for advanced card parsing
const QRegularExpression reMultiplier(R"(^[xX\(\[]*(\d+)[xX\*\)\]]* ?(.+))"); const QRegularExpression reMultiplier(R"(^[xX\(\[]*(\d+)[xX\*\)\]]* ?(.+))");
const QRegularExpression reSplitCard(R"( ?\/\/ ?)");
const QRegularExpression reBrace(R"( ?[\[\{][^\]\}]*[\]\}] ?)"); // not nested
const QRegularExpression reRoundBrace(R"(^\([^\)]*\) ?)"); // () are only matched at start of string
const QRegularExpression reDigitBrace(R"( ?\(\d*\) ?)"); // () are matched if containing digits
const QRegularExpression reBraceDigit(
R"( ?\([\dA-Z]+\) *\d+$)"); // () are matched if containing setcode then a number
const QRegularExpression reDoubleFacedMarker(R"( ?\(Transform\) ?)");
// Regex for extracting set code and collector number with attached symbols // Regex for extracting set code and collector number with attached symbols
const QRegularExpression reHyphenFormat(R"(\((\w{3,})\)\s+(\w{3,})-(\d+[^\w\s]*))"); const QRegularExpression reHyphenFormat(R"(\((\w{3,})\)\s+(\w{3,})-(\d+[^\w\s]*))");
const QRegularExpression reRegularFormat(R"(\((\w{3,})\)\s+(\d+[^\w\s]*))"); const QRegularExpression reRegularFormat(R"(\((\w{3,})\)\s+(\d+[^\w\s]*))");
const QHash<QRegularExpression, QString> differences{{QRegularExpression(""), QString("'")},
{QRegularExpression("Æ"), QString("Ae")},
{QRegularExpression("æ"), QString("ae")},
{QRegularExpression(" ?[|/]+ ?"), QString(" // ")}};
cleanList(preserveMetadata); cleanList(preserveMetadata);
auto inputs = in.readAll().trimmed().split('\n'); auto inputs = in.readAll().trimmed().split('\n');
@ -355,26 +346,8 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata)
cardName = match.captured(2); cardName = match.captured(2);
} }
// Handle advanced card types // Normalize the card name
if (cardName.contains(reSplitCard)) { cardName = cardNameNormalizer(cardName);
cardName = cardName.split(reSplitCard).join(" // ");
}
if (cardName.contains(reDoubleFacedMarker)) {
QStringList faces = cardName.split(reDoubleFacedMarker);
cardName = faces.first().trimmed();
}
// Remove unnecessary characters
cardName.remove(reBrace);
cardName.remove(reRoundBrace); // I'll be entirely honest here, these are split to accommodate just three cards
cardName.remove(reDigitBrace); // from un-sets that have a word in between round braces at the end
cardName.remove(reBraceDigit); // very specific format with the set code in () and collectors number after
// Normalize names
for (auto diff = differences.constBegin(); diff != differences.constEnd(); ++diff) {
cardName.replace(diff.key(), diff.value());
}
// Determine the zone (mainboard/sideboard) // Determine the zone (mainboard/sideboard)
QString zoneName = sideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; QString zoneName = sideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN;
@ -387,10 +360,10 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata)
return true; return true;
} }
bool DeckList::loadFromFile_Plain(QIODevice *device) bool DeckList::loadFromFile_Plain(QIODevice *device, const std::function<QString(const QString &)> &cardNameNormalizer)
{ {
QTextStream in(device); QTextStream in(device);
return loadFromStream_Plain(in, false); return loadFromStream_Plain(in, false, cardNameNormalizer);
} }
bool DeckList::saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards) const bool DeckList::saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards) const

View file

@ -206,8 +206,10 @@ public:
/// @name Serialization (Plain text) /// @name Serialization (Plain text)
///@{ ///@{
bool loadFromStream_Plain(QTextStream &stream, bool preserveMetadata); bool loadFromStream_Plain(QTextStream &stream,
bool loadFromFile_Plain(QIODevice *device); bool preserveMetadata,
const std::function<QString(const QString &)> &cardNameNormalizer);
bool loadFromFile_Plain(QIODevice *device, const std::function<QString(const QString &)> &cardNameNormalizer);
bool saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards) const; bool saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards) const;
bool bool
saveToFile_Plain(QIODevice *device, bool prefixSideboardCards = true, bool slashTappedOutSplitCards = false) const; saveToFile_Plain(QIODevice *device, bool prefixSideboardCards = true, bool slashTappedOutSplitCards = false) const;

View file

@ -85,6 +85,8 @@ void DeckListModel::rebuildTree()
} }
endResetModel(); endResetModel();
refreshCardFormatLegalities();
} }
int DeckListModel::rowCount(const QModelIndex &parent) const int DeckListModel::rowCount(const QModelIndex &parent) const
@ -649,7 +651,6 @@ void DeckListModel::setDeckList(const QSharedPointer<DeckList> &_deck)
deckList = _deck; deckList = _deck;
} }
rebuildTree(); rebuildTree();
refreshCardFormatLegalities();
emit deckReplaced(); emit deckReplaced();
} }

View file

@ -48,6 +48,7 @@
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h> #include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#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>
Server_AbstractPlayer::Server_AbstractPlayer(Server_Game *_game, Server_AbstractPlayer::Server_AbstractPlayer(Server_Game *_game,
int _playerId, int _playerId,
@ -190,14 +191,43 @@ shouldDestroyOnMove(const Server_Card *card, const Server_CardZone *startZone, c
} }
// Allow tokens on the stack // Allow tokens on the stack
if ((startZone->getName() == "table" || startZone->getName() == "stack") && if ((startZone->getName() == ZoneNames::TABLE || startZone->getName() == ZoneNames::STACK) &&
(targetZone->getName() == "table" || targetZone->getName() == "stack")) { (targetZone->getName() == ZoneNames::TABLE || targetZone->getName() == ZoneNames::STACK)) {
return false; return false;
} }
return true; return true;
} }
/**
* @brief Determines whether the moved card should be face-down
*/
static bool
shouldBeFaceDown(const MoveCardStruct &cardStruct, const Server_CardZone *startZone, const Server_CardZone *targetZone)
{
if (!targetZone) {
return false;
}
// being face-down only makes sense for public zones
if (targetZone->getType() != ServerInfo_Zone::PublicZone) {
return false;
}
// face-down property in proto takes precedence
if (cardStruct.cardToMove->has_face_down()) {
return cardStruct.cardToMove->face_down();
}
// Default to keep face-down the same if zone didn't change.
// Compare using zone names because face-down is maintained when changing controllers.
if (startZone && startZone->getName() == targetZone->getName()) {
return cardStruct.card->getFaceDown();
}
return false;
}
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,
@ -235,7 +265,7 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges,
} }
// do not allow attached cards to move around on the table // do not allow attached cards to move around on the table
if (card->getParentCard() && targetzone->getName() == "table") { if (card->getParentCard() && targetzone->getName() == ZoneNames::TABLE) {
continue; continue;
} }
@ -257,10 +287,7 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges,
for (auto cardStruct : cardsToMove) { for (auto cardStruct : cardsToMove) {
Server_Card *card = cardStruct.card; Server_Card *card = cardStruct.card;
const CardToMove *thisCardProperties = cardStruct.cardToMove;
int originalPosition = cardStruct.position; int originalPosition = cardStruct.position;
bool faceDown = targetzone->hasCoords() &&
(thisCardProperties->has_face_down() ? thisCardProperties->face_down() : card->getFaceDown());
bool sourceBeingLookedAt; bool sourceBeingLookedAt;
int position = startzone->removeCard(card, sourceBeingLookedAt); int position = startzone->removeCard(card, sourceBeingLookedAt);
@ -315,11 +342,13 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges,
++xIndex; ++xIndex;
int newX = isReversed ? targetzone->getCards().size() - xCoord + xIndex : xCoord + xIndex; int newX = isReversed ? targetzone->getCards().size() - xCoord + xIndex : xCoord + xIndex;
bool faceDown = shouldBeFaceDown(cardStruct, startzone, targetzone);
if (targetzone->hasCoords()) { if (targetzone->hasCoords()) {
newX = targetzone->getFreeGridColumn(newX, yCoord, card->getName(), faceDown); newX = targetzone->getFreeGridColumn(newX, yCoord, card->getName(), faceDown);
} else { } else {
yCoord = 0; yCoord = 0;
card->resetState(targetzone->getName() == "stack"); card->resetState(targetzone->getName() == ZoneNames::STACK);
} }
targetzone->insertCard(card, newX, yCoord); targetzone->insertCard(card, newX, yCoord);

View file

@ -51,6 +51,7 @@
#include <libcockatrice/protocol/pb/event_set_active_phase.pb.h> #include <libcockatrice/protocol/pb/event_set_active_phase.pb.h>
#include <libcockatrice/protocol/pb/event_set_active_player.pb.h> #include <libcockatrice/protocol/pb/event_set_active_player.pb.h>
#include <libcockatrice/protocol/pb/game_replay.pb.h> #include <libcockatrice/protocol/pb/game_replay.pb.h>
#include <libcockatrice/utility/zone_names.h>
Server_Game::Server_Game(const ServerInfo_User &_creatorInfo, Server_Game::Server_Game(const ServerInfo_User &_creatorInfo,
int _gameId, int _gameId,
@ -832,7 +833,7 @@ void Server_Game::returnCardsFromPlayer(GameEventStorage &ges, Server_AbstractPl
QMutexLocker locker(&gameMutex); QMutexLocker locker(&gameMutex);
// Return cards to their rightful owners before conceding the game // Return cards to their rightful owners before conceding the game
static const QRegularExpression ownerRegex{"Owner: ?([^\n]+)"}; static const QRegularExpression ownerRegex{"Owner: ?([^\n]+)"};
const auto &playerTable = player->getZones().value("table"); const auto &playerTable = player->getZones().value(ZoneNames::TABLE);
for (const auto &card : playerTable->getCards()) { for (const auto &card : playerTable->getCards()) {
if (card == nullptr) { if (card == nullptr) {
continue; continue;
@ -858,7 +859,7 @@ void Server_Game::returnCardsFromPlayer(GameEventStorage &ges, Server_AbstractPl
continue; continue;
} }
const auto &targetZone = otherPlayer->getZones().value("table"); const auto &targetZone = otherPlayer->getZones().value(ZoneNames::TABLE);
if (playerTable == nullptr || targetZone == nullptr) { if (playerTable == nullptr || targetZone == nullptr) {
continue; continue;

View file

@ -47,6 +47,7 @@
#include <libcockatrice/rng/rng_abstract.h> #include <libcockatrice/rng/rng_abstract.h>
#include <libcockatrice/utility/color.h> #include <libcockatrice/utility/color.h>
#include <libcockatrice/utility/trice_limits.h> #include <libcockatrice/utility/trice_limits.h>
#include <libcockatrice/utility/zone_names.h>
Server_Player::Server_Player(Server_Game *_game, Server_Player::Server_Player(Server_Game *_game,
int _playerId, int _playerId,
@ -80,15 +81,15 @@ void Server_Player::setupZones()
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// Create zones // Create zones
auto *deckZone = new Server_CardZone(this, "deck", false, ServerInfo_Zone::HiddenZone); auto *deckZone = new Server_CardZone(this, ZoneNames::DECK, false, ServerInfo_Zone::HiddenZone);
addZone(deckZone); addZone(deckZone);
auto *sbZone = new Server_CardZone(this, "sb", false, ServerInfo_Zone::HiddenZone); auto *sbZone = new Server_CardZone(this, ZoneNames::SIDEBOARD, false, ServerInfo_Zone::HiddenZone);
addZone(sbZone); addZone(sbZone);
addZone(new Server_CardZone(this, "table", true, ServerInfo_Zone::PublicZone)); addZone(new Server_CardZone(this, ZoneNames::TABLE, true, ServerInfo_Zone::PublicZone));
addZone(new Server_CardZone(this, "hand", false, ServerInfo_Zone::PrivateZone)); addZone(new Server_CardZone(this, ZoneNames::HAND, false, ServerInfo_Zone::PrivateZone));
addZone(new Server_CardZone(this, "stack", false, ServerInfo_Zone::PublicZone)); addZone(new Server_CardZone(this, ZoneNames::STACK, false, ServerInfo_Zone::PublicZone));
addZone(new Server_CardZone(this, "grave", false, ServerInfo_Zone::PublicZone)); addZone(new Server_CardZone(this, ZoneNames::GRAVE, false, ServerInfo_Zone::PublicZone));
addZone(new Server_CardZone(this, "rfg", false, ServerInfo_Zone::PublicZone)); addZone(new Server_CardZone(this, ZoneNames::EXILE, false, ServerInfo_Zone::PublicZone));
addCounter(new Server_Counter(0, "life", makeColor(255, 255, 255), 25, game->getStartingLifeTotal())); addCounter(new Server_Counter(0, "life", makeColor(255, 255, 255), 25, game->getStartingLifeTotal()));
addCounter(new Server_Counter(1, "w", makeColor(255, 255, 150), 20, 0)); addCounter(new Server_Counter(1, "w", makeColor(255, 255, 150), 20, 0));
@ -164,8 +165,8 @@ void Server_Player::addCounter(Server_Counter *counter)
Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int number) Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int number)
{ {
Server_CardZone *deckZone = zones.value("deck"); Server_CardZone *deckZone = zones.value(ZoneNames::DECK);
Server_CardZone *handZone = zones.value("hand"); Server_CardZone *handZone = zones.value(ZoneNames::HAND);
if (deckZone->getCards().size() < number) { if (deckZone->getCards().size() < number) {
number = deckZone->getCards().size(); number = deckZone->getCards().size();
} }
@ -210,7 +211,7 @@ void Server_Player::onCardBeingMoved(GameEventStorage &ges,
// "Undo draw" should only remain valid if the just-drawn card stays within the user's hand (e.g., they only // "Undo draw" should only remain valid if the just-drawn card stays within the user's hand (e.g., they only
// reorder their hand). If a just-drawn card leaves the hand then remove cards before it from the list // reorder their hand). If a just-drawn card leaves the hand then remove cards before it from the list
// (Ignore the case where the card is currently being un-drawn.) // (Ignore the case where the card is currently being un-drawn.)
if (startzone->getName() == "hand" && targetzone->getName() != "hand" && !undoingDraw) { if (startzone->getName() == ZoneNames::HAND && targetzone->getName() != ZoneNames::HAND && !undoingDraw) {
int index = lastDrawList.lastIndexOf(card->getId()); int index = lastDrawList.lastIndexOf(card->getId());
if (index != -1) { if (index != -1) {
lastDrawList.erase(lastDrawList.begin(), lastDrawList.begin() + index); lastDrawList.erase(lastDrawList.begin(), lastDrawList.begin() + index);
@ -326,11 +327,11 @@ Server_Player::cmdShuffle(const Command_Shuffle &cmd, ResponseContainer & /*rc*/
return Response::RespContextError; return Response::RespContextError;
} }
if (cmd.has_zone_name() && cmd.zone_name() != "deck") { if (cmd.has_zone_name() && cmd.zone_name() != ZoneNames::DECK) {
return Response::RespFunctionNotAllowed; return Response::RespFunctionNotAllowed;
} }
Server_CardZone *zone = zones.value("deck"); Server_CardZone *zone = zones.value(ZoneNames::DECK);
if (!zone) { if (!zone) {
return Response::RespNameNotFound; return Response::RespNameNotFound;
} }
@ -357,8 +358,8 @@ Server_Player::cmdMulligan(const Command_Mulligan &cmd, ResponseContainer & /*rc
return Response::RespContextError; return Response::RespContextError;
} }
Server_CardZone *hand = zones.value("hand"); Server_CardZone *hand = zones.value(ZoneNames::HAND);
Server_CardZone *_deck = zones.value("deck"); Server_CardZone *_deck = zones.value(ZoneNames::DECK);
int number = cmd.number(); int number = cmd.number();
if (!hand->getCards().isEmpty()) { if (!hand->getCards().isEmpty()) {
@ -414,8 +415,8 @@ Server_Player::cmdUndoDraw(const Command_UndoDraw & /*cmd*/, ResponseContainer &
Response::ResponseCode retVal; Response::ResponseCode retVal;
auto *cardToMove = new CardToMove; auto *cardToMove = new CardToMove;
cardToMove->set_card_id(lastDrawList.takeLast()); cardToMove->set_card_id(lastDrawList.takeLast());
retVal = moveCard(ges, zones.value("hand"), QList<const CardToMove *>() << cardToMove, zones.value("deck"), 0, 0, retVal = moveCard(ges, zones.value(ZoneNames::HAND), QList<const CardToMove *>() << cardToMove,
false, true); zones.value(ZoneNames::DECK), 0, 0, false, true);
delete cardToMove; delete cardToMove;
return retVal; return retVal;

View file

@ -6,7 +6,9 @@ message CardToMove {
// Id of the card in its current zone // Id of the card in its current zone
optional sint32 card_id = 1 [default = -1]; optional sint32 card_id = 1 [default = -1];
// Places the card face down, hiding its name // If true, places the card face down, hiding its name.
// If false, forcibly turns the card face up.
// If not set, defers the resulting face down state to the server.
optional bool face_down = 2; optional bool face_down = 2;
// When moving add this value to the power/toughness field of the card // When moving add this value to the power/toughness field of the card

View file

@ -10,8 +10,13 @@ set(UTILITY_SOURCES libcockatrice/utility/expression.cpp libcockatrice/utility/l
) )
set(UTILITY_HEADERS set(UTILITY_HEADERS
libcockatrice/utility/color.h libcockatrice/utility/expression.h libcockatrice/utility/levenshtein.h libcockatrice/utility/color.h
libcockatrice/utility/macros.h libcockatrice/utility/passwordhasher.h libcockatrice/utility/trice_limits.h libcockatrice/utility/expression.h
libcockatrice/utility/levenshtein.h
libcockatrice/utility/macros.h
libcockatrice/utility/passwordhasher.h
libcockatrice/utility/trice_limits.h
libcockatrice/utility/zone_names.h
) )
add_library(libcockatrice_utility STATIC ${UTILITY_SOURCES} ${UTILITY_HEADERS}) add_library(libcockatrice_utility STATIC ${UTILITY_SOURCES} ${UTILITY_HEADERS})

View file

@ -0,0 +1,19 @@
#ifndef ZONE_NAMES_H
#define ZONE_NAMES_H
namespace ZoneNames
{
// Protocol-level zone identifiers shared between client and server.
// These must match exactly across all components.
constexpr const char *TABLE = "table";
constexpr const char *GRAVE = "grave";
constexpr const char *EXILE = "rfg"; // "removed from game"
constexpr const char *HAND = "hand";
constexpr const char *DECK = "deck";
constexpr const char *SIDEBOARD = "sb";
constexpr const char *STACK = "stack";
} // namespace ZoneNames
#endif // ZONE_NAMES_H

View file

@ -1,5 +1,5 @@
add_definitions("-DCARDDB_DATADIR=\"${CMAKE_CURRENT_SOURCE_DIR}/data/\"") add_definitions("-DCARDDB_DATADIR=\"${CMAKE_CURRENT_SOURCE_DIR}/data/\"")
add_executable(loading_from_clipboard_test clipboard_testing.cpp loading_from_clipboard_test.cpp) add_executable(loading_from_clipboard_test ${VERSION_STRING_CPP} clipboard_testing.cpp loading_from_clipboard_test.cpp)
if(NOT GTEST_FOUND) if(NOT GTEST_FOUND)
add_dependencies(loading_from_clipboard_test gtest) add_dependencies(loading_from_clipboard_test gtest)
@ -10,6 +10,7 @@ set(TEST_QT_MODULES ${COCKATRICE_QT_VERSION_NAME}::Concurrent ${COCKATRICE_QT_VE
) )
target_link_libraries( target_link_libraries(
loading_from_clipboard_test libcockatrice_deck_list Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} loading_from_clipboard_test libcockatrice_deck_list libcockatrice_card Threads::Threads ${GTEST_BOTH_LIBRARIES}
${TEST_QT_MODULES}
) )
add_test(NAME loading_from_clipboard_test COMMAND loading_from_clipboard_test) add_test(NAME loading_from_clipboard_test COMMAND loading_from_clipboard_test)

View file

@ -1,6 +1,7 @@
#include "clipboard_testing.h" #include "clipboard_testing.h"
#include <QTextStream> #include <QTextStream>
#include <libcockatrice/card/import/card_name_normalizer.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h> #include <libcockatrice/deck_list/tree/deck_list_card_node.h>
DeckList getDeckList(const QString &clipboard) DeckList getDeckList(const QString &clipboard)
@ -8,7 +9,7 @@ DeckList getDeckList(const QString &clipboard)
DeckList deckList; DeckList deckList;
QString cp(clipboard); QString cp(clipboard);
QTextStream stream(&cp); // text stream requires local copy QTextStream stream(&cp); // text stream requires local copy
deckList.loadFromStream_Plain(stream, false); deckList.loadFromStream_Plain(stream, false, CardNameNormalizer());
return deckList; return deckList;
} }