mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
Compare commits
84 commits
dd1f0d3eec
...
164a67404d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
164a67404d | ||
|
|
77f60e0526 | ||
|
|
e08402ac94 | ||
|
|
ff393b8990 | ||
|
|
00151380e3 | ||
|
|
344dbd2caa | ||
|
|
825aba953a | ||
|
|
ae43659b91 | ||
|
|
d4002dc52b | ||
|
|
f2c359ba9f | ||
|
|
52d3978f84 | ||
|
|
345b0e8ba9 | ||
|
|
78964779c6 | ||
|
|
7ca23430cd | ||
|
|
65e9f1b370 | ||
|
|
5d0d466930 | ||
|
|
edb5caec8a | ||
|
|
2335e064ac | ||
|
|
47c90cda10 | ||
|
|
4879ddbd0f | ||
|
|
c9853734ad | ||
|
|
6f0c5dca12 | ||
|
|
85fd052fdb | ||
|
|
3ccd44155d | ||
|
|
77f882ca37 | ||
|
|
674a607960 | ||
|
|
73d5a2a0ab | ||
|
|
095b135640 | ||
|
|
563833a88a | ||
|
|
4972ebcb97 | ||
|
|
94b91be5a2 | ||
|
|
93c5d85996 | ||
|
|
52c5bd2ea3 | ||
|
|
d30d275296 | ||
|
|
bbc7e462ff | ||
|
|
720adf5410 | ||
|
|
0369aaffa9 | ||
|
|
ded83aaab5 | ||
|
|
9e50c5ad6e | ||
|
|
06da7f815a | ||
|
|
5cf3358c99 | ||
|
|
d89b502342 | ||
|
|
a83d82b5e8 | ||
|
|
bf3562a85a | ||
|
|
30d3afc7b4 | ||
|
|
92d082e735 | ||
|
|
aa42cfbccc | ||
|
|
d42b9607c5 | ||
|
|
4793c1fc04 | ||
|
|
675e1f7447 | ||
|
|
858b270fa9 | ||
|
|
4d8ab54313 | ||
|
|
e6b4c51585 | ||
|
|
4ab875be48 | ||
|
|
f3fb3331d1 | ||
|
|
93309e01a2 | ||
|
|
b4e24f958a | ||
|
|
a4c4c67674 | ||
|
|
f33cfcb288 | ||
|
|
5fbc636f93 | ||
|
|
5ddfcd7ead | ||
|
|
518c7e6125 | ||
|
|
3b07b10b19 | ||
|
|
66b9338c90 | ||
|
|
77a3875c25 | ||
|
|
28c8474f03 | ||
|
|
11e2bb0d2f | ||
|
|
eac149656b | ||
|
|
e806085681 | ||
|
|
06b9ca368f | ||
|
|
67a212d677 | ||
|
|
4028606bd9 | ||
|
|
6e770eb7b2 | ||
|
|
e73dd2b520 | ||
|
|
ab13a6748f | ||
|
|
1e0fc35d94 | ||
|
|
bbc6003f0a | ||
|
|
ad4320cd7b | ||
|
|
c25a2c5c3b | ||
|
|
b85436461e | ||
|
|
f4b4365464 | ||
|
|
4980f88a7a | ||
|
|
e666a4978d | ||
|
|
c4c7cc0253 |
391 changed files with 9975 additions and 3574 deletions
200
.ci/compile.sh
200
.ci/compile.sh
|
|
@ -459,6 +459,206 @@ if [[ $MAKE_COVERAGE ]]; then
|
|||
echo "::endgroup::"
|
||||
fi
|
||||
|
||||
if [[ $RUNNER_OS == Windows && ! $MAKE_NO_CLIENT ]]; then
|
||||
echo "::group::Probe the installer's holder lookup"
|
||||
# The working directory is still the build dir here: the package steps below are what
|
||||
# leave and re-enter it, and that is what made a $BUILD_DIR-prefixed path name
|
||||
# build/build. Both paths are relative to the dir the probe was built into.
|
||||
probe="./installer-holder-probe.exe"
|
||||
if [[ ! -f $probe ]]; then
|
||||
echo "::error file=$0::installer-holder-probe.exe was not built"
|
||||
exit 1
|
||||
fi
|
||||
# The probe exists to name a holder that a process-and-module scan cannot see: one
|
||||
# that has a file open without having loaded it. Holding a temporary DLL with
|
||||
# FileShare.None is exactly that case, so this asserts the reason the probe was
|
||||
# added instead of only that the Restart Manager answers at all. It needs no NSIS
|
||||
# and no packaging, so it runs on every Windows build that gets this far.
|
||||
# The holder is a process of its own: the probe never names the process that started it,
|
||||
# which is what keeps an in-place uninstaller from being named as a holder of the
|
||||
# directory it is emptying. The same DLL held by the script that starts the probe
|
||||
# therefore has to come back as nothing, and that is the second case below.
|
||||
probe_check="./holder-probe-check.ps1"
|
||||
cat >"$probe_check" <<'POWERSHELL'
|
||||
$ErrorActionPreference = "Stop"
|
||||
$dir = Join-Path ([IO.Path]::GetTempPath()) "cockatrice-holder-probe-$PID"
|
||||
Remove-Item -Recurse -Force $dir -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force $dir | Out-Null
|
||||
$probe = (Resolve-Path -LiteralPath $env:PROBE_EXE).Path
|
||||
|
||||
function Invoke-Probe([string] $guardDir) {
|
||||
$start = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$start.FileName = $probe
|
||||
$start.UseShellExecute = $false
|
||||
$start.RedirectStandardOutput = $true
|
||||
$start.EnvironmentVariables["COCKATRICE_GUARD_DIR"] = $guardDir
|
||||
$process = [System.Diagnostics.Process]::Start($start)
|
||||
$read = $process.StandardOutput.ReadToEndAsync()
|
||||
if (-not $process.WaitForExit(20000)) {
|
||||
try { $process.Kill() } catch { }
|
||||
throw "the holder probe did not finish within 20 s for $guardDir"
|
||||
}
|
||||
$output = $read.Result
|
||||
$code = $process.ExitCode
|
||||
$process.Dispose()
|
||||
return @{ Code = $code; Output = $output }
|
||||
}
|
||||
|
||||
function Wait-UntilLocked([string] $path) {
|
||||
$deadline = (Get-Date).AddSeconds(15)
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
try {
|
||||
$open = [IO.File]::Open($path, "Open", "ReadWrite", "None")
|
||||
$open.Dispose()
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
Start-Sleep -Milliseconds 100
|
||||
}
|
||||
throw "the holder never locked $path"
|
||||
}
|
||||
|
||||
try {
|
||||
$file = Join-Path $dir "locked.dll"
|
||||
[IO.File]::WriteAllText($file, "x")
|
||||
|
||||
# The holder is a process of its own, not this script: the probe never names the process
|
||||
# that started it (the case below asserts that), so a file held here would be left out on
|
||||
# purpose and the check would pass for the wrong reason.
|
||||
$holderScript = Join-Path $dir "hold.ps1"
|
||||
Set-Content -LiteralPath $holderScript -Value @'
|
||||
param([string] $Path)
|
||||
$handle = [IO.File]::Open($Path, "Open", "ReadWrite", "None")
|
||||
Start-Sleep -Seconds 120
|
||||
'@
|
||||
$powershell = Join-Path $env:SystemRoot "System32\WindowsPowerShell\v1.0\powershell.exe"
|
||||
$holder = Start-Process -FilePath $powershell -PassThru -ArgumentList @(
|
||||
"-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "`"$holderScript`"", "-Path", "`"$file`"")
|
||||
try {
|
||||
Wait-UntilLocked $file
|
||||
$locked = Invoke-Probe $dir
|
||||
if ($locked.Code -ne 1) { throw "expected exit 1 for a locked file, got $($locked.Code) : $($locked.Output)" }
|
||||
if ($locked.Output -notmatch "pid\s+$($holder.Id)\b") {
|
||||
throw "the lookup did not name the process holding the file: $($locked.Output)"
|
||||
}
|
||||
} finally {
|
||||
try { $holder.Kill() } catch { }
|
||||
try { $holder.WaitForExit(5000) | Out-Null } catch { }
|
||||
}
|
||||
|
||||
# The probe is started by the installer, or by an uninstaller that runs in place from the
|
||||
# directory it is emptying, so the asking process holds files there itself. Naming it would
|
||||
# ask the user to close the program that is asking - a question neither the close nor the
|
||||
# end offer can answer, so the guard repeated it until the update was cancelled. A holder
|
||||
# that is only the asking process has to come back as nothing.
|
||||
$handle = [IO.File]::Open($file, "Open", "ReadWrite", "None")
|
||||
try {
|
||||
$self = Invoke-Probe $dir
|
||||
} finally {
|
||||
$handle.Dispose()
|
||||
}
|
||||
if ($self.Code -ne 0) { throw "expected exit 0 when the asking process is the only holder, got $($self.Code) : $($self.Output)" }
|
||||
if ($self.Output -match "pid\s+$PID\b") { throw "the lookup named the process that started it: $($self.Output)" }
|
||||
|
||||
# A payload executable that refuses to be replaced is running, whether or not this probe
|
||||
# may name it: the Restart Manager does not report every mapping, and the image path of a
|
||||
# process under another account cannot be read from here, so both process lookups can
|
||||
# come back empty while the program is still there. Held here by the asking process, so
|
||||
# that it cannot be named - the answer has to be "could not name it", never "nothing is
|
||||
# holding it", which is what sent an update into an uninstall that could only fail
|
||||
# halfway. Exit 3 is the code the guard answers by asking the user to close it and retry.
|
||||
$image = Join-Path $dir "oracle.exe"
|
||||
[IO.File]::WriteAllText($image, "x")
|
||||
$handle = [IO.File]::Open($image, "Open", "ReadWrite", "None")
|
||||
try {
|
||||
$unnameable = Invoke-Probe $dir
|
||||
} finally {
|
||||
$handle.Dispose()
|
||||
}
|
||||
if ($unnameable.Code -ne 3) {
|
||||
throw "expected exit 3 for a payload executable that is in use but cannot be named, got $($unnameable.Code) : $($unnameable.Output)"
|
||||
}
|
||||
if ($unnameable.Output -notmatch "oracle\.exe is in use") {
|
||||
throw "the answer did not name the executable it could not replace: $($unnameable.Output)"
|
||||
}
|
||||
|
||||
$empty = Join-Path $dir "empty"
|
||||
New-Item -ItemType Directory -Force $empty | Out-Null
|
||||
$nothing = Invoke-Probe $empty
|
||||
if ($nothing.Code -ne 0) { throw "expected exit 0 for a directory with nothing open, got $($nothing.Code) : $($nothing.Output)" }
|
||||
# A process running from the directory is a holder even when it has no file open: an
|
||||
# instance holds its payload as mapped images, not as handles, and that is the state
|
||||
# the guard used to miss - it passed, the purge failed, and the install ended in a
|
||||
# warning with no offer to close and try again. The image is a copy of a native
|
||||
# Windows program, so it starts without its usual directory next to it, and it carries
|
||||
# one of the payload's own names because that is what the lookup names.
|
||||
$copy = Join-Path $dir "cockatrice.exe"
|
||||
Copy-Item -LiteralPath (Join-Path $env:SystemRoot "System32\cmd.exe") -Destination $copy
|
||||
$running = Start-Process -FilePath $copy -ArgumentList "/c", "ping -n 20 127.0.0.1 >nul" -PassThru
|
||||
try {
|
||||
Start-Sleep -Milliseconds 1500
|
||||
$byImage = Invoke-Probe $dir
|
||||
} finally {
|
||||
try { $running.Kill() } catch { }
|
||||
# Wait for the image to be released, or the cleanup below leaves the copy behind.
|
||||
try { $running.WaitForExit(5000) | Out-Null } catch { }
|
||||
}
|
||||
if ($byImage.Code -ne 1) { throw "expected exit 1 for a process running from the directory, got $($byImage.Code) : $($byImage.Output)" }
|
||||
if ($byImage.Output -notmatch "cockatrice\.exe \(pid\s+$($running.Id)") {
|
||||
throw "the lookup did not name the process running from the directory: $($byImage.Output)"
|
||||
}
|
||||
|
||||
# A program in a folder below the directory is not an instance of the installation, even
|
||||
# when it carries a payload program's name: the install directory can be a folder the
|
||||
# user keeps other applications in, each in its own subfolder, and naming one of those
|
||||
# asked for a close the probe never sends and an end it refuses to perform, so the guard
|
||||
# could only abort the update. A program that really does hold a payload file is still
|
||||
# named, by the Restart Manager lookup exercised above.
|
||||
$nested = Join-Path $dir "other"
|
||||
New-Item -ItemType Directory -Force $nested | Out-Null
|
||||
$nestedImage = Join-Path $nested "cockatrice.exe"
|
||||
Copy-Item -LiteralPath (Join-Path $env:SystemRoot "System32\cmd.exe") -Destination $nestedImage
|
||||
$nestedRunning = Start-Process -FilePath $nestedImage -ArgumentList "/c", "ping -n 20 127.0.0.1 >nul" -PassThru
|
||||
try {
|
||||
Start-Sleep -Milliseconds 1500
|
||||
$inSubfolder = Invoke-Probe $dir
|
||||
} finally {
|
||||
try { $nestedRunning.Kill() } catch { }
|
||||
try { $nestedRunning.WaitForExit(5000) | Out-Null } catch { }
|
||||
}
|
||||
if ($inSubfolder.Code -ne 0) {
|
||||
throw "expected exit 0 for a program in a folder below the directory, got $($inSubfolder.Code) : $($inSubfolder.Output)"
|
||||
}
|
||||
|
||||
# A program the user keeps in the directory itself is not a payload program either: the
|
||||
# guard ends only what the payload installs, and naming anything else asks for an answer
|
||||
# that cannot free a single file.
|
||||
$foreign = Join-Path $dir "tool.exe"
|
||||
Copy-Item -LiteralPath (Join-Path $env:SystemRoot "System32\cmd.exe") -Destination $foreign
|
||||
$foreignRunning = Start-Process -FilePath $foreign -ArgumentList "/c", "ping -n 20 127.0.0.1 >nul" -PassThru
|
||||
try {
|
||||
Start-Sleep -Milliseconds 1500
|
||||
$ignored = Invoke-Probe $dir
|
||||
} finally {
|
||||
try { $foreignRunning.Kill() } catch { }
|
||||
try { $foreignRunning.WaitForExit(5000) | Out-Null } catch { }
|
||||
}
|
||||
if ($ignored.Code -ne 0) {
|
||||
throw "expected exit 0 for a program the payload does not install, got $($ignored.Code) : $($ignored.Output)"
|
||||
}
|
||||
Write-Host "holder lookup named pid $($holder.Id) for a file another process holds, named nothing for a file only the asking process holds, reported a payload executable it could not name as in use instead of free, named nothing for an empty directory, named pid $($running.Id) for a payload program running from the directory, and named nothing for a payload program in a folder below it or for a program the payload does not install"
|
||||
} finally {
|
||||
Remove-Item -Recurse -Force $dir -ErrorAction SilentlyContinue
|
||||
}
|
||||
POWERSHELL
|
||||
if ! PROBE_EXE="$probe" powershell -NoProfile -File "$probe_check"; then
|
||||
rm -f "$probe_check"
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$probe_check"
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
|
||||
if [[ $MAKE_INSTALL ]]; then
|
||||
echo "::group::Install"
|
||||
cmake --build . --target install --config "$BUILDTYPE"
|
||||
|
|
|
|||
8
.github/workflows/desktop-build.yml
vendored
8
.github/workflows/desktop-build.yml
vendored
|
|
@ -584,11 +584,17 @@ jobs:
|
|||
# job. A scope nobody has written to yet therefore leaves the directory missing
|
||||
# and fails the job, which is exactly the state every scope starts in. Make the
|
||||
# directory up front so an empty scope is a cache miss instead of an error.
|
||||
# always() covers the cache step itself failing: the action's post save still runs
|
||||
# then, and it should report the real error instead of ENOENT. It cannot rescue a
|
||||
# failure before the cache step, since a post step only runs for an action whose
|
||||
# main step did. The fallback keeps this guard from failing when the cache step was
|
||||
# skipped and left no path output.
|
||||
- name: "Create vcpkg cache directory"
|
||||
if: always()
|
||||
shell: bash
|
||||
env:
|
||||
CACHE_DIR: '${{ steps.vcpkg-cache.outputs.path }}'
|
||||
run: mkdir -p "$CACHE_DIR"
|
||||
run: mkdir -p "${CACHE_DIR:-$GITHUB_WORKSPACE/.vcpkg-cache}"
|
||||
|
||||
# Uses environment variables, see compile.sh for more details
|
||||
- name: "Build Cockatrice"
|
||||
|
|
|
|||
|
|
@ -250,6 +250,15 @@ message(STATUS "Update Translations: ${UPDATE_TRANSLATIONS}")
|
|||
|
||||
include(FindQtRuntime)
|
||||
|
||||
# Reject APIs that were deprecated before the oldest Qt release we support
|
||||
# (6.4). Qt 6.5 renamed the switch, so use whichever spelling the found
|
||||
# version understands while still compiling everywhere.
|
||||
if(Qt6_VERSION VERSION_GREATER_EQUAL 6.5)
|
||||
add_compile_definitions(QT_DISABLE_DEPRECATED_UP_TO=0x060400)
|
||||
else()
|
||||
add_compile_definitions(QT_DISABLE_DEPRECATED_BEFORE=0x060400)
|
||||
endif()
|
||||
|
||||
set(CMAKE_AUTOMOC TRUE)
|
||||
|
||||
# Find other needed libraries
|
||||
|
|
@ -420,6 +429,9 @@ if(WITH_ORACLE OR WITH_CLIENT)
|
|||
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_models ${CMAKE_BINARY_DIR}/libcockatrice_models)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_filters ${CMAKE_BINARY_DIR}/libcockatrice_filters)
|
||||
endif()
|
||||
if(WITH_CLIENT)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_game ${CMAKE_BINARY_DIR}/libcockatrice_game)
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER)
|
||||
add_subdirectory(servatrice)
|
||||
|
|
@ -431,6 +443,13 @@ if(WITH_CLIENT)
|
|||
set(CPACK_INSTALL_CMAKE_PROJECTS "Cockatrice;Cockatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
|
||||
endif()
|
||||
|
||||
if(WIN32 AND WITH_CLIENT)
|
||||
# Runs from the installer's $PLUGINSDIR when a purge fails, and is never installed
|
||||
# (see LogProcessesUsingInstallDir in cmake/NSIS.template.in), so it has no install
|
||||
# rules and is not part of the client's staged tree.
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/tools/installer-holder-probe ${CMAKE_BINARY_DIR}/tools/installer-holder-probe)
|
||||
endif()
|
||||
|
||||
if(WITH_ORACLE)
|
||||
add_subdirectory(oracle)
|
||||
set(CPACK_INSTALL_CMAKE_PROJECTS "Oracle;Oracle;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
|
||||
|
|
@ -443,5 +462,5 @@ endif()
|
|||
|
||||
if(Qt6_FOUND AND Qt6_VERSION_MINOR GREATER_EQUAL 3)
|
||||
# Qt6.3+ requires project finalization to support translations
|
||||
qt6_finalize_project()
|
||||
qt_finalize_project()
|
||||
endif()
|
||||
|
|
|
|||
14
README.md
14
README.md
|
|
@ -15,9 +15,10 @@
|
|||
---
|
||||
|
||||
<br><pre><p align='center'>
|
||||
<b>To get started with Cockatrice ⇢ [view our webpage](https://cockatrice.github.io/)</b><br>
|
||||
<b>To get support, or suggest changes to the app ⇢ [file an issue](https://github.com/Cockatrice/Cockatrice/issues) ([How?](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))</b>
|
||||
<b>To help with development ⇢ learn [how to contribute](#contribute-)</b>
|
||||
<b>To get started with Cockatrice ⇢ [view our webpage](https://cockatrice.github.io/)</b>
|
||||
<b>To get support, or suggest changes to the app ⇢ [file an issue](https://github.com/Cockatrice/Cockatrice/issues) ([How?](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))</b><br>
|
||||
<b>To learn about the app and its code ⇢ [read our code documentation](https://cockatrice.github.io/docs/)</b>
|
||||
<b>To help with development ⇢ learn [how to contribute](#contribute)</b>
|
||||
</pre><p><br>
|
||||
|
||||
|
||||
|
|
@ -83,7 +84,7 @@ Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other p
|
|||
|
||||
### Code
|
||||
|
||||
To contribute code to the project, please review our [guidelines](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md) first.<br>
|
||||
To commit code to the project, please review our [Contribution Guidelines](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md) first.<br>
|
||||
We maintain two tags for contributors to easier find issues to potentially work on:
|
||||
- [](https://github.com/Cockatrice/Cockatrice/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22Good%20first%20issue%22%20)<br>
|
||||
Issues tagged in this way provide a simple way to get started. They don't require much experience to be worked on.
|
||||
|
|
@ -95,7 +96,7 @@ We'll happily advice on how best to implement a feature, or we can show you wher
|
|||
|
||||
You can also have a look at our `Todo List` in our [Code Documentation](https://cockatrice.github.io/docs) or search the repo for [`\todo` comments](https://github.com/search?q=repo%3ACockatrice%2FCockatrice%20%5Ctodo&type=code).
|
||||
|
||||
### Documentation [](https://github.com/Cockatrice/Cockatrice/actions/workflows/documentation-build.yml?query=event%3Apush)
|
||||
### Documentation [](https://github.com/Cockatrice/Cockatrice/actions/workflows/documentation-build.yml?query=event%3Arelease+event%3Aworkflow_dispatch)
|
||||
|
||||
There are various places where useful information for different needs are maintained:
|
||||
- [Official Code Documentation](https://cockatrice.github.io/docs/)
|
||||
|
|
@ -104,7 +105,8 @@ There are various places where useful information for different needs are mainta
|
|||
- [Official README](https://github.com/Cockatrice/Cockatrice/blob/master/README.md) `This file`
|
||||
- [Official CONTRIBUTING](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md)
|
||||
|
||||
Cockatrice tries to use the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. We encourage you to improve the documentation by suggesting edits based on this guide.
|
||||
Cockatrice tries to use the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation.
|
||||
We encourage everybody to improve the documentation by suggesting edits based on this guide.
|
||||
|
||||
### Translation [](https://explore.transifex.com/cockatrice/cockatrice/)
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -57,6 +57,29 @@ try {
|
|||
}
|
||||
}
|
||||
|
||||
# --- What was holding the files, read from the log as well -------------------
|
||||
# The guard and the close attempt each log one line naming every holder and what
|
||||
# happened to it. Carrying the last of each as event data keeps an abort caused by
|
||||
# a running instance actionable without opening the attachment. Kept out of the
|
||||
# message so the issue still groups on the reason alone.
|
||||
$Holders = ''
|
||||
$CloseResult = ''
|
||||
$EndResult = ''
|
||||
if (Test-Path -LiteralPath $LogPath) {
|
||||
$holderLine = Select-String -LiteralPath $LogPath -Pattern 'guard: still in use by (.*)$' | Select-Object -Last 1
|
||||
if ($holderLine) {
|
||||
$Holders = $holderLine.Matches[0].Groups[1].Value
|
||||
}
|
||||
$closeLine = Select-String -LiteralPath $LogPath -Pattern '(close-holders: .*)$' | Select-Object -Last 1
|
||||
if ($closeLine) {
|
||||
$CloseResult = $closeLine.Matches[0].Groups[1].Value
|
||||
}
|
||||
$endLine = Select-String -LiteralPath $LogPath -Pattern '(end-holders: .*)$' | Select-Object -Last 1
|
||||
if ($endLine) {
|
||||
$EndResult = $endLine.Matches[0].Groups[1].Value
|
||||
}
|
||||
}
|
||||
|
||||
# --- Event ---------------------------------------------------------------
|
||||
$eventId = [guid]::NewGuid().ToString('N')
|
||||
$now = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
|
||||
|
|
@ -75,6 +98,9 @@ try {
|
|||
extra = @{
|
||||
reason = $Reason
|
||||
os = [System.Environment]::OSVersion.VersionString
|
||||
holders = $Holders
|
||||
close_result = $CloseResult
|
||||
end_result = $EndResult
|
||||
}
|
||||
}
|
||||
$eventJson = $sentryEvent | ConvertTo-Json -Compress -Depth 6
|
||||
|
|
|
|||
|
|
@ -25,9 +25,7 @@ set(cockatrice_SOURCES
|
|||
src/client/settings/shortcut_treeview.cpp
|
||||
src/client/settings/shortcuts_settings.cpp
|
||||
src/interface/deck_loader/card_node_function.cpp
|
||||
src/interface/deck_loader/deck_file_format.cpp
|
||||
src/interface/deck_loader/deck_loader.cpp
|
||||
src/interface/deck_loader/loaded_deck.cpp
|
||||
src/interface/widgets/dialogs/dlg_connect.cpp
|
||||
src/interface/widgets/dialogs/dlg_convert_deck_to_cod_format.cpp
|
||||
src/interface/widgets/dialogs/dlg_create_game.cpp
|
||||
|
|
@ -71,36 +69,27 @@ set(cockatrice_SOURCES
|
|||
src/filters/filter_builder.cpp
|
||||
src/filters/filter_tree_model.cpp
|
||||
src/filters/syntax_help.cpp
|
||||
src/game/abstract_game.cpp
|
||||
src/game/arrow_registry.cpp
|
||||
src/game_graphics/board/abstract_card_drag_item.cpp
|
||||
src/game_graphics/board/abstract_card_item.cpp
|
||||
src/game_graphics/board/abstract_counter.cpp
|
||||
src/game/board/arrow_data.cpp
|
||||
src/game_graphics/board/arrow_item.cpp
|
||||
src/game_graphics/board/arrow_registry.cpp
|
||||
src/game_graphics/board/arrow_target.cpp
|
||||
src/game_graphics/board/card_drag_item.cpp
|
||||
src/game_graphics/board/card_item.cpp
|
||||
src/game/board/card_list.cpp
|
||||
src/game/board/card_state.cpp
|
||||
src/game_graphics/board/counter_general.cpp
|
||||
src/game/board/counter_state.cpp
|
||||
src/game_graphics/board/translate_counter_name.cpp
|
||||
src/game_graphics/deckview/deck_view.cpp
|
||||
src/game_graphics/deckview/deck_view_container.cpp
|
||||
src/game_graphics/deckview/tabbed_deck_view_container.cpp
|
||||
src/game_graphics/dialogs/dlg_annotation.cpp
|
||||
src/game_graphics/dialogs/dlg_create_token.cpp
|
||||
src/game_graphics/dialogs/dlg_move_top_cards_until.cpp
|
||||
src/game_graphics/dialogs/dlg_roll_dice.cpp
|
||||
src/game/game.cpp
|
||||
src/game/game_event_handler.cpp
|
||||
src/game/game_meta_info.cpp
|
||||
src/game_graphics/game_scene.cpp
|
||||
src/game/game_state.cpp
|
||||
src/game_graphics/game_view.cpp
|
||||
src/game_graphics/hand_counter.cpp
|
||||
src/game_graphics/log/message_log_widget.cpp
|
||||
src/game/phase.cpp
|
||||
src/game_graphics/phases_toolbar.cpp
|
||||
src/game_graphics/player/menu/card_menu.cpp
|
||||
src/game_graphics/player/menu/custom_zone_menu.cpp
|
||||
|
|
@ -118,23 +107,11 @@ set(cockatrice_SOURCES
|
|||
src/game_graphics/tally/stats_tally.cpp
|
||||
src/game_graphics/tally/subtype_tally.cpp
|
||||
src/game_graphics/tally/tally.cpp
|
||||
src/game/player/player_actions.cpp
|
||||
src/game_graphics/player/player_area.cpp
|
||||
src/game_graphics/player/player_dialogs.cpp
|
||||
src/game/player/player_event_handler.cpp
|
||||
src/game_graphics/player/player_graphics_item.cpp
|
||||
src/game/player/player_info.cpp
|
||||
src/game_graphics/player/player_list_widget.cpp
|
||||
src/game/player/player_logic.cpp
|
||||
src/game/player/player_manager.cpp
|
||||
src/game_graphics/player/player_target.cpp
|
||||
src/game/replay.cpp
|
||||
src/game/zones/card_zone_logic.cpp
|
||||
src/game/zones/hand_zone_logic.cpp
|
||||
src/game/zones/pile_zone_logic.cpp
|
||||
src/game/zones/stack_zone_logic.cpp
|
||||
src/game/zones/table_zone_logic.cpp
|
||||
src/game/zones/view_zone_logic.cpp
|
||||
src/game_graphics/zones/card_zone.cpp
|
||||
src/game_graphics/zones/hand_zone.cpp
|
||||
src/game_graphics/zones/pile_zone.cpp
|
||||
|
|
@ -346,6 +323,7 @@ set(cockatrice_SOURCES
|
|||
src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp
|
||||
src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
|
||||
src/interface/window_main.cpp
|
||||
src/installer_close_server.cpp
|
||||
src/main.cpp
|
||||
src/single_instance_manager.cpp
|
||||
src/interface/widgets/tabs/abstract_tab_deck_editor.cpp
|
||||
|
|
@ -451,6 +429,7 @@ set(cockatrice_SOURCES
|
|||
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h
|
||||
src/interface/widgets/utility/compact_push_button.cpp
|
||||
src/interface/widgets/utility/compact_push_button.h
|
||||
src/installer_close_server.h
|
||||
src/single_instance_manager.h
|
||||
src/client/url_scheme_event_filter.h
|
||||
src/interface/intents/intent_connect_to_server.cpp
|
||||
|
|
@ -512,7 +491,7 @@ if(APPLE)
|
|||
set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
|
||||
endif(APPLE)
|
||||
|
||||
qt6_add_resources(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
|
||||
qt_add_resources(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
|
||||
|
||||
# Declare path variables
|
||||
set(ICONDIR
|
||||
|
|
@ -533,7 +512,7 @@ set(COCKATRICE_MAC_QM_INSTALL_DIR "cockatrice.app/Contents/Resources/translation
|
|||
set(COCKATRICE_UNIX_QM_INSTALL_DIR "share/cockatrice/translations")
|
||||
set(COCKATRICE_WIN32_QM_INSTALL_DIR "translations")
|
||||
|
||||
qt6_add_executable(
|
||||
qt_add_executable(
|
||||
cockatrice
|
||||
WIN32
|
||||
MACOSX_BUNDLE
|
||||
|
|
@ -545,7 +524,7 @@ qt6_add_executable(
|
|||
|
||||
target_precompile_headers(cockatrice PRIVATE "${CMAKE_SOURCE_DIR}/cmake/pch/qtwidgets_pch.h")
|
||||
|
||||
qt6_add_shaders(
|
||||
qt_add_shaders(
|
||||
cockatrice
|
||||
"onboarding_shaders"
|
||||
PREFIX
|
||||
|
|
@ -557,7 +536,7 @@ qt6_add_shaders(
|
|||
src/interface/widgets/onboarding/shaders/brand_plate.frag
|
||||
)
|
||||
|
||||
qt6_add_resources(
|
||||
qt_add_resources(
|
||||
cockatrice
|
||||
"onboarding_qml"
|
||||
PREFIX
|
||||
|
|
@ -573,6 +552,7 @@ target_link_libraries(
|
|||
PUBLIC libcockatrice_card
|
||||
libcockatrice_deck_list
|
||||
libcockatrice_filters
|
||||
libcockatrice_game
|
||||
libcockatrice_utility
|
||||
libcockatrice_network
|
||||
libcockatrice_models
|
||||
|
|
@ -789,7 +769,7 @@ endif()
|
|||
if(Qt6LinguistTools_FOUND)
|
||||
#Qt6 Translations happen after the executable is built up
|
||||
if(UPDATE_TRANSLATIONS)
|
||||
qt6_add_translations(
|
||||
qt_add_translations(
|
||||
cockatrice
|
||||
TS_FILES
|
||||
${cockatrice_TS}
|
||||
|
|
@ -799,7 +779,7 @@ if(Qt6LinguistTools_FOUND)
|
|||
cockatrice_QM
|
||||
)
|
||||
else()
|
||||
qt6_add_translations(cockatrice TS_FILES ${cockatrice_TS} QM_FILES_OUTPUT_VARIABLE cockatrice_QM)
|
||||
qt_add_translations(cockatrice TS_FILES ${cockatrice_TS} QM_FILES_OUTPUT_VARIABLE cockatrice_QM)
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
|
|
@ -825,4 +805,4 @@ if(Qt6LinguistTools_FOUND)
|
|||
endforeach()
|
||||
endif()
|
||||
|
||||
qt6_finalize_target(cockatrice)
|
||||
qt_finalize_target(cockatrice)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
LatencyStatusWidget::LatencyStatusWidget(QWidget *parent) : QWidget(parent)
|
||||
|
|
@ -24,9 +25,7 @@ LatencyStatusWidget::LatencyStatusWidget(QWidget *parent) : QWidget(parent)
|
|||
latencyGraph = new LatencyGraphWidget(this);
|
||||
latencyGraph->setFixedSize(90, 14);
|
||||
|
||||
auto *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(4);
|
||||
auto *layout = QtUtils::makeLayout<QHBoxLayout>(this, 4);
|
||||
layout->addWidget(latencyGraph);
|
||||
layout->addWidget(pingLabel);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <libcockatrice/network/client/remote/remote_client.h>
|
||||
#include <libcockatrice/protocol/pb/response.pb.h>
|
||||
#include <libcockatrice/settings/servers_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
class QWidget;
|
||||
|
|
@ -243,17 +244,27 @@ void ConnectionController::onLoginError(int r,
|
|||
{
|
||||
switch (static_cast<Response::ResponseCode>(r)) {
|
||||
case Response::RespClientUpdateRequired: {
|
||||
QString formatted = "Missing Features: ";
|
||||
for (int i = 0; i < missingFeatures.size(); ++i) {
|
||||
formatted.append(QString("\n %1").arg(QChar(0x2022)) + " " + missingFeatures.value(i));
|
||||
}
|
||||
|
||||
QMessageBox msgBox(dialogParent);
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setWindowTitle(tr("Failed Login"));
|
||||
msgBox.setText(tr("Your client seems to be missing features this server requires for connection.") +
|
||||
"\n\n" + tr("To update your client, go to 'Help -> Check for Client Updates'."));
|
||||
msgBox.setDetailedText(formatted);
|
||||
|
||||
if (missingFeatures.isEmpty()) {
|
||||
// Without a feature list this is either a login the client stopped itself or a
|
||||
// strict server asking for a password; the reason string already says which and
|
||||
// what to do. No client update can fix any of those causes, so show it instead of
|
||||
// the update advice.
|
||||
msgBox.setText(reasonStr.isEmpty() ? tr("The login did not complete.") : reasonStr);
|
||||
} else {
|
||||
QString formatted = tr("Missing Features:");
|
||||
for (const QString &feature : missingFeatures) {
|
||||
formatted.append(QString("\n %1 ").arg(QChar(0x2022)) + feature);
|
||||
}
|
||||
|
||||
msgBox.setText(tr("Your client seems to be missing features this server requires for connection.") +
|
||||
"\n\n" + tr("To update your client, go to 'Help -> Check for Client Updates'."));
|
||||
msgBox.setDetailedText(formatted);
|
||||
}
|
||||
|
||||
msgBox.exec();
|
||||
break;
|
||||
}
|
||||
|
|
@ -300,9 +311,8 @@ void ConnectionController::onLoginError(int r,
|
|||
}
|
||||
|
||||
case Response::RespRegistrationRequired: {
|
||||
if (QMessageBox::question(dialogParent, tr("Error"),
|
||||
tr("This server requires user registration. Do you want to register now?"),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
if (QtUtils::confirm(dialogParent, tr("Error"),
|
||||
tr("This server requires user registration. Do you want to register now?"))) {
|
||||
registerToServer();
|
||||
}
|
||||
return; // don't re-prompt connect
|
||||
|
|
@ -517,14 +527,38 @@ void ConnectionController::onForgotPasswordSuccess()
|
|||
SettingsCache::instance().servers().setFPPlayerName("");
|
||||
}
|
||||
|
||||
void ConnectionController::onForgotPasswordError()
|
||||
void ConnectionController::onForgotPasswordError(int responseCode)
|
||||
{
|
||||
QMessageBox::warning(
|
||||
dialogParent, tr("Reset Password"),
|
||||
tr("Failed to reset user account password, please contact the server operator to reset your password."));
|
||||
SettingsCache::instance().servers().setFPHostName("");
|
||||
SettingsCache::instance().servers().setFPPort("");
|
||||
SettingsCache::instance().servers().setFPPlayerName("");
|
||||
// Rate limiting is retryable, so it must not clear the prefill.
|
||||
bool keepPrefill = false;
|
||||
QString message;
|
||||
switch (static_cast<Response::ResponseCode>(responseCode)) {
|
||||
case Response::RespTooManyRequests:
|
||||
// Retryable: keep the host/port/user prefill so the user can try again. The window
|
||||
// is server-configured (an hour by default), so do not promise a shorter wait.
|
||||
keepPrefill = true;
|
||||
message = tr("Too many password reset attempts from your address. Please wait before "
|
||||
"requesting a new reset.");
|
||||
break;
|
||||
case Response::RespFunctionNotAllowed:
|
||||
// Also returned for an unknown user, a banned user, and an expired token or
|
||||
// challenge, so the message must not claim a single cause.
|
||||
message = tr("The server did not accept the reset request. Check the user name, or contact "
|
||||
"the server operator to reset your password.");
|
||||
break;
|
||||
default:
|
||||
message = tr("Failed to reset user account password, please contact the server operator to reset your "
|
||||
"password.");
|
||||
break;
|
||||
}
|
||||
|
||||
QMessageBox::warning(dialogParent, tr("Reset Password"), message);
|
||||
|
||||
if (!keepPrefill) {
|
||||
SettingsCache::instance().servers().setFPHostName("");
|
||||
SettingsCache::instance().servers().setFPPort("");
|
||||
SettingsCache::instance().servers().setFPPlayerName("");
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionController::onPromptForgotPasswordReset()
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ private slots:
|
|||
|
||||
// Forgot-password flow
|
||||
void onForgotPasswordSuccess();
|
||||
void onForgotPasswordError();
|
||||
void onForgotPasswordError(int responseCode);
|
||||
void onPromptForgotPasswordReset();
|
||||
void onPromptForgotPasswordChallenge();
|
||||
|
||||
|
|
|
|||
|
|
@ -20,11 +20,12 @@
|
|||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <version_string.h>
|
||||
|
||||
DeckStatsInterface::DeckStatsInterface(QObject *parent) : QObject(parent)
|
||||
{
|
||||
manager = new QNetworkAccessManager(this);
|
||||
manager = NetworkManagerFactory::create(this);
|
||||
connect(manager, &QNetworkAccessManager::finished, this, &DeckStatsInterface::queryFinished);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,11 +26,12 @@
|
|||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <version_string.h>
|
||||
|
||||
TappedOutInterface::TappedOutInterface(QObject *parent) : QObject(parent)
|
||||
{
|
||||
manager = new QNetworkAccessManager(this);
|
||||
manager = NetworkManagerFactory::create(this);
|
||||
connect(manager, &QNetworkAccessManager::finished, this, &TappedOutInterface::queryFinished);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <functional>
|
||||
#include <libcockatrice/card/database/card_database.h>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <libcockatrice/settings/download_settings.h>
|
||||
#include <libcockatrice/settings/paths_settings.h>
|
||||
#include <tuple>
|
||||
|
|
@ -56,7 +57,7 @@ void SpoilerBackgroundUpdater::startSpoilerDownloadProcess(QString url, bool sav
|
|||
|
||||
void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults)
|
||||
{
|
||||
auto *nam = new QNetworkAccessManager(this);
|
||||
auto *nam = NetworkManagerFactory::create(this);
|
||||
auto request = QNetworkRequest(url);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
|
||||
QNetworkReply *reply = nam->get(request);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <QVariant>
|
||||
#include <QVariantMap>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <optional>
|
||||
#include <qnamespace.h>
|
||||
#include <qprocessordetection.h>
|
||||
|
|
@ -62,7 +63,7 @@ std::optional<int> getProductVersionMajor()
|
|||
|
||||
} // namespace
|
||||
|
||||
ReleaseChannel::ReleaseChannel() : netMan(new QNetworkAccessManager(this)), response(nullptr), lastRelease(nullptr)
|
||||
ReleaseChannel::ReleaseChannel() : netMan(NetworkManagerFactory::create(this)), response(nullptr), lastRelease(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@
|
|||
#include <QNetworkRequest>
|
||||
#include <QUrl>
|
||||
#include <QVariant>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
|
||||
UpdateDownloader::UpdateDownloader(QObject *parent) : QObject(parent), response(nullptr)
|
||||
{
|
||||
netMan = new QNetworkAccessManager(this);
|
||||
netMan = NetworkManagerFactory::create(this);
|
||||
}
|
||||
|
||||
void UpdateDownloader::beginDownload(QUrl downloadUrl)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ inline Q_LOGGING_CATEGORY(SettingsCacheLog, "settings_cache");
|
|||
|
||||
class ReleaseChannel;
|
||||
|
||||
#define DEFAULT_LANG_NAME "English"
|
||||
#define CLIENT_INFO_NOT_SET "notset"
|
||||
|
||||
#define DEFAULT_FONT_SIZE 12
|
||||
|
|
|
|||
|
|
@ -2,13 +2,18 @@
|
|||
#define COCKATRICE_SETTINGS_CARD_PREFERENCE_PROVIDER_H
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <libcockatrice/interfaces/interface_card_preference_provider.h>
|
||||
#include <libcockatrice/settings/card_override_settings.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
|
||||
class SettingsCardPreferenceProvider : public ICardPreferenceProvider
|
||||
class SettingsCardPreferenceProvider : public QObject, public ICardPreferenceProvider
|
||||
{
|
||||
public:
|
||||
explicit SettingsCardPreferenceProvider(QObject *parent = nullptr) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] QString getCardPreferenceOverride(const QString &cardName) const override
|
||||
{
|
||||
return SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardName);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include "deck_filter_string.h"
|
||||
|
||||
#include "../interface/deck_loader/loaded_deck.h"
|
||||
#include "libcockatrice/card/card_localization.h"
|
||||
#include "libcockatrice/card/database/card_database_querier.h"
|
||||
#include "libcockatrice/deck_list/deck_list.h"
|
||||
|
|
@ -15,6 +14,7 @@
|
|||
#include <algorithm>
|
||||
#include <any>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <libcockatrice/filters/filter_string.h>
|
||||
#include <libcockatrice/utility/peglib.h>
|
||||
#include <mutex>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <QString>
|
||||
#include <functional>
|
||||
#include <libcockatrice/card/card_localization.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
|
||||
struct CardSearchLanguage;
|
||||
struct LoadedDeck;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/filters/filter_card.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <stddef.h>
|
||||
|
||||
FilterBuilder::FilterBuilder(QWidget *parent) : QWidget(parent)
|
||||
|
|
@ -39,9 +40,8 @@ FilterBuilder::FilterBuilder(QWidget *parent) : QWidget(parent)
|
|||
edit->setClearButtonEnabled(true);
|
||||
edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
|
||||
QGridLayout *layout = new QGridLayout;
|
||||
QGridLayout *layout = QtUtils::makeLayout<QGridLayout>();
|
||||
layout->setObjectName("layout");
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
layout->addWidget(typeCombo, 0, 0, 1, 2);
|
||||
layout->addWidget(filterCombo, 0, 2, 1, 2);
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
#include "card_state.h"
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
void CardState::resetState(bool keepAnnotations)
|
||||
{
|
||||
attacking = false;
|
||||
counters.clear();
|
||||
pt.clear();
|
||||
if (!keepAnnotations) {
|
||||
annotation.clear();
|
||||
}
|
||||
attachedTo = nullptr;
|
||||
}
|
||||
|
||||
void CardState::setZone(CardZoneLogic *_zone)
|
||||
{
|
||||
if (zone == _zone) {
|
||||
return;
|
||||
}
|
||||
|
||||
zone = _zone;
|
||||
emit zoneChanged(this, zone);
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
void CardState::setAttacking(bool _attacking)
|
||||
{
|
||||
if (attacking == _attacking) {
|
||||
return;
|
||||
}
|
||||
attacking = _attacking;
|
||||
emit attackingChanged(_attacking);
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
void CardState::insertCounter(int id, int value)
|
||||
{
|
||||
counters.insert(id, value);
|
||||
|
||||
emit countersChanged(counters);
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
void CardState::setCounter(int id, int value)
|
||||
{
|
||||
if (value) {
|
||||
counters[id] = value;
|
||||
} else {
|
||||
counters.remove(id);
|
||||
}
|
||||
|
||||
emit countersChanged(counters);
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
void CardState::clearCounters()
|
||||
{
|
||||
counters.clear();
|
||||
emit countersChanged(counters);
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
void CardState::setAnnotation(const QString &_annotation)
|
||||
{
|
||||
if (annotation == _annotation) {
|
||||
return;
|
||||
}
|
||||
annotation = _annotation;
|
||||
emit annotationChanged(annotation);
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
void CardState::setPT(const QString &_pt)
|
||||
{
|
||||
if (pt == _pt) {
|
||||
return;
|
||||
}
|
||||
pt = _pt;
|
||||
emit ptChanged(pt);
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
void CardState::setDoesntUntap(bool _doesntUntap)
|
||||
{
|
||||
if (doesntUntap == _doesntUntap) {
|
||||
return;
|
||||
}
|
||||
doesntUntap = _doesntUntap;
|
||||
emit doesntUntapChanged(_doesntUntap);
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
void CardState::setDestroyOnZoneChange(bool _destroyOnZoneChange)
|
||||
{
|
||||
if (destroyOnZoneChange == _destroyOnZoneChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
destroyOnZoneChange = _destroyOnZoneChange;
|
||||
emit destroyOnZoneChangeChanged(_destroyOnZoneChange);
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
void CardState::setAttachedTo(CardItem *_attachedTo)
|
||||
{
|
||||
if (attachedTo == _attachedTo) {
|
||||
return;
|
||||
}
|
||||
attachedTo = _attachedTo;
|
||||
emit attachedToChanged(_attachedTo);
|
||||
emit stateChanged();
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
#ifndef COCKATRICE_CARD_STATE_H
|
||||
#define COCKATRICE_CARD_STATE_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class CardZoneLogic;
|
||||
class CardItem;
|
||||
|
||||
class CardState : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
bool attacking = false;
|
||||
QMap<int, int> counters;
|
||||
QString annotation;
|
||||
QString pt;
|
||||
bool doesntUntap = false;
|
||||
bool destroyOnZoneChange = false;
|
||||
|
||||
CardItem *attachedTo = nullptr;
|
||||
CardZoneLogic *zone = nullptr;
|
||||
|
||||
signals:
|
||||
void stateChanged();
|
||||
|
||||
void attackingChanged(bool newValue);
|
||||
void countersChanged(const QMap<int, int> &newCounters);
|
||||
void annotationChanged(const QString &newAnnotation);
|
||||
void ptChanged(const QString &newPt);
|
||||
void doesntUntapChanged(bool newValue);
|
||||
void destroyOnZoneChangeChanged(bool newValue);
|
||||
void attachedToChanged(CardItem *newAttachedTo);
|
||||
void zoneChanged(CardState *changedCard, CardZoneLogic *newZone);
|
||||
|
||||
public:
|
||||
explicit CardState(QObject *parent, CardZoneLogic *_zone) : QObject(parent), zone(_zone)
|
||||
{
|
||||
}
|
||||
|
||||
void resetState(bool keepAnnotations);
|
||||
|
||||
CardZoneLogic *getZone() const
|
||||
{
|
||||
return zone;
|
||||
}
|
||||
|
||||
void setZone(CardZoneLogic *_zone);
|
||||
|
||||
bool getAttacking() const
|
||||
{
|
||||
return attacking;
|
||||
}
|
||||
void setAttacking(bool _attacking);
|
||||
|
||||
const QMap<int, int> &getCounters() const
|
||||
{
|
||||
return counters;
|
||||
}
|
||||
|
||||
void insertCounter(int id, int value);
|
||||
|
||||
void setCounter(int id, int value);
|
||||
|
||||
void clearCounters();
|
||||
|
||||
QString getAnnotation() const
|
||||
{
|
||||
return annotation;
|
||||
}
|
||||
|
||||
void setAnnotation(const QString &_annotation);
|
||||
|
||||
QString getPT() const
|
||||
{
|
||||
return pt;
|
||||
}
|
||||
|
||||
void setPT(const QString &_pt);
|
||||
|
||||
bool getDoesntUntap() const
|
||||
{
|
||||
return doesntUntap;
|
||||
}
|
||||
|
||||
void setDoesntUntap(bool _doesntUntap);
|
||||
|
||||
bool getDestroyOnZoneChange() const
|
||||
{
|
||||
return destroyOnZoneChange;
|
||||
}
|
||||
|
||||
void setDestroyOnZoneChange(bool _destroyOnZoneChange);
|
||||
|
||||
CardItem *getAttachedTo() const
|
||||
{
|
||||
return attachedTo;
|
||||
}
|
||||
|
||||
void setAttachedTo(CardItem *_attachedTo);
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_CARD_STATE_H
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/**
|
||||
* @file replay.h
|
||||
* @ingroup GameLogic
|
||||
* @ingroup Replay
|
||||
*/
|
||||
//! \todo Document this file.
|
||||
|
||||
#ifndef COCKATRICE_REPLAY_H
|
||||
#define COCKATRICE_REPLAY_H
|
||||
|
||||
#include "abstract_game.h"
|
||||
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class GameReplay;
|
||||
class QObject;
|
||||
|
||||
class Replay : public AbstractGame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Replay(QObject *_parent, const GameReplay *_replay, bool isLocalGame);
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_REPLAY_H
|
||||
|
|
@ -34,7 +34,6 @@
|
|||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <algorithm>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/settings/appearance_settings.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/settings/debug_settings.h>
|
||||
|
|
@ -43,13 +42,23 @@
|
|||
class QSizeF;
|
||||
|
||||
AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef, PlayerLogic *_owner, int _id)
|
||||
: ArrowTarget(_owner, parent), id(_id), cardRef(cardRef), tapped(false), facedown(false), tapAngle(0),
|
||||
bgColor(Qt::transparent), isHovered(false), realZValue(0), pendingSelectionCollapse(false)
|
||||
: ArrowTarget(_owner, parent), state(new CardState(this, nullptr, cardRef, _id)), tapAngle(0),
|
||||
bgColor(Qt::transparent), isHovered(false), realZValue(0)
|
||||
{
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
setFlag(ItemIsSelectable);
|
||||
setCacheMode(DeviceCoordinateCache);
|
||||
|
||||
connect(state, &CardState::cardPixmapUpdated, this, &AbstractCardItem::pixmapUpdated);
|
||||
connect(state, &CardState::cardInfoChanged, this, &AbstractCardItem::onCardInfoChanged);
|
||||
connect(state, &CardState::cardRefChanged, this, &AbstractCardItem::onCardRefChanged);
|
||||
connect(state, &CardState::tappedChanged, this, &AbstractCardItem::onTappedChanged);
|
||||
connect(state, &CardState::facedownChanged, this, [this] { update(); });
|
||||
connect(state, &CardState::colorChanged, this, [this] {
|
||||
cacheBgColor();
|
||||
update();
|
||||
});
|
||||
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::displayCardNamesChanged, this,
|
||||
[this] { update(); });
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
|
||||
|
|
@ -67,7 +76,7 @@ AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef
|
|||
|
||||
AbstractCardItem::~AbstractCardItem()
|
||||
{
|
||||
emit deleteCardInfoPopup(cardRef.name);
|
||||
emit deleteCardInfoPopup(state->getCardRef().name);
|
||||
}
|
||||
|
||||
QRectF AbstractCardItem::boundingRect() const
|
||||
|
|
@ -90,30 +99,29 @@ void AbstractCardItem::pixmapUpdated()
|
|||
emit sigPixmapUpdated();
|
||||
}
|
||||
|
||||
void AbstractCardItem::refreshCardInfo()
|
||||
void AbstractCardItem::onCardInfoChanged()
|
||||
{
|
||||
exactCard = CardDatabaseManager::query()->getCard(cardRef);
|
||||
|
||||
if (!exactCard && !cardRef.name.isEmpty()) {
|
||||
CardInfo::UiAttributes attributes = {.tableRow = -1};
|
||||
auto info = CardInfo::newInstance(cardRef.name, "", true, {}, {}, {}, {}, attributes);
|
||||
exactCard = ExactCard(info);
|
||||
}
|
||||
if (exactCard) {
|
||||
connect(exactCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &AbstractCardItem::pixmapUpdated);
|
||||
}
|
||||
|
||||
cacheBgColor();
|
||||
update();
|
||||
}
|
||||
|
||||
void AbstractCardItem::onCardRefChanged(const CardRef &oldCardRef, const CardRef & /*newCardRef*/)
|
||||
{
|
||||
emit deleteCardInfoPopup(oldCardRef.name);
|
||||
}
|
||||
|
||||
void AbstractCardItem::refreshCardInfo()
|
||||
{
|
||||
state->refreshCardInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to get the CardInfo of the exactCard
|
||||
* @return A const reference to the CardInfo, or an empty CardInfo if card was null
|
||||
*/
|
||||
const CardInfo &AbstractCardItem::getCardInfo() const
|
||||
{
|
||||
return exactCard.getInfo();
|
||||
return state->getCardInfo();
|
||||
}
|
||||
|
||||
void AbstractCardItem::setRealZValue(qreal _zValue)
|
||||
|
|
@ -158,13 +166,13 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
|
|||
QPixmap translatedPixmap;
|
||||
bool paintImage = true;
|
||||
|
||||
if (facedown || cardRef.name.isEmpty()) {
|
||||
if (state->getFaceDown() || state->getCardRef().name.isEmpty()) {
|
||||
// never reveal card color, always paint the card back
|
||||
CardPictureLoader::getCardBackPixmap(translatedPixmap, translatedSize.toSize());
|
||||
} else {
|
||||
// don't even spend time trying to load the picture if our size is too small
|
||||
if (translatedSize.width() > 10) {
|
||||
CardPictureLoader::getPixmap(translatedPixmap, exactCard, translatedSize.toSize());
|
||||
CardPictureLoader::getPixmap(translatedPixmap, state->getCard(), translatedSize.toSize());
|
||||
if (translatedPixmap.isNull()) {
|
||||
paintImage = false;
|
||||
}
|
||||
|
|
@ -185,19 +193,20 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
|
|||
painter->drawPath(shape());
|
||||
}
|
||||
|
||||
if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() || facedown) {
|
||||
if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() ||
|
||||
state->getFaceDown()) {
|
||||
painter->save();
|
||||
transformPainter(painter, translatedSize, angle);
|
||||
painter->setPen(Qt::white);
|
||||
painter->setBackground(Qt::black);
|
||||
painter->setBackgroundMode(Qt::OpaqueMode);
|
||||
QString nameStr;
|
||||
if (facedown) {
|
||||
nameStr = "# " + QString::number(id);
|
||||
if (state->getFaceDown()) {
|
||||
nameStr = "# " + QString::number(state->getId());
|
||||
} else {
|
||||
QString prefix = "";
|
||||
if (SettingsCache::instance().debug().getShowCardId()) {
|
||||
prefix = "#" + QString::number(id) + " ";
|
||||
prefix = "#" + QString::number(state->getId()) + " ";
|
||||
}
|
||||
nameStr = prefix + CardLocalization::displayName(getCardInfo());
|
||||
}
|
||||
|
|
@ -235,21 +244,6 @@ void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
|
|||
painter->restore();
|
||||
}
|
||||
|
||||
void AbstractCardItem::setCardRef(const CardRef &_cardRef)
|
||||
{
|
||||
if (cardRef == _cardRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit deleteCardInfoPopup(cardRef.name);
|
||||
if (exactCard) {
|
||||
disconnect(exactCard.getCardPtr().data(), nullptr, this, nullptr);
|
||||
}
|
||||
cardRef = _cardRef;
|
||||
|
||||
refreshCardInfo();
|
||||
}
|
||||
|
||||
void AbstractCardItem::setHovered(bool _hovered)
|
||||
{
|
||||
if (isHovered == _hovered) {
|
||||
|
|
@ -275,18 +269,17 @@ void AbstractCardItem::setHovered(bool _hovered)
|
|||
|
||||
void AbstractCardItem::setColor(const QString &_color)
|
||||
{
|
||||
color = _color;
|
||||
cacheBgColor();
|
||||
update();
|
||||
state->setColor(_color);
|
||||
}
|
||||
|
||||
void AbstractCardItem::cacheBgColor()
|
||||
{
|
||||
QChar colorChar;
|
||||
if (color.isEmpty()) {
|
||||
colorChar = exactCard.getInfo().getColorChar();
|
||||
const QString cardColor = state->getColor();
|
||||
if (cardColor.isEmpty()) {
|
||||
colorChar = state->getCardInfo().getColorChar();
|
||||
} else {
|
||||
colorChar = color.at(0);
|
||||
colorChar = cardColor.at(0);
|
||||
}
|
||||
|
||||
switch (colorChar.toLower().toLatin1()) {
|
||||
|
|
@ -314,17 +307,12 @@ void AbstractCardItem::cacheBgColor()
|
|||
}
|
||||
}
|
||||
|
||||
void AbstractCardItem::setTapped(bool _tapped, bool canAnimate)
|
||||
void AbstractCardItem::onTappedChanged(bool newTapped, bool canAnimate)
|
||||
{
|
||||
if (tapped == _tapped) {
|
||||
return;
|
||||
}
|
||||
|
||||
tapped = _tapped;
|
||||
if (SettingsCache::instance().cardsDisplay().getTapAnimation() && canAnimate) {
|
||||
static_cast<GameScene *>(scene())->registerAnimationItem(this);
|
||||
} else {
|
||||
tapAngle = tapped ? 90 : 0;
|
||||
tapAngle = newTapped ? 90 : 0;
|
||||
setTransform(QTransform()
|
||||
.translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F)
|
||||
.rotate(tapAngle)
|
||||
|
|
@ -338,12 +326,6 @@ bool AbstractCardItem::animationEvent()
|
|||
return false;
|
||||
}
|
||||
|
||||
void AbstractCardItem::setFaceDown(bool _facedown)
|
||||
{
|
||||
facedown = _facedown;
|
||||
update();
|
||||
}
|
||||
|
||||
void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
cancelPendingSelectionCollapse();
|
||||
|
|
@ -357,7 +339,7 @@ void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|||
}
|
||||
}
|
||||
if ((event->modifiers() & Qt::AltModifier) && event->button() == Qt::LeftButton) {
|
||||
emit cardShiftClicked(cardRef.name);
|
||||
emit cardShiftClicked(state->getCardRef().name);
|
||||
} else if ((event->modifiers() & Qt::ControlModifier)) {
|
||||
setSelected(!isSelected());
|
||||
} else if (!isSelected()) {
|
||||
|
|
@ -371,7 +353,7 @@ void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|||
if (event->button() == Qt::LeftButton) {
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
} else if (event->button() == Qt::MiddleButton) {
|
||||
emit showCardInfoPopup(event->screenPos(), cardRef);
|
||||
emit showCardInfoPopup(event->screenPos(), state->getCardRef());
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
|
|
@ -405,7 +387,7 @@ void AbstractCardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|||
}
|
||||
|
||||
if (event->button() == Qt::MiddleButton) {
|
||||
emit deleteCardInfoPopup(cardRef.name);
|
||||
emit deleteCardInfoPopup(state->getCardRef().name);
|
||||
}
|
||||
|
||||
// This function ensures the parent function doesn't mess around with our selection.
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
#include <QSizeF>
|
||||
#include <QString>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/card/printing/exact_card.h>
|
||||
#include <libcockatrice/utility/card_ref.h>
|
||||
#include <libcockatrice/game/board/card_state.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class PlayerLogic;
|
||||
|
|
@ -29,13 +28,8 @@ class AbstractCardItem : public ArrowTarget, public IAnimatedItem
|
|||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
ExactCard exactCard;
|
||||
int id;
|
||||
CardRef cardRef;
|
||||
bool tapped;
|
||||
bool facedown;
|
||||
CardState *state;
|
||||
int tapAngle;
|
||||
QString color;
|
||||
QColor bgColor;
|
||||
|
||||
private:
|
||||
|
|
@ -45,6 +39,9 @@ private:
|
|||
int selectionCollapseGeneration = 0; ///< Guards the deferred collapse timer against stale firings
|
||||
private slots:
|
||||
void pixmapUpdated();
|
||||
void onCardInfoChanged();
|
||||
void onCardRefChanged(const CardRef &oldCardRef, const CardRef &newCardRef);
|
||||
void onTappedChanged(bool newTapped, bool canAnimate);
|
||||
|
||||
public slots:
|
||||
void refreshCardInfo();
|
||||
|
|
@ -80,31 +77,38 @@ public:
|
|||
QSizeF getTranslatedSize(QPainter *painter) const;
|
||||
void paintPicture(QPainter *painter, const QSizeF &translatedSize, int angle);
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
|
||||
[[nodiscard]] CardState *getState() const
|
||||
{
|
||||
return state;
|
||||
}
|
||||
ExactCard getCard() const
|
||||
{
|
||||
return exactCard;
|
||||
return state->getCard();
|
||||
}
|
||||
const CardInfo &getCardInfo() const;
|
||||
int getId() const
|
||||
{
|
||||
return id;
|
||||
return state->getId();
|
||||
}
|
||||
void setId(int _id)
|
||||
{
|
||||
id = _id;
|
||||
state->setId(_id);
|
||||
}
|
||||
QString getName() const
|
||||
{
|
||||
return cardRef.name;
|
||||
return state->getCardRef().name;
|
||||
}
|
||||
QString getProviderId() const
|
||||
{
|
||||
return cardRef.providerId;
|
||||
return state->getCardRef().providerId;
|
||||
}
|
||||
void setCardRef(const CardRef &_cardRef)
|
||||
{
|
||||
state->setCardRef(_cardRef);
|
||||
}
|
||||
void setCardRef(const CardRef &_cardRef);
|
||||
CardRef getCardRef() const
|
||||
{
|
||||
return cardRef;
|
||||
return state->getCardRef();
|
||||
}
|
||||
qreal getRealZValue() const
|
||||
{
|
||||
|
|
@ -116,27 +120,33 @@ public:
|
|||
{
|
||||
return isHovered;
|
||||
}
|
||||
QString getColor() const
|
||||
[[nodiscard]] QString getColor() const
|
||||
{
|
||||
return color;
|
||||
return state->getColor();
|
||||
}
|
||||
void setColor(const QString &_color);
|
||||
bool getTapped() const
|
||||
{
|
||||
return tapped;
|
||||
return state->getTapped();
|
||||
}
|
||||
void setTapped(bool _tapped, bool canAnimate = false)
|
||||
{
|
||||
state->setTapped(_tapped, canAnimate);
|
||||
}
|
||||
void setTapped(bool _tapped, bool canAnimate = false);
|
||||
bool getFaceDown() const
|
||||
{
|
||||
return facedown;
|
||||
return state->getFaceDown();
|
||||
}
|
||||
void setFaceDown(bool _facedown)
|
||||
{
|
||||
state->setFaceDown(_facedown);
|
||||
}
|
||||
void setFaceDown(bool _facedown);
|
||||
void processHoverEvent();
|
||||
/** @brief Drop a pending collapse and disarm any deferred collapse timer. */
|
||||
void cancelPendingSelectionCollapse();
|
||||
void deleteCardInfoPopup()
|
||||
{
|
||||
emit deleteCardInfoPopup(cardRef.name);
|
||||
emit deleteCardInfoPopup(state->getCardRef().name);
|
||||
}
|
||||
|
||||
/** @brief Default: no per-tick animation. Subclasses override to animate. */
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../client/settings/shortcuts_settings.h"
|
||||
#include "../../game/board/counter_state.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game_graphics/board/translate_counter_name.h"
|
||||
#include "../../interface/widgets/menus/tearoff_menu.h"
|
||||
|
||||
|
|
@ -23,8 +19,10 @@
|
|||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/protocol/pb/command_inc_counter.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_counter.pb.h>
|
||||
#include <libcockatrice/game/board/counter_state.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/utility/expression.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
|
|
@ -151,10 +149,7 @@ void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|||
menu->exec(event->screenPos());
|
||||
}
|
||||
} else {
|
||||
Command_IncCounter cmd;
|
||||
cmd.set_counter_id(id);
|
||||
cmd.set_delta(event->button() == Qt::LeftButton ? 1 : -1);
|
||||
player->getPlayerActions()->sendGameCommand(cmd);
|
||||
player->getPlayerActions()->actIncrementCounter(id, event->button() == Qt::LeftButton ? 1 : -1);
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
|
|
@ -172,10 +167,7 @@ void AbstractCounter::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
|
|||
|
||||
void AbstractCounter::incrementCounter()
|
||||
{
|
||||
Command_IncCounter cmd;
|
||||
cmd.set_counter_id(id);
|
||||
cmd.set_delta(static_cast<QAction *>(sender())->data().toInt());
|
||||
player->getPlayerActions()->sendGameCommand(cmd);
|
||||
player->getPlayerActions()->actIncrementCounter(id, static_cast<QAction *>(sender())->data().toInt());
|
||||
}
|
||||
|
||||
void AbstractCounter::setCounter()
|
||||
|
|
@ -199,10 +191,7 @@ void AbstractCounter::setCounter()
|
|||
}
|
||||
|
||||
Expression exp(value);
|
||||
Command_SetCounter cmd;
|
||||
cmd.set_counter_id(id);
|
||||
cmd.set_value(static_cast<int>(exp.parse(dlg.textValue())));
|
||||
player->getPlayerActions()->sendGameCommand(cmd);
|
||||
player->getPlayerActions()->actSetCounter(id, static_cast<int>(exp.parse(dlg.textValue())));
|
||||
}
|
||||
|
||||
AbstractCounterDialog::AbstractCounterDialog(const QString &name, const QString &value, QWidget *parent)
|
||||
|
|
|
|||
|
|
@ -2,11 +2,6 @@
|
|||
#include "arrow_item.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../game/board/arrow_data.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../game_scene.h"
|
||||
#include "../player/player_target.h"
|
||||
#include "../z_values.h"
|
||||
|
|
@ -27,12 +22,12 @@
|
|||
#include <QTransform>
|
||||
#include <QtGlobal>
|
||||
#include <QtMath>
|
||||
#include <libcockatrice/card/card_info.h>
|
||||
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_create_arrow.pb.h>
|
||||
#include <libcockatrice/game/board/arrow_data.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/utility/color.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
#include <math.h>
|
||||
#include <qnamespace.h>
|
||||
|
|
@ -426,19 +421,19 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|||
|
||||
CardZoneLogic *startZone = startCard->getZone();
|
||||
|
||||
Command_CreateArrow cmd;
|
||||
cmd.mutable_arrow_color()->CopyFrom(convertQColorToColor(data->color));
|
||||
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_start_zone(startZone->getName().toStdString());
|
||||
cmd.set_start_card_id(startCard->getId());
|
||||
ArrowData arrow;
|
||||
arrow.color = data->color;
|
||||
arrow.startPlayerId = startZone->getPlayer()->getPlayerInfo()->getId();
|
||||
arrow.startZone = startZone->getName();
|
||||
arrow.startCardId = startCard->getId();
|
||||
|
||||
if (auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem)) {
|
||||
CardZoneLogic *targetZone = targetCard->getZone();
|
||||
cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(targetZone->getName().toStdString());
|
||||
cmd.set_target_card_id(targetCard->getId());
|
||||
arrow.targetPlayerId = targetZone->getPlayer()->getPlayerInfo()->getId();
|
||||
arrow.targetZone = targetZone->getName();
|
||||
arrow.targetCardId = targetCard->getId();
|
||||
} else if (auto *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem)) {
|
||||
cmd.set_target_player_id(targetPlayer->getOwner()->getPlayerInfo()->getId());
|
||||
arrow.targetPlayerId = targetPlayer->getOwner()->getPlayerInfo()->getId();
|
||||
} else {
|
||||
delArrow();
|
||||
return;
|
||||
|
|
@ -447,22 +442,9 @@ 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() == ZoneNames::HAND) {
|
||||
startCard->playCard(false);
|
||||
CardInfoPtr ci = startCard->getCard().getCardPtr();
|
||||
bool playToStack = SettingsCache::instance().userInterface().getPlayToStack();
|
||||
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 ? ZoneNames::STACK : ZoneNames::TABLE);
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteInPhase != 0) {
|
||||
cmd.set_delete_in_phase(deleteInPhase);
|
||||
}
|
||||
|
||||
player->getPlayerActions()->sendGameCommand(cmd);
|
||||
player->getPlayerActions()->createArrow(startCard->getState(), arrow, deleteInPhase);
|
||||
}
|
||||
|
||||
delArrow();
|
||||
|
|
@ -554,7 +536,7 @@ void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|||
auto *startCard = qgraphicsitem_cast<CardItem *>(startItem);
|
||||
auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem);
|
||||
if (startCard && targetCard) {
|
||||
attachCards(startCard, targetCard);
|
||||
player->getPlayerActions()->attachCards(startCard->getState(), targetCard->getState());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -563,23 +545,3 @@ void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|||
child->mouseReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCard)
|
||||
{
|
||||
if (targetCard->getAttachedTo() || targetCard->getZone()->getName() != ZoneNames::TABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// move card onto table first if attaching from some other zone
|
||||
if (startCard->getZone()->getName() != ZoneNames::TABLE) {
|
||||
player->getPlayerActions()->playCardToTable(startCard, false);
|
||||
}
|
||||
|
||||
Command_AttachCard cmd;
|
||||
cmd.set_start_zone(ZoneNames::TABLE);
|
||||
cmd.set_card_id(startCard->getId());
|
||||
cmd.set_target_player_id(targetCard->getZone()->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(targetCard->getZone()->getName().toStdString());
|
||||
cmd.set_target_card_id(targetCard->getId());
|
||||
player->getPlayerActions()->sendGameCommand(cmd);
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef ARROWITEM_H
|
||||
#define ARROWITEM_H
|
||||
|
||||
#include "../../game/board/arrow_data.h"
|
||||
#include "../animated_item.h"
|
||||
#include "arrow_target.h"
|
||||
#include "graphics_item_type.h"
|
||||
|
|
@ -18,6 +17,7 @@
|
|||
#include <QRectF>
|
||||
#include <QSharedPointer>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/game/board/arrow_data.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class CardItem;
|
||||
|
|
@ -131,7 +131,6 @@ private:
|
|||
PlayerLogic *player;
|
||||
QList<ArrowAttachItem *> childArrows;
|
||||
QMetaObject::Connection positionConnection;
|
||||
void attachCards(CardItem *startCard, const CardItem *targetCard);
|
||||
|
||||
public:
|
||||
explicit ArrowAttachItem(ArrowTarget *_startItem);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#include "arrow_registry.h"
|
||||
|
||||
#include "../game_graphics/board/arrow_item.h"
|
||||
#include "board/arrow_data.h"
|
||||
#include "arrow_item.h"
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <libcockatrice/game/board/arrow_data.h>
|
||||
|
||||
void ArrowRegistry::insert(QSharedPointer<ArrowData> data, ArrowItem *arrow)
|
||||
{
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
#include <libcockatrice/game/board/arrow_data.h>
|
||||
|
||||
class ArrowItem;
|
||||
struct ArrowData;
|
||||
|
|
@ -1,17 +1,6 @@
|
|||
#include "card_item.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../game/abstract_game.h"
|
||||
#include "../../game/board/card_state.h"
|
||||
#include "../../game/game_state.h"
|
||||
#include "../../game/phase.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game/player/player_manager.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../../game/zones/table_zone_logic.h"
|
||||
#include "../../game/zones/view_zone_logic.h"
|
||||
#include "../card_dimensions.h"
|
||||
#include "../game_scene.h"
|
||||
#include "arrow_item.h"
|
||||
|
|
@ -41,6 +30,17 @@
|
|||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/card/card_info.h>
|
||||
#include <libcockatrice/game/abstract_game.h>
|
||||
#include <libcockatrice/game/board/card_state.h>
|
||||
#include <libcockatrice/game/game_state.h>
|
||||
#include <libcockatrice/game/phase.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/player/player_manager.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/table_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/view_zone_logic.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/utility/card_ref.h>
|
||||
|
|
@ -53,8 +53,9 @@ CardItem::CardItem(PlayerLogic *_owner,
|
|||
const CardRef &cardRef,
|
||||
int _cardid,
|
||||
CardZoneLogic *_zone)
|
||||
: AbstractCardItem(parent, cardRef, _owner, _cardid), state(new CardState(this, _zone)), dragItem(nullptr)
|
||||
: AbstractCardItem(parent, cardRef, _owner, _cardid), dragItem(nullptr)
|
||||
{
|
||||
state->setZone(_zone);
|
||||
owner->addCard(this);
|
||||
|
||||
connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, this, [this](int counterId) {
|
||||
|
|
@ -62,25 +63,44 @@ CardItem::CardItem(PlayerLogic *_owner,
|
|||
update();
|
||||
}
|
||||
});
|
||||
|
||||
connect(state, &CardState::stateReset, this, [this] {
|
||||
if (scene()) {
|
||||
static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
|
||||
}
|
||||
update();
|
||||
});
|
||||
|
||||
// The retyped logic layer drives CardState setters directly (bypassing the CardItem wrappers that called
|
||||
// update()), so repaint on every state change: attacking, annotation, doesntUntap, P/T and counters.
|
||||
connect(state, &CardState::stateChanged, this, [this] { update(); });
|
||||
|
||||
connect(state, &CardState::viewDeleteRequested, this, &CardItem::deleteLater);
|
||||
}
|
||||
|
||||
void CardItem::prepareDelete()
|
||||
{
|
||||
if (owner != nullptr) {
|
||||
if (owner->getGame()->getActiveCard() == this) {
|
||||
auto *gameScene = qobject_cast<GameScene *>(scene());
|
||||
if (gameScene && gameScene->getActiveCard() == this) {
|
||||
emit owner->requestCardMenuUpdate(nullptr);
|
||||
owner->getGame()->setActiveCard(nullptr);
|
||||
gameScene->setActiveCard(nullptr);
|
||||
}
|
||||
owner = nullptr;
|
||||
}
|
||||
|
||||
while (!attachedCards.isEmpty()) {
|
||||
attachedCards.first()->setZone(nullptr); // so that it won't try to call reorganizeCards()
|
||||
attachedCards.first()->setAttachedTo(nullptr);
|
||||
const QList<CardState *> &attachedStates = state->getAttachedCards();
|
||||
while (!attachedStates.isEmpty()) {
|
||||
auto *attachedItem = qobject_cast<CardItem *>(attachedStates.first()->parent());
|
||||
if (attachedItem == nullptr) {
|
||||
break;
|
||||
}
|
||||
attachedItem->setZone(nullptr); // so that it won't try to call reorganizeCards()
|
||||
attachedItem->setAttachedTo(nullptr);
|
||||
}
|
||||
|
||||
if (state->getAttachedTo() != nullptr) {
|
||||
state->getAttachedTo()->removeAttachedCard(this);
|
||||
state->getAttachedTo()->removeAttachedCard(state);
|
||||
state->setAttachedTo(nullptr);
|
||||
}
|
||||
}
|
||||
|
|
@ -127,7 +147,7 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
|||
painter->save();
|
||||
transformPainter(painter, translatedSize, tapAngle);
|
||||
|
||||
if (!getFaceDown() && state->getPT() == exactCard.getInfo().getPowTough()) {
|
||||
if (!getFaceDown() && state->getPT() == state->getCardInfo().getPowTough()) {
|
||||
painter->setPen(Qt::white);
|
||||
} else {
|
||||
painter->setPen(QColor(255, 150, 0)); // dark orange
|
||||
|
|
@ -209,36 +229,13 @@ void CardItem::setPT(const QString &_pt)
|
|||
|
||||
void CardItem::setAttachedTo(CardItem *_attachedTo)
|
||||
{
|
||||
if (state->getAttachedTo() != nullptr) {
|
||||
state->getAttachedTo()->removeAttachedCard(this);
|
||||
}
|
||||
CardState *newParent = _attachedTo == nullptr ? nullptr : _attachedTo->getState();
|
||||
state->setAttachedTo(newParent);
|
||||
|
||||
gridPoint.setX(-1);
|
||||
state->setAttachedTo(_attachedTo);
|
||||
if (state->getAttachedTo() != nullptr) {
|
||||
// If the zone is being torn down, it might already be null by the time a card tries to un-attach all its
|
||||
// attached cards
|
||||
if (state->getAttachedTo()->getZone() == nullptr) {
|
||||
deleteLater();
|
||||
} else {
|
||||
emit state->getAttachedTo()->getZone()->cardAdded(this);
|
||||
state->getAttachedTo()->addAttachedCard(this);
|
||||
if (state->getZone() != state->getAttachedTo()->getZone()) {
|
||||
state->getAttachedTo()->getZone()->reorganizeCards();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the zone is being torn down, it might already be null by the time a card tries to un-attach all its
|
||||
// attached cards
|
||||
if (state->getZone() == nullptr) {
|
||||
deleteLater();
|
||||
} else {
|
||||
emit state->getZone()->cardAdded(this);
|
||||
}
|
||||
}
|
||||
|
||||
if (state->getZone() != nullptr) {
|
||||
state->getZone()->reorganizeCards();
|
||||
// If the zone is being torn down, it might already be null by the time a card tries to un-attach all its attached
|
||||
// cards
|
||||
if (newParent != nullptr ? newParent->getZone() == nullptr : state->getZone() == nullptr) {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -248,13 +245,6 @@ void CardItem::setAttachedTo(CardItem *_attachedTo)
|
|||
void CardItem::resetState(bool keepAnnotations)
|
||||
{
|
||||
state->resetState(keepAnnotations);
|
||||
attachedCards.clear();
|
||||
setTapped(false, false);
|
||||
setDoesntUntap(false);
|
||||
if (scene()) {
|
||||
static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void CardItem::processCardInfo(const ServerInfo_Card &_info)
|
||||
|
|
@ -390,7 +380,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(), forceFaceDown);
|
||||
createDragItem(state->getId(), event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown);
|
||||
dragItem->grabMouse();
|
||||
|
||||
int childIndex = 0;
|
||||
|
|
@ -433,42 +423,11 @@ void CardItem::playCard(bool faceDown)
|
|||
emit playSelected(this);
|
||||
}
|
||||
} else {
|
||||
state->getZone()->getPlayer()->getPlayerActions()->playCard(this, faceDown);
|
||||
state->getZone()->getPlayer()->getPlayerActions()->playCard(state, faceDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList CardItem::parsePT(const QString &pt)
|
||||
{
|
||||
QVariantList ptList = QVariantList();
|
||||
if (!pt.isEmpty()) {
|
||||
int sep = pt.indexOf('/');
|
||||
if (sep == 0) {
|
||||
ptList.append(QVariant(pt.mid(1))); // cut off starting '/' and take full string
|
||||
} else {
|
||||
int start = 0;
|
||||
for (;;) {
|
||||
QString item = pt.mid(start, sep - start);
|
||||
if (item.isEmpty()) {
|
||||
ptList.append(QVariant(QString()));
|
||||
} else if (item[0] == '+') {
|
||||
ptList.append(QVariant(item.mid(1).toInt())); // add as int
|
||||
} else if (item[0] == '-') {
|
||||
ptList.append(QVariant(item.toInt())); // add as int
|
||||
} else {
|
||||
ptList.append(QVariant(item)); // add as qstring
|
||||
}
|
||||
if (sep == -1) {
|
||||
break;
|
||||
}
|
||||
start = sep + 1;
|
||||
sep = pt.indexOf('/', start);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ptList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns true if the zone is a unwritable reveal zone view (eg a card reveal window). Will return false if zone
|
||||
* is nullptr.
|
||||
|
|
@ -493,7 +452,7 @@ void CardItem::handleClickedToPlay(bool shiftHeld)
|
|||
if (SettingsCache::instance().userInterface().getClickPlaysAllSelected()) {
|
||||
emit hideSelected(this);
|
||||
} else {
|
||||
state->getZone()->removeCard(this);
|
||||
state->getZone()->removeCard(state);
|
||||
}
|
||||
} else {
|
||||
playCard(shiftHeld);
|
||||
|
|
@ -537,16 +496,16 @@ bool CardItem::animationEvent()
|
|||
{
|
||||
int rotation = ROTATION_DEGREES_PER_FRAME;
|
||||
bool animationIncomplete = true;
|
||||
if (!tapped) {
|
||||
if (!state->getTapped()) {
|
||||
rotation *= -1;
|
||||
}
|
||||
|
||||
tapAngle += rotation;
|
||||
if (tapped && (tapAngle > 90)) {
|
||||
if (state->getTapped() && (tapAngle > 90)) {
|
||||
tapAngle = 90;
|
||||
animationIncomplete = false;
|
||||
}
|
||||
if (!tapped && (tapAngle < 0)) {
|
||||
if (!state->getTapped() && (tapAngle < 0)) {
|
||||
tapAngle = 0;
|
||||
animationIncomplete = false;
|
||||
}
|
||||
|
|
@ -567,7 +526,9 @@ QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value)
|
|||
bool selected = value.toBool();
|
||||
|
||||
if (selected) {
|
||||
owner->getGame()->setActiveCard(this);
|
||||
if (auto *gameScene = qobject_cast<GameScene *>(scene())) {
|
||||
gameScene->setActiveCard(this);
|
||||
}
|
||||
}
|
||||
|
||||
emit selectionChanged(this, selected);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#ifndef CARDITEM_H
|
||||
#define CARDITEM_H
|
||||
|
||||
#include "../../game/board/card_state.h"
|
||||
#include "abstract_card_item.h"
|
||||
#include "graphics_item_type.h"
|
||||
|
||||
|
|
@ -16,6 +15,7 @@
|
|||
#include <QPointF>
|
||||
#include <QString>
|
||||
#include <QVariantList>
|
||||
#include <libcockatrice/game/board/card_state.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class CardDragItem;
|
||||
|
|
@ -33,11 +33,7 @@ class CardItem : public AbstractCardItem
|
|||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
CardState *state;
|
||||
|
||||
QPoint gridPoint;
|
||||
CardDragItem *dragItem;
|
||||
QList<CardItem *> attachedCards;
|
||||
|
||||
void prepareDelete();
|
||||
void handleClickedToPlay(bool shiftHeld);
|
||||
|
|
@ -60,10 +56,6 @@ public:
|
|||
CardZoneLogic *_zone = nullptr);
|
||||
|
||||
void retranslateUi();
|
||||
[[nodiscard]] CardState *getState() const
|
||||
{
|
||||
return state;
|
||||
}
|
||||
[[nodiscard]] CardZoneLogic *getZone() const
|
||||
{
|
||||
return state->getZone();
|
||||
|
|
@ -72,15 +64,15 @@ public:
|
|||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
|
||||
[[nodiscard]] QPoint getGridPoint() const
|
||||
{
|
||||
return gridPoint;
|
||||
return state->getGridPoint();
|
||||
}
|
||||
void setGridPoint(const QPoint &_gridPoint)
|
||||
{
|
||||
gridPoint = _gridPoint;
|
||||
state->setGridPoint(_gridPoint);
|
||||
}
|
||||
[[nodiscard]] QPoint getGridPos() const
|
||||
{
|
||||
return gridPoint;
|
||||
return state->getGridPoint();
|
||||
}
|
||||
[[nodiscard]] PlayerLogic *getOwner() const
|
||||
{
|
||||
|
|
@ -123,22 +115,22 @@ public:
|
|||
{
|
||||
state->setDestroyOnZoneChange(_destroy);
|
||||
}
|
||||
[[nodiscard]] CardItem *getAttachedTo() const
|
||||
[[nodiscard]] CardState *getAttachedTo() const
|
||||
{
|
||||
return state->getAttachedTo();
|
||||
}
|
||||
void setAttachedTo(CardItem *_attachedTo);
|
||||
void addAttachedCard(CardItem *card)
|
||||
void addAttachedCard(CardState *card)
|
||||
{
|
||||
attachedCards.append(card);
|
||||
state->addAttachedCard(card);
|
||||
}
|
||||
void removeAttachedCard(CardItem *card)
|
||||
void removeAttachedCard(CardState *card)
|
||||
{
|
||||
attachedCards.removeOne(card);
|
||||
state->removeAttachedCard(card);
|
||||
}
|
||||
[[nodiscard]] const QList<CardItem *> &getAttachedCards() const
|
||||
[[nodiscard]] const QList<CardState *> &getAttachedCards() const
|
||||
{
|
||||
return attachedCards;
|
||||
return state->getAttachedCards();
|
||||
}
|
||||
void resetState(bool keepAnnotations = false);
|
||||
void processCardInfo(const ServerInfo_Card &_info);
|
||||
|
|
@ -150,26 +142,6 @@ public:
|
|||
void drawAttachArrow();
|
||||
void playCard(bool faceDown);
|
||||
|
||||
/**
|
||||
* @brief Parses a string representing a p/t in order to extract the values from it.
|
||||
*
|
||||
* If the string contains '/', the string will be split at the '/' and each side will be parsed separately,
|
||||
* which means the result list will have two elements.
|
||||
*
|
||||
* If '/' is not found, then the entire string is parsed together, which means the result list will
|
||||
* have a single element.
|
||||
*
|
||||
* If either side of the split is empty, there will also only be a single element in the result list.
|
||||
*
|
||||
* This function will attempt to parse each substring as an int first, handling plus and minus prefixes.
|
||||
* If successful, it will put the parsed value into the QVariant as an int.
|
||||
* If failed, it will just put the substring into the QVariant as a QString.
|
||||
*
|
||||
* @param pt The p/t string
|
||||
* @return A QVariantList that can contain one or two elements, where each QVariant can be either int or QString
|
||||
*/
|
||||
static QVariantList parsePT(const QString &pt);
|
||||
|
||||
protected:
|
||||
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,8 @@
|
|||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../client/settings/shortcuts_settings.h"
|
||||
#include "../../game/abstract_game.h"
|
||||
#include "../../game/game_event_handler.h"
|
||||
#include "../../game/game_state.h"
|
||||
#include "../../interface/card_picture_loader/card_picture_loader.h"
|
||||
#include "../../interface/deck_loader/deck_file_format.h"
|
||||
#include "../../interface/deck_loader/deck_loader.h"
|
||||
#include "../../interface/deck_loader/loaded_deck.h"
|
||||
#include "../../interface/widgets/dialogs/dlg_load_deck.h"
|
||||
#include "../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h"
|
||||
#include "../../interface/widgets/dialogs/dlg_load_deck_from_website.h"
|
||||
|
|
@ -33,16 +28,17 @@
|
|||
#include <QRect>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/deck_file_format.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <libcockatrice/deck_list/playmat_resolver.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_select.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_ready_start.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_playmat.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_sideboard_lock.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_sideboard_plan.pb.h>
|
||||
#include <libcockatrice/game/abstract_game.h>
|
||||
#include <libcockatrice/game/game_event_handler.h>
|
||||
#include <libcockatrice/game/game_state.h>
|
||||
#include <libcockatrice/protocol/pb/response_deck_download.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/settings/visual_deck_storage_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <libcockatrice/utility/string_limits.h>
|
||||
#include <optional>
|
||||
#include <qnamespace.h>
|
||||
|
|
@ -111,10 +107,9 @@ DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent)
|
|||
connect(deckView, &DeckView::newCardAdded, this, &DeckViewContainer::newCardAdded);
|
||||
connect(deckView, &DeckView::sideboardPlanChanged, this, &DeckViewContainer::sideboardPlanChanged);
|
||||
|
||||
deckViewLayout = new QVBoxLayout;
|
||||
deckViewLayout = QtUtils::makeLayout<QVBoxLayout>();
|
||||
deckViewLayout->addLayout(buttonHBox);
|
||||
deckViewLayout->addWidget(deckView);
|
||||
deckViewLayout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(deckViewLayout);
|
||||
|
||||
retranslateUi();
|
||||
|
|
@ -314,9 +309,7 @@ void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck)
|
|||
return;
|
||||
}
|
||||
|
||||
Command_DeckSelect cmd;
|
||||
cmd.set_deck(deckString.toStdString());
|
||||
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(cmd);
|
||||
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareDeckSelect(deckString);
|
||||
connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished);
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId);
|
||||
|
||||
|
|
@ -345,15 +338,7 @@ void DeckViewContainer::resolveAndSendPlaymat()
|
|||
|
||||
lastResolvedPlaymat = resolved;
|
||||
|
||||
Command_SetPlaymat playmatCmd;
|
||||
auto *pp = playmatCmd.mutable_playmat_params();
|
||||
pp->set_card_name(resolved.card.name.toStdString());
|
||||
pp->set_card_provider_id(resolved.card.providerId.toStdString());
|
||||
pp->set_margin_pct_l(resolved.params.marginPctL);
|
||||
pp->set_margin_pct_r(resolved.params.marginPctR);
|
||||
pp->set_vertical_offset(resolved.params.verticalOffset);
|
||||
pp->set_zoom(resolved.params.zoom);
|
||||
PendingCommand *playmatPend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(playmatCmd);
|
||||
PendingCommand *playmatPend = parentGame->getGame()->getGameEventHandler()->prepareSetPlaymat(resolved);
|
||||
const QString sentName = resolved.card.name;
|
||||
connect(playmatPend, &PendingCommand::finished, this, [this, sentName](const Response &r) {
|
||||
// Only RespFunctionNotAllowed signals a card art DENY rule. Older servers that predate
|
||||
|
|
@ -384,9 +369,7 @@ void DeckViewContainer::loadRemoteDeck()
|
|||
{
|
||||
DlgLoadRemoteDeck dlg(parentGame->getGame()->getClientForPlayer(playerId), this);
|
||||
if (dlg.exec()) {
|
||||
Command_DeckSelect cmd;
|
||||
cmd.set_deck_id(dlg.getDeckId());
|
||||
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(cmd);
|
||||
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareDeckSelectId(dlg.getDeckId());
|
||||
connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished);
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId);
|
||||
}
|
||||
|
|
@ -442,34 +425,21 @@ void DeckViewContainer::readyStart()
|
|||
void DeckViewContainer::forceStart()
|
||||
{
|
||||
const auto msg = tr("Are you sure you want to force start?\nThis will kick all non-ready players from the game.");
|
||||
const auto res =
|
||||
QMessageBox::question(this, tr("Cockatrice"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
if (res == QMessageBox::No) {
|
||||
if (!QtUtils::confirm(this, tr("Cockatrice"), msg, QMessageBox::Yes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Command_ReadyStart cmd;
|
||||
cmd.set_force_start(true);
|
||||
cmd.set_ready(true);
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
|
||||
parentGame->getGame()->getGameEventHandler()->sendForceStart(playerId);
|
||||
}
|
||||
|
||||
void DeckViewContainer::sideboardLockButtonClicked()
|
||||
{
|
||||
Command_SetSideboardLock cmd;
|
||||
cmd.set_locked(sideboardLockButton->getState());
|
||||
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
|
||||
parentGame->getGame()->getGameEventHandler()->sendSetSideboardLock(sideboardLockButton->getState(), playerId);
|
||||
}
|
||||
|
||||
void DeckViewContainer::sideboardPlanChanged()
|
||||
{
|
||||
Command_SetSideboardPlan cmd;
|
||||
const QList<MoveCard_ToZone> &newPlan = deckView->getSideboardPlan();
|
||||
for (const auto &i : newPlan) {
|
||||
cmd.add_move_list()->CopyFrom(i);
|
||||
}
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
|
||||
parentGame->getGame()->getGameEventHandler()->sendSetSideboardPlan(deckView->getSideboardPlan(), playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -481,9 +451,7 @@ void DeckViewContainer::sendReadyStartCommand(bool ready)
|
|||
resolveAndSendPlaymat();
|
||||
}
|
||||
|
||||
Command_ReadyStart cmd;
|
||||
cmd.set_ready(ready);
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
|
||||
parentGame->getGame()->getGameEventHandler()->sendReadyStart(ready, playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
13
cockatrice/src/game_graphics/dialogs/dlg_annotation.cpp
Normal file
13
cockatrice/src/game_graphics/dialogs/dlg_annotation.cpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#include "dlg_annotation.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
|
||||
void AnnotationDialog::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->key() == Qt::Key_Return && event->modifiers() & Qt::ControlModifier) {
|
||||
event->accept();
|
||||
accept();
|
||||
return;
|
||||
}
|
||||
QInputDialog::keyPressEvent(event);
|
||||
}
|
||||
22
cockatrice/src/game_graphics/dialogs/dlg_annotation.h
Normal file
22
cockatrice/src/game_graphics/dialogs/dlg_annotation.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* @file dlg_annotation.h
|
||||
* @ingroup GameDialogs
|
||||
*/
|
||||
|
||||
#ifndef COCKATRICE_DLG_ANNOTATION_H
|
||||
#define COCKATRICE_DLG_ANNOTATION_H
|
||||
|
||||
#include <QInputDialog>
|
||||
|
||||
class AnnotationDialog : public QInputDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
public:
|
||||
explicit AnnotationDialog(QWidget *parent = nullptr) : QInputDialog(parent)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_DLG_ANNOTATION_H
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <libcockatrice/game/player/token_info.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class QLabel;
|
||||
|
|
@ -25,17 +26,6 @@ class CardInfoPictureWidget;
|
|||
class QModelIndex;
|
||||
class QWidget;
|
||||
|
||||
struct TokenInfo
|
||||
{
|
||||
QString name;
|
||||
QString color;
|
||||
QString pt;
|
||||
QString annotation;
|
||||
bool destroy = true;
|
||||
bool faceDown = false;
|
||||
QString providerId;
|
||||
};
|
||||
|
||||
class DlgCreateToken : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <libcockatrice/game/player/move_top_cards_until_options.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class FilterString;
|
||||
|
|
@ -21,13 +22,6 @@ class QLabel;
|
|||
class QSpinBox;
|
||||
class QWidget;
|
||||
|
||||
struct MoveTopCardsUntilOptions
|
||||
{
|
||||
QStringList exprs = {};
|
||||
int numberOfHits = 1;
|
||||
bool autoPlay = false;
|
||||
};
|
||||
|
||||
class DlgMoveTopCardsUntil : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
|||
|
|
@ -1,18 +1,10 @@
|
|||
#include "game_scene.h"
|
||||
|
||||
#include "../client/settings/cache_settings.h"
|
||||
#include "../game/abstract_game.h"
|
||||
#include "../game/arrow_registry.h"
|
||||
#include "../game/board/arrow_data.h"
|
||||
#include "../game/player/player_actions.h"
|
||||
#include "../game/player/player_event_handler.h"
|
||||
#include "../game/player/player_info.h"
|
||||
#include "../game/player/player_logic.h"
|
||||
#include "../game/zones/card_zone_logic.h"
|
||||
#include "../game/zones/view_zone_logic.h"
|
||||
#include "animated_item.h"
|
||||
#include "board/abstract_card_item.h"
|
||||
#include "board/arrow_item.h"
|
||||
#include "board/arrow_registry.h"
|
||||
#include "board/card_item.h"
|
||||
#include "phases_toolbar.h"
|
||||
#include "player/menu/player_menu.h"
|
||||
|
|
@ -40,6 +32,15 @@
|
|||
#include <QSharedPointer>
|
||||
#include <QtMath>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <libcockatrice/game/abstract_game.h>
|
||||
#include <libcockatrice/game/board/arrow_data.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_event_handler.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/view_zone_logic.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
#include <numeric>
|
||||
|
|
@ -112,6 +113,15 @@ void GameScene::retranslateUi()
|
|||
}
|
||||
}
|
||||
|
||||
QList<CardState *> GameScene::selectedCardStates() const
|
||||
{
|
||||
QList<CardState *> selectedStates;
|
||||
for (auto *card : selectedCards()) {
|
||||
selectedStates.append(card->getState());
|
||||
}
|
||||
return selectedStates;
|
||||
}
|
||||
|
||||
QList<CardItem *> GameScene::selectedCards() const
|
||||
{
|
||||
QList<CardItem *> selectedCards;
|
||||
|
|
@ -124,6 +134,67 @@ QList<CardItem *> GameScene::selectedCards() const
|
|||
return selectedCards;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Selects every card of @p zone for which @p filter holds.
|
||||
* @param filter A predicate to filter which cards are selected.
|
||||
*/
|
||||
static void selectCardsInZone(
|
||||
const CardZoneLogic *zone,
|
||||
std::function<bool(const CardItem *)> filter = [](const CardItem *) { return true; })
|
||||
{
|
||||
if (!zone) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto *cardState : zone->getCards()) {
|
||||
auto *cardItem = cardState == nullptr ? nullptr : qobject_cast<CardItem *>(cardState->parent());
|
||||
if (cardItem && filter(cardItem)) {
|
||||
cardItem->setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameScene::selectAllInZone(const CardItem *card)
|
||||
{
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
selectCardsInZone(card->getZone());
|
||||
}
|
||||
|
||||
void GameScene::selectSameRow(const CardItem *card)
|
||||
{
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto isSameRow = [card](const CardItem *cardItem) {
|
||||
return qAbs(card->scenePos().y() - cardItem->scenePos().y()) < 50;
|
||||
};
|
||||
selectCardsInZone(card->getZone(), isSameRow);
|
||||
}
|
||||
|
||||
void GameScene::selectSameColumn(const CardItem *card)
|
||||
{
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto isSameColumn = [card](const CardItem *cardItem) { return cardItem->x() == card->x(); };
|
||||
selectCardsInZone(card->getZone(), isSameColumn);
|
||||
}
|
||||
|
||||
CardItem *GameScene::getActiveCard() const
|
||||
{
|
||||
return activeCard;
|
||||
}
|
||||
|
||||
void GameScene::setActiveCard(CardItem *card)
|
||||
{
|
||||
activeCard = card;
|
||||
}
|
||||
|
||||
void GameScene::onCardSelectionChanged(AbstractCardItem *abstractCard, bool selected)
|
||||
{
|
||||
CardItem *card = qobject_cast<CardItem *>(abstractCard);
|
||||
|
|
@ -134,12 +205,12 @@ void GameScene::onCardSelectionChanged(AbstractCardItem *abstractCard, bool sele
|
|||
auto *owner = card->getOwner();
|
||||
|
||||
if (selected) {
|
||||
owner->requestCardMenuUpdate(card);
|
||||
owner->requestCardMenuUpdate(card->getState());
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedItems().isEmpty()) {
|
||||
owner->getGame()->setActiveCard(nullptr);
|
||||
setActiveCard(nullptr);
|
||||
owner->requestCardMenuUpdate(nullptr);
|
||||
}
|
||||
}
|
||||
|
|
@ -158,9 +229,9 @@ void GameScene::onCardRightClicked(AbstractCardItem *abstractCard, QPoint screen
|
|||
return;
|
||||
}
|
||||
|
||||
card->getOwner()->getGame()->setActiveCard(card);
|
||||
setActiveCard(card);
|
||||
|
||||
if (auto *menu = view->getPlayerMenu()->updateCardMenu(card)) {
|
||||
if (auto *menu = view->getPlayerMenu()->updateCardMenu(card->getState())) {
|
||||
menu->popup(screenPos);
|
||||
}
|
||||
}
|
||||
|
|
@ -173,7 +244,7 @@ void GameScene::playSelected(AbstractCardItem *card)
|
|||
if (!card->getOwner()) {
|
||||
return;
|
||||
}
|
||||
card->getOwner()->getPlayerActions()->actPlay(selectedCards());
|
||||
card->getOwner()->getPlayerActions()->actPlay(selectedCardStates());
|
||||
}
|
||||
|
||||
void GameScene::playSelectedFaceDown(AbstractCardItem *card)
|
||||
|
|
@ -184,7 +255,7 @@ void GameScene::playSelectedFaceDown(AbstractCardItem *card)
|
|||
if (!card->getOwner()) {
|
||||
return;
|
||||
}
|
||||
card->getOwner()->getPlayerActions()->actPlayFacedown(selectedCards());
|
||||
card->getOwner()->getPlayerActions()->actPlayFacedown(selectedCardStates());
|
||||
}
|
||||
|
||||
void GameScene::hideSelected(AbstractCardItem *card)
|
||||
|
|
@ -195,7 +266,7 @@ void GameScene::hideSelected(AbstractCardItem *card)
|
|||
if (!card->getOwner()) {
|
||||
return;
|
||||
}
|
||||
card->getOwner()->getPlayerActions()->actHide(selectedCards());
|
||||
card->getOwner()->getPlayerActions()->actHide(selectedCardStates());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -230,6 +301,8 @@ void GameScene::addPlayer(PlayerLogic *player)
|
|||
[this, id = player->getPlayerInfo()->getId()]() { clearArrowsForPlayerLocally(id); });
|
||||
|
||||
connect(player->getPlayerEventHandler(), &PlayerEventHandler::cardZoneChanged, this, &GameScene::onCardZoneChanged);
|
||||
connect(player->getPlayerEventHandler(), &PlayerEventHandler::cardViewRefreshRequested, this,
|
||||
&GameScene::onCardViewRefreshRequested);
|
||||
|
||||
rearrange();
|
||||
}
|
||||
|
|
@ -533,7 +606,11 @@ void GameScene::addArrow(QSharedPointer<ArrowData> data)
|
|||
return;
|
||||
}
|
||||
|
||||
CardItem *startCard = startZone->getCard(data->startCardId);
|
||||
CardState *startCardState = startZone->getCard(data->startCardId);
|
||||
if (!startCardState) {
|
||||
return;
|
||||
}
|
||||
CardItem *startCard = qobject_cast<CardItem *>(startCardState->parent());
|
||||
if (!startCard) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -544,7 +621,8 @@ void GameScene::addArrow(QSharedPointer<ArrowData> data)
|
|||
} else {
|
||||
auto *zone = targetView->getLogic()->getZones().value(data->targetZone);
|
||||
if (zone) {
|
||||
targetItem = zone->getCard(data->targetCardId);
|
||||
CardState *targetCardState = zone->getCard(data->targetCardId);
|
||||
targetItem = targetCardState == nullptr ? nullptr : qobject_cast<CardItem *>(targetCardState->parent());
|
||||
}
|
||||
}
|
||||
if (!targetItem) {
|
||||
|
|
@ -572,11 +650,15 @@ void GameScene::requestArrowDeletion(int playerId, int arrowId)
|
|||
}
|
||||
}
|
||||
|
||||
void GameScene::onCardZoneChanged(CardItem *card, bool sameZone)
|
||||
void GameScene::onCardZoneChanged(CardState *card, bool sameZone)
|
||||
{
|
||||
auto *item = qobject_cast<CardItem *>(card->parent());
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
QList<ArrowItem *> toDelete;
|
||||
for (auto *arrow : arrowRegistry.all()) {
|
||||
if (arrow->getStartItem() == card || arrow->getTargetItem() == card) {
|
||||
if (arrow->getStartItem() == item || arrow->getTargetItem() == item) {
|
||||
if (sameZone) {
|
||||
arrow->updatePath();
|
||||
} else {
|
||||
|
|
@ -589,6 +671,25 @@ void GameScene::onCardZoneChanged(CardItem *card, bool sameZone)
|
|||
}
|
||||
}
|
||||
|
||||
void GameScene::onCardViewRefreshRequested(CardState *card, bool zoneChanged, PlayerLogic *newOwner)
|
||||
{
|
||||
auto *item = qobject_cast<CardItem *>(card->parent());
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (zoneChanged) {
|
||||
item->deleteCardInfoPopup();
|
||||
}
|
||||
item->deleteDragItem();
|
||||
if (zoneChanged) {
|
||||
item->setBeingPointedAt(false);
|
||||
item->setHovered(false);
|
||||
}
|
||||
if (newOwner) {
|
||||
item->setOwner(newOwner);
|
||||
}
|
||||
}
|
||||
|
||||
void GameScene::clearArrowsForPlayer(int playerId)
|
||||
{
|
||||
for (int arrowId : arrowRegistry.idsForPlayer(playerId)) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#ifndef GAMESCENE_H
|
||||
#define GAMESCENE_H
|
||||
|
||||
#include "../game/arrow_registry.h"
|
||||
#include "animated_item.h"
|
||||
#include "board/arrow_item.h"
|
||||
#include "board/arrow_registry.h"
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QHash>
|
||||
|
|
@ -16,6 +18,8 @@
|
|||
#include <QString>
|
||||
#include <QTransform>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/game/board/arrow_data.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
// IWYU pragma: keep
|
||||
|
|
@ -39,6 +43,7 @@ class ZoneViewWidget;
|
|||
class CardZone;
|
||||
class AbstractCardItem;
|
||||
class CardItem;
|
||||
class CardState;
|
||||
class ServerInfo_Card;
|
||||
class PhasesToolbar;
|
||||
class QBasicTimer;
|
||||
|
|
@ -69,6 +74,7 @@ private:
|
|||
QList<ZoneViewWidget *> zoneViews; ///< Active zone view widgets
|
||||
QSize viewSize; ///< Current view size
|
||||
QPointer<CardItem> hoveredCard; ///< Currently hovered card
|
||||
QPointer<CardItem> activeCard; ///< Card the currently open context menu targets
|
||||
QBasicTimer *animationTimer; ///< Timer for scene animations
|
||||
QHash<QObject *, IAnimatedItem *> animatedItems; ///< Items currently animating
|
||||
QHash<QObject *, QMetaObject::Connection>
|
||||
|
|
@ -107,6 +113,22 @@ public:
|
|||
|
||||
/** @brief Gets all selected CardItems. */
|
||||
QList<CardItem *> selectedCards() const;
|
||||
QList<CardState *> selectedCardStates() const;
|
||||
|
||||
/** @brief Selects every card sharing the zone of @p card. */
|
||||
void selectAllInZone(const CardItem *card);
|
||||
|
||||
/** @brief Selects the cards laid out in the same visual row as @p card. */
|
||||
void selectSameRow(const CardItem *card);
|
||||
|
||||
/** @brief Selects the cards laid out in the same visual column as @p card. */
|
||||
void selectSameColumn(const CardItem *card);
|
||||
|
||||
/** @brief Card the currently open context menu targets, or null. */
|
||||
CardItem *getActiveCard() const;
|
||||
|
||||
/** @brief Sets the card the currently open context menu targets. */
|
||||
void setActiveCard(CardItem *card);
|
||||
|
||||
/**
|
||||
* @brief Adds a player to the scene and stores their graphics item.
|
||||
|
|
@ -261,7 +283,15 @@ public slots:
|
|||
/// Queues up arrow deletion but doesn't directly modify the scene
|
||||
void requestArrowDeletion(int playerId, int arrowId);
|
||||
|
||||
void onCardZoneChanged(CardItem *card, bool sameZone);
|
||||
void onCardZoneChanged(CardState *card, bool sameZone);
|
||||
/**
|
||||
* @brief Refreshes the view of a card that the server just moved.
|
||||
*
|
||||
* @param card The card that moved.
|
||||
* @param zoneChanged Whether the card crossed a zone boundary.
|
||||
* @param newOwner The player the card now belongs to, or nullptr to leave the owner alone.
|
||||
*/
|
||||
void onCardViewRefreshRequested(CardState *card, bool zoneChanged, PlayerLogic *newOwner);
|
||||
|
||||
protected:
|
||||
/** @brief Handles hover updates. */
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
#include "hand_counter.h"
|
||||
|
||||
#include "../game/board/card_list.h"
|
||||
#include "../game/zones/card_zone_logic.h"
|
||||
#include "../interface/pixel_map_generator.h"
|
||||
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
|
|
@ -14,6 +12,8 @@
|
|||
#include <QString>
|
||||
#include <QStringLiteral>
|
||||
#include <QTransform>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
class QGraphicsItem;
|
||||
|
|
|
|||
|
|
@ -3,16 +3,6 @@
|
|||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../client/settings/card_counter_settings.h"
|
||||
#include "../../client/sound_engine.h"
|
||||
#include "../../client/translation.h"
|
||||
#include "../../game/abstract_game.h"
|
||||
#include "../../game/board/card_list.h"
|
||||
#include "../../game/game_state.h"
|
||||
#include "../../game/phase.h"
|
||||
#include "../../game/player/player_event_handler.h"
|
||||
#include "../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game/player/player_manager.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../../interface/card_localization.h"
|
||||
#include "../../interface/widgets/server/chat_view/chat_view.h"
|
||||
#include "../board/card_item.h"
|
||||
|
|
@ -24,6 +14,16 @@
|
|||
#include <QList>
|
||||
#include <QSharedPointer>
|
||||
#include <QStringList>
|
||||
#include <libcockatrice/game/abstract_game.h>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <libcockatrice/game/game_state.h>
|
||||
#include <libcockatrice/game/phase.h>
|
||||
#include <libcockatrice/game/player/player_event_handler.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/player/player_manager.h>
|
||||
#include <libcockatrice/game/translation.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/protocol/pb/context_move_card.pb.h>
|
||||
#include <libcockatrice/protocol/pb/context_mulligan.pb.h>
|
||||
#include <libcockatrice/settings/chat_settings.h>
|
||||
|
|
@ -283,7 +283,7 @@ void MessageLogWidget::logDestroyCard(PlayerLogic *player, QString cardName)
|
|||
}
|
||||
|
||||
void MessageLogWidget::logMoveCard(PlayerLogic *player,
|
||||
CardItem *card,
|
||||
CardState *card,
|
||||
CardZoneLogic *startZone,
|
||||
int oldX,
|
||||
CardZoneLogic *targetZone,
|
||||
|
|
@ -662,7 +662,7 @@ void MessageLogWidget::logSetActivePlayer(PlayerLogic *player)
|
|||
QString(tr("%1's turn.")).arg(player->getPlayerInfo()->getName()) + "</b></font><br>");
|
||||
}
|
||||
|
||||
void MessageLogWidget::logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation)
|
||||
void MessageLogWidget::logSetAnnotation(PlayerLogic *player, CardState *card, QString newAnnotation)
|
||||
{
|
||||
appendHtmlServerMessage(
|
||||
QString(tr("%1 sets annotation of %2 to %3."))
|
||||
|
|
@ -706,7 +706,7 @@ void MessageLogWidget::logSetCounter(PlayerLogic *player, QString counterName, i
|
|||
.arg(value - oldValue));
|
||||
}
|
||||
|
||||
void MessageLogWidget::logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap)
|
||||
void MessageLogWidget::logSetDoesntUntap(PlayerLogic *player, CardState *card, bool doesntUntap)
|
||||
{
|
||||
QString str;
|
||||
if (doesntUntap) {
|
||||
|
|
@ -717,7 +717,7 @@ void MessageLogWidget::logSetDoesntUntap(PlayerLogic *player, CardItem *card, bo
|
|||
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(card->getName())));
|
||||
}
|
||||
|
||||
void MessageLogWidget::logSetPT(PlayerLogic *player, CardItem *card, QString newPT)
|
||||
void MessageLogWidget::logSetPT(PlayerLogic *player, CardState *card, QString newPT)
|
||||
{
|
||||
if (currentContext == MessageContext_MoveCard) {
|
||||
return;
|
||||
|
|
@ -755,7 +755,7 @@ void MessageLogWidget::logSetSideboardLock(PlayerLogic *player, bool locked)
|
|||
}
|
||||
}
|
||||
|
||||
void MessageLogWidget::logSetTapped(PlayerLogic *player, CardItem *card, bool tapped)
|
||||
void MessageLogWidget::logSetTapped(PlayerLogic *player, CardState *card, bool tapped)
|
||||
{
|
||||
if (currentContext == MessageContext_MoveCard) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <qtmetamacros.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
class CardState;
|
||||
class AbstractGame;
|
||||
class CardItem;
|
||||
class GameEventContext;
|
||||
|
|
@ -76,7 +77,7 @@ public slots:
|
|||
void logLeaveSpectator(QString name, QString reason);
|
||||
void logNotReadyStart(PlayerLogic *player);
|
||||
void logMoveCard(PlayerLogic *player,
|
||||
CardItem *card,
|
||||
CardState *card,
|
||||
CardZoneLogic *startZone,
|
||||
int oldX,
|
||||
CardZoneLogic *targetZone,
|
||||
|
|
@ -97,13 +98,13 @@ public slots:
|
|||
void logSay(PlayerLogic *player, QString message);
|
||||
void logSetActivePhase(int phase);
|
||||
void logSetActivePlayer(PlayerLogic *player);
|
||||
void logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation);
|
||||
void logSetAnnotation(PlayerLogic *player, CardState *card, QString newAnnotation);
|
||||
void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue);
|
||||
void logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue);
|
||||
void logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap);
|
||||
void logSetPT(PlayerLogic *player, CardItem *card, QString newPT);
|
||||
void logSetDoesntUntap(PlayerLogic *player, CardState *card, bool doesntUntap);
|
||||
void logSetPT(PlayerLogic *player, CardState *card, QString newPT);
|
||||
void logSetSideboardLock(PlayerLogic *player, bool locked);
|
||||
void logSetTapped(PlayerLogic *player, CardItem *card, bool tapped);
|
||||
void logSetTapped(PlayerLogic *player, CardState *card, bool tapped);
|
||||
void logShuffle(PlayerLogic *player, CardZoneLogic *zone, int start, int end);
|
||||
void logSpectatorSay(const ServerInfo_User &spectator, QString message);
|
||||
void logUnattachCard(PlayerLogic *player, QString cardName);
|
||||
|
|
|
|||
|
|
@ -14,11 +14,6 @@
|
|||
#include <QTransform>
|
||||
#include <QtGlobal>
|
||||
#include <google/protobuf/stubs/port.h>
|
||||
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_next_turn.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_active_phase.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qnumeric.h>
|
||||
|
||||
|
|
@ -120,9 +115,9 @@ PhasesToolbar::PhasesToolbar(QGraphicsItem *parent)
|
|||
: QGraphicsItem(parent), width(100), height(100), ySpacing(1), symbolSize(8)
|
||||
{
|
||||
auto *aUntapAll = new QAction(this);
|
||||
connect(aUntapAll, &QAction::triggered, this, &PhasesToolbar::actUntapAll);
|
||||
connect(aUntapAll, &QAction::triggered, this, &PhasesToolbar::untapAllRequested);
|
||||
auto *aDrawCard = new QAction(this);
|
||||
connect(aDrawCard, &QAction::triggered, this, &PhasesToolbar::actDrawCard);
|
||||
connect(aDrawCard, &QAction::triggered, this, &PhasesToolbar::drawCardRequested);
|
||||
|
||||
PhaseButton *untapButton = new PhaseButton("untap", this, aUntapAll);
|
||||
PhaseButton *upkeepButton = new PhaseButton("upkeep", this);
|
||||
|
|
@ -144,7 +139,7 @@ PhasesToolbar::PhasesToolbar(QGraphicsItem *parent)
|
|||
}
|
||||
|
||||
nextTurnButton = new PhaseButton("nextturn", this, nullptr, false);
|
||||
connect(nextTurnButton, &PhaseButton::clicked, this, &PhasesToolbar::actNextTurn);
|
||||
connect(nextTurnButton, &PhaseButton::clicked, this, &PhasesToolbar::nextTurnRequested);
|
||||
|
||||
rearrangeButtons();
|
||||
|
||||
|
|
@ -265,31 +260,5 @@ void PhasesToolbar::phaseButtonClicked()
|
|||
button->triggerDoubleClickAction();
|
||||
}
|
||||
|
||||
Command_SetActivePhase cmd;
|
||||
cmd.set_phase(static_cast<google::protobuf::uint32>(buttonList.indexOf(button)));
|
||||
|
||||
emit sendGameCommand(cmd, -1);
|
||||
}
|
||||
|
||||
void PhasesToolbar::actNextTurn()
|
||||
{
|
||||
emit sendGameCommand(Command_NextTurn(), -1);
|
||||
}
|
||||
|
||||
void PhasesToolbar::actUntapAll()
|
||||
{
|
||||
Command_SetCardAttr cmd;
|
||||
cmd.set_zone(ZoneNames::TABLE);
|
||||
cmd.set_attribute(AttrTapped);
|
||||
cmd.set_attr_value("0");
|
||||
|
||||
emit sendGameCommand(cmd, -1);
|
||||
}
|
||||
|
||||
void PhasesToolbar::actDrawCard()
|
||||
{
|
||||
Command_DrawCards cmd;
|
||||
cmd.set_number(1);
|
||||
|
||||
emit sendGameCommand(cmd, -1);
|
||||
emit setActivePhaseRequested(buttonList.indexOf(button));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,14 +17,6 @@
|
|||
class QAction;
|
||||
class QTimer;
|
||||
|
||||
namespace google
|
||||
{
|
||||
namespace protobuf
|
||||
{
|
||||
class Message;
|
||||
}
|
||||
} // namespace google
|
||||
|
||||
class PhaseButton : public QObject, public QGraphicsItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -95,11 +87,16 @@ public slots:
|
|||
void triggerPhaseAction(int phase);
|
||||
private slots:
|
||||
void phaseButtonClicked();
|
||||
void actNextTurn();
|
||||
void actUntapAll();
|
||||
void actDrawCard();
|
||||
signals:
|
||||
void sendGameCommand(const ::google::protobuf::Message &command, int playerId);
|
||||
/** @name Intent signals
|
||||
* The view reports what the user asked for; GameEventHandler builds the commands.
|
||||
* @{
|
||||
*/
|
||||
void setActivePhaseRequested(int phase);
|
||||
void nextTurnRequested();
|
||||
void untapAllRequested();
|
||||
void drawCardRequested();
|
||||
/** @} */
|
||||
|
||||
protected:
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) override;
|
||||
|
|
|
|||
|
|
@ -3,16 +3,8 @@
|
|||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/card_counter_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../../game/abstract_game.h"
|
||||
#include "../../../game/player/player_info.h"
|
||||
#include "../../../game/player/player_manager.h"
|
||||
#include "../../../game/zones/card_zone_logic.h"
|
||||
#include "../../board/card_item.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game/zones/view_zone_logic.h"
|
||||
#include "../../game_scene.h"
|
||||
#include "../card_menu_action_type.h"
|
||||
#include "../player_graphics_item.h"
|
||||
#include "libcockatrice/card/card_info.h"
|
||||
#include "libcockatrice/card/database/card_database_querier.h"
|
||||
|
|
@ -35,6 +27,14 @@
|
|||
#include <QtGlobal>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
#include <libcockatrice/game/abstract_game.h>
|
||||
#include <libcockatrice/game/player/card_menu_action_type.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/player/player_manager.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/view_zone_logic.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
#include <qnamespace.h>
|
||||
#include <utility>
|
||||
|
|
@ -67,7 +67,7 @@ static QAction *makeAction(QObject *parent, Slot &&slot, bool checkable = false,
|
|||
return a;
|
||||
}
|
||||
|
||||
CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _shortcutsActive)
|
||||
CardMenu::CardMenu(PlayerGraphicsItem *_player, CardItem *_card, bool _shortcutsActive)
|
||||
: player(_player), card(_card), shortcutsActive(_shortcutsActive)
|
||||
{
|
||||
const QList<PlayerLogic *> &players = player->getLogic()->getGame()->getPlayerManager()->getPlayers().values();
|
||||
|
|
@ -86,7 +86,7 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _sho
|
|||
auto *gameScene = player->getGameScene();
|
||||
|
||||
// Single selection resolver used by all lambdas — called at trigger time
|
||||
auto sel = [gameScene]() { return gameScene->selectedCards(); };
|
||||
auto sel = [gameScene]() { return gameScene->selectedCardStates(); };
|
||||
|
||||
// Unified dispatcher for card menu actions
|
||||
auto invoke = [actions, sel](CardMenuActionType type) {
|
||||
|
|
@ -108,18 +108,18 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _sho
|
|||
aHide = makeAction(this, [actions, sel]() { actions->actHide(sel()); });
|
||||
aReduceLifeByPower = makeAction(this, [actions, sel]() { actions->actReduceLifeByPower(sel()); });
|
||||
|
||||
// Actions that use activeCard, not selection — direct connection
|
||||
// Actions bound to the card this menu was opened for, not to the current selection
|
||||
aAttach = new QAction(this);
|
||||
aDrawArrow = new QAction(this);
|
||||
aSelectAll = new QAction(this);
|
||||
aSelectRow = new QAction(this);
|
||||
aSelectColumn = new QAction(this);
|
||||
|
||||
connect(aAttach, &QAction::triggered, actions, &PlayerActions::actAttach);
|
||||
connect(aDrawArrow, &QAction::triggered, actions, &PlayerActions::actDrawArrow);
|
||||
connect(aSelectAll, &QAction::triggered, actions, &PlayerActions::actSelectAll);
|
||||
connect(aSelectRow, &QAction::triggered, actions, &PlayerActions::actSelectRow);
|
||||
connect(aSelectColumn, &QAction::triggered, actions, &PlayerActions::actSelectColumn);
|
||||
connect(aAttach, &QAction::triggered, this, &CardMenu::attachCard);
|
||||
connect(aDrawArrow, &QAction::triggered, this, &CardMenu::drawArrow);
|
||||
connect(aSelectAll, &QAction::triggered, this, [this, gameScene]() { gameScene->selectAllInZone(card); });
|
||||
connect(aSelectRow, &QAction::triggered, this, [this, gameScene]() { gameScene->selectSameRow(card); });
|
||||
connect(aSelectColumn, &QAction::triggered, this, [this, gameScene]() { gameScene->selectSameColumn(card); });
|
||||
|
||||
aRevealToAll = new QAction(this);
|
||||
|
||||
|
|
@ -337,7 +337,7 @@ void CardMenu::createHandOrCustomZoneMenu(bool canModifyCard)
|
|||
initContextualPlayersMenu(revealMenu, aRevealToAll);
|
||||
|
||||
connect(revealMenu, &QMenu::triggered, this, [this](QAction *action) {
|
||||
player->getLogic()->getPlayerActions()->actReveal(player->getGameScene()->selectedCards(), action);
|
||||
player->getLogic()->getPlayerActions()->actReveal(player->getGameScene()->selectedCardStates(), action);
|
||||
});
|
||||
|
||||
addSeparator();
|
||||
|
|
@ -441,6 +441,7 @@ void CardMenu::addRelatedCardActions()
|
|||
if (relatedCards.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto *playerActions = player->getLogic()->getPlayerActions();
|
||||
|
||||
addSeparator();
|
||||
int index = 0;
|
||||
|
|
@ -484,9 +485,10 @@ void CardMenu::addRelatedCardActions()
|
|||
}
|
||||
|
||||
auto *createRelated = new QAction(text, this);
|
||||
createRelated->setData(QVariant(index++));
|
||||
connect(createRelated, &QAction::triggered, player->getLogic()->getPlayerActions(),
|
||||
&PlayerActions::actCreateRelatedCard);
|
||||
const int relatedIndex = index++;
|
||||
connect(createRelated, &QAction::triggered, this, [this, playerActions, relatedIndex] {
|
||||
playerActions->actCreateRelatedCard(card->getState(), relatedIndex);
|
||||
});
|
||||
addAction(createRelated);
|
||||
}
|
||||
|
||||
|
|
@ -495,12 +497,26 @@ void CardMenu::addRelatedCardActions()
|
|||
createRelatedCards->setShortcuts(
|
||||
SettingsCache::instance().shortcuts().getShortcut("Player/aCreateRelatedTokens"));
|
||||
}
|
||||
connect(createRelatedCards, &QAction::triggered, player->getLogic()->getPlayerActions(),
|
||||
&PlayerActions::actCreateAllRelatedCards);
|
||||
connect(createRelatedCards, &QAction::triggered, this,
|
||||
[this, playerActions] { playerActions->actCreateAllRelatedCards(card->getState()); });
|
||||
addAction(createRelatedCards);
|
||||
}
|
||||
}
|
||||
|
||||
void CardMenu::attachCard()
|
||||
{
|
||||
if (card) {
|
||||
card->drawAttachArrow();
|
||||
}
|
||||
}
|
||||
|
||||
void CardMenu::drawArrow()
|
||||
{
|
||||
if (card) {
|
||||
card->drawArrow(Qt::red);
|
||||
}
|
||||
}
|
||||
|
||||
void CardMenu::retranslateUi()
|
||||
{
|
||||
aSelectAll->setText(tr("&Select All"));
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ signals:
|
|||
void cardInfoRequested(const CardRef &cardRef);
|
||||
|
||||
public:
|
||||
explicit CardMenu(PlayerGraphicsItem *player, const CardItem *card, bool shortcutsActive);
|
||||
explicit CardMenu(PlayerGraphicsItem *player, CardItem *card, bool shortcutsActive);
|
||||
void removePlayer(PlayerLogic *playerToRemove);
|
||||
void createTableMenu(bool canModifyCard);
|
||||
void createStackMenu(bool canModifyCard);
|
||||
|
|
@ -53,11 +53,13 @@ public:
|
|||
|
||||
private:
|
||||
PlayerGraphicsItem *player;
|
||||
const CardItem *card;
|
||||
CardItem *card;
|
||||
QList<QPair<QString, int>> playersInfo;
|
||||
bool shortcutsActive;
|
||||
|
||||
void addRelatedCardActions();
|
||||
void attachCard();
|
||||
void drawArrow();
|
||||
void retranslateUi();
|
||||
void initContextualPlayersMenu(QMenu *menu, QAction *allPlayersAction);
|
||||
void setShortcutsActive();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#include "custom_zone_menu.h"
|
||||
|
||||
#include "../../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game_scene.h"
|
||||
#include "../player_graphics_item.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
|
||||
CustomZoneMenu::CustomZoneMenu(PlayerGraphicsItem *_player) : player(_player)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,13 +2,7 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../../game/player/player_info.h"
|
||||
#include "../../../game/player/player_manager.h"
|
||||
#include "../../../game/zones/pile_zone_logic.h"
|
||||
#include "../../../interface/widgets/menus/tearoff_menu.h"
|
||||
#include "../../game/abstract_game.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../player_graphics_item.h"
|
||||
|
||||
#include <QAction>
|
||||
|
|
@ -19,6 +13,12 @@
|
|||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <libcockatrice/game/abstract_game.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/player/player_manager.h>
|
||||
#include <libcockatrice/game/zones/pile_zone_logic.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
class QWidget;
|
||||
|
|
|
|||
|
|
@ -2,14 +2,7 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../../game/board/card_list.h"
|
||||
#include "../../../game/player/player_info.h"
|
||||
#include "../../../game/player/player_manager.h"
|
||||
#include "../../../game/zones/hand_zone_logic.h"
|
||||
#include "../../../interface/widgets/menus/tearoff_menu.h"
|
||||
#include "../../game/abstract_game.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../player_graphics_item.h"
|
||||
|
||||
#include <QAction>
|
||||
|
|
@ -20,6 +13,13 @@
|
|||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <libcockatrice/game/abstract_game.h>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/player/player_manager.h>
|
||||
#include <libcockatrice/game/zones/hand_zone_logic.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
class QWidget;
|
||||
|
|
|
|||
|
|
@ -2,14 +2,7 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../../game/board/card_list.h"
|
||||
#include "../../../game/player/player_info.h"
|
||||
#include "../../../game/player/player_manager.h"
|
||||
#include "../../../game/zones/pile_zone_logic.h"
|
||||
#include "../../../interface/widgets/menus/tearoff_menu.h"
|
||||
#include "../../game/abstract_game.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../player_graphics_item.h"
|
||||
|
||||
#include <QAction>
|
||||
|
|
@ -23,6 +16,13 @@
|
|||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <libcockatrice/game/abstract_game.h>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/player/player_manager.h>
|
||||
#include <libcockatrice/game/zones/pile_zone_logic.h>
|
||||
|
||||
class QWidget;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game_scene.h"
|
||||
#include "../card_menu_action_type.h"
|
||||
#include "../player_graphics_item.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <libcockatrice/game/player/card_menu_action_type.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
|
||||
MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to"))
|
||||
{
|
||||
|
|
@ -30,7 +30,7 @@ MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to"))
|
|||
|
||||
auto invoke = [player](CardMenuActionType type) {
|
||||
return [type, player]() {
|
||||
player->getLogic()->getPlayerActions()->cardMenuAction(player->getGameScene()->selectedCards(), type);
|
||||
player->getLogic()->getPlayerActions()->cardMenuAction(player->getGameScene()->selectedCardStates(), type);
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../../game/abstract_game.h"
|
||||
#include "../../../game/player/player_info.h"
|
||||
#include "../../../game/player/player_logic.h"
|
||||
#include "../../../game/player/player_manager.h"
|
||||
#include "../../../game_graphics/zones/hand_zone.h"
|
||||
#include "../../../game_graphics/zones/pile_zone.h"
|
||||
#include "../../../game_graphics/zones/table_zone.h"
|
||||
|
|
@ -26,6 +22,10 @@
|
|||
|
||||
#include <QMenu>
|
||||
#include <QString>
|
||||
#include <libcockatrice/game/abstract_game.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/player/player_manager.h>
|
||||
|
||||
PlayerMenu::PlayerMenu(PlayerGraphicsItem *_player) : QObject(_player), player(_player)
|
||||
{
|
||||
|
|
@ -87,22 +87,24 @@ void PlayerMenu::setMenusForGraphicItems()
|
|||
}
|
||||
}
|
||||
|
||||
QMenu *PlayerMenu::updateCardMenu(const CardItem *card)
|
||||
QMenu *PlayerMenu::updateCardMenu(CardState *card)
|
||||
{
|
||||
if (!card) {
|
||||
emit cardMenuUpdated(nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto *cardItem = qobject_cast<CardItem *>(card->parent());
|
||||
|
||||
// If is spectator (as spectators don't need card menus), return
|
||||
// only update the menu if the card is actually selected
|
||||
if ((player->getLogic()->getGame()->getPlayerManager()->isSpectator() &&
|
||||
!player->getLogic()->getGame()->getPlayerManager()->isJudge()) ||
|
||||
player->getLogic()->getGame()->getActiveCard() != card) {
|
||||
cardItem == nullptr || player->getGameScene()->getActiveCard() != cardItem) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CardMenu *menu = new CardMenu(player, card, shortcutsActive);
|
||||
CardMenu *menu = new CardMenu(player, cardItem, shortcutsActive);
|
||||
connect(menu, &CardMenu::cardInfoRequested, this, &PlayerMenu::cardInfoRequested);
|
||||
emit cardMenuUpdated(menu);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#include <qtmetamacros.h>
|
||||
#include <utility>
|
||||
|
||||
class CardItem;
|
||||
class CardState;
|
||||
class CardMenu;
|
||||
class PlayerGraphicsItem;
|
||||
class AbstractPlayerComponent;
|
||||
|
|
@ -43,7 +43,7 @@ signals:
|
|||
|
||||
public slots:
|
||||
void setMenusForGraphicItems();
|
||||
QMenu *updateCardMenu(const CardItem *card);
|
||||
QMenu *updateCardMenu(CardState *card);
|
||||
|
||||
private slots:
|
||||
void refreshShortcuts();
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game_scene.h"
|
||||
#include "../player_graphics_item.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
|
||||
PtMenu::PtMenu(PlayerGraphicsItem *player) : QMenu(tr("Power / toughness"))
|
||||
{
|
||||
|
|
@ -15,34 +15,35 @@ PtMenu::PtMenu(PlayerGraphicsItem *player) : QMenu(tr("Power / toughness"))
|
|||
|
||||
aIncP = new QAction(this);
|
||||
connect(aIncP, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actIncP(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actIncP(player->getGameScene()->selectedCardStates()); });
|
||||
aDecP = new QAction(this);
|
||||
connect(aDecP, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actDecP(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actDecP(player->getGameScene()->selectedCardStates()); });
|
||||
aIncT = new QAction(this);
|
||||
connect(aIncT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actIncT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actIncT(player->getGameScene()->selectedCardStates()); });
|
||||
aDecT = new QAction(this);
|
||||
connect(aDecT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actDecT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actDecT(player->getGameScene()->selectedCardStates()); });
|
||||
aIncPT = new QAction(this);
|
||||
connect(aIncPT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actIncPT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actIncPT(player->getGameScene()->selectedCardStates()); });
|
||||
aDecPT = new QAction(this);
|
||||
connect(aDecPT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actDecPT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actDecPT(player->getGameScene()->selectedCardStates()); });
|
||||
aFlowP = new QAction(this);
|
||||
connect(aFlowP, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actFlowP(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actFlowP(player->getGameScene()->selectedCardStates()); });
|
||||
aFlowT = new QAction(this);
|
||||
connect(aFlowT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actFlowT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actFlowT(player->getGameScene()->selectedCardStates()); });
|
||||
aSetPT = new QAction(this);
|
||||
connect(aSetPT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actRequestSetPTDialog(player->getGameScene()->selectedCards()); });
|
||||
connect(aSetPT, &QAction::triggered, playerActions, [player, playerActions] {
|
||||
playerActions->actRequestSetPTDialog(player->getGameScene()->selectedCardStates());
|
||||
});
|
||||
aResetPT = new QAction(this);
|
||||
connect(aResetPT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actResetPT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actResetPT(player->getGameScene()->selectedCardStates()); });
|
||||
|
||||
addAction(aIncP);
|
||||
addAction(aDecP);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
#include "rfg_menu.h"
|
||||
|
||||
#include "../../../game/player/player_info.h"
|
||||
#include "../../../game/zones/pile_zone_logic.h"
|
||||
#include "../../../interface/widgets/menus/tearoff_menu.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../player_graphics_item.h"
|
||||
|
||||
#include <QAction>
|
||||
|
|
@ -12,6 +8,10 @@
|
|||
#include <QMenu>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/zones/pile_zone_logic.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
|
||||
class QWidget;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
#include "say_menu.h"
|
||||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../player_graphics_item.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QKeySequence>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/settings/message_settings.h>
|
||||
|
||||
SayMenu::SayMenu(PlayerGraphicsItem *_player) : player(_player)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../../game/player/player_info.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../player_graphics_item.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QKeySequence>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
|
||||
SideboardMenu::SideboardMenu(PlayerGraphicsItem *player, QMenu *playerMenu) : QMenu(playerMenu)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../../game/player/player_info.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game_scene.h"
|
||||
#include "../player_graphics_item.h"
|
||||
#include "libcockatrice/card/card_info.h"
|
||||
|
|
@ -14,6 +11,9 @@
|
|||
#include <QSet>
|
||||
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
|
||||
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
|
||||
UtilityMenu::UtilityMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) : QMenu(playerMenu), player(_player)
|
||||
{
|
||||
|
|
@ -44,7 +44,7 @@ UtilityMenu::UtilityMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) : QMenu
|
|||
aIncrementAllCardCounters = new QAction(this);
|
||||
connect(aIncrementAllCardCounters, &QAction::triggered, playerActions, [this]() {
|
||||
player->getLogic()->getPlayerActions()->actIncrementAllCardCounters(
|
||||
player->getGameScene()->selectedCards());
|
||||
player->getGameScene()->selectedCardStates());
|
||||
});
|
||||
|
||||
createPredefinedTokenMenu = new QMenu(QString());
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../client/settings/card_counter_settings.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../interface/widgets/utility/get_text_with_max.h"
|
||||
#include "../board/abstract_counter.h"
|
||||
#include "../board/card_item.h"
|
||||
#include "../dialogs/dlg_annotation.h"
|
||||
#include "../dialogs/dlg_create_token.h"
|
||||
#include "../dialogs/dlg_move_top_cards_until.h"
|
||||
#include "../dialogs/dlg_roll_dice.h"
|
||||
|
|
@ -17,6 +16,8 @@
|
|||
#include <QInputDialog>
|
||||
#include <QLineEdit>
|
||||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/utility/string_limits.h>
|
||||
|
||||
PlayerDialogs::PlayerDialogs(PlayerGraphicsItem *_player, PlayerActions *_playerActions)
|
||||
|
|
@ -34,7 +35,7 @@ PlayerDialogs::PlayerDialogs(PlayerGraphicsItem *_player, PlayerActions *_player
|
|||
&PlayerDialogs::onShuffleBottomDialogRequested);
|
||||
|
||||
connect(playerActions, &PlayerActions::requestMulliganDialog, this, &PlayerDialogs::onMulliganDialogRequested);
|
||||
|
||||
connect(playerActions, &PlayerActions::startingHandSizeChosen, this, &PlayerDialogs::onStartingHandSizeChosen);
|
||||
connect(playerActions, &PlayerActions::requestDrawCardsDialog, this, &PlayerDialogs::onDrawCardsDialogRequested);
|
||||
|
||||
connect(playerActions, &PlayerActions::requestMoveTopCardsToDialog, this,
|
||||
|
|
@ -126,6 +127,11 @@ void PlayerDialogs::onMulliganDialogRequested(int startSize, int handSize, int d
|
|||
}
|
||||
}
|
||||
|
||||
void PlayerDialogs::onStartingHandSizeChosen(int number)
|
||||
{
|
||||
SettingsCache::instance().userInterface().setStartingHandSize(number);
|
||||
}
|
||||
|
||||
void PlayerDialogs::onDrawCardsDialogRequested(int defaultNumberTopCards, int deckSize)
|
||||
{
|
||||
bool ok;
|
||||
|
|
@ -196,7 +202,7 @@ void PlayerDialogs::onRollDieDialogRequested()
|
|||
playerActions->actRollDie(dlg.getDieSideCount(), dlg.getDiceToRollCount());
|
||||
}
|
||||
|
||||
void PlayerDialogs::onCreateRelatedFromRelationDialogRequested(const CardItem *sourceCard,
|
||||
void PlayerDialogs::onCreateRelatedFromRelationDialogRequested(const CardState *sourceCard,
|
||||
const CardRelation *cardRelation)
|
||||
{
|
||||
if (sourceCard == nullptr || cardRelation == nullptr) {
|
||||
|
|
@ -251,14 +257,14 @@ void PlayerDialogs::onMoveCardXCardsFromTopDialogRequested(int defaultNumberTopC
|
|||
number -= 1; // indexes start at 0
|
||||
|
||||
if (ok) {
|
||||
playerActions->actMoveCardXCardsFromTop(player->getGameScene()->selectedCards(), number);
|
||||
playerActions->actMoveCardXCardsFromTop(player->getGameScene()->selectedCardStates(), number);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerDialogs::onSetPTDialogRequested(const QString &oldPT)
|
||||
{
|
||||
bool ok;
|
||||
auto cards = player->getGameScene()->selectedCards();
|
||||
auto cards = player->getGameScene()->selectedCardStates();
|
||||
emit requestDialogSemaphore(true);
|
||||
QString pt = getTextWithMax(dialogParent(), tr("Change power/toughness"), tr("Change stats to:"), QLineEdit::Normal,
|
||||
oldPT, &ok);
|
||||
|
|
@ -273,7 +279,7 @@ void PlayerDialogs::onSetPTDialogRequested(const QString &oldPT)
|
|||
|
||||
void PlayerDialogs::onSetAnnotationDialogRequested(const QString &oldAnnotation)
|
||||
{
|
||||
auto cards = player->getGameScene()->selectedCards();
|
||||
auto cards = player->getGameScene()->selectedCardStates();
|
||||
emit requestDialogSemaphore(true);
|
||||
AnnotationDialog *dialog = new AnnotationDialog(dialogParent());
|
||||
dialog->setOptions(QInputDialog::UsePlainTextEditForTextInput);
|
||||
|
|
@ -291,7 +297,7 @@ void PlayerDialogs::onSetAnnotationDialogRequested(const QString &oldAnnotation)
|
|||
|
||||
void PlayerDialogs::onSetCardCounterDialogRequested(int counterId, const QString &oldValueForDlg)
|
||||
{
|
||||
auto cards = player->getGameScene()->selectedCards();
|
||||
auto cards = player->getGameScene()->selectedCardStates();
|
||||
emit requestDialogSemaphore(true);
|
||||
|
||||
auto &cardCounterSettings = SettingsCache::instance().cardCounters();
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
class CardItem;
|
||||
class CardRelation;
|
||||
class CardState;
|
||||
class PlayerActions;
|
||||
class QWidget;
|
||||
struct MoveTopCardsUntilOptions;
|
||||
|
|
@ -52,12 +53,13 @@ public slots:
|
|||
bool faceDown);
|
||||
void onDrawBottomCardsDialogRequested(int defaultNumberBottomCards, int maxCards);
|
||||
void onRollDieDialogRequested();
|
||||
void onCreateRelatedFromRelationDialogRequested(const CardItem *sourceCard, const CardRelation *cardRelation);
|
||||
void onCreateRelatedFromRelationDialogRequested(const CardState *sourceCard, const CardRelation *cardRelation);
|
||||
void onCreateTokenDialogRequested(const QStringList &predefinedTokens);
|
||||
void onMoveCardXCardsFromTopDialogRequested(int defaultNumberTopCardsToPlaceBelow, int deckSize);
|
||||
void onSetPTDialogRequested(const QString &oldPT);
|
||||
void onSetAnnotationDialogRequested(const QString &oldAnnotation);
|
||||
void onSetCardCounterDialogRequested(int counterId, const QString &oldValueForDlg);
|
||||
void onStartingHandSizeChosen(int number);
|
||||
|
||||
private:
|
||||
PlayerGraphicsItem *player;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,6 @@
|
|||
#include "player_graphics_item.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../game/board/counter_state.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../../game/zones/hand_zone_logic.h"
|
||||
#include "../../game/zones/pile_zone_logic.h"
|
||||
#include "../../game/zones/stack_zone_logic.h"
|
||||
#include "../../game/zones/table_zone_logic.h"
|
||||
#include "../../interface/card_picture_loader/card_picture_loader.h"
|
||||
#include "../../interface/widgets/cards/art_crop_attribution.h"
|
||||
#include "../../interface/widgets/cards/card_art_utils.h"
|
||||
|
|
@ -45,6 +36,15 @@
|
|||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/playmat_resolver.h>
|
||||
#include <libcockatrice/game/board/counter_state.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/hand_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/pile_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/stack_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/table_zone_logic.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qnumeric.h>
|
||||
|
|
@ -177,6 +177,22 @@ void PlayerGraphicsItem::initializeZones()
|
|||
|
||||
void PlayerGraphicsItem::onCustomZoneAdded(QString customZoneName)
|
||||
{
|
||||
// Custom zones have no board view, but their cards still need a graphics owner.
|
||||
// Without one, PileZone::onCardAdded never runs and cards moved from the table
|
||||
// or hand into a custom zone stay drawn at their old spot. Give them a hidden
|
||||
// pile zone so added cards are reparented to (0, 0) and hidden.
|
||||
CardZoneLogic *logic = player->getZone(customZoneName);
|
||||
if (auto *pileLogic = qobject_cast<PileZoneLogic *>(logic)) {
|
||||
auto *hiddenZone = new PileZone(pileLogic, this);
|
||||
hiddenZone->setVisible(false);
|
||||
hiddenZone->setPos(0, 0);
|
||||
zoneGraphicsItems.insert(customZoneName, hiddenZone);
|
||||
// Hide any cards that arrived before the graphics item existed.
|
||||
for (CardState *state : pileLogic->getCards()) {
|
||||
hiddenZone->onCardAdded(state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
zoneGraphicsItems.insert(customZoneName, nullptr); // Custom zone view goes here, if we ever implement it.
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
#include "player_target.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../interface/pixel_map_generator.h"
|
||||
#include "../board/abstract_counter.h"
|
||||
#include "../board/abstract_graphics_item.h"
|
||||
|
|
@ -30,6 +28,8 @@
|
|||
#include <QTransform>
|
||||
#include <QtGlobal>
|
||||
#include <QtMath>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qnumeric.h>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ static int sumPowers(const QList<CardItem *> &cards)
|
|||
// calculate total power;
|
||||
int total = 0;
|
||||
for (auto card : cards) {
|
||||
QVariantList parsed = CardItem::parsePT(card->getPT());
|
||||
QVariantList parsed = CardState::parsePT(card->getPT());
|
||||
if (!parsed.isEmpty()) {
|
||||
int power = parsed.first().toInt(); // toInt will default to 0 if it's not an int
|
||||
total += qMax(power, 0);
|
||||
|
|
@ -44,7 +44,7 @@ static int sumToughness(const QList<CardItem *> &cards)
|
|||
{
|
||||
int total = 0;
|
||||
for (auto card : cards) {
|
||||
QVariantList parsed = CardItem::parsePT(card->getPT());
|
||||
QVariantList parsed = CardState::parsePT(card->getPT());
|
||||
if (parsed.size() == 2) {
|
||||
int toughness = parsed.at(1).toInt(); // toInt will default to 0 if it's not an int
|
||||
total += qMax(toughness, 0);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#include "card_zone.h"
|
||||
|
||||
#include "../../game/board/card_list.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../board/abstract_graphics_item.h"
|
||||
#include "../board/card_item.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QMenu>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
class QGraphicsItem;
|
||||
|
|
@ -18,23 +18,60 @@ CardZone::CardZone(CardZoneLogic *_logic, QGraphicsItem *parent)
|
|||
: AbstractGraphicsItem(parent), menu(nullptr), doubleClickAction(0), logic(_logic)
|
||||
{
|
||||
connect(logic, &CardZoneLogic::retranslateUi, this, &CardZone::retranslateUi);
|
||||
// Invalidation is connected before any consumer so a handler that reads cardItems()
|
||||
// during the same signal emission always sees a stale flag and rebuilds.
|
||||
connect(logic, &CardZoneLogic::cardAdded, this, &CardZone::invalidateItems);
|
||||
connect(logic, &CardZoneLogic::cardCountChanged, this, &CardZone::invalidateItems);
|
||||
connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::invalidateItems);
|
||||
connect(logic, &CardZoneLogic::cardAdded, this, &CardZone::onCardAdded);
|
||||
connect(logic, &CardZoneLogic::createCardRequested, this, &CardZone::onCreateCardRequested);
|
||||
connect(logic, &CardZoneLogic::setGraphicsVisibility, this, [this](bool v) { this->setVisible(v); });
|
||||
connect(logic, &CardZoneLogic::updateGraphics, this, [this]() { update(); });
|
||||
connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::reorganizeCards);
|
||||
}
|
||||
|
||||
void CardZone::onCardAdded(CardItem *addedCard)
|
||||
void CardZone::invalidateItems()
|
||||
{
|
||||
addedCard->setParentItem(this);
|
||||
addedCard->setVisible(true);
|
||||
addedCard->update();
|
||||
itemsDirty = true;
|
||||
}
|
||||
|
||||
const QList<CardItem *> &CardZone::cardItems() const
|
||||
{
|
||||
if (itemsDirty) {
|
||||
items.clear();
|
||||
for (auto *cardState : getLogic()->getCards()) {
|
||||
if (auto *cardItem = qobject_cast<CardItem *>(cardState->parent())) {
|
||||
items.append(cardItem);
|
||||
}
|
||||
}
|
||||
itemsDirty = false;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
void CardZone::onCardAdded(CardState *addedCard)
|
||||
{
|
||||
auto *addedItem = addedCard == nullptr ? nullptr : qobject_cast<CardItem *>(addedCard->parent());
|
||||
if (addedItem == nullptr) {
|
||||
return;
|
||||
}
|
||||
addedItem->setParentItem(this);
|
||||
addedItem->setVisible(true);
|
||||
addedItem->update();
|
||||
}
|
||||
|
||||
void CardZone::onCreateCardRequested(const ServerInfo_Card &cardInfo, bool reorganize)
|
||||
{
|
||||
CardRef cardRef = {QString::fromStdString(cardInfo.name()), QString::fromStdString(cardInfo.provider_id())};
|
||||
auto *card = new CardItem(getLogic()->getPlayer(), nullptr, cardRef, cardInfo.id());
|
||||
card->processCardInfo(cardInfo);
|
||||
getLogic()->addCard(card->getState(), reorganize, cardInfo.x(), cardInfo.y());
|
||||
}
|
||||
|
||||
void CardZone::retranslateUi()
|
||||
{
|
||||
for (int i = 0; i < getLogic()->getCards().size(); ++i) {
|
||||
getLogic()->getCards()[i]->retranslateUi();
|
||||
for (auto *card : cardItems()) {
|
||||
card->retranslateUi();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,17 @@
|
|||
|
||||
class CardDragItem;
|
||||
class CardItem;
|
||||
class CardState;
|
||||
class CardZoneLogic;
|
||||
class QAction;
|
||||
class QGraphicsItem;
|
||||
class QMenu;
|
||||
class QPoint;
|
||||
class ServerInfo_Card;
|
||||
template <typename T> class QList;
|
||||
|
||||
class CardItem;
|
||||
|
||||
inline Q_LOGGING_CATEGORY(CardZoneLog, "card_zone");
|
||||
|
||||
/**
|
||||
|
|
@ -55,7 +59,9 @@ public slots:
|
|||
* Virtual so subclasses (e.g. SelectZone) can override parenting behavior — the Qt signal
|
||||
* connection in CardZone's constructor dispatches through the vtable.
|
||||
*/
|
||||
virtual void onCardAdded(CardItem *addedCard);
|
||||
virtual void onCardAdded(CardState *addedCard);
|
||||
/** @brief Builds the card item for @p cardInfo and hands it to this zone's logic. */
|
||||
void onCreateCardRequested(const ServerInfo_Card &cardInfo, bool reorganize);
|
||||
|
||||
public:
|
||||
enum
|
||||
|
|
@ -76,6 +82,15 @@ public:
|
|||
return logic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The CardItems backing getLogic()->getCards(), in zone order.
|
||||
*
|
||||
* Game logic talks about cards as states; this is the graphics side's view of the same list.
|
||||
* It is rebuilt on demand after any content change, so views never reach into the logic's
|
||||
* container themselves and never observe a stale one.
|
||||
*/
|
||||
[[nodiscard]] const QList<CardItem *> &cardItems() const;
|
||||
|
||||
void setMenu(QMenu *_menu, QAction *_doubleClickAction = 0)
|
||||
{
|
||||
menu = _menu;
|
||||
|
|
@ -83,7 +98,12 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
/** @brief Marks the cached card items stale. Connected to every content-changing signal. */
|
||||
void invalidateItems();
|
||||
|
||||
CardZoneLogic *logic;
|
||||
mutable QList<CardItem *> items;
|
||||
mutable bool itemsDirty = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
#include "hand_zone.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../../game/zones/hand_zone_logic.h"
|
||||
#include "../../interface/theme_manager.h"
|
||||
#include "../board/card_drag_item.h"
|
||||
#include "../board/card_item.h"
|
||||
|
|
@ -18,7 +13,11 @@
|
|||
#include <QPointF>
|
||||
#include <QRectF>
|
||||
#include <QString>
|
||||
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/hand_zone_logic.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -48,8 +47,8 @@ void HandZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
|
|||
QPoint point = dropPoint + scenePos().toPoint();
|
||||
int x = -1;
|
||||
if (SettingsCache::instance().userInterface().getHorizontalHand()) {
|
||||
for (x = 0; x < getLogic()->getCards().size(); x++) {
|
||||
if (point.x() < static_cast<CardItem *>(getLogic()->getCards().at(x))->scenePos().x()) {
|
||||
for (x = 0; x < cardItems().size(); x++) {
|
||||
if (point.x() < cardItems().at(x)->scenePos().x()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -58,19 +57,13 @@ void HandZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
|
|||
x = calcDropIndexFromY(dropPoint.y(), !sameZone);
|
||||
}
|
||||
|
||||
Command_MoveCard cmd;
|
||||
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_start_zone(startZone->getName().toStdString());
|
||||
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(getLogic()->getName().toStdString());
|
||||
cmd.set_x(x);
|
||||
cmd.set_y(-1);
|
||||
|
||||
for (int i = 0; i < dragItems.size(); ++i) {
|
||||
cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId());
|
||||
QList<CardMoveRequest> cards;
|
||||
cards.reserve(dragItems.size());
|
||||
for (const CardDragItem *item : dragItems) {
|
||||
cards.append(CardMoveRequest{item->getId()});
|
||||
}
|
||||
|
||||
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
|
||||
getLogic()->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), x, -1, cards);
|
||||
}
|
||||
|
||||
QRectF HandZone::boundingRect() const
|
||||
|
|
@ -90,23 +83,23 @@ void HandZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio
|
|||
|
||||
void HandZone::reorganizeCards()
|
||||
{
|
||||
if (!getLogic()->getCards().isEmpty()) {
|
||||
const int cardCount = getLogic()->getCards().size();
|
||||
if (!cardItems().isEmpty()) {
|
||||
const int cardCount = cardItems().size();
|
||||
if (SettingsCache::instance().userInterface().getHorizontalHand()) {
|
||||
bool leftJustified = SettingsCache::instance().userInterface().getLeftJustified();
|
||||
qreal cardWidth = getLogic()->getCards().at(0)->boundingRect().width();
|
||||
qreal cardWidth = cardItems().at(0)->boundingRect().width();
|
||||
const int xPadding = leftJustified ? cardWidth * 1.4 : 5;
|
||||
qreal totalWidth =
|
||||
leftJustified ? boundingRect().width() - (1 * xPadding) - 5 : boundingRect().width() - 2 * xPadding;
|
||||
|
||||
if (cardCount == 1) {
|
||||
CardItem *c = getLogic()->getCards().at(0);
|
||||
CardItem *c = cardItems().at(0);
|
||||
qreal xPosition = leftJustified ? xPadding : xPadding + (totalWidth - cardWidth) / 2;
|
||||
c->setPos(xPosition, 5);
|
||||
c->setRealZValue(0);
|
||||
} else {
|
||||
for (int i = 0; i < cardCount; i++) {
|
||||
CardItem *c = getLogic()->getCards().at(i);
|
||||
CardItem *c = cardItems().at(i);
|
||||
// If the total width of the cards is smaller than the available width,
|
||||
// the cards do not need to overlap and are displayed in the center of the area.
|
||||
if (cardWidth * cardCount > totalWidth) {
|
||||
|
|
@ -131,7 +124,7 @@ void HandZone::reorganizeCards()
|
|||
|
||||
void HandZone::sortHand(const QList<CardList::SortOption> &options)
|
||||
{
|
||||
if (getLogic()->getCards().isEmpty()) {
|
||||
if (cardItems().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
#ifndef HANDZONE_H
|
||||
#define HANDZONE_H
|
||||
|
||||
#include "../../game/board/card_list.h"
|
||||
#include "select_zone.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class HandZoneLogic;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
#include "pile_zone.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../game/board/card_list.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../../game/zones/pile_zone_logic.h"
|
||||
#include "../board/card_drag_item.h"
|
||||
#include "../board/card_item.h"
|
||||
#include "../card_dimensions.h"
|
||||
|
|
@ -24,7 +18,12 @@
|
|||
#include <QString>
|
||||
#include <QTransform>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/pile_zone_logic.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <qnamespace.h>
|
||||
#include <string>
|
||||
|
|
@ -67,36 +66,35 @@ void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio
|
|||
{
|
||||
painter->drawPath(shape());
|
||||
|
||||
if (!getLogic()->getCards().isEmpty()) {
|
||||
getLogic()->getCards().at(0)->paintPicture(painter, getLogic()->getCards().at(0)->getTranslatedSize(painter),
|
||||
90);
|
||||
if (!cardItems().isEmpty()) {
|
||||
cardItems().at(0)->paintPicture(painter, cardItems().at(0)->getTranslatedSize(painter), 90);
|
||||
}
|
||||
|
||||
painter->translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F);
|
||||
painter->rotate(-90);
|
||||
painter->translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F);
|
||||
paintNumberEllipse(getLogic()->getCards().size(), 28, Qt::white, -1, -1, painter);
|
||||
paintNumberEllipse(cardItems().size(), 28, Qt::white, -1, -1, painter);
|
||||
}
|
||||
|
||||
void PileZone::handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneLogic *startZone, const QPoint &)
|
||||
{
|
||||
Command_MoveCard cmd;
|
||||
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_start_zone(startZone->getName().toStdString());
|
||||
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(getLogic()->getName().toStdString());
|
||||
cmd.set_x(0);
|
||||
cmd.set_y(0);
|
||||
|
||||
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);
|
||||
}
|
||||
QList<CardMoveRequest> cards;
|
||||
cards.reserve(dragItems.size());
|
||||
for (const CardDragItem *item : dragItems) {
|
||||
cards.append(CardMoveRequest{item->getId(), item->isForceFaceDown()});
|
||||
}
|
||||
|
||||
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
|
||||
getLogic()->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), 0, 0, cards);
|
||||
}
|
||||
|
||||
void PileZone::onCardAdded(CardState *addedCard)
|
||||
{
|
||||
CardZone::onCardAdded(addedCard);
|
||||
auto *addedItem = addedCard == nullptr ? nullptr : qobject_cast<CardItem *>(addedCard->parent());
|
||||
if (addedItem) {
|
||||
addedItem->setPos(0, 0);
|
||||
addedItem->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void PileZone::reorganizeCards()
|
||||
|
|
@ -126,15 +124,14 @@ void PileZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|||
return;
|
||||
}
|
||||
|
||||
if (getLogic()->getCards().isEmpty()) {
|
||||
if (cardItems().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
CardItem *card = bottomCard ? cardItems().last() : cardItems().first();
|
||||
const int cardid = getLogic()->contentsKnown() ? card->getId() : (bottomCard ? cardItems().size() - 1 : 0);
|
||||
CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), forceFaceDown);
|
||||
drag->grabMouse();
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
|
|
@ -147,8 +144,8 @@ void PileZone::mouseReleaseEvent(QGraphicsSceneMouseEvent * /*event*/)
|
|||
|
||||
void PileZone::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
|
||||
{
|
||||
if (!getLogic()->getCards().isEmpty()) {
|
||||
getLogic()->getCards()[0]->processHoverEvent();
|
||||
if (!cardItems().isEmpty()) {
|
||||
cardItems()[0]->processHoverEvent();
|
||||
}
|
||||
QGraphicsItem::hoverEnterEvent(event);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ public:
|
|||
[[nodiscard]] QPainterPath shape() const override;
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
|
||||
void reorganizeCards() override;
|
||||
void onCardAdded(CardState *addedCard) override;
|
||||
void
|
||||
handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
#include "select_zone.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../game/board/card_list.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../board/card_item.h"
|
||||
#include "../game_scene.h"
|
||||
|
||||
|
|
@ -17,6 +15,8 @@
|
|||
#include <QTransform>
|
||||
#include <QtGlobal>
|
||||
#include <QtMath>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qnumeric.h>
|
||||
|
|
@ -85,7 +85,7 @@ SelectZone *SelectZone::findOwningSelectZone(const QGraphicsItem *card)
|
|||
|
||||
SelectZone::StackLayoutParams SelectZone::buildStackParams(qreal minOffset) const
|
||||
{
|
||||
const auto &cards = getLogic()->getCards();
|
||||
const auto &cards = cardItems();
|
||||
if (cards.isEmpty()) {
|
||||
return {0, boundingRect().height(), 0.0, 0.0, minOffset};
|
||||
}
|
||||
|
|
@ -97,7 +97,7 @@ SelectZone::StackLayoutParams SelectZone::buildStackParams(qreal minOffset) cons
|
|||
|
||||
int SelectZone::calcDropIndexFromY(qreal dropY, bool allowCountExpand, qreal minOffset) const
|
||||
{
|
||||
const auto &cards = getLogic()->getCards();
|
||||
const auto &cards = cardItems();
|
||||
if (cards.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -115,7 +115,7 @@ void SelectZone::restoreStaleEscapedCards()
|
|||
if (!cardClipContainer) {
|
||||
return;
|
||||
}
|
||||
for (auto *card : getLogic()->getCards()) {
|
||||
for (auto *card : cardItems()) {
|
||||
// A card parented to the zone (instead of the clip container) should
|
||||
// only occur while it is actively hovered. If hover cleanup was
|
||||
// missed, reparent it back so clipping resumes.
|
||||
|
|
@ -127,7 +127,7 @@ void SelectZone::restoreStaleEscapedCards()
|
|||
|
||||
void SelectZone::layoutCardsVertically(const StackLayoutParams ¶ms)
|
||||
{
|
||||
const auto &cards = getLogic()->getCards();
|
||||
const auto &cards = cardItems();
|
||||
if (cards.isEmpty() || params.cardCount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -162,9 +162,9 @@ SelectZone::~SelectZone()
|
|||
if (cardClipContainer) {
|
||||
// Reparent any hover-escaped cards back to the clip container so Qt's
|
||||
// parent-child tree is consistent for destruction. setParentItem() does
|
||||
// not invalidate getLogic()->getCards() (it modifies the graphics tree,
|
||||
// not the zone's logical card list).
|
||||
for (auto *card : getLogic()->getCards()) {
|
||||
// not invalidate cardItems() — it only moves the item in the graphics
|
||||
// tree, it never changes the zone's contents.
|
||||
for (auto *card : cardItems()) {
|
||||
if (card && card->parentItem() == this) {
|
||||
card->setParentItem(cardClipContainer);
|
||||
}
|
||||
|
|
@ -172,12 +172,13 @@ SelectZone::~SelectZone()
|
|||
}
|
||||
}
|
||||
|
||||
void SelectZone::onCardAdded(CardItem *addedCard)
|
||||
void SelectZone::onCardAdded(CardState *addedCard)
|
||||
{
|
||||
if (cardClipContainer && addedCard) {
|
||||
addedCard->setParentItem(cardClipContainer);
|
||||
addedCard->setVisible(true);
|
||||
addedCard->update();
|
||||
auto *addedItem = addedCard == nullptr ? nullptr : qobject_cast<CardItem *>(addedCard->parent());
|
||||
if (cardClipContainer && addedItem) {
|
||||
addedItem->setParentItem(cardClipContainer);
|
||||
addedItem->setVisible(true);
|
||||
addedItem->update();
|
||||
} else {
|
||||
CardZone::onCardAdded(addedCard);
|
||||
}
|
||||
|
|
@ -247,7 +248,7 @@ void SelectZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|||
}
|
||||
|
||||
QRectF selectionRect = QRectF(selectionOrigin, pos).normalized();
|
||||
for (auto card : getLogic()->getCards()) {
|
||||
for (auto card : cardItems()) {
|
||||
if (card->getAttachedTo() && card->getAttachedTo()->getZone() != getLogic()) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public:
|
|||
|
||||
SelectZone(CardZoneLogic *logic, QGraphicsItem *parent = nullptr);
|
||||
~SelectZone() override;
|
||||
void onCardAdded(CardItem *addedCard) override;
|
||||
void onCardAdded(CardState *addedCard) override;
|
||||
|
||||
/**
|
||||
* @brief Temporarily reparents a card from the clip container to this zone so hover scaling is visible beyond clip
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
#include "stack_zone.h"
|
||||
|
||||
#include "../../game/board/card_list.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../../game/zones/stack_zone_logic.h"
|
||||
#include "../../interface/theme_manager.h"
|
||||
#include "../board/card_drag_item.h"
|
||||
#include "../board/card_item.h"
|
||||
|
|
@ -19,7 +13,12 @@
|
|||
#include <QPoint>
|
||||
#include <QRectF>
|
||||
#include <QString>
|
||||
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/stack_zone_logic.h>
|
||||
#include <qnumeric.h>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -73,31 +72,21 @@ void StackZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
|
|||
int index = calcDropIndexFromY(dropPoint.y(), !sameZone, MIN_CARD_VISIBLE);
|
||||
if (sameZone) {
|
||||
// Same-zone no-op: don't move a card onto itself
|
||||
const auto &cards = getLogic()->getCards();
|
||||
const auto &cards = cardItems();
|
||||
if (!cards.isEmpty() && cards.at(index)->getId() == dragItems.at(0)->getId()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Command_MoveCard cmd;
|
||||
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_start_zone(startZone->getName().toStdString());
|
||||
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(getLogic()->getName().toStdString());
|
||||
cmd.set_x(index);
|
||||
cmd.set_y(0);
|
||||
|
||||
QList<CardMoveRequest> cards;
|
||||
cards.reserve(dragItems.size());
|
||||
for (const CardDragItem *item : dragItems) {
|
||||
if (item) {
|
||||
auto *cardToMove = cmd.mutable_cards_to_move()->add_card();
|
||||
cardToMove->set_card_id(item->getId());
|
||||
if (item->isForceFaceDown()) {
|
||||
cardToMove->set_face_down(true);
|
||||
}
|
||||
cards.append(CardMoveRequest{item->getId(), item->isForceFaceDown()});
|
||||
}
|
||||
}
|
||||
|
||||
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
|
||||
getLogic()->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), index, 0, cards);
|
||||
}
|
||||
|
||||
void StackZone::setHeight(qreal newHeight)
|
||||
|
|
@ -113,7 +102,7 @@ void StackZone::setHeight(qreal newHeight)
|
|||
|
||||
void StackZone::reorganizeCards()
|
||||
{
|
||||
if (!getLogic()->getCards().isEmpty()) {
|
||||
if (!cardItems().isEmpty()) {
|
||||
const auto params = buildStackParams(MIN_CARD_VISIBLE);
|
||||
layoutCardsVertically(params);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
#include "table_zone.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../game/board/card_list.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../../game/zones/table_zone_logic.h"
|
||||
#include "../../interface/theme_manager.h"
|
||||
#include "../board/abstract_card_item.h"
|
||||
#include "../board/card_drag_item.h"
|
||||
|
|
@ -32,8 +26,12 @@
|
|||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#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/game/board/card_list.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/table_zone_logic.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
#include <string>
|
||||
|
|
@ -62,7 +60,8 @@ TableZone::TableZone(TableZoneLogic *_logic, bool _mirrored, QGraphicsItem *pare
|
|||
|
||||
updateBg();
|
||||
|
||||
height = MARGIN_TOP + MARGIN_BOTTOM + TABLEROWS * CardDimensions::HEIGHT + (TABLEROWS - 1) * PADDING_Y;
|
||||
height = MARGIN_TOP + MARGIN_BOTTOM + TableZoneLogic::ROW_COUNT * CardDimensions::HEIGHT +
|
||||
(TableZoneLogic::ROW_COUNT - 1) * PADDING_Y;
|
||||
width = MIN_WIDTH;
|
||||
currentMinimumWidth = width;
|
||||
|
||||
|
|
@ -208,29 +207,20 @@ void TableZone::handleDropEventByGrid(const QList<CardDragItem *> &dragItems,
|
|||
CardZoneLogic *startZone,
|
||||
const QPoint &gridPoint)
|
||||
{
|
||||
Command_MoveCard cmd;
|
||||
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_start_zone(startZone->getName().toStdString());
|
||||
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(getLogic()->getName().toStdString());
|
||||
cmd.set_x(gridPoint.x());
|
||||
cmd.set_y(gridPoint.y());
|
||||
|
||||
for (const auto &item : dragItems) {
|
||||
CardToMove *ctm = cmd.mutable_cards_to_move()->add_card();
|
||||
ctm->set_card_id(item->getId());
|
||||
if (item->isForceFaceDown()) {
|
||||
ctm->set_face_down(true);
|
||||
}
|
||||
QList<CardMoveRequest> cards;
|
||||
cards.reserve(dragItems.size());
|
||||
for (const CardDragItem *item : dragItems) {
|
||||
CardMoveRequest card{item->getId(), item->isForceFaceDown()};
|
||||
if (startZone->getName() != getLogic()->getName() && !item->isForceFaceDown()) {
|
||||
const auto &card = item->getItem()->getCard();
|
||||
if (card) {
|
||||
ctm->set_pt(card.getInfo().getPowTough().toStdString());
|
||||
const auto &cardInfo = item->getItem()->getCard();
|
||||
if (cardInfo) {
|
||||
card.pt = cardInfo.getInfo().getPowTough();
|
||||
}
|
||||
}
|
||||
cards.append(card);
|
||||
}
|
||||
|
||||
startZone->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
|
||||
startZone->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), gridPoint.x(), gridPoint.y(), cards);
|
||||
}
|
||||
|
||||
void TableZone::reorganizeCards()
|
||||
|
|
@ -238,8 +228,8 @@ void TableZone::reorganizeCards()
|
|||
// Calculate card stack widths so mapping functions work properly
|
||||
computeCardStackWidths();
|
||||
|
||||
for (int i = 0; i < getLogic()->getCards().size(); ++i) {
|
||||
QPoint gridPoint = getLogic()->getCards()[i]->getGridPos();
|
||||
for (int i = 0; i < cardItems().size(); ++i) {
|
||||
QPoint gridPoint = cardItems()[i]->getGridPos();
|
||||
if (gridPoint.x() == -1) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -248,21 +238,24 @@ void TableZone::reorganizeCards()
|
|||
qreal x = mapPoint.x();
|
||||
qreal y = mapPoint.y();
|
||||
|
||||
int numberAttachedCards = getLogic()->getCards()[i]->getAttachedCards().size();
|
||||
int numberAttachedCards = cardItems()[i]->getAttachedCards().size();
|
||||
qreal actualX = x + numberAttachedCards * STACKED_CARD_OFFSET_X;
|
||||
qreal actualY = y;
|
||||
if (numberAttachedCards) {
|
||||
actualY += 15;
|
||||
}
|
||||
|
||||
getLogic()->getCards()[i]->setPos(actualX, actualY);
|
||||
getLogic()->getCards()[i]->setRealZValue(ZValues::tableCardZValue(actualX, actualY));
|
||||
cardItems()[i]->setPos(actualX, actualY);
|
||||
cardItems()[i]->setRealZValue(ZValues::tableCardZValue(actualX, actualY));
|
||||
|
||||
QListIterator<CardItem *> attachedCardIterator(getLogic()->getCards()[i]->getAttachedCards());
|
||||
QListIterator<CardState *> attachedCardIterator(cardItems()[i]->getAttachedCards());
|
||||
int j = 0;
|
||||
while (attachedCardIterator.hasNext()) {
|
||||
++j;
|
||||
CardItem *attachedCard = attachedCardIterator.next();
|
||||
CardItem *attachedCard = qobject_cast<CardItem *>(attachedCardIterator.next()->parent());
|
||||
if (attachedCard == nullptr) {
|
||||
continue;
|
||||
}
|
||||
qreal childX = actualX - j * STACKED_CARD_OFFSET_X;
|
||||
qreal childY = y + 5;
|
||||
attachedCard->setPos(childX, childY);
|
||||
|
|
@ -276,35 +269,18 @@ void TableZone::reorganizeCards()
|
|||
|
||||
void TableZone::toggleTapped()
|
||||
{
|
||||
QList<QGraphicsItem *> selectedItemsRaw = scene()->selectedItems();
|
||||
QList<QGraphicsItem *> selectedItems;
|
||||
|
||||
auto isCardOnTable = [](const QGraphicsItem *item) {
|
||||
if (auto card = qgraphicsitem_cast<const CardItem *>(item)) {
|
||||
return card->getZone()->getName() == ZoneNames::TABLE;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
std::copy_if(selectedItemsRaw.begin(), selectedItemsRaw.end(), std::back_inserter(selectedItems), isCardOnTable);
|
||||
|
||||
bool tapAll = std::any_of(selectedItems.begin(), selectedItems.end(), [](const QGraphicsItem *item) {
|
||||
return !qgraphicsitem_cast<const CardItem *>(item)->getTapped();
|
||||
});
|
||||
QList<const ::google::protobuf::Message *> cmdList;
|
||||
for (const auto &selectedItem : selectedItems) {
|
||||
CardItem *temp = qgraphicsitem_cast<CardItem *>(selectedItem);
|
||||
if (temp->getTapped() != tapAll) {
|
||||
Command_SetCardAttr *cmd = new Command_SetCardAttr;
|
||||
cmd->set_zone(getLogic()->getName().toStdString());
|
||||
cmd->set_card_id(temp->getId());
|
||||
cmd->set_attribute(AttrTapped);
|
||||
cmd->set_attr_value(tapAll ? "1" : "0");
|
||||
cmdList.append(cmd);
|
||||
QList<CardState *> selectedCards;
|
||||
for (auto *item : scene()->selectedItems()) {
|
||||
auto *card = qgraphicsitem_cast<CardItem *>(item);
|
||||
if (card && card->getZone()->getName() == ZoneNames::TABLE) {
|
||||
selectedCards.append(card->getState());
|
||||
}
|
||||
}
|
||||
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(
|
||||
getLogic()->getPlayer()->getPlayerActions()->prepareGameCommand(cmdList));
|
||||
|
||||
const bool tapAll = std::any_of(selectedCards.begin(), selectedCards.end(),
|
||||
[](const CardState *card) { return !card->getTapped(); });
|
||||
|
||||
getLogic()->getPlayer()->getPlayerActions()->actToggleTapped(getLogic()->getName(), selectedCards, tapAll);
|
||||
}
|
||||
|
||||
void TableZone::resizeToContents()
|
||||
|
|
@ -312,9 +288,9 @@ void TableZone::resizeToContents()
|
|||
int xMax = 0;
|
||||
|
||||
// Find rightmost card position, which includes the left margin amount.
|
||||
for (int i = 0; i < getLogic()->getCards().size(); ++i) {
|
||||
if (getLogic()->getCards()[i]->pos().x() > xMax) {
|
||||
xMax = (int)getLogic()->getCards()[i]->pos().x();
|
||||
for (int i = 0; i < cardItems().size(); ++i) {
|
||||
if (cardItems()[i]->pos().x() > xMax) {
|
||||
xMax = (int)cardItems()[i]->pos().x();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -335,9 +311,9 @@ void TableZone::resizeToContents()
|
|||
|
||||
CardItem *TableZone::getCardFromGrid(const QPoint &gridPoint) const
|
||||
{
|
||||
for (int i = 0; i < getLogic()->getCards().size(); i++) {
|
||||
if (getLogic()->getCards().at(i)->getGridPoint() == gridPoint) {
|
||||
return getLogic()->getCards().at(i);
|
||||
for (int i = 0; i < cardItems().size(); i++) {
|
||||
if (cardItems().at(i)->getGridPoint() == gridPoint) {
|
||||
return cardItems().at(i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -354,8 +330,8 @@ void TableZone::computeCardStackWidths()
|
|||
// Each card stack is three grid points worth of card locations.
|
||||
// First pass: compute the number of cards at each card stack.
|
||||
QMap<int, int> cardStackCount;
|
||||
for (int i = 0; i < getLogic()->getCards().size(); ++i) {
|
||||
const QPoint &gridPoint = getLogic()->getCards()[i]->getGridPos();
|
||||
for (int i = 0; i < cardItems().size(); ++i) {
|
||||
const QPoint &gridPoint = cardItems()[i]->getGridPos();
|
||||
if (gridPoint.x() == -1) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -366,8 +342,8 @@ void TableZone::computeCardStackWidths()
|
|||
|
||||
// Second pass: compute the width at each card stack.
|
||||
cardStackWidth.clear();
|
||||
for (int i = 0; i < getLogic()->getCards().size(); ++i) {
|
||||
const QPoint &gridPoint = getLogic()->getCards()[i]->getGridPos();
|
||||
for (int i = 0; i < cardItems().size(); ++i) {
|
||||
const QPoint &gridPoint = cardItems()[i]->getGridPos();
|
||||
if (gridPoint.x() == -1) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -375,8 +351,8 @@ 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, CardDimensions::WIDTH + getLogic()->getCards()[i]->getAttachedCards().size() *
|
||||
STACKED_CARD_OFFSET_X);
|
||||
cardStackWidth.insert(key, CardDimensions::WIDTH +
|
||||
cardItems()[i]->getAttachedCards().size() * STACKED_CARD_OFFSET_X);
|
||||
} else {
|
||||
cardStackWidth.insert(key, CardDimensions::WIDTH + (stackCount - 1) * STACKED_CARD_OFFSET_X);
|
||||
}
|
||||
|
|
@ -397,7 +373,7 @@ QPointF TableZone::mapFromGrid(QPoint gridPoint) const
|
|||
}
|
||||
|
||||
if (isInverted()) {
|
||||
gridPoint.setY(TABLEROWS - 1 - gridPoint.y());
|
||||
gridPoint.setY(TableZoneLogic::ROW_COUNT - 1 - gridPoint.y());
|
||||
}
|
||||
|
||||
// Start with margin plus stacked card offset
|
||||
|
|
@ -423,10 +399,10 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const
|
|||
const int gridPointHeight = CardDimensions::HEIGHT + PADDING_Y;
|
||||
int gridPointY = (y + PADDING_Y / 2) / gridPointHeight;
|
||||
|
||||
gridPointY = clampValidTableRow(gridPointY);
|
||||
gridPointY = TableZoneLogic::clampValidTableRow(gridPointY);
|
||||
|
||||
if (isInverted()) {
|
||||
gridPointY = TABLEROWS - 1 - gridPointY;
|
||||
gridPointY = TableZoneLogic::ROW_COUNT - 1 - gridPointY;
|
||||
}
|
||||
|
||||
// Calculating the x-coordinate of the grid space requires adding up the
|
||||
|
|
@ -471,22 +447,3 @@ QPointF TableZone::closestGridPoint(const QPointF &point)
|
|||
}
|
||||
return mapFromGrid(gridPoint);
|
||||
}
|
||||
|
||||
int TableZone::clampValidTableRow(const int row)
|
||||
{
|
||||
if (row < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (row >= TABLEROWS) {
|
||||
return TABLEROWS - 1;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
int TableZone::tableRowToGridY(int tableRow)
|
||||
{
|
||||
if (tableRow > 2) {
|
||||
tableRow = 1;
|
||||
}
|
||||
return clampValidTableRow(2 - tableRow);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <QPoint>
|
||||
#include <QPointF>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/game/zones/table_zone_logic.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class CardDragItem;
|
||||
|
|
@ -25,7 +26,6 @@ class CardZoneLogic;
|
|||
class QColor;
|
||||
class QGraphicsItem;
|
||||
class QPainter;
|
||||
class TableZoneLogic;
|
||||
template <typename T> class QList;
|
||||
|
||||
/**
|
||||
|
|
@ -42,8 +42,6 @@ signals:
|
|||
void sizeChanged();
|
||||
|
||||
private:
|
||||
static const int TABLEROWS = 3;
|
||||
|
||||
/*
|
||||
Margins between table edges and cards, paddings between cards
|
||||
*/
|
||||
|
|
@ -181,15 +179,6 @@ public:
|
|||
|
||||
QPointF closestGridPoint(const QPointF &point) override;
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
#include "view_zone.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_info.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../../game/zones/view_zone_logic.h"
|
||||
#include "../board/card_drag_item.h"
|
||||
#include "../board/card_item.h"
|
||||
#include "../card_dimensions.h"
|
||||
|
|
@ -26,8 +21,11 @@
|
|||
#include <QtMath>
|
||||
#include <functional>
|
||||
#include <libcockatrice/card/card_localization.h>
|
||||
#include <libcockatrice/protocol/pb/command_dump_zone.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/view_zone_logic.h>
|
||||
#include <libcockatrice/protocol/pb/response_dump_zone.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
|
|
@ -42,21 +40,23 @@ class QGraphicsItem;
|
|||
ZoneViewZone::ZoneViewZone(ZoneViewZoneLogic *_logic, QGraphicsItem *parent)
|
||||
: SelectZone(_logic, parent), bRect(QRectF()), minRows(0), groupBy(CardList::NoSort), sortBy(CardList::NoSort)
|
||||
{
|
||||
if (!(qobject_cast<ZoneViewZoneLogic *>(getLogic())->getRevealZone() &&
|
||||
!qobject_cast<ZoneViewZoneLogic *>(getLogic())->getWriteableRevealZone())) {
|
||||
qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->getViews().append(this);
|
||||
auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(getLogic());
|
||||
if (!(viewLogic->getRevealZone() && !viewLogic->getWriteableRevealZone())) {
|
||||
viewLogic->getOriginalZone()->getViews().append(viewLogic);
|
||||
}
|
||||
connect(_logic, &ZoneViewZoneLogic::closeView, this, &ZoneViewZone::close);
|
||||
}
|
||||
|
||||
void ZoneViewZone::addToViews()
|
||||
{
|
||||
qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->getViews().append(this);
|
||||
auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(getLogic());
|
||||
viewLogic->getOriginalZone()->getViews().append(viewLogic);
|
||||
}
|
||||
|
||||
void ZoneViewZone::removeFromViews()
|
||||
{
|
||||
qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->getViews().removeOne(this);
|
||||
auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(getLogic());
|
||||
viewLogic->getOriginalZone()->getViews().removeOne(viewLogic);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -66,9 +66,9 @@ void ZoneViewZone::removeFromViews()
|
|||
void ZoneViewZone::close()
|
||||
{
|
||||
emit closed();
|
||||
if (!(qobject_cast<ZoneViewZoneLogic *>(getLogic())->getRevealZone() &&
|
||||
!qobject_cast<ZoneViewZoneLogic *>(getLogic())->getWriteableRevealZone())) {
|
||||
qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->getViews().removeOne(this);
|
||||
auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(getLogic());
|
||||
if (!(viewLogic->getRevealZone() && !viewLogic->getWriteableRevealZone())) {
|
||||
viewLogic->getOriginalZone()->getViews().removeOne(viewLogic);
|
||||
}
|
||||
deleteLater();
|
||||
}
|
||||
|
|
@ -95,28 +95,27 @@ void ZoneViewZone::initializeCards(const QList<const ServerInfo_Card *> &cardLis
|
|||
auto copy = new CardItem(getLogic()->getPlayer(), this, cardRef, card->id());
|
||||
copy->setFaceDown(card->face_down());
|
||||
|
||||
getLogic()->addCard(copy, false, i);
|
||||
getLogic()->addCard(copy->getState(), false, i);
|
||||
}
|
||||
reorganizeCards();
|
||||
} else if (!qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->contentsKnown()) {
|
||||
Command_DumpZone cmd;
|
||||
cmd.set_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_zone_name(getLogic()->getName().toStdString());
|
||||
cmd.set_number_cards(numberCards);
|
||||
cmd.set_is_reversed(qobject_cast<ZoneViewZoneLogic *>(getLogic())->getIsReversed());
|
||||
|
||||
PendingCommand *pend = getLogic()->getPlayer()->getPlayerActions()->prepareGameCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &ZoneViewZone::zoneDumpReceived);
|
||||
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(pend);
|
||||
auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(getLogic());
|
||||
PendingCommand *pend = viewLogic->prepareZoneDump();
|
||||
if (pend != nullptr) {
|
||||
// A local game answers synchronously, so the handler has to be connected before the
|
||||
// command is sent or the response would be delivered to nobody.
|
||||
connect(pend, &PendingCommand::finished, this, &ZoneViewZone::zoneDumpReceived);
|
||||
viewLogic->sendZoneDump(pend);
|
||||
}
|
||||
} else {
|
||||
const CardList &c = qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->getCards();
|
||||
int number = numberCards == -1 ? c.size() : (numberCards < c.size() ? numberCards : c.size());
|
||||
for (int i = 0; i < number; i++) {
|
||||
CardItem *card = c.at(i);
|
||||
CardState *card = c.at(i);
|
||||
auto copy = new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId());
|
||||
copy->setFaceDown(card->getFaceDown());
|
||||
|
||||
getLogic()->addCard(copy, false, i);
|
||||
getLogic()->addCard(copy->getState(), false, i);
|
||||
}
|
||||
reorganizeCards();
|
||||
}
|
||||
|
|
@ -132,7 +131,7 @@ void ZoneViewZone::zoneDumpReceived(const Response &r)
|
|||
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);
|
||||
getLogic()->rawInsertCard(card->getState(), i);
|
||||
}
|
||||
|
||||
qobject_cast<ZoneViewZoneLogic *>(getLogic())->updateCardIds(ZoneViewZoneLogic::INITIALIZE);
|
||||
|
|
@ -146,11 +145,11 @@ void ZoneViewZone::zoneDumpReceived(const Response &r)
|
|||
void ZoneViewZone::reorganizeCards()
|
||||
{
|
||||
// filter cards
|
||||
CardList cardsToDisplay = CardList(getLogic()->getCards().getContentsKnown());
|
||||
for (auto card : getLogic()->getCards()) {
|
||||
CardList cardsToDisplay = CardList(getLogic()->contentsKnown());
|
||||
for (auto card : cardItems()) {
|
||||
if (filterString.check(card->getCard().getCardPtr())) {
|
||||
card->show();
|
||||
cardsToDisplay.append(card);
|
||||
cardsToDisplay.append(card->getState());
|
||||
} else {
|
||||
card->hide();
|
||||
}
|
||||
|
|
@ -218,7 +217,8 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca
|
|||
const auto extractor = CardList::getExtractorFor(pileOption);
|
||||
|
||||
for (int i = 0; i < cardCount; i++) {
|
||||
CardItem *c = cards.at(i);
|
||||
CardState *c = cards.at(i);
|
||||
CardItem *cItem = qobject_cast<CardItem *>(c->parent());
|
||||
QString columnProp = extractor(c);
|
||||
|
||||
if (i) { // if not the first card
|
||||
|
|
@ -233,8 +233,8 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca
|
|||
lastColumnProp = columnProp;
|
||||
qreal x = col * CardDimensions::WIDTH_F;
|
||||
qreal y = row * CardDimensions::HEIGHT_F / 3;
|
||||
c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y);
|
||||
c->setRealZValue(i);
|
||||
cItem->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y);
|
||||
cItem->setRealZValue(i);
|
||||
longestRow = qMax(row, longestRow);
|
||||
}
|
||||
|
||||
|
|
@ -259,11 +259,11 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca
|
|||
qCDebug(ViewZoneLog) << "reorganizeCards: rows=" << rows << "cols=" << cols;
|
||||
|
||||
for (int i = 0; i < cardCount; i++) {
|
||||
CardItem *c = cards.at(i);
|
||||
auto *cItem = qobject_cast<CardItem *>(cards.at(i)->parent());
|
||||
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);
|
||||
cItem->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y);
|
||||
cItem->setRealZValue(i);
|
||||
}
|
||||
|
||||
return GridSize{rows, qMax(cols, 1)};
|
||||
|
|
@ -301,24 +301,13 @@ void ZoneViewZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
|
|||
CardZoneLogic *startZone,
|
||||
const QPoint & /*dropPoint*/)
|
||||
{
|
||||
Command_MoveCard cmd;
|
||||
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_start_zone(startZone->getName().toStdString());
|
||||
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(getLogic()->getName().toStdString());
|
||||
cmd.set_x(0);
|
||||
cmd.set_y(0);
|
||||
cmd.set_is_reversed(qobject_cast<ZoneViewZoneLogic *>(getLogic())->getIsReversed());
|
||||
|
||||
QList<CardMoveRequest> cards;
|
||||
cards.reserve(dragItems.size());
|
||||
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);
|
||||
}
|
||||
cards.append(CardMoveRequest{dragItems[i]->getId(), dragItems[i]->isForceFaceDown()});
|
||||
}
|
||||
|
||||
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
|
||||
getLogic()->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), 0, 0, cards);
|
||||
}
|
||||
|
||||
void ZoneViewZone::setGeometry(const QRectF &rect)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#ifndef ZONEVIEWERZONE_H
|
||||
#define ZONEVIEWERZONE_H
|
||||
|
||||
#include "../../game/board/card_list.h"
|
||||
#include "select_zone.h"
|
||||
|
||||
#include <QGraphicsLayoutItem>
|
||||
|
|
@ -17,6 +16,7 @@
|
|||
#include <QSizeF>
|
||||
#include <QString>
|
||||
#include <libcockatrice/filters/filter_string.h>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,12 @@
|
|||
#include "view_zone_widget.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../client/translation.h"
|
||||
#include "../../filters/syntax_help.h"
|
||||
#include "../../game/board/card_list.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../../game/zones/view_zone_logic.h"
|
||||
#include "../../interface/pixel_map_generator.h"
|
||||
#include "../card_dimensions.h"
|
||||
#include "../z_values.h"
|
||||
#include "libcockatrice/utility/macros.h"
|
||||
#include "libcockatrice/utility/qt_utils.h"
|
||||
#include "view_zone.h"
|
||||
|
||||
#include <QAction>
|
||||
|
|
@ -44,7 +39,12 @@
|
|||
#include <QVariant>
|
||||
#include <QWidget>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/protocol/pb/command_shuffle.pb.h>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/translation.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <libcockatrice/game/zones/view_zone_logic.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
#include <qnamespace.h>
|
||||
|
|
@ -177,9 +177,10 @@ ZoneViewWidget::ZoneViewWidget(PlayerLogic *_player,
|
|||
|
||||
vbox->addItem(zoneHBox);
|
||||
|
||||
zone = new ZoneViewZone(new ZoneViewZoneLogic(player, _origZone, numberCards, _revealZone, _writeableRevealZone,
|
||||
_isReversed, zoneContainer),
|
||||
zoneContainer);
|
||||
zone =
|
||||
new ZoneViewZone(new ZoneViewZoneLogic(player, _origZone, numberCards, _revealZone, _writeableRevealZone,
|
||||
_isReversed, &SettingsCache::instance().userInterface(), zoneContainer),
|
||||
zoneContainer);
|
||||
connect(zone, &ZoneViewZone::wheelEventReceived, scrollBarProxy, &ScrollableGraphicsProxyWidget::recieveWheelEvent);
|
||||
|
||||
retranslateUi();
|
||||
|
|
@ -356,12 +357,8 @@ QRectF ZoneViewWidget::closeButtonRect(QWidget *styleWidget) const
|
|||
|
||||
QGraphicsView *ZoneViewWidget::findDragView(QWidget *eventWidget) const
|
||||
{
|
||||
QWidget *current = eventWidget;
|
||||
while (current) {
|
||||
if (auto *view = qobject_cast<QGraphicsView *>(current)) {
|
||||
return view;
|
||||
}
|
||||
current = current->parentWidget();
|
||||
if (auto *view = QtUtils::findParentOfType<QGraphicsView>(eventWidget, true)) {
|
||||
return view;
|
||||
}
|
||||
|
||||
if (scene() && !scene()->views().isEmpty()) {
|
||||
|
|
@ -573,7 +570,7 @@ void ZoneViewWidget::closeEvent(QCloseEvent *event)
|
|||
// manually call zone->close in order to remove it from the origZones views
|
||||
zone->close();
|
||||
if (shuffleCheckBox.isChecked()) {
|
||||
player->getPlayerActions()->sendGameCommand(Command_Shuffle());
|
||||
player->getPlayerActions()->actShuffle();
|
||||
}
|
||||
zoneDeleted();
|
||||
event->accept();
|
||||
|
|
|
|||
81
cockatrice/src/installer_close_server.cpp
Normal file
81
cockatrice/src/installer_close_server.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#include "installer_close_server.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QTimer>
|
||||
#include <libcockatrice/utility/installer_close_protocol.h>
|
||||
#include <libcockatrice/utility/local_server_name.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
/// The one line the probe writes, from the header both ends include. A QByteArray view of
|
||||
/// the shared literal, so the client cannot drift from the probe's copy.
|
||||
const QByteArray CLOSE_COMMAND = QByteArray::fromRawData(INSTALLER_CLOSE_COMMAND, sizeof(INSTALLER_CLOSE_COMMAND) - 1);
|
||||
|
||||
/// How long a connection that never sends the command may stay open. The probe writes and
|
||||
/// closes at once, so this only reaps a stray client; it must not run while a close request
|
||||
/// is being handled, which is why it is a timer on the socket and not a disconnected slot.
|
||||
constexpr int STRAY_CONNECTION_MILLISECONDS = 5000;
|
||||
} // namespace
|
||||
|
||||
InstallerCloseServer::InstallerCloseServer(QObject *parent) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString InstallerCloseServer::socketNameForUser(qint64 processId, const QString &userName)
|
||||
{
|
||||
return QStringLiteral("CockatriceInstallerClose-%1-%2").arg(processId).arg(userName);
|
||||
}
|
||||
|
||||
QString InstallerCloseServer::socketName(qint64 processId)
|
||||
{
|
||||
// The same scoping helper the single-instance socket uses, so the probe only has to
|
||||
// reproduce one rule (USER, then USERNAME, then the home directory name).
|
||||
return scopedLocalServerName(QStringLiteral("CockatriceInstallerClose-%1").arg(processId));
|
||||
}
|
||||
|
||||
bool InstallerCloseServer::listen()
|
||||
{
|
||||
if (server != nullptr) {
|
||||
return server->isListening();
|
||||
}
|
||||
name = socketName(QCoreApplication::applicationPid());
|
||||
server = new QLocalServer(this);
|
||||
// A crashed instance can leave a Unix socket file behind; on Windows this is a no-op.
|
||||
QLocalServer::removeServer(name);
|
||||
if (!server->listen(name)) {
|
||||
delete server;
|
||||
server = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
connect(server, &QLocalServer::newConnection, this, [this]() {
|
||||
while (QLocalSocket *socket = server->nextPendingConnection()) {
|
||||
// No disconnected -> deleteLater() slot: the close request can spin a nested
|
||||
// event loop, the probe disconnects while it runs, and a deferred delete would
|
||||
// then free the socket from inside its own readyRead emission. The reaper covers
|
||||
// a client that never sends the line; the handler below deletes after the emit.
|
||||
auto *reaper = new QTimer(socket);
|
||||
reaper->setSingleShot(true);
|
||||
connect(reaper, &QTimer::timeout, socket, &QObject::deleteLater);
|
||||
reaper->start(STRAY_CONNECTION_MILLISECONDS);
|
||||
connect(socket, &QLocalSocket::readyRead, this, [this, socket, reaper]() {
|
||||
if (!socket->canReadLine()) {
|
||||
return;
|
||||
}
|
||||
if (socket->readLine() != CLOSE_COMMAND) {
|
||||
return;
|
||||
}
|
||||
// Stop the reaper before the emit: the receiver may close the window and run
|
||||
// a nested event loop for as long as the user takes, and the reaper must not
|
||||
// free the socket from inside that loop while this handler still holds it.
|
||||
reaper->stop();
|
||||
emit closeRequested();
|
||||
socket->disconnectFromServer();
|
||||
socket->deleteLater();
|
||||
});
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
50
cockatrice/src/installer_close_server.h
Normal file
50
cockatrice/src/installer_close_server.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef COCKATRICE_INSTALLER_CLOSE_SERVER_H
|
||||
#define COCKATRICE_INSTALLER_CLOSE_SERVER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class QLocalServer;
|
||||
|
||||
/**
|
||||
* @brief Listens for the installer's request to close this instance gracefully.
|
||||
*
|
||||
* The Windows installer cannot reach an instance whose window is hidden in the notification
|
||||
* area with WM_CLOSE, and it must not kill a process that may hold an unsaved deck. It
|
||||
* connects to this local socket instead and writes one line; the instance then closes the
|
||||
* same way its own close button does - saving settings and answering its own confirmation,
|
||||
* never bypassing it.
|
||||
*
|
||||
* The socket name carries the process id, so every instance has its own and an installer
|
||||
* that found several can ask each one. It is built by installerCloseSocketName() in
|
||||
* tools/installer-holder-probe/installer_close_name.h; the two must stay identical.
|
||||
*/
|
||||
class InstallerCloseServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InstallerCloseServer(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Starts listening. A failure is not fatal: the installer falls back to
|
||||
* WM_CLOSE, which is what older clients answer.
|
||||
*/
|
||||
bool listen();
|
||||
|
||||
/// The socket name this process listens on. The installer's probe builds the same
|
||||
/// string from the process id and the account name.
|
||||
static QString socketName(qint64 processId);
|
||||
|
||||
/// The name for an explicit account, exposed so the probe's name helper and this one
|
||||
/// can be tested against each other.
|
||||
static QString socketNameForUser(qint64 processId, const QString &userName);
|
||||
|
||||
signals:
|
||||
void closeRequested();
|
||||
|
||||
private:
|
||||
QLocalServer *server = nullptr;
|
||||
QString name;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_INSTALLER_CLOSE_SERVER_H
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#include <QProgressBar>
|
||||
#include <QtGlobal>
|
||||
#include <climits>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -32,8 +33,7 @@ QString stageName(CardUpdateStage stage, const QString &fallback)
|
|||
|
||||
CardDatabaseUpdateStatusBar::CardDatabaseUpdateStatusBar(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout = QtUtils::makeLayout<QHBoxLayout>(this);
|
||||
|
||||
stageLabel = new QLabel(this);
|
||||
progressBar = new QProgressBar(this);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFileInfo>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QImageReader>
|
||||
#include <QList>
|
||||
#include <QMessageLogger>
|
||||
|
|
@ -19,35 +20,62 @@
|
|||
#include <QTimer>
|
||||
#include <libcockatrice/settings/paths_settings.h>
|
||||
|
||||
static constexpr int REFRESH_INTERVAL_MS = 10 * 1000;
|
||||
static constexpr int REFRESH_DEBOUNCE_MS = 500;
|
||||
// Watching can fail on network and FUSE mounts; the slow poll still picks those
|
||||
// changes up without the old ten-second full rescan.
|
||||
static constexpr int FALLBACK_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
||||
|
||||
CardPictureLoaderLocal::CardPictureLoaderLocal(QObject *parent)
|
||||
: QObject(parent), picsPath(SettingsCache::instance().paths().getPicsPath()),
|
||||
customPicsPath(SettingsCache::instance().paths().getCustomPicsPath())
|
||||
: CardPictureLoaderLocal(parent,
|
||||
SettingsCache::instance().paths().getPicsPath(),
|
||||
SettingsCache::instance().paths().getCustomPicsPath())
|
||||
{
|
||||
// Hook up signals to settings
|
||||
connect(&SettingsCache::instance().paths(), &PathsSettings::picsPathChanged, this,
|
||||
&CardPictureLoaderLocal::picsPathChanged);
|
||||
}
|
||||
|
||||
CardPictureLoaderLocal::CardPictureLoaderLocal(QObject *parent,
|
||||
const QString &_picsPath,
|
||||
const QString &_customPicsPath)
|
||||
: QObject(parent), picsPath(_picsPath), customPicsPath(_customPicsPath),
|
||||
folderWatcher(new QFileSystemWatcher(this)), indexRefreshTimer(new QTimer(this)),
|
||||
fallbackRefreshTimer(new QTimer(this))
|
||||
{
|
||||
indexRefreshTimer->setSingleShot(true);
|
||||
indexRefreshTimer->setInterval(REFRESH_DEBOUNCE_MS);
|
||||
connect(indexRefreshTimer, &QTimer::timeout, this, &CardPictureLoaderLocal::refreshIndex);
|
||||
|
||||
connect(folderWatcher, &QFileSystemWatcher::directoryChanged, indexRefreshTimer, qOverload<>(&QTimer::start));
|
||||
connect(folderWatcher, &QFileSystemWatcher::fileChanged, indexRefreshTimer, qOverload<>(&QTimer::start));
|
||||
|
||||
fallbackRefreshTimer->setInterval(FALLBACK_REFRESH_INTERVAL_MS);
|
||||
connect(fallbackRefreshTimer, &QTimer::timeout, this, &CardPictureLoaderLocal::refreshIndex);
|
||||
fallbackRefreshTimer->start();
|
||||
|
||||
refreshIndex();
|
||||
|
||||
refreshTimer = new QTimer(this);
|
||||
connect(refreshTimer, &QTimer::timeout, this, &CardPictureLoaderLocal::refreshIndex);
|
||||
refreshTimer->start(REFRESH_INTERVAL_MS);
|
||||
}
|
||||
|
||||
void CardPictureLoaderLocal::refreshIndex()
|
||||
{
|
||||
customFolderIndex.clear();
|
||||
QSet<QString> directories;
|
||||
|
||||
QDir customDir(customPicsPath);
|
||||
if (customDir.exists()) {
|
||||
directories.insert(customDir.absolutePath());
|
||||
}
|
||||
|
||||
QDirIterator it(customPicsPath, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
||||
|
||||
// Recursively check all subdirectories of the CUSTOM folder
|
||||
while (it.hasNext()) {
|
||||
QString thisPath(it.next());
|
||||
QFileInfo thisFileInfo(thisPath);
|
||||
const QString thisPath = it.next();
|
||||
const QFileInfo thisFileInfo = it.fileInfo();
|
||||
|
||||
if (thisFileInfo.isFile()) {
|
||||
if (thisFileInfo.isDir()) {
|
||||
directories.insert(thisFileInfo.absoluteFilePath());
|
||||
} else if (thisFileInfo.isFile()) {
|
||||
// We don't know which name is the correctedName because there might be '.'s in the cardName.
|
||||
// Just add all possibilities to be sure.
|
||||
customFolderIndex.insert(thisFileInfo.baseName(), thisFileInfo.absoluteFilePath());
|
||||
|
|
@ -55,8 +83,34 @@ void CardPictureLoaderLocal::refreshIndex()
|
|||
}
|
||||
}
|
||||
|
||||
updateWatchedDirectories(directories);
|
||||
|
||||
qCDebug(CardPictureLoaderLocalLog) << "Finished indexing local image folder CUSTOM; map now has"
|
||||
<< customFolderIndex.size() << "entries.";
|
||||
emit indexChanged();
|
||||
}
|
||||
|
||||
void CardPictureLoaderLocal::updateWatchedDirectories(const QSet<QString> &directories)
|
||||
{
|
||||
const QSet<QString> stale = watchedDirectories - directories;
|
||||
if (!stale.isEmpty()) {
|
||||
folderWatcher->removePaths(QStringList(stale.begin(), stale.end()));
|
||||
}
|
||||
|
||||
const QSet<QString> added = directories - watchedDirectories;
|
||||
if (!added.isEmpty()) {
|
||||
const QStringList failed = folderWatcher->addPaths(QStringList(added.begin(), added.end()));
|
||||
if (!failed.isEmpty()) {
|
||||
qCDebug(CardPictureLoaderLocalLog) << "Could not watch custom picture directories:" << failed;
|
||||
}
|
||||
}
|
||||
|
||||
watchedDirectories = directories;
|
||||
}
|
||||
|
||||
QStringList CardPictureLoaderLocal::indexedPaths(const QString &baseName) const
|
||||
{
|
||||
return customFolderIndex.values(baseName);
|
||||
}
|
||||
|
||||
QImage CardPictureLoaderLocal::tryLoad(const ExactCard &toLoad) const
|
||||
|
|
@ -145,4 +199,5 @@ void CardPictureLoaderLocal::picsPathChanged()
|
|||
{
|
||||
picsPath = SettingsCache::instance().paths().getPicsPath();
|
||||
customPicsPath = SettingsCache::instance().paths().getCustomPicsPath();
|
||||
refreshIndex();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@
|
|||
#include <QLoggingCategory>
|
||||
#include <QMultiHash>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class ExactCard;
|
||||
class QFileSystemWatcher;
|
||||
class QTimer;
|
||||
|
||||
inline Q_LOGGING_CATEGORY(CardPictureLoaderLocalLog, "card_picture_loader.local");
|
||||
|
|
@ -40,6 +42,18 @@ public:
|
|||
*/
|
||||
explicit CardPictureLoaderLocal(QObject *parent);
|
||||
|
||||
/**
|
||||
* @brief Constructs the loader against explicit folders without SettingsCache.
|
||||
*
|
||||
* Used by tests and by callers that already resolved the configured paths.
|
||||
*/
|
||||
CardPictureLoaderLocal(QObject *parent, const QString &picsPath, const QString &customPicsPath);
|
||||
|
||||
/**
|
||||
* @brief Returns every indexed file whose base name matches @p baseName.
|
||||
*/
|
||||
[[nodiscard]] QStringList indexedPaths(const QString &baseName) const;
|
||||
|
||||
/**
|
||||
* @brief Attempts to load a card image from local disk or custom folders.
|
||||
* @param toLoad ExactCard object representing the card to load.
|
||||
|
|
@ -54,7 +68,10 @@ private:
|
|||
QString customPicsPath; ///< Path to custom card image folder
|
||||
|
||||
QMultiHash<QString, QString> customFolderIndex; ///< Multimap from cardName to file paths in CUSTOM folder
|
||||
QTimer *refreshTimer; ///< Timer for periodic folder index refresh
|
||||
QFileSystemWatcher *folderWatcher; ///< Watches the custom folder tree for changes
|
||||
QTimer *indexRefreshTimer; ///< Debounces bursts of filesystem events
|
||||
QTimer *fallbackRefreshTimer; ///< Slow poll for filesystems without change notification
|
||||
QSet<QString> watchedDirectories; ///< Directories currently registered with folderWatcher
|
||||
|
||||
/**
|
||||
* @brief Rebuilds the index of the custom image folder.
|
||||
|
|
@ -65,6 +82,11 @@ private:
|
|||
*/
|
||||
void refreshIndex();
|
||||
|
||||
/**
|
||||
* @brief Keeps the watcher registered on exactly @p directories.
|
||||
*/
|
||||
void updateWatchedDirectories(const QSet<QString> &directories);
|
||||
|
||||
/**
|
||||
* @brief Attempts to load a card image from disk given its set and name info.
|
||||
* @param setName Corrected short name of the card's set.
|
||||
|
|
@ -82,6 +104,12 @@ private:
|
|||
const QString &collectorNumber,
|
||||
const QString &providerId) const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Emitted after the custom folder index has been rebuilt.
|
||||
*/
|
||||
void indexChanged();
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Updates internal paths when the user changes picture settings.
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <compare>
|
||||
#include <libcockatrice/network/network_manager.h>
|
||||
#include <libcockatrice/settings/cache_storage_settings.h>
|
||||
#include <libcockatrice/settings/download_settings.h>
|
||||
#include <libcockatrice/settings/paths_settings.h>
|
||||
|
|
@ -46,11 +47,10 @@ CardPictureLoaderWorker::CardPictureLoaderWorker()
|
|||
: QObject(nullptr), picDownload(SettingsCache::instance().downloads().getPicDownload()),
|
||||
hostRequestLimits(SettingsCache::instance().downloads().getHostRequestLimits())
|
||||
{
|
||||
networkManager = new QNetworkAccessManager(this);
|
||||
networkManager = NetworkManagerFactory::create(this);
|
||||
// We need a timeout to ensure requests don't hang indefinitely in case of
|
||||
// cache corruption, see related Qt bug: https://bugreports.qt.io/browse/QTBUG-111397
|
||||
// Use Qt's default timeout (30s, as of 2023-02-22)
|
||||
networkManager->setTransferTimeout();
|
||||
cache = new QNetworkDiskCache(this);
|
||||
cache->setCacheDirectory(SettingsCache::instance().getNetworkCachePath());
|
||||
cache->setMaximumCacheSize(1024L * 1024L *
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <libcockatrice/interfaces/noop_card_set_priority_controller.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/settings/download_settings.h>
|
||||
#include <utility>
|
||||
|
||||
CardPictureToLoad::CardPictureToLoad(const ExactCard &_card)
|
||||
: card(_card), urlTemplates(SettingsCache::instance().downloads().getAllURLs())
|
||||
|
|
@ -74,19 +75,20 @@ QList<CardSetPtr> CardPictureToLoad::extractSetsSorted(const ExactCard &card)
|
|||
//! \todo Check if going through all sets is still necessary after the ExactCard refactor.
|
||||
static PrintingInfo findPrintingForSet(const ExactCard &card, const QString &setName)
|
||||
{
|
||||
SetToPrintingsMap setsToPrintings = card.getInfo().getSets();
|
||||
const auto &setsToPrintings = card.getInfo().getSets();
|
||||
|
||||
if (!setsToPrintings.contains(setName)) {
|
||||
const auto setPrintings = setsToPrintings.constFind(setName);
|
||||
if (setPrintings == setsToPrintings.constEnd() || setPrintings.value().isEmpty()) {
|
||||
return PrintingInfo();
|
||||
}
|
||||
|
||||
for (const auto &printing : setsToPrintings[setName]) {
|
||||
for (const auto &printing : setPrintings.value()) {
|
||||
if (printing.getUuid() == card.getPrinting().getUuid()) {
|
||||
return printing;
|
||||
}
|
||||
}
|
||||
|
||||
return setsToPrintings[setName][0];
|
||||
return setPrintings.value().first();
|
||||
}
|
||||
|
||||
void CardPictureToLoad::populateSetUrls()
|
||||
|
|
@ -183,10 +185,14 @@ static int parse(const QString &urlTemplate,
|
|||
{
|
||||
static const QRegularExpression rxFillWith("^(.+)_fill_with_(.+)$");
|
||||
static const QRegularExpression rxSubStr("^(.+)_substr_(\\d+)_(\\d+)$");
|
||||
static const QRegularExpression rxCardProp("!prop:([^!]+)!");
|
||||
static const QRegularExpression rxCardSet("!set:([^!]+)!");
|
||||
|
||||
const QRegularExpression rxCardProp("!" + propType + ":([^!]+)!");
|
||||
// The property type only takes two values, so keep the expression that was
|
||||
// compiled for it instead of rebuilding one per URL template.
|
||||
const QRegularExpression &rxCardPropSelector = propType == "prop" ? rxCardProp : rxCardSet;
|
||||
|
||||
auto matches = rxCardProp.globalMatch(urlTemplate);
|
||||
auto matches = rxCardPropSelector.globalMatch(urlTemplate);
|
||||
while (matches.hasNext()) {
|
||||
auto match = matches.next();
|
||||
QString templatePropertyName = match.captured(1);
|
||||
|
|
@ -303,10 +309,10 @@ QString CardPictureToLoad::transformUrl(const QString &urlTemplate) const
|
|||
}
|
||||
|
||||
QString transformedUrl = urlTemplate;
|
||||
for (const QString &prop : transformMap.keys()) {
|
||||
for (const auto &[prop, value] : std::as_const(transformMap).asKeyValueRange()) {
|
||||
if (transformedUrl.contains(prop)) {
|
||||
if (!transformMap[prop].isEmpty()) {
|
||||
transformedUrl.replace(prop, QUrl::toPercentEncoding(transformMap[prop]));
|
||||
if (!value.isEmpty()) {
|
||||
transformedUrl.replace(prop, QUrl::toPercentEncoding(value));
|
||||
} else {
|
||||
/* This means the template is requesting information that is not
|
||||
* populated in this card, so it should return an empty string,
|
||||
|
|
|
|||
|
|
@ -7,13 +7,14 @@
|
|||
#ifndef DECK_LOADER_H
|
||||
#define DECK_LOADER_H
|
||||
|
||||
#include "deck_file_format.h"
|
||||
#include "loaded_deck.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
#include <QObject>
|
||||
#include <QPrinter>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QTextCursor>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <optional>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
#include "intent_open_local_deck.h"
|
||||
|
||||
#include "../deck_loader/deck_file_format.h"
|
||||
#include "../deck_loader/deck_loader.h"
|
||||
#include "../deck_loader/loaded_deck.h"
|
||||
#include "../widgets/tabs/tab_supervisor.h"
|
||||
#include "intent_wait_for_database_load.h"
|
||||
#include "libcockatrice/card/database/card_database.h"
|
||||
#include "libcockatrice/card/database/card_database_loader.h"
|
||||
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/deck_file_format.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <optional>
|
||||
|
||||
IntentOpenLocalDeck::IntentOpenLocalDeck(TabSupervisor *_tabSupervisor, const QString &_file)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <libcockatrice/protocol/pb/response_deck_share_list.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_deck_share_item.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <optional>
|
||||
#include <qnamespace.h>
|
||||
#include <string>
|
||||
|
|
@ -184,13 +185,11 @@ void IntentOpenSharedDeck::onItemFailure(const QString &reason)
|
|||
}
|
||||
|
||||
const int downloadedCount = loadedDecks.size();
|
||||
const QMessageBox::StandardButton answer = QMessageBox::question(
|
||||
previewDialog, tr("Open shared decks"),
|
||||
tr("Could not download the deck \"%1\".\n\n%n deck(s) were already downloaded. Open them?", "", downloadedCount)
|
||||
.arg(itemNames.value(currentItemId, tr("Unknown deck"))),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
|
||||
if (answer == QMessageBox::Yes) {
|
||||
if (QtUtils::confirm(previewDialog, tr("Open shared decks"),
|
||||
tr("Could not download the deck \"%1\".\n\n%n deck(s) were already downloaded. Open them?", "",
|
||||
downloadedCount)
|
||||
.arg(itemNames.value(currentItemId, tr("Unknown deck"))),
|
||||
QMessageBox::Yes)) {
|
||||
finishAll();
|
||||
} else {
|
||||
previewDialog->deleteLater();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef COCKATRICE_INTENT_OPEN_SHARED_DECK_H
|
||||
#define COCKATRICE_INTENT_OPEN_SHARED_DECK_H
|
||||
|
||||
#include "../deck_loader/loaded_deck.h"
|
||||
#include "contexts/context_open_deck.h"
|
||||
#include "intent.h"
|
||||
|
||||
|
|
@ -9,6 +8,7 @@
|
|||
#include <QMap>
|
||||
#include <QScopedPointer>
|
||||
#include <QString>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <memory>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/network/client/abstract/abstract_client.h>
|
||||
#include <libcockatrice/settings/servers_settings.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <memory>
|
||||
#include <qlogging.h>
|
||||
#include <qnamespace.h>
|
||||
|
|
@ -115,9 +116,7 @@ Intent *IntentUrlParser::createJoinGameIntent(const QUrlQuery &query, PendingInt
|
|||
const QString gameDescription = query.queryItemValue("game", QUrl::FullyDecoded);
|
||||
const QString message = generateJoinGameMessage(*ctx, gameDescription);
|
||||
|
||||
const QMessageBox::StandardButton answer = QMessageBox::question(
|
||||
mainWindow, tr("Join game"), message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
if (answer != QMessageBox::Yes) {
|
||||
if (!QtUtils::confirm(mainWindow, tr("Join game"), message, QMessageBox::Yes)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -231,11 +230,10 @@ Intent *IntentUrlParser::createOpenDeckIntent(const QUrlQuery &query, PendingInt
|
|||
if (client->getStatus() == StatusLoggedIn) {
|
||||
const QString current =
|
||||
QStringLiteral("%1:%2").arg(client->serverName(), QString::number(client->serverPort()));
|
||||
const QMessageBox::StandardButton answer = QMessageBox::question(
|
||||
mainWindow, tr("Open shared deck"),
|
||||
tr("Opening this share link connects you to %1 instead of %2.\n\nContinue?").arg(target, current),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
if (answer != QMessageBox::Yes) {
|
||||
if (!QtUtils::confirm(
|
||||
mainWindow, tr("Open shared deck"),
|
||||
tr("Opening this share link connects you to %1 instead of %2.\n\nContinue?").arg(target, current),
|
||||
QMessageBox::Yes)) {
|
||||
return nullptr;
|
||||
}
|
||||
chain.migrationTargetHost = ctx->serverContext.hostname;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <initializer_list>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
PaletteEditorDialog::PaletteEditorDialog(const QString &_themeDirPath, const QString &_themeName, QWidget *parent)
|
||||
|
|
@ -57,9 +58,7 @@ PaletteEditorDialog::PaletteEditorDialog(const QString &_themeDirPath, const QSt
|
|||
|
||||
void PaletteEditorDialog::setupUi()
|
||||
{
|
||||
auto *root = new QVBoxLayout(this);
|
||||
root->setSpacing(0);
|
||||
root->setContentsMargins(0, 0, 0, 0);
|
||||
auto *root = QtUtils::makeLayout<QVBoxLayout>(this, 0, 0);
|
||||
|
||||
// Header
|
||||
header = new QWidget;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <utility>
|
||||
|
||||
|
|
@ -65,8 +66,7 @@ static const QMap<AppColor::Role, const char *> APP_ROLE_DESCRIPTIONS = {
|
|||
|
||||
PaletteGridWidget::PaletteGridWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
scroll = new QScrollArea(this);
|
||||
scroll->setWidgetResizable(true);
|
||||
scroll = QtUtils::makeScrollArea(this, Qt::ScrollBarAsNeeded, Qt::ScrollBarAsNeeded);
|
||||
scroll->setFrameShape(QFrame::NoFrame);
|
||||
|
||||
gridHost = new QWidget;
|
||||
|
|
@ -74,8 +74,7 @@ PaletteGridWidget::PaletteGridWidget(QWidget *parent) : QWidget(parent)
|
|||
refreshChromePalettes();
|
||||
scroll->setWidget(gridHost);
|
||||
|
||||
layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout = QtUtils::makeLayout<QVBoxLayout>(this);
|
||||
layout->addWidget(scroll);
|
||||
}
|
||||
|
||||
|
|
@ -102,9 +101,7 @@ void PaletteGridWidget::buildGrid(QWidget *host)
|
|||
auto *label = new QLabel(groupHeaders[col], host);
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
label->setToolTip(groupTips[col]);
|
||||
QFont f = label->font();
|
||||
f.setBold(true);
|
||||
label->setFont(f);
|
||||
label->setFont(QtUtils::boldFont(label->font()));
|
||||
label->setAutoFillBackground(true);
|
||||
label->setContentsMargins(4, 4, 4, 4);
|
||||
grid->addWidget(label, 0, col + 1);
|
||||
|
|
@ -149,9 +146,7 @@ void PaletteGridWidget::buildGrid(QWidget *host)
|
|||
|
||||
auto *appHeader = new QLabel(tr("App colors"), host);
|
||||
appHeader->setToolTip(tr("Application-specific colors layered on top of the Qt palette"));
|
||||
QFont appHeaderFont = appHeader->font();
|
||||
appHeaderFont.setBold(true);
|
||||
appHeader->setFont(appHeaderFont);
|
||||
appHeader->setFont(QtUtils::boldFont(appHeader->font()));
|
||||
appHeader->setAutoFillBackground(true);
|
||||
appHeader->setContentsMargins(4, 4, 4, 4);
|
||||
grid->addWidget(appHeader, appHeaderRow, 0, 1, 4);
|
||||
|
|
|
|||
|
|
@ -558,6 +558,9 @@ void ThemeManager::applyStyleAndPalette(const QString &themeName,
|
|||
widget->update();
|
||||
}
|
||||
|
||||
// Palette-only previews do not run themeChangedSlot(), so drop snapshots
|
||||
// generated under the previous palette here as well.
|
||||
clearPixmapGeneratorCaches();
|
||||
emit paletteChanged();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@
|
|||
ColorIdentityWidget::ColorIdentityWidget(QWidget *parent, const QString &_colorIdentity)
|
||||
: QWidget(parent), colorIdentity(_colorIdentity)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setSpacing(5); // Small spacing between icons
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout = QtUtils::makeLayout<QHBoxLayout>(this, 5);
|
||||
layout->setAlignment(Qt::AlignCenter); // Ensure icons are centered
|
||||
setLayout(layout);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,11 @@
|
|||
#include <QResizeEvent>
|
||||
#include <QSize>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
ManaCostWidget::ManaCostWidget(QWidget *parent, CardInfoPtr _card) : QWidget(parent), card(_card)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setSpacing(5); // Small spacing between icons
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout = QtUtils::makeLayout<QHBoxLayout>(this, 5);
|
||||
setLayout(layout);
|
||||
|
||||
setFixedHeight(50); // Fixed height
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
|
||||
class QWidget;
|
||||
|
||||
|
|
@ -30,9 +31,8 @@ CardInfoDisplayWidget::CardInfoDisplayWidget(const CardRef &cardRef, QWidget *pa
|
|||
text->setObjectName("text");
|
||||
connect(text, &CardInfoTextWidget::linkActivated, this, [this](const QString &card) { setCard({card}); });
|
||||
|
||||
auto *layout = new QVBoxLayout();
|
||||
auto *layout = QtUtils::makeLayout<QVBoxLayout>();
|
||||
layout->setObjectName("layout");
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(pic, 0, Qt::AlignCenter);
|
||||
layout->addWidget(text, 0, Qt::AlignCenter);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/utility/card_ref.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
struct CardRef;
|
||||
|
|
@ -49,15 +50,13 @@ CardInfoFrameWidget::CardInfoFrameWidget(QWidget *parent)
|
|||
insertTab(ImageAndTextView, tab3, QString());
|
||||
connect(this, &CardInfoFrameWidget::currentChanged, this, &CardInfoFrameWidget::setViewMode);
|
||||
|
||||
tab1Layout = new QVBoxLayout();
|
||||
tab1Layout = QtUtils::makeLayout<QVBoxLayout>();
|
||||
tab1Layout->setObjectName("tab1Layout");
|
||||
tab1Layout->setContentsMargins(0, 0, 0, 0);
|
||||
tab1Layout->setSpacing(0);
|
||||
tab1->setLayout(tab1Layout);
|
||||
|
||||
tab2Layout = new QVBoxLayout();
|
||||
tab2Layout = QtUtils::makeLayout<QVBoxLayout>();
|
||||
tab2Layout->setObjectName("tab2Layout");
|
||||
tab2Layout->setContentsMargins(0, 0, 0, 0);
|
||||
tab2Layout->setSpacing(0);
|
||||
tab2->setLayout(tab2Layout);
|
||||
|
||||
|
|
@ -65,9 +64,8 @@ CardInfoFrameWidget::CardInfoFrameWidget(QWidget *parent)
|
|||
splitter->setObjectName("splitter");
|
||||
splitter->setOrientation(Qt::Vertical);
|
||||
|
||||
tab3Layout = new QVBoxLayout();
|
||||
tab3Layout = QtUtils::makeLayout<QVBoxLayout>();
|
||||
tab3Layout->setObjectName("tab3Layout");
|
||||
tab3Layout->setContentsMargins(0, 0, 0, 0);
|
||||
tab3Layout->setSpacing(0);
|
||||
tab3Layout->addWidget(splitter);
|
||||
tab3->setLayout(tab3Layout);
|
||||
|
|
|
|||
|
|
@ -491,7 +491,14 @@ void CardInfoPictureWidget::showEnlargedPixmap()
|
|||
enlargedPixmapWidget->setCardPixmap(exactCard, enlargedSize);
|
||||
|
||||
const QPoint cursorPos = QCursor::pos();
|
||||
const QRect screenGeometry = QGuiApplication::screenAt(cursorPos)->geometry();
|
||||
QScreen *screen = QGuiApplication::screenAt(cursorPos);
|
||||
if (!screen) {
|
||||
screen = QGuiApplication::primaryScreen();
|
||||
}
|
||||
if (!screen) {
|
||||
return;
|
||||
}
|
||||
const QRect screenGeometry = screen->geometry();
|
||||
const QSize widgetSize = enlargedPixmapWidget->size();
|
||||
|
||||
int newX = cursorPos.x() + ENLARGED_PIXMAP_OFFSET;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <QWidget>
|
||||
#include <libcockatrice/card/game_specific_terms.h>
|
||||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qobjectdefs.h>
|
||||
|
||||
|
|
@ -33,9 +34,7 @@ CardInfoTextWidget::CardInfoTextWidget(QWidget *parent) : QFrame(parent)
|
|||
textLabel->setReadOnly(true);
|
||||
textLabel->setMinimumSize(35, 35);
|
||||
|
||||
propsScroll = new QScrollArea(this);
|
||||
propsScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
propsScroll->setWidgetResizable(true);
|
||||
propsScroll = QtUtils::makeScrollArea(this);
|
||||
propsScroll->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
propsScroll->setContentsMargins(0, 0, 0, 0);
|
||||
propsScroll->setFrameStyle(QFrame::NoFrame);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/models/deck_list/deck_list_model.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
class CardSizeWidget;
|
||||
|
|
@ -179,15 +180,7 @@ void DeckCardZoneDisplayWidget::displayCards()
|
|||
void DeckCardZoneDisplayWidget::refreshDisplayType(const DisplayType &_displayType)
|
||||
{
|
||||
displayType = _displayType;
|
||||
QLayoutItem *item;
|
||||
while ((item = cardGroupLayout->takeAt(0)) != nullptr) {
|
||||
if (item->widget()) {
|
||||
item->widget()->deleteLater();
|
||||
} else if (item->layout()) {
|
||||
item->layout()->deleteLater();
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
QtUtils::clearLayoutRec(cardGroupLayout);
|
||||
|
||||
indexToWidgetMap.clear();
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <QWidget>
|
||||
#include <algorithm>
|
||||
#include <libcockatrice/utility/color.h>
|
||||
#include <libcockatrice/utility/qt_utils.h>
|
||||
#include <qnamespace.h>
|
||||
#include <utility>
|
||||
|
||||
|
|
@ -47,12 +48,7 @@ ManaBaseWidget::ManaBaseWidget(QWidget *parent, DeckListStatisticsAnalyzer *anal
|
|||
void ManaBaseWidget::updateDisplay()
|
||||
{
|
||||
// Clear previous widgets
|
||||
while (QLayoutItem *item = barLayout->takeAt(0)) {
|
||||
if (item->widget()) {
|
||||
item->widget()->deleteLater();
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
QtUtils::clearLayoutRec(barLayout);
|
||||
|
||||
auto &pipCount = analyzer->getProductionPipCount();
|
||||
auto &cardCount = analyzer->getProductionCardCount();
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue