mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-17 02:27:20 -05:00
Compare commits
99 Commits
2026-02-22
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87c4216e80 | ||
|
|
832f70496a | ||
|
|
f97864b72b | ||
|
|
338c56678a | ||
|
|
8cc65b8967 | ||
|
|
2e10b2f5d5 | ||
|
|
92fe406c22 | ||
|
|
d677e2bb70 | ||
|
|
e977f123ce | ||
|
|
f56b672307 | ||
|
|
29c1d7f3e4 | ||
|
|
36aba81b1b | ||
|
|
f9fb03b26b | ||
|
|
d2732ac742 | ||
|
|
9aa5702e14 | ||
|
|
2d412bfe52 | ||
|
|
ac06fb9d1c | ||
|
|
d7b31f2f9d | ||
|
|
635fae101d | ||
|
|
335022c4aa | ||
|
|
dbed6890da | ||
|
|
3ec9ae9772 | ||
|
|
a46ab5cd68 | ||
|
|
690a00aa6c | ||
|
|
ba786a0289 | ||
|
|
9dfac77ba2 | ||
|
|
f3370a4f52 | ||
|
|
7caa88bc58 | ||
|
|
34a5b8b9ce | ||
|
|
d8e3807ec5 | ||
|
|
42bd8164a0 | ||
|
|
abf6e72ad1 | ||
|
|
74cce5ccb2 | ||
|
|
dd053c76df | ||
|
|
5ef428b9d0 | ||
|
|
94ea574c76 | ||
|
|
70b41c2095 | ||
|
|
aa85a39d6a | ||
|
|
51c684251f | ||
|
|
fa2934373c | ||
|
|
414567f8b6 | ||
|
|
bc219191db | ||
|
|
652c8464a7 | ||
|
|
067fe9b534 | ||
|
|
38c85e6db1 | ||
|
|
c5cd7d8700 | ||
|
|
fc453c68a7 | ||
|
|
69c046cca4 | ||
|
|
dd8164611b | ||
|
|
b4a5c863d7 | ||
|
|
4a3d79d00b | ||
|
|
96f436b65e | ||
|
|
cf01dc770b | ||
|
|
ce652de272 | ||
|
|
9bb399606c | ||
|
|
8180d2e3b0 | ||
|
|
33d5721490 | ||
|
|
2b2a6db081 | ||
|
|
aa4592dc9e | ||
|
|
20ad9af989 | ||
|
|
9e2276a59f | ||
|
|
42cec10457 | ||
|
|
95d7e027fc | ||
|
|
8268311fab | ||
|
|
413b4b637b | ||
|
|
e79bbc67b9 | ||
|
|
15a1d5440b | ||
|
|
fd293444c5 | ||
|
|
852429f248 | ||
|
|
4606fcdbd5 | ||
|
|
c375cdbb1a | ||
|
|
f15b70e4ae | ||
|
|
2f10634ca2 | ||
|
|
dead993639 | ||
|
|
bd5cbb89d4 | ||
|
|
14f1925edc | ||
|
|
e39bbd2b31 | ||
|
|
04f06206b7 | ||
|
|
1bcea27a44 | ||
|
|
e7a3ad86eb | ||
|
|
b36ab66583 | ||
|
|
566c876bdc | ||
|
|
43acac5f5d | ||
|
|
846ecb7e8d | ||
|
|
2fba5dcd20 | ||
|
|
2828854d32 | ||
|
|
9794893b63 | ||
|
|
e57ee8e9c9 | ||
|
|
f978407a19 | ||
|
|
59ce70afc5 | ||
|
|
208ccc3a1a | ||
|
|
6f8f9f016a | ||
|
|
7ad2481e3d | ||
|
|
12c667afd7 | ||
|
|
2cb16c9fd0 | ||
|
|
9f00c6f955 | ||
|
|
a90997353b | ||
|
|
c6dc7eee64 | ||
|
|
0f2899b5c7 |
|
|
@ -9,20 +9,18 @@ RUN apt-get update && \
|
|||
file \
|
||||
g++ \
|
||||
git \
|
||||
libgl-dev \
|
||||
liblzma-dev \
|
||||
libmariadb-dev-compat \
|
||||
libprotobuf-dev \
|
||||
libqt6multimedia6 \
|
||||
libqt6sql6-mysql \
|
||||
libqt6svg6-dev \
|
||||
libqt6websockets6-dev \
|
||||
libqt5multimedia5-plugins \
|
||||
libqt5sql5-mysql \
|
||||
libqt5svg5-dev \
|
||||
libqt5websockets5-dev \
|
||||
ninja-build \
|
||||
protobuf-compiler \
|
||||
qt6-image-formats-plugins \
|
||||
qt6-l10n-tools \
|
||||
qt6-multimedia-dev \
|
||||
qt6-tools-dev \
|
||||
qt6-tools-dev-tools \
|
||||
qt5-image-formats-plugins \
|
||||
qtmultimedia5-dev \
|
||||
qttools5-dev \
|
||||
qttools5-dev-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
|
|
|||
29
.ci/Ubuntu26.04/Dockerfile
Normal file
29
.ci/Ubuntu26.04/Dockerfile
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
FROM ubuntu:26.04
|
||||
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
ccache \
|
||||
clang-format \
|
||||
cmake \
|
||||
file \
|
||||
g++ \
|
||||
git \
|
||||
libgl-dev \
|
||||
liblzma-dev \
|
||||
libmariadb-dev-compat \
|
||||
libprotobuf-dev \
|
||||
libqt6multimedia6 \
|
||||
libqt6sql6-mysql \
|
||||
ninja-build \
|
||||
protobuf-compiler \
|
||||
qt6-image-formats-plugins \
|
||||
qt6-l10n-tools \
|
||||
qt6-multimedia-dev \
|
||||
qt6-svg-dev \
|
||||
qt6-tools-dev \
|
||||
qt6-tools-dev-tools \
|
||||
qt6-websockets-dev \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
|
@ -10,9 +10,11 @@
|
|||
# --test runs tests
|
||||
# --debug or --release sets the build type ie CMAKE_BUILD_TYPE
|
||||
# --ccache [<size>] uses ccache and shows stats, optionally provide size
|
||||
# --evict-ccache <age> runs ccache eviction based on given age after build
|
||||
# --dir <dir> sets the name of the build dir, default is "build"
|
||||
# --cmake-generator <generator> sets CMAKE_GENERATOR as used by cmake
|
||||
# --target-macos-version <version> sets the min os version - only used for macOS builds
|
||||
# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_NO_CLIENT MAKE_TEST USE_CCACHE CCACHE_SIZE BUILD_DIR CMAKE_GENERATOR TARGET_MACOS_VERSION
|
||||
# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_NO_CLIENT MAKE_TEST USE_CCACHE CCACHE_SIZE CCACHE_EVICTION_AGE BUILD_DIR CMAKE_GENERATOR TARGET_MACOS_VERSION
|
||||
# (correspond to args: --debug/--release --install --package <package type> --suffix <suffix> --server --test --ccache <ccache_size> --dir <dir>)
|
||||
# exitcode: 1 for failure, 3 for invalid arguments
|
||||
|
||||
|
|
@ -71,6 +73,15 @@ while [[ $# != 0 ]]; do
|
|||
shift
|
||||
fi
|
||||
;;
|
||||
'--evict-ccache')
|
||||
shift
|
||||
if [[ $# == 0 ]]; then
|
||||
echo "::error file=$0::--evict-ccache expects an argument"
|
||||
exit 3
|
||||
fi
|
||||
CCACHE_EVICTION_AGE=$1
|
||||
shift
|
||||
;;
|
||||
'--vcpkg')
|
||||
USE_VCPKG=1
|
||||
shift
|
||||
|
|
@ -84,6 +95,15 @@ while [[ $# != 0 ]]; do
|
|||
BUILD_DIR="$1"
|
||||
shift
|
||||
;;
|
||||
'--cmake-generator')
|
||||
shift
|
||||
if [[ $# == 0 ]]; then
|
||||
echo "::error file=$0::--cmake-generator expects an argument"
|
||||
exit 3
|
||||
fi
|
||||
export CMAKE_GENERATOR=$1
|
||||
shift
|
||||
;;
|
||||
'--target-macos-version')
|
||||
shift
|
||||
if [[ $# == 0 ]]; then
|
||||
|
|
@ -251,9 +271,16 @@ cmake --build . "${buildflags[@]}"
|
|||
echo "::endgroup::"
|
||||
|
||||
if [[ $USE_CCACHE ]]; then
|
||||
if [[ $CCACHE_EVICTION_AGE ]]; then
|
||||
echo "::group::evict ccache files older than $CCACHE_EVICTION_AGE"
|
||||
ccache --evict-older-than "$CCACHE_EVICTION_AGE"
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
echo "::group::Show ccache stats again"
|
||||
ccachestatsverbose
|
||||
echo "::endgroup::"
|
||||
elif [[ $CCACHE_EVICTION_AGE ]]; then
|
||||
echo "::error file=$0::ccache eviction is enabled while ccache is disabled!"
|
||||
fi
|
||||
|
||||
if [[ $RUNNER_OS == macOS ]]; then
|
||||
|
|
|
|||
|
|
@ -3,17 +3,28 @@
|
|||
# This script is to be used by the ci environment from the project root directory, do not use it from somewhere else.
|
||||
|
||||
# Creates or loads docker images to use in compilation, creates RUN function to start compilation on the docker image.
|
||||
# <arg> sets the name of the docker image, these correspond to directories in .ci
|
||||
#
|
||||
# usage: source <script> <name> [--get] [--build] [--save] [--interactive] [--set-cache <location>]
|
||||
# <name> sets the name of the docker image, these correspond to directories in .ci
|
||||
# --get loads the image from a previously saved image cache, will build if no image is found
|
||||
# --build builds the image from the Dockerfile in .ci/$NAME
|
||||
# --save stores the image, if an image was loaded it will not be stored
|
||||
# --interactive immediately starts the image interactively for debugging
|
||||
# --set-cache <location> sets the location to cache the image or for ccache
|
||||
#
|
||||
# requires: docker
|
||||
# uses env: NAME CACHE BUILD GET SAVE INTERACTIVE
|
||||
# (correspond to args: <name> --set-cache <cache> --build --get --save --interactive)
|
||||
# sets env: RUN CCACHE_DIR IMAGE_NAME RUN_ARGS RUN_OPTS BUILD_SCRIPT
|
||||
# exitcode: 1 for failure, 2 for missing dockerfile, 3 for invalid arguments
|
||||
#
|
||||
# exported RUN function will run the BUILD_SCRIPT inside of the docker container.
|
||||
# note that the docker container will not inherit any environment variables!
|
||||
#
|
||||
# usage: RUN [arguments for build script]
|
||||
# roughly equivalent to `docker run $IMAGE_NAME bash $BUILD_SCRIPT $@`
|
||||
# uses env: CCACHE_DIR IMAGE_NAME RUN_ARGS RUN_OPTS BUILD_SCRIPT
|
||||
# exitcode: 3 for invalid arguments, returns the returncode of docker run
|
||||
export BUILD_SCRIPT=".ci/compile.sh"
|
||||
|
||||
project_name="cockatrice"
|
||||
|
|
@ -41,12 +52,17 @@ while [[ $# != 0 ]]; do
|
|||
shift
|
||||
;;
|
||||
'--set-cache')
|
||||
CACHE=$2
|
||||
shift
|
||||
if [[ $# == 0 ]]; then
|
||||
echo "--set-cache expects an argument" >&2
|
||||
exit 3
|
||||
fi
|
||||
CACHE=$1
|
||||
shift
|
||||
if ! [[ -d $CACHE ]]; then
|
||||
echo "could not find cache path: $CACHE" >&2
|
||||
return 3
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
if [[ ${1:0:1} == - ]]; then
|
||||
|
|
@ -149,10 +165,11 @@ function RUN ()
|
|||
args+=(--mount "type=bind,source=$CCACHE_DIR,target=/.ccache")
|
||||
args+=(--env "CCACHE_DIR=/.ccache")
|
||||
fi
|
||||
if [[ -n "$CMAKE_GENERATOR" ]]; then
|
||||
args+=(--env "CMAKE_GENERATOR=$CMAKE_GENERATOR")
|
||||
if [[ $GITHUB_OUTPUT ]]; then
|
||||
args+=(--mount "type=bind,source=$GITHUB_OUTPUT,target=/gh_output")
|
||||
args+=(--env "GITHUB_OUTPUT=/gh_output")
|
||||
fi
|
||||
# shellcheck disable=2086
|
||||
# shellcheck disable=2086
|
||||
docker run "${args[@]}" $RUN_ARGS "$IMAGE_NAME" bash "$BUILD_SCRIPT" $RUN_OPTS "$@"
|
||||
return $?
|
||||
else
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ Available pre-compiled binaries for installation:
|
|||
|
||||
<b>Windows</b>
|
||||
• <kbd>Windows 10+</kbd>
|
||||
• <kbd>Windows 7+</kbd>
|
||||
|
||||
<b>macOS</b>
|
||||
• <kbd>macOS 15+</kbd> <sub><i>Sequoia</i></sub> <sub>Apple M</sub>
|
||||
|
|
@ -18,6 +17,7 @@ Available pre-compiled binaries for installation:
|
|||
• <kbd>macOS 13+</kbd> <sub><i>Ventura</i></sub> <sub>Intel</sub>
|
||||
|
||||
<b>Linux</b>
|
||||
• <kbd>Ubuntu 26.04 LTS</kbd> <sub><i>Resolute Racoon</i></sub>
|
||||
• <kbd>Ubuntu 24.04 LTS</kbd> <sub><i>Noble Numbat</i></sub>
|
||||
• <kbd>Ubuntu 22.04 LTS</kbd> <sub><i>Jammy Jellyfish</i></sub>
|
||||
• <kbd>Debian 13</kbd> <sub><i>Trixie</i></sub>
|
||||
|
|
@ -28,6 +28,8 @@ Available pre-compiled binaries for installation:
|
|||
|
||||
<sub>We are also packaged in <kbd>Arch Linux</kbd>'s <a href="https://archlinux.org/packages/extra/x86_64/cockatrice">official extra repository</a>, courtesy of @FFY00.</sub>
|
||||
<sub>General Linux support is available via a <kbd>flatpak</kbd> package at <a href="https://flathub.org/apps/io.github.Cockatrice.cockatrice">Flathub</a>!</sub>
|
||||
|
||||
<sub>We provide a <kbd>Docker</kbd> image for "Servatrice" in <a href="https://github.com/Cockatrice/Cockatrice/pkgs/container/servatrice">GHCR</a>. You can docker pull it or use our Docker Compose files!</sub>
|
||||
</pre>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,16 @@ if ! hash aqt; then
|
|||
fi
|
||||
|
||||
# Resolve latest patch
|
||||
if ! qt_resolved=$(aqt list-qt mac desktop --spec "$qt_spec" --latest-version); then
|
||||
if [[ $RUNNER_OS == macOS ]]; then
|
||||
if ! qt_resolved=$(aqt list-qt mac desktop --spec "$qt_spec" --latest-version); then
|
||||
exit 1
|
||||
fi
|
||||
elif [[ $RUNNER_OS == Windows ]]; then
|
||||
if ! qt_resolved=$(aqt list-qt windows desktop --spec "$qt_spec" --latest-version); then
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "aqt command for $RUNNER_OS not defined."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
|
|||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,9 +1,12 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💬 Discord Community (Get help with server issues, e.g. Login)
|
||||
url: https://discord.gg/3Z9yzmA
|
||||
url: https://discord.com/invite/3Z9yzmA
|
||||
about: Need help with using the client? Want to find some games? Try the Discord server!
|
||||
- name: 🌐 Translations (Help improve the localization of the app)
|
||||
url: https://explore.transifex.com/cockatrice/cockatrice/
|
||||
# it is not possible to add a link to the wiki to this description
|
||||
about: For more information and guidance check our Translation FAQ on our wiki!
|
||||
- name: 📖 Code Documentation
|
||||
url: https://cockatrice.github.io/docs/
|
||||
about: Helpful source focusing on developers, but there are also references for users!
|
||||
|
|
|
|||
24
.github/dependabot.yml
vendored
24
.github/dependabot.yml
vendored
|
|
@ -2,19 +2,21 @@
|
|||
|
||||
version: 2
|
||||
updates:
|
||||
# # Enable version updates for git submodules
|
||||
# Not yet possible to bump only on tags or releases, see:
|
||||
# Enable version updates for git submodules
|
||||
# If SemVer is used, updates will happen to new releases only (not HEAD)
|
||||
# https://github.com/dependabot/dependabot-core/issues/1639
|
||||
# https://github.com/dependabot/dependabot-core/issues/2192
|
||||
# Alternative: Action that updates submodule and can be manually run on demand (workflow_dispatch)
|
||||
# - package-ecosystem: "gitsubmodule"
|
||||
# # Look for `.gitmodules` in the `root` directory
|
||||
# directory: "/"
|
||||
# # Check for updates once a month
|
||||
# schedule:
|
||||
# interval: "monthly"
|
||||
# # Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
|
||||
# open-pull-requests-limit: 1
|
||||
- package-ecosystem: "gitsubmodule"
|
||||
# Look for `.gitmodules` in the `root` directory
|
||||
directory: "/"
|
||||
ignore:
|
||||
# Ignore updates for vcpkg (Bump to latest tag not working (no SemVer used) & macOS Intel triplet broken with newer releases)
|
||||
- dependency-name: "vcpkg"
|
||||
# Check for updates once a month
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
# Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
|
||||
open-pull-requests-limit: 2
|
||||
|
||||
# # Enable version updates for Docker
|
||||
# Not yet possible to bump from one LTS version to the next and skip others, see:
|
||||
|
|
|
|||
157
.github/workflows/desktop-build.yml
vendored
157
.github/workflows/desktop-build.yml
vendored
|
|
@ -38,10 +38,10 @@ on:
|
|||
- 'vcpkg.json'
|
||||
- 'vcpkg'
|
||||
|
||||
# Cancel earlier, unfinished runs of this workflow on the same branch (unless on master)
|
||||
# Cancel earlier, unfinished runs of this workflow on the same branch (unless on release)
|
||||
concurrency:
|
||||
group: "${{ github.workflow }} @ ${{ github.ref_name }}"
|
||||
cancel-in-progress: ${{ github.ref_name != 'master' }}
|
||||
cancel-in-progress: ${{ github.ref_type != 'tag' }}
|
||||
|
||||
jobs:
|
||||
configure:
|
||||
|
|
@ -142,21 +142,28 @@ jobs:
|
|||
- distro: Ubuntu
|
||||
version: 22.04
|
||||
package: DEB
|
||||
test: skip # Running tests on all distros is superfluous
|
||||
|
||||
- distro: Ubuntu
|
||||
version: 24.04
|
||||
package: DEB
|
||||
|
||||
- distro: Ubuntu
|
||||
version: 26.04
|
||||
package: DEB
|
||||
|
||||
name: ${{matrix.distro}} ${{matrix.version}}
|
||||
needs: configure
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: ${{matrix.allow-failure == 'yes'}}
|
||||
timeout-minutes: 70
|
||||
env:
|
||||
NAME: ${{matrix.distro}}${{matrix.version}}
|
||||
CACHE: ${{github.workspace}}/.cache/${{matrix.distro}}${{matrix.version}} # directory for caching docker image and ccache
|
||||
# Cache size over the entire repo is 10Gi:
|
||||
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
|
||||
CCACHE_SIZE: 500M
|
||||
CCACHE_SIZE: 550M
|
||||
CCACHE_EVICTION_AGE: 7d
|
||||
CMAKE_GENERATOR: 'Ninja'
|
||||
|
||||
steps:
|
||||
|
|
@ -180,29 +187,45 @@ jobs:
|
|||
- name: Build debug and test
|
||||
if: matrix.test != 'skip'
|
||||
shell: bash
|
||||
env:
|
||||
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
|
||||
run: |
|
||||
source .ci/docker.sh
|
||||
RUN --server --debug --test --ccache "$CCACHE_SIZE"
|
||||
RUN --server --debug --test --ccache "$CCACHE_SIZE" \
|
||||
--cmake-generator "$CMAKE_GENERATOR"
|
||||
|
||||
- name: Build release package
|
||||
id: build
|
||||
if: matrix.package != 'skip'
|
||||
shell: bash
|
||||
env:
|
||||
BUILD_DIR: build
|
||||
SUFFIX: '-${{matrix.distro}}${{matrix.version}}'
|
||||
package: '${{matrix.package}}'
|
||||
CMAKE_GENERATOR: '${{env.CMAKE_GENERATOR}}'
|
||||
NO_CLIENT: ${{matrix.server_only == 'yes' && '--no-client' || '' }}
|
||||
server_only: '${{matrix.server_only}}'
|
||||
run: |
|
||||
source .ci/docker.sh
|
||||
RUN --server --release --package "$package" --dir "$BUILD_DIR" \
|
||||
--ccache "$CCACHE_SIZE" $NO_CLIENT
|
||||
.ci/name_build.sh
|
||||
args=()
|
||||
if [[ $server_only == yes ]]; then
|
||||
args+=(--no-client)
|
||||
fi
|
||||
if [[ $GITHUB_REF == "refs/heads/master" ]]; then
|
||||
args+=(--evict-ccache "$CCACHE_EVICTION_AGE")
|
||||
fi
|
||||
args+=(--ccache "$CCACHE_SIZE")
|
||||
args+=(--cmake-generator "$CMAKE_GENERATOR")
|
||||
args+=(--suffix "$SUFFIX")
|
||||
RUN --server --release --package "$package" "${args[@]}"
|
||||
|
||||
- name: Save compiler cache (ccache)
|
||||
# Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342
|
||||
- name: Delete remote compiler cache (ccache)
|
||||
if: github.ref == 'refs/heads/master' && steps.ccache_restore.outputs.cache-hit
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
if gh cache delete --repo ${{ github.repository }} ${{ steps.ccache_restore.outputs.cache-primary-key }}; then
|
||||
echo "Cache deleted successfully"
|
||||
fi
|
||||
|
||||
- name: Save updated compiler cache (ccache)
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
|
|
@ -212,29 +235,28 @@ jobs:
|
|||
- name: Upload artifact
|
||||
id: upload_artifact
|
||||
if: matrix.package != 'skip'
|
||||
uses: actions/upload-artifact@v6
|
||||
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@v3
|
||||
uses: actions/attest@v4
|
||||
with:
|
||||
subject-name: ${{steps.build.outputs.name}}
|
||||
subject-path: ${{steps.build.outputs.path}}
|
||||
show-summary: false
|
||||
|
||||
|
|
@ -259,12 +281,12 @@ jobs:
|
|||
override_target: 13
|
||||
make_package: 1
|
||||
package_suffix: "-macOS13_Intel"
|
||||
artifact_name: macOS13_Intel-package
|
||||
qt_version: 6.10.*
|
||||
qt_version: 6.11.*
|
||||
qt_arch: clang_64
|
||||
qt_modules: qtimageformats qtmultimedia qtwebsockets
|
||||
cmake_generator: Ninja
|
||||
use_ccache: 1
|
||||
ccache_eviction_age: 7d
|
||||
|
||||
- os: macOS
|
||||
target: 14
|
||||
|
|
@ -274,12 +296,12 @@ jobs:
|
|||
type: Release
|
||||
make_package: 1
|
||||
package_suffix: "-macOS14"
|
||||
artifact_name: macOS14-package
|
||||
qt_version: 6.10.*
|
||||
qt_version: 6.11.*
|
||||
qt_arch: clang_64
|
||||
qt_modules: qtimageformats qtmultimedia qtwebsockets
|
||||
cmake_generator: Ninja
|
||||
use_ccache: 1
|
||||
ccache_eviction_age: 7d
|
||||
|
||||
- os: macOS
|
||||
target: 15
|
||||
|
|
@ -289,12 +311,12 @@ jobs:
|
|||
type: Release
|
||||
make_package: 1
|
||||
package_suffix: "-macOS15"
|
||||
artifact_name: macOS15-package
|
||||
qt_version: 6.10.*
|
||||
qt_version: 6.11.*
|
||||
qt_arch: clang_64
|
||||
qt_modules: qtimageformats qtmultimedia qtwebsockets
|
||||
cmake_generator: Ninja
|
||||
use_ccache: 1
|
||||
ccache_eviction_age: 7d
|
||||
|
||||
- os: macOS
|
||||
target: 15
|
||||
|
|
@ -302,32 +324,20 @@ jobs:
|
|||
soc: Apple
|
||||
xcode: "16.4"
|
||||
type: Debug
|
||||
qt_version: 6.10.*
|
||||
qt_version: 6.11.*
|
||||
qt_arch: clang_64
|
||||
qt_modules: qtimageformats qtmultimedia qtwebsockets
|
||||
cmake_generator: Ninja
|
||||
use_ccache: 1
|
||||
|
||||
- os: Windows
|
||||
target: 7
|
||||
runner: windows-2022
|
||||
type: Release
|
||||
make_package: 1
|
||||
package_suffix: "-Win7"
|
||||
artifact_name: Windows7-installer
|
||||
qt_version: 5.15.*
|
||||
qt_arch: win64_msvc2019_64
|
||||
cmake_generator: "Visual Studio 17 2022"
|
||||
cmake_generator_platform: x64
|
||||
ccache_eviction_age: 7d
|
||||
|
||||
- os: Windows
|
||||
target: 10
|
||||
runner: windows-2022
|
||||
runner: windows-2025
|
||||
type: Release
|
||||
make_package: 1
|
||||
package_suffix: "-Win10"
|
||||
artifact_name: Windows10-installer
|
||||
qt_version: 6.10.*
|
||||
qt_version: 6.11.*
|
||||
qt_arch: win64_msvc2022_64
|
||||
qt_modules: qtimageformats qtmultimedia qtwebsockets
|
||||
cmake_generator: "Visual Studio 17 2022"
|
||||
|
|
@ -336,11 +346,12 @@ jobs:
|
|||
name: ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
|
||||
needs: configure
|
||||
runs-on: ${{matrix.runner}}
|
||||
timeout-minutes: 100
|
||||
env:
|
||||
CCACHE_DIR: ${{github.workspace}}/.cache/
|
||||
# Cache size over the entire repo is 10Gi:
|
||||
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy
|
||||
CCACHE_SIZE: 500M
|
||||
CCACHE_SIZE: 550M
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
@ -351,7 +362,7 @@ jobs:
|
|||
- name: Add msbuild to PATH
|
||||
if: matrix.os == 'Windows'
|
||||
id: add-msbuild
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
uses: microsoft/setup-msbuild@v3
|
||||
with:
|
||||
msbuild-architecture: x64
|
||||
|
||||
|
|
@ -371,15 +382,12 @@ jobs:
|
|||
restore-keys: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}-
|
||||
|
||||
- name: Install aqtinstall
|
||||
if: matrix.os == 'macOS'
|
||||
run: pipx install aqtinstall
|
||||
|
||||
# Checking if there's a newer, uncached version of Qt available to install via aqtinstall
|
||||
# Resolve given wildcard versions (e.g. Qt 6.6.*) to latest version via aqtinstall to avoid stale caches on new releases
|
||||
- name: Resolve latest Qt patch version
|
||||
if: matrix.os == 'macOS'
|
||||
id: resolve_qt_version
|
||||
shell: bash
|
||||
# Ouputs the version of Qt to install via aqtinstall
|
||||
run: .ci/resolve_latest_aqt_qt_version.sh "${{matrix.qt_version}}"
|
||||
|
||||
- name: Restore thin Qt ${{ steps.resolve_qt_version.outputs.version }} libraries (${{ matrix.soc }} macOS)
|
||||
|
|
@ -396,10 +404,10 @@ jobs:
|
|||
if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true'
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
cache: false
|
||||
version: ${{ steps.resolve_qt_version.outputs.version }}
|
||||
arch: ${{matrix.qt_arch}}
|
||||
modules: ${{matrix.qt_modules}}
|
||||
cache: false
|
||||
dir: ${{github.workspace}}
|
||||
|
||||
- name: Thin Qt libraries (${{ matrix.soc }} macOS)
|
||||
|
|
@ -417,11 +425,18 @@ jobs:
|
|||
if: matrix.os == 'Windows'
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: ${{matrix.qt_version}}
|
||||
# qt 6.11.0 only works with aqtinstall directly from git until aqtinstall 3.4 is released
|
||||
aqtsource: git+https://github.com/miurahr/aqtinstall.git
|
||||
version: ${{ steps.resolve_qt_version.outputs.version }}
|
||||
arch: ${{matrix.qt_arch}}
|
||||
modules: ${{matrix.qt_modules}}
|
||||
cache: true
|
||||
|
||||
- name: Install NSIS
|
||||
if: matrix.os == 'Windows'
|
||||
shell: bash
|
||||
run: choco install nsis
|
||||
|
||||
- name: Setup vcpkg cache
|
||||
id: vcpkg-cache
|
||||
uses: TAServers/vcpkg-cache@v3
|
||||
|
|
@ -448,19 +463,30 @@ jobs:
|
|||
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
|
||||
DEVELOPER_DIR: '/Applications/Xcode_${{matrix.xcode}}.app/Contents/Developer'
|
||||
TARGET_MACOS_VERSION: ${{ matrix.override_target }}
|
||||
CCACHE_EVICTION_AGE: ${{ matrix.ccache_eviction_age }}
|
||||
run: .ci/compile.sh --server --test --vcpkg
|
||||
|
||||
- name: Save compiler cache (ccache)
|
||||
# Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342
|
||||
- name: Delete remote compiler cache (ccache)
|
||||
if: github.ref == 'refs/heads/master' && matrix.use_ccache == 1 && steps.ccache_restore.outputs.cache-hit
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
if gh cache delete --repo ${{ github.repository }} ${{ steps.ccache_restore.outputs.cache-primary-key }}; then
|
||||
echo "Cache deleted successfully"
|
||||
fi
|
||||
|
||||
- name: Save updated compiler cache (ccache)
|
||||
if: github.ref == 'refs/heads/master' && matrix.use_ccache == 1
|
||||
uses: actions/cache/save@v5
|
||||
env:
|
||||
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
with:
|
||||
path: ${{env.CCACHE_DIR}}
|
||||
key: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}-${{env.BRANCH_NAME}}
|
||||
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 }}
|
||||
|
|
@ -472,7 +498,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 }}
|
||||
|
|
@ -504,19 +530,19 @@ jobs:
|
|||
fi
|
||||
|
||||
- name: Upload artifact
|
||||
id: upload_artifact
|
||||
if: matrix.make_package
|
||||
uses: actions/upload-artifact@v6
|
||||
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'
|
||||
uses: actions/upload-artifact@v6
|
||||
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/Release/*.pdb
|
||||
build/oracle/Release/*.pdb
|
||||
|
|
@ -524,22 +550,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@v3
|
||||
id: attestation
|
||||
uses: actions/attest@v4
|
||||
with:
|
||||
subject-name: ${{steps.build.outputs.name}}
|
||||
subject-path: ${{steps.build.outputs.path}}
|
||||
show-summary: false
|
||||
|
||||
|
|
|
|||
20
.github/workflows/docker-release.yml
vendored
20
.github/workflows/docker-release.yml
vendored
|
|
@ -13,6 +13,11 @@ on:
|
|||
- '.github/workflows/docker-release.yml'
|
||||
- 'Dockerfile'
|
||||
|
||||
# Cancel earlier, unfinished runs of this workflow on the same branch (unless on release)
|
||||
concurrency:
|
||||
group: "${{ github.workflow }} @ ${{ github.ref_name }}"
|
||||
cancel-in-progress: ${{ github.ref_type != 'tag' }}
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
name: amd64 & arm64
|
||||
|
|
@ -27,7 +32,7 @@ jobs:
|
|||
|
||||
- name: Docker metadata
|
||||
id: metadata
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/cockatrice/servatrice
|
||||
|
|
@ -41,26 +46,27 @@ 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'
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.ref_type == 'tag' }}
|
||||
tags: ${{ steps.metadata.outputs.tags }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
annotations: ${{ steps.metadata.outputs.annotations }}
|
||||
cache-from: type=gha,scope=servatrice
|
||||
cache-to: type=gha,mode=max,scope=servatrice
|
||||
|
|
|
|||
|
|
@ -254,7 +254,9 @@ endif()
|
|||
set(CPACK_PACKAGE_CONTACT "Zach Halpern <zach@cockatrice.us>")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_NAME}")
|
||||
set(CPACK_PACKAGE_VENDOR "Cockatrice Development Team")
|
||||
set(CPACK_PACKAGE_DESCRIPTION "Cockatrice is an open-source, multiplatform application for playing tabletop card games over a network. The program's server design prevents users from manipulating the game for unfair advantage. The client also provides a single-player mode, which allows users to brew while offline.")
|
||||
set(CPACK_PACKAGE_DESCRIPTION
|
||||
"Cockatrice is an open-source, multiplatform application for playing tabletop card games over a network. The program's server design prevents users from manipulating the game for unfair advantage. The client also provides a single-player mode, which allows users to brew while offline."
|
||||
)
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
|
||||
|
|
@ -328,7 +330,12 @@ include(CPack)
|
|||
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_interfaces ${CMAKE_BINARY_DIR}/libcockatrice_interfaces)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_protocol ${CMAKE_BINARY_DIR}/libcockatrice_protocol)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_network ${CMAKE_BINARY_DIR}/libcockatrice_network)
|
||||
if(WITH_CLIENT
|
||||
OR WITH_SERVER
|
||||
OR WITH_ORACLE
|
||||
)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_network ${CMAKE_BINARY_DIR}/libcockatrice_network)
|
||||
endif()
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_deck_list ${CMAKE_BINARY_DIR}/libcockatrice_deck_list)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_rng ${CMAKE_BINARY_DIR}/libcockatrice_rng)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_card ${CMAKE_BINARY_DIR}/libcockatrice_card)
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ Latest <kbd>beta</kbd> version:
|
|||
|
||||
# Community Resources [](https://discord.gg/3Z9yzmA)
|
||||
|
||||
Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other projet contributors (`#dev` channel) or fellow users of the app. Come here to talk about the application, features, or just to hang out.
|
||||
Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other project contributors (`#dev` channel) or fellow users of the app. Come here to talk about the application, features, or just to hang out.
|
||||
- [Official Website](https://cockatrice.github.io)
|
||||
- [Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki)
|
||||
- [Official Discord](https://discord.gg/3Z9yzmA)
|
||||
|
|
@ -109,7 +109,7 @@ Cockatrice tries to use the [Google Developer Documentation Style Guide](https:/
|
|||
|
||||
Cockatrice uses Transifex to manage translations. You can help us bring <kbd>Cockatrice</kbd>, <kbd>Oracle</kbd> and <kbd>Webatrice</kbd> to your language and just adjust single wordings right from within your browser by visiting our [Transifex project page](https://explore.transifex.com/cockatrice/cockatrice/).<br>
|
||||
|
||||
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about getting invovled, and join a group of hundreds of others!<br>
|
||||
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about getting involved, and join a group of hundreds of others!<br>
|
||||
|
||||
|
||||
# Build [](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml?query=branch%3Amaster+event%3Apush) [](https://github.com/Cockatrice/Cockatrice/actions/workflows/docker-release.yml?query=branch%3Amaster+event%3Apush) [](https://github.com/Cockatrice/Cockatrice/actions/workflows/web-build.yml?query=branch%3Amaster+event%3Apush)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ if(WITH_ORACLE)
|
|||
set(_ORACLE_NEEDED Concurrent Network Svg Widgets)
|
||||
endif()
|
||||
if(TEST)
|
||||
set(_TEST_NEEDED Widgets)
|
||||
# Union of Qt modules required across all test targets (independent of application targets).
|
||||
# When adding a new test that needs additional Qt modules, add them here rather than in the test's CMakeLists.txt.
|
||||
set(_TEST_NEEDED Concurrent Network Svg Widgets)
|
||||
endif()
|
||||
|
||||
set(REQUIRED_QT_COMPONENTS ${REQUIRED_QT_COMPONENTS} ${_SERVATRICE_NEEDED} ${_COCKATRICE_NEEDED} ${_ORACLE_NEEDED}
|
||||
|
|
@ -40,7 +42,7 @@ list(REMOVE_DUPLICATES REQUIRED_QT_COMPONENTS)
|
|||
if(NOT FORCE_USE_QT5)
|
||||
# Linguist is now a component in Qt6 instead of an external package
|
||||
find_package(
|
||||
Qt6 6.2.3
|
||||
Qt6 6.4.2
|
||||
COMPONENTS ${REQUIRED_QT_COMPONENTS} Linguist
|
||||
QUIET HINTS ${Qt6_DIR}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ set(cockatrice_SOURCES
|
|||
src/interface/widgets/dialogs/dlg_load_deck_from_clipboard.cpp
|
||||
src/interface/widgets/dialogs/dlg_load_deck_from_website.cpp
|
||||
src/interface/widgets/dialogs/dlg_load_remote_deck.cpp
|
||||
src/interface/widgets/dialogs/dlg_local_game_options.cpp
|
||||
src/interface/widgets/dialogs/dlg_manage_sets.cpp
|
||||
src/interface/widgets/dialogs/dlg_register.cpp
|
||||
src/interface/widgets/dialogs/dlg_select_set_for_cards.cpp
|
||||
|
|
@ -563,6 +564,9 @@ if(WIN32)
|
|||
PATTERN "styles/qopensslbackend.dll"
|
||||
PATTERN "styles/qschannelbackend.dll"
|
||||
PATTERN "styles/qwindowsvistastyle.dll"
|
||||
PATTERN "styles/qwindows11style.dll"
|
||||
PATTERN "styles/qmodernwindowsstyle.dll"
|
||||
PATTERN "styles/qmodernwindowsstyled.dll"
|
||||
PATTERN "tls/qcertonlybackend.dll"
|
||||
PATTERN "tls/qopensslbackend.dll"
|
||||
PATTERN "tls/qschannelbackend.dll"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
<file>resources/icons/dragon.svg</file>
|
||||
<file>resources/icons/dropdown_collapsed.svg</file>
|
||||
<file>resources/icons/dropdown_expanded.svg</file>
|
||||
<file>resources/icons/filter.svg</file>
|
||||
<file>resources/icons/floppy_disk.svg</file>
|
||||
<file>resources/icons/forgot_password.svg</file>
|
||||
<file>resources/icons/gear.svg</file>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -47,6 +47,6 @@ searches are case insensitive.
|
|||
<dd>[t:aggro OR o:control](#t:aggro OR o:control) <small>(Any deck filename that contains either aggro or control)</small></dd>
|
||||
|
||||
<dt>Grouping:</dt>
|
||||
<dd><a href="#red -([[]]:100 or aggro)">red -([[]]:100 or aggro)</a> <small>(Any deck that has red in its filename but is not 100 cards or has aggro in its filename)</small></dd>
|
||||
<dd><a href="#red -([[]]:100 OR aggro)">red -([[]]:100 OR aggro)</a> <small>(Any deck that has red in its filename but is not 100 cards or has aggro in its filename)</small></dd>
|
||||
|
||||
</dl>
|
||||
|
|
@ -51,16 +51,16 @@ In this list of examples below, each entry has an explanation and can be clicked
|
|||
|
||||
<dt><u>E</u>dition:</dt>
|
||||
<dd>[set:lea](#set:lea) <small>(Cards that appear in Alpha, which has the set code LEA)</small></dd>
|
||||
<dd>[e:lea or e:leb](#e:lea or e:leb) <small>(Cards that appear in Alpha or Beta)</small></dd>
|
||||
<dd>[e:lea OR e:leb](#e:lea OR e:leb) <small>(Cards that appear in Alpha or Beta)</small></dd>
|
||||
|
||||
<dt>Negate:</dt>
|
||||
<dd>[c:wu -c:m](#c:wu -c:m) <small>(Any card that is white or blue, but not multicolored)</small></dd>
|
||||
|
||||
<dt>Branching:</dt>
|
||||
<dd>[t:sliver or o:changeling](#t:sliver or o:changeling) <small>(Any card that is either a sliver or has changeling)</small></dd>
|
||||
<dd>[t:sliver OR o:changeling](#t:sliver OR o:changeling) <small>(Any card that is either a sliver or has changeling)</small></dd>
|
||||
|
||||
<dt>Grouping:</dt>
|
||||
<dd><a href="#t:angel -(angel or c:w)">t:angel -(angel or c:w)</a> <small>(Any angel that doesn't have angel in its name and isn't white)</small></dd>
|
||||
<dd><a href="#t:angel -(angel OR c:w)">t:angel -(angel OR c:w)</a> <small>(Any angel that doesn't have angel in its name and isn't white)</small></dd>
|
||||
|
||||
<dt>Regular Expression:</dt>
|
||||
<dd>[/^fell/](#/^fell/) <small>(Any card name that begins with "fell")</small></dd>
|
||||
|
|
|
|||
12
cockatrice/resources/icons/filter.svg
Normal file
12
cockatrice/resources/icons/filter.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg"
|
||||
width="800px" height="800px" viewBox="0 0 971.986 971.986"
|
||||
xml:space="preserve">
|
||||
<g>
|
||||
<path d="M370.216,459.3c10.2,11.1,15.8,25.6,15.8,40.6v442c0,26.601,32.1,40.101,51.1,21.4l123.3-141.3
|
||||
c16.5-19.8,25.6-29.601,25.6-49.2V500c0-15,5.7-29.5,15.8-40.601L955.615,75.5c26.5-28.8,6.101-75.5-33.1-75.5h-873
|
||||
c-39.2,0-59.7,46.6-33.1,75.5L370.216,459.3z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 676 B |
|
|
@ -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 <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <libcockatrice/card/import/card_name_normalizer.h>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -284,6 +284,9 @@ SettingsCache::SettingsCache()
|
|||
closeEmptyCardView = settings->value("interface/closeEmptyCardView", true).toBool();
|
||||
focusCardViewSearchBar = settings->value("interface/focusCardViewSearchBar", true).toBool();
|
||||
|
||||
showDragSelectionCount = settings->value("interface/showlassoselectioncount", true).toBool();
|
||||
showTotalSelectionCount = settings->value("interface/showpersistentselectioncount", true).toBool();
|
||||
|
||||
showShortcuts = settings->value("menu/showshortcuts", true).toBool();
|
||||
showGameSelectorFilterToolbar = settings->value("menu/showgameselectorfiltertoolbar", true).toBool();
|
||||
displayCardNames = settings->value("cards/displaycardnames", true).toBool();
|
||||
|
|
@ -385,6 +388,13 @@ SettingsCache::SettingsCache()
|
|||
defaultStartingLifeTotal = settings->value("game/defaultstartinglifetotal", 20).toInt();
|
||||
shareDecklistsOnLoad = settings->value("game/sharedecklistsonload", false).toBool();
|
||||
rememberGameSettings = settings->value("game/remembergamesettings", true).toBool();
|
||||
|
||||
// Local game settings use "localgameoptions/" prefix to keep them separate
|
||||
// from server game settings which use "game/" prefix
|
||||
localGameRememberSettings = settings->value("localgameoptions/remembersettings", false).toBool();
|
||||
localGameMaxPlayers = settings->value("localgameoptions/maxplayers", 1).toInt();
|
||||
localGameStartingLifeTotal = settings->value("localgameoptions/startinglifetotal", 20).toInt();
|
||||
|
||||
clientID = settings->value("personal/clientid", CLIENT_INFO_NOT_SET).toString();
|
||||
clientVersion = settings->value("personal/clientversion", CLIENT_INFO_NOT_SET).toString();
|
||||
}
|
||||
|
|
@ -1115,257 +1125,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;
|
||||
}
|
||||
|
|
@ -1478,6 +1252,24 @@ void SettingsCache::setRememberGameSettings(const bool _rememberGameSettings)
|
|||
settings->setValue("game/remembergamesettings", rememberGameSettings);
|
||||
}
|
||||
|
||||
void SettingsCache::setLocalGameRememberSettings(bool value)
|
||||
{
|
||||
localGameRememberSettings = value;
|
||||
settings->setValue("localgameoptions/remembersettings", value);
|
||||
}
|
||||
|
||||
void SettingsCache::setLocalGameMaxPlayers(int value)
|
||||
{
|
||||
localGameMaxPlayers = value;
|
||||
settings->setValue("localgameoptions/maxplayers", value);
|
||||
}
|
||||
|
||||
void SettingsCache::setLocalGameStartingLifeTotal(int value)
|
||||
{
|
||||
localGameStartingLifeTotal = value;
|
||||
settings->setValue("localgameoptions/startinglifetotal", value);
|
||||
}
|
||||
|
||||
void SettingsCache::setNotifyAboutUpdate(QT_STATE_CHANGED_T _notifyaboutupdate)
|
||||
{
|
||||
notifyAboutUpdates = static_cast<bool>(_notifyaboutupdate);
|
||||
|
|
@ -1519,6 +1311,18 @@ void SettingsCache::setRoundCardCorners(bool _roundCardCorners)
|
|||
emit roundCardCornersChanged(roundCardCorners);
|
||||
}
|
||||
|
||||
void SettingsCache::setShowDragSelectionCount(QT_STATE_CHANGED_T _showDragSelectionCount)
|
||||
{
|
||||
showDragSelectionCount = static_cast<bool>(_showDragSelectionCount);
|
||||
settings->setValue("interface/showlassoselectioncount", showDragSelectionCount);
|
||||
}
|
||||
|
||||
void SettingsCache::setShowTotalSelectionCount(QT_STATE_CHANGED_T _showTotalSelectionCount)
|
||||
{
|
||||
showTotalSelectionCount = static_cast<bool>(_showTotalSelectionCount);
|
||||
settings->setValue("interface/showpersistentselectioncount", showTotalSelectionCount);
|
||||
}
|
||||
|
||||
void SettingsCache::loadPaths()
|
||||
{
|
||||
QString dataPath = getDataPath();
|
||||
|
|
|
|||
|
|
@ -330,10 +330,18 @@ private:
|
|||
[[nodiscard]] QString getSafeConfigFilePath(QString configEntry, QString defaultPath) const;
|
||||
void loadPaths();
|
||||
bool rememberGameSettings;
|
||||
|
||||
// Local game settings (separate from server game settings in game/*)
|
||||
bool localGameRememberSettings;
|
||||
int localGameMaxPlayers;
|
||||
int localGameStartingLifeTotal;
|
||||
|
||||
QList<ReleaseChannel *> releaseChannels;
|
||||
bool isPortableBuild;
|
||||
bool roundCardCorners;
|
||||
bool showStatusBar;
|
||||
bool showDragSelectionCount;
|
||||
bool showTotalSelectionCount;
|
||||
|
||||
public:
|
||||
SettingsCache();
|
||||
|
|
@ -449,6 +457,14 @@ public:
|
|||
{
|
||||
return showStatusBar;
|
||||
}
|
||||
[[nodiscard]] bool getShowDragSelectionCount() const
|
||||
{
|
||||
return showDragSelectionCount;
|
||||
}
|
||||
[[nodiscard]] bool getShowTotalSelectionCount() const
|
||||
{
|
||||
return showTotalSelectionCount;
|
||||
}
|
||||
[[nodiscard]] bool getNotificationsEnabled() const
|
||||
{
|
||||
return notificationsEnabled;
|
||||
|
|
@ -862,6 +878,18 @@ public:
|
|||
{
|
||||
return rememberGameSettings;
|
||||
}
|
||||
[[nodiscard]] bool getLocalGameRememberSettings() const
|
||||
{
|
||||
return localGameRememberSettings;
|
||||
}
|
||||
[[nodiscard]] int getLocalGameMaxPlayers() const
|
||||
{
|
||||
return localGameMaxPlayers;
|
||||
}
|
||||
[[nodiscard]] int getLocalGameStartingLifeTotal() const
|
||||
{
|
||||
return localGameStartingLifeTotal;
|
||||
}
|
||||
[[nodiscard]] int getKeepAlive() const override
|
||||
{
|
||||
return keepalive;
|
||||
|
|
@ -1089,6 +1117,9 @@ public slots:
|
|||
void setDefaultStartingLifeTotal(const int _defaultStartingLifeTotal);
|
||||
void setShareDecklistsOnLoad(const bool _shareDecklistsOnLoad);
|
||||
void setRememberGameSettings(const bool _rememberGameSettings);
|
||||
void setLocalGameRememberSettings(bool value);
|
||||
void setLocalGameMaxPlayers(int value);
|
||||
void setLocalGameStartingLifeTotal(int value);
|
||||
void setCheckUpdatesOnStartup(QT_STATE_CHANGED_T value);
|
||||
void setStartupCardUpdateCheckPromptForUpdate(bool value);
|
||||
void setStartupCardUpdateCheckAlwaysUpdate(bool value);
|
||||
|
|
@ -1099,5 +1130,7 @@ public slots:
|
|||
void setUpdateReleaseChannelIndex(int value);
|
||||
void setMaxFontSize(int _max);
|
||||
void setRoundCardCorners(bool _roundCardCorners);
|
||||
void setShowDragSelectionCount(QT_STATE_CHANGED_T _showDragSelectionCount);
|
||||
void setShowTotalSelectionCount(QT_STATE_CHANGED_T _showTotalSelectionCount);
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ CardCounterSettings::CardCounterSettings(const QString &settingsPath, QObject *p
|
|||
|
||||
void CardCounterSettings::setColor(int counterId, const QColor &color)
|
||||
{
|
||||
QSettings settings = getSettings();
|
||||
|
||||
QString key = QString("cards/counters/%1/color").arg(counterId);
|
||||
|
||||
if (settings.value(key).value<QColor>() == color)
|
||||
|
|
@ -36,7 +38,7 @@ QColor CardCounterSettings::color(int counterId) const
|
|||
defaultColor = QColor::fromHsv(h, s, v);
|
||||
}
|
||||
|
||||
return settings.value(QString("cards/counters/%1/color").arg(counterId), defaultColor).value<QColor>();
|
||||
return getSettings().value(QString("cards/counters/%1/color").arg(counterId), defaultColor).value<QColor>();
|
||||
}
|
||||
|
||||
QString CardCounterSettings::displayName(int counterId) const
|
||||
|
|
|
|||
|
|
@ -501,7 +501,7 @@ private:
|
|||
{"Player/aUntapAll", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Untap All"),
|
||||
parseSequenceString("Ctrl+U"),
|
||||
ShortcutGroup::Playing_Area)},
|
||||
{"Player/aDoesntUntap", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Toggle Untap"),
|
||||
{"Player/aDoesntUntap", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Toggle Skip Untapping"),
|
||||
parseSequenceString("Alt+U"),
|
||||
ShortcutGroup::Playing_Area)},
|
||||
{"Player/aFlip", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Turn Card Over"),
|
||||
|
|
@ -513,6 +513,9 @@ private:
|
|||
{"Player/aPlay", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Play Card"),
|
||||
parseSequenceString(""),
|
||||
ShortcutGroup::Playing_Area)},
|
||||
{"Player/aPlayFacedown", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Play Card, Face Down"),
|
||||
parseSequenceString(""),
|
||||
ShortcutGroup::Playing_Area)},
|
||||
{"Player/aAttach", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Attach Card..."),
|
||||
parseSequenceString("Ctrl+Alt+A"),
|
||||
ShortcutGroup::Playing_Area)},
|
||||
|
|
@ -560,12 +563,9 @@ private:
|
|||
{"Player/aMoveToTopLibrary", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Top of Library"),
|
||||
parseSequenceString(""),
|
||||
ShortcutGroup::Move_selected)},
|
||||
{"Player/aPlayFacedown", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Battlefield, Face Down"),
|
||||
parseSequenceString(""),
|
||||
ShortcutGroup::Move_selected)},
|
||||
{"Player/aPlay", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Battlefield"),
|
||||
parseSequenceString(""),
|
||||
ShortcutGroup::Move_selected)},
|
||||
{"Player/aMoveToTable", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Battlefield"),
|
||||
parseSequenceString(""),
|
||||
ShortcutGroup::Move_selected)},
|
||||
{"Player/aViewHand",
|
||||
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Hand"), parseSequenceString(""), ShortcutGroup::View)},
|
||||
{"Player/aViewGraveyard",
|
||||
|
|
@ -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)},
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ GenericQuery <- String
|
|||
|
||||
NonDoubleQuoteUnlessEscaped <- '\\\"'. / !["].
|
||||
NonSingleQuoteUnlessEscaped <- "\\\'". / !['].
|
||||
UnescapedStringListPart <- !['":<>=! ].
|
||||
UnescapedStringListPart <- !['":<>()=! ].
|
||||
SingleApostropheString <- (UnescapedStringListPart+ ws*)* ['] (UnescapedStringListPart+ ws*)*
|
||||
|
||||
String <- SingleApostropheString / UnescapedStringListPart+ / ["] <NonDoubleQuoteUnlessEscaped*> ["] / ['] <NonSingleQuoteUnlessEscaped*> [']
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
#include "abstract_game.h"
|
||||
|
||||
#include "../interface/widgets/tabs/tab_game.h"
|
||||
#include "player/player.h"
|
||||
|
||||
AbstractGame::AbstractGame(TabGame *_tab) : tab(_tab)
|
||||
AbstractGame::AbstractGame(TabGame *_tab) : QObject(_tab), tab(_tab)
|
||||
{
|
||||
gameMetaInfo = new GameMetaInfo(this);
|
||||
gameEventHandler = new GameEventHandler(this);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
#include "abstract_card_drag_item.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../z_values.h"
|
||||
|
||||
#include <QCursor>
|
||||
#include <QDebug>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QPainter>
|
||||
|
||||
static const float CARD_WIDTH_HALF = CARD_WIDTH / 2;
|
||||
static const float CARD_HEIGHT_HALF = CARD_HEIGHT / 2;
|
||||
const QColor GHOST_MASK = QColor(255, 255, 255, 50);
|
||||
|
||||
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<qreal>(CARD_WIDTH - 1)),
|
||||
qBound(0.0, hotSpot.y(), static_cast<qreal>(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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 <QCursor>
|
||||
#include <QGraphicsScene>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#define ABSTRACTCARDITEM_H
|
||||
|
||||
#include "../../game_graphics/board/graphics_item_type.h"
|
||||
#include "../card_dimensions.h"
|
||||
#include "arrow_target.h"
|
||||
|
||||
#include <libcockatrice/card/printing/exact_card.h>
|
||||
|
|
@ -15,9 +16,6 @@
|
|||
|
||||
class Player;
|
||||
|
||||
const int CARD_WIDTH = 72;
|
||||
const int CARD_HEIGHT = 102;
|
||||
|
||||
class AbstractCardItem : public ArrowTarget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#define COUNTER_H
|
||||
|
||||
#include "../../interface/widgets/menus/tearoff_menu.h"
|
||||
#include "../player/menu/abstract_player_component.h"
|
||||
|
||||
#include <QGraphicsItem>
|
||||
#include <QInputDialog>
|
||||
|
|
@ -18,7 +19,7 @@ class QKeyEvent;
|
|||
class QMenu;
|
||||
class QString;
|
||||
|
||||
class AbstractCounter : public QObject, public QGraphicsItem
|
||||
class AbstractCounter : public QObject, public QGraphicsItem, public AbstractPlayerComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QGraphicsItem)
|
||||
|
|
@ -56,10 +57,10 @@ public:
|
|||
QGraphicsItem *parent = nullptr);
|
||||
~AbstractCounter() override;
|
||||
|
||||
void retranslateUi();
|
||||
void retranslateUi() override;
|
||||
void setValue(int _value);
|
||||
void setShortcutsActive();
|
||||
void setShortcutsInactive();
|
||||
void setShortcutsActive() override;
|
||||
void setShortcutsInactive() override;
|
||||
void delCounter();
|
||||
|
||||
QMenu *getMenu() const
|
||||
|
|
|
|||
|
|
@ -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 <libcockatrice/protocol/pb/command_create_arrow.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_delete_arrow.pb.h>
|
||||
#include <libcockatrice/utility/color.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTarget *_targetItem, const QColor &_color)
|
||||
: 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());
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -458,6 +460,9 @@ void CardItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
|
|||
|
||||
bool CardItem::animationEvent()
|
||||
{
|
||||
if (owner == nullptr) {
|
||||
return false;
|
||||
}
|
||||
int rotation = ROTATION_DEGREES_PER_FRAME;
|
||||
bool animationIncomplete = true;
|
||||
if (!tapped)
|
||||
|
|
@ -474,9 +479,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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
29
cockatrice/src/game/card_dimensions.h
Normal file
29
cockatrice/src/game/card_dimensions.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef CARD_DIMENSIONS_H
|
||||
#define CARD_DIMENSIONS_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
/**
|
||||
* @file card_dimensions.h
|
||||
* @brief Canonical card dimension constants for layout and Z-value calculations.
|
||||
*
|
||||
* These values represent the logical pixel dimensions of a standard card graphic.
|
||||
* They are used throughout the game scene for layout, rendering, and Z-value computation.
|
||||
*/
|
||||
namespace CardDimensions
|
||||
{
|
||||
/// Card width in pixels
|
||||
constexpr int WIDTH = 72;
|
||||
/// Card height in pixels
|
||||
constexpr int HEIGHT = 102;
|
||||
|
||||
/// Pre-converted for floating-point contexts (Z-value calculations)
|
||||
constexpr qreal WIDTH_F = static_cast<qreal>(WIDTH);
|
||||
constexpr qreal HEIGHT_F = static_cast<qreal>(HEIGHT);
|
||||
|
||||
/// Half-dimensions for centering and rotation transforms
|
||||
constexpr qreal WIDTH_HALF_F = WIDTH_F / 2;
|
||||
constexpr qreal HEIGHT_HALF_F = HEIGHT_F / 2;
|
||||
} // namespace CardDimensions
|
||||
|
||||
#endif // CARD_DIMENSIONS_H
|
||||
|
|
@ -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<QPair<int, int>>
|
|||
|
||||
// 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<QPair<int, int>> &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<MoveCard_ToZone> &plan)
|
|||
|
||||
void DeckViewScene::rearrangeItems()
|
||||
{
|
||||
const int spacing = CARD_HEIGHT / 3;
|
||||
const int spacing = CardDimensions::HEIGHT / 3;
|
||||
QList<DeckViewCardContainer *> contList = cardContainers.values();
|
||||
|
||||
// Initialize space requirements
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@
|
|||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/filters/filter_string.h>
|
||||
|
||||
DlgMoveTopCardsUntil::DlgMoveTopCardsUntil(QWidget *parent, QStringList exprs, uint _numberOfHits, bool autoPlay)
|
||||
: QDialog(parent)
|
||||
DlgMoveTopCardsUntil::DlgMoveTopCardsUntil(QWidget *parent, const MoveTopCardsUntilOptions &options) : QDialog(parent)
|
||||
{
|
||||
exprLabel = new QLabel(tr("Card name (or search expressions):"));
|
||||
|
||||
|
|
@ -21,13 +20,13 @@ DlgMoveTopCardsUntil::DlgMoveTopCardsUntil(QWidget *parent, QStringList exprs, u
|
|||
exprComboBox->setFocus();
|
||||
exprComboBox->setEditable(true);
|
||||
exprComboBox->setInsertPolicy(QComboBox::InsertAtTop);
|
||||
exprComboBox->insertItems(0, exprs);
|
||||
exprComboBox->insertItems(0, options.exprs);
|
||||
exprLabel->setBuddy(exprComboBox);
|
||||
|
||||
numberOfHitsLabel = new QLabel(tr("Number of hits:"));
|
||||
numberOfHitsEdit = new QSpinBox(this);
|
||||
numberOfHitsEdit->setRange(1, 99);
|
||||
numberOfHitsEdit->setValue(_numberOfHits);
|
||||
numberOfHitsEdit->setValue(options.numberOfHits);
|
||||
numberOfHitsLabel->setBuddy(numberOfHitsEdit);
|
||||
|
||||
auto *grid = new QGridLayout;
|
||||
|
|
@ -35,7 +34,7 @@ DlgMoveTopCardsUntil::DlgMoveTopCardsUntil(QWidget *parent, QStringList exprs, u
|
|||
grid->addWidget(numberOfHitsEdit, 0, 1);
|
||||
|
||||
autoPlayCheckBox = new QCheckBox(tr("Auto play hits"));
|
||||
autoPlayCheckBox->setChecked(autoPlay);
|
||||
autoPlayCheckBox->setChecked(options.autoPlay);
|
||||
|
||||
buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &DlgMoveTopCardsUntil::validateAndAccept);
|
||||
|
|
@ -118,6 +117,13 @@ QString DlgMoveTopCardsUntil::getExpr() const
|
|||
return exprComboBox->currentText();
|
||||
}
|
||||
|
||||
MoveTopCardsUntilOptions DlgMoveTopCardsUntil::getOptions() const
|
||||
{
|
||||
return {.exprs = getExprs(),
|
||||
.numberOfHits = numberOfHitsEdit->text().toInt(),
|
||||
.autoPlay = autoPlayCheckBox->isChecked()};
|
||||
}
|
||||
|
||||
QStringList DlgMoveTopCardsUntil::getExprs() const
|
||||
{
|
||||
QStringList exprs;
|
||||
|
|
@ -125,14 +131,4 @@ QStringList DlgMoveTopCardsUntil::getExprs() const
|
|||
exprs.append(exprComboBox->itemText(i));
|
||||
}
|
||||
return exprs;
|
||||
}
|
||||
|
||||
uint DlgMoveTopCardsUntil::getNumberOfHits() const
|
||||
{
|
||||
return numberOfHitsEdit->text().toUInt();
|
||||
}
|
||||
|
||||
bool DlgMoveTopCardsUntil::isAutoPlay() const
|
||||
{
|
||||
return autoPlayCheckBox->isChecked();
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,13 @@
|
|||
|
||||
class FilterString;
|
||||
|
||||
struct MoveTopCardsUntilOptions
|
||||
{
|
||||
QStringList exprs = {};
|
||||
int numberOfHits = 1;
|
||||
bool autoPlay = false;
|
||||
};
|
||||
|
||||
class DlgMoveTopCardsUntil : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -29,15 +36,12 @@ class DlgMoveTopCardsUntil : public QDialog
|
|||
void validateAndAccept();
|
||||
bool validateMatchExists(const FilterString &filterString);
|
||||
|
||||
public:
|
||||
explicit DlgMoveTopCardsUntil(QWidget *parent = nullptr,
|
||||
QStringList exprs = QStringList(),
|
||||
uint numberOfHits = 1,
|
||||
bool autoPlay = false);
|
||||
[[nodiscard]] QString getExpr() const;
|
||||
[[nodiscard]] QStringList getExprs() const;
|
||||
[[nodiscard]] uint getNumberOfHits() const;
|
||||
[[nodiscard]] bool isAutoPlay() const;
|
||||
|
||||
public:
|
||||
explicit DlgMoveTopCardsUntil(QWidget *parent = nullptr, const MoveTopCardsUntilOptions &options = {});
|
||||
[[nodiscard]] QString getExpr() const;
|
||||
[[nodiscard]] MoveTopCardsUntilOptions getOptions() const;
|
||||
};
|
||||
|
||||
#endif // DLG_MOVE_TOP_CARDS_UNTIL_H
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <QGraphicsView>
|
||||
#include <QSet>
|
||||
#include <QtMath>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
#include <numeric>
|
||||
|
||||
/**
|
||||
|
|
@ -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);
|
||||
|
|
@ -520,9 +521,9 @@ void GameScene::startRubberBand(const QPointF &selectionOrigin)
|
|||
emit sigStartRubberBand(selectionOrigin);
|
||||
}
|
||||
|
||||
void GameScene::resizeRubberBand(const QPointF &cursorPoint)
|
||||
void GameScene::resizeRubberBand(const QPointF &cursorPoint, int selectedCount)
|
||||
{
|
||||
emit sigResizeRubberBand(cursorPoint);
|
||||
emit sigResizeRubberBand(cursorPoint, selectedCount);
|
||||
}
|
||||
|
||||
void GameScene::stopRubberBand()
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ public:
|
|||
/** Unregisters a card from animation updates. */
|
||||
void unregisterAnimationItem(AbstractCardItem *card);
|
||||
void startRubberBand(const QPointF &selectionOrigin);
|
||||
void resizeRubberBand(const QPointF &cursorPoint);
|
||||
void resizeRubberBand(const QPointF &cursorPoint, int selectedCount);
|
||||
void stopRubberBand();
|
||||
|
||||
public slots:
|
||||
|
|
@ -196,7 +196,7 @@ protected:
|
|||
|
||||
signals:
|
||||
void sigStartRubberBand(const QPointF &selectionOrigin);
|
||||
void sigResizeRubberBand(const QPointF &cursorPoint);
|
||||
void sigResizeRubberBand(const QPointF &cursorPoint, int selectedCount);
|
||||
void sigStopRubberBand();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,32 @@
|
|||
#include "game_scene.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QLabel>
|
||||
#include <QResizeEvent>
|
||||
#include <QRubberBand>
|
||||
|
||||
// QRubberBand calls raise() in showEvent() and changeEvent() to stay on top of siblings.
|
||||
// This subclass disables that behavior so dragCountLabel can appear above it.
|
||||
class SelectionRubberBand : public QRubberBand
|
||||
{
|
||||
public:
|
||||
using QRubberBand::QRubberBand;
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override
|
||||
{
|
||||
QWidget::showEvent(event); // Skip QRubberBand's raise()
|
||||
}
|
||||
|
||||
void changeEvent(QEvent *event) override
|
||||
{
|
||||
if (event->type() == QEvent::ZOrderChange) {
|
||||
return; // Skip QRubberBand's raise() on z-order changes
|
||||
}
|
||||
QRubberBand::changeEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
GameView::GameView(GameScene *scene, QWidget *parent) : QGraphicsView(scene, parent), rubberBand(0)
|
||||
{
|
||||
setBackgroundBrush(QBrush(QColor(0, 0, 0)));
|
||||
|
|
@ -19,6 +42,7 @@ GameView::GameView(GameScene *scene, QWidget *parent) : QGraphicsView(scene, par
|
|||
connect(scene, &GameScene::sigStartRubberBand, this, &GameView::startRubberBand);
|
||||
connect(scene, &GameScene::sigResizeRubberBand, this, &GameView::resizeRubberBand);
|
||||
connect(scene, &GameScene::sigStopRubberBand, this, &GameView::stopRubberBand);
|
||||
connect(scene, &QGraphicsScene::selectionChanged, this, [this]() { updateTotalSelectionCount(); });
|
||||
|
||||
aCloseMostRecentZoneView = new QAction(this);
|
||||
|
||||
|
|
@ -27,7 +51,23 @@ GameView::GameView(GameScene *scene, QWidget *parent) : QGraphicsView(scene, par
|
|||
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
|
||||
&GameView::refreshShortcuts);
|
||||
refreshShortcuts();
|
||||
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
|
||||
rubberBand = new SelectionRubberBand(QRubberBand::Rectangle, this);
|
||||
|
||||
const QString countLabelStyle = "color: white; "
|
||||
"font-size: 14px; "
|
||||
"font-weight: bold; "
|
||||
"background-color: rgba(0, 0, 0, 160); "
|
||||
"border-radius: 3px; "
|
||||
"padding: 1px 2px;";
|
||||
|
||||
dragCountLabel = new QLabel(this);
|
||||
dragCountLabel->setStyleSheet(countLabelStyle);
|
||||
dragCountLabel->hide();
|
||||
dragCountLabel->raise();
|
||||
|
||||
totalCountLabel = new QLabel(this);
|
||||
totalCountLabel->setStyleSheet(countLabelStyle);
|
||||
totalCountLabel->hide();
|
||||
}
|
||||
|
||||
void GameView::resizeEvent(QResizeEvent *event)
|
||||
|
|
@ -39,6 +79,7 @@ void GameView::resizeEvent(QResizeEvent *event)
|
|||
s->processViewSizeChange(event->size());
|
||||
|
||||
updateSceneRect(scene()->sceneRect());
|
||||
updateTotalSelectionCount(event->size());
|
||||
}
|
||||
|
||||
void GameView::updateSceneRect(const QRectF &rect)
|
||||
|
|
@ -48,20 +89,67 @@ void GameView::updateSceneRect(const QRectF &rect)
|
|||
|
||||
void GameView::startRubberBand(const QPointF &_selectionOrigin)
|
||||
{
|
||||
if (!rubberBand)
|
||||
return;
|
||||
|
||||
selectionOrigin = _selectionOrigin;
|
||||
rubberBand->setGeometry(QRect(mapFromScene(selectionOrigin), QSize(0, 0)));
|
||||
rubberBand->show();
|
||||
}
|
||||
|
||||
void GameView::resizeRubberBand(const QPointF &cursorPoint)
|
||||
void GameView::resizeRubberBand(const QPointF &cursorPoint, int selectedCount)
|
||||
{
|
||||
if (rubberBand)
|
||||
rubberBand->setGeometry(QRect(mapFromScene(selectionOrigin), cursorPoint.toPoint()).normalized());
|
||||
if (!rubberBand)
|
||||
return;
|
||||
|
||||
constexpr int kLabelPaddingInPixels = 4;
|
||||
|
||||
QPoint cursor = cursorPoint.toPoint();
|
||||
QRect rect = QRect(mapFromScene(selectionOrigin), cursor).normalized();
|
||||
rubberBand->setGeometry(rect);
|
||||
|
||||
if (!SettingsCache::instance().getShowDragSelectionCount()) {
|
||||
dragCountLabel->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedCount > 0) {
|
||||
dragCountLabel->setText(QString::number(selectedCount));
|
||||
dragCountLabel->adjustSize();
|
||||
QSize labelSize = dragCountLabel->size();
|
||||
|
||||
if (rect.width() < labelSize.width() + 2 * kLabelPaddingInPixels ||
|
||||
rect.height() < labelSize.height() + 2 * kLabelPaddingInPixels) {
|
||||
dragCountLabel->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const int minX = rect.left() + kLabelPaddingInPixels;
|
||||
const int minY = rect.top() + kLabelPaddingInPixels;
|
||||
|
||||
int x = qMax(minX, cursor.x() - labelSize.width() - kLabelPaddingInPixels);
|
||||
int y = qMax(minY, cursor.y() - labelSize.height() - kLabelPaddingInPixels);
|
||||
|
||||
bool isAtTopLeftCorner = (x == minX) && (y == minY);
|
||||
if (isAtTopLeftCorner) {
|
||||
constexpr int kCursorClearanceInPixels = 16;
|
||||
x = qMin(cursor.x() + kCursorClearanceInPixels, rect.right() - labelSize.width() - kLabelPaddingInPixels);
|
||||
}
|
||||
|
||||
dragCountLabel->move(x, y);
|
||||
dragCountLabel->show();
|
||||
} else {
|
||||
dragCountLabel->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void GameView::stopRubberBand()
|
||||
{
|
||||
if (!rubberBand)
|
||||
return;
|
||||
|
||||
rubberBand->hide();
|
||||
dragCountLabel->hide();
|
||||
}
|
||||
|
||||
void GameView::refreshShortcuts()
|
||||
|
|
@ -69,3 +157,28 @@ void GameView::refreshShortcuts()
|
|||
aCloseMostRecentZoneView->setShortcuts(
|
||||
SettingsCache::instance().shortcuts().getShortcut("Player/aCloseMostRecentZoneView"));
|
||||
}
|
||||
|
||||
void GameView::updateTotalSelectionCount(const QSize &viewSize)
|
||||
{
|
||||
if (!SettingsCache::instance().getShowTotalSelectionCount()) {
|
||||
totalCountLabel->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
int count = scene()->selectedItems().count();
|
||||
|
||||
if (count > 1) {
|
||||
totalCountLabel->setText(QString::number(count));
|
||||
totalCountLabel->adjustSize();
|
||||
|
||||
constexpr int kMarginInPixels = 10;
|
||||
int availableWidth = viewSize.isValid() ? viewSize.width() : viewport()->width();
|
||||
int availableHeight = viewSize.isValid() ? viewSize.height() : viewport()->height();
|
||||
int x = availableWidth - totalCountLabel->width() - kMarginInPixels;
|
||||
int y = availableHeight - totalCountLabel->height() - kMarginInPixels;
|
||||
totalCountLabel->move(x, y);
|
||||
totalCountLabel->show();
|
||||
} else {
|
||||
totalCountLabel->hide();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <QGraphicsView>
|
||||
|
||||
class GameScene;
|
||||
class QLabel;
|
||||
class QRubberBand;
|
||||
|
||||
class GameView : public QGraphicsView
|
||||
|
|
@ -18,15 +19,18 @@ class GameView : public QGraphicsView
|
|||
private:
|
||||
QAction *aCloseMostRecentZoneView;
|
||||
QRubberBand *rubberBand;
|
||||
QLabel *dragCountLabel;
|
||||
QLabel *totalCountLabel;
|
||||
QPointF selectionOrigin;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
private slots:
|
||||
void startRubberBand(const QPointF &selectionOrigin);
|
||||
void resizeRubberBand(const QPointF &cursorPoint);
|
||||
void resizeRubberBand(const QPointF &cursorPoint, int selectedCount);
|
||||
void stopRubberBand();
|
||||
void refreshShortcuts();
|
||||
void updateTotalSelectionCount(const QSize &viewSize = QSize());
|
||||
public slots:
|
||||
void updateSceneRect(const QRectF &rect);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,16 +10,9 @@
|
|||
#include <../../client/settings/card_counter_settings.h>
|
||||
#include <libcockatrice/protocol/pb/context_move_card.pb.h>
|
||||
#include <libcockatrice/protocol/pb/context_mulligan.pb.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
#include <utility>
|
||||
|
||||
static const QString TABLE_ZONE_NAME = "table";
|
||||
static const QString GRAVE_ZONE_NAME = "grave";
|
||||
static const QString EXILE_ZONE_NAME = "rfg";
|
||||
static const QString HAND_ZONE_NAME = "hand";
|
||||
static const QString DECK_ZONE_NAME = "deck";
|
||||
static const QString SIDEBOARD_ZONE_NAME = "sb";
|
||||
static const QString STACK_ZONE_NAME = "stack";
|
||||
|
||||
static QString sanitizeHtml(QString dirty)
|
||||
{
|
||||
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<QString> 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);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <libcockatrice/protocol/pb/command_next_turn.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_active_phase.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
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");
|
||||
|
||||
|
|
|
|||
|
|
@ -9,17 +9,20 @@
|
|||
|
||||
enum CardMenuActionType
|
||||
{
|
||||
// Per-card attribute actions (must be <= cmClone for cardMenuAction() dispatch)
|
||||
cmTap,
|
||||
cmUntap,
|
||||
cmDoesntUntap,
|
||||
cmFlip,
|
||||
cmPeek,
|
||||
cmClone,
|
||||
// Move actions (must be > cmClone for cardMenuAction() dispatch)
|
||||
cmMoveToTopLibrary,
|
||||
cmMoveToBottomLibrary,
|
||||
cmMoveToHand,
|
||||
cmMoveToGraveyard,
|
||||
cmMoveToExile
|
||||
cmMoveToExile,
|
||||
cmMoveToTable
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_CARD_MENU_ACTION_TYPE_H
|
||||
|
|
|
|||
32
cockatrice/src/game/player/menu/abstract_player_component.h
Normal file
32
cockatrice/src/game/player/menu/abstract_player_component.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @file abstract_player_component.h
|
||||
* @ingroup GameMenusPlayers
|
||||
* @brief Polymorphic interface for player-bound UI components managed by PlayerMenu.
|
||||
*/
|
||||
|
||||
#ifndef COCKATRICE_ABSTRACT_PLAYER_COMPONENT_H
|
||||
#define COCKATRICE_ABSTRACT_PLAYER_COMPONENT_H
|
||||
|
||||
/**
|
||||
* @brief Interface for player-bound UI components that need shortcut and translation lifecycle management.
|
||||
*
|
||||
* Not a QObject — avoids diamond inheritance with Qt's MOC. Each concrete component
|
||||
* inherits QObject through its Qt base class (QMenu, TearOffMenu, QGraphicsItem, etc.)
|
||||
* and this interface through regular multiple inheritance.
|
||||
*/
|
||||
class AbstractPlayerComponent
|
||||
{
|
||||
public:
|
||||
virtual ~AbstractPlayerComponent() = default;
|
||||
|
||||
/// Bind keyboard shortcuts. Called when this player gains focus.
|
||||
virtual void setShortcutsActive() = 0;
|
||||
|
||||
/// Unbind keyboard shortcuts. Called when this player loses focus.
|
||||
virtual void setShortcutsInactive() = 0;
|
||||
|
||||
/// Retranslate all user-visible strings. Called on language change.
|
||||
virtual void retranslateUi() = 0;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_ABSTRACT_PLAYER_COMPONENT_H
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive)
|
||||
: player(_player), card(_card), shortcutsActive(_shortcutsActive)
|
||||
|
|
@ -34,6 +35,8 @@ CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive
|
|||
connect(aTap, &QAction::triggered, playerActions, &PlayerActions::cardMenuAction);
|
||||
aDoesntUntap = new QAction(this);
|
||||
aDoesntUntap->setData(cmDoesntUntap);
|
||||
aDoesntUntap->setCheckable(true);
|
||||
aDoesntUntap->setChecked(card != nullptr && card->getDoesntUntap());
|
||||
connect(aDoesntUntap, &QAction::triggered, playerActions, &PlayerActions::cardMenuAction);
|
||||
aAttach = new QAction(this);
|
||||
connect(aAttach, &QAction::triggered, playerActions, &PlayerActions::actAttach);
|
||||
|
|
@ -107,36 +110,26 @@ CardMenu::CardMenu(Player *_player, const CardItem *_card, bool _shortcutsActive
|
|||
|
||||
if (revealedCard) {
|
||||
addAction(aHide);
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addSeparator();
|
||||
addAction(aSelectAll);
|
||||
addAction(aSelectColumn);
|
||||
addRelatedCardView();
|
||||
} else if (writeableCard) {
|
||||
|
||||
} else {
|
||||
if (card->getZone()) {
|
||||
if (card->getZone()->getName() == "table") {
|
||||
createTableMenu();
|
||||
} else if (card->getZone()->getName() == "stack") {
|
||||
createStackMenu();
|
||||
} else if (card->getZone()->getName() == "rfg" || card->getZone()->getName() == "grave") {
|
||||
createGraveyardOrExileMenu();
|
||||
if (card->getZone()->getName() == ZoneNames::TABLE) {
|
||||
createTableMenu(writeableCard);
|
||||
} else if (card->getZone()->getName() == ZoneNames::STACK) {
|
||||
createStackMenu(writeableCard);
|
||||
} else if (card->getZone()->getName() == ZoneNames::EXILE ||
|
||||
card->getZone()->getName() == ZoneNames::GRAVE) {
|
||||
createGraveyardOrExileMenu(writeableCard);
|
||||
} else {
|
||||
createHandOrCustomZoneMenu();
|
||||
createHandOrCustomZoneMenu(writeableCard);
|
||||
}
|
||||
} else {
|
||||
addMenu(new MoveMenu(player));
|
||||
}
|
||||
} else {
|
||||
if (card->getZone() && card->getZone()->getName() != "hand") {
|
||||
addAction(aDrawArrow);
|
||||
addSeparator();
|
||||
addRelatedCardView();
|
||||
addRelatedCardActions();
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addSeparator();
|
||||
addAction(aSelectAll);
|
||||
createZonelessMenu(writeableCard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -152,22 +145,18 @@ void CardMenu::removePlayer(Player *playerToRemove)
|
|||
}
|
||||
}
|
||||
|
||||
void CardMenu::createTableMenu()
|
||||
void CardMenu::createTableMenu(bool canModifyCard)
|
||||
{
|
||||
// Card is on the battlefield
|
||||
bool canModifyCard = player->getPlayerInfo()->judge || card->getOwner() == player;
|
||||
|
||||
if (!canModifyCard) {
|
||||
addRelatedCardView();
|
||||
addRelatedCardActions();
|
||||
|
||||
addSeparator();
|
||||
addAction(aDrawArrow);
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addSeparator();
|
||||
addAction(aSelectAll);
|
||||
addAction(aSelectRow);
|
||||
addRelatedCardView();
|
||||
addRelatedCardActions();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -177,10 +166,9 @@ void CardMenu::createTableMenu()
|
|||
if (card->getFaceDown()) {
|
||||
addAction(aPeek);
|
||||
}
|
||||
|
||||
addRelatedCardView();
|
||||
addRelatedCardActions();
|
||||
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addMenu(new MoveMenu(player));
|
||||
addSeparator();
|
||||
addAction(aAttach);
|
||||
if (card->getAttachedTo()) {
|
||||
|
|
@ -191,9 +179,6 @@ void CardMenu::createTableMenu()
|
|||
addMenu(new PtMenu(player));
|
||||
addAction(aSetAnnotation);
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addMenu(new MoveMenu(player));
|
||||
addSeparator();
|
||||
addAction(aSelectAll);
|
||||
addAction(aSelectRow);
|
||||
|
||||
|
|
@ -209,67 +194,81 @@ void CardMenu::createTableMenu()
|
|||
}
|
||||
addSeparator();
|
||||
addMenu(mCardCounters);
|
||||
addRelatedCardView();
|
||||
addRelatedCardActions();
|
||||
}
|
||||
|
||||
void CardMenu::createStackMenu()
|
||||
void CardMenu::createStackMenu(bool canModifyCard)
|
||||
{
|
||||
bool canModifyCard = player->getPlayerInfo()->judge || card->getOwner() == player;
|
||||
|
||||
// Card is on the stack
|
||||
if (canModifyCard) {
|
||||
addAction(aAttach);
|
||||
addAction(aDrawArrow);
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addMenu(new MoveMenu(player));
|
||||
addSeparator();
|
||||
addAction(aSelectAll);
|
||||
} else {
|
||||
if (!canModifyCard) {
|
||||
addAction(aDrawArrow);
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addSeparator();
|
||||
addAction(aSelectAll);
|
||||
addRelatedCardView();
|
||||
addRelatedCardActions();
|
||||
return;
|
||||
}
|
||||
|
||||
addAction(aPlay);
|
||||
addAction(aPlayFacedown);
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addMenu(new MoveMenu(player));
|
||||
addSeparator();
|
||||
addAction(aAttach);
|
||||
addAction(aDrawArrow);
|
||||
addSeparator();
|
||||
addAction(aSelectAll);
|
||||
addRelatedCardView();
|
||||
addRelatedCardActions();
|
||||
}
|
||||
|
||||
void CardMenu::createGraveyardOrExileMenu()
|
||||
void CardMenu::createGraveyardOrExileMenu(bool canModifyCard)
|
||||
{
|
||||
bool canModifyCard = player->getPlayerInfo()->judge || card->getOwner() == player;
|
||||
|
||||
// Card is in the graveyard or exile
|
||||
if (canModifyCard) {
|
||||
addAction(aPlay);
|
||||
addAction(aPlayFacedown);
|
||||
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addMenu(new MoveMenu(player));
|
||||
addSeparator();
|
||||
addAction(aSelectAll);
|
||||
addAction(aSelectColumn);
|
||||
|
||||
addSeparator();
|
||||
addAction(aAttach);
|
||||
if (!canModifyCard) {
|
||||
addAction(aDrawArrow);
|
||||
} else {
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addSeparator();
|
||||
addAction(aSelectAll);
|
||||
addAction(aSelectColumn);
|
||||
addSeparator();
|
||||
addAction(aDrawArrow);
|
||||
addRelatedCardView();
|
||||
addRelatedCardActions();
|
||||
return;
|
||||
}
|
||||
|
||||
addAction(aPlay);
|
||||
addAction(aPlayFacedown);
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addMenu(new MoveMenu(player));
|
||||
addSeparator();
|
||||
addAction(aAttach);
|
||||
addAction(aDrawArrow);
|
||||
addSeparator();
|
||||
addAction(aSelectAll);
|
||||
addAction(aSelectColumn);
|
||||
addRelatedCardView();
|
||||
addRelatedCardActions();
|
||||
}
|
||||
|
||||
void CardMenu::createHandOrCustomZoneMenu()
|
||||
void CardMenu::createHandOrCustomZoneMenu(bool canModifyCard)
|
||||
{
|
||||
if (!canModifyCard) {
|
||||
addAction(aDrawArrow);
|
||||
addSeparator();
|
||||
addAction(aClone);
|
||||
addSeparator();
|
||||
addAction(aSelectAll);
|
||||
addRelatedCardView();
|
||||
addRelatedCardActions();
|
||||
return;
|
||||
}
|
||||
|
||||
// Card is in hand or a custom zone specified by server
|
||||
addAction(aPlay);
|
||||
addAction(aPlayFacedown);
|
||||
|
|
@ -285,7 +284,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,11 +297,18 @@ void CardMenu::createHandOrCustomZoneMenu()
|
|||
}
|
||||
|
||||
addRelatedCardView();
|
||||
if (card->getZone()->getName() == "hand") {
|
||||
if (card->getZone()->getName() == ZoneNames::HAND) {
|
||||
addRelatedCardActions();
|
||||
}
|
||||
}
|
||||
|
||||
void CardMenu::createZonelessMenu(bool canModifyCard)
|
||||
{
|
||||
if (canModifyCard) {
|
||||
addMenu(new MoveMenu(player));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Populates the menu with an action for each active player.
|
||||
*
|
||||
|
|
@ -446,7 +452,7 @@ void CardMenu::retranslateUi()
|
|||
aRevealToAll->setText(tr("&All players"));
|
||||
//: Turn sideways or back again
|
||||
aTap->setText(tr("&Tap / Untap"));
|
||||
aDoesntUntap->setText(tr("Toggle &normal untapping"));
|
||||
aDoesntUntap->setText(tr("Skip &untapping"));
|
||||
//: Turn face up/face down
|
||||
aFlip->setText(tr("T&urn Over")); // Only the user facing names in client got renamed to "turn over"
|
||||
// All code and proto bits are still unchanged (flip) for compatibility reasons
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@ class CardMenu : public QMenu
|
|||
public:
|
||||
explicit CardMenu(Player *player, const CardItem *card, bool shortcutsActive);
|
||||
void removePlayer(Player *playerToRemove);
|
||||
void createTableMenu();
|
||||
void createStackMenu();
|
||||
void createGraveyardOrExileMenu();
|
||||
void createHandOrCustomZoneMenu();
|
||||
void createTableMenu(bool canModifyCard);
|
||||
void createStackMenu(bool canModifyCard);
|
||||
void createGraveyardOrExileMenu(bool canModifyCard);
|
||||
void createHandOrCustomZoneMenu(bool canModifyCard);
|
||||
void createZonelessMenu(bool canModifyCard);
|
||||
|
||||
QMenu *mCardCounters;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,15 +7,23 @@
|
|||
#ifndef COCKATRICE_CUSTOM_ZONE_MENU_H
|
||||
#define COCKATRICE_CUSTOM_ZONE_MENU_H
|
||||
|
||||
#include "abstract_player_component.h"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
class Player;
|
||||
class CustomZoneMenu : public QMenu
|
||||
class CustomZoneMenu : public QMenu, public AbstractPlayerComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CustomZoneMenu(Player *player);
|
||||
void retranslateUi();
|
||||
void retranslateUi() override;
|
||||
void setShortcutsActive() override
|
||||
{
|
||||
}
|
||||
void setShortcutsInactive() override
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
Player *player;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
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<QVariant>() << "deck" << 0);
|
||||
aMoveGraveToTopLibrary->setData(QList<QVariant>() << ZoneNames::DECK << 0);
|
||||
|
||||
aMoveGraveToBottomLibrary = new QAction(this);
|
||||
aMoveGraveToBottomLibrary->setData(QList<QVariant>() << "deck" << -1);
|
||||
aMoveGraveToBottomLibrary->setData(QList<QVariant>() << ZoneNames::DECK << -1);
|
||||
|
||||
aMoveGraveToHand = new QAction(this);
|
||||
aMoveGraveToHand->setData(QList<QVariant>() << "hand" << 0);
|
||||
aMoveGraveToHand->setData(QList<QVariant>() << ZoneNames::HAND << 0);
|
||||
|
||||
aMoveGraveToRfg = new QAction(this);
|
||||
aMoveGraveToRfg->setData(QList<QVariant>() << "rfg" << 0);
|
||||
aMoveGraveToRfg->setData(QList<QVariant>() << ZoneNames::EXILE << 0);
|
||||
|
||||
connect(aMoveGraveToTopLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone);
|
||||
connect(aMoveGraveToBottomLibrary, &QAction::triggered, grave, &PileZoneLogic::moveAllToZone);
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@
|
|||
#define COCKATRICE_GRAVE_MENU_H
|
||||
|
||||
#include "../../../interface/widgets/menus/tearoff_menu.h"
|
||||
#include "abstract_player_component.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
|
||||
class Player;
|
||||
class GraveyardMenu : public TearOffMenu
|
||||
class GraveyardMenu : public TearOffMenu, public AbstractPlayerComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
|
|
@ -25,9 +26,9 @@ public:
|
|||
void createViewActions();
|
||||
void populateRevealRandomMenuWithActivePlayers();
|
||||
void onRevealRandomTriggered();
|
||||
void retranslateUi();
|
||||
void setShortcutsActive();
|
||||
void setShortcutsInactive();
|
||||
void retranslateUi() override;
|
||||
void setShortcutsActive() override;
|
||||
void setShortcutsInactive() override;
|
||||
|
||||
QMenu *mRevealRandomGraveyardCard = nullptr;
|
||||
QMenu *moveGraveMenu = nullptr;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
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<QVariant>() << "deck" << 0);
|
||||
aMoveHandToTopLibrary->setData(QList<QVariant>() << ZoneNames::DECK << 0);
|
||||
aMoveHandToBottomLibrary = new QAction(this);
|
||||
aMoveHandToBottomLibrary->setData(QList<QVariant>() << "deck" << -1);
|
||||
aMoveHandToBottomLibrary->setData(QList<QVariant>() << ZoneNames::DECK << -1);
|
||||
aMoveHandToGrave = new QAction(this);
|
||||
aMoveHandToGrave->setData(QList<QVariant>() << "grave" << 0);
|
||||
aMoveHandToGrave->setData(QList<QVariant>() << ZoneNames::GRAVE << 0);
|
||||
aMoveHandToRfg = new QAction(this);
|
||||
aMoveHandToRfg->setData(QList<QVariant>() << "rfg" << 0);
|
||||
aMoveHandToRfg->setData(QList<QVariant>() << ZoneNames::EXILE << 0);
|
||||
|
||||
auto hand = player->getHandZone();
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#define COCKATRICE_HAND_MENU_H
|
||||
|
||||
#include "../../../interface/widgets/menus/tearoff_menu.h"
|
||||
#include "abstract_player_component.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
|
|
@ -15,7 +16,7 @@
|
|||
class Player;
|
||||
class PlayerActions;
|
||||
|
||||
class HandMenu : public TearOffMenu
|
||||
class HandMenu : public TearOffMenu, public AbstractPlayerComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
|
@ -31,9 +32,9 @@ public:
|
|||
return mRevealRandomHandCard;
|
||||
}
|
||||
|
||||
void retranslateUi();
|
||||
void setShortcutsActive();
|
||||
void setShortcutsInactive();
|
||||
void retranslateUi() override;
|
||||
void setShortcutsActive() override;
|
||||
void setShortcutsInactive() override;
|
||||
|
||||
private slots:
|
||||
void populateRevealHandMenuWithActivePlayers();
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#define COCKATRICE_LIBRARY_MENU_H
|
||||
|
||||
#include "../../../interface/widgets/menus/tearoff_menu.h"
|
||||
#include "abstract_player_component.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
|
|
@ -15,7 +16,7 @@
|
|||
class Player;
|
||||
class PlayerActions;
|
||||
|
||||
class LibraryMenu : public TearOffMenu
|
||||
class LibraryMenu : public TearOffMenu, public AbstractPlayerComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
|
|
@ -28,15 +29,15 @@ public:
|
|||
void createShuffleActions();
|
||||
void createMoveActions();
|
||||
void createViewActions();
|
||||
void retranslateUi();
|
||||
void retranslateUi() override;
|
||||
void populateRevealLibraryMenuWithActivePlayers();
|
||||
void populateLendLibraryMenuWithActivePlayers();
|
||||
void populateRevealTopCardMenuWithActivePlayers();
|
||||
void onRevealLibraryTriggered();
|
||||
void onLendLibraryTriggered();
|
||||
void onRevealTopCardTriggered();
|
||||
void setShortcutsActive();
|
||||
void setShortcutsInactive();
|
||||
void setShortcutsActive() override;
|
||||
void setShortcutsInactive() override;
|
||||
|
||||
[[nodiscard]] bool isAlwaysRevealTopCardChecked() const
|
||||
{
|
||||
|
|
@ -88,7 +89,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 +103,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;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ MoveMenu::MoveMenu(Player *player) : QMenu(tr("Move to"))
|
|||
aMoveToBottomLibrary = new QAction(this);
|
||||
aMoveToBottomLibrary->setData(cmMoveToBottomLibrary);
|
||||
aMoveToXfromTopOfLibrary = new QAction(this);
|
||||
aMoveToTable = new QAction(this);
|
||||
aMoveToTable->setData(cmMoveToTable);
|
||||
aMoveToGraveyard = new QAction(this);
|
||||
aMoveToHand = new QAction(this);
|
||||
aMoveToHand->setData(cmMoveToHand);
|
||||
|
|
@ -22,6 +24,7 @@ MoveMenu::MoveMenu(Player *player) : QMenu(tr("Move to"))
|
|||
connect(aMoveToBottomLibrary, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction);
|
||||
connect(aMoveToXfromTopOfLibrary, &QAction::triggered, player->getPlayerActions(),
|
||||
&PlayerActions::actMoveCardXCardsFromTop);
|
||||
connect(aMoveToTable, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction);
|
||||
connect(aMoveToHand, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction);
|
||||
connect(aMoveToGraveyard, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction);
|
||||
connect(aMoveToExile, &QAction::triggered, player->getPlayerActions(), &PlayerActions::cardMenuAction);
|
||||
|
|
@ -30,6 +33,8 @@ MoveMenu::MoveMenu(Player *player) : QMenu(tr("Move to"))
|
|||
addAction(aMoveToXfromTopOfLibrary);
|
||||
addAction(aMoveToBottomLibrary);
|
||||
addSeparator();
|
||||
addAction(aMoveToTable);
|
||||
addSeparator();
|
||||
addAction(aMoveToHand);
|
||||
addSeparator();
|
||||
addAction(aMoveToGraveyard);
|
||||
|
|
@ -47,6 +52,7 @@ void MoveMenu::setShortcutsActive()
|
|||
|
||||
aMoveToTopLibrary->setShortcuts(shortcuts.getShortcut("Player/aMoveToTopLibrary"));
|
||||
aMoveToBottomLibrary->setShortcuts(shortcuts.getShortcut("Player/aMoveToBottomLibrary"));
|
||||
aMoveToTable->setShortcuts(shortcuts.getShortcut("Player/aMoveToTable"));
|
||||
aMoveToHand->setShortcuts(shortcuts.getShortcut("Player/aMoveToHand"));
|
||||
aMoveToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveToGraveyard"));
|
||||
aMoveToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveToExile"));
|
||||
|
|
@ -57,7 +63,8 @@ void MoveMenu::retranslateUi()
|
|||
aMoveToTopLibrary->setText(tr("&Top of library in random order"));
|
||||
aMoveToXfromTopOfLibrary->setText(tr("X cards from the top of library..."));
|
||||
aMoveToBottomLibrary->setText(tr("&Bottom of library in random order"));
|
||||
aMoveToTable->setText(tr("T&able"));
|
||||
aMoveToHand->setText(tr("&Hand"));
|
||||
aMoveToGraveyard->setText(tr("&Graveyard"));
|
||||
aMoveToExile->setText(tr("&Exile"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public:
|
|||
QAction *aMoveToBottomLibrary = nullptr;
|
||||
|
||||
QAction *aMoveToHand = nullptr;
|
||||
QAction *aMoveToTable = nullptr;
|
||||
QAction *aMoveToGraveyard = nullptr;
|
||||
QAction *aMoveToExile = nullptr;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,38 +10,29 @@
|
|||
|
||||
#include <libcockatrice/protocol/pb/command_reveal_cards.pb.h>
|
||||
|
||||
PlayerMenu::PlayerMenu(Player *_player) : player(_player)
|
||||
PlayerMenu::PlayerMenu(Player *_player) : QObject(_player), player(_player)
|
||||
{
|
||||
playerMenu = new TearOffMenu();
|
||||
|
||||
if (player->getPlayerInfo()->getLocalOrJudge()) {
|
||||
handMenu = new HandMenu(player, player->getPlayerActions(), playerMenu);
|
||||
playerMenu->addMenu(handMenu);
|
||||
|
||||
libraryMenu = new LibraryMenu(player, playerMenu);
|
||||
playerMenu->addMenu(libraryMenu);
|
||||
handMenu = addManagedMenu<HandMenu>(player, player->getPlayerActions(), playerMenu);
|
||||
libraryMenu = addManagedMenu<LibraryMenu>(player, playerMenu);
|
||||
} else {
|
||||
handMenu = nullptr;
|
||||
libraryMenu = nullptr;
|
||||
}
|
||||
|
||||
graveMenu = new GraveyardMenu(player, playerMenu);
|
||||
playerMenu->addMenu(graveMenu);
|
||||
|
||||
rfgMenu = new RfgMenu(player, playerMenu);
|
||||
playerMenu->addMenu(rfgMenu);
|
||||
graveMenu = addManagedMenu<GraveyardMenu>(player, playerMenu);
|
||||
rfgMenu = addManagedMenu<RfgMenu>(player, playerMenu);
|
||||
|
||||
if (player->getPlayerInfo()->getLocalOrJudge()) {
|
||||
sideboardMenu = new SideboardMenu(player, playerMenu);
|
||||
playerMenu->addMenu(sideboardMenu);
|
||||
|
||||
customZonesMenu = new CustomZoneMenu(player);
|
||||
playerMenu->addMenu(customZonesMenu);
|
||||
sideboardMenu = addManagedMenu<SideboardMenu>(player, playerMenu);
|
||||
customZonesMenu = addManagedMenu<CustomZoneMenu>(player);
|
||||
playerMenu->addSeparator();
|
||||
|
||||
countersMenu = playerMenu->addMenu(QString());
|
||||
|
||||
utilityMenu = new UtilityMenu(player, playerMenu);
|
||||
utilityMenu = createManagedComponent<UtilityMenu>(player, playerMenu);
|
||||
} else {
|
||||
sideboardMenu = nullptr;
|
||||
customZonesMenu = nullptr;
|
||||
|
|
@ -50,8 +41,7 @@ PlayerMenu::PlayerMenu(Player *_player) : player(_player)
|
|||
}
|
||||
|
||||
if (player->getPlayerInfo()->getLocal()) {
|
||||
sayMenu = new SayMenu(player);
|
||||
playerMenu->addMenu(sayMenu);
|
||||
sayMenu = addManagedMenu<SayMenu>(player);
|
||||
} else {
|
||||
sayMenu = nullptr;
|
||||
}
|
||||
|
|
@ -99,40 +89,18 @@ void PlayerMenu::retranslateUi()
|
|||
{
|
||||
playerMenu->setTitle(tr("Player \"%1\"").arg(player->getPlayerInfo()->getName()));
|
||||
|
||||
if (handMenu) {
|
||||
handMenu->retranslateUi();
|
||||
}
|
||||
if (libraryMenu) {
|
||||
libraryMenu->retranslateUi();
|
||||
}
|
||||
|
||||
graveMenu->retranslateUi();
|
||||
rfgMenu->retranslateUi();
|
||||
|
||||
if (sideboardMenu) {
|
||||
sideboardMenu->retranslateUi();
|
||||
for (auto *component : managedComponents) {
|
||||
component->retranslateUi();
|
||||
}
|
||||
|
||||
if (countersMenu) {
|
||||
countersMenu->setTitle(tr("&Counters"));
|
||||
}
|
||||
|
||||
if (customZonesMenu) {
|
||||
customZonesMenu->retranslateUi();
|
||||
}
|
||||
|
||||
QMapIterator<int, AbstractCounter *> counterIterator(player->getCounters());
|
||||
while (counterIterator.hasNext()) {
|
||||
counterIterator.next().value()->retranslateUi();
|
||||
}
|
||||
|
||||
if (utilityMenu) {
|
||||
utilityMenu->retranslateUi();
|
||||
}
|
||||
|
||||
if (sayMenu) {
|
||||
sayMenu->setTitle(tr("S&ay"));
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerMenu::refreshShortcuts()
|
||||
|
|
@ -153,52 +121,29 @@ void PlayerMenu::setShortcutsActive()
|
|||
{
|
||||
shortcutsActive = true;
|
||||
|
||||
if (handMenu) {
|
||||
handMenu->setShortcutsActive();
|
||||
}
|
||||
if (libraryMenu) {
|
||||
libraryMenu->setShortcutsActive();
|
||||
}
|
||||
graveMenu->setShortcutsActive();
|
||||
// No shortcuts for RfgMenu yet
|
||||
|
||||
if (sideboardMenu) {
|
||||
sideboardMenu->setShortcutsActive();
|
||||
for (auto *component : managedComponents) {
|
||||
component->setShortcutsActive();
|
||||
}
|
||||
|
||||
// Counters implement AbstractPlayerComponent but are iterated via Player::counters
|
||||
// (the authoritative source) rather than managedComponents to avoid a redundant
|
||||
// list that must stay in sync with the map.
|
||||
QMapIterator<int, AbstractCounter *> counterIterator(player->getCounters());
|
||||
while (counterIterator.hasNext()) {
|
||||
counterIterator.next().value()->setShortcutsActive();
|
||||
}
|
||||
|
||||
if (utilityMenu) {
|
||||
utilityMenu->setShortcutsActive();
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerMenu::setShortcutsInactive()
|
||||
{
|
||||
shortcutsActive = false;
|
||||
|
||||
if (handMenu) {
|
||||
handMenu->setShortcutsInactive();
|
||||
}
|
||||
if (libraryMenu) {
|
||||
libraryMenu->setShortcutsInactive();
|
||||
}
|
||||
graveMenu->setShortcutsInactive();
|
||||
// No shortcuts for RfgMenu yet
|
||||
|
||||
if (sideboardMenu) {
|
||||
sideboardMenu->setShortcutsInactive();
|
||||
for (auto *component : managedComponents) {
|
||||
component->setShortcutsInactive();
|
||||
}
|
||||
|
||||
QMapIterator<int, AbstractCounter *> counterIterator(player->getCounters());
|
||||
while (counterIterator.hasNext()) {
|
||||
counterIterator.next().value()->setShortcutsInactive();
|
||||
}
|
||||
|
||||
if (utilityMenu) {
|
||||
utilityMenu->setShortcutsInactive();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* @file player_menu.h
|
||||
* @ingroup GameMenusPlayers
|
||||
* @brief TODO: Document this.
|
||||
* @brief Orchestrates lifecycle management for all player-bound UI components.
|
||||
*/
|
||||
|
||||
#ifndef COCKATRICE_PLAYER_MENU_H
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
#include "sideboard_menu.h"
|
||||
#include "utility_menu.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QObject>
|
||||
|
||||
|
|
@ -36,7 +37,8 @@ private slots:
|
|||
void refreshShortcuts();
|
||||
|
||||
public:
|
||||
PlayerMenu(Player *player);
|
||||
explicit PlayerMenu(Player *player);
|
||||
/// Lifecycle methods: delegate to all managedComponents, plus counters separately via player->getCounters().
|
||||
void retranslateUi();
|
||||
|
||||
QMenu *updateCardMenu(const CardItem *card);
|
||||
|
|
@ -66,7 +68,9 @@ public:
|
|||
return shortcutsActive;
|
||||
}
|
||||
|
||||
/// Delegates to all managedComponents, plus counters separately.
|
||||
void setShortcutsActive();
|
||||
/// Delegates to all managedComponents, plus counters separately.
|
||||
void setShortcutsInactive();
|
||||
|
||||
private:
|
||||
|
|
@ -82,9 +86,26 @@ private:
|
|||
SayMenu *sayMenu;
|
||||
CustomZoneMenu *customZonesMenu;
|
||||
|
||||
bool shortcutsActive;
|
||||
/// Drives AbstractPlayerComponent lifecycle delegation. Counters are iterated separately via player->getCounters().
|
||||
QList<AbstractPlayerComponent *> managedComponents;
|
||||
bool shortcutsActive = false;
|
||||
|
||||
void initSayMenu();
|
||||
/// Creates component, adds it as a submenu of playerMenu, and registers in managedComponents.
|
||||
template <typename MenuT, typename... Args> MenuT *addManagedMenu(Args &&...args)
|
||||
{
|
||||
auto *menu = new MenuT(std::forward<Args>(args)...);
|
||||
playerMenu->addMenu(menu);
|
||||
managedComponents.append(menu);
|
||||
return menu;
|
||||
}
|
||||
|
||||
/// Creates component and registers in managedComponents, but does NOT add it as a submenu.
|
||||
template <typename ComponentT, typename... Args> ComponentT *createManagedComponent(Args &&...args)
|
||||
{
|
||||
auto *component = new ComponentT(std::forward<Args>(args)...);
|
||||
managedComponents.append(component);
|
||||
return component;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_PLAYER_MENU_H
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
#include "../player.h"
|
||||
#include "../player_actions.h"
|
||||
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
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<QVariant>() << "deck" << 0);
|
||||
aMoveRfgToTopLibrary->setData(QList<QVariant>() << ZoneNames::DECK << 0);
|
||||
aMoveRfgToBottomLibrary = new QAction(this);
|
||||
aMoveRfgToBottomLibrary->setData(QList<QVariant>() << "deck" << -1);
|
||||
aMoveRfgToBottomLibrary->setData(QList<QVariant>() << ZoneNames::DECK << -1);
|
||||
aMoveRfgToHand = new QAction(this);
|
||||
aMoveRfgToHand->setData(QList<QVariant>() << "hand" << 0);
|
||||
aMoveRfgToHand->setData(QList<QVariant>() << ZoneNames::HAND << 0);
|
||||
aMoveRfgToGrave = new QAction(this);
|
||||
aMoveRfgToGrave->setData(QList<QVariant>() << "grave" << 0);
|
||||
aMoveRfgToGrave->setData(QList<QVariant>() << ZoneNames::GRAVE << 0);
|
||||
|
||||
connect(aMoveRfgToTopLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone);
|
||||
connect(aMoveRfgToBottomLibrary, &QAction::triggered, rfg, &PileZoneLogic::moveAllToZone);
|
||||
|
|
|
|||
|
|
@ -8,19 +8,26 @@
|
|||
#define COCKATRICE_RFG_MENU_H
|
||||
|
||||
#include "../../../interface/widgets/menus/tearoff_menu.h"
|
||||
#include "abstract_player_component.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
|
||||
class Player;
|
||||
class RfgMenu : public TearOffMenu
|
||||
class RfgMenu : public TearOffMenu, public AbstractPlayerComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RfgMenu(Player *player, QWidget *parent = nullptr);
|
||||
void createMoveActions();
|
||||
void createViewActions();
|
||||
void retranslateUi();
|
||||
void retranslateUi() override;
|
||||
void setShortcutsActive() override
|
||||
{
|
||||
}
|
||||
void setShortcutsInactive() override
|
||||
{
|
||||
}
|
||||
|
||||
QMenu *moveRfgMenu = nullptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,31 @@ SayMenu::SayMenu(Player *_player) : player(_player)
|
|||
{
|
||||
connect(&SettingsCache::instance().messages(), &MessageSettings::messageMacrosChanged, this, &SayMenu::initSayMenu);
|
||||
initSayMenu();
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void SayMenu::retranslateUi()
|
||||
{
|
||||
setTitle(tr("S&ay"));
|
||||
}
|
||||
|
||||
void SayMenu::setShortcutsActive()
|
||||
{
|
||||
shortcutsActive = true;
|
||||
|
||||
const auto menuActions = actions();
|
||||
for (int i = 0; i < menuActions.size() && i < 10; ++i) {
|
||||
menuActions[i]->setShortcut(QKeySequence("Ctrl+" + QString::number((i + 1) % 10)));
|
||||
}
|
||||
}
|
||||
|
||||
void SayMenu::setShortcutsInactive()
|
||||
{
|
||||
shortcutsActive = false;
|
||||
|
||||
for (auto *action : actions()) {
|
||||
action->setShortcut(QKeySequence());
|
||||
}
|
||||
}
|
||||
|
||||
void SayMenu::initSayMenu()
|
||||
|
|
@ -19,10 +44,11 @@ void SayMenu::initSayMenu()
|
|||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto *newAction = new QAction(SettingsCache::instance().messages().getMessageAt(i), this);
|
||||
if (i < 10) {
|
||||
newAction->setShortcut(QKeySequence("Ctrl+" + QString::number((i + 1) % 10)));
|
||||
}
|
||||
connect(newAction, &QAction::triggered, player->getPlayerActions(), &PlayerActions::actSayMessage);
|
||||
addAction(newAction);
|
||||
}
|
||||
}
|
||||
|
||||
if (shortcutsActive) {
|
||||
setShortcutsActive();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,18 +7,27 @@
|
|||
#ifndef COCKATRICE_SAY_MENU_H
|
||||
#define COCKATRICE_SAY_MENU_H
|
||||
|
||||
#include "abstract_player_component.h"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
class Player;
|
||||
class SayMenu : public QMenu
|
||||
class SayMenu : public QMenu, public AbstractPlayerComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SayMenu(Player *player);
|
||||
|
||||
void retranslateUi() override;
|
||||
void setShortcutsActive() override;
|
||||
void setShortcutsInactive() override;
|
||||
|
||||
private slots:
|
||||
void initSayMenu();
|
||||
|
||||
private:
|
||||
Player *player;
|
||||
bool shortcutsActive = false;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_SAY_MENU_H
|
||||
|
|
|
|||
|
|
@ -7,18 +7,20 @@
|
|||
#ifndef COCKATRICE_SIDEBOARD_MENU_H
|
||||
#define COCKATRICE_SIDEBOARD_MENU_H
|
||||
|
||||
#include "abstract_player_component.h"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
class Player;
|
||||
class SideboardMenu : public QMenu
|
||||
class SideboardMenu : public QMenu, public AbstractPlayerComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SideboardMenu(Player *player, QMenu *playerMenu);
|
||||
void retranslateUi();
|
||||
void setShortcutsActive();
|
||||
void setShortcutsInactive();
|
||||
void retranslateUi() override;
|
||||
void setShortcutsActive() override;
|
||||
void setShortcutsInactive() override;
|
||||
|
||||
private:
|
||||
Player *player;
|
||||
|
|
|
|||
|
|
@ -7,17 +7,19 @@
|
|||
#ifndef COCKATRICE_UTILITY_MENU_H
|
||||
#define COCKATRICE_UTILITY_MENU_H
|
||||
|
||||
#include "abstract_player_component.h"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
class Player;
|
||||
class UtilityMenu : public QMenu
|
||||
class UtilityMenu : public QMenu, public AbstractPlayerComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void populatePredefinedTokensMenu();
|
||||
void retranslateUi();
|
||||
void setShortcutsActive();
|
||||
void setShortcutsInactive();
|
||||
void retranslateUi() override;
|
||||
void setShortcutsActive() override;
|
||||
void setShortcutsInactive() override;
|
||||
|
||||
public:
|
||||
explicit UtilityMenu(Player *player, QMenu *playerMenu);
|
||||
|
|
|
|||
|
|
@ -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<QString> 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();
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <libcockatrice/filters/filter_string.h>
|
||||
#include <libcockatrice/protocol/pb/card_attributes.pb.h>
|
||||
#include <libcockatrice/protocol/pb/game_event.pb.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
inline Q_LOGGING_CATEGORY(PlayerLog, "player");
|
||||
|
||||
|
|
@ -155,37 +156,37 @@ public:
|
|||
|
||||
PileZoneLogic *getDeckZone()
|
||||
{
|
||||
return qobject_cast<PileZoneLogic *>(zones.value("deck"));
|
||||
return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::DECK));
|
||||
}
|
||||
|
||||
PileZoneLogic *getGraveZone()
|
||||
{
|
||||
return qobject_cast<PileZoneLogic *>(zones.value("grave"));
|
||||
return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::GRAVE));
|
||||
}
|
||||
|
||||
PileZoneLogic *getRfgZone()
|
||||
{
|
||||
return qobject_cast<PileZoneLogic *>(zones.value("rfg"));
|
||||
return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::EXILE));
|
||||
}
|
||||
|
||||
PileZoneLogic *getSideboardZone()
|
||||
{
|
||||
return qobject_cast<PileZoneLogic *>(zones.value("sb"));
|
||||
return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::SIDEBOARD));
|
||||
}
|
||||
|
||||
TableZoneLogic *getTableZone()
|
||||
{
|
||||
return qobject_cast<TableZoneLogic *>(zones.value("table"));
|
||||
return qobject_cast<TableZoneLogic *>(zones.value(ZoneNames::TABLE));
|
||||
}
|
||||
|
||||
StackZoneLogic *getStackZone()
|
||||
{
|
||||
return qobject_cast<StackZoneLogic *>(zones.value("stack"));
|
||||
return qobject_cast<StackZoneLogic *>(zones.value(ZoneNames::STACK));
|
||||
}
|
||||
|
||||
HandZoneLogic *getHandZone()
|
||||
{
|
||||
return qobject_cast<HandZoneLogic *>(zones.value("hand"));
|
||||
return qobject_cast<HandZoneLogic *>(zones.value(ZoneNames::HAND));
|
||||
}
|
||||
|
||||
AbstractCounter *addCounter(const ServerInfo_Counter &counter);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "../../interface/widgets/tabs/tab_game.h"
|
||||
#include "../../interface/widgets/utility/get_text_with_max.h"
|
||||
#include "../board/card_item.h"
|
||||
#include "../client/settings/card_counter_settings.h"
|
||||
#include "../dialogs/dlg_move_top_cards_until.h"
|
||||
#include "../dialogs/dlg_roll_dice.h"
|
||||
#include "../zones/hand_zone.h"
|
||||
|
|
@ -27,12 +28,15 @@
|
|||
#include <libcockatrice/protocol/pb/command_shuffle.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_undo_draw.pb.h>
|
||||
#include <libcockatrice/protocol/pb/context_move_card.pb.h>
|
||||
#include <libcockatrice/utility/expression.h>
|
||||
#include <libcockatrice/utility/trice_limits.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
// milliseconds in between triggers of the move top cards until action
|
||||
static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100;
|
||||
|
||||
PlayerActions::PlayerActions(Player *_player) : player(_player), lastTokenTableRow(0), movingCardsUntil(false)
|
||||
PlayerActions::PlayerActions(Player *_player)
|
||||
: QObject(_player), player(_player), lastTokenTableRow(0), movingCardsUntil(false)
|
||||
{
|
||||
moveTopCardTimer = new QTimer(this);
|
||||
moveTopCardTimer->setInterval(MOVE_TOP_CARD_UNTIL_INTERVAL);
|
||||
|
|
@ -63,25 +67,25 @@ 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 (!faceDown && 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 {
|
||||
tableRow = faceDown ? 2 : info.getUiAttributes().tableRow;
|
||||
QPoint gridPoint = QPoint(-1, TableZone::clampValidTableRow(2 - tableRow));
|
||||
QPoint gridPoint = QPoint(-1, TableZone::tableRowToGridY(tableRow));
|
||||
cardToMove->set_face_down(faceDown);
|
||||
if (!faceDown) {
|
||||
cardToMove->set_pt(info.getPowTough().toStdString());
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
|
@ -113,18 +117,13 @@ void PlayerActions::playCardToTable(const CardItem *card, bool faceDown)
|
|||
const CardInfo &info = exactCard.getInfo();
|
||||
|
||||
int tableRow = faceDown ? 2 : info.getUiAttributes().tableRow;
|
||||
// default instant/sorcery cards to the noncreatures row
|
||||
if (tableRow > 2) {
|
||||
tableRow = 1;
|
||||
}
|
||||
|
||||
QPoint gridPoint = QPoint(-1, TableZone::clampValidTableRow(2 - tableRow));
|
||||
QPoint gridPoint = QPoint(-1, TableZone::tableRowToGridY(tableRow));
|
||||
cardToMove->set_face_down(faceDown);
|
||||
if (!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 +131,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 +180,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 +193,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 +209,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 +222,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 +262,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 +291,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 +375,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 +385,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 +399,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 +414,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 +423,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 +449,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);
|
||||
|
|
@ -490,22 +483,19 @@ void PlayerActions::actMoveTopCardsUntil()
|
|||
{
|
||||
stopMoveTopCardsUntil();
|
||||
|
||||
DlgMoveTopCardsUntil dlg(player->getGame()->getTab(), movingCardsUntilExprs, movingCardsUntilNumberOfHits,
|
||||
movingCardsUntilAutoPlay);
|
||||
DlgMoveTopCardsUntil dlg(player->getGame()->getTab(), movingCardsUntilOptions);
|
||||
if (!dlg.exec()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto expr = dlg.getExpr();
|
||||
movingCardsUntilExprs = dlg.getExprs();
|
||||
movingCardsUntilNumberOfHits = dlg.getNumberOfHits();
|
||||
movingCardsUntilAutoPlay = dlg.isAutoPlay();
|
||||
movingCardsUntilOptions = dlg.getOptions();
|
||||
|
||||
if (player->getDeckZone()->getCards().empty()) {
|
||||
stopMoveTopCardsUntil();
|
||||
} else {
|
||||
movingCardsUntilFilter = FilterString(expr);
|
||||
movingCardsUntilCounter = movingCardsUntilNumberOfHits;
|
||||
movingCardsUntilCounter = movingCardsUntilOptions.numberOfHits;
|
||||
movingCardsUntil = true;
|
||||
actMoveTopCardToPlay();
|
||||
}
|
||||
|
|
@ -517,7 +507,7 @@ void PlayerActions::moveOneCardUntil(CardItem *card)
|
|||
|
||||
const bool isMatch = card && movingCardsUntilFilter.check(card->getCard().getCardPtr());
|
||||
|
||||
if (isMatch && movingCardsUntilAutoPlay) {
|
||||
if (isMatch && movingCardsUntilOptions.autoPlay) {
|
||||
// Directly calling playCard will deadlock, since we are already in the middle of processing an event.
|
||||
// Use QTimer::singleShot to queue up the playCard on the event loop.
|
||||
QTimer::singleShot(0, this, [card, this] { playCard(card, false); });
|
||||
|
|
@ -555,7 +545,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 +560,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 +574,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 +594,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 +609,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 +618,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 +644,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 +682,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 +752,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 +778,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 +799,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 +816,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 +832,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");
|
||||
|
||||
|
|
@ -880,7 +864,7 @@ void PlayerActions::actCreateToken()
|
|||
ExactCard correctedCard = CardDatabaseManager::query()->guessCard({lastTokenInfo.name, lastTokenInfo.providerId});
|
||||
if (correctedCard) {
|
||||
lastTokenInfo.name = correctedCard.getName();
|
||||
lastTokenTableRow = TableZone::clampValidTableRow(2 - correctedCard.getInfo().getUiAttributes().tableRow);
|
||||
lastTokenTableRow = TableZone::tableRowToGridY(correctedCard.getInfo().getUiAttributes().tableRow);
|
||||
if (lastTokenInfo.pt.isEmpty()) {
|
||||
lastTokenInfo.pt = correctedCard.getInfo().getPowTough();
|
||||
}
|
||||
|
|
@ -898,7 +882,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());
|
||||
|
|
@ -931,7 +915,7 @@ void PlayerActions::setLastToken(CardInfoPtr cardInfo)
|
|||
.providerId =
|
||||
SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardInfo->getName())};
|
||||
|
||||
lastTokenTableRow = TableZone::clampValidTableRow(2 - cardInfo->getUiAttributes().tableRow);
|
||||
lastTokenTableRow = TableZone::tableRowToGridY(cardInfo->getUiAttributes().tableRow);
|
||||
|
||||
utilityMenu->setAndEnableCreateAnotherTokenAction(tr("C&reate another %1 token").arg(lastTokenInfo.name));
|
||||
}
|
||||
|
|
@ -1079,7 +1063,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);
|
||||
}
|
||||
|
||||
|
|
@ -1099,13 +1083,11 @@ void PlayerActions::createCard(const CardItem *sourceCard,
|
|||
return;
|
||||
}
|
||||
|
||||
// get the target token's location
|
||||
// TODO: Define this QPoint into its own function along with the one below
|
||||
QPoint gridPoint = QPoint(-1, TableZone::clampValidTableRow(2 - cardInfo->getUiAttributes().tableRow));
|
||||
QPoint gridPoint = QPoint(-1, TableZone::tableRowToGridY(cardInfo->getUiAttributes().tableRow));
|
||||
|
||||
// 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 +1116,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 +1129,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 +1244,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);
|
||||
|
|
@ -1593,23 +1575,34 @@ void PlayerActions::actCardCounterTrigger()
|
|||
break;
|
||||
}
|
||||
case 11: { // set counter with dialog
|
||||
bool ok;
|
||||
player->setDialogSemaphore(true);
|
||||
|
||||
int oldValue = 0;
|
||||
if (player->getGameScene()->selectedItems().size() == 1) {
|
||||
auto *card = static_cast<CardItem *>(player->getGameScene()->selectedItems().first());
|
||||
oldValue = card->getCounters().value(counterId, 0);
|
||||
// If a single card is selected, we show the old value in the dialog. Otherwise, we show "x"
|
||||
QList<QGraphicsItem *> sel = player->getGameScene()->selectedItems();
|
||||
QString oldValueForDlg = "x";
|
||||
if (sel.size() == 1) {
|
||||
auto *card = dynamic_cast<CardItem *>(sel.first());
|
||||
oldValueForDlg = QString::number(card->getCounters().value(counterId, 0));
|
||||
}
|
||||
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Set counters"), tr("Number:"), oldValue,
|
||||
0, MAX_COUNTERS_ON_CARD, 1, &ok);
|
||||
|
||||
auto &cardCounterSettings = SettingsCache::instance().cardCounters();
|
||||
QString counterName = cardCounterSettings.displayName(counterId);
|
||||
|
||||
AbstractCounterDialog dialog(counterName, oldValueForDlg, player->getGame()->getTab());
|
||||
int ok = dialog.exec();
|
||||
|
||||
player->setDialogSemaphore(false);
|
||||
if (player->clearCardsToDelete() || !ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &item : player->getGameScene()->selectedItems()) {
|
||||
auto *card = static_cast<CardItem *>(item);
|
||||
for (const auto &item : sel) {
|
||||
auto *card = dynamic_cast<CardItem *>(item);
|
||||
|
||||
int oldValue = card->getCounters().value(counterId, 0);
|
||||
Expression exp(oldValue);
|
||||
int number = static_cast<int>(exp.parse(dialog.textValue()));
|
||||
|
||||
auto *cmd = new Command_SetCardCounter;
|
||||
cmd->set_zone(card->getZone()->getName().toStdString());
|
||||
cmd->set_card_id(card->getId());
|
||||
|
|
@ -1651,7 +1644,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 +1697,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 +1708,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 +1720,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 +1731,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 +1744,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 +1758,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 +1821,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 +1863,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 +1885,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 +1907,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 +1919,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,12 +1931,40 @@ 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);
|
||||
break;
|
||||
}
|
||||
case cmMoveToTable: {
|
||||
// Each card needs its own command because table row, pt, and cipt vary per card
|
||||
for (const auto &card : cardList) {
|
||||
auto *cmd = new Command_MoveCard;
|
||||
cmd->set_start_player_id(startPlayerId);
|
||||
cmd->set_start_zone(startZone.toStdString());
|
||||
cmd->set_target_player_id(player->getPlayerInfo()->getId());
|
||||
cmd->set_target_zone(ZoneNames::TABLE);
|
||||
cmd->set_x(-1);
|
||||
|
||||
CardToMove *ctm = cmd->mutable_cards_to_move()->add_card();
|
||||
ctm->set_card_id(card->getId());
|
||||
ctm->set_face_down(false);
|
||||
|
||||
int tableRow = 0;
|
||||
ExactCard exactCard = card->getCard();
|
||||
if (exactCard) {
|
||||
const CardInfo &info = exactCard.getInfo();
|
||||
tableRow = info.getUiAttributes().tableRow;
|
||||
ctm->set_pt(info.getPowTough().toStdString());
|
||||
ctm->set_tapped(info.getUiAttributes().cipt);
|
||||
}
|
||||
|
||||
cmd->set_y(TableZone::tableRowToGridY(tableRow));
|
||||
commandList.append(cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#ifndef COCKATRICE_PLAYER_ACTIONS_H
|
||||
#define COCKATRICE_PLAYER_ACTIONS_H
|
||||
#include "../dialogs/dlg_create_token.h"
|
||||
#include "../dialogs/dlg_move_top_cards_until.h"
|
||||
#include "event_processing_options.h"
|
||||
#include "player.h"
|
||||
|
||||
|
|
@ -98,7 +99,9 @@ public slots:
|
|||
void actMoveTopCardToGrave();
|
||||
void actMoveTopCardToExile();
|
||||
void actMoveTopCardsToGrave();
|
||||
void actMoveTopCardsToGraveFaceDown();
|
||||
void actMoveTopCardsToExile();
|
||||
void actMoveTopCardsToExileFaceDown();
|
||||
void actMoveTopCardsUntil();
|
||||
void actMoveTopCardToBottom();
|
||||
void actDrawBottomCard();
|
||||
|
|
@ -108,7 +111,9 @@ public slots:
|
|||
void actMoveBottomCardToGrave();
|
||||
void actMoveBottomCardToExile();
|
||||
void actMoveBottomCardsToGrave();
|
||||
void actMoveBottomCardsToGraveFaceDown();
|
||||
void actMoveBottomCardsToExile();
|
||||
void actMoveBottomCardsToExileFaceDown();
|
||||
void actMoveBottomCardToTop();
|
||||
|
||||
void actSelectAll();
|
||||
|
|
@ -174,11 +179,12 @@ private:
|
|||
|
||||
bool movingCardsUntil;
|
||||
QTimer *moveTopCardTimer;
|
||||
QStringList movingCardsUntilExprs = {};
|
||||
int movingCardsUntilNumberOfHits = 1;
|
||||
bool movingCardsUntilAutoPlay = false;
|
||||
FilterString movingCardsUntilFilter;
|
||||
int movingCardsUntilCounter = 0;
|
||||
MoveTopCardsUntilOptions movingCardsUntilOptions;
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -30,8 +30,9 @@
|
|||
#include <libcockatrice/protocol/pb/event_set_card_counter.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_set_counter.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_shuffle.pb.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
PlayerEventHandler::PlayerEventHandler(Player *_player) : player(_player)
|
||||
PlayerEventHandler::PlayerEventHandler(Player *_player) : QObject(_player), player(_player)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +60,6 @@ void PlayerEventHandler::eventShuffle(const Event_Shuffle &event)
|
|||
// we want to close empty views as well
|
||||
if (length == 0 || length > absStart) { // note this assumes views always start at the top of the library
|
||||
view->close();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
qWarning() << zone->getName() << "of" << player->getPlayerInfo()->getName() << "holds empty zoneview!";
|
||||
|
|
@ -321,8 +321,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);
|
||||
}
|
||||
}
|
||||
|
|
@ -594,4 +594,4 @@ void PlayerEventHandler::processGameEvent(GameEvent::GameEventType type,
|
|||
qWarning() << "unhandled game event" << type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
136
cockatrice/src/game/z_value_layer_manager.h
Normal file
136
cockatrice/src/game/z_value_layer_manager.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/**
|
||||
* @file z_value_layer_manager.h
|
||||
* @ingroup GameGraphics
|
||||
* @brief Semantic Z-value layer management for game scene rendering.
|
||||
*
|
||||
* This file provides a structured approach to Z-value allocation in the game scene.
|
||||
* Z-values in Qt determine stacking order - higher values render on top of lower values.
|
||||
*
|
||||
* ## Layer Architecture
|
||||
*
|
||||
* The game scene is organized into three conceptual layers:
|
||||
*
|
||||
* 1. **Zone Layer (0-999)**: Zone backgrounds, containers, and static elements
|
||||
* - Zone backgrounds (0.5-1.0)
|
||||
* - Cards within zones (1.0 base + index)
|
||||
*
|
||||
* 2. **Card Layer (1-40,000,000)**: Dynamic card rendering on the table zone
|
||||
* - Cards use formula: (actualY + CardDimensions::HEIGHT) * 100000 + (actualX + 1) * 100
|
||||
* - Maximum card Z-value: ~40,000,000 (with 3 rows, actualY <= ~289)
|
||||
*
|
||||
* 3. **Overlay Layer (2,000,000,000+)**: UI elements that must appear above all cards
|
||||
* - Hovered cards (+1)
|
||||
* - Arrows (+3)
|
||||
* - Zone views (+4)
|
||||
* - Drag items (+5, +6)
|
||||
* - Top UI elements (+7)
|
||||
*
|
||||
* ## Design Rationale
|
||||
*
|
||||
* The large gap between card Z-values (max ~40M) and overlay base (2B) provides
|
||||
* safety margin for future table zone expansions while ensuring overlays always
|
||||
* render above cards regardless of table position.
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* Prefer using the semantic constants from ZValues namespace:
|
||||
* @code
|
||||
* card->setZValue(ZValues::HOVERED_CARD);
|
||||
* arrow->setZValue(ZValues::ARROWS);
|
||||
* @endcode
|
||||
*
|
||||
* Use validation functions to verify card Z-values during development:
|
||||
* @code
|
||||
* Q_ASSERT(ZValueLayerManager::isValidCardZValue(cardZ));
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
#ifndef Z_VALUE_LAYER_MANAGER_H
|
||||
#define Z_VALUE_LAYER_MANAGER_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
/**
|
||||
* @namespace ZValueLayerManager
|
||||
* @brief Utilities for Z-value validation and layer management.
|
||||
*/
|
||||
namespace ZValueLayerManager
|
||||
{
|
||||
|
||||
/**
|
||||
* @enum Layer
|
||||
* @brief Semantic layer identifiers for Z-value allocation.
|
||||
*
|
||||
* These represent conceptual rendering layers, not actual Z-values.
|
||||
* Use the corresponding ZValues constants for actual rendering.
|
||||
*/
|
||||
enum class Layer
|
||||
{
|
||||
/// Zone-level elements like backgrounds and containers
|
||||
Zone,
|
||||
/// Cards rendered in zones (uses sequential Z-values)
|
||||
Card,
|
||||
/// Temporary UI elements like hovered cards and drag items
|
||||
Overlay
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Maximum Z-value a card can have on the table zone.
|
||||
*
|
||||
* Based on table zone formula: (actualY + CardDimensions::HEIGHT) * 100000 + (actualX + 1) * 100
|
||||
* With maximum 3 rows and CardDimensions::HEIGHT = 102, actualY <= ~289.
|
||||
* Maximum: (289 + 102) * 100000 + 100 * 100 = 39,110,000
|
||||
*
|
||||
* We use 40,000,000 as a safe upper bound with margin.
|
||||
*/
|
||||
constexpr qreal CARD_Z_VALUE_MAX = 40000000.0;
|
||||
|
||||
/**
|
||||
* @brief Base Z-value for overlay elements.
|
||||
*
|
||||
* Must exceed CARD_Z_VALUE_MAX to ensure overlays render above all cards.
|
||||
* The 50x margin (2B vs 40M) provides safety for future expansion.
|
||||
*/
|
||||
constexpr qreal OVERLAY_BASE = 2000000000.0;
|
||||
|
||||
/**
|
||||
* @brief Validates that a Z-value is within the valid card range.
|
||||
*
|
||||
* Cards should have Z-values between CARD_BASE (1.0) and CARD_Z_VALUE_MAX.
|
||||
* Values outside this range may interfere with overlay rendering.
|
||||
*
|
||||
* @param zValue The Z-value to validate
|
||||
* @return true if the Z-value is valid for a card
|
||||
*/
|
||||
[[nodiscard]] constexpr bool isValidCardZValue(qreal zValue)
|
||||
{
|
||||
return zValue >= 1.0 && zValue <= CARD_Z_VALUE_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validates that a Z-value is in the overlay layer.
|
||||
*
|
||||
* Overlay elements should have Z-values at or above OVERLAY_BASE.
|
||||
*
|
||||
* @param zValue The Z-value to validate
|
||||
* @return true if the Z-value is valid for an overlay element
|
||||
*/
|
||||
[[nodiscard]] constexpr bool isOverlayZValue(qreal zValue)
|
||||
{
|
||||
return zValue >= OVERLAY_BASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the Z-value for a specific overlay element.
|
||||
*
|
||||
* @param offset Offset from OVERLAY_BASE (0-7 for current elements)
|
||||
* @return The absolute Z-value for the overlay element
|
||||
*/
|
||||
[[nodiscard]] constexpr qreal overlayZValue(qreal offset)
|
||||
{
|
||||
return OVERLAY_BASE + offset;
|
||||
}
|
||||
|
||||
} // namespace ZValueLayerManager
|
||||
|
||||
#endif // Z_VALUE_LAYER_MANAGER_H
|
||||
83
cockatrice/src/game/z_values.h
Normal file
83
cockatrice/src/game/z_values.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#ifndef Z_VALUES_H
|
||||
#define Z_VALUES_H
|
||||
|
||||
#include "card_dimensions.h"
|
||||
#include "z_value_layer_manager.h"
|
||||
|
||||
/**
|
||||
* @file z_values.h
|
||||
* @ingroup GameGraphics
|
||||
* @brief Centralized Z-value constants for rendering layer order.
|
||||
*
|
||||
* Z-values in Qt determine stacking order. Higher values render on top.
|
||||
* These constants define the visual layering hierarchy for the game scene.
|
||||
*
|
||||
* ## Layer Architecture
|
||||
*
|
||||
* See z_value_layer_manager.h for detailed documentation on the three-layer
|
||||
* architecture (Zone, Card, Overlay) and the rationale for Z-value choices.
|
||||
*
|
||||
* ## Quick Reference
|
||||
*
|
||||
* | Layer | Z-Value Range | Purpose |
|
||||
* |----------|------------------|-----------------------------------|
|
||||
* | Zone | 0.5 - 1.0 | Zone backgrounds, containers |
|
||||
* | Card | 1.0 - 40,000,000 | Cards on table (position-based) |
|
||||
* | Overlay | 2,000,000,000+ | UI elements above all cards |
|
||||
*/
|
||||
|
||||
namespace ZValues
|
||||
{
|
||||
|
||||
// Expose base for callers that need it
|
||||
constexpr qreal OVERLAY_BASE = ZValueLayerManager::OVERLAY_BASE;
|
||||
|
||||
// Overlay layer Z-values for items that should appear above normal cards
|
||||
constexpr qreal HOVERED_CARD = ZValueLayerManager::overlayZValue(1.0);
|
||||
constexpr qreal ARROWS = ZValueLayerManager::overlayZValue(3.0);
|
||||
constexpr qreal ZONE_VIEW_WIDGET = ZValueLayerManager::overlayZValue(4.0);
|
||||
constexpr qreal DRAG_ITEM = ZValueLayerManager::overlayZValue(5.0);
|
||||
constexpr qreal DRAG_ITEM_CHILD = ZValueLayerManager::overlayZValue(6.0);
|
||||
constexpr qreal TOP_UI = ZValueLayerManager::overlayZValue(7.0);
|
||||
|
||||
/**
|
||||
* @brief Compute Z-value for child drag items based on hotspot position.
|
||||
*
|
||||
* When dragging multiple cards together, each child card needs a unique Z-value
|
||||
* to prevent Z-fighting (flickering/flashing). The Z-values are derived from
|
||||
* their position when grabbed to conserve original stacking. The formula encodes
|
||||
* 2D coordinates into a single value where X has higher weight, ensuring
|
||||
* deterministic visual stacking.
|
||||
*
|
||||
* @param hotSpotX The X coordinate of the grab position
|
||||
* @param hotSpotY The Y coordinate of the grab position
|
||||
* @return Unique Z-value for the child drag item
|
||||
*/
|
||||
[[nodiscard]] constexpr qreal childDragZValue(qreal hotSpotX, qreal hotSpotY)
|
||||
{
|
||||
return DRAG_ITEM_CHILD + hotSpotX * 1000000 + hotSpotY * 1000 + 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compute Z-value for cards on the table zone based on position.
|
||||
*
|
||||
* Cards lower on the table (higher Y) render above cards higher up,
|
||||
* and cards to the right (higher X) render above cards to the left.
|
||||
* This creates natural visual stacking for overlapping cards.
|
||||
*
|
||||
* @param x The X coordinate of the card position
|
||||
* @param y The Y coordinate of the card position
|
||||
* @return Z-value for the card's table position
|
||||
*/
|
||||
[[nodiscard]] constexpr qreal tableCardZValue(qreal x, qreal y)
|
||||
{
|
||||
return (y + CardDimensions::HEIGHT_F) * 100000.0 + (x + 1) * 100.0;
|
||||
}
|
||||
|
||||
// Card layering (general architecture, not command-zone specific)
|
||||
constexpr qreal CARD_BASE = 1.0;
|
||||
constexpr qreal CARD_MAX = ZValueLayerManager::CARD_Z_VALUE_MAX;
|
||||
|
||||
} // namespace ZValues
|
||||
|
||||
#endif // Z_VALUES_H
|
||||
|
|
@ -56,7 +56,7 @@ void HandZone::handleDropEvent(const QList<CardDragItem *> &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);
|
||||
}
|
||||
|
|
|
|||
45
cockatrice/src/game/zones/logic/card_zone_algorithms.h
Normal file
45
cockatrice/src/game/zones/logic/card_zone_algorithms.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef COCKATRICE_CARD_ZONE_ALGORITHMS_H
|
||||
#define COCKATRICE_CARD_ZONE_ALGORITHMS_H
|
||||
|
||||
namespace CardZoneAlgorithms
|
||||
{
|
||||
|
||||
/**
|
||||
* Shared insertion logic for zones where cards become visible on add and follow
|
||||
* the standard pattern: clamp index, insert, clear identity if contents unknown,
|
||||
* reset state, show card.
|
||||
*
|
||||
* Zones with different post-add behavior (signal connections, positional resets,
|
||||
* hidden cards, or coordinate-based placement) should NOT use this — implement
|
||||
* addCardImpl directly instead.
|
||||
*
|
||||
* Template parameters allow testing with lightweight mocks that avoid Qt graphics
|
||||
* dependencies.
|
||||
*
|
||||
* @tparam CardList Must provide: size() -> int, insert(int, CardType*),
|
||||
* getContentsKnown() -> bool
|
||||
* @tparam CardType Must provide: setId(int), setCardRef(CardRefType),
|
||||
* resetState(bool), setVisible(bool)
|
||||
* @param keepAnnotations Forwarded to card->resetState(). Stack-like zones preserve
|
||||
* annotations across zone transitions; hand-like zones clear them.
|
||||
*/
|
||||
template <typename CardList, typename CardType>
|
||||
void addCardToList(CardList &cards, CardType *card, int x, bool keepAnnotations)
|
||||
{
|
||||
if (x < 0 || x >= cards.size()) {
|
||||
x = static_cast<int>(cards.size());
|
||||
}
|
||||
cards.insert(x, card);
|
||||
|
||||
if (!cards.getContentsKnown()) {
|
||||
card->setId(-1);
|
||||
card->setCardRef({});
|
||||
}
|
||||
|
||||
card->resetState(keepAnnotations);
|
||||
card->setVisible(true);
|
||||
}
|
||||
|
||||
} // namespace CardZoneAlgorithms
|
||||
|
||||
#endif // COCKATRICE_CARD_ZONE_ALGORITHMS_H
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
#include <QDebug>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
/**
|
||||
* @param _player the player that the zone belongs to
|
||||
|
|
@ -43,8 +44,10 @@ void CardZoneLogic::addCard(CardItem *card, const bool reorganize, const int x,
|
|||
|
||||
for (auto *view : views) {
|
||||
if (qobject_cast<ZoneViewZoneLogic *>(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")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "hand_zone_logic.h"
|
||||
|
||||
#include "../../board/card_item.h"
|
||||
#include "card_zone_algorithms.h"
|
||||
|
||||
HandZoneLogic::HandZoneLogic(Player *_player,
|
||||
const QString &_name,
|
||||
|
|
@ -14,16 +15,5 @@ HandZoneLogic::HandZoneLogic(Player *_player,
|
|||
|
||||
void HandZoneLogic::addCardImpl(CardItem *card, int x, int /*y*/)
|
||||
{
|
||||
// if x is negative set it to add at end
|
||||
if (x < 0 || x >= cards.size()) {
|
||||
x = cards.size();
|
||||
}
|
||||
cards.insert(x, card);
|
||||
|
||||
if (!cards.getContentsKnown()) {
|
||||
card->setId(-1);
|
||||
card->setCardRef({});
|
||||
}
|
||||
card->resetState();
|
||||
card->setVisible(true);
|
||||
}
|
||||
CardZoneAlgorithms::addCardToList(cards, card, x, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "stack_zone_logic.h"
|
||||
|
||||
#include "../../board/card_item.h"
|
||||
#include "card_zone_algorithms.h"
|
||||
|
||||
StackZoneLogic::StackZoneLogic(Player *_player,
|
||||
const QString &_name,
|
||||
|
|
@ -14,16 +15,5 @@ StackZoneLogic::StackZoneLogic(Player *_player,
|
|||
|
||||
void StackZoneLogic::addCardImpl(CardItem *card, int x, int /*y*/)
|
||||
{
|
||||
// if x is negative set it to add at end
|
||||
if (x < 0 || x >= cards.size()) {
|
||||
x = static_cast<int>(cards.size());
|
||||
}
|
||||
cards.insert(x, card);
|
||||
|
||||
if (!cards.getContentsKnown()) {
|
||||
card->setId(-1);
|
||||
card->setCardRef({});
|
||||
}
|
||||
card->resetState(true);
|
||||
card->setVisible(true);
|
||||
}
|
||||
CardZoneAlgorithms::addCardToList(cards, card, x, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<CardDragItem *> &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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@ void SelectZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|||
}
|
||||
}
|
||||
static_cast<GameScene *>(scene())->resizeRubberBand(
|
||||
deviceTransform(static_cast<GameScene *>(scene())->getViewportTransform()).map(pos));
|
||||
deviceTransform(static_cast<GameScene *>(scene())->getViewportTransform()).map(pos),
|
||||
cardsInSelectionRect.size());
|
||||
event->accept();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,11 @@ void StackZone::handleDropEvent(const QList<CardDragItem *> &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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <QGraphicsScene>
|
||||
|
|
@ -14,6 +15,7 @@
|
|||
#include <libcockatrice/card/card_info.h>
|
||||
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
const QColor TableZone::BACKGROUND_COLOR = QColor(100, 100, 100);
|
||||
const QColor TableZone::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<CardDragItem *> &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<CardItem *> 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<const CardItem *>(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);
|
||||
|
|
@ -378,3 +382,11 @@ int TableZone::clampValidTableRow(const int row)
|
|||
return TABLEROWS - 1;
|
||||
return row;
|
||||
}
|
||||
|
||||
int TableZone::tableRowToGridY(int tableRow)
|
||||
{
|
||||
if (tableRow > 2) {
|
||||
tableRow = 1;
|
||||
}
|
||||
return clampValidTableRow(2 - tableRow);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
@ -151,6 +151,13 @@ public:
|
|||
|
||||
static int clampValidTableRow(const int row);
|
||||
|
||||
/**
|
||||
* Converts a card's logical table row (0=creatures, 1=noncreatures, 2=lands)
|
||||
* to the corresponding grid Y coordinate. Cards with tableRow > 2 (e.g.,
|
||||
* instants/sorceries) default to the noncreatures row.
|
||||
*/
|
||||
static int tableRowToGridY(int tableRow);
|
||||
|
||||
/**
|
||||
Resizes the TableZone in case CardItems are within or
|
||||
outside of the TableZone constraints.
|
||||
|
|
|
|||
|
|
@ -73,7 +73,10 @@ void ZoneViewZone::initializeCards(const QList<const ServerInfo_Card *> &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<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->contentsKnown()) {
|
||||
|
|
@ -91,8 +94,10 @@ void ZoneViewZone::initializeCards(const QList<const ServerInfo_Card *> &cardLis
|
|||
int number = numberCards == -1 ? c.size() : (numberCards < c.size() ? numberCards : c.size());
|
||||
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<ZoneViewZoneLogic *>(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<CardDragItem *> &dragItems,
|
|||
cmd.set_y(0);
|
||||
cmd.set_is_reversed(qobject_cast<ZoneViewZoneLogic *>(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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <QCheckBox>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ CardPictureLoader::CardPictureLoader() : QObject(nullptr)
|
|||
connect(&SettingsCache::instance(), &SettingsCache::picDownloadChanged, this,
|
||||
&CardPictureLoader::picDownloadChanged);
|
||||
|
||||
qRegisterMetaType<ExactCard>();
|
||||
connect(worker, &CardPictureLoaderWorker::imageLoaded, this, &CardPictureLoader::imageLoaded);
|
||||
|
||||
statusBar = new CardPictureLoaderStatusBar(nullptr);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <QtConcurrentRun>
|
||||
#include <libcockatrice/card/database/card_database.h>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/card/import/card_name_normalizer.h>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <QPrinter>
|
||||
#include <QTextCursor>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <optional>
|
||||
|
||||
inline Q_LOGGING_CATEGORY(DeckLoaderLog, "deck_loader");
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <QMap>
|
||||
#include <QPixmap>
|
||||
#include <libcockatrice/network/server/remote/user_level.h>
|
||||
#include <optional>
|
||||
|
||||
inline Q_LOGGING_CATEGORY(PixelMapGeneratorLog, "pixel_map_generator");
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <QPixmapCache>
|
||||
#include <QStandardPaths>
|
||||
#include <QString>
|
||||
#include <QStyle>
|
||||
#include <QStyleFactory>
|
||||
#include <QStyleHints>
|
||||
#include <Qt>
|
||||
|
|
@ -89,6 +90,11 @@ struct PaletteColorInfo
|
|||
|
||||
ThemeManager::ThemeManager(QObject *parent) : QObject(parent)
|
||||
{
|
||||
defaultStyleName = qApp->style()->objectName();
|
||||
// FIXME workaround for windows11 style being broken
|
||||
if (defaultStyleName == "windows11") {
|
||||
defaultStyleName = "windowsvista";
|
||||
}
|
||||
ensureThemeDirectoryExists();
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
|
||||
connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, &ThemeManager::themeChangedSlot);
|
||||
|
|
@ -177,7 +183,7 @@ QBrush ThemeManager::loadExtraBrush(QString fileName, QBrush &fallbackBrush)
|
|||
|
||||
static inline QPalette createDarkGreenFusionPalette()
|
||||
{
|
||||
QPalette p;
|
||||
QPalette p = QStyleFactory::create("Fusion")->standardPalette();
|
||||
|
||||
// ---------- Core backgrounds ----------
|
||||
p.setColor(QPalette::Window, QColor(30, 30, 30)); // #ff1e1e1e
|
||||
|
|
@ -246,7 +252,7 @@ static inline QPalette createDarkGreenFusionPalette()
|
|||
|
||||
static inline QPalette createLightGreenFusionPalette()
|
||||
{
|
||||
QPalette p;
|
||||
QPalette p = QStyleFactory::create("Fusion")->standardPalette();
|
||||
|
||||
// ---------- Core backgrounds ----------
|
||||
p.setColor(QPalette::Window, QColor(240, 240, 240)); // #fff0f0f0
|
||||
|
|
@ -330,13 +336,15 @@ void ThemeManager::themeChangedSlot()
|
|||
}
|
||||
|
||||
if (themeName == FUSION_THEME_NAME) {
|
||||
qApp->setStyle(QStyleFactory::create("Fusion"));
|
||||
QStyle *fusionStyle = QStyleFactory::create("Fusion");
|
||||
qApp->setStyle(fusionStyle);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
|
||||
QPalette palette;
|
||||
// Start from Fusion's own palette so dark mode is handled correctly,
|
||||
// then apply any tweaks on top of it.
|
||||
QPalette palette = fusionStyle->standardPalette();
|
||||
if (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark) {
|
||||
palette.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
|
||||
}
|
||||
|
||||
qApp->setPalette(palette);
|
||||
#endif
|
||||
} else if (themeName == FUSION_THEME_NAME_LIGHT) {
|
||||
|
|
@ -346,7 +354,7 @@ void ThemeManager::themeChangedSlot()
|
|||
qApp->setStyle(QStyleFactory::create("Fusion"));
|
||||
qApp->setPalette(createDarkGreenFusionPalette());
|
||||
} else {
|
||||
qApp->setStyle("");
|
||||
qApp->setStyle(QStyleFactory::create(defaultStyleName)); // setting the style also sets the palette
|
||||
}
|
||||
|
||||
if (dirPath.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
QString defaultStyleName;
|
||||
std::array<QBrush, Role::MaxRole + 1> brushes;
|
||||
QStringMap availableThemes;
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -343,11 +343,9 @@ void CardInfoPictureWidget::mousePressEvent(QMouseEvent *event)
|
|||
QWidget::mousePressEvent(event);
|
||||
if (event->button() == Qt::RightButton) {
|
||||
createRightClickMenu()->popup(QCursor::pos());
|
||||
} else {
|
||||
emit cardClicked();
|
||||
}
|
||||
|
||||
emit cardClicked();
|
||||
emit cardClicked(event);
|
||||
}
|
||||
|
||||
void CardInfoPictureWidget::hideEvent(QHideEvent *event)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ signals:
|
|||
void hoveredOnCard(const ExactCard &hoveredCard);
|
||||
void cardScaleFactorChanged(int _scale);
|
||||
void cardChanged(const ExactCard &card);
|
||||
void cardClicked();
|
||||
void cardClicked(QMouseEvent *event);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user