diff --git a/.ci/name_build.sh b/.ci/name_build.sh index 060f8a68e..85818bbd9 100755 --- a/.ci/name_build.sh +++ b/.ci/name_build.sh @@ -2,7 +2,6 @@ # used by the ci to rename build artifacts # renames the file to [original name][SUFFIX].[original extension] # 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 # adds output to GITHUB_OUTPUT builddir="${BUILD_DIR:=.}" @@ -22,8 +21,8 @@ set -e # find file found="$(find "$builddir" -maxdepth 1 -type f -name "$findrx" -print -quit)" -path="${found%/*}" # remove all after last / -file="${found##*/}" # remove all before last / +path="${found%/*}" # remove all including first "/" from right side +file="${found##*/}" # remove all including last "/" from left side if [[ ! $file ]]; then echo "::error file=$0::could not find package" exit 1 @@ -35,21 +34,16 @@ if ! cd "$path"; then fi # set filename -name="${file%.*}" # remove all after last . -new_name="$name$SUFFIX." -if [[ $MAKE_ZIP ]]; then - filename="${new_name}zip" - echo "creating zip '$filename' from '$file'" - zip "$filename" "$file" -else - extension="${file##*.}" # remove all before last . - filename="$new_name$extension" - echo "renaming '$file' to '$filename'" - mv "$file" "$filename" -fi +name="${file%.*}" # remove all including first "." from right side +new_name="$name$SUFFIX" +extension="${file##*.}" # remove all including last "." from left side +filename="$new_name.$extension" +echo "renaming '$file' to '$filename'" +mv "$file" "$filename" cd "$oldpwd" relative_path="$path/$filename" ls -l "$relative_path" echo "path=$relative_path" >>"$GITHUB_OUTPUT" -echo "name=$filename" >>"$GITHUB_OUTPUT" +echo "name=$new_name" >>"$GITHUB_OUTPUT" +echo "fullname=$filename" >>"$GITHUB_OUTPUT" diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index f9abf643d..02bdc148a 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -9,4 +9,4 @@ contact_links: about: For more information and guidance check our Translation FAQ on our wiki! - name: 📖 Code Documentation url: https://cockatrice.github.io/docs/ - about: + about: Helpful source focusing on developers, but there are also references for users! diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 5bcd160de..a8b633ef4 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -220,27 +220,26 @@ jobs: if: matrix.package != 'skip' uses: actions/upload-artifact@v7 with: - name: ${{matrix.distro}}${{matrix.version}}-package path: ${{steps.build.outputs.path}} + archive: false if-no-files-found: error - name: Upload to release id: upload_release - if: needs.configure.outputs.tag != null && matrix.package != 'skip' + if: matrix.package != 'skip' && needs.configure.outputs.tag != null shell: bash env: GH_TOKEN: ${{github.token}} tag_name: ${{needs.configure.outputs.tag}} - asset_name: ${{steps.build.outputs.name}} + asset_name: ${{steps.build.outputs.fullname}} asset_path: ${{steps.build.outputs.path}} run: gh release upload "$tag_name" "$asset_path#$asset_name" - name: Attest binary provenance id: attestation if: steps.upload_release.outcome == 'success' - uses: actions/attest-build-provenance@v4 + uses: actions/attest@v4 with: - subject-name: ${{steps.build.outputs.name}} subject-path: ${{steps.build.outputs.path}} show-summary: false @@ -265,7 +264,6 @@ jobs: override_target: 13 make_package: 1 package_suffix: "-macOS13_Intel" - artifact_name: macOS13_Intel-package qt_version: 6.10.* qt_arch: clang_64 qt_modules: qtimageformats qtmultimedia qtwebsockets @@ -280,7 +278,6 @@ jobs: type: Release make_package: 1 package_suffix: "-macOS14" - artifact_name: macOS14-package qt_version: 6.10.* qt_arch: clang_64 qt_modules: qtimageformats qtmultimedia qtwebsockets @@ -295,7 +292,6 @@ jobs: type: Release make_package: 1 package_suffix: "-macOS15" - artifact_name: macOS15-package qt_version: 6.10.* qt_arch: clang_64 qt_modules: qtimageformats qtmultimedia qtwebsockets @@ -320,7 +316,6 @@ jobs: type: Release make_package: 1 package_suffix: "-Win10" - artifact_name: Windows10-installer qt_version: 6.10.* qt_arch: win64_msvc2022_64 qt_modules: qtimageformats qtmultimedia qtwebsockets @@ -464,7 +459,8 @@ jobs: key: ${{ steps.ccache_restore.outputs.cache-primary-key }} - 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: MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }} MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} @@ -476,7 +472,7 @@ jobs: fi - 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: MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} @@ -508,19 +504,19 @@ jobs: fi - name: Upload artifact - id: upload_artifact if: matrix.make_package + id: upload_artifact uses: actions/upload-artifact@v7 with: - name: ${{matrix.artifact_name}} path: ${{steps.build.outputs.path}} + archive: false if-no-files-found: error - name: Upload PDBs (Program Databases) - if: matrix.os == 'Windows' + if: matrix.os == 'Windows' && github.ref_type != 'tag' uses: actions/upload-artifact@v7 with: - name: Windows${{matrix.target}}-PDBs + name: ${{steps.build.outputs.name}}-PDBs path: | build/cockatrice/**/*.pdb build/oracle/**/*.pdb @@ -528,22 +524,21 @@ jobs: if-no-files-found: error - name: Upload to release + if: needs.configure.outputs.tag != null && matrix.make_package == '1' id: upload_release - if: needs.configure.outputs.tag != null shell: bash env: GH_TOKEN: ${{github.token}} tag_name: ${{needs.configure.outputs.tag}} - asset_name: ${{steps.build.outputs.name}} + asset_name: ${{steps.build.outputs.fullname}} asset_path: ${{steps.build.outputs.path}} run: gh release upload "$tag_name" "$asset_path#$asset_name" - name: Attest binary provenance - id: attestation if: steps.upload_release.outcome == 'success' - uses: actions/attest-build-provenance@v4 + id: attestation + uses: actions/attest@v4 with: - subject-name: ${{steps.build.outputs.name}} subject-path: ${{steps.build.outputs.path}} show-summary: false diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 1846766bf..65ccd1322 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -41,10 +41,10 @@ jobs: org.opencontainers.image.description=Server for Cockatrice, a cross-platform virtual tabletop for multiplayer card games - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Login to GitHub Container Registry if: github.ref_type == 'tag' @@ -62,5 +62,6 @@ jobs: push: ${{ github.ref_type == 'tag' }} tags: ${{ steps.metadata.outputs.tags }} labels: ${{ steps.metadata.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max + annotations: ${{ steps.metadata.outputs.annotations }} + cache-from: type=gha,scope=servatrice + cache-to: type=gha,mode=max,scope=servatrice diff --git a/cmake/NSIS.template.in b/cmake/NSIS.template.in index a0d51fa2b..2fdc61fb9 100644 --- a/cmake/NSIS.template.in +++ b/cmake/NSIS.template.in @@ -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_FINISHPAGE_RUN "$INSTDIR/cockatrice.exe" !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_LICENSE "${NSIS_SOURCE_PATH}\LICENSE" diff --git a/cockatrice/src/client/network/parsers/interface_json_deck_parser.h b/cockatrice/src/client/network/parsers/interface_json_deck_parser.h index e19e4f4e4..1818aa35c 100644 --- a/cockatrice/src/client/network/parsers/interface_json_deck_parser.h +++ b/cockatrice/src/client/network/parsers/interface_json_deck_parser.h @@ -8,10 +8,11 @@ #define INTERFACE_JSON_DECK_PARSER_H #include "../../../interface/deck_loader/card_node_function.h" -#include "../../../interface/deck_loader/deck_loader.h" #include #include +#include +#include class IJsonDeckParser { @@ -49,7 +50,7 @@ public: outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n'; } - deckList.loadFromStream_Plain(outStream, false); + deckList.loadFromStream_Plain(outStream, false, CardNameNormalizer()); deckList.forEachCard(CardNodeFunction::ResolveProviderId()); return deckList; @@ -96,7 +97,7 @@ public: outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n'; } - deckList.loadFromStream_Plain(outStream, false); + deckList.loadFromStream_Plain(outStream, false, CardNameNormalizer()); deckList.forEachCard(CardNodeFunction::ResolveProviderId()); QJsonObject commandersObj = obj.value("commanders").toObject(); diff --git a/cockatrice/src/client/settings/cache_settings.cpp b/cockatrice/src/client/settings/cache_settings.cpp index 30093fd52..e7a5d114e 100644 --- a/cockatrice/src/client/settings/cache_settings.cpp +++ b/cockatrice/src/client/settings/cache_settings.cpp @@ -1115,257 +1115,21 @@ void SettingsCache::setClientVersion(const QString &_clientVersion) QStringList SettingsCache::getCountries() const { - static QStringList countries = QStringList() << "ad" - << "ae" - << "af" - << "ag" - << "ai" - << "al" - << "am" - << "ao" - << "aq" - << "ar" - << "as" - << "at" - << "au" - << "aw" - << "ax" - << "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"; + static const QStringList countries = { + "ad", "ae", "af", "ag", "ai", "al", "am", "ao", "aq", "ar", "as", "at", "au", "aw", "ax", "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; } diff --git a/cockatrice/src/client/settings/shortcuts_settings.h b/cockatrice/src/client/settings/shortcuts_settings.h index 156ee82ea..47332b8c4 100644 --- a/cockatrice/src/client/settings/shortcuts_settings.h +++ b/cockatrice/src/client/settings/shortcuts_settings.h @@ -598,11 +598,19 @@ private: {"Player/aMoveTopCardsToGraveyard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple)"), parseSequenceString("Alt+M"), ShortcutGroup::Move_top)}, + {"Player/aMoveTopCardsToGraveyardFaceDown", + ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple), Face Down"), + parseSequenceString(""), + ShortcutGroup::Move_top)}, {"Player/aMoveTopCardToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile"), parseSequenceString(""), ShortcutGroup::Move_top)}, {"Player/aMoveTopCardsToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple)"), parseSequenceString(""), 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"), parseSequenceString("Ctrl+Shift+Y"), ShortcutGroup::Move_top)}, @@ -620,11 +628,19 @@ private: {"Player/aMoveBottomCardsToGrave", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple)"), parseSequenceString(""), ShortcutGroup::Move_bottom)}, + {"Player/aMoveBottomCardsToGraveFaceDown", + ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Graveyard (Multiple), Face Down"), + parseSequenceString(""), + ShortcutGroup::Move_bottom)}, {"Player/aMoveBottomCardToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile"), parseSequenceString(""), ShortcutGroup::Move_bottom)}, {"Player/aMoveBottomCardsToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile (Multiple)"), parseSequenceString(""), 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"), parseSequenceString(""), ShortcutGroup::Move_bottom)}, diff --git a/cockatrice/src/game/board/abstract_card_drag_item.cpp b/cockatrice/src/game/board/abstract_card_drag_item.cpp index c961cbcb6..8e3def4ca 100644 --- a/cockatrice/src/game/board/abstract_card_drag_item.cpp +++ b/cockatrice/src/game/board/abstract_card_drag_item.cpp @@ -1,14 +1,13 @@ #include "abstract_card_drag_item.h" #include "../../client/settings/cache_settings.h" +#include "../z_values.h" #include #include #include #include -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); AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, @@ -18,19 +17,19 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, { if (parentDrag) { 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); } else { - hotSpot = QPointF{qBound(0.0, hotSpot.x(), static_cast(CARD_WIDTH - 1)), - qBound(0.0, hotSpot.y(), static_cast(CARD_HEIGHT - 1))}; + hotSpot = QPointF{qBound(0.0, hotSpot.x(), CardDimensions::WIDTH_F - 1), + qBound(0.0, hotSpot.y(), CardDimensions::HEIGHT_F - 1)}; setCursor(Qt::ClosedHandCursor); - setZValue(2000000007); + setZValue(ZValues::DRAG_ITEM); } if (item->getTapped()) setTransform(QTransform() - .translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF) + .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F) .rotate(90) - .translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF)); + .translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F)); setCacheMode(DeviceCoordinateCache); @@ -47,7 +46,7 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, QPainterPath AbstractCardDragItem::shape() const { 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); return shape; } diff --git a/cockatrice/src/game/board/abstract_card_drag_item.h b/cockatrice/src/game/board/abstract_card_drag_item.h index 1d741eded..fe3b87983 100644 --- a/cockatrice/src/game/board/abstract_card_drag_item.h +++ b/cockatrice/src/game/board/abstract_card_drag_item.h @@ -34,7 +34,7 @@ public: AbstractCardDragItem(AbstractCardItem *_item, const QPointF &_hotSpot, AbstractCardDragItem *parentDrag = 0); [[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; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; diff --git a/cockatrice/src/game/board/abstract_card_item.cpp b/cockatrice/src/game/board/abstract_card_item.cpp index d5538011d..9ec6ada9a 100644 --- a/cockatrice/src/game/board/abstract_card_item.cpp +++ b/cockatrice/src/game/board/abstract_card_item.cpp @@ -3,6 +3,7 @@ #include "../../client/settings/cache_settings.h" #include "../../interface/card_picture_loader/card_picture_loader.h" #include "../game_scene.h" +#include "../z_values.h" #include #include @@ -38,13 +39,13 @@ AbstractCardItem::~AbstractCardItem() 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 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); return shape; } @@ -215,9 +216,9 @@ void AbstractCardItem::setHovered(bool _hovered) if (_hovered) processHoverEvent(); isHovered = _hovered; - setZValue(_hovered ? 2000000004 : realZValue); + setZValue(_hovered ? ZValues::HOVERED_CARD : realZValue); 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(); } @@ -273,9 +274,9 @@ void AbstractCardItem::setTapped(bool _tapped, bool canAnimate) else { tapAngle = tapped ? 90 : 0; setTransform(QTransform() - .translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2) + .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F) .rotate(tapAngle) - .translate((float)-CARD_WIDTH / 2, (float)-CARD_HEIGHT / 2)); + .translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F)); update(); } } diff --git a/cockatrice/src/game/board/abstract_card_item.h b/cockatrice/src/game/board/abstract_card_item.h index dad73dc14..7d2c29cae 100644 --- a/cockatrice/src/game/board/abstract_card_item.h +++ b/cockatrice/src/game/board/abstract_card_item.h @@ -8,6 +8,7 @@ #define ABSTRACTCARDITEM_H #include "../../game_graphics/board/graphics_item_type.h" +#include "../card_dimensions.h" #include "arrow_target.h" #include @@ -15,9 +16,6 @@ class Player; -const int CARD_WIDTH = 72; -const int CARD_HEIGHT = 102; - class AbstractCardItem : public ArrowTarget { Q_OBJECT diff --git a/cockatrice/src/game/board/arrow_item.cpp b/cockatrice/src/game/board/arrow_item.cpp index 1352b3a05..60585a774 100644 --- a/cockatrice/src/game/board/arrow_item.cpp +++ b/cockatrice/src/game/board/arrow_item.cpp @@ -5,6 +5,7 @@ #include "../player/player.h" #include "../player/player_actions.h" #include "../player/player_target.h" +#include "../z_values.h" #include "../zones/card_zone.h" #include "card_item.h" @@ -18,12 +19,13 @@ #include #include #include +#include ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color) : QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), targetLocked(false), color(_color), fullColor(true) { - setZValue(2000000005); + setZValue(ZValues::ARROWS); if (startItem) 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 (startZone->getName() == "hand") { + if (startZone->getName() == ZoneNames::HAND) { startCard->playCard(false); CardInfoPtr ci = startCard->getCard().getCardPtr(); bool playToStack = SettingsCache::instance().getPlayToStack(); - if (ci && - ((!playToStack && ci->getUiAttributes().tableRow == 3) || - (playToStack && ci->getUiAttributes().tableRow != 0 && startCard->getZone()->getName() != "stack"))) - cmd.set_start_zone("stack"); + if (ci && ((!playToStack && ci->getUiAttributes().tableRow == 3) || + (playToStack && ci->getUiAttributes().tableRow != 0 && + startCard->getZone()->getName() != ZoneNames::STACK))) + cmd.set_start_zone(ZoneNames::STACK); else - cmd.set_start_zone(playToStack ? "stack" : "table"); + cmd.set_start_zone(playToStack ? ZoneNames::STACK : ZoneNames::TABLE); } if (deleteInPhase != 0) { @@ -317,7 +319,7 @@ void ArrowAttachItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCard) { // 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; } @@ -325,12 +327,12 @@ void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCar CardZoneLogic *targetZone = targetCard->getZone(); // 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); } Command_AttachCard cmd; - cmd.set_start_zone("table"); + cmd.set_start_zone(ZoneNames::TABLE); cmd.set_card_id(startCard->getId()); cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId()); cmd.set_target_zone(targetZone->getName().toStdString()); diff --git a/cockatrice/src/game/board/card_drag_item.cpp b/cockatrice/src/game/board/card_drag_item.cpp index 9eadadeda..5ae56ccba 100644 --- a/cockatrice/src/game/board/card_drag_item.cpp +++ b/cockatrice/src/game/board/card_drag_item.cpp @@ -13,9 +13,10 @@ CardDragItem::CardDragItem(CardItem *_item, int _id, const QPointF &_hotSpot, - bool _faceDown, + bool _forceFaceDown, 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) { } diff --git a/cockatrice/src/game/board/card_drag_item.h b/cockatrice/src/game/board/card_drag_item.h index 257fde150..930c6be6f 100644 --- a/cockatrice/src/game/board/card_drag_item.h +++ b/cockatrice/src/game/board/card_drag_item.h @@ -16,7 +16,7 @@ class CardDragItem : public AbstractCardDragItem Q_OBJECT private: int id; - bool faceDown; + bool forceFaceDown; bool occupied; CardZone *currentZone; @@ -24,15 +24,15 @@ public: CardDragItem(CardItem *_item, int _id, const QPointF &_hotSpot, - bool _faceDown, + bool _forceFaceDown, AbstractCardDragItem *parentDrag = 0); int getId() const { return id; } - bool getFaceDown() const + bool isForceFaceDown() const { - return faceDown; + return forceFaceDown; } void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void updatePosition(const QPointF &cursorScenePos) override; diff --git a/cockatrice/src/game/board/card_item.cpp b/cockatrice/src/game/board/card_item.cpp index 34c2f9a98..62de4a02e 100644 --- a/cockatrice/src/game/board/card_item.cpp +++ b/cockatrice/src/game/board/card_item.cpp @@ -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) { attacking = false; - facedown = false; counters.clear(); pt.clear(); if (!keepAnnotations) { @@ -251,10 +253,10 @@ void CardItem::processCardInfo(const ServerInfo_Card &_info) 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(); - dragItem = new CardDragItem(this, _id, _pos, faceDown); + dragItem = new CardDragItem(this, _id, _pos, forceFaceDown); dragItem->setVisible(false); scene()->addItem(dragItem); dragItem->updatePosition(_scenePos); @@ -352,7 +354,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) // Use the buttonDownPos to align the hot spot with the position when // 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(); int childIndex = 0; @@ -365,7 +367,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) if (zone->getHasCardAttr()) childPos = card->pos() - pos(); else - childPos = QPointF(childIndex * CARD_WIDTH / 2, 0); + childPos = QPointF(childIndex * CardDimensions::WIDTH_HALF_F, 0); CardDragItem *drag = new CardDragItem(card, card->getId(), childPos, card->getFaceDown() || forceFaceDown, dragItem); drag->setPos(dragItem->pos() + childPos); @@ -474,9 +476,9 @@ bool CardItem::animationEvent() } setTransform(QTransform() - .translate(CARD_WIDTH_HALF, CARD_HEIGHT_HALF) + .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F) .rotate(tapAngle) - .translate(-CARD_WIDTH_HALF, -CARD_HEIGHT_HALF)); + .translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F)); setHovered(false); update(); diff --git a/cockatrice/src/game/board/card_item.h b/cockatrice/src/game/board/card_item.h index 86c4fe594..da2097a2c 100644 --- a/cockatrice/src/game/board/card_item.h +++ b/cockatrice/src/game/board/card_item.h @@ -21,8 +21,6 @@ class QAction; class QColor; 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; class CardItem : public AbstractCardItem @@ -142,7 +140,7 @@ public: void processCardInfo(const ServerInfo_Card &_info); 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 drawArrow(const QColor &arrowColor); void drawAttachArrow(); diff --git a/cockatrice/src/game/card_dimensions.h b/cockatrice/src/game/card_dimensions.h new file mode 100644 index 000000000..255d0bc04 --- /dev/null +++ b/cockatrice/src/game/card_dimensions.h @@ -0,0 +1,29 @@ +#ifndef CARD_DIMENSIONS_H +#define CARD_DIMENSIONS_H + +#include + +/** + * @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(WIDTH); +constexpr qreal HEIGHT_F = static_cast(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 diff --git a/cockatrice/src/game/deckview/deck_view.cpp b/cockatrice/src/game/deckview/deck_view.cpp index 383545cf3..620dfaa5f 100644 --- a/cockatrice/src/game/deckview/deck_view.cpp +++ b/cockatrice/src/game/deckview/deck_view.cpp @@ -95,8 +95,9 @@ void DeckViewCard::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti pen.setJoinStyle(Qt::MiterJoin); pen.setColor(originZone == DECK_ZONE_MAIN ? Qt::green : Qt::red); painter->setPen(pen); - qreal cardRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * (CARD_WIDTH - 3) : 0.0; - painter->drawRoundedRect(QRectF(1.5, 1.5, CARD_WIDTH - 3., CARD_HEIGHT - 3.), cardRadius, cardRadius); + qreal cardRadius = SettingsCache::instance().getRoundCardCorners() ? 0.05 * (CardDimensions::WIDTH_F - 3) : 0.0; + painter->drawRoundedRect(QRectF(1.5, 1.5, CardDimensions::WIDTH_F - 3, CardDimensions::HEIGHT_F - 3), cardRadius, + cardRadius); painter->restore(); } @@ -122,7 +123,7 @@ void DeckViewCard::mouseMoveEvent(QGraphicsSceneMouseEvent *event) if (c == this) continue; ++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); drag->setPos(dragItem->pos() + childPos); scene()->addItem(drag); @@ -204,7 +205,7 @@ void DeckViewCardContainer::paint(QPainter *painter, const QStyleOptionGraphicsI painter->setPen(QColor(255, 255, 255, 100)); 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); yUntilNow += thisRowHeight + paddingY; @@ -260,9 +261,9 @@ QSizeF DeckViewCardContainer::calculateBoundingRect(const QList> // Calculate space needed for cards for (int i = 0; i < rowsAndCols.size(); ++i) { - totalHeight += CARD_HEIGHT * rowsAndCols[i].first + paddingY; - if (CARD_WIDTH * rowsAndCols[i].second > totalWidth) - totalWidth = CARD_WIDTH * rowsAndCols[i].second; + totalHeight += CardDimensions::HEIGHT_F * rowsAndCols[i].first + paddingY; + if (CardDimensions::WIDTH_F * rowsAndCols[i].second > totalWidth) + totalWidth = CardDimensions::WIDTH_F * rowsAndCols[i].second; } return QSizeF(getCardTypeTextWidth() + totalWidth, totalHeight + separatorY + paddingY); @@ -289,9 +290,10 @@ void DeckViewCardContainer::rearrangeItems(const QList> &rowsAnd std::sort(row.begin(), row.end(), DeckViewCardContainer::sortCardsByName); for (int j = 0; j < row.size(); ++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(); @@ -392,7 +394,7 @@ void DeckViewScene::applySideboardPlan(const QList &plan) void DeckViewScene::rearrangeItems() { - const int spacing = CARD_HEIGHT / 3; + const int spacing = CardDimensions::HEIGHT / 3; QList contList = cardContainers.values(); // Initialize space requirements diff --git a/cockatrice/src/game/game_scene.cpp b/cockatrice/src/game/game_scene.cpp index 77037cd6e..5dc3b48f7 100644 --- a/cockatrice/src/game/game_scene.cpp +++ b/cockatrice/src/game/game_scene.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include /** @@ -410,9 +411,9 @@ void GameScene::toggleZoneView(Player *player, const QString &zoneName, int numb connect(item, &ZoneViewWidget::closePressed, this, &GameScene::removeZoneView); addItem(item); - if (zoneName == "grave") + if (zoneName == ZoneNames::GRAVE) item->setPos(360, 100); - else if (zoneName == "rfg") + else if (zoneName == ZoneNames::EXILE) item->setPos(380, 120); else item->setPos(340, 80); diff --git a/cockatrice/src/game/log/message_log_widget.cpp b/cockatrice/src/game/log/message_log_widget.cpp index 1ff3e32ff..c38e433eb 100644 --- a/cockatrice/src/game/log/message_log_widget.cpp +++ b/cockatrice/src/game/log/message_log_widget.cpp @@ -10,16 +10,9 @@ #include <../../client/settings/card_counter_settings.h> #include #include +#include #include -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) { return dirty.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """); @@ -37,15 +30,15 @@ MessageLogWidget::getFromStr(CardZoneLogic *zone, QString cardName, int position QString fromStr; QString zoneName = zone->getName(); - if (zoneName == TABLE_ZONE_NAME) { + if (zoneName == ZoneNames::TABLE) { fromStr = tr(" from play"); - } else if (zoneName == GRAVE_ZONE_NAME) { + } else if (zoneName == ZoneNames::GRAVE) { fromStr = tr(" from their graveyard"); - } else if (zoneName == EXILE_ZONE_NAME) { + } else if (zoneName == ZoneNames::EXILE) { fromStr = tr(" from exile"); - } else if (zoneName == HAND_ZONE_NAME) { + } else if (zoneName == ZoneNames::HAND) { fromStr = tr(" from their hand"); - } else if (zoneName == DECK_ZONE_NAME) { + } else if (zoneName == ZoneNames::DECK) { if (position == 0) { if (cardName.isEmpty()) { if (ownerChange) { @@ -83,9 +76,9 @@ MessageLogWidget::getFromStr(CardZoneLogic *zone, QString cardName, int position fromStr = tr(" from their library"); } } - } else if (zoneName == SIDEBOARD_ZONE_NAME) { + } else if (zoneName == ZoneNames::SIDEBOARD) { fromStr = tr(" from sideboard"); - } else if (zoneName == STACK_ZONE_NAME) { + } else if (zoneName == ZoneNames::STACK) { fromStr = tr(" from the stack"); } else { fromStr = tr(" from custom zone '%1'").arg(zoneName); @@ -275,9 +268,9 @@ void MessageLogWidget::logMoveCard(Player *player, bool ownerChanged = startZone->getPlayer() != targetZone->getPlayer(); // do not log if moved within the same zone - if ((startZoneName == TABLE_ZONE_NAME && targetZoneName == TABLE_ZONE_NAME && !ownerChanged) || - (startZoneName == HAND_ZONE_NAME && targetZoneName == HAND_ZONE_NAME) || - (startZoneName == EXILE_ZONE_NAME && targetZoneName == EXILE_ZONE_NAME)) { + if ((startZoneName == ZoneNames::TABLE && targetZoneName == ZoneNames::TABLE && !ownerChanged) || + (startZoneName == ZoneNames::HAND && targetZoneName == ZoneNames::HAND) || + (startZoneName == ZoneNames::EXILE && targetZoneName == ZoneNames::EXILE)) { return; } @@ -306,20 +299,28 @@ void MessageLogWidget::logMoveCard(Player *player, QString finalStr; std::optional fourthArg; - if (targetZoneName == TABLE_ZONE_NAME) { + if (targetZoneName == ZoneNames::TABLE) { soundEngine->playSound("play_card"); if (card->getFaceDown()) { finalStr = tr("%1 puts %2 into play%3 face down."); } else { finalStr = tr("%1 puts %2 into play%3."); } - } else if (targetZoneName == GRAVE_ZONE_NAME) { - finalStr = tr("%1 puts %2%3 into their graveyard."); - } else if (targetZoneName == EXILE_ZONE_NAME) { - finalStr = tr("%1 exiles %2%3."); - } else if (targetZoneName == HAND_ZONE_NAME) { + } else if (targetZoneName == ZoneNames::GRAVE) { + if (card->getFaceDown()) { + finalStr = tr("%1 puts %2%3 into their graveyard face down."); + } else { + 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."); - } else if (targetZoneName == DECK_ZONE_NAME) { + } else if (targetZoneName == ZoneNames::DECK) { if (newX == -1) { finalStr = tr("%1 puts %2%3 into their library."); } else if (newX >= targetZone->getCards().size()) { @@ -331,14 +332,22 @@ void MessageLogWidget::logMoveCard(Player *player, fourthArg = QString::number(newX); 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."); - } else if (targetZoneName == STACK_ZONE_NAME) { + } else if (targetZoneName == ZoneNames::STACK) { 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 { 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); diff --git a/cockatrice/src/game/phases_toolbar.cpp b/cockatrice/src/game/phases_toolbar.cpp index 5106e40de..2341a1d7f 100644 --- a/cockatrice/src/game/phases_toolbar.cpp +++ b/cockatrice/src/game/phases_toolbar.cpp @@ -11,6 +11,7 @@ #include #include #include +#include PhaseButton::PhaseButton(const QString &_name, QGraphicsItem *parent, QAction *_doubleClickAction, bool _highlightable) : QObject(), QGraphicsItem(parent), name(_name), active(false), highlightable(_highlightable), @@ -259,7 +260,7 @@ void PhasesToolbar::actNextTurn() void PhasesToolbar::actUntapAll() { Command_SetCardAttr cmd; - cmd.set_zone("table"); + cmd.set_zone(ZoneNames::TABLE); cmd.set_attribute(AttrTapped); cmd.set_attr_value("0"); diff --git a/cockatrice/src/game/player/menu/card_menu.cpp b/cockatrice/src/game/player/menu/card_menu.cpp index ddf0a55f3..ef95f052e 100644 --- a/cockatrice/src/game/player/menu/card_menu.cpp +++ b/cockatrice/src/game/player/menu/card_menu.cpp @@ -12,6 +12,7 @@ #include #include +#include CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive) : player(_player), card(_card), shortcutsActive(_shortcutsActive) @@ -115,11 +116,12 @@ CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive } else if (writeableCard) { if (card->getZone()) { - if (card->getZone()->getName() == "table") { + if (card->getZone()->getName() == ZoneNames::TABLE) { createTableMenu(); - } else if (card->getZone()->getName() == "stack") { + } else if (card->getZone()->getName() == ZoneNames::STACK) { createStackMenu(); - } else if (card->getZone()->getName() == "rfg" || card->getZone()->getName() == "grave") { + } else if (card->getZone()->getName() == ZoneNames::EXILE || + card->getZone()->getName() == ZoneNames::GRAVE) { createGraveyardOrExileMenu(); } else { createHandOrCustomZoneMenu(); @@ -128,7 +130,7 @@ CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive addMenu(new MoveMenu(player)); } } else { - if (card->getZone() && card->getZone()->getName() != "hand") { + if (card->getZone() && card->getZone()->getName() != ZoneNames::HAND) { addAction(aDrawArrow); addSeparator(); addRelatedCardView(); @@ -285,7 +287,7 @@ void CardMenu::createHandOrCustomZoneMenu() addMenu(new MoveMenu(player)); // actions that are really wonky when done from deck or sideboard - if (card->getZone()->getName() == "hand") { + if (card->getZone()->getName() == ZoneNames::HAND) { addSeparator(); addAction(aAttach); addAction(aDrawArrow); @@ -298,7 +300,7 @@ void CardMenu::createHandOrCustomZoneMenu() } addRelatedCardView(); - if (card->getZone()->getName() == "hand") { + if (card->getZone()->getName() == ZoneNames::HAND) { addRelatedCardActions(); } } diff --git a/cockatrice/src/game/player/menu/grave_menu.cpp b/cockatrice/src/game/player/menu/grave_menu.cpp index d4faca42b..2af62c08a 100644 --- a/cockatrice/src/game/player/menu/grave_menu.cpp +++ b/cockatrice/src/game/player/menu/grave_menu.cpp @@ -6,6 +6,7 @@ #include #include +#include GraveyardMenu::GraveyardMenu(Player *_player, QWidget *parent) : TearOffMenu(parent), player(_player) { @@ -39,16 +40,16 @@ void GraveyardMenu::createMoveActions() if (player->getPlayerInfo()->local || player->getPlayerInfo()->judge) { aMoveGraveToTopLibrary = new QAction(this); - aMoveGraveToTopLibrary->setData(QList() << "deck" << 0); + aMoveGraveToTopLibrary->setData(QList() << ZoneNames::DECK << 0); aMoveGraveToBottomLibrary = new QAction(this); - aMoveGraveToBottomLibrary->setData(QList() << "deck" << -1); + aMoveGraveToBottomLibrary->setData(QList() << ZoneNames::DECK << -1); aMoveGraveToHand = new QAction(this); - aMoveGraveToHand->setData(QList() << "hand" << 0); + aMoveGraveToHand->setData(QList() << ZoneNames::HAND << 0); aMoveGraveToRfg = new QAction(this); - aMoveGraveToRfg->setData(QList() << "rfg" << 0); + aMoveGraveToRfg->setData(QList() << ZoneNames::EXILE << 0); connect(aMoveGraveToTopLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone); connect(aMoveGraveToBottomLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone); diff --git a/cockatrice/src/game/player/menu/hand_menu.cpp b/cockatrice/src/game/player/menu/hand_menu.cpp index 019ab925c..d65c136bf 100644 --- a/cockatrice/src/game/player/menu/hand_menu.cpp +++ b/cockatrice/src/game/player/menu/hand_menu.cpp @@ -9,6 +9,7 @@ #include #include +#include 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) { aMoveHandToTopLibrary = new QAction(this); - aMoveHandToTopLibrary->setData(QList() << "deck" << 0); + aMoveHandToTopLibrary->setData(QList() << ZoneNames::DECK << 0); aMoveHandToBottomLibrary = new QAction(this); - aMoveHandToBottomLibrary->setData(QList() << "deck" << -1); + aMoveHandToBottomLibrary->setData(QList() << ZoneNames::DECK << -1); aMoveHandToGrave = new QAction(this); - aMoveHandToGrave->setData(QList() << "grave" << 0); + aMoveHandToGrave->setData(QList() << ZoneNames::GRAVE << 0); aMoveHandToRfg = new QAction(this); - aMoveHandToRfg->setData(QList() << "rfg" << 0); + aMoveHandToRfg->setData(QList() << ZoneNames::EXILE << 0); auto hand = player->getHandZone(); diff --git a/cockatrice/src/game/player/menu/library_menu.cpp b/cockatrice/src/game/player/menu/library_menu.cpp index 749439558..1bb647d06 100644 --- a/cockatrice/src/game/player/menu/library_menu.cpp +++ b/cockatrice/src/game/player/menu/library_menu.cpp @@ -51,8 +51,10 @@ LibraryMenu::LibraryMenu(Player *_player, QWidget *parent) : TearOffMenu(parent) topLibraryMenu->addSeparator(); topLibraryMenu->addAction(aMoveTopCardToGraveyard); topLibraryMenu->addAction(aMoveTopCardsToGraveyard); + topLibraryMenu->addAction(aMoveTopCardsToGraveyardFaceDown); topLibraryMenu->addAction(aMoveTopCardToExile); topLibraryMenu->addAction(aMoveTopCardsToExile); + topLibraryMenu->addAction(aMoveTopCardsToExileFaceDown); topLibraryMenu->addAction(aMoveTopCardsUntil); topLibraryMenu->addSeparator(); topLibraryMenu->addAction(aShuffleTopCards); @@ -66,8 +68,10 @@ LibraryMenu::LibraryMenu(Player *_player, QWidget *parent) : TearOffMenu(parent) bottomLibraryMenu->addSeparator(); bottomLibraryMenu->addAction(aMoveBottomCardToGraveyard); bottomLibraryMenu->addAction(aMoveBottomCardsToGraveyard); + bottomLibraryMenu->addAction(aMoveBottomCardsToGraveyardFaceDown); bottomLibraryMenu->addAction(aMoveBottomCardToExile); bottomLibraryMenu->addAction(aMoveBottomCardsToExile); + bottomLibraryMenu->addAction(aMoveBottomCardsToExileFaceDown); bottomLibraryMenu->addSeparator(); bottomLibraryMenu->addAction(aShuffleBottomCards); @@ -136,8 +140,14 @@ void LibraryMenu::createMoveActions() connect(aMoveTopCardToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardToExile); aMoveTopCardsToGraveyard = new QAction(this); connect(aMoveTopCardsToGraveyard, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsToGrave); + aMoveTopCardsToGraveyardFaceDown = new QAction(this); + connect(aMoveTopCardsToGraveyardFaceDown, &QAction::triggered, playerActions, + &PlayerActions::actMoveTopCardsToGraveFaceDown); aMoveTopCardsToExile = new QAction(this); connect(aMoveTopCardsToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsToExile); + aMoveTopCardsToExileFaceDown = new QAction(this); + connect(aMoveTopCardsToExileFaceDown, &QAction::triggered, playerActions, + &PlayerActions::actMoveTopCardsToExileFaceDown); aMoveTopCardsUntil = new QAction(this); connect(aMoveTopCardsUntil, &QAction::triggered, playerActions, &PlayerActions::actMoveTopCardsUntil); aMoveTopCardToBottom = new QAction(this); @@ -156,8 +166,14 @@ void LibraryMenu::createMoveActions() aMoveBottomCardsToGraveyard = new QAction(this); connect(aMoveBottomCardsToGraveyard, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardsToGrave); + aMoveBottomCardsToGraveyardFaceDown = new QAction(this); + connect(aMoveBottomCardsToGraveyardFaceDown, &QAction::triggered, playerActions, + &PlayerActions::actMoveBottomCardsToGraveFaceDown); aMoveBottomCardsToExile = new QAction(this); connect(aMoveBottomCardsToExile, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardsToExile); + aMoveBottomCardsToExileFaceDown = new QAction(this); + connect(aMoveBottomCardsToExileFaceDown, &QAction::triggered, playerActions, + &PlayerActions::actMoveBottomCardsToExileFaceDown); aMoveBottomCardToTop = new QAction(this); connect(aMoveBottomCardToTop, &QAction::triggered, playerActions, &PlayerActions::actMoveBottomCardToTop); } @@ -216,7 +232,9 @@ void LibraryMenu::retranslateUi() aMoveTopCardToGraveyard->setText(tr("Move top card to grave&yard")); aMoveTopCardToExile->setText(tr("Move top card to e&xile")); 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...")); + aMoveTopCardsToExileFaceDown->setText(tr("Move top cards to exile face down...")); aMoveTopCardsUntil->setText(tr("Put top cards on stack &until...")); aShuffleTopCards->setText(tr("Shuffle top cards...")); @@ -227,7 +245,9 @@ void LibraryMenu::retranslateUi() aMoveBottomCardToGraveyard->setText(tr("Move bottom card to grave&yard")); aMoveBottomCardToExile->setText(tr("Move bottom card to e&xile")); 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...")); + aMoveBottomCardsToExileFaceDown->setText(tr("Move bottom cards to exile face down...")); aMoveBottomCardToTop->setText(tr("Put bottom card on &top")); aShuffleBottomCards->setText(tr("Shuffle bottom cards...")); } @@ -335,8 +355,10 @@ void LibraryMenu::setShortcutsActive() aMoveTopToPlayFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveTopToPlayFaceDown")); aMoveTopCardToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToGraveyard")); aMoveTopCardsToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToGraveyard")); + aMoveTopCardsToGraveyardFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToGraveyardFaceDown")); aMoveTopCardToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToExile")); aMoveTopCardsToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToExile")); + aMoveTopCardsToExileFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsToExileFaceDown")); aMoveTopCardsUntil->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardsUntil")); aMoveTopCardToBottom->setShortcuts(shortcuts.getShortcut("Player/aMoveTopCardToBottom")); aDrawBottomCard->setShortcuts(shortcuts.getShortcut("Player/aDrawBottomCard")); @@ -345,8 +367,10 @@ void LibraryMenu::setShortcutsActive() aMoveBottomToPlayFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomToPlayFaceDown")); aMoveBottomCardToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToGrave")); aMoveBottomCardsToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToGrave")); + aMoveBottomCardsToGraveyardFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToGraveFaceDown")); aMoveBottomCardToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToExile")); aMoveBottomCardsToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToExile")); + aMoveBottomCardsToExileFaceDown->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardsToExileFaceDown")); aMoveBottomCardToTop->setShortcuts(shortcuts.getShortcut("Player/aMoveBottomCardToTop")); } @@ -367,8 +391,10 @@ void LibraryMenu::setShortcutsInactive() aMoveTopToPlayFaceDown->setShortcut(QKeySequence()); aMoveTopCardToGraveyard->setShortcut(QKeySequence()); aMoveTopCardsToGraveyard->setShortcut(QKeySequence()); + aMoveTopCardsToGraveyardFaceDown->setShortcut(QKeySequence()); aMoveTopCardToExile->setShortcut(QKeySequence()); aMoveTopCardsToExile->setShortcut(QKeySequence()); + aMoveTopCardsToExileFaceDown->setShortcut(QKeySequence()); aMoveTopCardsUntil->setShortcut(QKeySequence()); aDrawBottomCard->setShortcut(QKeySequence()); aDrawBottomCards->setShortcut(QKeySequence()); @@ -376,6 +402,8 @@ void LibraryMenu::setShortcutsInactive() aMoveBottomToPlayFaceDown->setShortcut(QKeySequence()); aMoveBottomCardToGraveyard->setShortcut(QKeySequence()); aMoveBottomCardsToGraveyard->setShortcut(QKeySequence()); + aMoveBottomCardsToGraveyardFaceDown->setShortcut(QKeySequence()); aMoveBottomCardToExile->setShortcut(QKeySequence()); aMoveBottomCardsToExile->setShortcut(QKeySequence()); + aMoveBottomCardsToExileFaceDown->setShortcut(QKeySequence()); } diff --git a/cockatrice/src/game/player/menu/library_menu.h b/cockatrice/src/game/player/menu/library_menu.h index 96d4b82b1..c0883107c 100644 --- a/cockatrice/src/game/player/menu/library_menu.h +++ b/cockatrice/src/game/player/menu/library_menu.h @@ -88,7 +88,9 @@ public: QAction *aMoveTopCardToGraveyard = nullptr; QAction *aMoveTopCardToExile = nullptr; QAction *aMoveTopCardsToGraveyard = nullptr; + QAction *aMoveTopCardsToGraveyardFaceDown = nullptr; QAction *aMoveTopCardsToExile = nullptr; + QAction *aMoveTopCardsToExileFaceDown = nullptr; QAction *aMoveTopCardsUntil = nullptr; QAction *aShuffleTopCards = nullptr; @@ -100,7 +102,9 @@ public: QAction *aMoveBottomCardToGraveyard = nullptr; QAction *aMoveBottomCardToExile = nullptr; QAction *aMoveBottomCardsToGraveyard = nullptr; + QAction *aMoveBottomCardsToGraveyardFaceDown = nullptr; QAction *aMoveBottomCardsToExile = nullptr; + QAction *aMoveBottomCardsToExileFaceDown = nullptr; QAction *aShuffleBottomCards = nullptr; int defaultNumberTopCards = 1; diff --git a/cockatrice/src/game/player/menu/rfg_menu.cpp b/cockatrice/src/game/player/menu/rfg_menu.cpp index 25e162581..8a101c04c 100644 --- a/cockatrice/src/game/player/menu/rfg_menu.cpp +++ b/cockatrice/src/game/player/menu/rfg_menu.cpp @@ -3,6 +3,8 @@ #include "../player.h" #include "../player_actions.h" +#include + RfgMenu::RfgMenu(Player *_player, QWidget *parent) : TearOffMenu(parent), player(_player) { createMoveActions(); @@ -30,13 +32,13 @@ void RfgMenu::createMoveActions() auto rfg = player->getRfgZone(); aMoveRfgToTopLibrary = new QAction(this); - aMoveRfgToTopLibrary->setData(QList() << "deck" << 0); + aMoveRfgToTopLibrary->setData(QList() << ZoneNames::DECK << 0); aMoveRfgToBottomLibrary = new QAction(this); - aMoveRfgToBottomLibrary->setData(QList() << "deck" << -1); + aMoveRfgToBottomLibrary->setData(QList() << ZoneNames::DECK << -1); aMoveRfgToHand = new QAction(this); - aMoveRfgToHand->setData(QList() << "hand" << 0); + aMoveRfgToHand->setData(QList() << ZoneNames::HAND << 0); aMoveRfgToGrave = new QAction(this); - aMoveRfgToGrave->setData(QList() << "grave" << 0); + aMoveRfgToGrave->setData(QList() << ZoneNames::GRAVE << 0); connect(aMoveRfgToTopLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone); connect(aMoveRfgToBottomLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone); diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp index 0723ae6bc..ac4149f0e 100644 --- a/cockatrice/src/game/player/player.cpp +++ b/cockatrice/src/game/player/player.cpp @@ -61,15 +61,15 @@ void Player::forwardActionSignalsToEventHandler() void Player::initializeZones() { - addZone(new PileZoneLogic(this, "deck", false, true, false, this)); - addZone(new PileZoneLogic(this, "grave", false, false, true, this)); - addZone(new PileZoneLogic(this, "rfg", false, false, true, this)); - addZone(new PileZoneLogic(this, "sb", false, false, false, this)); - addZone(new TableZoneLogic(this, "table", true, false, true, this)); - addZone(new StackZoneLogic(this, "stack", true, false, true, this)); + addZone(new PileZoneLogic(this, ZoneNames::DECK, false, true, false, this)); + addZone(new PileZoneLogic(this, ZoneNames::GRAVE, false, false, true, this)); + addZone(new PileZoneLogic(this, ZoneNames::EXILE, false, false, true, this)); + addZone(new PileZoneLogic(this, ZoneNames::SIDEBOARD, false, false, false, this)); + addZone(new TableZoneLogic(this, ZoneNames::TABLE, true, false, true, this)); + addZone(new StackZoneLogic(this, ZoneNames::STACK, true, false, true, this)); bool visibleHand = playerInfo->getLocalOrJudge() || (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() @@ -119,13 +119,13 @@ void Player::setZoneId(int _zoneId) void Player::processPlayerInfo(const ServerInfo_Player &info) { static QSet builtinZones{/* PileZones */ - "deck", "grave", "rfg", "sb", + ZoneNames::DECK, ZoneNames::GRAVE, ZoneNames::EXILE, ZoneNames::SIDEBOARD, /* TableZone */ - "table", + ZoneNames::TABLE, /* StackZone */ - "stack", + ZoneNames::STACK, /* HandZone */ - "hand"}; + ZoneNames::HAND}; clearCounters(); clearArrows(); diff --git a/cockatrice/src/game/player/player.h b/cockatrice/src/game/player/player.h index 0a03b3abe..e9c008821 100644 --- a/cockatrice/src/game/player/player.h +++ b/cockatrice/src/game/player/player.h @@ -27,6 +27,7 @@ #include #include #include +#include inline Q_LOGGING_CATEGORY(PlayerLog, "player"); @@ -155,37 +156,37 @@ public: PileZoneLogic *getDeckZone() { - return qobject_cast(zones.value("deck")); + return qobject_cast(zones.value(ZoneNames::DECK)); } PileZoneLogic *getGraveZone() { - return qobject_cast(zones.value("grave")); + return qobject_cast(zones.value(ZoneNames::GRAVE)); } PileZoneLogic *getRfgZone() { - return qobject_cast(zones.value("rfg")); + return qobject_cast(zones.value(ZoneNames::EXILE)); } PileZoneLogic *getSideboardZone() { - return qobject_cast(zones.value("sb")); + return qobject_cast(zones.value(ZoneNames::SIDEBOARD)); } TableZoneLogic *getTableZone() { - return qobject_cast(zones.value("table")); + return qobject_cast(zones.value(ZoneNames::TABLE)); } StackZoneLogic *getStackZone() { - return qobject_cast(zones.value("stack")); + return qobject_cast(zones.value(ZoneNames::STACK)); } HandZoneLogic *getHandZone() { - return qobject_cast(zones.value("hand")); + return qobject_cast(zones.value(ZoneNames::HAND)); } AbstractCounter *addCounter(const ServerInfo_Counter &counter); diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index ed77808b0..20e526727 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -28,6 +28,7 @@ #include #include #include +#include // milliseconds in between triggers of the move top cards until action 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; bool playToStack = SettingsCache::instance().getPlayToStack(); QString currentZone = card->getZone()->getName(); - if (currentZone == "stack" && tableRow == 3) { - cmd.set_target_zone("grave"); + if (currentZone == ZoneNames::STACK && tableRow == 3) { + cmd.set_target_zone(ZoneNames::GRAVE); cmd.set_x(0); cmd.set_y(0); - } else if (!faceDown && - ((!playToStack && tableRow == 3) || ((playToStack && tableRow != 0) && currentZone != "stack"))) { - cmd.set_target_zone("stack"); + } else if (!faceDown && ((!playToStack && tableRow == 3) || + ((playToStack && tableRow != 0) && currentZone != ZoneNames::STACK))) { + cmd.set_target_zone(ZoneNames::STACK); cmd.set_x(-1); cmd.set_y(0); } else { @@ -81,7 +82,7 @@ void PlayerActions::playCard(CardItem *card, bool faceDown) } cardToMove->set_tapped(!faceDown && info.getUiAttributes().cipt); if (tableRow != 3) - cmd.set_target_zone("table"); + cmd.set_target_zone(ZoneNames::TABLE); cmd.set_x(gridPoint.x()); 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_tapped(!faceDown && info.getUiAttributes().cipt); - cmd.set_target_zone("table"); + cmd.set_target_zone(ZoneNames::TABLE); cmd.set_x(gridPoint.x()); cmd.set_y(gridPoint.y()); sendGameCommand(cmd); @@ -132,12 +133,12 @@ void PlayerActions::playCardToTable(const CardItem *card, bool faceDown) void PlayerActions::actViewLibrary() { - player->getGameScene()->toggleZoneView(player, "deck", -1); + player->getGameScene()->toggleZoneView(player, ZoneNames::DECK, -1); } 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); if (ok) { 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); if (ok) { defaultNumberBottomCards = number; - player->getGameScene()->toggleZoneView(player, "deck", number, true); + player->getGameScene()->toggleZoneView(player, ZoneNames::DECK, number, true); } } void PlayerActions::actAlwaysRevealTopCard() { Command_ChangeZoneProperties cmd; - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); cmd.set_always_reveal_top_card(player->getPlayerMenu()->getLibraryMenu()->isAlwaysRevealTopCardChecked()); sendGameCommand(cmd); @@ -210,7 +211,7 @@ void PlayerActions::actAlwaysRevealTopCard() void PlayerActions::actAlwaysLookAtTopCard() { 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()); sendGameCommand(cmd); @@ -223,17 +224,17 @@ void PlayerActions::actOpenDeckInDeckEditor() void PlayerActions::actViewGraveyard() { - player->getGameScene()->toggleZoneView(player, "grave", -1); + player->getGameScene()->toggleZoneView(player, ZoneNames::GRAVE, -1); } void PlayerActions::actViewRfg() { - player->getGameScene()->toggleZoneView(player, "rfg", -1); + player->getGameScene()->toggleZoneView(player, ZoneNames::EXILE, -1); } void PlayerActions::actViewSideboard() { - player->getGameScene()->toggleZoneView(player, "sb", -1); + player->getGameScene()->toggleZoneView(player, ZoneNames::SIDEBOARD, -1); } void PlayerActions::actShuffle() @@ -263,7 +264,7 @@ void PlayerActions::actShuffleTop() defaultNumberTopCards = number; Command_Shuffle cmd; - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); cmd.set_start(0); cmd.set_end(number - 1); // inclusive, the indexed card at end will be shuffled @@ -292,7 +293,7 @@ void PlayerActions::actShuffleBottom() defaultNumberBottomCards = number; Command_Shuffle cmd; - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); cmd.set_start(-number); cmd.set_end(-1); @@ -376,7 +377,7 @@ void PlayerActions::actUndoDraw() 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(); cardToMove->set_card_id(0); cmd.set_target_player_id(player->getPlayerInfo()->getId()); @@ -386,7 +387,7 @@ void PlayerActions::cmdSetBottomCard(Command_MoveCard &cmd) { CardZoneLogic *zone = player->getDeckZone(); 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(); cardToMove->set_card_id(lastCard); cmd.set_target_player_id(player->getPlayerInfo()->getId()); @@ -400,7 +401,7 @@ void PlayerActions::actMoveTopCardToGrave() Command_MoveCard cmd; cmdSetTopCard(cmd); - cmd.set_target_zone("grave"); + cmd.set_target_zone(ZoneNames::GRAVE); cmd.set_x(0); cmd.set_y(0); @@ -415,7 +416,7 @@ void PlayerActions::actMoveTopCardToExile() Command_MoveCard cmd; cmdSetTopCard(cmd); - cmd.set_target_zone("rfg"); + cmd.set_target_zone(ZoneNames::EXILE); cmd.set_x(0); cmd.set_y(0); @@ -424,37 +425,25 @@ void PlayerActions::actMoveTopCardToExile() void PlayerActions::actMoveTopCardsToGrave() { - const int maxCards = player->getDeckZone()->getCards().size(); - if (maxCards == 0) { - return; - } + moveTopCardsTo(ZoneNames::GRAVE, tr("grave"), false); +} - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move top cards to grave"), - tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1, - 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::actMoveTopCardsToGraveFaceDown() +{ + moveTopCardsTo(ZoneNames::GRAVE, tr("grave"), true); } 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(); if (maxCards == 0) { @@ -462,25 +451,31 @@ void PlayerActions::actMoveTopCardsToExile() } 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, maxCards, 1, &ok); if (!ok) { return; - } else if (number > maxCards) { + } + + if (number > maxCards) { number = maxCards; } defaultNumberTopCards = number; 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_zone("rfg"); + cmd.set_target_zone(targetZone.toStdString()); 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); + auto card = cmd.mutable_cards_to_move()->add_card(); + card->set_card_id(i); + if (faceDown) { + card->set_face_down(true); + } } sendGameCommand(cmd); @@ -555,7 +550,7 @@ void PlayerActions::actMoveTopCardToBottom() Command_MoveCard cmd; cmdSetTopCard(cmd); - cmd.set_target_zone("deck"); + cmd.set_target_zone(ZoneNames::DECK); cmd.set_x(-1); // bottom of deck cmd.set_y(0); @@ -570,7 +565,7 @@ void PlayerActions::actMoveTopCardToPlay() Command_MoveCard cmd; cmdSetTopCard(cmd); - cmd.set_target_zone("stack"); + cmd.set_target_zone(ZoneNames::STACK); cmd.set_x(-1); cmd.set_y(0); @@ -584,12 +579,12 @@ void PlayerActions::actMoveTopCardToPlayFaceDown() } Command_MoveCard cmd; - cmd.set_start_zone("deck"); + cmd.set_start_zone(ZoneNames::DECK); CardToMove *cardToMove = cmd.mutable_cards_to_move()->add_card(); cardToMove->set_card_id(0); cardToMove->set_face_down(true); 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_y(0); @@ -604,7 +599,7 @@ void PlayerActions::actMoveBottomCardToGrave() Command_MoveCard cmd; cmdSetBottomCard(cmd); - cmd.set_target_zone("grave"); + cmd.set_target_zone(ZoneNames::GRAVE); cmd.set_x(0); cmd.set_y(0); @@ -619,7 +614,7 @@ void PlayerActions::actMoveBottomCardToExile() Command_MoveCard cmd; cmdSetBottomCard(cmd); - cmd.set_target_zone("rfg"); + cmd.set_target_zone(ZoneNames::EXILE); cmd.set_x(0); cmd.set_y(0); @@ -628,37 +623,25 @@ void PlayerActions::actMoveBottomCardToExile() void PlayerActions::actMoveBottomCardsToGrave() { - const int maxCards = player->getDeckZone()->getCards().size(); - if (maxCards == 0) { - return; - } + moveBottomCardsTo(ZoneNames::GRAVE, tr("grave"), false); +} - bool ok; - int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move bottom cards to grave"), - tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1, - 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::actMoveBottomCardsToGraveFaceDown() +{ + moveBottomCardsTo(ZoneNames::GRAVE, tr("grave"), true); } 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(); if (maxCards == 0) { @@ -666,25 +649,31 @@ void PlayerActions::actMoveBottomCardsToExile() } 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, maxCards, 1, &ok); if (!ok) { return; - } else if (number > maxCards) { + } + + if (number > maxCards) { number = maxCards; } defaultNumberBottomCards = number; 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_zone("rfg"); + cmd.set_target_zone(targetZone.toStdString()); 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); + auto card = cmd.mutable_cards_to_move()->add_card(); + card->set_card_id(i); + if (faceDown) { + card->set_face_down(true); + } } sendGameCommand(cmd); @@ -698,7 +687,7 @@ void PlayerActions::actMoveBottomCardToTop() Command_MoveCard cmd; cmdSetBottomCard(cmd); - cmd.set_target_zone("deck"); + cmd.set_target_zone(ZoneNames::DECK); cmd.set_x(0); // top of deck cmd.set_y(0); @@ -768,7 +757,7 @@ void PlayerActions::actDrawBottomCard() Command_MoveCard cmd; cmdSetBottomCard(cmd); - cmd.set_target_zone("hand"); + cmd.set_target_zone(ZoneNames::HAND); cmd.set_x(0); cmd.set_y(0); @@ -794,9 +783,9 @@ void PlayerActions::actDrawBottomCards() defaultNumberBottomCards = number; 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_zone("hand"); + cmd.set_target_zone(ZoneNames::HAND); cmd.set_x(0); cmd.set_y(0); @@ -815,7 +804,7 @@ void PlayerActions::actMoveBottomCardToPlay() Command_MoveCard cmd; cmdSetBottomCard(cmd); - cmd.set_target_zone("stack"); + cmd.set_target_zone(ZoneNames::STACK); cmd.set_x(-1); cmd.set_y(0); @@ -832,13 +821,13 @@ void PlayerActions::actMoveBottomCardToPlayFaceDown() int lastCard = zone->getCards().size() - 1; Command_MoveCard cmd; - cmd.set_start_zone("deck"); + cmd.set_start_zone(ZoneNames::DECK); auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); cardToMove->set_card_id(lastCard); cardToMove->set_face_down(true); 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_y(0); @@ -848,7 +837,7 @@ void PlayerActions::actMoveBottomCardToPlayFaceDown() void PlayerActions::actUntapAll() { Command_SetCardAttr cmd; - cmd.set_zone("table"); + cmd.set_zone(ZoneNames::TABLE); cmd.set_attribute(AttrTapped); cmd.set_attr_value("0"); @@ -898,7 +887,7 @@ void PlayerActions::actCreateAnotherToken() } Command_CreateToken cmd; - cmd.set_zone("table"); + cmd.set_zone(ZoneNames::TABLE); cmd.set_card_name(lastTokenInfo.name.toStdString()); cmd.set_card_provider_id(lastTokenInfo.providerId.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 // 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); } @@ -1105,7 +1094,7 @@ void PlayerActions::createCard(const CardItem *sourceCard, // create the token for the related card Command_CreateToken cmd; - cmd.set_zone("table"); + cmd.set_zone(ZoneNames::TABLE); cmd.set_card_name(cardInfo->getName().toStdString()); switch (cardInfo->getColors().size()) { case 0: @@ -1134,12 +1123,12 @@ void PlayerActions::createCard(const CardItem *sourceCard, switch (attachType) { case CardRelationType::DoesNotAttach: - cmd.set_target_zone("table"); + cmd.set_target_zone(ZoneNames::TABLE); cmd.set_card_provider_id(relatedCard.getPrinting().getUuid().toStdString()); break; 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_target_card_id(sourceCard->getId()); cmd.set_target_mode(Command_CreateToken::ATTACH_TO); @@ -1147,7 +1136,7 @@ void PlayerActions::createCard(const CardItem *sourceCard, case CardRelationType::TransformInto: // 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 cmd.set_target_zone(sourceCard->getZone()->getName().toStdString()); cmd.set_target_card_id(sourceCard->getId()); @@ -1262,7 +1251,7 @@ void PlayerActions::actMoveCardXCardsFromTop() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); 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_y(0); commandList.append(cmd); @@ -1651,7 +1640,7 @@ void PlayerActions::playSelectedCards(const bool faceDown) [](const auto &card1, const auto &card2) { return card1->getId() > card2->getId(); }); 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); } } @@ -1704,7 +1693,7 @@ void PlayerActions::actRevealHand(int revealToPlayerId) if (revealToPlayerId != -1) { cmd.set_player_id(revealToPlayerId); } - cmd.set_zone_name("hand"); + cmd.set_zone_name(ZoneNames::HAND); sendGameCommand(cmd); } @@ -1715,7 +1704,7 @@ void PlayerActions::actRevealRandomHandCard(int revealToPlayerId) if (revealToPlayerId != -1) { cmd.set_player_id(revealToPlayerId); } - cmd.set_zone_name("hand"); + cmd.set_zone_name(ZoneNames::HAND); cmd.add_card_id(RANDOM_CARD_FROM_ZONE); sendGameCommand(cmd); @@ -1727,7 +1716,7 @@ void PlayerActions::actRevealLibrary(int revealToPlayerId) if (revealToPlayerId != -1) { cmd.set_player_id(revealToPlayerId); } - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); sendGameCommand(cmd); } @@ -1738,7 +1727,7 @@ void PlayerActions::actLendLibrary(int lendToPlayerId) if (lendToPlayerId != -1) { cmd.set_player_id(lendToPlayerId); } - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); cmd.set_grant_write_access(true); sendGameCommand(cmd); @@ -1751,7 +1740,7 @@ void PlayerActions::actRevealTopCards(int revealToPlayerId, int amount) cmd.set_player_id(revealToPlayerId); } - cmd.set_zone_name("deck"); + cmd.set_zone_name(ZoneNames::DECK); cmd.set_top_cards(amount); // backward compatibility: servers before #1051 only permits to reveal the first card cmd.add_card_id(0); @@ -1765,7 +1754,7 @@ void PlayerActions::actRevealRandomGraveyardCard(int revealToPlayerId) if (revealToPlayerId != -1) { cmd.set_player_id(revealToPlayerId); } - cmd.set_zone_name("grave"); + cmd.set_zone_name(ZoneNames::GRAVE); cmd.add_card_id(RANDOM_CARD_FROM_ZONE); sendGameCommand(cmd); } @@ -1828,7 +1817,7 @@ void PlayerActions::cardMenuAction() } case cmClone: { auto *cmd = new Command_CreateToken; - cmd->set_zone("table"); + cmd->set_zone(ZoneNames::TABLE); cmd->set_card_name(card->getName().toStdString()); cmd->set_card_provider_id(card->getProviderId().toStdString()); cmd->set_color(card->getColor().toStdString()); @@ -1870,13 +1859,13 @@ void PlayerActions::cardMenuAction() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); 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_y(0); if (idList.card_size() > 1) { auto *scmd = new Command_Shuffle; - scmd->set_zone_name("deck"); + scmd->set_zone_name(ZoneNames::DECK); scmd->set_start(0); scmd->set_end(idList.card_size() - 1); // inclusive, the indexed card at end will be shuffled // Server process events backwards, so... @@ -1892,13 +1881,13 @@ void PlayerActions::cardMenuAction() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); 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_y(0); if (idList.card_size() > 1) { auto *scmd = new Command_Shuffle; - scmd->set_zone_name("deck"); + scmd->set_zone_name(ZoneNames::DECK); scmd->set_start(-idList.card_size()); scmd->set_end(-1); // Server process events backwards, so... @@ -1914,7 +1903,7 @@ void PlayerActions::cardMenuAction() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); 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_y(0); commandList.append(cmd); @@ -1926,7 +1915,7 @@ void PlayerActions::cardMenuAction() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); 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_y(0); commandList.append(cmd); @@ -1938,7 +1927,7 @@ void PlayerActions::cardMenuAction() cmd->set_start_zone(startZone.toStdString()); cmd->mutable_cards_to_move()->CopyFrom(idList); 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_y(0); commandList.append(cmd); diff --git a/cockatrice/src/game/player/player_actions.h b/cockatrice/src/game/player/player_actions.h index b294b5946..1d695578d 100644 --- a/cockatrice/src/game/player/player_actions.h +++ b/cockatrice/src/game/player/player_actions.h @@ -98,7 +98,9 @@ public slots: void actMoveTopCardToGrave(); void actMoveTopCardToExile(); void actMoveTopCardsToGrave(); + void actMoveTopCardsToGraveFaceDown(); void actMoveTopCardsToExile(); + void actMoveTopCardsToExileFaceDown(); void actMoveTopCardsUntil(); void actMoveTopCardToBottom(); void actDrawBottomCard(); @@ -108,7 +110,9 @@ public slots: void actMoveBottomCardToGrave(); void actMoveBottomCardToExile(); void actMoveBottomCardsToGrave(); + void actMoveBottomCardsToGraveFaceDown(); void actMoveBottomCardsToExile(); + void actMoveBottomCardsToExileFaceDown(); void actMoveBottomCardToTop(); void actSelectAll(); @@ -180,6 +184,9 @@ private: FilterString movingCardsUntilFilter; 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, const QString &dbCardName, CardRelationType attach = CardRelationType::DoesNotAttach, diff --git a/cockatrice/src/game/player/player_event_handler.cpp b/cockatrice/src/game/player/player_event_handler.cpp index 331605918..f4c3840e0 100644 --- a/cockatrice/src/game/player/player_event_handler.cpp +++ b/cockatrice/src/game/player/player_event_handler.cpp @@ -30,6 +30,7 @@ #include #include #include +#include PlayerEventHandler::PlayerEventHandler(Player *_player) : player(_player) { @@ -321,8 +322,8 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv } player->getPlayerMenu()->updateCardMenu(card); - if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == "deck" && - targetZone->getName() == "stack") { + if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == ZoneNames::DECK && + targetZone->getName() == ZoneNames::STACK) { player->getPlayerActions()->moveOneCardUntil(card); } } diff --git a/cockatrice/src/game/player/player_graphics_item.cpp b/cockatrice/src/game/player/player_graphics_item.cpp index 30648c926..bcc4b7f72 100644 --- a/cockatrice/src/game/player/player_graphics_item.cpp +++ b/cockatrice/src/game/player/player_graphics_item.cpp @@ -19,7 +19,8 @@ PlayerGraphicsItem::PlayerGraphicsItem(Player *_player) : player(_player) playerArea = new PlayerArea(this); 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)); initializeZones(); @@ -55,8 +56,9 @@ void PlayerGraphicsItem::onPlayerActiveChanged(bool _active) void PlayerGraphicsItem::initializeZones() { deckZoneGraphicsItem = new PileZone(player->getDeckZone(), this); - auto base = QPointF(counterAreaWidth + (CARD_HEIGHT - CARD_WIDTH + 15) / 2.0, - 10 + playerTarget->boundingRect().height() + 5 - (CARD_HEIGHT - CARD_WIDTH) / 2.0); + auto base = QPointF(counterAreaWidth + (CardDimensions::HEIGHT_F - CardDimensions::WIDTH_F + 15) / 2.0, + 10 + playerTarget->boundingRect().height() + 5 - + (CardDimensions::HEIGHT_F - CardDimensions::WIDTH_F) / 2.0); deckZoneGraphicsItem->setPos(base); qreal h = deckZoneGraphicsItem->boundingRect().width() + 5; @@ -95,7 +97,7 @@ QRectF PlayerGraphicsItem::boundingRect() 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(); if (!SettingsCache::instance().getHorizontalHand()) { result += handZoneGraphicsItem->boundingRect().width(); @@ -112,8 +114,8 @@ void PlayerGraphicsItem::paint(QPainter * /*painter*/, void PlayerGraphicsItem::processSceneSizeChange(int newPlayerWidth) { // Extend table (and hand, if horizontal) to accommodate the new player width. - qreal tableWidth = - newPlayerWidth - CARD_HEIGHT - 15 - counterAreaWidth - stackZoneGraphicsItem->boundingRect().width(); + qreal tableWidth = newPlayerWidth - CardDimensions::HEIGHT_F - 15 - counterAreaWidth - + stackZoneGraphicsItem->boundingRect().width(); if (!SettingsCache::instance().getHorizontalHand()) { tableWidth -= handZoneGraphicsItem->boundingRect().width(); } @@ -152,7 +154,7 @@ void PlayerGraphicsItem::rearrangeCounters() 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 (mirrored) { if (player->getHandZone()->contentsKnown()) { @@ -203,7 +205,7 @@ void PlayerGraphicsItem::rearrangeZones() void PlayerGraphicsItem::updateBoundingRect() { prepareGeometryChange(); - qreal width = CARD_HEIGHT + 15 + counterAreaWidth + stackZoneGraphicsItem->boundingRect().width(); + qreal width = CardDimensions::HEIGHT_F + 15 + counterAreaWidth + stackZoneGraphicsItem->boundingRect().width(); if (SettingsCache::instance().getHorizontalHand()) { qreal handHeight = player->getPlayerInfo()->getHandVisible() ? handZoneGraphicsItem->boundingRect().height() : 0; @@ -214,7 +216,7 @@ void PlayerGraphicsItem::updateBoundingRect() 0, 0, width + handZoneGraphicsItem->boundingRect().width() + tableZoneGraphicsItem->boundingRect().width(), tableZoneGraphicsItem->boundingRect().height()); } - playerArea->setSize(CARD_HEIGHT + counterAreaWidth + 15, bRect.height()); + playerArea->setSize(CardDimensions::HEIGHT_F + counterAreaWidth + 15, bRect.height()); emit sizeChanged(); -} \ No newline at end of file +} diff --git a/cockatrice/src/game/z_value_layer_manager.h b/cockatrice/src/game/z_value_layer_manager.h new file mode 100644 index 000000000..4eb864486 --- /dev/null +++ b/cockatrice/src/game/z_value_layer_manager.h @@ -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 + +/** + * @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 diff --git a/cockatrice/src/game/z_values.h b/cockatrice/src/game/z_values.h new file mode 100644 index 000000000..c6e7f2c8a --- /dev/null +++ b/cockatrice/src/game/z_values.h @@ -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 diff --git a/cockatrice/src/game/zones/hand_zone.cpp b/cockatrice/src/game/zones/hand_zone.cpp index cc3b44910..7badfcca4 100644 --- a/cockatrice/src/game/zones/hand_zone.cpp +++ b/cockatrice/src/game/zones/hand_zone.cpp @@ -56,7 +56,7 @@ void HandZone::handleDropEvent(const QList &dragItems, QRectF HandZone::boundingRect() const { if (SettingsCache::instance().getHorizontalHand()) - return QRectF(0, 0, width, CARD_HEIGHT + 10); + return QRectF(0, 0, width, CardDimensions::HEIGHT_F + 10); else return QRectF(0, 0, 100, zoneHeight); } diff --git a/cockatrice/src/game/zones/logic/card_zone_logic.cpp b/cockatrice/src/game/zones/logic/card_zone_logic.cpp index bd32eab3e..e917e4ad7 100644 --- a/cockatrice/src/game/zones/logic/card_zone_logic.cpp +++ b/cockatrice/src/game/zones/logic/card_zone_logic.cpp @@ -10,6 +10,7 @@ #include #include #include +#include /** * @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) { if (qobject_cast(view->getLogic())->prepareAddCard(x)) { - view->getLogic()->addCard(new CardItem(player, nullptr, card->getCardRef(), card->getId()), reorganize, x, - y); + auto copy = new CardItem(player, nullptr, card->getCardRef(), card->getId()); + 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 ownerName = player->getPlayerInfo()->getName(); - if (name == "hand") + if (name == ZoneNames::HAND) return (theirOwn ? tr("their hand", "nominative") : tr("%1's hand", "nominative").arg(ownerName)); - else if (name == "deck") + else if (name == ZoneNames::DECK) switch (gc) { case CaseLookAtZone: return (theirOwn ? tr("their library", "look at zone") @@ -190,11 +193,11 @@ QString CardZoneLogic::getTranslatedName(bool theirOwn, GrammaticalCase gc) cons default: 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)); - else if (name == "rfg") + else if (name == ZoneNames::EXILE) return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName)); - else if (name == "sb") + else if (name == ZoneNames::SIDEBOARD) switch (gc) { case CaseLookAtZone: return (theirOwn ? tr("their sideboard", "look at zone") diff --git a/cockatrice/src/game/zones/pile_zone.cpp b/cockatrice/src/game/zones/pile_zone.cpp index 2d1390826..d85b5f4e2 100644 --- a/cockatrice/src/game/zones/pile_zone.cpp +++ b/cockatrice/src/game/zones/pile_zone.cpp @@ -19,9 +19,9 @@ PileZone::PileZone(PileZoneLogic *_logic, QGraphicsItem *parent) : CardZone(_log setCursor(Qt::OpenHandCursor); setTransform(QTransform() - .translate((float)CARD_WIDTH / 2, (float)CARD_HEIGHT / 2) + .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F) .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) { Q_UNUSED(_roundCardCorners); @@ -33,13 +33,13 @@ PileZone::PileZone(PileZoneLogic *_logic, QGraphicsItem *parent) : CardZone(_log 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 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); 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), 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->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); } @@ -68,8 +68,13 @@ void PileZone::handleDropEvent(const QList &dragItems, CardZoneL cmd.set_x(0); cmd.set_y(0); - for (int i = 0; i < dragItems.size(); ++i) - cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId()); + for (int i = 0; i < dragItems.size(); ++i) { + 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); } @@ -101,12 +106,12 @@ void PileZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event) if (getLogic()->getCards().isEmpty()) return; - bool faceDown = event->modifiers().testFlag(Qt::ShiftModifier); + bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier); bool bottomCard = event->modifiers().testFlag(Qt::ControlModifier); CardItem *card = bottomCard ? getLogic()->getCards().last() : getLogic()->getCards().first(); const int cardid = 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(); setCursor(Qt::OpenHandCursor); } diff --git a/cockatrice/src/game/zones/stack_zone.cpp b/cockatrice/src/game/zones/stack_zone.cpp index 820008b27..a9d649a4a 100644 --- a/cockatrice/src/game/zones/stack_zone.cpp +++ b/cockatrice/src/game/zones/stack_zone.cpp @@ -78,7 +78,11 @@ void StackZone::handleDropEvent(const QList &dragItems, for (CardDragItem *item : dragItems) { 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); + } } } diff --git a/cockatrice/src/game/zones/table_zone.cpp b/cockatrice/src/game/zones/table_zone.cpp index c76514350..b6ac2150b 100644 --- a/cockatrice/src/game/zones/table_zone.cpp +++ b/cockatrice/src/game/zones/table_zone.cpp @@ -7,6 +7,7 @@ #include "../board/card_item.h" #include "../player/player.h" #include "../player/player_actions.h" +#include "../z_values.h" #include "logic/table_zone_logic.h" #include @@ -14,6 +15,7 @@ #include #include #include +#include const QColor TableZone::BACKGROUND_COLOR = QColor(100, 100, 100); const QColor TableZone::FADE_MASK = QColor(0, 0, 0, 80); @@ -30,7 +32,7 @@ TableZone::TableZone(TableZoneLogic *_logic, QGraphicsItem *parent) : SelectZone 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; 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 // 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()) separatorY = height - separatorY; painter->setPen(QColor(255, 255, 255, 40)); @@ -134,8 +136,10 @@ void TableZone::handleDropEventByGrid(const QList &dragItems, for (const auto &item : dragItems) { CardToMove *ctm = cmd.mutable_cards_to_move()->add_card(); ctm->set_card_id(item->getId()); - ctm->set_face_down(item->getFaceDown()); - if (startZone->getName() != getLogic()->getName() && !item->getFaceDown()) { + if (item->isForceFaceDown()) { + ctm->set_face_down(true); + } + if (startZone->getName() != getLogic()->getName() && !item->isForceFaceDown()) { const auto &card = item->getItem()->getCard(); if (card) { ctm->set_pt(card.getInfo().getPowTough().toStdString()); @@ -167,7 +171,7 @@ void TableZone::reorganizeCards() actualY += 15; 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 attachedCardIterator(getLogic()->getCards()[i]->getAttachedCards()); int j = 0; @@ -177,7 +181,7 @@ void TableZone::reorganizeCards() qreal childX = actualX - j * STACKED_CARD_OFFSET_X; qreal childY = y + 5; 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) { if (auto card = qgraphicsitem_cast(item)) { - return card->getZone()->getName() == "table"; + return card->getZone()->getName() == ZoneNames::TABLE; } return false; }; @@ -229,7 +233,7 @@ void TableZone::resizeToContents() // Minimum width is the rightmost card position plus enough room for // 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) currentMinimumWidth = MIN_WIDTH; @@ -279,10 +283,10 @@ void TableZone::computeCardStackWidths() const int key = getCardStackMapKey(gridPoint.x() / 3, gridPoint.y()); const int stackCount = cardStackCount.value(key, 0); if (stackCount == 1) - cardStackWidth.insert(key, CARD_WIDTH + getLogic()->getCards()[i]->getAttachedCards().size() * - STACKED_CARD_OFFSET_X); + cardStackWidth.insert(key, CardDimensions::WIDTH + getLogic()->getCards()[i]->getAttachedCards().size() * + STACKED_CARD_OFFSET_X); 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 for (int i = 0; i < gridPoint.x() / 3; ++i) { 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()) @@ -307,7 +311,7 @@ QPointF TableZone::mapFromGrid(QPoint gridPoint) const // Add in card size and padding for each row for (int i = 0; i < gridPoint.y(); ++i) - y += CARD_HEIGHT + PADDING_Y; + y += CardDimensions::HEIGHT + PADDING_Y; return QPointF(x, y); } @@ -321,7 +325,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const int y = mapPoint.y() - MARGIN_TOP; // 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; 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 // 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 xNextStack = 0; @@ -345,7 +349,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const while ((xNextStack <= x) && (xNextStack <= xMax)) { xStack = xNextStack; const int key = getCardStackMapKey(nextStackCol, gridPointY); - xNextStack += cardStackWidth.value(key, CARD_WIDTH) + PADDING_X; + xNextStack += cardStackWidth.value(key, CardDimensions::WIDTH) + PADDING_X; nextStackCol++; } int stackCol = qMax(nextStackCol - 1, 0); diff --git a/cockatrice/src/game/zones/table_zone.h b/cockatrice/src/game/zones/table_zone.h index 3b353e76c..61eb48d7b 100644 --- a/cockatrice/src/game/zones/table_zone.h +++ b/cockatrice/src/game/zones/table_zone.h @@ -41,12 +41,12 @@ private: /* 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 */ - 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; /* diff --git a/cockatrice/src/game/zones/view_zone.cpp b/cockatrice/src/game/zones/view_zone.cpp index 4ae25989e..d2fd1e971 100644 --- a/cockatrice/src/game/zones/view_zone.cpp +++ b/cockatrice/src/game/zones/view_zone.cpp @@ -73,7 +73,10 @@ void ZoneViewZone::initializeCards(const QList &cardLis for (int i = 0; i < cardList.size(); ++i) { auto card = cardList[i]; 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(); } else if (!qobject_cast(getLogic())->getOriginalZone()->contentsKnown()) { @@ -91,8 +94,10 @@ void ZoneViewZone::initializeCards(const QList &cardLis int number = numberCards == -1 ? c.size() : (numberCards < c.size() ? numberCards : c.size()); for (int i = 0; i < number; i++) { CardItem *card = c.at(i); - getLogic()->addCard(new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId()), false, - i); + auto copy = new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId()); + copy->setFaceDown(card->getFaceDown()); + + getLogic()->addCard(copy, false, i); } reorganizeCards(); } @@ -107,12 +112,15 @@ void ZoneViewZone::zoneDumpReceived(const Response &r) auto cardName = QString::fromStdString(cardInfo.name()); auto cardProviderId = QString::fromStdString(cardInfo.provider_id()); auto card = new CardItem(getLogic()->getPlayer(), this, {cardName, cardProviderId}, cardInfo.id(), getLogic()); + card->setFaceDown(cardInfo.face_down()); getLogic()->rawInsertCard(card, i); } qobject_cast(getLogic())->updateCardIds(ZoneViewZoneLogic::INITIALIZE); 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. @@ -160,8 +168,8 @@ void ZoneViewZone::reorganizeCards() // determine bounding rect qreal aleft = 0; qreal atop = 0; - qreal awidth = gridSize.cols * CARD_WIDTH + (CARD_WIDTH / 2) + HORIZONTAL_PADDING; - qreal aheight = (gridSize.rows * CARD_HEIGHT) / 3 + CARD_HEIGHT * 1.3; + qreal awidth = gridSize.cols * CardDimensions::WIDTH_F + CardDimensions::WIDTH_HALF_F + HORIZONTAL_PADDING; + qreal aheight = (gridSize.rows * CardDimensions::HEIGHT_F) / 3 + CardDimensions::HEIGHT_F * 1.3; optimumRect = QRectF(aleft, atop, awidth, aheight); updateGeometry(); @@ -204,8 +212,8 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca } lastColumnProp = columnProp; - qreal x = col * CARD_WIDTH; - qreal y = row * CARD_HEIGHT / 3; + qreal x = col * CardDimensions::WIDTH_F; + qreal y = row * CardDimensions::HEIGHT_F / 3; c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); c->setRealZValue(i); longestRow = qMax(row, longestRow); @@ -232,8 +240,8 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca for (int i = 0; i < cardCount; i++) { CardItem *c = cards.at(i); - qreal x = (i / rows) * CARD_WIDTH; - qreal y = (i % rows) * CARD_HEIGHT / 3; + qreal x = (i / rows) * CardDimensions::WIDTH_F; + qreal y = (i % rows) * CardDimensions::HEIGHT_F / 3; c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); c->setRealZValue(i); } @@ -279,8 +287,13 @@ void ZoneViewZone::handleDropEvent(const QList &dragItems, cmd.set_y(0); cmd.set_is_reversed(qobject_cast(getLogic())->getIsReversed()); - for (int i = 0; i < dragItems.size(); ++i) - cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId()); + for (int i = 0; i < dragItems.size(); ++i) { + 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); } diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index 3ea4eb119..23d7d6a19 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -7,6 +7,7 @@ #include "../game_scene.h" #include "../player/player.h" #include "../player/player_actions.h" +#include "../z_values.h" #include "view_zone.h" #include @@ -47,7 +48,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, { setAcceptHoverEvents(true); setAttribute(Qt::WA_DeleteOnClose); - setZValue(2000000006); + setZValue(ZValues::ZONE_VIEW_WIDGET); setFlag(ItemIgnoresTransformations); QGraphicsLinearLayout *vbox = new QGraphicsLinearLayout(Qt::Vertical); @@ -71,7 +72,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, QGraphicsProxyWidget *searchEditProxy = new QGraphicsProxyWidget; searchEditProxy->setWidget(&searchEdit); - searchEditProxy->setZValue(2000000007); + searchEditProxy->setZValue(ZValues::DRAG_ITEM); vbox->addItem(searchEditProxy); // top row @@ -80,13 +81,13 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, // groupBy options QGraphicsProxyWidget *groupBySelectorProxy = new QGraphicsProxyWidget; groupBySelectorProxy->setWidget(&groupBySelector); - groupBySelectorProxy->setZValue(2000000008); + groupBySelectorProxy->setZValue(ZValues::TOP_UI); hTopRow->addItem(groupBySelectorProxy); // sortBy options QGraphicsProxyWidget *sortBySelectorProxy = new QGraphicsProxyWidget; sortBySelectorProxy->setWidget(&sortBySelector); - sortBySelectorProxy->setZValue(2000000007); + sortBySelectorProxy->setZValue(ZValues::DRAG_ITEM); hTopRow->addItem(sortBySelectorProxy); vbox->addItem(hTopRow); @@ -457,7 +458,7 @@ void ZoneViewWidget::resizeScrollbar(const qreal newZoneHeight) */ 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 } @@ -573,4 +574,4 @@ void ZoneViewWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) if (event->pos().y() <= 0) { expandWindow(); } -} \ No newline at end of file +} diff --git a/cockatrice/src/interface/deck_loader/deck_loader.cpp b/cockatrice/src/interface/deck_loader/deck_loader.cpp index 3481e245b..d71dca24a 100644 --- a/cockatrice/src/interface/deck_loader/deck_loader.cpp +++ b/cockatrice/src/interface/deck_loader/deck_loader.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -42,7 +43,7 @@ DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fmt, bo DeckList deckList; switch (fmt) { case DeckFileFormat::PlainText: - result = deckList.loadFromFile_Plain(&file); + result = deckList.loadFromFile_Plain(&file, CardNameNormalizer()); break; case DeckFileFormat::Cockatrice: { result = deckList.loadFromFile_Native(&file); @@ -50,7 +51,7 @@ DeckLoader::loadFromFile(const QString &fileName, DeckFileFormat::Format fmt, bo qCInfo(DeckLoaderLog) << "Failed to load " << fileName << "as cockatrice format; retrying as plain format"; file.seek(0); - result = deckList.loadFromFile_Plain(&file); + result = deckList.loadFromFile_Plain(&file, CardNameNormalizer()); fmt = DeckFileFormat::PlainText; } break; diff --git a/cockatrice/src/interface/theme_manager.cpp b/cockatrice/src/interface/theme_manager.cpp index abacb2d68..1dc61fdb7 100644 --- a/cockatrice/src/interface/theme_manager.cpp +++ b/cockatrice/src/interface/theme_manager.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -89,6 +90,7 @@ struct PaletteColorInfo ThemeManager::ThemeManager(QObject *parent) : QObject(parent) { + defaultStyleName = qApp->style()->objectName(); ensureThemeDirectoryExists(); #if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, &ThemeManager::themeChangedSlot); @@ -346,7 +348,7 @@ void ThemeManager::themeChangedSlot() qApp->setStyle(QStyleFactory::create("Fusion")); qApp->setPalette(createDarkGreenFusionPalette()); } else { - qApp->setStyle(""); + qApp->setStyle(defaultStyleName); // setting the style also sets the palette } if (dirPath.isEmpty()) { diff --git a/cockatrice/src/interface/theme_manager.h b/cockatrice/src/interface/theme_manager.h index 594ede2bf..d942f0fef 100644 --- a/cockatrice/src/interface/theme_manager.h +++ b/cockatrice/src/interface/theme_manager.h @@ -40,6 +40,7 @@ public: }; private: + QString defaultStyleName; std::array brushes; QStringMap availableThemes; /* diff --git a/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp b/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp index f8b581e33..4930fbbfd 100644 --- a/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_info_display_widget.cpp @@ -10,7 +10,7 @@ #include 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); pic = new CardInfoPictureWidget(); diff --git a/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.cpp b/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.cpp index 451104d17..e5b0c4872 100644 --- a/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.cpp @@ -8,7 +8,7 @@ CardInfoPictureArtCropWidget::CardInfoPictureArtCropWidget(QWidget *parent) hide(); } -QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &targetSize) +QPixmap CardInfoPictureArtCropWidget::getBackground() { // Load the full-resolution card image, not a pre-scaled one QPixmap fullResPixmap; @@ -17,10 +17,8 @@ QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &target } else { CardPictureLoader::getCardBackPixmap(fullResPixmap, QSize(745, 1040)); } - - // Fail-safe if loading failed if (fullResPixmap.isNull()) { - return QPixmap(targetSize); + return QPixmap(); // return null qpixmap } 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 - // Crop first, then scale for best quality + // return full resolution image crop QPixmap cropped = fullResPixmap.copy(foilRect); - QPixmap scaled = cropped.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); - - return scaled; + return cropped; } diff --git a/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.h b/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.h index aed951cc6..0185f758c 100644 --- a/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.h +++ b/cockatrice/src/interface/widgets/cards/card_info_picture_art_crop_widget.h @@ -16,8 +16,8 @@ class CardInfoPictureArtCropWidget : public CardInfoPictureWidget public: explicit CardInfoPictureArtCropWidget(QWidget *parent = nullptr); - // Returns a processed (cropped & scaled) version of the pixmap - QPixmap getProcessedBackground(const QSize &targetSize); + // Returns a cropped version of the pixmap + QPixmap getBackground(); }; #endif // CARD_INFO_PICTURE_ART_CROP_WIDGET_H diff --git a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp index 8afbfaaa2..8da27b63c 100644 --- a/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp +++ b/cockatrice/src/interface/widgets/deck_editor/deck_state_manager.cpp @@ -320,6 +320,7 @@ void DeckStateManager::undo(int steps) deckListModel->rebuildTree(); emit deckListModel->layoutChanged(); + emit deckReplaced(); } void DeckStateManager::redo(int steps) @@ -338,6 +339,7 @@ void DeckStateManager::redo(int steps) deckListModel->rebuildTree(); emit deckListModel->layoutChanged(); + emit deckReplaced(); } void DeckStateManager::requestHistorySave(const QString &reason) diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp index b72130c81..9ceb35d78 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp @@ -14,6 +14,7 @@ #include #include #include +#include /** * 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); - if (deckList.loadFromStream_Plain(stream, true)) { + if (deckList.loadFromStream_Plain(stream, true, CardNameNormalizer())) { if (loadSetNameAndNumberCheckBox->isChecked()) { deckList.forEachCard(CardNodeFunction::ResolveProviderId()); } else { diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp index da4135246..ba00873d9 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include DlgLoadDeckFromWebsite::DlgLoadDeckFromWebsite(QWidget *parent) : QDialog(parent) @@ -99,7 +100,7 @@ void DlgLoadDeckFromWebsite::accept() // Parse the plain text deck here DeckList deckList; QTextStream stream(&deckText); - deckList.loadFromStream_Plain(stream, false); + deckList.loadFromStream_Plain(stream, false, CardNameNormalizer()); deckList.forEachCard(CardNodeFunction::ResolveProviderId()); deck = deckList; diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h index fe6f0a7e9..1ac98d206 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h +++ b/cockatrice/src/interface/widgets/dialogs/dlg_load_deck_from_website.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/cockatrice/src/interface/widgets/general/home_widget.cpp b/cockatrice/src/interface/widgets/general/home_widget.cpp index 269326257..ea20ef6a0 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.cpp +++ b/cockatrice/src/interface/widgets/general/home_widget.cpp @@ -46,6 +46,8 @@ HomeWidget::HomeWidget(QWidget *parent, TabSupervisor *_tabSupervisor) &HomeWidget::onBackgroundShuffleFrequencyChanged); // Lambda is cleaner to read than overloading this connect(&SettingsCache::instance(), &SettingsCache::homeTabDisplayCardNameChanged, this, [this] { repaint(); }); + connect(&SettingsCache::instance(), &SettingsCache::themeChanged, this, + &HomeWidget::updateButtonsToBackgroundColor); } void HomeWidget::initializeBackgroundFromSource() @@ -58,20 +60,24 @@ void HomeWidget::initializeBackgroundFromSource() auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource()); - cardChangeTimer->stop(); - switch (backgroundSourceType) { case BackgroundSources::Theme: + cardChangeTimer->stop(); background = QPixmap("theme:backgrounds/home"); + backgroundSourceDeck = DeckList(); + backgroundSourceCard->setCard(ExactCard()); updateButtonsToBackgroundColor(); update(); break; case BackgroundSources::RandomCardArt: - cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); + backgroundSourceDeck = DeckList(); + updateRandomCard(); + onBackgroundShuffleFrequencyChanged(); break; case BackgroundSources::DeckFileArt: loadBackgroundSourceDeck(); - cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); + updateRandomCard(); + onBackgroundShuffleFrequencyChanged(); break; } } @@ -83,6 +89,20 @@ void HomeWidget::loadBackgroundSourceDeck() 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() { auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource()); @@ -93,10 +113,7 @@ void HomeWidget::updateRandomCard() case BackgroundSources::Theme: break; case BackgroundSources::RandomCardArt: - do { - newCard = CardDatabaseManager::query()->getRandomCard(); - } while (newCard == backgroundSourceCard->getCard() && - newCard.getCardPtr()->getProperty("layout") != "normal"); + setRandomCard(newCard); break; case BackgroundSources::DeckFileArt: QList cardRefs = backgroundSourceDeck.getCardRefList(); @@ -124,25 +141,20 @@ void HomeWidget::updateRandomCard() connect(newCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &HomeWidget::updateBackgroundProperties); backgroundSourceCard->setCard(newCard); - background = backgroundSourceCard->getProcessedBackground(size()); - - if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() <= 0) { - cardChangeTimer->stop(); - } + background = backgroundSourceCard->getBackground(); } void HomeWidget::onBackgroundShuffleFrequencyChanged() { cardChangeTimer->stop(); - if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() <= 0) { - return; + if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() > 0) { + cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); } - cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000); } void HomeWidget::updateBackgroundProperties() { - background = backgroundSourceCard->getProcessedBackground(size()); + background = backgroundSourceCard->getBackground(); updateButtonsToBackgroundColor(); update(); // Triggers repaint } @@ -300,14 +312,17 @@ void HomeWidget::paintEvent(QPaintEvent *event) QPainter painter(this); 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 - QSize widgetSize = size(); - QSize bgSize = background.size(); - QPoint topLeft((widgetSize.width() - bgSize.width()) / 2, (widgetSize.height() - bgSize.height()) / 2); + // Draw scaled background centered + QSize bgSize = toDraw.size(); + QPoint topLeft((widgetSize.width() - bgSize.width()) / (devicePixelRatio() * 2), // undo scaling for painter + (widgetSize.height() - bgSize.height()) / (devicePixelRatio() * 2)); - painter.drawPixmap(topLeft, background); + painter.drawPixmap(topLeft, toDraw); + } // Draw translucent black overlay with rounded corners QRectF overlayRect(5, 5, width() - 10, height() - 10); @@ -321,8 +336,11 @@ void HomeWidget::paintEvent(QPaintEvent *event) QString cardName; ExactCard card = backgroundSourceCard->getCard(); if (card) { - cardName = card.getCardPtr()->getName() + " (" + card.getPrinting().getSet()->getCorrectedShortName() + ") " + - card.getPrinting().getProperty("num"); + cardName = card.getCardPtr()->getName(); + if (card.getPrinting().getSet() != nullptr) { + cardName += " (" + card.getPrinting().getSet()->getCorrectedShortName() + ") " + + card.getPrinting().getProperty("num"); + } } if (!cardName.isEmpty() && SettingsCache::instance().getHomeTabDisplayCardName()) { diff --git a/cockatrice/src/interface/widgets/general/home_widget.h b/cockatrice/src/interface/widgets/general/home_widget.h index 233fda543..b30bb5407 100644 --- a/cockatrice/src/interface/widgets/general/home_widget.h +++ b/cockatrice/src/interface/widgets/general/home_widget.h @@ -45,6 +45,7 @@ private: QPair gradientColors; HomeStyledButton *connectButton; + void setRandomCard(ExactCard &newCard); void loadBackgroundSourceDeck(); }; diff --git a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp index f40b9094f..8b17cd49e 100644 --- a/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/archidekt/display/archidekt_api_response_deck_display_widget.cpp @@ -2,7 +2,6 @@ #include "../../../../../deck_loader/card_node_function.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/deck_card_zone_display_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 -#include +#include ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWidget *parent, ArchidektApiResponseDeck _response, @@ -80,7 +79,7 @@ ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWi connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset); auto decklist = QSharedPointer(new DeckList); - decklist->loadFromStream_Plain(deckStream, false); + decklist->loadFromStream_Plain(deckStream, false, CardNameNormalizer()); model->setDeckList(decklist); model->forEachCard(CardNodeFunction::ResolveProviderId()); diff --git a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp index a744a9b14..1cb070c30 100644 --- a/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp +++ b/cockatrice/src/interface/widgets/tabs/api/edhrec/api_response/average_deck/edhrec_deck_api_response.cpp @@ -1,11 +1,10 @@ #include "edhrec_deck_api_response.h" -#include "../../../../../../deck_loader/deck_loader.h" - #include #include #include #include +#include void EdhrecDeckApiResponse::fromJson(const QJsonArray &json) { @@ -15,7 +14,7 @@ void EdhrecDeckApiResponse::fromJson(const QJsonArray &json) } QTextStream stream(&deckList); - deck.loadFromStream_Plain(stream, true); + deck.loadFromStream_Plain(stream, true, CardNameNormalizer()); } void EdhrecDeckApiResponse::debugPrint() const diff --git a/libcockatrice_card/CMakeLists.txt b/libcockatrice_card/CMakeLists.txt index f516cde00..dd3799e33 100644 --- a/libcockatrice_card/CMakeLists.txt +++ b/libcockatrice_card/CMakeLists.txt @@ -12,6 +12,7 @@ set(HEADERS libcockatrice/card/database/parser/card_database_parser.h libcockatrice/card/database/parser/cockatrice_xml_3.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/printing_info.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/cockatrice_xml_3.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/printing_info.cpp libcockatrice/card/relation/card_relation.cpp diff --git a/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp new file mode 100644 index 000000000..91ebd6647 --- /dev/null +++ b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp @@ -0,0 +1,66 @@ +#include "card_name_normalizer.h" + +#include "../database/card_database_manager.h" +#include "../printing/exact_card.h" + +#include + +/** + * @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 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; +} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.h b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.h new file mode 100644 index 000000000..716e7da80 --- /dev/null +++ b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.h @@ -0,0 +1,15 @@ +#ifndef COCKATRICE_CARD_NAME_NORMALIZER_H +#define COCKATRICE_CARD_NAME_NORMALIZER_H + +#include + +/** + * 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 diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp index 3d1070f15..e3e7b41c0 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp @@ -199,9 +199,12 @@ bool DeckList::saveToFile_Native(QIODevice *device) const * * @param in The text to load * @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. */ -bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) +bool DeckList::loadFromStream_Plain(QTextStream &in, + bool preserveMetadata, + const std::function &cardNameNormalizer) { const QRegularExpression reCardLine(R"(^\s*[\w\[\(\{].*$)", QRegularExpression::UseUnicodePropertiesOption); const QRegularExpression reEmpty("^\\s*$"); @@ -213,23 +216,11 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) // Regex for advanced card parsing 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 const QRegularExpression reHyphenFormat(R"(\((\w{3,})\)\s+(\w{3,})-(\d+[^\w\s]*))"); const QRegularExpression reRegularFormat(R"(\((\w{3,})\)\s+(\d+[^\w\s]*))"); - const QHash differences{{QRegularExpression("’"), QString("'")}, - {QRegularExpression("Æ"), QString("Ae")}, - {QRegularExpression("æ"), QString("ae")}, - {QRegularExpression(" ?[|/]+ ?"), QString(" // ")}}; - cleanList(preserveMetadata); auto inputs = in.readAll().trimmed().split('\n'); @@ -355,26 +346,8 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) cardName = match.captured(2); } - // 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 names - for (auto diff = differences.constBegin(); diff != differences.constEnd(); ++diff) { - cardName.replace(diff.key(), diff.value()); - } + // Normalize the card name + cardName = cardNameNormalizer(cardName); // Determine the zone (mainboard/sideboard) QString zoneName = sideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; @@ -387,10 +360,10 @@ bool DeckList::loadFromStream_Plain(QTextStream &in, bool preserveMetadata) return true; } -bool DeckList::loadFromFile_Plain(QIODevice *device) +bool DeckList::loadFromFile_Plain(QIODevice *device, const std::function &cardNameNormalizer) { 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 diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h index 91e56fc9f..a96adeb38 100644 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h +++ b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h @@ -206,8 +206,10 @@ public: /// @name Serialization (Plain text) ///@{ - bool loadFromStream_Plain(QTextStream &stream, bool preserveMetadata); - bool loadFromFile_Plain(QIODevice *device); + bool loadFromStream_Plain(QTextStream &stream, + bool preserveMetadata, + const std::function &cardNameNormalizer); + bool loadFromFile_Plain(QIODevice *device, const std::function &cardNameNormalizer); bool saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards) const; bool saveToFile_Plain(QIODevice *device, bool prefixSideboardCards = true, bool slashTappedOutSplitCards = false) const; diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp index 8cbf23be4..e43c612f0 100644 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp @@ -85,6 +85,8 @@ void DeckListModel::rebuildTree() } endResetModel(); + + refreshCardFormatLegalities(); } int DeckListModel::rowCount(const QModelIndex &parent) const @@ -649,7 +651,6 @@ void DeckListModel::setDeckList(const QSharedPointer &_deck) deckList = _deck; } rebuildTree(); - refreshCardFormatLegalities(); emit deckReplaced(); } diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp index b08495bad..f04bcc849 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp @@ -48,6 +48,7 @@ #include #include #include +#include Server_AbstractPlayer::Server_AbstractPlayer(Server_Game *_game, int _playerId, @@ -190,14 +191,43 @@ shouldDestroyOnMove(const Server_Card *card, const Server_CardZone *startZone, c } // Allow tokens on the stack - if ((startZone->getName() == "table" || startZone->getName() == "stack") && - (targetZone->getName() == "table" || targetZone->getName() == "stack")) { + if ((startZone->getName() == ZoneNames::TABLE || startZone->getName() == ZoneNames::STACK) && + (targetZone->getName() == ZoneNames::TABLE || targetZone->getName() == ZoneNames::STACK)) { return false; } 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, Server_CardZone *startzone, const QList &_cards, @@ -235,7 +265,7 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges, } // 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; } @@ -257,10 +287,7 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges, for (auto cardStruct : cardsToMove) { Server_Card *card = cardStruct.card; - const CardToMove *thisCardProperties = cardStruct.cardToMove; int originalPosition = cardStruct.position; - bool faceDown = targetzone->hasCoords() && - (thisCardProperties->has_face_down() ? thisCardProperties->face_down() : card->getFaceDown()); bool sourceBeingLookedAt; int position = startzone->removeCard(card, sourceBeingLookedAt); @@ -315,11 +342,13 @@ Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges, ++xIndex; int newX = isReversed ? targetzone->getCards().size() - xCoord + xIndex : xCoord + xIndex; + bool faceDown = shouldBeFaceDown(cardStruct, startzone, targetzone); + if (targetzone->hasCoords()) { newX = targetzone->getFreeGridColumn(newX, yCoord, card->getName(), faceDown); } else { yCoord = 0; - card->resetState(targetzone->getName() == "stack"); + card->resetState(targetzone->getName() == ZoneNames::STACK); } targetzone->insertCard(card, newX, yCoord); diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp index eae23dacf..2224ddb13 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp @@ -51,6 +51,7 @@ #include #include #include +#include Server_Game::Server_Game(const ServerInfo_User &_creatorInfo, int _gameId, @@ -832,7 +833,7 @@ void Server_Game::returnCardsFromPlayer(GameEventStorage &ges, Server_AbstractPl QMutexLocker locker(&gameMutex); // Return cards to their rightful owners before conceding the game 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()) { if (card == nullptr) { continue; @@ -858,7 +859,7 @@ void Server_Game::returnCardsFromPlayer(GameEventStorage &ges, Server_AbstractPl continue; } - const auto &targetZone = otherPlayer->getZones().value("table"); + const auto &targetZone = otherPlayer->getZones().value(ZoneNames::TABLE); if (playerTable == nullptr || targetZone == nullptr) { continue; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp index e62f861a9..1175e4b57 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp @@ -47,6 +47,7 @@ #include #include #include +#include Server_Player::Server_Player(Server_Game *_game, int _playerId, @@ -80,15 +81,15 @@ void Server_Player::setupZones() // ------------------------------------------------------------------ // 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); - 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(new Server_CardZone(this, "table", true, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, "hand", false, ServerInfo_Zone::PrivateZone)); - addZone(new Server_CardZone(this, "stack", false, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, "grave", false, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, "rfg", false, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, ZoneNames::TABLE, true, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, ZoneNames::HAND, false, ServerInfo_Zone::PrivateZone)); + addZone(new Server_CardZone(this, ZoneNames::STACK, false, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, ZoneNames::GRAVE, 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(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) { - Server_CardZone *deckZone = zones.value("deck"); - Server_CardZone *handZone = zones.value("hand"); + Server_CardZone *deckZone = zones.value(ZoneNames::DECK); + Server_CardZone *handZone = zones.value(ZoneNames::HAND); if (deckZone->getCards().size() < number) { 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 // 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.) - if (startzone->getName() == "hand" && targetzone->getName() != "hand" && !undoingDraw) { + if (startzone->getName() == ZoneNames::HAND && targetzone->getName() != ZoneNames::HAND && !undoingDraw) { int index = lastDrawList.lastIndexOf(card->getId()); if (index != -1) { lastDrawList.erase(lastDrawList.begin(), lastDrawList.begin() + index); @@ -326,11 +327,11 @@ Server_Player::cmdShuffle(const Command_Shuffle &cmd, ResponseContainer & /*rc*/ 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; } - Server_CardZone *zone = zones.value("deck"); + Server_CardZone *zone = zones.value(ZoneNames::DECK); if (!zone) { return Response::RespNameNotFound; } @@ -357,8 +358,8 @@ Server_Player::cmdMulligan(const Command_Mulligan &cmd, ResponseContainer & /*rc return Response::RespContextError; } - Server_CardZone *hand = zones.value("hand"); - Server_CardZone *_deck = zones.value("deck"); + Server_CardZone *hand = zones.value(ZoneNames::HAND); + Server_CardZone *_deck = zones.value(ZoneNames::DECK); int number = cmd.number(); if (!hand->getCards().isEmpty()) { @@ -414,8 +415,8 @@ Server_Player::cmdUndoDraw(const Command_UndoDraw & /*cmd*/, ResponseContainer & Response::ResponseCode retVal; auto *cardToMove = new CardToMove; cardToMove->set_card_id(lastDrawList.takeLast()); - retVal = moveCard(ges, zones.value("hand"), QList() << cardToMove, zones.value("deck"), 0, 0, - false, true); + retVal = moveCard(ges, zones.value(ZoneNames::HAND), QList() << cardToMove, + zones.value(ZoneNames::DECK), 0, 0, false, true); delete cardToMove; return retVal; diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_move_card.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_move_card.proto index 6e9301d27..a5b96da2e 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_move_card.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/command_move_card.proto @@ -6,7 +6,9 @@ message CardToMove { // Id of the card in its current zone 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; // When moving add this value to the power/toughness field of the card diff --git a/libcockatrice_utility/CMakeLists.txt b/libcockatrice_utility/CMakeLists.txt index 0575c260f..c0c7d8cc9 100644 --- a/libcockatrice_utility/CMakeLists.txt +++ b/libcockatrice_utility/CMakeLists.txt @@ -10,8 +10,13 @@ set(UTILITY_SOURCES libcockatrice/utility/expression.cpp libcockatrice/utility/l ) set(UTILITY_HEADERS - libcockatrice/utility/color.h libcockatrice/utility/expression.h libcockatrice/utility/levenshtein.h - libcockatrice/utility/macros.h libcockatrice/utility/passwordhasher.h libcockatrice/utility/trice_limits.h + libcockatrice/utility/color.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}) diff --git a/libcockatrice_utility/libcockatrice/utility/zone_names.h b/libcockatrice_utility/libcockatrice/utility/zone_names.h new file mode 100644 index 000000000..d1463de6a --- /dev/null +++ b/libcockatrice_utility/libcockatrice/utility/zone_names.h @@ -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 diff --git a/tests/loading_from_clipboard/CMakeLists.txt b/tests/loading_from_clipboard/CMakeLists.txt index 40e85c66e..85133a8e8 100644 --- a/tests/loading_from_clipboard/CMakeLists.txt +++ b/tests/loading_from_clipboard/CMakeLists.txt @@ -1,5 +1,5 @@ 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) 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( - 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) diff --git a/tests/loading_from_clipboard/clipboard_testing.cpp b/tests/loading_from_clipboard/clipboard_testing.cpp index 29535c1dc..a9977b800 100644 --- a/tests/loading_from_clipboard/clipboard_testing.cpp +++ b/tests/loading_from_clipboard/clipboard_testing.cpp @@ -1,6 +1,7 @@ #include "clipboard_testing.h" #include +#include #include DeckList getDeckList(const QString &clipboard) @@ -8,7 +9,7 @@ DeckList getDeckList(const QString &clipboard) DeckList deckList; QString cp(clipboard); QTextStream stream(&cp); // text stream requires local copy - deckList.loadFromStream_Plain(stream, false); + deckList.loadFromStream_Plain(stream, false, CardNameNormalizer()); return deckList; }