Compare commits

..

2 commits

Author SHA1 Message Date
Lukas Brübach
dd1f0d3eec [Replay] Build a compact event history index
Turn replay protocol events into short localized summaries with an actor, badge, and time so the history UI can render them without retaining the replay or duplicating the chat log.
2026-10-20 22:15:24 +02:00
Lukas Brübach
b863a53b35 [Replay] Seek to the exact time of an event
Timeline seeks only expose whole timer intervals, so jumping to an event could leave that event unprocessed until the next tick. Add an event-time seek that also applies events at the target timestamp.
2026-10-20 22:15:24 +02:00
391 changed files with 3592 additions and 9993 deletions

View file

@ -459,206 +459,6 @@ if [[ $MAKE_COVERAGE ]]; then
echo "::endgroup::" echo "::endgroup::"
fi 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 if [[ $MAKE_INSTALL ]]; then
echo "::group::Install" echo "::group::Install"
cmake --build . --target install --config "$BUILDTYPE" cmake --build . --target install --config "$BUILDTYPE"

View file

@ -584,17 +584,11 @@ jobs:
# job. A scope nobody has written to yet therefore leaves the directory missing # 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 # 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. # 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" - name: "Create vcpkg cache directory"
if: always()
shell: bash shell: bash
env: env:
CACHE_DIR: '${{ steps.vcpkg-cache.outputs.path }}' CACHE_DIR: '${{ steps.vcpkg-cache.outputs.path }}'
run: mkdir -p "${CACHE_DIR:-$GITHUB_WORKSPACE/.vcpkg-cache}" run: mkdir -p "$CACHE_DIR"
# Uses environment variables, see compile.sh for more details # Uses environment variables, see compile.sh for more details
- name: "Build Cockatrice" - name: "Build Cockatrice"

View file

@ -250,15 +250,6 @@ message(STATUS "Update Translations: ${UPDATE_TRANSLATIONS}")
include(FindQtRuntime) 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) set(CMAKE_AUTOMOC TRUE)
# Find other needed libraries # Find other needed libraries
@ -429,9 +420,6 @@ if(WITH_ORACLE OR WITH_CLIENT)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_models ${CMAKE_BINARY_DIR}/libcockatrice_models) add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_models ${CMAKE_BINARY_DIR}/libcockatrice_models)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_filters ${CMAKE_BINARY_DIR}/libcockatrice_filters) add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_filters ${CMAKE_BINARY_DIR}/libcockatrice_filters)
endif() endif()
if(WITH_CLIENT)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_game ${CMAKE_BINARY_DIR}/libcockatrice_game)
endif()
if(WITH_SERVER) if(WITH_SERVER)
add_subdirectory(servatrice) add_subdirectory(servatrice)
@ -443,13 +431,6 @@ if(WITH_CLIENT)
set(CPACK_INSTALL_CMAKE_PROJECTS "Cockatrice;Cockatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) set(CPACK_INSTALL_CMAKE_PROJECTS "Cockatrice;Cockatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
endif() 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) if(WITH_ORACLE)
add_subdirectory(oracle) add_subdirectory(oracle)
set(CPACK_INSTALL_CMAKE_PROJECTS "Oracle;Oracle;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) set(CPACK_INSTALL_CMAKE_PROJECTS "Oracle;Oracle;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS})
@ -462,5 +443,5 @@ endif()
if(Qt6_FOUND AND Qt6_VERSION_MINOR GREATER_EQUAL 3) if(Qt6_FOUND AND Qt6_VERSION_MINOR GREATER_EQUAL 3)
# Qt6.3+ requires project finalization to support translations # Qt6.3+ requires project finalization to support translations
qt_finalize_project() qt6_finalize_project()
endif() endif()

View file

@ -15,10 +15,9 @@
--- ---
<br><pre><p align='center'> <br><pre><p align='center'>
<b>To get started with Cockatrice &#8674; [view our webpage](https://cockatrice.github.io/)</b> <b>To get started with Cockatrice &#8674; [view our webpage](https://cockatrice.github.io/)</b><br>
<b>To get support, or suggest changes to the app &#8674; [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 get support, or suggest changes to the app &#8674; [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 learn about the app and its code &#8674; [read our code documentation](https://cockatrice.github.io/docs/)</b> <b>To help with development &#8674; learn [how to contribute](#contribute-)</b>
<b>To help with development &#8674; learn [how to contribute](#contribute)</b>
</pre><p><br> </pre><p><br>
@ -84,7 +83,7 @@ Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other p
### Code ### Code
To commit code to the project, please review our [Contribution Guidelines](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md) first.<br> To contribute code to the project, please review our [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: We maintain two tags for contributors to easier find issues to potentially work on:
- [![Good first issue](https://img.shields.io/github/issues/cockatrice/cockatrice/Good%20First%20Issue)](https://github.com/Cockatrice/Cockatrice/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22Good%20first%20issue%22%20)<br> - [![Good first issue](https://img.shields.io/github/issues/cockatrice/cockatrice/Good%20First%20Issue)](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. Issues tagged in this way provide a simple way to get started. They don't require much experience to be worked on.
@ -96,7 +95,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). 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 [![CI Docs](https://github.com/Cockatrice/Cockatrice/actions/workflows/documentation-build.yml/badge.svg?event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/documentation-build.yml?query=event%3Arelease+event%3Aworkflow_dispatch) ### Documentation [![CI Docs](https://github.com/Cockatrice/Cockatrice/actions/workflows/documentation-build.yml/badge.svg?event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/documentation-build.yml?query=event%3Apush)
There are various places where useful information for different needs are maintained: There are various places where useful information for different needs are maintained:
- [Official Code Documentation](https://cockatrice.github.io/docs/) - [Official Code Documentation](https://cockatrice.github.io/docs/)
@ -105,8 +104,7 @@ 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 README](https://github.com/Cockatrice/Cockatrice/blob/master/README.md) `This file`
- [Official CONTRIBUTING](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md) - [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. 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.
We encourage everybody to improve the documentation by suggesting edits based on this guide.
### Translation [![Transifex Project](https://img.shields.io/badge/translate-on%20transifex-brightgreen)](https://explore.transifex.com/cockatrice/cockatrice/) ### Translation [![Transifex Project](https://img.shields.io/badge/translate-on%20transifex-brightgreen)](https://explore.transifex.com/cockatrice/cockatrice/)

File diff suppressed because it is too large Load diff

View file

@ -57,29 +57,6 @@ 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 --------------------------------------------------------------- # --- Event ---------------------------------------------------------------
$eventId = [guid]::NewGuid().ToString('N') $eventId = [guid]::NewGuid().ToString('N')
$now = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') $now = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
@ -98,9 +75,6 @@ try {
extra = @{ extra = @{
reason = $Reason reason = $Reason
os = [System.Environment]::OSVersion.VersionString os = [System.Environment]::OSVersion.VersionString
holders = $Holders
close_result = $CloseResult
end_result = $EndResult
} }
} }
$eventJson = $sentryEvent | ConvertTo-Json -Compress -Depth 6 $eventJson = $sentryEvent | ConvertTo-Json -Compress -Depth 6

View file

@ -25,7 +25,9 @@ set(cockatrice_SOURCES
src/client/settings/shortcut_treeview.cpp src/client/settings/shortcut_treeview.cpp
src/client/settings/shortcuts_settings.cpp src/client/settings/shortcuts_settings.cpp
src/interface/deck_loader/card_node_function.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/deck_loader.cpp
src/interface/deck_loader/loaded_deck.cpp
src/interface/widgets/dialogs/dlg_connect.cpp src/interface/widgets/dialogs/dlg_connect.cpp
src/interface/widgets/dialogs/dlg_convert_deck_to_cod_format.cpp src/interface/widgets/dialogs/dlg_convert_deck_to_cod_format.cpp
src/interface/widgets/dialogs/dlg_create_game.cpp src/interface/widgets/dialogs/dlg_create_game.cpp
@ -69,27 +71,36 @@ set(cockatrice_SOURCES
src/filters/filter_builder.cpp src/filters/filter_builder.cpp
src/filters/filter_tree_model.cpp src/filters/filter_tree_model.cpp
src/filters/syntax_help.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_drag_item.cpp
src/game_graphics/board/abstract_card_item.cpp src/game_graphics/board/abstract_card_item.cpp
src/game_graphics/board/abstract_counter.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_item.cpp
src/game_graphics/board/arrow_registry.cpp
src/game_graphics/board/arrow_target.cpp src/game_graphics/board/arrow_target.cpp
src/game_graphics/board/card_drag_item.cpp src/game_graphics/board/card_drag_item.cpp
src/game_graphics/board/card_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_graphics/board/counter_general.cpp
src/game/board/counter_state.cpp
src/game_graphics/board/translate_counter_name.cpp src/game_graphics/board/translate_counter_name.cpp
src/game_graphics/deckview/deck_view.cpp src/game_graphics/deckview/deck_view.cpp
src/game_graphics/deckview/deck_view_container.cpp src/game_graphics/deckview/deck_view_container.cpp
src/game_graphics/deckview/tabbed_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_create_token.cpp
src/game_graphics/dialogs/dlg_move_top_cards_until.cpp src/game_graphics/dialogs/dlg_move_top_cards_until.cpp
src/game_graphics/dialogs/dlg_roll_dice.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_graphics/game_scene.cpp
src/game/game_state.cpp
src/game_graphics/game_view.cpp src/game_graphics/game_view.cpp
src/game_graphics/hand_counter.cpp src/game_graphics/hand_counter.cpp
src/game_graphics/log/message_log_widget.cpp src/game_graphics/log/message_log_widget.cpp
src/game/phase.cpp
src/game_graphics/phases_toolbar.cpp src/game_graphics/phases_toolbar.cpp
src/game_graphics/player/menu/card_menu.cpp src/game_graphics/player/menu/card_menu.cpp
src/game_graphics/player/menu/custom_zone_menu.cpp src/game_graphics/player/menu/custom_zone_menu.cpp
@ -107,11 +118,23 @@ set(cockatrice_SOURCES
src/game_graphics/tally/stats_tally.cpp src/game_graphics/tally/stats_tally.cpp
src/game_graphics/tally/subtype_tally.cpp src/game_graphics/tally/subtype_tally.cpp
src/game_graphics/tally/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_area.cpp
src/game_graphics/player/player_dialogs.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_graphics/player/player_graphics_item.cpp
src/game/player/player_info.cpp
src/game_graphics/player/player_list_widget.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_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/card_zone.cpp
src/game_graphics/zones/hand_zone.cpp src/game_graphics/zones/hand_zone.cpp
src/game_graphics/zones/pile_zone.cpp src/game_graphics/zones/pile_zone.cpp
@ -323,7 +346,6 @@ 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_tag_filter_widget.cpp
src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.cpp src/interface/widgets/visual_deck_storage/visual_deck_storage_widget.cpp
src/interface/window_main.cpp src/interface/window_main.cpp
src/installer_close_server.cpp
src/main.cpp src/main.cpp
src/single_instance_manager.cpp src/single_instance_manager.cpp
src/interface/widgets/tabs/abstract_tab_deck_editor.cpp src/interface/widgets/tabs/abstract_tab_deck_editor.cpp
@ -429,7 +451,6 @@ set(cockatrice_SOURCES
src/interface/widgets/tabs/api/edhrec/display/commander/edhrec_commander_api_response_budget_navigation_widget.h 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.cpp
src/interface/widgets/utility/compact_push_button.h src/interface/widgets/utility/compact_push_button.h
src/installer_close_server.h
src/single_instance_manager.h src/single_instance_manager.h
src/client/url_scheme_event_filter.h src/client/url_scheme_event_filter.h
src/interface/intents/intent_connect_to_server.cpp src/interface/intents/intent_connect_to_server.cpp
@ -491,7 +512,7 @@ if(APPLE)
set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
endif(APPLE) endif(APPLE)
qt_add_resources(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES}) qt6_add_resources(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
# Declare path variables # Declare path variables
set(ICONDIR set(ICONDIR
@ -512,7 +533,7 @@ set(COCKATRICE_MAC_QM_INSTALL_DIR "cockatrice.app/Contents/Resources/translation
set(COCKATRICE_UNIX_QM_INSTALL_DIR "share/cockatrice/translations") set(COCKATRICE_UNIX_QM_INSTALL_DIR "share/cockatrice/translations")
set(COCKATRICE_WIN32_QM_INSTALL_DIR "translations") set(COCKATRICE_WIN32_QM_INSTALL_DIR "translations")
qt_add_executable( qt6_add_executable(
cockatrice cockatrice
WIN32 WIN32
MACOSX_BUNDLE MACOSX_BUNDLE
@ -524,7 +545,7 @@ qt_add_executable(
target_precompile_headers(cockatrice PRIVATE "${CMAKE_SOURCE_DIR}/cmake/pch/qtwidgets_pch.h") target_precompile_headers(cockatrice PRIVATE "${CMAKE_SOURCE_DIR}/cmake/pch/qtwidgets_pch.h")
qt_add_shaders( qt6_add_shaders(
cockatrice cockatrice
"onboarding_shaders" "onboarding_shaders"
PREFIX PREFIX
@ -536,7 +557,7 @@ qt_add_shaders(
src/interface/widgets/onboarding/shaders/brand_plate.frag src/interface/widgets/onboarding/shaders/brand_plate.frag
) )
qt_add_resources( qt6_add_resources(
cockatrice cockatrice
"onboarding_qml" "onboarding_qml"
PREFIX PREFIX
@ -552,7 +573,6 @@ target_link_libraries(
PUBLIC libcockatrice_card PUBLIC libcockatrice_card
libcockatrice_deck_list libcockatrice_deck_list
libcockatrice_filters libcockatrice_filters
libcockatrice_game
libcockatrice_utility libcockatrice_utility
libcockatrice_network libcockatrice_network
libcockatrice_models libcockatrice_models
@ -769,7 +789,7 @@ endif()
if(Qt6LinguistTools_FOUND) if(Qt6LinguistTools_FOUND)
#Qt6 Translations happen after the executable is built up #Qt6 Translations happen after the executable is built up
if(UPDATE_TRANSLATIONS) if(UPDATE_TRANSLATIONS)
qt_add_translations( qt6_add_translations(
cockatrice cockatrice
TS_FILES TS_FILES
${cockatrice_TS} ${cockatrice_TS}
@ -779,7 +799,7 @@ if(Qt6LinguistTools_FOUND)
cockatrice_QM cockatrice_QM
) )
else() else()
qt_add_translations(cockatrice TS_FILES ${cockatrice_TS} QM_FILES_OUTPUT_VARIABLE cockatrice_QM) qt6_add_translations(cockatrice TS_FILES ${cockatrice_TS} QM_FILES_OUTPUT_VARIABLE cockatrice_QM)
endif() endif()
if(UNIX) if(UNIX)
@ -805,4 +825,4 @@ if(Qt6LinguistTools_FOUND)
endforeach() endforeach()
endif() endif()
qt_finalize_target(cockatrice) qt6_finalize_target(cockatrice)

View file

@ -14,7 +14,6 @@
#include <QObject> #include <QObject>
#include <QPoint> #include <QPoint>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <libcockatrice/utility/qt_utils.h>
#include <qnamespace.h> #include <qnamespace.h>
LatencyStatusWidget::LatencyStatusWidget(QWidget *parent) : QWidget(parent) LatencyStatusWidget::LatencyStatusWidget(QWidget *parent) : QWidget(parent)
@ -25,7 +24,9 @@ LatencyStatusWidget::LatencyStatusWidget(QWidget *parent) : QWidget(parent)
latencyGraph = new LatencyGraphWidget(this); latencyGraph = new LatencyGraphWidget(this);
latencyGraph->setFixedSize(90, 14); latencyGraph->setFixedSize(90, 14);
auto *layout = QtUtils::makeLayout<QHBoxLayout>(this, 4); auto *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(4);
layout->addWidget(latencyGraph); layout->addWidget(latencyGraph);
layout->addWidget(pingLabel); layout->addWidget(pingLabel);

View file

@ -24,7 +24,6 @@
#include <libcockatrice/network/client/remote/remote_client.h> #include <libcockatrice/network/client/remote/remote_client.h>
#include <libcockatrice/protocol/pb/response.pb.h> #include <libcockatrice/protocol/pb/response.pb.h>
#include <libcockatrice/settings/servers_settings.h> #include <libcockatrice/settings/servers_settings.h>
#include <libcockatrice/utility/qt_utils.h>
#include <qnamespace.h> #include <qnamespace.h>
class QWidget; class QWidget;
@ -244,27 +243,17 @@ void ConnectionController::onLoginError(int r,
{ {
switch (static_cast<Response::ResponseCode>(r)) { switch (static_cast<Response::ResponseCode>(r)) {
case Response::RespClientUpdateRequired: { 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); QMessageBox msgBox(dialogParent);
msgBox.setIcon(QMessageBox::Critical); msgBox.setIcon(QMessageBox::Critical);
msgBox.setWindowTitle(tr("Failed Login")); msgBox.setWindowTitle(tr("Failed Login"));
msgBox.setText(tr("Your client seems to be missing features this server requires for connection.") +
if (missingFeatures.isEmpty()) { "\n\n" + tr("To update your client, go to 'Help -> Check for Client Updates'."));
// Without a feature list this is either a login the client stopped itself or a msgBox.setDetailedText(formatted);
// 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(); msgBox.exec();
break; break;
} }
@ -311,8 +300,9 @@ void ConnectionController::onLoginError(int r,
} }
case Response::RespRegistrationRequired: { case Response::RespRegistrationRequired: {
if (QtUtils::confirm(dialogParent, tr("Error"), if (QMessageBox::question(dialogParent, tr("Error"),
tr("This server requires user registration. Do you want to register now?"))) { tr("This server requires user registration. Do you want to register now?"),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
registerToServer(); registerToServer();
} }
return; // don't re-prompt connect return; // don't re-prompt connect
@ -527,38 +517,14 @@ void ConnectionController::onForgotPasswordSuccess()
SettingsCache::instance().servers().setFPPlayerName(""); SettingsCache::instance().servers().setFPPlayerName("");
} }
void ConnectionController::onForgotPasswordError(int responseCode) void ConnectionController::onForgotPasswordError()
{ {
// Rate limiting is retryable, so it must not clear the prefill. QMessageBox::warning(
bool keepPrefill = false; dialogParent, tr("Reset Password"),
QString message; tr("Failed to reset user account password, please contact the server operator to reset your password."));
switch (static_cast<Response::ResponseCode>(responseCode)) { SettingsCache::instance().servers().setFPHostName("");
case Response::RespTooManyRequests: SettingsCache::instance().servers().setFPPort("");
// Retryable: keep the host/port/user prefill so the user can try again. The window SettingsCache::instance().servers().setFPPlayerName("");
// 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() void ConnectionController::onPromptForgotPasswordReset()

View file

@ -87,7 +87,7 @@ private slots:
// Forgot-password flow // Forgot-password flow
void onForgotPasswordSuccess(); void onForgotPasswordSuccess();
void onForgotPasswordError(int responseCode); void onForgotPasswordError();
void onPromptForgotPasswordReset(); void onPromptForgotPasswordReset();
void onPromptForgotPasswordChallenge(); void onPromptForgotPasswordChallenge();

View file

@ -20,12 +20,11 @@
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list.h> #include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h> #include <libcockatrice/deck_list/tree/deck_list_card_node.h>
#include <libcockatrice/network/network_manager.h>
#include <version_string.h> #include <version_string.h>
DeckStatsInterface::DeckStatsInterface(QObject *parent) : QObject(parent) DeckStatsInterface::DeckStatsInterface(QObject *parent) : QObject(parent)
{ {
manager = NetworkManagerFactory::create(this); manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished, this, &DeckStatsInterface::queryFinished); connect(manager, &QNetworkAccessManager::finished, this, &DeckStatsInterface::queryFinished);
} }

View file

@ -26,12 +26,11 @@
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list.h> #include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h> #include <libcockatrice/deck_list/tree/deck_list_card_node.h>
#include <libcockatrice/network/network_manager.h>
#include <version_string.h> #include <version_string.h>
TappedOutInterface::TappedOutInterface(QObject *parent) : QObject(parent) TappedOutInterface::TappedOutInterface(QObject *parent) : QObject(parent)
{ {
manager = NetworkManagerFactory::create(this); manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished, this, &TappedOutInterface::queryFinished); connect(manager, &QNetworkAccessManager::finished, this, &TappedOutInterface::queryFinished);
} }

View file

@ -27,7 +27,6 @@
#include <functional> #include <functional>
#include <libcockatrice/card/database/card_database.h> #include <libcockatrice/card/database/card_database.h>
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/network/network_manager.h>
#include <libcockatrice/settings/download_settings.h> #include <libcockatrice/settings/download_settings.h>
#include <libcockatrice/settings/paths_settings.h> #include <libcockatrice/settings/paths_settings.h>
#include <tuple> #include <tuple>
@ -57,7 +56,7 @@ void SpoilerBackgroundUpdater::startSpoilerDownloadProcess(QString url, bool sav
void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults) void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults)
{ {
auto *nam = NetworkManagerFactory::create(this); auto *nam = new QNetworkAccessManager(this);
auto request = QNetworkRequest(url); auto request = QNetworkRequest(url);
request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING)); request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING));
QNetworkReply *reply = nam->get(request); QNetworkReply *reply = nam->get(request);

View file

@ -20,7 +20,6 @@
#include <QVariant> #include <QVariant>
#include <QVariantMap> #include <QVariantMap>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/network/network_manager.h>
#include <optional> #include <optional>
#include <qnamespace.h> #include <qnamespace.h>
#include <qprocessordetection.h> #include <qprocessordetection.h>
@ -63,7 +62,7 @@ std::optional<int> getProductVersionMajor()
} // namespace } // namespace
ReleaseChannel::ReleaseChannel() : netMan(NetworkManagerFactory::create(this)), response(nullptr), lastRelease(nullptr) ReleaseChannel::ReleaseChannel() : netMan(new QNetworkAccessManager(this)), response(nullptr), lastRelease(nullptr)
{ {
} }

View file

@ -9,11 +9,10 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QUrl> #include <QUrl>
#include <QVariant> #include <QVariant>
#include <libcockatrice/network/network_manager.h>
UpdateDownloader::UpdateDownloader(QObject *parent) : QObject(parent), response(nullptr) UpdateDownloader::UpdateDownloader(QObject *parent) : QObject(parent), response(nullptr)
{ {
netMan = NetworkManagerFactory::create(this); netMan = new QNetworkAccessManager(this);
} }
void UpdateDownloader::beginDownload(QUrl downloadUrl) void UpdateDownloader::beginDownload(QUrl downloadUrl)

View file

@ -19,6 +19,7 @@ inline Q_LOGGING_CATEGORY(SettingsCacheLog, "settings_cache");
class ReleaseChannel; class ReleaseChannel;
#define DEFAULT_LANG_NAME "English"
#define CLIENT_INFO_NOT_SET "notset" #define CLIENT_INFO_NOT_SET "notset"
#define DEFAULT_FONT_SIZE 12 #define DEFAULT_FONT_SIZE 12

View file

@ -2,18 +2,13 @@
#define COCKATRICE_SETTINGS_CARD_PREFERENCE_PROVIDER_H #define COCKATRICE_SETTINGS_CARD_PREFERENCE_PROVIDER_H
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include <QObject>
#include <libcockatrice/interfaces/interface_card_preference_provider.h> #include <libcockatrice/interfaces/interface_card_preference_provider.h>
#include <libcockatrice/settings/card_override_settings.h> #include <libcockatrice/settings/card_override_settings.h>
#include <libcockatrice/settings/cards_display_settings.h> #include <libcockatrice/settings/cards_display_settings.h>
class SettingsCardPreferenceProvider : public QObject, public ICardPreferenceProvider class SettingsCardPreferenceProvider : public ICardPreferenceProvider
{ {
public: public:
explicit SettingsCardPreferenceProvider(QObject *parent = nullptr) : QObject(parent)
{
}
[[nodiscard]] QString getCardPreferenceOverride(const QString &cardName) const override [[nodiscard]] QString getCardPreferenceOverride(const QString &cardName) const override
{ {
return SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardName); return SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardName);

View file

@ -1,5 +1,6 @@
#include "deck_filter_string.h" #include "deck_filter_string.h"
#include "../interface/deck_loader/loaded_deck.h"
#include "libcockatrice/card/card_localization.h" #include "libcockatrice/card/card_localization.h"
#include "libcockatrice/card/database/card_database_querier.h" #include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/deck_list/deck_list.h" #include "libcockatrice/deck_list/deck_list.h"
@ -14,7 +15,6 @@
#include <algorithm> #include <algorithm>
#include <any> #include <any>
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/filters/filter_string.h> #include <libcockatrice/filters/filter_string.h>
#include <libcockatrice/utility/peglib.h> #include <libcockatrice/utility/peglib.h>
#include <mutex> #include <mutex>

View file

@ -11,7 +11,6 @@
#include <QString> #include <QString>
#include <functional> #include <functional>
#include <libcockatrice/card/card_localization.h> #include <libcockatrice/card/card_localization.h>
#include <libcockatrice/deck_list/loaded_deck.h>
struct CardSearchLanguage; struct CardSearchLanguage;
struct LoadedDeck; struct LoadedDeck;

View file

@ -13,7 +13,6 @@
#include <QVariant> #include <QVariant>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/filters/filter_card.h> #include <libcockatrice/filters/filter_card.h>
#include <libcockatrice/utility/qt_utils.h>
#include <stddef.h> #include <stddef.h>
FilterBuilder::FilterBuilder(QWidget *parent) : QWidget(parent) FilterBuilder::FilterBuilder(QWidget *parent) : QWidget(parent)
@ -40,8 +39,9 @@ FilterBuilder::FilterBuilder(QWidget *parent) : QWidget(parent)
edit->setClearButtonEnabled(true); edit->setClearButtonEnabled(true);
edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QGridLayout *layout = QtUtils::makeLayout<QGridLayout>(); QGridLayout *layout = new QGridLayout;
layout->setObjectName("layout"); layout->setObjectName("layout");
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(typeCombo, 0, 0, 1, 2); layout->addWidget(typeCombo, 0, 0, 1, 2);
layout->addWidget(filterCombo, 0, 2, 1, 2); layout->addWidget(filterCombo, 0, 2, 1, 2);

View file

@ -18,6 +18,8 @@ AbstractGame::AbstractGame(QObject *_parent) : QObject(_parent)
{ {
gameMetaInfo = new GameMetaInfo(this); gameMetaInfo = new GameMetaInfo(this);
gameEventHandler = new GameEventHandler(this); gameEventHandler = new GameEventHandler(this);
activeCard = nullptr;
} }
bool AbstractGame::isHost() const bool AbstractGame::isHost() const
@ -46,7 +48,12 @@ void AbstractGame::loadReplay(const GameReplay *replay)
gameMetaInfo->setSpectatorsOmniscient(true); gameMetaInfo->setSpectatorsOmniscient(true);
} }
CardState *AbstractGame::getCard(int playerId, const QString &zoneName, int cardId) const void AbstractGame::setActiveCard(CardItem *card)
{
activeCard = card;
}
CardItem *AbstractGame::getCard(int playerId, const QString &zoneName, int cardId) const
{ {
PlayerLogic *player = playerManager->getPlayer(playerId); PlayerLogic *player = playerManager->getPlayer(playerId);
if (!player) { if (!player) {

View file

@ -11,14 +11,14 @@
#include <QString> #include <QString>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class CardItem;
class AbstractClient; class AbstractClient;
class CardState;
class GameEventHandler; class GameEventHandler;
class GameMetaInfo; class GameMetaInfo;
class GameReplay; class GameReplay;
class GameState; class GameState;
class PlayerManager; class PlayerManager;
class CardState;
class AbstractGame : public QObject class AbstractGame : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -30,6 +30,7 @@ public:
GameState *gameState; GameState *gameState;
GameEventHandler *gameEventHandler; GameEventHandler *gameEventHandler;
PlayerManager *playerManager; PlayerManager *playerManager;
CardItem *activeCard;
GameMetaInfo *getGameMetaInfo() GameMetaInfo *getGameMetaInfo()
{ {
@ -57,7 +58,13 @@ public:
void loadReplay(const GameReplay *replay); void loadReplay(const GameReplay *replay);
CardState *getCard(int playerId, const QString &zoneName, int cardId) const; CardItem *getCard(int playerId, const QString &zoneName, int cardId) const;
void setActiveCard(CardItem *card);
CardItem *getActiveCard() const
{
return activeCard;
}
}; };
#endif // COCKATRICE_ABSTRACT_GAME_H #endif // COCKATRICE_ABSTRACT_GAME_H

View file

@ -1,9 +1,9 @@
#include "arrow_registry.h" #include "arrow_registry.h"
#include "arrow_item.h" #include "../game_graphics/board/arrow_item.h"
#include "board/arrow_data.h"
#include <QSharedPointer> #include <QSharedPointer>
#include <libcockatrice/game/board/arrow_data.h>
void ArrowRegistry::insert(QSharedPointer<ArrowData> data, ArrowItem *arrow) void ArrowRegistry::insert(QSharedPointer<ArrowData> data, ArrowItem *arrow)
{ {

View file

@ -4,7 +4,6 @@
#include <QList> #include <QList>
#include <QMap> #include <QMap>
#include <QSet> #include <QSet>
#include <libcockatrice/game/board/arrow_data.h>
class ArrowItem; class ArrowItem;
struct ArrowData; struct ArrowData;

View file

@ -1,6 +1,7 @@
#include "card_list.h" #include "card_list.h"
#include "card_state.h" #include "../../game_graphics/board/card_item.h"
#include "libcockatrice/card/printing/exact_card.h"
#include <QChar> #include <QChar>
#include <QDebug> #include <QDebug>
@ -10,26 +11,26 @@
#include <compare> #include <compare>
#include <libcockatrice/card/card_info.h> #include <libcockatrice/card/card_info.h>
CardList::CardList(bool _contentsKnown) : QList<CardState *>(), contentsKnown(_contentsKnown) CardList::CardList(bool _contentsKnown) : QList<CardItem *>(), contentsKnown(_contentsKnown)
{ {
} }
/** /**
* @brief Finds the CardState with the given id in the list. * @brief Finds the CardItem with the given id in the list.
* If contentsKnown is false, then this just returns the first element of the list. * If contentsKnown is false, then this just returns the first element of the list.
* *
* @param cardId The id of the card to find. * @param cardId The id of the card to find.
* *
* @returns A pointer to the CardState, or a nullptr if not found. * @returns A pointer to the CardItem, or a nullptr if not found.
*/ */
CardState *CardList::findCard(const int cardId) const CardItem *CardList::findCard(const int cardId) const
{ {
if (!contentsKnown && !empty()) { if (!contentsKnown && !empty()) {
return at(0); return at(0);
} else { } else {
for (auto *cardState : *this) { for (auto *cardItem : *this) {
if (cardState->getId() == cardId) { if (cardItem->getId() == cardId) {
return cardState; return cardItem;
} }
} }
} }
@ -37,7 +38,7 @@ CardState *CardList::findCard(const int cardId) const
} }
/** /**
* @brief sorts the list by using string comparison on properties extracted from the CardState * @brief sorts the list by using string comparison on properties extracted from the CardItem
* The cards are compared using each property in order. * The cards are compared using each property in order.
* If two cards have the same value for a property, then the next property in the list is used. * If two cards have the same value for a property, then the next property in the list is used.
* *
@ -50,7 +51,7 @@ void CardList::sortBy(const QList<SortOption> &option)
return; return;
} }
auto comparator = [&option](CardState *a, CardState *b) { auto comparator = [&option](CardItem *a, CardItem *b) {
for (auto prop : option) { for (auto prop : option) {
auto extractor = getExtractorFor(prop); auto extractor = getExtractorFor(prop);
QString t1 = extractor(a); QString t1 = extractor(a);
@ -115,26 +116,26 @@ static QString getColorSortString(const CardInfo &c, bool appendAtEnd)
} }
/** /**
* @brief returns the function that extracts the given property from the CardState. * @brief returns the function that extracts the given property from the CardItem.
*/ */
std::function<QString(CardState *)> CardList::getExtractorFor(SortOption option) std::function<QString(CardItem *)> CardList::getExtractorFor(SortOption option)
{ {
switch (option) { switch (option) {
case NoSort: case NoSort:
return [](CardState *) { return ""; }; return [](CardItem *) { return ""; };
case SortByMainType: case SortByMainType:
return [](CardState *c) { return c->getCardInfo().getMainCardType(); }; return [](CardItem *c) { return c->getCardInfo().getMainCardType(); };
case SortByManaValue: case SortByManaValue:
// getCmc returns the int as a string. We pad with 0's so that string comp also works on it // getCmc returns the int as a string. We pad with 0's so that string comp also works on it
return [](CardState *c) { return c->getCard() ? c->getCardInfo().getCmc().rightJustified(4, '0') : ""; }; return [](CardItem *c) { return c->getCard() ? c->getCardInfo().getCmc().rightJustified(4, '0') : ""; };
case SortByColorGrouping: case SortByColorGrouping:
return [](CardState *c) { return c->getCard() ? getColorSortString(c->getCardInfo(), false) : ""; }; return [](CardItem *c) { return c->getCard() ? getColorSortString(c->getCardInfo(), false) : ""; };
case SortByName: case SortByName:
return [](CardState *c) { return c->getName(); }; return [](CardItem *c) { return c->getName(); };
case SortByType: case SortByType:
return [](CardState *c) { return c->getCardInfo().getCardType(); }; return [](CardItem *c) { return c->getCardInfo().getCardType(); };
case SortByManaCost: case SortByManaCost:
return [](CardState *c) { return [](CardItem *c) {
if (!c->getCard()) { if (!c->getCard()) {
return QString(); return QString();
} }
@ -146,18 +147,18 @@ std::function<QString(CardState *)> CardList::getExtractorFor(SortOption option)
return QString("%1%2").arg(info.getCmc(), 4, QChar('0')).arg(info.getManaCost()); return QString("%1%2").arg(info.getCmc(), 4, QChar('0')).arg(info.getManaCost());
}; };
case SortByColors: case SortByColors:
return [](CardState *c) { return c->getCard() ? getColorSortString(c->getCardInfo(), true) : ""; }; return [](CardItem *c) { return c->getCard() ? getColorSortString(c->getCardInfo(), true) : ""; };
case SortByPt: case SortByPt:
// do the same padding trick as above // do the same padding trick as above
return return
[](CardState *c) { return c->getCard() ? c->getCardInfo().getPowTough().rightJustified(10, '0') : ""; }; [](CardItem *c) { return c->getCard() ? c->getCardInfo().getPowTough().rightJustified(10, '0') : ""; };
case SortBySet: case SortBySet:
return [](CardState *c) { return c->getCardInfo().getSetsNames(); }; return [](CardItem *c) { return c->getCardInfo().getSetsNames(); };
case SortByPrinting: case SortByPrinting:
return [](CardState *c) { return c->getProviderId(); }; return [](CardItem *c) { return c->getProviderId(); };
} }
// this line should never be reached // this line should never be reached
qCWarning(CardListLog) << "cardlist.cpp: Could not find extractor for SortOption" << option; qCWarning(CardListLog) << "cardlist.cpp: Could not find extractor for SortOption" << option;
return [](CardState *) { return ""; }; return [](CardItem *) { return ""; };
} }

View file

@ -14,9 +14,9 @@
inline Q_LOGGING_CATEGORY(CardListLog, "card_list"); inline Q_LOGGING_CATEGORY(CardListLog, "card_list");
class CardState; class CardItem;
class CardList : public QList<CardState *> class CardList : public QList<CardItem *>
{ {
protected: protected:
bool contentsKnown; bool contentsKnown;
@ -43,7 +43,7 @@ public:
SortByPrinting SortByPrinting
}; };
explicit CardList(bool _contentsKnown); explicit CardList(bool _contentsKnown);
CardState *findCard(const int cardId) const; CardItem *findCard(const int cardId) const;
bool getContentsKnown() const bool getContentsKnown() const
{ {
return contentsKnown; return contentsKnown;
@ -51,7 +51,7 @@ public:
void sortBy(const QList<SortOption> &options); void sortBy(const QList<SortOption> &options);
static std::function<QString(CardState *)> getExtractorFor(SortOption option); static std::function<QString(CardItem *)> getExtractorFor(SortOption option);
}; };
#endif #endif

View file

@ -0,0 +1,113 @@
#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();
}

View file

@ -0,0 +1,106 @@
#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

View file

@ -16,16 +16,13 @@ Game::Game(QObject *_parent,
bool isLocalGame, bool isLocalGame,
QList<AbstractClient *> &_clients, QList<AbstractClient *> &_clients,
const Event_GameJoined &event, const Event_GameJoined &event,
const QMap<int, QString> &_roomGameTypes, const QMap<int, QString> &_roomGameTypes)
IInterfaceSettingsProvider *interfaceSettings,
ICardPreferenceProvider *cardPreferences)
: AbstractGame(_parent) : AbstractGame(_parent)
{ {
gameMetaInfo->setFromProto(event.game_info()); gameMetaInfo->setFromProto(event.game_info());
gameMetaInfo->setRoomGameTypes(_roomGameTypes); gameMetaInfo->setRoomGameTypes(_roomGameTypes);
gameState = new GameState(this, 0, event.host_id(), isLocalGame, _clients, false, event.resuming(), -1, false); gameState = new GameState(this, 0, event.host_id(), isLocalGame, _clients, false, event.resuming(), -1, false);
connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged); connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged);
playerManager = new PlayerManager(this, event.player_id(), event.judge(), event.spectator(), interfaceSettings, playerManager = new PlayerManager(this, event.player_id(), event.judge(), event.spectator());
cardPreferences);
gameMetaInfo->setStarted(false); gameMetaInfo->setStarted(false);
} }

View file

@ -18,9 +18,6 @@ class QString;
template <class Key, class T> class QMap; template <class Key, class T> class QMap;
template <typename T> class QList; template <typename T> class QList;
class ICardPreferenceProvider;
class IInterfaceSettingsProvider;
class Game : public AbstractGame class Game : public AbstractGame
{ {
Q_OBJECT Q_OBJECT
@ -30,9 +27,7 @@ public:
bool isLocalGame, bool isLocalGame,
QList<AbstractClient *> &_clients, QList<AbstractClient *> &_clients,
const Event_GameJoined &event, const Event_GameJoined &event,
const QMap<int, QString> &_roomGameTypes, const QMap<int, QString> &_roomGameTypes);
IInterfaceSettingsProvider *interfaceSettings = nullptr,
ICardPreferenceProvider *cardPreferences = nullptr);
}; };
#endif // COCKATRICE_GAME_H #endif // COCKATRICE_GAME_H

View file

@ -28,20 +28,12 @@
#include <libcockatrice/network/client/abstract/abstract_client.h> #include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/get_pb_extension.h> #include <libcockatrice/protocol/get_pb_extension.h>
#include <libcockatrice/protocol/pb/command_concede.pb.h> #include <libcockatrice/protocol/pb/command_concede.pb.h>
#include <libcockatrice/protocol/pb/command_deck_select.pb.h>
#include <libcockatrice/protocol/pb/command_delete_arrow.pb.h> #include <libcockatrice/protocol/pb/command_delete_arrow.pb.h>
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
#include <libcockatrice/protocol/pb/command_game_say.pb.h> #include <libcockatrice/protocol/pb/command_game_say.pb.h>
#include <libcockatrice/protocol/pb/command_leave_game.pb.h> #include <libcockatrice/protocol/pb/command_leave_game.pb.h>
#include <libcockatrice/protocol/pb/command_next_turn.pb.h> #include <libcockatrice/protocol/pb/command_next_turn.pb.h>
#include <libcockatrice/protocol/pb/command_ready_start.pb.h>
#include <libcockatrice/protocol/pb/command_reverse_turn.pb.h> #include <libcockatrice/protocol/pb/command_reverse_turn.pb.h>
#include <libcockatrice/protocol/pb/command_set_active_phase.pb.h> #include <libcockatrice/protocol/pb/command_set_active_phase.pb.h>
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
#include <libcockatrice/protocol/pb/command_set_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/protocol/pb/context_connection_state_changed.pb.h>
#include <libcockatrice/protocol/pb/context_deck_select.pb.h> #include <libcockatrice/protocol/pb/context_deck_select.pb.h>
#include <libcockatrice/protocol/pb/event_game_closed.pb.h> #include <libcockatrice/protocol/pb/event_game_closed.pb.h>
#include <libcockatrice/protocol/pb/event_game_host_changed.pb.h> #include <libcockatrice/protocol/pb/event_game_host_changed.pb.h>
@ -56,7 +48,6 @@
#include <libcockatrice/protocol/pb/event_set_active_player.pb.h> #include <libcockatrice/protocol/pb/event_set_active_player.pb.h>
#include <libcockatrice/protocol/pb/game_event_container.pb.h> #include <libcockatrice/protocol/pb/game_event_container.pb.h>
#include <libcockatrice/protocol/pending_command.h> #include <libcockatrice/protocol/pending_command.h>
#include <libcockatrice/utility/zone_names.h>
#include <string> #include <string>
#include <utility> #include <utility>
@ -232,89 +223,6 @@ void GameEventHandler::handleActivePhaseChanged(int phase)
sendGameCommand(cmd); sendGameCommand(cmd);
} }
void GameEventHandler::handleUntapAll()
{
Command_SetCardAttr cmd;
cmd.set_zone(ZoneNames::TABLE);
cmd.set_attribute(AttrTapped);
cmd.set_attr_value("0");
sendGameCommand(cmd);
}
void GameEventHandler::handleDrawCard()
{
Command_DrawCards cmd;
cmd.set_number(1);
sendGameCommand(cmd);
}
PendingCommand *GameEventHandler::prepareDeckSelect(const QString &deckList)
{
Command_DeckSelect cmd;
cmd.set_deck(deckList.toStdString());
return prepareGameCommand(cmd);
}
PendingCommand *GameEventHandler::prepareDeckSelectId(int deckId)
{
Command_DeckSelect cmd;
cmd.set_deck_id(deckId);
return prepareGameCommand(cmd);
}
PendingCommand *GameEventHandler::prepareSetPlaymat(const PlaymatInfo &playmat)
{
Command_SetPlaymat cmd;
auto *pp = cmd.mutable_playmat_params();
pp->set_card_name(playmat.card.name.toStdString());
pp->set_card_provider_id(playmat.card.providerId.toStdString());
pp->set_margin_pct_l(playmat.params.marginPctL);
pp->set_margin_pct_r(playmat.params.marginPctR);
pp->set_vertical_offset(playmat.params.verticalOffset);
pp->set_zoom(playmat.params.zoom);
return prepareGameCommand(cmd);
}
void GameEventHandler::sendReadyStart(bool ready, int playerId)
{
Command_ReadyStart cmd;
cmd.set_ready(ready);
sendGameCommand(cmd, playerId);
}
void GameEventHandler::sendForceStart(int playerId)
{
Command_ReadyStart cmd;
cmd.set_force_start(true);
cmd.set_ready(true);
sendGameCommand(cmd, playerId);
}
void GameEventHandler::sendSetSideboardLock(bool locked, int playerId)
{
Command_SetSideboardLock cmd;
cmd.set_locked(locked);
sendGameCommand(cmd, playerId);
}
void GameEventHandler::sendSetSideboardPlan(const QList<MoveCard_ToZone> &plan, int playerId)
{
Command_SetSideboardPlan cmd;
for (const auto &zone : plan) {
cmd.add_move_list()->CopyFrom(zone);
}
sendGameCommand(cmd, playerId);
}
void GameEventHandler::handleGameLeft() void GameEventHandler::handleGameLeft()
{ {
sendGameCommand(Command_LeaveGame()); sendGameCommand(Command_LeaveGame());

View file

@ -29,14 +29,13 @@
// GameEventContext, ServerInfo_Player and ServerInfo_User are passed by value // GameEventContext, ServerInfo_Player and ServerInfo_User are passed by value
// to signals and slots, so the moc-generated code needs the complete types. // to signals and slots, so the moc-generated code needs the complete types.
#include <libcockatrice/protocol/pb/game_event_context.pb.h> #include <libcockatrice/protocol/pb/game_event_context.pb.h>
#include <libcockatrice/protocol/pb/move_card_to_zone.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_player.pb.h> #include <libcockatrice/protocol/pb/serverinfo_player.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h> #include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <libcockatrice/utility/playmat_params.h>
class AbstractClient; class AbstractClient;
class AbstractGame; class AbstractGame;
class GameEventContainer; class GameEventContainer;
class GameEventContext;
class PendingCommand; class PendingCommand;
class PlayerLogic; class PlayerLogic;
class Response; class Response;
@ -128,12 +127,6 @@ public:
*/ */
void handleActivePhaseChanged(int phase); void handleActivePhaseChanged(int phase);
/** @brief Request untapping every permanent on the table. */
void handleUntapAll();
/** @brief Request drawing a single card. */
void handleDrawCard();
/** @brief Leave the current game session. */ /** @brief Leave the current game session. */
void handleGameLeft(); void handleGameLeft();
@ -152,70 +145,6 @@ public:
void handleArrowDeletion(int creatorId, int arrowId); void handleArrowDeletion(int creatorId, int arrowId);
void handleArrowDeletionFinished(const Response &response, int creatorId, int arrowId); void handleArrowDeletionFinished(const Response &response, int creatorId, int arrowId);
/**
* @brief Prepare a deck selection from its native deck list text.
*
* The returned command is not sent yet, so the caller can connect to
* PendingCommand::finished before handing it to sendGameCommand().
*
* @param deckList Serialized deck list.
* @return The pending command awaiting the deck download response.
*/
PendingCommand *prepareDeckSelect(const QString &deckList);
/**
* @brief Prepare a deck selection for a deck stored on the server.
*
* The returned command is not sent yet, so the caller can connect to
* PendingCommand::finished before handing it to sendGameCommand().
*
* @param deckId Server-side deck identifier.
* @return The pending command awaiting the deck download response.
*/
PendingCommand *prepareDeckSelectId(int deckId);
/**
* @brief Build the playmat command for a player without sending it.
*
* The returned command is not sent yet, so the caller can connect to
* PendingCommand::finished before handing it to sendGameCommand().
*
* @param playmat Resolved playmat card and positioning parameters.
* @return The pending command awaiting the playmat response.
*/
PendingCommand *prepareSetPlaymat(const PlaymatInfo &playmat);
/**
* @brief Set a player's ready state.
*
* @param ready Whether the player is ready.
* @param playerId Player the state applies to.
*/
void sendReadyStart(bool ready, int playerId);
/**
* @brief Force-start the game, kicking every player who is not ready.
*
* @param playerId Player issuing the force start.
*/
void sendForceStart(int playerId);
/**
* @brief Lock or unlock a player's sideboard.
*
* @param locked Whether the sideboard is locked.
* @param playerId Player the lock applies to.
*/
void sendSetSideboardLock(bool locked, int playerId);
/**
* @brief Send a player's sideboard plan.
*
* @param plan Cards to move between zones when leaving sideboarding.
* @param playerId Player the plan belongs to.
*/
void sendSetSideboardPlan(const QList<MoveCard_ToZone> &plan, int playerId);
/** @} */ /** @} */
/** @name Incoming event processing /** @name Incoming event processing

View file

@ -1,8 +1,13 @@
#include "player_actions.h" #include "player_actions.h"
#include "../../client/settings/cache_settings.h"
#include "../../game_graphics/board/card_item.h"
#include "../../game_graphics/dialogs/dlg_create_token.h"
#include "../../game_graphics/dialogs/dlg_move_top_cards_until.h"
#include "../../game_graphics/player/card_menu_action_type.h"
#include "../../game_graphics/zones/table_zone.h"
#include "../../interface/deck_loader/loaded_deck.h"
#include "../abstract_game.h" #include "../abstract_game.h"
#include "../board/arrow_data.h"
#include "../board/card_state.h"
#include "../board/counter_state.h" #include "../board/counter_state.h"
#include "../game_event_handler.h" #include "../game_event_handler.h"
#include "../zones/card_zone_logic.h" #include "../zones/card_zone_logic.h"
@ -26,6 +31,8 @@
#include <QChar> #include <QChar>
#include <QColor> #include <QColor>
#include <QFlags> #include <QFlags>
#include <QInputDialog>
#include <QKeyEvent>
#include <QList> #include <QList>
#include <QMap> #include <QMap>
#include <QMapIterator> #include <QMapIterator>
@ -44,15 +51,10 @@
#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/port.h>
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/relation/card_relation.h> #include <libcockatrice/card/relation/card_relation.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/interfaces/interface_card_preference_provider.h>
#include <libcockatrice/interfaces/interface_interface_settings_provider.h>
#include <libcockatrice/protocol/pb/command_attach_card.pb.h> #include <libcockatrice/protocol/pb/command_attach_card.pb.h>
#include <libcockatrice/protocol/pb/command_change_zone_properties.pb.h> #include <libcockatrice/protocol/pb/command_change_zone_properties.pb.h>
#include <libcockatrice/protocol/pb/command_create_arrow.pb.h>
#include <libcockatrice/protocol/pb/command_create_token.pb.h> #include <libcockatrice/protocol/pb/command_create_token.pb.h>
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h> #include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
#include <libcockatrice/protocol/pb/command_dump_zone.pb.h>
#include <libcockatrice/protocol/pb/command_flip_card.pb.h> #include <libcockatrice/protocol/pb/command_flip_card.pb.h>
#include <libcockatrice/protocol/pb/command_game_say.pb.h> #include <libcockatrice/protocol/pb/command_game_say.pb.h>
#include <libcockatrice/protocol/pb/command_inc_counter.pb.h> #include <libcockatrice/protocol/pb/command_inc_counter.pb.h>
@ -62,12 +64,11 @@
#include <libcockatrice/protocol/pb/command_roll_die.pb.h> #include <libcockatrice/protocol/pb/command_roll_die.pb.h>
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h> #include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
#include <libcockatrice/protocol/pb/command_set_card_counter.pb.h> #include <libcockatrice/protocol/pb/command_set_card_counter.pb.h>
#include <libcockatrice/protocol/pb/command_set_counter.pb.h>
#include <libcockatrice/protocol/pb/command_shuffle.pb.h> #include <libcockatrice/protocol/pb/command_shuffle.pb.h>
#include <libcockatrice/protocol/pb/command_undo_draw.pb.h> #include <libcockatrice/protocol/pb/command_undo_draw.pb.h>
#include <libcockatrice/protocol/pb/context_move_card.pb.h> #include <libcockatrice/settings/card_override_settings.h>
#include <libcockatrice/settings/interface_settings.h>
#include <libcockatrice/utility/clamped_arithmetic.h> #include <libcockatrice/utility/clamped_arithmetic.h>
#include <libcockatrice/utility/color.h>
#include <libcockatrice/utility/counter_limits.h> #include <libcockatrice/utility/counter_limits.h>
#include <libcockatrice/utility/expression.h> #include <libcockatrice/utility/expression.h>
#include <libcockatrice/utility/zone_names.h> #include <libcockatrice/utility/zone_names.h>
@ -79,11 +80,8 @@
// milliseconds in between triggers of the move top cards until action // milliseconds in between triggers of the move top cards until action
static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100; static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100;
PlayerActions::PlayerActions(PlayerLogic *_player, PlayerActions::PlayerActions(PlayerLogic *_player)
IInterfaceSettingsProvider *_interfaceSettings, : QObject(_player), player(_player), lastTokenTableRow(0), movingCardsUntil(false)
ICardPreferenceProvider *_cardPreferences)
: QObject(_player), player(_player), interfaceSettings(_interfaceSettings), cardPreferences(_cardPreferences),
lastTokenTableRow(0), movingCardsUntil(false)
{ {
connect(this, &PlayerActions::requestZoneViewToggle, player, &PlayerLogic::onRequestZoneViewToggle); connect(this, &PlayerActions::requestZoneViewToggle, player, &PlayerLogic::onRequestZoneViewToggle);
@ -93,27 +91,7 @@ PlayerActions::PlayerActions(PlayerLogic *_player,
connect(moveTopCardTimer, &QTimer::timeout, [this]() { actMoveTopCardToPlay(); }); connect(moveTopCardTimer, &QTimer::timeout, [this]() { actMoveTopCardToPlay(); });
} }
bool PlayerActions::getPlayToStack() const void PlayerActions::playCard(CardItem *card, bool faceDown)
{
return interfaceSettings ? interfaceSettings->getPlayToStack() : true;
}
int PlayerActions::getStartingHandSize() const
{
return interfaceSettings ? interfaceSettings->getStartingHandSize() : 7;
}
bool PlayerActions::getAnnotateTokens() const
{
return interfaceSettings ? interfaceSettings->getAnnotateTokens() : false;
}
QString PlayerActions::getCardPreferenceOverride(const QString &cardName) const
{
return cardPreferences ? cardPreferences->getCardPreferenceOverride(cardName) : QString();
}
void PlayerActions::playCard(CardState *card, bool faceDown)
{ {
if (card == nullptr) { if (card == nullptr) {
return; return;
@ -134,7 +112,7 @@ void PlayerActions::playCard(CardState *card, bool faceDown)
const CardInfo &info = exactCard.getInfo(); const CardInfo &info = exactCard.getInfo();
int tableRow = info.getUiAttributes().tableRow; int tableRow = info.getUiAttributes().tableRow;
bool playToStack = getPlayToStack(); bool playToStack = SettingsCache::instance().userInterface().getPlayToStack();
QString currentZone = card->getZone()->getName(); QString currentZone = card->getZone()->getName();
if (!faceDown && currentZone == ZoneNames::STACK && tableRow == 3) { if (!faceDown && currentZone == ZoneNames::STACK && tableRow == 3) {
cmd.set_target_zone(ZoneNames::GRAVE); cmd.set_target_zone(ZoneNames::GRAVE);
@ -147,7 +125,7 @@ void PlayerActions::playCard(CardState *card, bool faceDown)
cmd.set_y(0); cmd.set_y(0);
} else { } else {
tableRow = faceDown ? 2 : info.getUiAttributes().tableRow; tableRow = faceDown ? 2 : info.getUiAttributes().tableRow;
QPoint gridPoint = QPoint(-1, TableZoneLogic::tableRowToGridY(tableRow)); QPoint gridPoint = QPoint(-1, TableZone::tableRowToGridY(tableRow));
cardToMove->set_face_down(faceDown); cardToMove->set_face_down(faceDown);
if (!faceDown) { if (!faceDown) {
cardToMove->set_pt(info.getPowTough().toStdString()); cardToMove->set_pt(info.getPowTough().toStdString());
@ -166,7 +144,7 @@ void PlayerActions::playCard(CardState *card, bool faceDown)
* Like {@link PlayerActions::playCard}, but forces the card to be played to the table zone. * Like {@link PlayerActions::playCard}, but forces the card to be played to the table zone.
* Cards with tablerow 3 (the stack) will be played to tablerow 1 (the noncreatures row). * Cards with tablerow 3 (the stack) will be played to tablerow 1 (the noncreatures row).
*/ */
void PlayerActions::playCardToTable(const CardState *card, bool faceDown) void PlayerActions::playCardToTable(const CardItem *card, bool faceDown)
{ {
if (card == nullptr) { if (card == nullptr) {
return; return;
@ -187,7 +165,7 @@ void PlayerActions::playCardToTable(const CardState *card, bool faceDown)
const CardInfo &info = exactCard.getInfo(); const CardInfo &info = exactCard.getInfo();
int tableRow = faceDown ? 2 : info.getUiAttributes().tableRow; int tableRow = faceDown ? 2 : info.getUiAttributes().tableRow;
QPoint gridPoint = QPoint(-1, TableZoneLogic::tableRowToGridY(tableRow)); QPoint gridPoint = QPoint(-1, TableZone::tableRowToGridY(tableRow));
cardToMove->set_face_down(faceDown); cardToMove->set_face_down(faceDown);
if (!faceDown) { if (!faceDown) {
cardToMove->set_pt(info.getPowTough().toStdString()); cardToMove->set_pt(info.getPowTough().toStdString());
@ -199,60 +177,6 @@ void PlayerActions::playCardToTable(const CardState *card, bool faceDown)
sendGameCommand(cmd); sendGameCommand(cmd);
} }
void PlayerActions::attachCards(CardState *startCard, const CardState *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) {
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());
sendGameCommand(cmd);
}
void PlayerActions::createArrow(CardState *startCard, const ArrowData &arrow, int deleteInPhase)
{
Command_CreateArrow cmd;
cmd.mutable_arrow_color()->CopyFrom(convertQColorToColor(arrow.color));
cmd.set_start_player_id(arrow.startPlayerId);
cmd.set_start_zone(arrow.startZone.toStdString());
cmd.set_start_card_id(arrow.startCardId);
cmd.set_target_player_id(arrow.targetPlayerId);
if (!arrow.isPlayerTargeted()) {
cmd.set_target_zone(arrow.targetZone.toStdString());
cmd.set_target_card_id(arrow.targetCardId);
}
// if the card is in hand then it has just been moved to the stack or table as part of drawing the arrow
if (arrow.startZone == ZoneNames::HAND) {
CardInfoPtr ci = startCard->getCard().getCardPtr();
bool playToStack = 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);
}
sendGameCommand(cmd);
}
void PlayerActions::actViewLibrary() void PlayerActions::actViewLibrary()
{ {
emit requestZoneViewToggle(ZoneNames::DECK, -1); emit requestZoneViewToggle(ZoneNames::DECK, -1);
@ -431,7 +355,7 @@ void PlayerActions::actDrawCard()
void PlayerActions::actRequestMulliganDialog() void PlayerActions::actRequestMulliganDialog()
{ {
int startSize = getStartingHandSize(); int startSize = SettingsCache::instance().userInterface().getStartingHandSize();
int handSize = player->getHandZone()->getCards().size(); int handSize = player->getHandZone()->getCards().size();
int deckSize = player->getDeckZone()->getCards().size() + handSize; int deckSize = player->getDeckZone()->getCards().size() + handSize;
@ -447,7 +371,7 @@ void PlayerActions::actMulligan(int number)
} }
doMulligan(number); doMulligan(number);
emit startingHandSizeChosen(number); SettingsCache::instance().userInterface().setStartingHandSize(number);
} }
void PlayerActions::actMulliganSameSize() void PlayerActions::actMulliganSameSize()
@ -625,7 +549,7 @@ void PlayerActions::moveTopCardsUntil(const QString &expr, MoveTopCardsUntilOpti
} }
} }
void PlayerActions::moveOneCardUntil(CardState *card) void PlayerActions::moveOneCardUntil(CardItem *card)
{ {
moveTopCardTimer->stop(); moveTopCardTimer->stop();
@ -817,6 +741,61 @@ void PlayerActions::actMoveBottomCardToTop()
sendGameCommand(cmd); sendGameCommand(cmd);
} }
/**
* Selects all cards in the given zone.
*
* @param zone The zone to select from
* @param filter A predicate to filter which cards are selected. Defaults to always returning true.
*/
static void selectCardsInZone(
const CardZoneLogic *zone,
std::function<bool(const CardItem *)> filter = [](const CardItem *) { return true; })
{
if (!zone) {
return;
}
for (auto &cardItem : zone->getCards()) {
if (cardItem && filter(cardItem)) {
cardItem->setSelected(true);
}
}
}
void PlayerActions::actSelectAll()
{
const CardItem *card = player->getGame()->getActiveCard();
if (!card) {
return;
}
selectCardsInZone(card->getZone());
}
void PlayerActions::actSelectRow()
{
const CardItem *card = player->getGame()->getActiveCard();
if (!card) {
return;
}
auto isSameRow = [card](const CardItem *cardItem) {
return qAbs(card->scenePos().y() - cardItem->scenePos().y()) < 50;
};
selectCardsInZone(card->getZone(), isSameRow);
}
void PlayerActions::actSelectColumn()
{
const CardItem *card = player->getGame()->getActiveCard();
if (!card) {
return;
}
auto isSameColumn = [card](const CardItem *cardItem) { return cardItem->x() == card->x(); };
selectCardsInZone(card->getZone(), isSameColumn);
}
void PlayerActions::actDrawBottomCard() void PlayerActions::actDrawBottomCard()
{ {
if (player->getDeckZone()->getCards().empty()) { if (player->getDeckZone()->getCards().empty()) {
@ -950,7 +929,7 @@ void PlayerActions::actCreateToken(TokenInfo tokenToCreate)
if (correctedCard) { if (correctedCard) {
lastTokenInfo.name = correctedCard.getName(); lastTokenInfo.name = correctedCard.getName();
int tableRow = lastTokenInfo.faceDown ? 2 : correctedCard.getInfo().getUiAttributes().tableRow; int tableRow = lastTokenInfo.faceDown ? 2 : correctedCard.getInfo().getUiAttributes().tableRow;
lastTokenTableRow = TableZoneLogic::tableRowToGridY(tableRow); lastTokenTableRow = TableZone::tableRowToGridY(tableRow);
if (lastTokenInfo.pt.isEmpty()) { if (lastTokenInfo.pt.isEmpty()) {
lastTokenInfo.pt = correctedCard.getInfo().getPowTough(); lastTokenInfo.pt = correctedCard.getInfo().getPowTough();
} }
@ -996,14 +975,15 @@ void PlayerActions::setLastTokenInfo(CardInfoPtr cardInfo)
return; return;
} }
lastTokenInfo = {.name = cardInfo->getName(), lastTokenInfo = {
.color = cardInfo->getColors().isEmpty() ? QString() : cardInfo->getColors().left(1).toLower(), .name = cardInfo->getName(),
.pt = cardInfo->getPowTough(), .color = cardInfo->getColors().isEmpty() ? QString() : cardInfo->getColors().left(1).toLower(),
.annotation = getAnnotateTokens() ? cardInfo->getText() : "", .pt = cardInfo->getPowTough(),
.destroy = true, .annotation = SettingsCache::instance().userInterface().getAnnotateTokens() ? cardInfo->getText() : "",
.providerId = getCardPreferenceOverride(cardInfo->getName())}; .destroy = true,
.providerId = SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardInfo->getName())};
lastTokenTableRow = TableZoneLogic::tableRowToGridY(cardInfo->getUiAttributes().tableRow); lastTokenTableRow = TableZone::tableRowToGridY(cardInfo->getUiAttributes().tableRow);
emit requestEnableAndSetCreateAnotherTokenAction(lastTokenInfo.name); emit requestEnableAndSetCreateAnotherTokenAction(lastTokenInfo.name);
} }
@ -1021,20 +1001,25 @@ void PlayerActions::actCreatePredefinedToken()
actCreateAnotherToken(); actCreateAnotherToken();
} }
void PlayerActions::actCreateRelatedCard(const CardState *sourceCard, int index) void PlayerActions::actCreateRelatedCard()
{ {
const CardItem *sourceCard = player->getGame()->getActiveCard();
if (!sourceCard) { if (!sourceCard) {
return; return;
} }
auto *action = static_cast<QAction *>(sender());
// If there is a better way of passing a CardRelation through a QAction, please add it here.
auto relatedCards = sourceCard->getCardInfo().getAllRelatedCards(); auto relatedCards = sourceCard->getCardInfo().getAllRelatedCards();
CardRelation *cardRelation = relatedCards.at(index); CardRelation *cardRelation = relatedCards.at(action->data().toInt());
actRequestCreateRelatedFromRelationDialog(sourceCard, cardRelation); actRequestCreateRelatedFromRelationDialog(sourceCard, cardRelation);
} }
void PlayerActions::actCreateAllRelatedCards(const CardState *sourceCard) void PlayerActions::actCreateAllRelatedCards()
{ {
const CardItem *sourceCard = player->getGame()->getActiveCard();
if (!sourceCard) { if (!sourceCard) {
return; return;
} }
@ -1120,13 +1105,13 @@ void PlayerActions::actCreateAllRelatedCards(const CardState *sourceCard)
} }
} }
void PlayerActions::actRequestCreateRelatedFromRelationDialog(const CardState *sourceCard, void PlayerActions::actRequestCreateRelatedFromRelationDialog(const CardItem *sourceCard,
const CardRelation *cardRelation) const CardRelation *cardRelation)
{ {
emit requestCreateRelatedFromRelationDialog(sourceCard, cardRelation); emit requestCreateRelatedFromRelationDialog(sourceCard, cardRelation);
} }
bool PlayerActions::createRelatedFromRelation(const CardState *sourceCard, bool PlayerActions::createRelatedFromRelation(const CardItem *sourceCard,
const CardRelation *cardRelation, const CardRelation *cardRelation,
int variableCount) int variableCount)
{ {
@ -1178,7 +1163,7 @@ bool PlayerActions::createRelatedFromRelation(const CardState *sourceCard,
return true; return true;
} }
void PlayerActions::onRelatedCardCreated(const CardState *sourceCard, const CardRelation *cardRelation) void PlayerActions::onRelatedCardCreated(const CardItem *sourceCard, const CardRelation *cardRelation)
{ {
if (sourceCard == nullptr || cardRelation == nullptr) { if (sourceCard == nullptr || cardRelation == nullptr) {
return; return;
@ -1198,7 +1183,7 @@ void PlayerActions::onRelatedCardCreated(const CardState *sourceCard, const Card
setLastToken(relatedCard.getCardPtr()); setLastToken(relatedCard.getCardPtr());
} }
void PlayerActions::createCard(const CardState *sourceCard, void PlayerActions::createCard(const CardItem *sourceCard,
const QString &dbCardName, const QString &dbCardName,
CardRelationType attachType, CardRelationType attachType,
bool persistent, bool persistent,
@ -1210,7 +1195,7 @@ void PlayerActions::createCard(const CardState *sourceCard,
return; return;
} }
QPoint gridPoint = QPoint(-1, TableZoneLogic::tableRowToGridY(cardInfo->getUiAttributes().tableRow)); QPoint gridPoint = QPoint(-1, TableZone::tableRowToGridY(cardInfo->getUiAttributes().tableRow));
// create the token for the related card // create the token for the related card
Command_CreateToken cmd; Command_CreateToken cmd;
@ -1229,7 +1214,7 @@ void PlayerActions::createCard(const CardState *sourceCard,
} }
cmd.set_pt(cardInfo->getPowTough().toStdString()); cmd.set_pt(cardInfo->getPowTough().toStdString());
if (getAnnotateTokens()) { if (SettingsCache::instance().userInterface().getAnnotateTokens()) {
cmd.set_annotation(cardInfo->getText().toStdString()); cmd.set_annotation(cardInfo->getText().toStdString());
} else { } else {
cmd.set_annotation(""); cmd.set_annotation("");
@ -1284,7 +1269,7 @@ void PlayerActions::actRequestMoveCardXCardsFromTopDialog()
emit requestMoveCardXCardsFromTopDialog(defaultNumberTopCardsToPlaceBelow, deckSize); emit requestMoveCardXCardsFromTopDialog(defaultNumberTopCardsToPlaceBelow, deckSize);
} }
void PlayerActions::actMoveCardXCardsFromTop(QList<CardState *> selectedCards, int number) void PlayerActions::actMoveCardXCardsFromTop(QList<CardItem *> selectedCards, int number)
{ {
defaultNumberTopCardsToPlaceBelow = number; defaultNumberTopCardsToPlaceBelow = number;
@ -1318,14 +1303,14 @@ void PlayerActions::actMoveCardXCardsFromTop(QList<CardState *> selectedCards, i
} }
} }
void PlayerActions::actIncPT(QList<CardState *> selectedCards, int deltaP, int deltaT) void PlayerActions::actIncPT(QList<CardItem *> selectedCards, int deltaP, int deltaT)
{ {
int playerid = player->getPlayerInfo()->getId(); int playerid = player->getPlayerInfo()->getId();
QList<const ::google::protobuf::Message *> commandList; QList<const ::google::protobuf::Message *> commandList;
for (auto card : selectedCards) { for (auto card : selectedCards) {
QString pt = card->getPT(); QString pt = card->getPT();
const auto ptList = CardState::parsePT(pt); const auto ptList = CardItem::parsePT(pt);
QString newpt; QString newpt;
if (ptList.isEmpty()) { if (ptList.isEmpty()) {
newpt = QString::number(deltaP) + (deltaT ? "/" + QString::number(deltaT) : ""); newpt = QString::number(deltaP) + (deltaT ? "/" + QString::number(deltaT) : "");
@ -1351,7 +1336,7 @@ void PlayerActions::actIncPT(QList<CardState *> selectedCards, int deltaP, int d
player->getGame()->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid); player->getGame()->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid);
} }
void PlayerActions::actResetPT(QList<CardState *> selectedCards) void PlayerActions::actResetPT(QList<CardItem *> selectedCards)
{ {
int playerid = player->getPlayerInfo()->getId(); int playerid = player->getPlayerInfo()->getId();
QList<const ::google::protobuf::Message *> commandList; QList<const ::google::protobuf::Message *> commandList;
@ -1384,7 +1369,7 @@ void PlayerActions::actResetPT(QList<CardState *> selectedCards)
} }
} }
void PlayerActions::actRequestSetPTDialog(QList<CardState *> selectedCards) void PlayerActions::actRequestSetPTDialog(QList<CardItem *> selectedCards)
{ {
QString oldPT; QString oldPT;
@ -1397,11 +1382,11 @@ void PlayerActions::actRequestSetPTDialog(QList<CardState *> selectedCards)
emit requestSetPTDialog(oldPT); emit requestSetPTDialog(oldPT);
} }
void PlayerActions::actSetPT(QList<CardState *> selectedCards, const QString &pt) void PlayerActions::actSetPT(QList<CardItem *> selectedCards, const QString &pt)
{ {
int playerid = player->getPlayerInfo()->getId(); int playerid = player->getPlayerInfo()->getId();
const auto ptList = CardState::parsePT(pt); const auto ptList = CardItem::parsePT(pt);
bool empty = ptList.isEmpty(); bool empty = ptList.isEmpty();
QList<const ::google::protobuf::Message *> commandList; QList<const ::google::protobuf::Message *> commandList;
@ -1409,7 +1394,7 @@ void PlayerActions::actSetPT(QList<CardState *> selectedCards, const QString &pt
auto *cmd = new Command_SetCardAttr; auto *cmd = new Command_SetCardAttr;
QString newpt = QString(); QString newpt = QString();
if (!empty) { if (!empty) {
const auto oldpt = CardState::parsePT(card->getPT()); const auto oldpt = CardItem::parsePT(card->getPT());
int ptIter = 0; int ptIter = 0;
for (const auto &_item : ptList) { for (const auto &_item : ptList) {
if (_item.typeId() == QMetaType::Type::Int) { if (_item.typeId() == QMetaType::Type::Int) {
@ -1437,65 +1422,55 @@ void PlayerActions::actSetPT(QList<CardState *> selectedCards, const QString &pt
player->getGame()->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid); player->getGame()->getGameEventHandler()->sendGameCommand(prepareGameCommand(commandList), playerid);
} }
void PlayerActions::actIncP(QList<CardState *> selectedCards) void PlayerActions::actDrawArrow()
{
auto *card = player->getGame()->getActiveCard();
if (card) {
card->drawArrow(Qt::red);
}
}
void PlayerActions::actIncP(QList<CardItem *> selectedCards)
{ {
actIncPT(selectedCards, 1, 0); actIncPT(selectedCards, 1, 0);
} }
void PlayerActions::actDecP(QList<CardState *> selectedCards) void PlayerActions::actDecP(QList<CardItem *> selectedCards)
{ {
actIncPT(selectedCards, -1, 0); actIncPT(selectedCards, -1, 0);
} }
void PlayerActions::actIncT(QList<CardState *> selectedCards) void PlayerActions::actIncT(QList<CardItem *> selectedCards)
{ {
actIncPT(selectedCards, 0, 1); actIncPT(selectedCards, 0, 1);
} }
void PlayerActions::actDecT(QList<CardState *> selectedCards) void PlayerActions::actDecT(QList<CardItem *> selectedCards)
{ {
actIncPT(selectedCards, 0, -1); actIncPT(selectedCards, 0, -1);
} }
void PlayerActions::actIncPT(QList<CardState *> selectedCards) void PlayerActions::actIncPT(QList<CardItem *> selectedCards)
{ {
actIncPT(selectedCards, 1, 1); actIncPT(selectedCards, 1, 1);
} }
void PlayerActions::actDecPT(QList<CardState *> selectedCards) void PlayerActions::actDecPT(QList<CardItem *> selectedCards)
{ {
actIncPT(selectedCards, -1, -1); actIncPT(selectedCards, -1, -1);
} }
void PlayerActions::actFlowP(QList<CardState *> selectedCards) void PlayerActions::actFlowP(QList<CardItem *> selectedCards)
{ {
actIncPT(selectedCards, 1, -1); actIncPT(selectedCards, 1, -1);
} }
void PlayerActions::actFlowT(QList<CardState *> selectedCards) void PlayerActions::actFlowT(QList<CardItem *> selectedCards)
{ {
actIncPT(selectedCards, -1, 1); actIncPT(selectedCards, -1, 1);
} }
void PlayerActions::actIncrementCounter(int counterId, int delta) void PlayerActions::actReduceLifeByPower(QList<CardItem *> selectedCards)
{
Command_IncCounter cmd;
cmd.set_counter_id(counterId);
cmd.set_delta(delta);
sendGameCommand(cmd);
}
void PlayerActions::actSetCounter(int counterId, int value)
{
Command_SetCounter cmd;
cmd.set_counter_id(counterId);
cmd.set_value(value);
sendGameCommand(cmd);
}
void PlayerActions::actReduceLifeByPower(QList<CardState *> selectedCards)
{ {
// find life counter // find life counter
auto lifeCounter = player->getLifeCounter(); auto lifeCounter = player->getLifeCounter();
@ -1506,7 +1481,7 @@ void PlayerActions::actReduceLifeByPower(QList<CardState *> selectedCards)
// calculate total power; // calculate total power;
int total = 0; int total = 0;
for (auto card : selectedCards) { for (auto card : selectedCards) {
QVariantList parsed = CardState::parsePT(card->getPT()); QVariantList parsed = CardItem::parsePT(card->getPT());
if (!parsed.isEmpty()) { if (!parsed.isEmpty()) {
int power = parsed.first().toInt(); // toInt will default to 0 if it's not an int int power = parsed.first().toInt(); // toInt will default to 0 if it's not an int
total += qMax(power, 0); total += qMax(power, 0);
@ -1520,7 +1495,17 @@ void PlayerActions::actReduceLifeByPower(QList<CardState *> selectedCards)
sendGameCommand(prepareGameCommand(cmd)); sendGameCommand(prepareGameCommand(cmd));
} }
void PlayerActions::actRequestSetAnnotationDialog(QList<CardState *> selectedCards) void AnnotationDialog::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Return && event->modifiers() & Qt::ControlModifier) {
event->accept();
accept();
return;
}
QInputDialog::keyPressEvent(event);
}
void PlayerActions::actRequestSetAnnotationDialog(QList<CardItem *> selectedCards)
{ {
QString oldAnnotation; QString oldAnnotation;
for (auto card : selectedCards) { for (auto card : selectedCards) {
@ -1532,7 +1517,7 @@ void PlayerActions::actRequestSetAnnotationDialog(QList<CardState *> selectedCar
emit requestSetAnnotationDialog(oldAnnotation); emit requestSetAnnotationDialog(oldAnnotation);
} }
void PlayerActions::actSetAnnotation(QList<CardState *> selectedCards, const QString &annotation) void PlayerActions::actSetAnnotation(QList<CardItem *> selectedCards, const QString &annotation)
{ {
QList<const ::google::protobuf::Message *> commandList; QList<const ::google::protobuf::Message *> commandList;
for (auto card : selectedCards) { for (auto card : selectedCards) {
@ -1546,7 +1531,17 @@ void PlayerActions::actSetAnnotation(QList<CardState *> selectedCards, const QSt
sendGameCommand(prepareGameCommand(commandList)); sendGameCommand(prepareGameCommand(commandList));
} }
void PlayerActions::actUnattach(QList<CardState *> selectedCards) void PlayerActions::actAttach()
{
auto *card = player->getGame()->getActiveCard();
if (!card) {
return;
}
card->drawAttachArrow();
}
void PlayerActions::actUnattach(QList<CardItem *> selectedCards)
{ {
QList<const ::google::protobuf::Message *> commandList; QList<const ::google::protobuf::Message *> commandList;
for (auto card : selectedCards) { for (auto card : selectedCards) {
@ -1562,17 +1557,17 @@ void PlayerActions::actUnattach(QList<CardState *> selectedCards)
sendGameCommand(prepareGameCommand(commandList)); sendGameCommand(prepareGameCommand(commandList));
} }
void PlayerActions::actAddCardCounter(QList<CardState *> selectedCards, int counterId) void PlayerActions::actAddCardCounter(QList<CardItem *> selectedCards, int counterId)
{ {
offsetCardCounter(selectedCards, counterId, 1); offsetCardCounter(selectedCards, counterId, 1);
} }
void PlayerActions::actRemoveCardCounter(QList<CardState *> selectedCards, int counterId) void PlayerActions::actRemoveCardCounter(QList<CardItem *> selectedCards, int counterId)
{ {
offsetCardCounter(selectedCards, counterId, -1); offsetCardCounter(selectedCards, counterId, -1);
} }
void PlayerActions::offsetCardCounter(QList<CardState *> selectedCards, int counterId, int offset) void PlayerActions::offsetCardCounter(QList<CardItem *> selectedCards, int counterId, int offset)
{ {
QList<const ::google::protobuf::Message *> commandList; QList<const ::google::protobuf::Message *> commandList;
for (auto card : selectedCards) { for (auto card : selectedCards) {
@ -1598,7 +1593,7 @@ void PlayerActions::offsetCardCounter(QList<CardState *> selectedCards, int coun
sendGameCommand(prepareGameCommand(commandList)); sendGameCommand(prepareGameCommand(commandList));
} }
void PlayerActions::actRequestSetCardCounterDialog(QList<CardState *> selectedCards, int counterId) void PlayerActions::actRequestSetCardCounterDialog(QList<CardItem *> selectedCards, int counterId)
{ {
// If a single card is selected, we show the old value in the dialog. Otherwise, we show "x" // If a single card is selected, we show the old value in the dialog. Otherwise, we show "x"
QString oldValueForDlg = "x"; QString oldValueForDlg = "x";
@ -1610,7 +1605,7 @@ void PlayerActions::actRequestSetCardCounterDialog(QList<CardState *> selectedCa
emit requestSetCardCounterDialog(counterId, oldValueForDlg); emit requestSetCardCounterDialog(counterId, oldValueForDlg);
} }
void PlayerActions::actSetCardCounter(QList<CardState *> selectedCards, int counterId, const QString &counterValue) void PlayerActions::actSetCardCounter(QList<CardItem *> selectedCards, int counterId, const QString &counterValue)
{ {
QList<const ::google::protobuf::Message *> commandList; QList<const ::google::protobuf::Message *> commandList;
for (auto card : selectedCards) { for (auto card : selectedCards) {
@ -1631,13 +1626,11 @@ void PlayerActions::actSetCardCounter(QList<CardState *> selectedCards, int coun
sendGameCommand(prepareGameCommand(commandList)); sendGameCommand(prepareGameCommand(commandList));
} }
void PlayerActions::actIncrementAllCardCounters(QList<CardState *> cardsToUpdate) void PlayerActions::actIncrementAllCardCounters(QList<CardItem *> cardsToUpdate)
{ {
if (cardsToUpdate.isEmpty()) { if (cardsToUpdate.isEmpty()) {
// If no cards selected, update all cards on table // If no cards selected, update all cards on table
for (auto *state : player->getTableZone()->getCards()) { cardsToUpdate = static_cast<QList<CardItem *>>(player->getTableZone()->getCards());
cardsToUpdate.append(state);
}
} }
QList<const ::google::protobuf::Message *> commandList; QList<const ::google::protobuf::Message *> commandList;
@ -1680,7 +1673,7 @@ static bool isUnwritableRevealZone(CardZoneLogic *zone)
return false; return false;
} }
void PlayerActions::playSelectedCards(QList<CardState *> selectedCards, const bool faceDown) void PlayerActions::playSelectedCards(QList<CardItem *> selectedCards, const bool faceDown)
{ {
// CardIds will get shuffled downwards when cards leave the deck. // CardIds will get shuffled downwards when cards leave the deck.
// We need to iterate through the cards in reverse order so cardIds don't get changed out from under us as we play // We need to iterate through the cards in reverse order so cardIds don't get changed out from under us as we play
@ -1695,26 +1688,27 @@ void PlayerActions::playSelectedCards(QList<CardState *> selectedCards, const bo
} }
} }
void PlayerActions::actPlay(QList<CardState *> selectedCards) void PlayerActions::actPlay(QList<CardItem *> selectedCards)
{ {
playSelectedCards(selectedCards, false); playSelectedCards(selectedCards, false);
} }
void PlayerActions::actPlayFacedown(QList<CardState *> selectedCards) void PlayerActions::actPlayFacedown(QList<CardItem *> selectedCards)
{ {
playSelectedCards(selectedCards, true); playSelectedCards(selectedCards, true);
} }
void PlayerActions::actHide(QList<CardState *> selectedCards) void PlayerActions::actHide(QList<CardItem *> selectedCards)
{ {
for (const auto &card : selectedCards) { for (const auto &item : selectedCards) {
if (isUnwritableRevealZone(card->getZone())) { auto *card = static_cast<CardItem *>(item);
if (card && isUnwritableRevealZone(card->getZone())) {
card->getZone()->removeCard(card); card->getZone()->removeCard(card);
} }
} }
} }
void PlayerActions::actReveal(QList<CardState *> selectedCards, QAction *action) void PlayerActions::actReveal(QList<CardItem *> selectedCards, QAction *action)
{ {
const int otherPlayerId = action->data().toInt(); const int otherPlayerId = action->data().toInt();
@ -1805,9 +1799,9 @@ void PlayerActions::actRevealRandomGraveyardCard(int revealToPlayerId)
sendGameCommand(cmd); sendGameCommand(cmd);
} }
void PlayerActions::cardMenuAction(QList<CardState *> selectedCards, CardMenuActionType type) void PlayerActions::cardMenuAction(QList<CardItem *> selectedCards, CardMenuActionType type)
{ {
QList<CardState *> cardList = selectedCards; QList<CardItem *> cardList = selectedCards;
QList<const ::google::protobuf::Message *> commandList; QList<const ::google::protobuf::Message *> commandList;
if (type <= cmClone) { if (type <= cmClone) {
@ -1997,7 +1991,7 @@ void PlayerActions::cardMenuAction(QList<CardState *> selectedCards, CardMenuAct
ctm->set_tapped(info.getUiAttributes().cipt); ctm->set_tapped(info.getUiAttributes().cipt);
} }
cmd->set_y(TableZoneLogic::tableRowToGridY(tableRow)); cmd->set_y(TableZone::tableRowToGridY(tableRow));
commandList.append(cmd); commandList.append(cmd);
} }
break; break;
@ -2046,76 +2040,6 @@ PendingCommand *PlayerActions::prepareGameCommand(const QList<const ::google::pr
} }
} }
void PlayerActions::moveCards(CardZoneLogic *startZone,
CardZoneLogic *targetZone,
int x,
int y,
const QList<CardMoveRequest> &cards)
{
if (!startZone || !targetZone) {
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(targetZone->getPlayer()->getPlayerInfo()->getId());
cmd.set_target_zone(targetZone->getName().toStdString());
cmd.set_x(x);
cmd.set_y(y);
if (auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(targetZone)) {
cmd.set_is_reversed(viewLogic->getIsReversed());
}
for (const CardMoveRequest &card : cards) {
auto *cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(card.cardId);
if (card.faceDown) {
cardToMove->set_face_down(true);
}
if (!card.pt.isEmpty()) {
cardToMove->set_pt(card.pt.toStdString());
}
}
sendGameCommand(cmd);
}
PendingCommand *PlayerActions::prepareZoneDump(CardZoneLogic *zone, int numberCards)
{
if (!zone) {
return nullptr;
}
Command_DumpZone cmd;
cmd.set_player_id(zone->getPlayer()->getPlayerInfo()->getId());
cmd.set_zone_name(zone->getName().toStdString());
cmd.set_number_cards(numberCards);
if (auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(zone)) {
cmd.set_is_reversed(viewLogic->getIsReversed());
}
return prepareGameCommand(cmd);
}
void PlayerActions::actToggleTapped(const QString &zoneName, const QList<CardState *> &cards, bool tapped)
{
QList<const ::google::protobuf::Message *> cmdList;
for (CardState *card : cards) {
if (card->getTapped() != tapped) {
auto *cmd = new Command_SetCardAttr;
cmd->set_zone(zoneName.toStdString());
cmd->set_card_id(card->getId());
cmd->set_attribute(AttrTapped);
cmd->set_attr_value(tapped ? "1" : "0");
cmdList.append(cmd);
}
}
sendGameCommand(prepareGameCommand(cmdList));
}
void PlayerActions::sendGameCommand(const google::protobuf::Message &command) void PlayerActions::sendGameCommand(const google::protobuf::Message &command)
{ {
if (player->getPlayerInfo()->getJudge() && !player->getPlayerInfo()->getLocal()) { if (player->getPlayerInfo()->getJudge() && !player->getPlayerInfo()->getLocal()) {

View file

@ -8,13 +8,12 @@
#ifndef COCKATRICE_PLAYER_ACTIONS_H #ifndef COCKATRICE_PLAYER_ACTIONS_H
#define COCKATRICE_PLAYER_ACTIONS_H #define COCKATRICE_PLAYER_ACTIONS_H
#include "../../game_graphics/board/card_item.h" // IWYU pragma: keep - moc instantiates the metatype for QList<CardItem *>, which Qt 6.4 needs complete
#include "../../game_graphics/dialogs/dlg_create_token.h"
#include "../../game_graphics/dialogs/dlg_move_top_cards_until.h"
#include "../../game_graphics/player/card_menu_action_type.h"
#include "../board/card_list.h" #include "../board/card_list.h"
#include "../board/card_state.h"
#include "card_menu_action_type.h"
#include "card_move_request.h"
#include "libcockatrice/card/card_info.h" #include "libcockatrice/card/card_info.h"
#include "move_top_cards_until_options.h"
#include "token_info.h"
#include <QObject> #include <QObject>
#include <QString> #include <QString>
@ -23,6 +22,7 @@
#include <libcockatrice/filters/filter_string.h> #include <libcockatrice/filters/filter_string.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class CardItem;
class CardRelation; class CardRelation;
class QAction; class QAction;
class QTimer; class QTimer;
@ -36,16 +36,10 @@ class Message;
} }
} // namespace google } // namespace google
class CardState;
class CardZoneLogic;
class Command_MoveCard; class Command_MoveCard;
class GameEventContext;
class ICardPreferenceProvider;
class IInterfaceSettingsProvider;
class PendingCommand; class PendingCommand;
class PlayerLogic; class PlayerLogic;
class QAction;
struct ArrowData;
class PlayerActions : public QObject class PlayerActions : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -56,9 +50,7 @@ public:
RANDOM_CARD_FROM_ZONE = -2 RANDOM_CARD_FROM_ZONE = -2
}; };
explicit PlayerActions(PlayerLogic *player, explicit PlayerActions(PlayerLogic *player);
IInterfaceSettingsProvider *interfaceSettings = nullptr,
ICardPreferenceProvider *cardPreferences = nullptr);
void sendGameCommand(PendingCommand *pend); void sendGameCommand(PendingCommand *pend);
void sendGameCommand(const google::protobuf::Message &command); void sendGameCommand(const google::protobuf::Message &command);
@ -66,52 +58,7 @@ public:
PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd); PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd);
PendingCommand *prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList); PendingCommand *prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList);
/** void moveOneCardUntil(CardItem *card);
* @brief Moves the requested cards between zones on behalf of a view.
* @param startZone Zone the cards are dragged out of.
* @param targetZone Zone the cards are dropped onto.
* @param x Horizontal target index within the zone.
* @param y Vertical target index within the zone.
* @param cards The cards to move, as chosen by the view.
*/
void
moveCards(CardZoneLogic *startZone, CardZoneLogic *targetZone, int x, int y, const QList<CardMoveRequest> &cards);
/**
* @brief Prepares a dump of the given zone, for the caller to connect to and send.
* @param zone Zone to dump.
* @param numberCards How many cards to dump, -1 for all.
*/
PendingCommand *prepareZoneDump(CardZoneLogic *zone, int numberCards);
/**
* @brief Sets the tapped state of the cards that do not already have it.
* @param zoneName Zone the cards belong to.
* @param cards Cards the view wants toggled.
* @param tapped Desired tapped state.
*/
void actToggleTapped(const QString &zoneName, const QList<CardState *> &cards, bool tapped);
/**
* @brief Attaches one card to another on the table on behalf of a view.
* @param startCard Card being attached.
* @param targetCard Card being attached to.
*/
void attachCards(CardState *startCard, const CardState *targetCard);
/**
* @brief Draws an arrow from a card to another card or a player on behalf of a view.
*
* The view resolves the endpoints before playing the card out of hand, so the
* arrow still records the zone the card started in.
*
* @param startCard Card the arrow starts from.
* @param arrow Arrow endpoints and color, as resolved by the view.
* @param deleteInPhase Phase the arrow expires in, 0 to keep it until removed.
*/
void createArrow(CardState *startCard, const ArrowData &arrow, int deleteInPhase);
void moveOneCardUntil(CardState *card);
void stopMoveTopCardsUntil(); void stopMoveTopCardsUntil();
[[nodiscard]] bool isMovingCardsUntil() const [[nodiscard]] bool isMovingCardsUntil() const
@ -140,7 +87,7 @@ signals:
void requestDrawBottomCardsDialog(int defaultNumberBottomCards, int maxCards); void requestDrawBottomCardsDialog(int defaultNumberBottomCards, int maxCards);
void requestRollDieDialog(); void requestRollDieDialog();
void requestCreateTokenDialog(const QStringList &predefinedTokens); void requestCreateTokenDialog(const QStringList &predefinedTokens);
void requestCreateRelatedFromRelationDialog(const CardState *sourceCard, const CardRelation *cardRelation); void requestCreateRelatedFromRelationDialog(const CardItem *sourceCard, const CardRelation *cardRelation);
void requestMoveCardXCardsFromTopDialog(int defaultNumberTopCardsToPlaceBelow, int deckSize); void requestMoveCardXCardsFromTopDialog(int defaultNumberTopCardsToPlaceBelow, int deckSize);
void requestSetPTDialog(const QString &oldPT); void requestSetPTDialog(const QString &oldPT);
void requestSetAnnotationDialog(const QString &oldAnnotation); void requestSetAnnotationDialog(const QString &oldAnnotation);
@ -149,18 +96,12 @@ signals:
void requestSortHand(const QList<CardList::SortOption> &options); void requestSortHand(const QList<CardList::SortOption> &options);
void requestEnableAndSetCreateAnotherTokenAction(const QString &lastTokenName); void requestEnableAndSetCreateAnotherTokenAction(const QString &lastTokenName);
void requestSetLastToken(CardInfoPtr lastToken); void requestSetLastToken(CardInfoPtr lastToken);
/**
* @brief The player chose a mulligan size to remember as the starting hand size.
*
* Game logic must not write client settings itself, so persistence is left to the application.
*/
void startingHandSizeChosen(int number);
public slots: public slots:
void setLastToken(CardInfoPtr cardInfo); void setLastToken(CardInfoPtr cardInfo);
void setLastTokenInfo(CardInfoPtr cardInfo); void setLastTokenInfo(CardInfoPtr cardInfo);
void playCard(CardState *c, bool faceDown); void playCard(CardItem *c, bool faceDown);
void playCardToTable(const CardState *c, bool faceDown); void playCardToTable(const CardItem *c, bool faceDown);
void actUntapAll(); void actUntapAll();
void actRequestRollDieDialog(); void actRequestRollDieDialog();
@ -169,9 +110,9 @@ public slots:
void actRequestCreateTokenDialog(const QStringList &predefinedTokens); void actRequestCreateTokenDialog(const QStringList &predefinedTokens);
void actCreateToken(TokenInfo tokenToCreate); void actCreateToken(TokenInfo tokenToCreate);
void actCreateAnotherToken(); void actCreateAnotherToken();
void actRequestCreateRelatedFromRelationDialog(const CardState *sourceCard, const CardRelation *cardRelation); void actRequestCreateRelatedFromRelationDialog(const CardItem *sourceCard, const CardRelation *cardRelation);
bool createRelatedFromRelation(const CardState *sourceCard, const CardRelation *cardRelation, int variableCount); bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation, int variableCount);
void onRelatedCardCreated(const CardState *sourceCard, const CardRelation *cardRelation); void onRelatedCardCreated(const CardItem *sourceCard, const CardRelation *cardRelation);
void setLastRelatedCreationSucceeded(bool succeeded) void setLastRelatedCreationSucceeded(bool succeeded)
{ {
lastRelatedCreationSucceeded = succeeded; lastRelatedCreationSucceeded = succeeded;
@ -191,9 +132,9 @@ public slots:
void actMulliganMinusOne(); void actMulliganMinusOne();
void doMulligan(int number); void doMulligan(int number);
void actPlay(QList<CardState *> selectedCards); void actPlay(QList<CardItem *> selectedCards);
void actPlayFacedown(QList<CardState *> selectedCards); void actPlayFacedown(QList<CardItem *> selectedCards);
void actHide(QList<CardState *> selectedCards); void actHide(QList<CardItem *> selectedCards);
void actMoveTopCardToPlay(); void actMoveTopCardToPlay();
void actMoveTopCardToPlayFaceDown(); void actMoveTopCardToPlayFaceDown();
@ -223,6 +164,10 @@ public slots:
void actRequestMoveBottomCardsToDialog(const QString &targetZone, const QString &zoneDisplayName, bool faceDown); void actRequestMoveBottomCardsToDialog(const QString &targetZone, const QString &zoneDisplayName, bool faceDown);
void moveBottomCardsTo(int number, const QString &targetZone, bool faceDown); void moveBottomCardsTo(int number, const QString &targetZone, bool faceDown);
void actSelectAll();
void actSelectRow();
void actSelectColumn();
void actViewLibrary(); void actViewLibrary();
void actViewHand(); void actViewHand();
void actRequestViewTopCardsDialog(); void actRequestViewTopCardsDialog();
@ -242,55 +187,47 @@ public slots:
void actOpenDeckInDeckEditor(); void actOpenDeckInDeckEditor();
void actCreatePredefinedToken(); void actCreatePredefinedToken();
void actCreateRelatedCard(const CardState *sourceCard, int index); void actCreateRelatedCard();
void actCreateAllRelatedCards(const CardState *sourceCard); void actCreateAllRelatedCards();
void actRequestMoveCardXCardsFromTopDialog(); void actRequestMoveCardXCardsFromTopDialog();
void actMoveCardXCardsFromTop(QList<CardState *> selectedCards, int number); void actMoveCardXCardsFromTop(QList<CardItem *> selectedCards, int number);
void actRemoveCardCounter(QList<CardState *> selectedCards, int counterId); void actRemoveCardCounter(QList<CardItem *> selectedCards, int counterId);
void actAddCardCounter(QList<CardState *> selectedCards, int counterId); void actAddCardCounter(QList<CardItem *> selectedCards, int counterId);
void actRequestSetCardCounterDialog(QList<CardState *> selectedCards, int counterId); void actRequestSetCardCounterDialog(QList<CardItem *> selectedCards, int counterId);
void actSetCardCounter(QList<CardState *> selectedCards, int counterId, const QString &counterValue); void actSetCardCounter(QList<CardItem *> selectedCards, int counterId, const QString &counterValue);
void actIncrementAllCardCounters(QList<CardState *> cardsToUpdate); void actIncrementAllCardCounters(QList<CardItem *> cardsToUpdate);
void actUnattach(QList<CardState *> selectedCards); void actAttach();
void actIncPT(QList<CardState *> selectedCards, int deltaP, int deltaT); void actUnattach(QList<CardItem *> selectedCards);
void actResetPT(QList<CardState *> selectedCards); void actDrawArrow();
void actRequestSetPTDialog(QList<CardState *> selectedCards); void actIncPT(QList<CardItem *> selectedCards, int deltaP, int deltaT);
void actSetPT(QList<CardState *> selectedCards, const QString &pt); void actResetPT(QList<CardItem *> selectedCards);
void actIncP(QList<CardState *> selectedCards); void actRequestSetPTDialog(QList<CardItem *> selectedCards);
void actDecP(QList<CardState *> selectedCards); void actSetPT(QList<CardItem *> selectedCards, const QString &pt);
void actIncT(QList<CardState *> selectedCards); void actIncP(QList<CardItem *> selectedCards);
void actDecT(QList<CardState *> selectedCards); void actDecP(QList<CardItem *> selectedCards);
void actIncPT(QList<CardState *> selectedCards); void actIncT(QList<CardItem *> selectedCards);
void actDecPT(QList<CardState *> selectedCards); void actDecT(QList<CardItem *> selectedCards);
void actFlowP(QList<CardState *> selectedCards); void actIncPT(QList<CardItem *> selectedCards);
void actFlowT(QList<CardState *> selectedCards); void actDecPT(QList<CardItem *> selectedCards);
void actFlowP(QList<CardItem *> selectedCards);
void actFlowT(QList<CardItem *> selectedCards);
/** @brief Adjusts a player counter by a signed delta. */ void actReduceLifeByPower(QList<CardItem *> selectedCards);
void actIncrementCounter(int counterId, int delta);
/** @brief Sets a player counter to an absolute value. */ void actRequestSetAnnotationDialog(QList<CardItem *> selectedCards);
void actSetCounter(int counterId, int value); void actSetAnnotation(QList<CardItem *> selectedCards, const QString &annotation);
void actReveal(QList<CardItem *> selectedCards, QAction *action);
void actReduceLifeByPower(QList<CardState *> selectedCards);
void actRequestSetAnnotationDialog(QList<CardState *> selectedCards);
void actSetAnnotation(QList<CardState *> selectedCards, const QString &annotation);
void actReveal(QList<CardState *> selectedCards, QAction *action);
void actRevealHand(int revealToPlayerId); void actRevealHand(int revealToPlayerId);
void actRevealRandomHandCard(int revealToPlayerId); void actRevealRandomHandCard(int revealToPlayerId);
void actRevealLibrary(int revealToPlayerId); void actRevealLibrary(int revealToPlayerId);
void actSortHand(); void actSortHand();
void cardMenuAction(QList<CardState *> selectedCards, CardMenuActionType type); void cardMenuAction(QList<CardItem *> selectedCards, CardMenuActionType type);
private: private:
PlayerLogic *player; PlayerLogic *player;
// Borrowed settings providers (owned by the application). Null outside the client, in which case the compiled-in
// defaults below apply.
IInterfaceSettingsProvider *interfaceSettings;
ICardPreferenceProvider *cardPreferences;
int defaultNumberTopCards = 1; int defaultNumberTopCards = 1;
int defaultNumberTopCardsToPlaceBelow = 1; int defaultNumberTopCardsToPlaceBelow = 1;
@ -308,23 +245,18 @@ private:
bool lastRelatedCreationSucceeded = false; bool lastRelatedCreationSucceeded = false;
[[nodiscard]] bool getPlayToStack() const; void createCard(const CardItem *sourceCard,
[[nodiscard]] int getStartingHandSize() const;
[[nodiscard]] bool getAnnotateTokens() const;
[[nodiscard]] QString getCardPreferenceOverride(const QString &cardName) const;
void createCard(const CardState *sourceCard,
const QString &dbCardName, const QString &dbCardName,
CardRelationType attach = CardRelationType::DoesNotAttach, CardRelationType attach = CardRelationType::DoesNotAttach,
bool persistent = false, bool persistent = false,
bool faceDown = false); bool faceDown = false);
void playSelectedCards(QList<CardState *> selectedCards, bool faceDown = false); void playSelectedCards(QList<CardItem *> selectedCards, bool faceDown = false);
void cmdSetTopCard(Command_MoveCard &cmd); void cmdSetTopCard(Command_MoveCard &cmd);
void cmdSetBottomCard(Command_MoveCard &cmd); void cmdSetBottomCard(Command_MoveCard &cmd);
void offsetCardCounter(QList<CardState *> selectedCards, int counterId, int offset); void offsetCardCounter(QList<CardItem *> selectedCards, int counterId, int offset);
}; };
#endif // COCKATRICE_PLAYER_ACTIONS_H #endif // COCKATRICE_PLAYER_ACTIONS_H

View file

@ -1,14 +1,14 @@
#include "player_event_handler.h" #include "player_event_handler.h"
#include "../../game_graphics/board/card_item.h"
#include "../../game_graphics/zones/view_zone.h"
#include "../abstract_game.h" #include "../abstract_game.h"
#include "../board/arrow_data.h" #include "../board/arrow_data.h"
#include "../board/card_list.h" #include "../board/card_list.h"
#include "../board/card_state.h"
#include "../board/counter_state.h" #include "../board/counter_state.h"
#include "../zones/card_zone_logic.h" #include "../zones/card_zone_logic.h"
#include "../zones/hand_zone_logic.h" #include "../zones/hand_zone_logic.h"
#include "../zones/pile_zone_logic.h" #include "../zones/pile_zone_logic.h"
#include "../zones/view_zone_logic.h"
#include "libcockatrice/card/card_info.h" #include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/printing/exact_card.h" #include "libcockatrice/card/printing/exact_card.h"
#include "libcockatrice/protocol/pb/card_attributes.pb.h" #include "libcockatrice/protocol/pb/card_attributes.pb.h"
@ -27,9 +27,6 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QtGlobal> #include <QtGlobal>
#include <algorithm> #include <algorithm>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/database/card_database_querier.h>
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
#include <libcockatrice/protocol/pb/context_move_card.pb.h> #include <libcockatrice/protocol/pb/context_move_card.pb.h>
#include <libcockatrice/protocol/pb/context_undo_draw.pb.h> #include <libcockatrice/protocol/pb/context_undo_draw.pb.h>
#include <libcockatrice/protocol/pb/event_attach_card.pb.h> #include <libcockatrice/protocol/pb/event_attach_card.pb.h>
@ -52,7 +49,6 @@
#include <libcockatrice/protocol/pb/event_set_card_counter.pb.h> #include <libcockatrice/protocol/pb/event_set_card_counter.pb.h>
#include <libcockatrice/protocol/pb/event_set_counter.pb.h> #include <libcockatrice/protocol/pb/event_set_counter.pb.h>
#include <libcockatrice/protocol/pb/event_shuffle.pb.h> #include <libcockatrice/protocol/pb/event_shuffle.pb.h>
#include <libcockatrice/utility/color.h>
#include <libcockatrice/utility/zone_names.h> #include <libcockatrice/utility/zone_names.h>
#include <qlogging.h> #include <qlogging.h>
#include <string> #include <string>
@ -80,12 +76,12 @@ void PlayerEventHandler::eventShuffle(const Event_Shuffle &event)
} }
// close all views that contain shuffled cards // close all views that contain shuffled cards
for (auto *viewLogic : zone->getViews()) { for (ZoneViewZone *view : zone->getViews()) {
if (viewLogic != nullptr) { if (view != nullptr) {
int length = viewLogic->getCards().length(); int length = view->getLogic()->getCards().length();
// we want to close empty views as well // we want to close empty views as well
if (length == 0 || length > absStart) { // note this assumes views always start at the top of the library if (length == 0 || length > absStart) { // note this assumes views always start at the top of the library
viewLogic->requestClose(); view->close();
} }
} else { } else {
qWarning() << zone->getName() << "of" << player->getPlayerInfo()->getName() << "holds empty zoneview!"; qWarning() << zone->getName() << "of" << player->getPlayerInfo()->getName() << "holds empty zoneview!";
@ -159,29 +155,23 @@ void PlayerEventHandler::eventCreateToken(const Event_CreateToken &event)
} }
CardRef cardRef = {QString::fromStdString(event.card_name()), QString::fromStdString(event.card_provider_id())}; CardRef cardRef = {QString::fromStdString(event.card_name()), QString::fromStdString(event.card_provider_id())};
CardItem *card = new CardItem(player, nullptr, cardRef, event.card_id());
// use db PT if not provided in event and not face-down // use db PT if not provided in event and not face-down
QString pt = QString::fromStdString(event.pt()); if (!QString::fromStdString(event.pt()).isEmpty()) {
if (pt.isEmpty() && !event.face_down()) { card->setPT(QString::fromStdString(event.pt()));
ExactCard dbCard = CardDatabaseManager::query()->getCard(cardRef); } else if (!event.face_down()) {
ExactCard dbCard = card->getCard();
if (dbCard) { if (dbCard) {
pt = dbCard.getInfo().getPowTough(); card->setPT(dbCard.getInfo().getPowTough());
} }
} }
card->setColor(QString::fromStdString(event.color()));
card->setAnnotation(QString::fromStdString(event.annotation()));
card->setDestroyOnZoneChange(event.destroy_on_zone_change());
card->setFaceDown(event.face_down());
emit logCreateToken(player, cardRef.name, pt, event.face_down()); emit logCreateToken(player, card->getName(), card->getPT(), card->getFaceDown());
zone->addCard(card, true, event.x(), event.y());
ServerInfo_Card cardInfo;
cardInfo.set_id(event.card_id());
cardInfo.set_name(event.card_name());
cardInfo.set_provider_id(event.card_provider_id());
cardInfo.set_pt(pt.toStdString());
cardInfo.set_color(event.color());
cardInfo.set_annotation(event.annotation());
cardInfo.set_destroy_on_zone_change(event.destroy_on_zone_change());
cardInfo.set_face_down(event.face_down());
cardInfo.set_x(event.x());
cardInfo.set_y(event.y());
emit zone->createCardRequested(cardInfo, true);
} }
void PlayerEventHandler::eventSetCardAttr(const Event_SetCardAttr &event, void PlayerEventHandler::eventSetCardAttr(const Event_SetCardAttr &event,
@ -203,7 +193,7 @@ void PlayerEventHandler::eventSetCardAttr(const Event_SetCardAttr &event,
emit logSetTapped(player, nullptr, event.attr_value() == "1"); emit logSetTapped(player, nullptr, event.attr_value() == "1");
} }
} else { } else {
CardState *card = zone->getCard(event.card_id()); CardItem *card = zone->getCard(event.card_id());
if (!card) { if (!card) {
qWarning() << "PlayerEventHandler::eventSetCardAttr: card id=" << event.card_id() << "not found"; qWarning() << "PlayerEventHandler::eventSetCardAttr: card id=" << event.card_id() << "not found";
return; return;
@ -213,7 +203,7 @@ void PlayerEventHandler::eventSetCardAttr(const Event_SetCardAttr &event,
} }
void PlayerEventHandler::setCardAttrHelper(const GameEventContext &context, void PlayerEventHandler::setCardAttrHelper(const GameEventContext &context,
CardState *card, CardItem *card,
CardAttribute attribute, CardAttribute attribute,
const QString &avalue, const QString &avalue,
bool allCards, bool allCards,
@ -274,7 +264,7 @@ void PlayerEventHandler::eventSetCardCounter(const Event_SetCardCounter &event)
return; return;
} }
CardState *card = zone->getCard(event.card_id()); CardItem *card = zone->getCard(event.card_id());
if (!card) { if (!card) {
return; return;
} }
@ -351,13 +341,13 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv
if (x == -1) { if (x == -1) {
x = 0; x = 0;
} }
CardState *card = startZone->takeCard(position, event.card_id(), startZone != targetZone); CardItem *card = startZone->takeCard(position, event.card_id(), startZone != targetZone);
if (card == nullptr) { if (card == nullptr) {
return; return;
} }
const bool zoneChanged = startZone != targetZone; if (startZone != targetZone) {
const bool ownerChanged = zoneChanged && startZone->getPlayer() != targetZone->getPlayer(); card->deleteCardInfoPopup();
emit cardViewRefreshRequested(card, zoneChanged, ownerChanged ? targetZone->getPlayer() : nullptr); }
if (event.has_card_name()) { if (event.has_card_name()) {
QString name = QString::fromStdString(event.card_name()); QString name = QString::fromStdString(event.card_name());
QString providerId = QString providerId =
@ -366,19 +356,27 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv
} }
if (card->getAttachedTo() && (startZone != targetZone)) { if (card->getAttachedTo() && (startZone != targetZone)) {
CardState *parentCard = card->getAttachedTo(); CardItem *parentCard = card->getAttachedTo();
// CardState::setAttachedTo removes the card from the old parent's attachedCards list and emits the zone events.
card->setAttachedTo(nullptr); card->setAttachedTo(nullptr);
parentCard->getZone()->reorganizeCards(); parentCard->getZone()->reorganizeCards();
} }
card->deleteDragItem();
card->setId(event.new_card_id()); card->setId(event.new_card_id());
card->setFaceDown(event.face_down()); card->setFaceDown(event.face_down());
if (startZone != targetZone) { if (startZone != targetZone) {
const QList<CardState *> &attachedCards = card->getAttachedCards(); card->setBeingPointedAt(false);
card->setHovered(false);
const QList<CardItem *> &attachedCards = card->getAttachedCards();
for (auto attachedCard : attachedCards) { for (auto attachedCard : attachedCards) {
emit targetZone->cardAdded(attachedCard); emit targetZone->cardAdded(attachedCard);
} }
if (startZone->getPlayer() != targetZone->getPlayer()) {
card->setOwner(targetZone->getPlayer());
}
} }
// The log event has to be sent before the card is added to the target zone // The log event has to be sent before the card is added to the target zone
@ -406,7 +404,7 @@ void PlayerEventHandler::eventFlipCard(const Event_FlipCard &event)
if (!zone) { if (!zone) {
return; return;
} }
CardState *card = zone->getCard(event.card_id()); CardItem *card = zone->getCard(event.card_id());
if (!card) { if (!card) {
return; return;
} }
@ -429,28 +427,20 @@ void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event)
return; return;
} }
CardState *card = zone->getCard(event.card_id()); CardItem *card = zone->getCard(event.card_id());
if (!card) { if (!card) {
return; return;
} }
QList<CardState *> attachedCards = card->getAttachedCards(); QList<CardItem *> attachedCards = card->getAttachedCards();
// This list is always empty except for buggy server implementations. // This list is always empty except for buggy server implementations.
for (auto &attachedCard : attachedCards) { for (auto &attachedCard : attachedCards) {
card->removeAttachedCard(attachedCard);
attachedCard->setGridPoint(QPoint(-1, attachedCard->getGridPoint().y()));
attachedCard->setAttachedTo(nullptr); attachedCard->setAttachedTo(nullptr);
if (attachedCard->getZone() == nullptr) {
attachedCard->deleteView();
} else {
emit attachedCard->getZone()->cardAdded(attachedCard);
attachedCard->getZone()->reorganizeCards();
}
} }
emit logDestroyCard(player, card->getName()); emit logDestroyCard(player, card->getName());
zone->takeCard(-1, event.card_id(), true); zone->takeCard(-1, event.card_id(), true);
card->deleteView(); card->deleteLater();
} }
void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event) void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
@ -458,7 +448,7 @@ void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
const QMap<int, PlayerLogic *> &playerList = player->getGame()->getPlayerManager()->getPlayers(); const QMap<int, PlayerLogic *> &playerList = player->getGame()->getPlayerManager()->getPlayers();
PlayerLogic *targetPlayer = nullptr; PlayerLogic *targetPlayer = nullptr;
CardZoneLogic *targetZone = nullptr; CardZoneLogic *targetZone = nullptr;
CardState *targetCard = nullptr; CardItem *targetCard = nullptr;
if (event.has_target_player_id()) { if (event.has_target_player_id()) {
targetPlayer = playerList.value(event.target_player_id(), 0); targetPlayer = playerList.value(event.target_player_id(), 0);
if (targetPlayer) { if (targetPlayer) {
@ -474,12 +464,12 @@ void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
return; return;
} }
CardState *startCard = startZone->getCard(event.card_id()); CardItem *startCard = startZone->getCard(event.card_id());
if (!startCard) { if (!startCard) {
return; return;
} }
CardState *oldParent = startCard->getAttachedTo(); CardItem *oldParent = startCard->getAttachedTo();
startCard->setAttachedTo(targetCard); startCard->setAttachedTo(targetCard);
@ -508,7 +498,7 @@ void PlayerEventHandler::eventDrawCards(const Event_DrawCards &event)
if (listSize) { if (listSize) {
for (int i = 0; i < listSize; ++i) { for (int i = 0; i < listSize; ++i) {
const ServerInfo_Card &cardInfo = event.cards(i); const ServerInfo_Card &cardInfo = event.cards(i);
CardState *card = _deck->takeCard(0, cardInfo.id()); CardItem *card = _deck->takeCard(0, cardInfo.id());
QString cardName = QString::fromStdString(cardInfo.name()); QString cardName = QString::fromStdString(cardInfo.name());
QString providerId = QString::fromStdString(cardInfo.provider_id()); QString providerId = QString::fromStdString(cardInfo.provider_id());
card->setCardRef({cardName, providerId}); card->setCardRef({cardName, providerId});
@ -556,7 +546,7 @@ void PlayerEventHandler::eventRevealCards(const Event_RevealCards &event, EventP
for (const auto &card : cardList) { for (const auto &card : cardList) {
QString cardName = QString::fromStdString(card->name()); QString cardName = QString::fromStdString(card->name());
QString providerId = QString::fromStdString(card->provider_id()); QString providerId = QString::fromStdString(card->provider_id());
CardState *cardItem = zone->getCard(card->id()); CardItem *cardItem = zone->getCard(card->id());
if (!cardItem) { if (!cardItem) {
continue; continue;
} }

View file

@ -27,7 +27,7 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <sys/types.h> #include <sys/types.h>
class CardState; class CardItem;
class CardZoneLogic; class CardZoneLogic;
class PlayerLogic; class PlayerLogic;
class Event_AttachCard; class Event_AttachCard;
@ -61,7 +61,7 @@ template <typename T> class QList;
* Design notes: * Design notes:
* - All event handlers assume events are authoritative and already validated * - All event handlers assume events are authoritative and already validated
* by the server. * by the server.
* - Most handlers mutate both logical state (CardState, CardZoneLogic, counters) * - Most handlers mutate both logical state (CardItem, CardZoneLogic, counters)
* and visual/UI state (views, arrows, menus). * and visual/UI state (views, arrows, menus).
* - Logging signals are emitted *after* or *during* state mutation, depending * - Logging signals are emitted *after* or *during* state mutation, depending
* on whether later mutations would invalidate log data. * on whether later mutations would invalidate log data.
@ -238,7 +238,7 @@ signals:
void logUndoDraw(PlayerLogic *player, QString cardName); void logUndoDraw(PlayerLogic *player, QString cardName);
void logUndoDrawFailed(PlayerLogic *player); void logUndoDrawFailed(PlayerLogic *player);
void logMoveCard(PlayerLogic *player, void logMoveCard(PlayerLogic *player,
CardState *card, CardItem *card,
CardZoneLogic *startZone, CardZoneLogic *startZone,
int oldX, int oldX,
CardZoneLogic *targetZone, CardZoneLogic *targetZone,
@ -248,11 +248,11 @@ signals:
void logAttachCard(PlayerLogic *player, QString cardName, PlayerLogic *targetPlayer, QString targetCardName); void logAttachCard(PlayerLogic *player, QString cardName, PlayerLogic *targetPlayer, QString targetCardName);
void logUnattachCard(PlayerLogic *player, QString cardName); void logUnattachCard(PlayerLogic *player, QString cardName);
void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue); void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue);
void logSetTapped(PlayerLogic *player, CardState *card, bool tapped); void logSetTapped(PlayerLogic *player, CardItem *card, bool tapped);
void logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue); void logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue);
void logSetDoesntUntap(PlayerLogic *player, CardState *card, bool doesntUntap); void logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap);
void logSetPT(PlayerLogic *player, CardState *card, QString newPT); void logSetPT(PlayerLogic *player, CardItem *card, QString newPT);
void logSetAnnotation(PlayerLogic *player, CardState *card, QString newAnnotation); void logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation);
void logDumpZone(PlayerLogic *player, CardZoneLogic *zone, int numberCards, bool isReversed = false); void logDumpZone(PlayerLogic *player, CardZoneLogic *zone, int numberCards, bool isReversed = false);
void logRevealCards(PlayerLogic *player, void logRevealCards(PlayerLogic *player,
CardZoneLogic *zone, CardZoneLogic *zone,
@ -266,23 +266,15 @@ signals:
void logAlwaysLookAtTopCard(PlayerLogic *player, CardZoneLogic *zone, bool reveal); void logAlwaysLookAtTopCard(PlayerLogic *player, CardZoneLogic *zone, bool reveal);
/** @} */ /** @} */
void cardZoneChanged(CardState *card, bool sameZone); void cardZoneChanged(CardItem *card, bool sameZone);
void requestCardMenuUpdate(CardState *card); void requestCardMenuUpdate(const CardItem *card);
/**
* @brief A card was just moved by the server and its view needs refreshing.
*
* @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 cardViewRefreshRequested(CardState *card, bool zoneChanged, PlayerLogic *newOwner);
private: private:
/** Owning player instance. */ /** Owning player instance. */
PlayerLogic *player; PlayerLogic *player;
void setCardAttrHelper(const GameEventContext &context, void setCardAttrHelper(const GameEventContext &context,
CardState *card, CardItem *card,
CardAttribute attribute, CardAttribute attribute,
const QString &avalue, const QString &avalue,
bool allCards, bool allCards,

View file

@ -1,9 +1,8 @@
#include "player_logic.h" #include "player_logic.h"
#include "../../game_graphics/board/card_item.h"
#include "../abstract_game.h" #include "../abstract_game.h"
#include "../board/arrow_data.h" #include "../board/arrow_data.h"
#include "../board/card_list.h"
#include "../board/card_state.h"
#include "../board/counter_state.h" #include "../board/counter_state.h"
#include "../game_meta_info.h" #include "../game_meta_info.h"
#include "../zones/hand_zone_logic.h" #include "../zones/hand_zone_logic.h"
@ -34,17 +33,10 @@
#include <qalgorithms.h> #include <qalgorithms.h>
#include <string> #include <string>
PlayerLogic::PlayerLogic(const ServerInfo_User &info, PlayerLogic::PlayerLogic(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent)
int _id,
bool _local,
bool _judge,
AbstractGame *_parent,
IInterfaceSettingsProvider *interfaceSettings,
ICardPreferenceProvider *cardPreferences)
: QObject(_parent), game(_parent), playerInfo(new PlayerInfo(info, _id, _local, _judge)), : QObject(_parent), game(_parent), playerInfo(new PlayerInfo(info, _id, _local, _judge)),
playerEventHandler(new PlayerEventHandler(this)), playerEventHandler(new PlayerEventHandler(this)), playerActions(new PlayerActions(this)), active(false),
playerActions(new PlayerActions(this, interfaceSettings, cardPreferences)), active(false), conceded(false), conceded(false), zoneId(0), dialogSemaphore(false)
zoneId(0), dialogSemaphore(false)
{ {
initializeZones(); initializeZones();
} }
@ -180,20 +172,22 @@ void PlayerLogic::processPlayerInfo(const ServerInfo_Player &info)
const int cardListSize = zoneInfo.card_list_size(); const int cardListSize = zoneInfo.card_list_size();
if (!cardListSize) { if (!cardListSize) {
for (int j = 0; j < zoneInfo.card_count(); ++j) { for (int j = 0; j < zoneInfo.card_count(); ++j) {
emit zone->createCardRequested(ServerInfo_Card(), false); zone->addCard(new CardItem(this), false, -1);
} }
} else { } else {
for (int j = 0; j < cardListSize; ++j) { for (int j = 0; j < cardListSize; ++j) {
ServerInfo_Card cardInfo = zoneInfo.card_list(j); const ServerInfo_Card &cardInfo = zoneInfo.card_list(j);
auto *card = new CardItem(this);
card->processCardInfo(cardInfo);
// Zones without coordinates (hand, piles, stack) preserve the order // Zones without coordinates (hand, piles, stack) preserve the order
// they arrive in on the server in the positions of their cards list. // they arrive in on the server in the positions of their cards list.
// The x coordinate of such cards is always 0, so inserting at it // The x coordinate of such cards is always 0, so inserting at it
// would reverse the list on reconnect. Append instead. // would reverse the list on reconnect. Append instead.
if (!zoneInfo.with_coords()) { if (zoneInfo.with_coords()) {
cardInfo.set_x(-1); zone->addCard(card, false, cardInfo.x(), cardInfo.y());
cardInfo.set_y(-1); } else {
zone->addCard(card, false, -1);
} }
emit zone->createCardRequested(cardInfo, false);
} }
} }
if (zoneInfo.has_always_reveal_top_card()) { if (zoneInfo.has_always_reveal_top_card()) {
@ -225,8 +219,8 @@ void PlayerLogic::processCardAttachment(const ServerInfo_Player &info)
for (int j = 0; j < cardListSize; ++j) { for (int j = 0; j < cardListSize; ++j) {
const ServerInfo_Card &cardInfo = zoneInfo.card_list(j); const ServerInfo_Card &cardInfo = zoneInfo.card_list(j);
if (cardInfo.has_attach_player_id()) { if (cardInfo.has_attach_player_id()) {
CardState *startCard = zone->getCard(cardInfo.id()); CardItem *startCard = zone->getCard(cardInfo.id());
CardState *targetCard = CardItem *targetCard =
game->getCard(cardInfo.attach_player_id(), QString::fromStdString(cardInfo.attach_zone()), game->getCard(cardInfo.attach_player_id(), QString::fromStdString(cardInfo.attach_zone()),
cardInfo.attach_card_id()); cardInfo.attach_card_id());
if (!targetCard) { if (!targetCard) {
@ -245,19 +239,19 @@ void PlayerLogic::processCardAttachment(const ServerInfo_Player &info)
} }
} }
void PlayerLogic::addCard(AbstractCardItem *card) void PlayerLogic::addCard(CardItem *card)
{ {
emit newCardAdded(card); emit newCardAdded(card);
} }
void PlayerLogic::deleteCard(CardState *card) void PlayerLogic::deleteCard(CardItem *card)
{ {
if (card == nullptr) { if (card == nullptr) {
return; return;
} else if (dialogSemaphore) { } else if (dialogSemaphore) {
cardsToDelete.append(card); cardsToDelete.append(card);
} else { } else {
card->deleteView(); card->deleteLater();
} }
} }
@ -338,7 +332,7 @@ bool PlayerLogic::clearCardsToDelete()
for (auto &i : cardsToDelete) { for (auto &i : cardsToDelete) {
if (i != nullptr) { if (i != nullptr) {
i->deleteView(); i->deleteLater();
} }
} }
cardsToDelete.clear(); cardsToDelete.clear();

View file

@ -7,8 +7,6 @@
#ifndef PLAYER_H #ifndef PLAYER_H
#define PLAYER_H #define PLAYER_H
#include "../board/arrow_data.h"
#include "../board/counter_state.h"
#include "../zones/card_zone_logic.h" #include "../zones/card_zone_logic.h"
#include "../zones/hand_zone_logic.h" #include "../zones/hand_zone_logic.h"
#include "../zones/pile_zone_logic.h" #include "../zones/pile_zone_logic.h"
@ -19,16 +17,12 @@
#include "libcockatrice/utility/playmat_params.h" #include "libcockatrice/utility/playmat_params.h"
#include <QColor> #include <QColor>
#include <QInputDialog>
#include <QList> #include <QList>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QMap> #include <QMap>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QTimer>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/filters/filter_string.h>
#include <libcockatrice/protocol/pb/card_attributes.pb.h>
#include <libcockatrice/protocol/pb/game_event.pb.h>
#include <libcockatrice/utility/zone_names.h> #include <libcockatrice/utility/zone_names.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
@ -37,6 +31,7 @@
// signatures, which the moc-generated code needs to be complete. // signatures, which the moc-generated code needs to be complete.
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h> #include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
class CardItem;
class CounterState; class CounterState;
class QWidget; class QWidget;
class ServerInfo_PlayerProperties; class ServerInfo_PlayerProperties;
@ -47,8 +42,6 @@ template <class T> class QSharedPointer;
inline Q_LOGGING_CATEGORY(PlayerLog, "player"); inline Q_LOGGING_CATEGORY(PlayerLog, "player");
class AbstractCardItem; class AbstractCardItem;
class AbstractGame; class AbstractGame;
class ICardPreferenceProvider;
class IInterfaceSettingsProvider;
class PlayerInfo; class PlayerInfo;
class PlayerEventHandler; class PlayerEventHandler;
class PlayerActions; class PlayerActions;
@ -74,7 +67,7 @@ signals:
/** @brief Emitted when the remote playmat (card/params) is updated from player properties. */ /** @brief Emitted when the remote playmat (card/params) is updated from player properties. */
void playmatChanged(); void playmatChanged();
void newCardAdded(AbstractCardItem *card); void newCardAdded(AbstractCardItem *card);
void requestCardMenuUpdate(CardState *card); void requestCardMenuUpdate(const CardItem *card);
void counterAdded(CounterState *state); void counterAdded(CounterState *state);
void counterRemoved(int counterId); void counterRemoved(int counterId);
void rearrangeCounters(); void rearrangeCounters();
@ -94,13 +87,7 @@ public slots:
void onRequestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed); void onRequestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed);
public: public:
PlayerLogic(const ServerInfo_User &info, PlayerLogic(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent);
int _id,
bool _local,
bool _judge,
AbstractGame *_parent,
IInterfaceSettingsProvider *interfaceSettings = nullptr,
ICardPreferenceProvider *cardPreferences = nullptr);
~PlayerLogic() override; ~PlayerLogic() override;
void initializeZones(); void initializeZones();
@ -110,8 +97,8 @@ public:
void processPlayerInfo(const ServerInfo_Player &info); void processPlayerInfo(const ServerInfo_Player &info);
void processCardAttachment(const ServerInfo_Player &info); void processCardAttachment(const ServerInfo_Player &info);
void addCard(AbstractCardItem *c); void addCard(CardItem *c);
void deleteCard(CardState *c); void deleteCard(CardItem *c);
bool clearCardsToDelete(); bool clearCardsToDelete();
@ -263,7 +250,7 @@ private:
QMap<int, CounterState *> counters; QMap<int, CounterState *> counters;
bool dialogSemaphore; bool dialogSemaphore;
QList<CardState *> cardsToDelete; QList<CardItem *> cardsToDelete;
// Playmat from player properties (for opponent display) // Playmat from player properties (for opponent display)
CardRef remotePlaymatCard; CardRef remotePlaymatCard;
@ -271,4 +258,15 @@ private:
bool hasRemotePlaymat = false; bool hasRemotePlaymat = false;
}; };
class AnnotationDialog : public QInputDialog
{
Q_OBJECT
void keyPressEvent(QKeyEvent *e) override;
public:
explicit AnnotationDialog(QWidget *parent = nullptr) : QInputDialog(parent)
{
}
};
#endif #endif

View file

@ -12,12 +12,9 @@
PlayerManager::PlayerManager(AbstractGame *_game, PlayerManager::PlayerManager(AbstractGame *_game,
int _localPlayerId, int _localPlayerId,
bool _localPlayerIsJudge, bool _localPlayerIsJudge,
bool localPlayerIsSpectator, bool localPlayerIsSpectator)
IInterfaceSettingsProvider *_interfaceSettings,
ICardPreferenceProvider *_cardPreferences)
: QObject(_game), game(_game), players(QMap<int, PlayerLogic *>()), localPlayerId(_localPlayerId), : QObject(_game), game(_game), players(QMap<int, PlayerLogic *>()), localPlayerId(_localPlayerId),
localPlayerIsJudge(_localPlayerIsJudge), localPlayerIsSpectator(localPlayerIsSpectator), localPlayerIsJudge(_localPlayerIsJudge), localPlayerIsSpectator(localPlayerIsSpectator)
interfaceSettings(_interfaceSettings), cardPreferences(_cardPreferences)
{ {
} }
@ -55,7 +52,7 @@ bool PlayerManager::isLocalPlayer(int playerId)
PlayerLogic *PlayerManager::addPlayer(int playerId, const ServerInfo_User &info) PlayerLogic *PlayerManager::addPlayer(int playerId, const ServerInfo_User &info)
{ {
auto *newPlayer = new PlayerLogic(info, playerId, isLocalPlayer(playerId) || game->getGameState()->getIsLocalGame(), auto *newPlayer = new PlayerLogic(info, playerId, isLocalPlayer(playerId) || game->getGameState()->getIsLocalGame(),
isJudge(), getGame(), interfaceSettings, cardPreferences); isJudge(), getGame());
connect(newPlayer, &PlayerLogic::concededChanged, this, &PlayerManager::onPlayerConceded); connect(newPlayer, &PlayerLogic::concededChanged, this, &PlayerManager::onPlayerConceded);
players.insert(playerId, newPlayer); players.insert(playerId, newPlayer);
emit playerAdded(newPlayer); emit playerAdded(newPlayer);

View file

@ -16,8 +16,6 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
class AbstractGame; class AbstractGame;
class ICardPreferenceProvider;
class IInterfaceSettingsProvider;
class PlayerLogic; class PlayerLogic;
class PlayerManager : public QObject class PlayerManager : public QObject
@ -25,12 +23,7 @@ class PlayerManager : public QObject
Q_OBJECT Q_OBJECT
public: public:
PlayerManager(AbstractGame *_game, PlayerManager(AbstractGame *_game, int _localPlayerId, bool _localPlayerIsJudge, bool localPlayerIsSpectator);
int _localPlayerId,
bool _localPlayerIsJudge,
bool localPlayerIsSpectator,
IInterfaceSettingsProvider *interfaceSettings = nullptr,
ICardPreferenceProvider *cardPreferences = nullptr);
AbstractGame *game; AbstractGame *game;
QMap<int, PlayerLogic *> players; QMap<int, PlayerLogic *> players;
@ -38,9 +31,6 @@ public:
bool localPlayerIsJudge; bool localPlayerIsJudge;
bool localPlayerIsSpectator; bool localPlayerIsSpectator;
QMap<int, ServerInfo_User> spectators; QMap<int, ServerInfo_User> spectators;
// Borrowed settings providers forwarded to each PlayerLogic (owned by the application).
IInterfaceSettingsProvider *interfaceSettings;
ICardPreferenceProvider *cardPreferences;
[[nodiscard]] bool isSpectator() const [[nodiscard]] bool isSpectator() const
{ {

View file

@ -9,15 +9,10 @@
class GameReplay; class GameReplay;
class QObject; class QObject;
Replay::Replay(QObject *_parent, Replay::Replay(QObject *_parent, const GameReplay *_replay, bool isLocalGame) : AbstractGame(_parent)
const GameReplay *_replay,
bool isLocalGame,
IInterfaceSettingsProvider *interfaceSettings,
ICardPreferenceProvider *cardPreferences)
: AbstractGame(_parent)
{ {
gameState = new GameState(this, 0, -1, isLocalGame, {}, false, false, -1, false); gameState = new GameState(this, 0, -1, isLocalGame, {}, false, false, -1, false);
connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged); connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged);
playerManager = new PlayerManager(this, -1, false, true, interfaceSettings, cardPreferences); playerManager = new PlayerManager(this, -1, false, true);
loadReplay(_replay); loadReplay(_replay);
} }

View file

@ -0,0 +1,26 @@
/**
* @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

View file

@ -5,9 +5,9 @@ namespace CardZoneAlgorithms
{ {
/** /**
* Shared insertion logic for zones where cards follow the standard pattern: * Shared insertion logic for zones where cards become visible on add and follow
* clamp index, insert, clear identity if contents unknown, reset state. * the standard pattern: clamp index, insert, clear identity if contents unknown,
* Visibility and parenting are handled by the graphics zone's onCardAdded(). * reset state, show card.
* *
* Zones with different post-add behavior (signal connections, positional resets, * Zones with different post-add behavior (signal connections, positional resets,
* hidden cards, or coordinate-based placement) should NOT use this — implement * hidden cards, or coordinate-based placement) should NOT use this — implement
@ -18,7 +18,8 @@ namespace CardZoneAlgorithms
* *
* @tparam CardList Must provide: size() -> int, insert(int, CardType*), * @tparam CardList Must provide: size() -> int, insert(int, CardType*),
* getContentsKnown() -> bool * getContentsKnown() -> bool
* @tparam CardType Must provide: setId(int), setCardRef(CardRefType), resetState(bool) * @tparam CardType Must provide: setId(int), setCardRef(CardRefType),
* resetState(bool), setVisible(bool)
* @param keepAnnotations Forwarded to card->resetState(). Stack-like zones preserve * @param keepAnnotations Forwarded to card->resetState(). Stack-like zones preserve
* annotations across zone transitions; hand-like zones clear them. * annotations across zone transitions; hand-like zones clear them.
*/ */
@ -36,6 +37,7 @@ void addCardToList(CardList &cards, CardType *card, int x, bool keepAnnotations)
} }
card->resetState(keepAnnotations); card->resetState(keepAnnotations);
card->setVisible(true);
} }
} // namespace CardZoneAlgorithms } // namespace CardZoneAlgorithms

View file

@ -1,11 +1,13 @@
#include "card_zone_logic.h" #include "card_zone_logic.h"
#include "../../client/translation.h"
#include "../../game_graphics/board/card_item.h"
#include "../../game_graphics/zones/card_zone.h"
#include "../../game_graphics/zones/view_zone.h"
#include "../board/card_list.h" #include "../board/card_list.h"
#include "../board/card_state.h"
#include "../player/player_actions.h" #include "../player/player_actions.h"
#include "../player/player_info.h" #include "../player/player_info.h"
#include "../player/player_logic.h" #include "../player/player_logic.h"
#include "../translation.h"
#include "libcockatrice/card/database/card_database.h" #include "libcockatrice/card/database/card_database.h"
#include "view_zone_logic.h" #include "view_zone_logic.h"
@ -42,24 +44,19 @@ CardZoneLogic::CardZoneLogic(PlayerLogic *_player,
&CardZoneLogic::refreshCardInfos); &CardZoneLogic::refreshCardInfos);
} }
void CardZoneLogic::addCard(CardState *card, const bool reorganize, const int x, const int y) void CardZoneLogic::addCard(CardItem *card, const bool reorganize, const int x, const int y)
{ {
if (!card) { if (!card) {
qCWarning(CardZoneLogicLog) << "CardZoneLogic::addCard() card is null; this shouldn't normally happen"; qCWarning(CardZoneLog) << "CardZoneLogic::addCard() card is null; this shouldn't normally happen";
return; return;
} }
for (auto *viewLogic : views) { for (auto *view : views) {
if (viewLogic->prepareAddCard(x)) { if (qobject_cast<ZoneViewZoneLogic *>(view->getLogic())->prepareAddCard(x)) {
ServerInfo_Card cardInfo; auto copy = new CardItem(player, nullptr, card->getCardRef(), card->getId());
const CardRef &cardRef = card->getCardRef(); copy->setFaceDown(card->getFaceDown());
cardInfo.set_name(cardRef.name.toStdString());
cardInfo.set_provider_id(cardRef.providerId.toStdString()); view->getLogic()->addCard(copy, reorganize, x, y);
cardInfo.set_id(card->getId());
cardInfo.set_face_down(card->getFaceDown());
cardInfo.set_x(x);
cardInfo.set_y(y);
emit viewLogic->createCardRequested(cardInfo, reorganize);
} }
} }
@ -74,7 +71,7 @@ void CardZoneLogic::addCard(CardState *card, const bool reorganize, const int x,
emit cardCountChanged(); emit cardCountChanged();
} }
CardState *CardZoneLogic::takeCard(int position, int cardId, bool toNewZone) CardItem *CardZoneLogic::takeCard(int position, int cardId, bool toNewZone)
{ {
if (position == -1) { if (position == -1) {
// position == -1 means either that the zone is indexed by card id // position == -1 means either that the zone is indexed by card id
@ -93,11 +90,11 @@ CardState *CardZoneLogic::takeCard(int position, int cardId, bool toNewZone)
return nullptr; return nullptr;
} }
for (auto *viewLogic : views) { for (auto *view : views) {
viewLogic->removeCard(position, toNewZone); qobject_cast<ZoneViewZoneLogic *>(view->getLogic())->removeCard(position, toNewZone);
} }
CardState *c = cards.takeAt(position); CardItem *c = cards.takeAt(position);
c->setId(cardId); c->setId(cardId);
@ -106,11 +103,11 @@ CardState *CardZoneLogic::takeCard(int position, int cardId, bool toNewZone)
return c; return c;
} }
CardState *CardZoneLogic::getCard(int cardId) CardItem *CardZoneLogic::getCard(int cardId)
{ {
CardState *c = cards.findCard(cardId); CardItem *c = cards.findCard(cardId);
if (!c) { if (!c) {
qCWarning(CardZoneLogicLog) << "CardZoneLogic::getCard: card id=" << cardId << "not found"; qCWarning(CardZoneLog) << "CardZoneLogic::getCard: card id=" << cardId << "not found";
return nullptr; return nullptr;
} }
// If the card's id is -1, this zone is invisible, // If the card's id is -1, this zone is invisible,
@ -122,10 +119,10 @@ CardState *CardZoneLogic::getCard(int cardId)
return c; return c;
} }
void CardZoneLogic::removeCard(CardState *card) void CardZoneLogic::removeCard(CardItem *card)
{ {
if (!card) { if (!card) {
qCWarning(CardZoneLogicLog) << "CardZoneLogic::removeCard: card is null, this shouldn't normally happen"; qCWarning(CardZoneLog) << "CardZoneLogic::removeCard: card is null, this shouldn't normally happen";
return; return;
} }
@ -138,8 +135,8 @@ void CardZoneLogic::removeCard(CardState *card)
void CardZoneLogic::refreshCardInfos() void CardZoneLogic::refreshCardInfos()
{ {
for (const auto &cardState : cards) { for (const auto &cardItem : cards) {
cardState->refreshCardInfo(); cardItem->refreshCardInfo();
} }
} }
@ -171,17 +168,17 @@ void CardZoneLogic::clearContents()
const CardList toClear = cards; const CardList toClear = cards;
// Detach and notify attached cards and zones *before* deleting anything. // Detach and notify attached cards and zones *before* deleting anything.
for (CardState *card : toClear) { for (CardItem *card : toClear) {
// If an incorrectly implemented server doesn't return attached cards to whom they belong before dropping a // If an incorrectly implemented server doesn't return attached cards to whom they belong before dropping a
// player, we have to return them to avoid a crash. // player, we have to return them to avoid a crash.
const QList<CardState *> &attachedCards = card->getAttachedCards(); const QList<CardItem *> &attachedCards = card->getAttachedCards();
for (CardState *attachedCard : attachedCards) { for (CardItem *attachedCard : attachedCards) {
emit attachedCard->getZone()->cardAdded(attachedCard); emit attachedCard->getZone()->cardAdded(attachedCard);
} }
} }
// Now request deletions after all manipulations are done. // Now request deletions after all manipulations are done.
for (CardState *card : toClear) { for (CardItem *card : toClear) {
player->deleteCard(card); player->deleteCard(card);
} }

View file

@ -7,30 +7,28 @@
#ifndef COCKATRICE_CARD_ZONE_LOGIC_H #ifndef COCKATRICE_CARD_ZONE_LOGIC_H
#define COCKATRICE_CARD_ZONE_LOGIC_H #define COCKATRICE_CARD_ZONE_LOGIC_H
#include "../../client/translation.h"
#include "../board/card_list.h" #include "../board/card_list.h"
#include "../translation.h"
#include <QList> #include <QList>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class CardItem;
inline Q_LOGGING_CATEGORY(CardZoneLogicLog, "card_zone_logic"); inline Q_LOGGING_CATEGORY(CardZoneLogicLog, "card_zone_logic");
class PlayerLogic; class PlayerLogic;
class ZoneViewZoneLogic; class ZoneViewZone;
class CardDragItem;
class CardZoneLogic : public QObject class CardZoneLogic : public QObject
{ {
Q_OBJECT Q_OBJECT
signals: signals:
void cardAdded(CardState *addedCard); void cardAdded(CardItem *addedCard);
/** @brief Asks the graphics side to build the card item for @p cardInfo and add it to this zone. */
void createCardRequested(const ServerInfo_Card &cardInfo, bool reorganize);
void cardCountChanged(); void cardCountChanged();
void reorganizeCards(); void reorganizeCards();
void updateGraphics(); void updateGraphics();
@ -45,17 +43,16 @@ public:
bool _contentsKnown, bool _contentsKnown,
QObject *parent = nullptr); QObject *parent = nullptr);
void addCard(CardState *card, bool reorganize, int x, int y = -1); void addCard(CardItem *card, bool reorganize, int x, int y = -1);
// getCard() finds a card by id. // getCard() finds a card by id.
CardState *getCard(int cardId); CardItem *getCard(int cardId);
void removeCard(CardState *card); void removeCard(CardItem *card);
// takeCard() finds a card by position and removes it from the zone and from all of its views. // takeCard() finds a card by position and removes it from the zone and from all of its views.
virtual CardState *takeCard(int position, int cardId, bool canResize = true); virtual CardItem *takeCard(int position, int cardId, bool canResize = true);
void rawInsertCard(CardState *card, int index) void rawInsertCard(CardItem *card, int index)
{ {
cards.insert(index, card); cards.insert(index, card);
emit cardCountChanged();
} }
[[nodiscard]] const CardList &getCards() const [[nodiscard]] const CardList &getCards() const
@ -66,7 +63,6 @@ public:
void sortCards(const QList<CardList::SortOption> &options) void sortCards(const QList<CardList::SortOption> &options)
{ {
cards.sortBy(options); cards.sortBy(options);
emit cardCountChanged();
} }
[[nodiscard]] QString getName() const [[nodiscard]] QString getName() const
{ {
@ -81,7 +77,7 @@ public:
{ {
return cards.getContentsKnown(); return cards.getContentsKnown();
} }
QList<ZoneViewZoneLogic *> &getViews() QList<ZoneViewZone *> &getViews()
{ {
return views; return views;
} }
@ -113,12 +109,12 @@ protected:
PlayerLogic *player; PlayerLogic *player;
QString name; QString name;
CardList cards; CardList cards;
QList<ZoneViewZoneLogic *> views; QList<ZoneViewZone *> views;
bool hasCardAttr; bool hasCardAttr;
bool isShufflable; bool isShufflable;
bool alwaysRevealTopCard; bool alwaysRevealTopCard;
virtual void addCardImpl(CardState *card, int x, int y) = 0; virtual void addCardImpl(CardItem *card, int x, int y) = 0;
}; };
#endif // COCKATRICE_CARD_ZONE_LOGIC_H #endif // COCKATRICE_CARD_ZONE_LOGIC_H

View file

@ -1,6 +1,6 @@
#include "hand_zone_logic.h" #include "hand_zone_logic.h"
#include "../board/card_state.h" #include "../../game_graphics/board/card_item.h"
#include "card_zone_algorithms.h" #include "card_zone_algorithms.h"
class PlayerLogic; class PlayerLogic;
@ -16,7 +16,7 @@ HandZoneLogic::HandZoneLogic(PlayerLogic *_player,
{ {
} }
void HandZoneLogic::addCardImpl(CardState *card, int x, int /*y*/) void HandZoneLogic::addCardImpl(CardItem *card, int x, int /*y*/)
{ {
CardZoneAlgorithms::addCardToList(cards, card, x, false); CardZoneAlgorithms::addCardToList(cards, card, x, false);
} }

View file

@ -26,7 +26,7 @@ public:
QObject *parent = nullptr); QObject *parent = nullptr);
protected: protected:
void addCardImpl(CardState *card, int x, int y) override; void addCardImpl(CardItem *card, int x, int y) override;
}; };
#endif // COCKATRICE_HAND_ZONE_LOGIC_H #endif // COCKATRICE_HAND_ZONE_LOGIC_H

View file

@ -1,6 +1,10 @@
#include "pile_zone_logic.h" #include "pile_zone_logic.h"
#include "../board/card_state.h" #include "../../game_graphics/board/card_item.h"
#include "../board/card_list.h"
class PlayerLogic;
class QObject;
PileZoneLogic::PileZoneLogic(PlayerLogic *_player, PileZoneLogic::PileZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
@ -12,14 +16,15 @@ PileZoneLogic::PileZoneLogic(PlayerLogic *_player,
{ {
} }
void PileZoneLogic::addCardImpl(CardState *card, int x, int /*y*/) void PileZoneLogic::addCardImpl(CardItem *card, int x, int /*y*/)
{ {
connect(card, &CardState::cardPixmapUpdated, this, &PileZoneLogic::callUpdate); connect(card, &CardItem::sigPixmapUpdated, this, &PileZoneLogic::callUpdate);
// if x is negative set it to add at end // if x is negative set it to add at end
if (x < 0 || x >= cards.size()) { if (x < 0 || x >= cards.size()) {
x = cards.size(); x = cards.size();
} }
cards.insert(x, card); cards.insert(x, card);
card->setPos(0, 0);
if (!contentsKnown()) { if (!contentsKnown()) {
card->setCardRef({}); card->setCardRef({});
card->setId(-1); card->setId(-1);
@ -28,5 +33,6 @@ void PileZoneLogic::addCardImpl(CardState *card, int x, int /*y*/)
cards.at(x + 1)->setCardRef({}); cards.at(x + 1)->setCardRef({});
} }
} }
card->resetState(false); card->setVisible(false);
card->resetState();
} }

View file

@ -31,7 +31,7 @@ public:
QObject *parent = nullptr); QObject *parent = nullptr);
protected: protected:
void addCardImpl(CardState *card, int x, int y) override; void addCardImpl(CardItem *card, int x, int y) override;
}; };
#endif // COCKATRICE_PILE_ZONE_LOGIC_H #endif // COCKATRICE_PILE_ZONE_LOGIC_H

View file

@ -1,6 +1,6 @@
#include "stack_zone_logic.h" #include "stack_zone_logic.h"
#include "../board/card_state.h" #include "../../game_graphics/board/card_item.h"
#include "card_zone_algorithms.h" #include "card_zone_algorithms.h"
class PlayerLogic; class PlayerLogic;
@ -16,7 +16,7 @@ StackZoneLogic::StackZoneLogic(PlayerLogic *_player,
{ {
} }
void StackZoneLogic::addCardImpl(CardState *card, int x, int /*y*/) void StackZoneLogic::addCardImpl(CardItem *card, int x, int /*y*/)
{ {
CardZoneAlgorithms::addCardToList(cards, card, x, true); CardZoneAlgorithms::addCardToList(cards, card, x, true);
} }

View file

@ -26,7 +26,7 @@ public:
QObject *parent = nullptr); QObject *parent = nullptr);
protected: protected:
void addCardImpl(CardState *card, int x, int y) override; void addCardImpl(CardItem *card, int x, int y) override;
}; };
#endif // COCKATRICE_STACK_ZONE_LOGIC_H #endif // COCKATRICE_STACK_ZONE_LOGIC_H

View file

@ -1,9 +1,14 @@
#include "table_zone_logic.h" #include "table_zone_logic.h"
#include "../board/card_state.h" #include "../../game_graphics/board/card_item.h"
#include "../board/card_list.h"
#include "libcockatrice/card/card_info.h"
#include <QPoint> #include <QPoint>
class PlayerLogic;
class QObject;
TableZoneLogic::TableZoneLogic(PlayerLogic *_player, TableZoneLogic::TableZoneLogic(PlayerLogic *_player,
const QString &_name, const QString &_name,
bool _hasCardAttr, bool _hasCardAttr,
@ -14,7 +19,7 @@ TableZoneLogic::TableZoneLogic(PlayerLogic *_player,
{ {
} }
void TableZoneLogic::addCardImpl(CardState *card, int _x, int _y) void TableZoneLogic::addCardImpl(CardItem *card, int _x, int _y)
{ {
cards.append(card); cards.append(card);
if (!card->getFaceDown() && card->getPT().isEmpty()) { if (!card->getFaceDown() && card->getPT().isEmpty()) {
@ -24,33 +29,15 @@ void TableZoneLogic::addCardImpl(CardState *card, int _x, int _y)
card->setDoesntUntap(true); card->setDoesntUntap(true);
} }
card->setGridPoint(QPoint(_x, _y)); card->setGridPoint(QPoint(_x, _y));
card->setVisible(true);
} }
CardState *TableZoneLogic::takeCard(int position, int cardId, bool toNewZone) CardItem *TableZoneLogic::takeCard(int position, int cardId, bool toNewZone)
{ {
CardState *result = CardZoneLogic::takeCard(position, cardId); CardItem *result = CardZoneLogic::takeCard(position, cardId);
if (toNewZone) { if (toNewZone) {
emit contentSizeChanged(); emit contentSizeChanged();
} }
return result; return result;
} }
int TableZoneLogic::clampValidTableRow(const int row)
{
if (row < 0) {
return 0;
}
if (row >= ROW_COUNT) {
return ROW_COUNT - 1;
}
return row;
}
int TableZoneLogic::tableRowToGridY(int tableRow)
{
if (tableRow > 2) {
tableRow = 1;
}
return clampValidTableRow(2 - tableRow);
}

View file

@ -29,23 +29,8 @@ public:
bool _contentsKnown, bool _contentsKnown,
QObject *parent = nullptr); QObject *parent = nullptr);
/** @brief Number of rows in the table zone grid (0=creatures, 1=noncreatures, 2=lands). */
static const int ROW_COUNT = 3;
/**
* Clamps a grid row index into [0, ROW_COUNT - 1].
*/
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);
protected: protected:
void addCardImpl(CardState *card, int x, int y) override; void addCardImpl(CardItem *card, int x, int y) override;
/** /**
* @brief Removes a card from view. * @brief Removes a card from view.
@ -53,9 +38,9 @@ protected:
* @param position card position * @param position card position
* @param cardId id of card to take * @param cardId id of card to take
* @param toNewZone Whether the destination of the card is not the same as the starting zone. Defaults to true * @param toNewZone Whether the destination of the card is not the same as the starting zone. Defaults to true
* @return CardState that has been removed * @return CardItem that has been removed
*/ */
CardState *takeCard(int position, int cardId, bool toNewZone = true) override; CardItem *takeCard(int position, int cardId, bool toNewZone = true) override;
}; };
#endif // COCKATRICE_TABLE_ZONE_LOGIC_H #endif // COCKATRICE_TABLE_ZONE_LOGIC_H

View file

@ -1,11 +1,10 @@
#include "view_zone_logic.h" #include "view_zone_logic.h"
#include "../board/card_state.h" #include "../../client/settings/cache_settings.h"
#include "../player/player_actions.h" #include "../../game_graphics/board/card_item.h"
#include "../player/player_logic.h" #include "../board/card_list.h"
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/interfaces/interface_interface_settings_provider.h>
#include <libcockatrice/settings/interface_settings.h> #include <libcockatrice/settings/interface_settings.h>
class PlayerLogic; class PlayerLogic;
@ -23,19 +22,13 @@ ZoneViewZoneLogic::ZoneViewZoneLogic(PlayerLogic *_player,
bool _revealZone, bool _revealZone,
bool _writeableRevealZone, bool _writeableRevealZone,
bool _isReversed, bool _isReversed,
IInterfaceSettingsProvider *_interfaceSettings,
QObject *parent) QObject *parent)
: CardZoneLogic(_player, _origZone->getName(), false, false, true, parent), origZone(_origZone), : CardZoneLogic(_player, _origZone->getName(), false, false, true, parent), origZone(_origZone),
numberCards(_numberCards), revealZone(_revealZone), writeableRevealZone(_writeableRevealZone), numberCards(_numberCards), revealZone(_revealZone), writeableRevealZone(_writeableRevealZone),
isReversed(_isReversed), interfaceSettings(_interfaceSettings) isReversed(_isReversed)
{ {
} }
bool ZoneViewZoneLogic::getCloseEmptyCardView() const
{
return interfaceSettings ? interfaceSettings->getCloseEmptyCardView() : true;
}
/** /**
* Checks if inserting a card at the given position requires an actual new card to be created and added to the view. * Checks if inserting a card at the given position requires an actual new card to be created and added to the view.
* Also does any cardId updates that would be required if a card is inserted in that position. * Also does any cardId updates that would be required if a card is inserted in that position.
@ -43,7 +36,7 @@ bool ZoneViewZoneLogic::getCloseEmptyCardView() const
* Note that this method can end up modifying the cardIds despite returning false. * Note that this method can end up modifying the cardIds despite returning false.
* (for example, if the card is inserted into a hidden portion of the deck while the view is reversed) * (for example, if the card is inserted into a hidden portion of the deck while the view is reversed)
* *
* Make sure to call this method once before calling addCard(), so that you skip creating the card and calling * Make sure to call this method once before calling addCard(), so that you skip creating a new CardItem and calling
* addCard() if it's not required. * addCard() if it's not required.
* *
* @param x The position to insert the card at. * @param x The position to insert the card at.
@ -71,7 +64,7 @@ bool ZoneViewZoneLogic::prepareAddCard(int x)
// autoclose check is done both here and in removeCard // autoclose check is done both here and in removeCard
if (cards.isEmpty() && !doInsert && getCloseEmptyCardView()) { if (cards.isEmpty() && !doInsert && SettingsCache::instance().userInterface().getCloseEmptyCardView()) {
emit closeView(); emit closeView();
} }
@ -82,7 +75,7 @@ bool ZoneViewZoneLogic::prepareAddCard(int x)
* Make sure prepareAddCard() was called before calling addCard(). * Make sure prepareAddCard() was called before calling addCard().
* This method assumes we already checked that the card is being inserted into the visible portion * This method assumes we already checked that the card is being inserted into the visible portion
*/ */
void ZoneViewZoneLogic::addCardImpl(CardState *card, int x, int /*y*/) void ZoneViewZoneLogic::addCardImpl(CardItem *card, int x, int /*y*/)
{ {
if (!isReversed) { if (!isReversed) {
// if x is negative set it to add at end // if x is negative set it to add at end
@ -151,14 +144,14 @@ void ZoneViewZoneLogic::removeCard(int position, bool toNewZone)
return; return;
} }
CardState *card = cards.takeAt(position); CardItem *card = cards.takeAt(position);
card->deleteView(); card->deleteLater();
// The toNewZone check is to prevent the view from auto-closing if the view contains only a single card and that // The toNewZone check is to prevent the view from auto-closing if the view contains only a single card and that
// card gets dragged within the view. // card gets dragged within the view.
// Another autoclose check is done in prepareAddCard so that the view autocloses if the last card was moved to an // Another autoclose check is done in prepareAddCard so that the view autocloses if the last card was moved to an
// unrevealed portion of the same zone. // unrevealed portion of the same zone.
if (cards.isEmpty() && getCloseEmptyCardView() && toNewZone) { if (cards.isEmpty() && SettingsCache::instance().userInterface().getCloseEmptyCardView() && toNewZone) {
emit closeView(); emit closeView();
return; return;
} }
@ -167,18 +160,12 @@ void ZoneViewZoneLogic::removeCard(int position, bool toNewZone)
reorganizeCards(); reorganizeCards();
} }
void ZoneViewZoneLogic::requestClose()
{
emit closeView();
}
void ZoneViewZoneLogic::clearCards() void ZoneViewZoneLogic::clearCards()
{ {
for (auto card : cards) { for (auto card : cards) {
card->deleteView(); card->deleteLater();
} }
cards.clear(); cards.clear();
emit cardCountChanged();
} }
void ZoneViewZoneLogic::setWriteableRevealZone(bool _writeableRevealZone) void ZoneViewZoneLogic::setWriteableRevealZone(bool _writeableRevealZone)
@ -191,15 +178,3 @@ void ZoneViewZoneLogic::setWriteableRevealZone(bool _writeableRevealZone)
} }
writeableRevealZone = _writeableRevealZone; writeableRevealZone = _writeableRevealZone;
} }
PendingCommand *ZoneViewZoneLogic::prepareZoneDump()
{
return getPlayer()->getPlayerActions()->prepareZoneDump(this, numberCards);
}
void ZoneViewZoneLogic::sendZoneDump(PendingCommand *pendingCommand)
{
if (pendingCommand != nullptr) {
getPlayer()->getPlayerActions()->sendGameCommand(pendingCommand);
}
}

View file

@ -12,8 +12,6 @@
class PlayerLogic; class PlayerLogic;
class QObject; class QObject;
class PendingCommand;
class IInterfaceSettingsProvider;
class ZoneViewZoneLogic : public CardZoneLogic class ZoneViewZoneLogic : public CardZoneLogic
{ {
@ -28,8 +26,6 @@ private:
int numberCards; int numberCards;
bool revealZone, writeableRevealZone; bool revealZone, writeableRevealZone;
bool isReversed; bool isReversed;
// Borrowed settings provider (owned by the application). Null outside the client, in which case views stay open.
IInterfaceSettingsProvider *interfaceSettings;
public: public:
enum CardAction enum CardAction
@ -45,25 +41,15 @@ public:
bool _revealZone, bool _revealZone,
bool _writeableRevealZone, bool _writeableRevealZone,
bool _isReversed, bool _isReversed,
IInterfaceSettingsProvider *interfaceSettings = nullptr,
QObject *parent = nullptr); QObject *parent = nullptr);
bool prepareAddCard(int x); bool prepareAddCard(int x);
void removeCard(int position, bool toNewZone); void removeCard(int position, bool toNewZone);
void updateCardIds(CardAction action); void updateCardIds(CardAction action);
/**
* @brief Asks the graphics view showing this zone to close itself.
*
* Logic code has no handle on the view, so it goes through the logic object the view was built from.
*/
void requestClose();
/** @brief Removes all cards from the view, without touching anything else. Used in replay rewind */ /** @brief Removes all cards from the view, without touching anything else. Used in replay rewind */
void clearCards(); void clearCards();
[[nodiscard]] bool getCloseEmptyCardView() const;
int getNumberCards() const int getNumberCards() const
{ {
return numberCards; return numberCards;
@ -87,25 +73,8 @@ public:
return origZone; return origZone;
} }
/**
* @brief Builds the zone-dump command for this view without sending it.
* @return The pending command, or nullptr when there is no zone to dump.
*
* Keeps command construction in game/ so graphics views don't reach
* through the player into PlayerActions to set it up. The response can
* arrive synchronously (local games), so callers must connect to
* PendingCommand::finished before calling sendZoneDump().
*/
PendingCommand *prepareZoneDump();
/**
* @brief Sends a command returned by prepareZoneDump().
* @param pendingCommand The prepared command; nullptr is ignored.
*/
void sendZoneDump(PendingCommand *pendingCommand);
protected: protected:
void addCardImpl(CardState *card, int x, int y) override; void addCardImpl(CardItem *card, int x, int y) override;
}; };
#endif // COCKATRICE_VIEW_ZONE_LOGIC_H #endif // COCKATRICE_VIEW_ZONE_LOGIC_H

View file

@ -34,6 +34,7 @@
#include <QVariant> #include <QVariant>
#include <QtGlobal> #include <QtGlobal>
#include <algorithm> #include <algorithm>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/settings/appearance_settings.h> #include <libcockatrice/settings/appearance_settings.h>
#include <libcockatrice/settings/cards_display_settings.h> #include <libcockatrice/settings/cards_display_settings.h>
#include <libcockatrice/settings/debug_settings.h> #include <libcockatrice/settings/debug_settings.h>
@ -42,23 +43,13 @@
class QSizeF; class QSizeF;
AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef, PlayerLogic *_owner, int _id) AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef, PlayerLogic *_owner, int _id)
: ArrowTarget(_owner, parent), state(new CardState(this, nullptr, cardRef, _id)), tapAngle(0), : ArrowTarget(_owner, parent), id(_id), cardRef(cardRef), tapped(false), facedown(false), tapAngle(0),
bgColor(Qt::transparent), isHovered(false), realZValue(0) bgColor(Qt::transparent), isHovered(false), realZValue(0), pendingSelectionCollapse(false)
{ {
setCursor(Qt::OpenHandCursor); setCursor(Qt::OpenHandCursor);
setFlag(ItemIsSelectable); setFlag(ItemIsSelectable);
setCacheMode(DeviceCoordinateCache); 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, connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::displayCardNamesChanged, this,
[this] { update(); }); [this] { update(); });
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this, connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
@ -76,7 +67,7 @@ AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef
AbstractCardItem::~AbstractCardItem() AbstractCardItem::~AbstractCardItem()
{ {
emit deleteCardInfoPopup(state->getCardRef().name); emit deleteCardInfoPopup(cardRef.name);
} }
QRectF AbstractCardItem::boundingRect() const QRectF AbstractCardItem::boundingRect() const
@ -99,20 +90,21 @@ void AbstractCardItem::pixmapUpdated()
emit sigPixmapUpdated(); emit sigPixmapUpdated();
} }
void AbstractCardItem::onCardInfoChanged()
{
cacheBgColor();
update();
}
void AbstractCardItem::onCardRefChanged(const CardRef &oldCardRef, const CardRef & /*newCardRef*/)
{
emit deleteCardInfoPopup(oldCardRef.name);
}
void AbstractCardItem::refreshCardInfo() void AbstractCardItem::refreshCardInfo()
{ {
state->refreshCardInfo(); 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();
} }
/** /**
@ -121,7 +113,7 @@ void AbstractCardItem::refreshCardInfo()
*/ */
const CardInfo &AbstractCardItem::getCardInfo() const const CardInfo &AbstractCardItem::getCardInfo() const
{ {
return state->getCardInfo(); return exactCard.getInfo();
} }
void AbstractCardItem::setRealZValue(qreal _zValue) void AbstractCardItem::setRealZValue(qreal _zValue)
@ -166,13 +158,13 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
QPixmap translatedPixmap; QPixmap translatedPixmap;
bool paintImage = true; bool paintImage = true;
if (state->getFaceDown() || state->getCardRef().name.isEmpty()) { if (facedown || cardRef.name.isEmpty()) {
// never reveal card color, always paint the card back // never reveal card color, always paint the card back
CardPictureLoader::getCardBackPixmap(translatedPixmap, translatedSize.toSize()); CardPictureLoader::getCardBackPixmap(translatedPixmap, translatedSize.toSize());
} else { } else {
// don't even spend time trying to load the picture if our size is too small // don't even spend time trying to load the picture if our size is too small
if (translatedSize.width() > 10) { if (translatedSize.width() > 10) {
CardPictureLoader::getPixmap(translatedPixmap, state->getCard(), translatedSize.toSize()); CardPictureLoader::getPixmap(translatedPixmap, exactCard, translatedSize.toSize());
if (translatedPixmap.isNull()) { if (translatedPixmap.isNull()) {
paintImage = false; paintImage = false;
} }
@ -193,20 +185,19 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
painter->drawPath(shape()); painter->drawPath(shape());
} }
if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() || if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() || facedown) {
state->getFaceDown()) {
painter->save(); painter->save();
transformPainter(painter, translatedSize, angle); transformPainter(painter, translatedSize, angle);
painter->setPen(Qt::white); painter->setPen(Qt::white);
painter->setBackground(Qt::black); painter->setBackground(Qt::black);
painter->setBackgroundMode(Qt::OpaqueMode); painter->setBackgroundMode(Qt::OpaqueMode);
QString nameStr; QString nameStr;
if (state->getFaceDown()) { if (facedown) {
nameStr = "# " + QString::number(state->getId()); nameStr = "# " + QString::number(id);
} else { } else {
QString prefix = ""; QString prefix = "";
if (SettingsCache::instance().debug().getShowCardId()) { if (SettingsCache::instance().debug().getShowCardId()) {
prefix = "#" + QString::number(state->getId()) + " "; prefix = "#" + QString::number(id) + " ";
} }
nameStr = prefix + CardLocalization::displayName(getCardInfo()); nameStr = prefix + CardLocalization::displayName(getCardInfo());
} }
@ -244,6 +235,21 @@ void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
painter->restore(); 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) void AbstractCardItem::setHovered(bool _hovered)
{ {
if (isHovered == _hovered) { if (isHovered == _hovered) {
@ -269,17 +275,18 @@ void AbstractCardItem::setHovered(bool _hovered)
void AbstractCardItem::setColor(const QString &_color) void AbstractCardItem::setColor(const QString &_color)
{ {
state->setColor(_color); color = _color;
cacheBgColor();
update();
} }
void AbstractCardItem::cacheBgColor() void AbstractCardItem::cacheBgColor()
{ {
QChar colorChar; QChar colorChar;
const QString cardColor = state->getColor(); if (color.isEmpty()) {
if (cardColor.isEmpty()) { colorChar = exactCard.getInfo().getColorChar();
colorChar = state->getCardInfo().getColorChar();
} else { } else {
colorChar = cardColor.at(0); colorChar = color.at(0);
} }
switch (colorChar.toLower().toLatin1()) { switch (colorChar.toLower().toLatin1()) {
@ -307,12 +314,17 @@ void AbstractCardItem::cacheBgColor()
} }
} }
void AbstractCardItem::onTappedChanged(bool newTapped, bool canAnimate) void AbstractCardItem::setTapped(bool _tapped, bool canAnimate)
{ {
if (tapped == _tapped) {
return;
}
tapped = _tapped;
if (SettingsCache::instance().cardsDisplay().getTapAnimation() && canAnimate) { if (SettingsCache::instance().cardsDisplay().getTapAnimation() && canAnimate) {
static_cast<GameScene *>(scene())->registerAnimationItem(this); static_cast<GameScene *>(scene())->registerAnimationItem(this);
} else { } else {
tapAngle = newTapped ? 90 : 0; tapAngle = tapped ? 90 : 0;
setTransform(QTransform() setTransform(QTransform()
.translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F) .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F)
.rotate(tapAngle) .rotate(tapAngle)
@ -326,6 +338,12 @@ bool AbstractCardItem::animationEvent()
return false; return false;
} }
void AbstractCardItem::setFaceDown(bool _facedown)
{
facedown = _facedown;
update();
}
void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event) void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{ {
cancelPendingSelectionCollapse(); cancelPendingSelectionCollapse();
@ -339,7 +357,7 @@ void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
} }
} }
if ((event->modifiers() & Qt::AltModifier) && event->button() == Qt::LeftButton) { if ((event->modifiers() & Qt::AltModifier) && event->button() == Qt::LeftButton) {
emit cardShiftClicked(state->getCardRef().name); emit cardShiftClicked(cardRef.name);
} else if ((event->modifiers() & Qt::ControlModifier)) { } else if ((event->modifiers() & Qt::ControlModifier)) {
setSelected(!isSelected()); setSelected(!isSelected());
} else if (!isSelected()) { } else if (!isSelected()) {
@ -353,7 +371,7 @@ void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
setCursor(Qt::ClosedHandCursor); setCursor(Qt::ClosedHandCursor);
} else if (event->button() == Qt::MiddleButton) { } else if (event->button() == Qt::MiddleButton) {
emit showCardInfoPopup(event->screenPos(), state->getCardRef()); emit showCardInfoPopup(event->screenPos(), cardRef);
} }
event->accept(); event->accept();
} }
@ -387,7 +405,7 @@ void AbstractCardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
} }
if (event->button() == Qt::MiddleButton) { if (event->button() == Qt::MiddleButton) {
emit deleteCardInfoPopup(state->getCardRef().name); emit deleteCardInfoPopup(cardRef.name);
} }
// This function ensures the parent function doesn't mess around with our selection. // This function ensures the parent function doesn't mess around with our selection.

View file

@ -16,7 +16,8 @@
#include <QSizeF> #include <QSizeF>
#include <QString> #include <QString>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/game/board/card_state.h> #include <libcockatrice/card/printing/exact_card.h>
#include <libcockatrice/utility/card_ref.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class PlayerLogic; class PlayerLogic;
@ -28,8 +29,13 @@ class AbstractCardItem : public ArrowTarget, public IAnimatedItem
{ {
Q_OBJECT Q_OBJECT
protected: protected:
CardState *state; ExactCard exactCard;
int id;
CardRef cardRef;
bool tapped;
bool facedown;
int tapAngle; int tapAngle;
QString color;
QColor bgColor; QColor bgColor;
private: private:
@ -39,9 +45,6 @@ private:
int selectionCollapseGeneration = 0; ///< Guards the deferred collapse timer against stale firings int selectionCollapseGeneration = 0; ///< Guards the deferred collapse timer against stale firings
private slots: private slots:
void pixmapUpdated(); void pixmapUpdated();
void onCardInfoChanged();
void onCardRefChanged(const CardRef &oldCardRef, const CardRef &newCardRef);
void onTappedChanged(bool newTapped, bool canAnimate);
public slots: public slots:
void refreshCardInfo(); void refreshCardInfo();
@ -77,38 +80,31 @@ public:
QSizeF getTranslatedSize(QPainter *painter) const; QSizeF getTranslatedSize(QPainter *painter) const;
void paintPicture(QPainter *painter, const QSizeF &translatedSize, int angle); void paintPicture(QPainter *painter, const QSizeF &translatedSize, int angle);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
[[nodiscard]] CardState *getState() const
{
return state;
}
ExactCard getCard() const ExactCard getCard() const
{ {
return state->getCard(); return exactCard;
} }
const CardInfo &getCardInfo() const; const CardInfo &getCardInfo() const;
int getId() const int getId() const
{ {
return state->getId(); return id;
} }
void setId(int _id) void setId(int _id)
{ {
state->setId(_id); id = _id;
} }
QString getName() const QString getName() const
{ {
return state->getCardRef().name; return cardRef.name;
} }
QString getProviderId() const QString getProviderId() const
{ {
return state->getCardRef().providerId; return cardRef.providerId;
}
void setCardRef(const CardRef &_cardRef)
{
state->setCardRef(_cardRef);
} }
void setCardRef(const CardRef &_cardRef);
CardRef getCardRef() const CardRef getCardRef() const
{ {
return state->getCardRef(); return cardRef;
} }
qreal getRealZValue() const qreal getRealZValue() const
{ {
@ -120,33 +116,27 @@ public:
{ {
return isHovered; return isHovered;
} }
[[nodiscard]] QString getColor() const QString getColor() const
{ {
return state->getColor(); return color;
} }
void setColor(const QString &_color); void setColor(const QString &_color);
bool getTapped() const bool getTapped() const
{ {
return state->getTapped(); return tapped;
}
void setTapped(bool _tapped, bool canAnimate = false)
{
state->setTapped(_tapped, canAnimate);
} }
void setTapped(bool _tapped, bool canAnimate = false);
bool getFaceDown() const bool getFaceDown() const
{ {
return state->getFaceDown(); return facedown;
}
void setFaceDown(bool _facedown)
{
state->setFaceDown(_facedown);
} }
void setFaceDown(bool _facedown);
void processHoverEvent(); void processHoverEvent();
/** @brief Drop a pending collapse and disarm any deferred collapse timer. */ /** @brief Drop a pending collapse and disarm any deferred collapse timer. */
void cancelPendingSelectionCollapse(); void cancelPendingSelectionCollapse();
void deleteCardInfoPopup() void deleteCardInfoPopup()
{ {
emit deleteCardInfoPopup(state->getCardRef().name); emit deleteCardInfoPopup(cardRef.name);
} }
/** @brief Default: no per-tick animation. Subclasses override to animate. */ /** @brief Default: no per-tick animation. Subclasses override to animate. */

View file

@ -2,6 +2,10 @@
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../client/settings/shortcuts_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 "../../game_graphics/board/translate_counter_name.h"
#include "../../interface/widgets/menus/tearoff_menu.h" #include "../../interface/widgets/menus/tearoff_menu.h"
@ -19,10 +23,8 @@
#include <QString> #include <QString>
#include <QVariant> #include <QVariant>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/game/board/counter_state.h> #include <libcockatrice/protocol/pb/command_inc_counter.pb.h>
#include <libcockatrice/game/player/player_actions.h> #include <libcockatrice/protocol/pb/command_set_counter.pb.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/utility/expression.h> #include <libcockatrice/utility/expression.h>
#include <qnamespace.h> #include <qnamespace.h>
@ -149,7 +151,10 @@ void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
menu->exec(event->screenPos()); menu->exec(event->screenPos());
} }
} else { } else {
player->getPlayerActions()->actIncrementCounter(id, event->button() == Qt::LeftButton ? 1 : -1); Command_IncCounter cmd;
cmd.set_counter_id(id);
cmd.set_delta(event->button() == Qt::LeftButton ? 1 : -1);
player->getPlayerActions()->sendGameCommand(cmd);
} }
event->accept(); event->accept();
} }
@ -167,7 +172,10 @@ void AbstractCounter::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
void AbstractCounter::incrementCounter() void AbstractCounter::incrementCounter()
{ {
player->getPlayerActions()->actIncrementCounter(id, static_cast<QAction *>(sender())->data().toInt()); Command_IncCounter cmd;
cmd.set_counter_id(id);
cmd.set_delta(static_cast<QAction *>(sender())->data().toInt());
player->getPlayerActions()->sendGameCommand(cmd);
} }
void AbstractCounter::setCounter() void AbstractCounter::setCounter()
@ -191,7 +199,10 @@ void AbstractCounter::setCounter()
} }
Expression exp(value); Expression exp(value);
player->getPlayerActions()->actSetCounter(id, static_cast<int>(exp.parse(dlg.textValue()))); Command_SetCounter cmd;
cmd.set_counter_id(id);
cmd.set_value(static_cast<int>(exp.parse(dlg.textValue())));
player->getPlayerActions()->sendGameCommand(cmd);
} }
AbstractCounterDialog::AbstractCounterDialog(const QString &name, const QString &value, QWidget *parent) AbstractCounterDialog::AbstractCounterDialog(const QString &name, const QString &value, QWidget *parent)

View file

@ -2,6 +2,11 @@
#include "arrow_item.h" #include "arrow_item.h"
#include "../../client/settings/cache_settings.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 "../game_scene.h"
#include "../player/player_target.h" #include "../player/player_target.h"
#include "../z_values.h" #include "../z_values.h"
@ -22,12 +27,12 @@
#include <QTransform> #include <QTransform>
#include <QtGlobal> #include <QtGlobal>
#include <QtMath> #include <QtMath>
#include <libcockatrice/game/board/arrow_data.h> #include <libcockatrice/card/card_info.h>
#include <libcockatrice/game/player/player_actions.h> #include <libcockatrice/protocol/pb/command_attach_card.pb.h>
#include <libcockatrice/game/player/player_info.h> #include <libcockatrice/protocol/pb/command_create_arrow.pb.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/cards_display_settings.h>
#include <libcockatrice/settings/interface_settings.h>
#include <libcockatrice/utility/color.h>
#include <libcockatrice/utility/zone_names.h> #include <libcockatrice/utility/zone_names.h>
#include <math.h> #include <math.h>
#include <qnamespace.h> #include <qnamespace.h>
@ -421,19 +426,19 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
CardZoneLogic *startZone = startCard->getZone(); CardZoneLogic *startZone = startCard->getZone();
ArrowData arrow; Command_CreateArrow cmd;
arrow.color = data->color; cmd.mutable_arrow_color()->CopyFrom(convertQColorToColor(data->color));
arrow.startPlayerId = startZone->getPlayer()->getPlayerInfo()->getId(); cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
arrow.startZone = startZone->getName(); cmd.set_start_zone(startZone->getName().toStdString());
arrow.startCardId = startCard->getId(); cmd.set_start_card_id(startCard->getId());
if (auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem)) { if (auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem)) {
CardZoneLogic *targetZone = targetCard->getZone(); CardZoneLogic *targetZone = targetCard->getZone();
arrow.targetPlayerId = targetZone->getPlayer()->getPlayerInfo()->getId(); cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId());
arrow.targetZone = targetZone->getName(); cmd.set_target_zone(targetZone->getName().toStdString());
arrow.targetCardId = targetCard->getId(); cmd.set_target_card_id(targetCard->getId());
} else if (auto *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem)) { } else if (auto *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem)) {
arrow.targetPlayerId = targetPlayer->getOwner()->getPlayerInfo()->getId(); cmd.set_target_player_id(targetPlayer->getOwner()->getPlayerInfo()->getId());
} else { } else {
delArrow(); delArrow();
return; return;
@ -442,9 +447,22 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
// if the card is in hand then we will move the card to stack or table as part of drawing the arrow // if the card is in hand then we will move the card to stack or table as part of drawing the arrow
if (startZone->getName() == ZoneNames::HAND) { if (startZone->getName() == ZoneNames::HAND) {
startCard->playCard(false); 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);
}
} }
player->getPlayerActions()->createArrow(startCard->getState(), arrow, deleteInPhase); if (deleteInPhase != 0) {
cmd.set_delete_in_phase(deleteInPhase);
}
player->getPlayerActions()->sendGameCommand(cmd);
} }
delArrow(); delArrow();
@ -536,7 +554,7 @@ void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
auto *startCard = qgraphicsitem_cast<CardItem *>(startItem); auto *startCard = qgraphicsitem_cast<CardItem *>(startItem);
auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem); auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem);
if (startCard && targetCard) { if (startCard && targetCard) {
player->getPlayerActions()->attachCards(startCard->getState(), targetCard->getState()); attachCards(startCard, targetCard);
} }
} }
@ -545,3 +563,23 @@ void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
child->mouseReleaseEvent(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);
}

View file

@ -1,6 +1,7 @@
#ifndef ARROWITEM_H #ifndef ARROWITEM_H
#define ARROWITEM_H #define ARROWITEM_H
#include "../../game/board/arrow_data.h"
#include "../animated_item.h" #include "../animated_item.h"
#include "arrow_target.h" #include "arrow_target.h"
#include "graphics_item_type.h" #include "graphics_item_type.h"
@ -17,7 +18,6 @@
#include <QRectF> #include <QRectF>
#include <QSharedPointer> #include <QSharedPointer>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/game/board/arrow_data.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class CardItem; class CardItem;
@ -131,6 +131,7 @@ private:
PlayerLogic *player; PlayerLogic *player;
QList<ArrowAttachItem *> childArrows; QList<ArrowAttachItem *> childArrows;
QMetaObject::Connection positionConnection; QMetaObject::Connection positionConnection;
void attachCards(CardItem *startCard, const CardItem *targetCard);
public: public:
explicit ArrowAttachItem(ArrowTarget *_startItem); explicit ArrowAttachItem(ArrowTarget *_startItem);

View file

@ -1,6 +1,17 @@
#include "card_item.h" #include "card_item.h"
#include "../../client/settings/cache_settings.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 "../card_dimensions.h"
#include "../game_scene.h" #include "../game_scene.h"
#include "arrow_item.h" #include "arrow_item.h"
@ -30,17 +41,6 @@
#include <QVariant> #include <QVariant>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/card/card_info.h> #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/protocol/pb/serverinfo_card.pb.h>
#include <libcockatrice/settings/interface_settings.h> #include <libcockatrice/settings/interface_settings.h>
#include <libcockatrice/utility/card_ref.h> #include <libcockatrice/utility/card_ref.h>
@ -53,9 +53,8 @@ CardItem::CardItem(PlayerLogic *_owner,
const CardRef &cardRef, const CardRef &cardRef,
int _cardid, int _cardid,
CardZoneLogic *_zone) CardZoneLogic *_zone)
: AbstractCardItem(parent, cardRef, _owner, _cardid), dragItem(nullptr) : AbstractCardItem(parent, cardRef, _owner, _cardid), state(new CardState(this, _zone)), dragItem(nullptr)
{ {
state->setZone(_zone);
owner->addCard(this); owner->addCard(this);
connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, this, [this](int counterId) { connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, this, [this](int counterId) {
@ -63,44 +62,25 @@ CardItem::CardItem(PlayerLogic *_owner,
update(); 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() void CardItem::prepareDelete()
{ {
if (owner != nullptr) { if (owner != nullptr) {
auto *gameScene = qobject_cast<GameScene *>(scene()); if (owner->getGame()->getActiveCard() == this) {
if (gameScene && gameScene->getActiveCard() == this) {
emit owner->requestCardMenuUpdate(nullptr); emit owner->requestCardMenuUpdate(nullptr);
gameScene->setActiveCard(nullptr); owner->getGame()->setActiveCard(nullptr);
} }
owner = nullptr; owner = nullptr;
} }
const QList<CardState *> &attachedStates = state->getAttachedCards(); while (!attachedCards.isEmpty()) {
while (!attachedStates.isEmpty()) { attachedCards.first()->setZone(nullptr); // so that it won't try to call reorganizeCards()
auto *attachedItem = qobject_cast<CardItem *>(attachedStates.first()->parent()); attachedCards.first()->setAttachedTo(nullptr);
if (attachedItem == nullptr) {
break;
}
attachedItem->setZone(nullptr); // so that it won't try to call reorganizeCards()
attachedItem->setAttachedTo(nullptr);
} }
if (state->getAttachedTo() != nullptr) { if (state->getAttachedTo() != nullptr) {
state->getAttachedTo()->removeAttachedCard(state); state->getAttachedTo()->removeAttachedCard(this);
state->setAttachedTo(nullptr); state->setAttachedTo(nullptr);
} }
} }
@ -147,7 +127,7 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
painter->save(); painter->save();
transformPainter(painter, translatedSize, tapAngle); transformPainter(painter, translatedSize, tapAngle);
if (!getFaceDown() && state->getPT() == state->getCardInfo().getPowTough()) { if (!getFaceDown() && state->getPT() == exactCard.getInfo().getPowTough()) {
painter->setPen(Qt::white); painter->setPen(Qt::white);
} else { } else {
painter->setPen(QColor(255, 150, 0)); // dark orange painter->setPen(QColor(255, 150, 0)); // dark orange
@ -229,13 +209,36 @@ void CardItem::setPT(const QString &_pt)
void CardItem::setAttachedTo(CardItem *_attachedTo) void CardItem::setAttachedTo(CardItem *_attachedTo)
{ {
CardState *newParent = _attachedTo == nullptr ? nullptr : _attachedTo->getState(); if (state->getAttachedTo() != nullptr) {
state->setAttachedTo(newParent); state->getAttachedTo()->removeAttachedCard(this);
}
// If the zone is being torn down, it might already be null by the time a card tries to un-attach all its attached gridPoint.setX(-1);
// cards state->setAttachedTo(_attachedTo);
if (newParent != nullptr ? newParent->getZone() == nullptr : state->getZone() == nullptr) { if (state->getAttachedTo() != nullptr) {
deleteLater(); // 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();
} }
} }
@ -245,6 +248,13 @@ void CardItem::setAttachedTo(CardItem *_attachedTo)
void CardItem::resetState(bool keepAnnotations) void CardItem::resetState(bool keepAnnotations)
{ {
state->resetState(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) void CardItem::processCardInfo(const ServerInfo_Card &_info)
@ -380,7 +390,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
// Use the buttonDownPos to align the hot spot with the position when // Use the buttonDownPos to align the hot spot with the position when
// the user originally clicked // the user originally clicked
createDragItem(state->getId(), event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown); createDragItem(id, event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown);
dragItem->grabMouse(); dragItem->grabMouse();
int childIndex = 0; int childIndex = 0;
@ -423,11 +433,42 @@ void CardItem::playCard(bool faceDown)
emit playSelected(this); emit playSelected(this);
} }
} else { } else {
state->getZone()->getPlayer()->getPlayerActions()->playCard(state, faceDown); state->getZone()->getPlayer()->getPlayerActions()->playCard(this, 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 * @brief returns true if the zone is a unwritable reveal zone view (eg a card reveal window). Will return false if zone
* is nullptr. * is nullptr.
@ -452,7 +493,7 @@ void CardItem::handleClickedToPlay(bool shiftHeld)
if (SettingsCache::instance().userInterface().getClickPlaysAllSelected()) { if (SettingsCache::instance().userInterface().getClickPlaysAllSelected()) {
emit hideSelected(this); emit hideSelected(this);
} else { } else {
state->getZone()->removeCard(state); state->getZone()->removeCard(this);
} }
} else { } else {
playCard(shiftHeld); playCard(shiftHeld);
@ -496,16 +537,16 @@ bool CardItem::animationEvent()
{ {
int rotation = ROTATION_DEGREES_PER_FRAME; int rotation = ROTATION_DEGREES_PER_FRAME;
bool animationIncomplete = true; bool animationIncomplete = true;
if (!state->getTapped()) { if (!tapped) {
rotation *= -1; rotation *= -1;
} }
tapAngle += rotation; tapAngle += rotation;
if (state->getTapped() && (tapAngle > 90)) { if (tapped && (tapAngle > 90)) {
tapAngle = 90; tapAngle = 90;
animationIncomplete = false; animationIncomplete = false;
} }
if (!state->getTapped() && (tapAngle < 0)) { if (!tapped && (tapAngle < 0)) {
tapAngle = 0; tapAngle = 0;
animationIncomplete = false; animationIncomplete = false;
} }
@ -526,9 +567,7 @@ QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value)
bool selected = value.toBool(); bool selected = value.toBool();
if (selected) { if (selected) {
if (auto *gameScene = qobject_cast<GameScene *>(scene())) { owner->getGame()->setActiveCard(this);
gameScene->setActiveCard(this);
}
} }
emit selectionChanged(this, selected); emit selectionChanged(this, selected);

View file

@ -7,6 +7,7 @@
#ifndef CARDITEM_H #ifndef CARDITEM_H
#define CARDITEM_H #define CARDITEM_H
#include "../../game/board/card_state.h"
#include "abstract_card_item.h" #include "abstract_card_item.h"
#include "graphics_item_type.h" #include "graphics_item_type.h"
@ -15,7 +16,6 @@
#include <QPointF> #include <QPointF>
#include <QString> #include <QString>
#include <QVariantList> #include <QVariantList>
#include <libcockatrice/game/board/card_state.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class CardDragItem; class CardDragItem;
@ -33,7 +33,11 @@ class CardItem : public AbstractCardItem
{ {
Q_OBJECT Q_OBJECT
private: private:
CardState *state;
QPoint gridPoint;
CardDragItem *dragItem; CardDragItem *dragItem;
QList<CardItem *> attachedCards;
void prepareDelete(); void prepareDelete();
void handleClickedToPlay(bool shiftHeld); void handleClickedToPlay(bool shiftHeld);
@ -56,6 +60,10 @@ public:
CardZoneLogic *_zone = nullptr); CardZoneLogic *_zone = nullptr);
void retranslateUi(); void retranslateUi();
[[nodiscard]] CardState *getState() const
{
return state;
}
[[nodiscard]] CardZoneLogic *getZone() const [[nodiscard]] CardZoneLogic *getZone() const
{ {
return state->getZone(); return state->getZone();
@ -64,15 +72,15 @@ public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
[[nodiscard]] QPoint getGridPoint() const [[nodiscard]] QPoint getGridPoint() const
{ {
return state->getGridPoint(); return gridPoint;
} }
void setGridPoint(const QPoint &_gridPoint) void setGridPoint(const QPoint &_gridPoint)
{ {
state->setGridPoint(_gridPoint); gridPoint = _gridPoint;
} }
[[nodiscard]] QPoint getGridPos() const [[nodiscard]] QPoint getGridPos() const
{ {
return state->getGridPoint(); return gridPoint;
} }
[[nodiscard]] PlayerLogic *getOwner() const [[nodiscard]] PlayerLogic *getOwner() const
{ {
@ -115,22 +123,22 @@ public:
{ {
state->setDestroyOnZoneChange(_destroy); state->setDestroyOnZoneChange(_destroy);
} }
[[nodiscard]] CardState *getAttachedTo() const [[nodiscard]] CardItem *getAttachedTo() const
{ {
return state->getAttachedTo(); return state->getAttachedTo();
} }
void setAttachedTo(CardItem *_attachedTo); void setAttachedTo(CardItem *_attachedTo);
void addAttachedCard(CardState *card) void addAttachedCard(CardItem *card)
{ {
state->addAttachedCard(card); attachedCards.append(card);
} }
void removeAttachedCard(CardState *card) void removeAttachedCard(CardItem *card)
{ {
state->removeAttachedCard(card); attachedCards.removeOne(card);
} }
[[nodiscard]] const QList<CardState *> &getAttachedCards() const [[nodiscard]] const QList<CardItem *> &getAttachedCards() const
{ {
return state->getAttachedCards(); return attachedCards;
} }
void resetState(bool keepAnnotations = false); void resetState(bool keepAnnotations = false);
void processCardInfo(const ServerInfo_Card &_info); void processCardInfo(const ServerInfo_Card &_info);
@ -142,6 +150,26 @@ public:
void drawAttachArrow(); void drawAttachArrow();
void playCard(bool faceDown); 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: protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;

View file

@ -2,8 +2,13 @@
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../client/settings/shortcuts_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/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/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.h"
#include "../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h" #include "../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h"
#include "../../interface/widgets/dialogs/dlg_load_deck_from_website.h" #include "../../interface/widgets/dialogs/dlg_load_deck_from_website.h"
@ -28,17 +33,16 @@
#include <QRect> #include <QRect>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <libcockatrice/card/database/card_database_manager.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 <libcockatrice/deck_list/playmat_resolver.h> #include <libcockatrice/deck_list/playmat_resolver.h>
#include <libcockatrice/game/abstract_game.h> #include <libcockatrice/protocol/pb/command_deck_select.pb.h>
#include <libcockatrice/game/game_event_handler.h> #include <libcockatrice/protocol/pb/command_ready_start.pb.h>
#include <libcockatrice/game/game_state.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/protocol/pb/response_deck_download.pb.h> #include <libcockatrice/protocol/pb/response_deck_download.pb.h>
#include <libcockatrice/protocol/pending_command.h> #include <libcockatrice/protocol/pending_command.h>
#include <libcockatrice/settings/interface_settings.h> #include <libcockatrice/settings/interface_settings.h>
#include <libcockatrice/settings/visual_deck_storage_settings.h> #include <libcockatrice/settings/visual_deck_storage_settings.h>
#include <libcockatrice/utility/qt_utils.h>
#include <libcockatrice/utility/string_limits.h> #include <libcockatrice/utility/string_limits.h>
#include <optional> #include <optional>
#include <qnamespace.h> #include <qnamespace.h>
@ -107,9 +111,10 @@ DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent)
connect(deckView, &DeckView::newCardAdded, this, &DeckViewContainer::newCardAdded); connect(deckView, &DeckView::newCardAdded, this, &DeckViewContainer::newCardAdded);
connect(deckView, &DeckView::sideboardPlanChanged, this, &DeckViewContainer::sideboardPlanChanged); connect(deckView, &DeckView::sideboardPlanChanged, this, &DeckViewContainer::sideboardPlanChanged);
deckViewLayout = QtUtils::makeLayout<QVBoxLayout>(); deckViewLayout = new QVBoxLayout;
deckViewLayout->addLayout(buttonHBox); deckViewLayout->addLayout(buttonHBox);
deckViewLayout->addWidget(deckView); deckViewLayout->addWidget(deckView);
deckViewLayout->setContentsMargins(0, 0, 0, 0);
setLayout(deckViewLayout); setLayout(deckViewLayout);
retranslateUi(); retranslateUi();
@ -309,7 +314,9 @@ void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck)
return; return;
} }
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareDeckSelect(deckString); Command_DeckSelect cmd;
cmd.set_deck(deckString.toStdString());
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(cmd);
connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished); connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished);
parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId); parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId);
@ -338,7 +345,15 @@ void DeckViewContainer::resolveAndSendPlaymat()
lastResolvedPlaymat = resolved; lastResolvedPlaymat = resolved;
PendingCommand *playmatPend = parentGame->getGame()->getGameEventHandler()->prepareSetPlaymat(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);
const QString sentName = resolved.card.name; const QString sentName = resolved.card.name;
connect(playmatPend, &PendingCommand::finished, this, [this, sentName](const Response &r) { connect(playmatPend, &PendingCommand::finished, this, [this, sentName](const Response &r) {
// Only RespFunctionNotAllowed signals a card art DENY rule. Older servers that predate // Only RespFunctionNotAllowed signals a card art DENY rule. Older servers that predate
@ -369,7 +384,9 @@ void DeckViewContainer::loadRemoteDeck()
{ {
DlgLoadRemoteDeck dlg(parentGame->getGame()->getClientForPlayer(playerId), this); DlgLoadRemoteDeck dlg(parentGame->getGame()->getClientForPlayer(playerId), this);
if (dlg.exec()) { if (dlg.exec()) {
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareDeckSelectId(dlg.getDeckId()); Command_DeckSelect cmd;
cmd.set_deck_id(dlg.getDeckId());
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(cmd);
connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished); connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished);
parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId); parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId);
} }
@ -425,21 +442,34 @@ void DeckViewContainer::readyStart()
void DeckViewContainer::forceStart() 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 msg = tr("Are you sure you want to force start?\nThis will kick all non-ready players from the game.");
if (!QtUtils::confirm(this, tr("Cockatrice"), msg, QMessageBox::Yes)) { const auto res =
QMessageBox::question(this, tr("Cockatrice"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (res == QMessageBox::No) {
return; return;
} }
parentGame->getGame()->getGameEventHandler()->sendForceStart(playerId); Command_ReadyStart cmd;
cmd.set_force_start(true);
cmd.set_ready(true);
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
} }
void DeckViewContainer::sideboardLockButtonClicked() void DeckViewContainer::sideboardLockButtonClicked()
{ {
parentGame->getGame()->getGameEventHandler()->sendSetSideboardLock(sideboardLockButton->getState(), playerId); Command_SetSideboardLock cmd;
cmd.set_locked(sideboardLockButton->getState());
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
} }
void DeckViewContainer::sideboardPlanChanged() void DeckViewContainer::sideboardPlanChanged()
{ {
parentGame->getGame()->getGameEventHandler()->sendSetSideboardPlan(deckView->getSideboardPlan(), playerId); 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);
} }
/** /**
@ -451,7 +481,9 @@ void DeckViewContainer::sendReadyStartCommand(bool ready)
resolveAndSendPlaymat(); resolveAndSendPlaymat();
} }
parentGame->getGame()->getGameEventHandler()->sendReadyStart(ready, playerId); Command_ReadyStart cmd;
cmd.set_ready(ready);
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
} }
/** /**

View file

@ -1,13 +0,0 @@
#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);
}

View file

@ -1,22 +0,0 @@
/**
* @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

View file

@ -11,7 +11,6 @@
#include <QList> #include <QList>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <libcockatrice/game/player/token_info.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class QLabel; class QLabel;
@ -26,6 +25,17 @@ class CardInfoPictureWidget;
class QModelIndex; class QModelIndex;
class QWidget; class QWidget;
struct TokenInfo
{
QString name;
QString color;
QString pt;
QString annotation;
bool destroy = true;
bool faceDown = false;
QString providerId;
};
class DlgCreateToken : public QDialog class DlgCreateToken : public QDialog
{ {
Q_OBJECT Q_OBJECT

View file

@ -11,7 +11,6 @@
#include <QList> #include <QList>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <libcockatrice/game/player/move_top_cards_until_options.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class FilterString; class FilterString;
@ -22,6 +21,13 @@ class QLabel;
class QSpinBox; class QSpinBox;
class QWidget; class QWidget;
struct MoveTopCardsUntilOptions
{
QStringList exprs = {};
int numberOfHits = 1;
bool autoPlay = false;
};
class DlgMoveTopCardsUntil : public QDialog class DlgMoveTopCardsUntil : public QDialog
{ {
Q_OBJECT Q_OBJECT

View file

@ -1,10 +1,18 @@
#include "game_scene.h" #include "game_scene.h"
#include "../client/settings/cache_settings.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 "animated_item.h"
#include "board/abstract_card_item.h" #include "board/abstract_card_item.h"
#include "board/arrow_item.h" #include "board/arrow_item.h"
#include "board/arrow_registry.h"
#include "board/card_item.h" #include "board/card_item.h"
#include "phases_toolbar.h" #include "phases_toolbar.h"
#include "player/menu/player_menu.h" #include "player/menu/player_menu.h"
@ -32,15 +40,6 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QtMath> #include <QtMath>
#include <algorithm> #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/settings/interface_settings.h>
#include <libcockatrice/utility/zone_names.h> #include <libcockatrice/utility/zone_names.h>
#include <numeric> #include <numeric>
@ -113,15 +112,6 @@ 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 *> GameScene::selectedCards() const
{ {
QList<CardItem *> selectedCards; QList<CardItem *> selectedCards;
@ -134,67 +124,6 @@ QList<CardItem *> GameScene::selectedCards() const
return selectedCards; 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) void GameScene::onCardSelectionChanged(AbstractCardItem *abstractCard, bool selected)
{ {
CardItem *card = qobject_cast<CardItem *>(abstractCard); CardItem *card = qobject_cast<CardItem *>(abstractCard);
@ -205,12 +134,12 @@ void GameScene::onCardSelectionChanged(AbstractCardItem *abstractCard, bool sele
auto *owner = card->getOwner(); auto *owner = card->getOwner();
if (selected) { if (selected) {
owner->requestCardMenuUpdate(card->getState()); owner->requestCardMenuUpdate(card);
return; return;
} }
if (selectedItems().isEmpty()) { if (selectedItems().isEmpty()) {
setActiveCard(nullptr); owner->getGame()->setActiveCard(nullptr);
owner->requestCardMenuUpdate(nullptr); owner->requestCardMenuUpdate(nullptr);
} }
} }
@ -229,9 +158,9 @@ void GameScene::onCardRightClicked(AbstractCardItem *abstractCard, QPoint screen
return; return;
} }
setActiveCard(card); card->getOwner()->getGame()->setActiveCard(card);
if (auto *menu = view->getPlayerMenu()->updateCardMenu(card->getState())) { if (auto *menu = view->getPlayerMenu()->updateCardMenu(card)) {
menu->popup(screenPos); menu->popup(screenPos);
} }
} }
@ -244,7 +173,7 @@ void GameScene::playSelected(AbstractCardItem *card)
if (!card->getOwner()) { if (!card->getOwner()) {
return; return;
} }
card->getOwner()->getPlayerActions()->actPlay(selectedCardStates()); card->getOwner()->getPlayerActions()->actPlay(selectedCards());
} }
void GameScene::playSelectedFaceDown(AbstractCardItem *card) void GameScene::playSelectedFaceDown(AbstractCardItem *card)
@ -255,7 +184,7 @@ void GameScene::playSelectedFaceDown(AbstractCardItem *card)
if (!card->getOwner()) { if (!card->getOwner()) {
return; return;
} }
card->getOwner()->getPlayerActions()->actPlayFacedown(selectedCardStates()); card->getOwner()->getPlayerActions()->actPlayFacedown(selectedCards());
} }
void GameScene::hideSelected(AbstractCardItem *card) void GameScene::hideSelected(AbstractCardItem *card)
@ -266,7 +195,7 @@ void GameScene::hideSelected(AbstractCardItem *card)
if (!card->getOwner()) { if (!card->getOwner()) {
return; return;
} }
card->getOwner()->getPlayerActions()->actHide(selectedCardStates()); card->getOwner()->getPlayerActions()->actHide(selectedCards());
} }
/** /**
@ -301,8 +230,6 @@ void GameScene::addPlayer(PlayerLogic *player)
[this, id = player->getPlayerInfo()->getId()]() { clearArrowsForPlayerLocally(id); }); [this, id = player->getPlayerInfo()->getId()]() { clearArrowsForPlayerLocally(id); });
connect(player->getPlayerEventHandler(), &PlayerEventHandler::cardZoneChanged, this, &GameScene::onCardZoneChanged); connect(player->getPlayerEventHandler(), &PlayerEventHandler::cardZoneChanged, this, &GameScene::onCardZoneChanged);
connect(player->getPlayerEventHandler(), &PlayerEventHandler::cardViewRefreshRequested, this,
&GameScene::onCardViewRefreshRequested);
rearrange(); rearrange();
} }
@ -606,11 +533,7 @@ void GameScene::addArrow(QSharedPointer<ArrowData> data)
return; return;
} }
CardState *startCardState = startZone->getCard(data->startCardId); CardItem *startCard = startZone->getCard(data->startCardId);
if (!startCardState) {
return;
}
CardItem *startCard = qobject_cast<CardItem *>(startCardState->parent());
if (!startCard) { if (!startCard) {
return; return;
} }
@ -621,8 +544,7 @@ void GameScene::addArrow(QSharedPointer<ArrowData> data)
} else { } else {
auto *zone = targetView->getLogic()->getZones().value(data->targetZone); auto *zone = targetView->getLogic()->getZones().value(data->targetZone);
if (zone) { if (zone) {
CardState *targetCardState = zone->getCard(data->targetCardId); targetItem = zone->getCard(data->targetCardId);
targetItem = targetCardState == nullptr ? nullptr : qobject_cast<CardItem *>(targetCardState->parent());
} }
} }
if (!targetItem) { if (!targetItem) {
@ -650,15 +572,11 @@ void GameScene::requestArrowDeletion(int playerId, int arrowId)
} }
} }
void GameScene::onCardZoneChanged(CardState *card, bool sameZone) void GameScene::onCardZoneChanged(CardItem *card, bool sameZone)
{ {
auto *item = qobject_cast<CardItem *>(card->parent());
if (!item) {
return;
}
QList<ArrowItem *> toDelete; QList<ArrowItem *> toDelete;
for (auto *arrow : arrowRegistry.all()) { for (auto *arrow : arrowRegistry.all()) {
if (arrow->getStartItem() == item || arrow->getTargetItem() == item) { if (arrow->getStartItem() == card || arrow->getTargetItem() == card) {
if (sameZone) { if (sameZone) {
arrow->updatePath(); arrow->updatePath();
} else { } else {
@ -671,25 +589,6 @@ void GameScene::onCardZoneChanged(CardState *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) void GameScene::clearArrowsForPlayer(int playerId)
{ {
for (int arrowId : arrowRegistry.idsForPlayer(playerId)) { for (int arrowId : arrowRegistry.idsForPlayer(playerId)) {

View file

@ -1,9 +1,7 @@
#ifndef GAMESCENE_H #ifndef GAMESCENE_H
#define GAMESCENE_H #define GAMESCENE_H
#include "animated_item.h" #include "../game/arrow_registry.h"
#include "board/arrow_item.h"
#include "board/arrow_registry.h"
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QHash> #include <QHash>
@ -18,8 +16,6 @@
#include <QString> #include <QString>
#include <QTransform> #include <QTransform>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/game/board/arrow_data.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
// IWYU pragma: keep // IWYU pragma: keep
@ -43,7 +39,6 @@ class ZoneViewWidget;
class CardZone; class CardZone;
class AbstractCardItem; class AbstractCardItem;
class CardItem; class CardItem;
class CardState;
class ServerInfo_Card; class ServerInfo_Card;
class PhasesToolbar; class PhasesToolbar;
class QBasicTimer; class QBasicTimer;
@ -74,7 +69,6 @@ private:
QList<ZoneViewWidget *> zoneViews; ///< Active zone view widgets QList<ZoneViewWidget *> zoneViews; ///< Active zone view widgets
QSize viewSize; ///< Current view size QSize viewSize; ///< Current view size
QPointer<CardItem> hoveredCard; ///< Currently hovered card QPointer<CardItem> hoveredCard; ///< Currently hovered card
QPointer<CardItem> activeCard; ///< Card the currently open context menu targets
QBasicTimer *animationTimer; ///< Timer for scene animations QBasicTimer *animationTimer; ///< Timer for scene animations
QHash<QObject *, IAnimatedItem *> animatedItems; ///< Items currently animating QHash<QObject *, IAnimatedItem *> animatedItems; ///< Items currently animating
QHash<QObject *, QMetaObject::Connection> QHash<QObject *, QMetaObject::Connection>
@ -113,22 +107,6 @@ public:
/** @brief Gets all selected CardItems. */ /** @brief Gets all selected CardItems. */
QList<CardItem *> selectedCards() const; 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. * @brief Adds a player to the scene and stores their graphics item.
@ -283,15 +261,7 @@ public slots:
/// Queues up arrow deletion but doesn't directly modify the scene /// Queues up arrow deletion but doesn't directly modify the scene
void requestArrowDeletion(int playerId, int arrowId); void requestArrowDeletion(int playerId, int arrowId);
void onCardZoneChanged(CardState *card, bool sameZone); void onCardZoneChanged(CardItem *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: protected:
/** @brief Handles hover updates. */ /** @brief Handles hover updates. */

View file

@ -1,5 +1,7 @@
#include "hand_counter.h" #include "hand_counter.h"
#include "../game/board/card_list.h"
#include "../game/zones/card_zone_logic.h"
#include "../interface/pixel_map_generator.h" #include "../interface/pixel_map_generator.h"
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
@ -12,8 +14,6 @@
#include <QString> #include <QString>
#include <QStringLiteral> #include <QStringLiteral>
#include <QTransform> #include <QTransform>
#include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <qnamespace.h> #include <qnamespace.h>
class QGraphicsItem; class QGraphicsItem;

View file

@ -3,6 +3,16 @@
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../client/settings/card_counter_settings.h" #include "../../client/settings/card_counter_settings.h"
#include "../../client/sound_engine.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/card_localization.h"
#include "../../interface/widgets/server/chat_view/chat_view.h" #include "../../interface/widgets/server/chat_view/chat_view.h"
#include "../board/card_item.h" #include "../board/card_item.h"
@ -14,16 +24,6 @@
#include <QList> #include <QList>
#include <QSharedPointer> #include <QSharedPointer>
#include <QStringList> #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_move_card.pb.h>
#include <libcockatrice/protocol/pb/context_mulligan.pb.h> #include <libcockatrice/protocol/pb/context_mulligan.pb.h>
#include <libcockatrice/settings/chat_settings.h> #include <libcockatrice/settings/chat_settings.h>
@ -283,7 +283,7 @@ void MessageLogWidget::logDestroyCard(PlayerLogic *player, QString cardName)
} }
void MessageLogWidget::logMoveCard(PlayerLogic *player, void MessageLogWidget::logMoveCard(PlayerLogic *player,
CardState *card, CardItem *card,
CardZoneLogic *startZone, CardZoneLogic *startZone,
int oldX, int oldX,
CardZoneLogic *targetZone, CardZoneLogic *targetZone,
@ -662,7 +662,7 @@ void MessageLogWidget::logSetActivePlayer(PlayerLogic *player)
QString(tr("%1's turn.")).arg(player->getPlayerInfo()->getName()) + "</b></font><br>"); QString(tr("%1's turn.")).arg(player->getPlayerInfo()->getName()) + "</b></font><br>");
} }
void MessageLogWidget::logSetAnnotation(PlayerLogic *player, CardState *card, QString newAnnotation) void MessageLogWidget::logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation)
{ {
appendHtmlServerMessage( appendHtmlServerMessage(
QString(tr("%1 sets annotation of %2 to %3.")) QString(tr("%1 sets annotation of %2 to %3."))
@ -706,7 +706,7 @@ void MessageLogWidget::logSetCounter(PlayerLogic *player, QString counterName, i
.arg(value - oldValue)); .arg(value - oldValue));
} }
void MessageLogWidget::logSetDoesntUntap(PlayerLogic *player, CardState *card, bool doesntUntap) void MessageLogWidget::logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap)
{ {
QString str; QString str;
if (doesntUntap) { if (doesntUntap) {
@ -717,7 +717,7 @@ void MessageLogWidget::logSetDoesntUntap(PlayerLogic *player, CardState *card, b
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(card->getName()))); appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(card->getName())));
} }
void MessageLogWidget::logSetPT(PlayerLogic *player, CardState *card, QString newPT) void MessageLogWidget::logSetPT(PlayerLogic *player, CardItem *card, QString newPT)
{ {
if (currentContext == MessageContext_MoveCard) { if (currentContext == MessageContext_MoveCard) {
return; return;
@ -755,7 +755,7 @@ void MessageLogWidget::logSetSideboardLock(PlayerLogic *player, bool locked)
} }
} }
void MessageLogWidget::logSetTapped(PlayerLogic *player, CardState *card, bool tapped) void MessageLogWidget::logSetTapped(PlayerLogic *player, CardItem *card, bool tapped)
{ {
if (currentContext == MessageContext_MoveCard) { if (currentContext == MessageContext_MoveCard) {
return; return;

View file

@ -14,7 +14,6 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <sys/types.h> #include <sys/types.h>
class CardState;
class AbstractGame; class AbstractGame;
class CardItem; class CardItem;
class GameEventContext; class GameEventContext;
@ -77,7 +76,7 @@ public slots:
void logLeaveSpectator(QString name, QString reason); void logLeaveSpectator(QString name, QString reason);
void logNotReadyStart(PlayerLogic *player); void logNotReadyStart(PlayerLogic *player);
void logMoveCard(PlayerLogic *player, void logMoveCard(PlayerLogic *player,
CardState *card, CardItem *card,
CardZoneLogic *startZone, CardZoneLogic *startZone,
int oldX, int oldX,
CardZoneLogic *targetZone, CardZoneLogic *targetZone,
@ -98,13 +97,13 @@ public slots:
void logSay(PlayerLogic *player, QString message); void logSay(PlayerLogic *player, QString message);
void logSetActivePhase(int phase); void logSetActivePhase(int phase);
void logSetActivePlayer(PlayerLogic *player); void logSetActivePlayer(PlayerLogic *player);
void logSetAnnotation(PlayerLogic *player, CardState *card, QString newAnnotation); void logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation);
void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue); void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue);
void logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue); void logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue);
void logSetDoesntUntap(PlayerLogic *player, CardState *card, bool doesntUntap); void logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap);
void logSetPT(PlayerLogic *player, CardState *card, QString newPT); void logSetPT(PlayerLogic *player, CardItem *card, QString newPT);
void logSetSideboardLock(PlayerLogic *player, bool locked); void logSetSideboardLock(PlayerLogic *player, bool locked);
void logSetTapped(PlayerLogic *player, CardState *card, bool tapped); void logSetTapped(PlayerLogic *player, CardItem *card, bool tapped);
void logShuffle(PlayerLogic *player, CardZoneLogic *zone, int start, int end); void logShuffle(PlayerLogic *player, CardZoneLogic *zone, int start, int end);
void logSpectatorSay(const ServerInfo_User &spectator, QString message); void logSpectatorSay(const ServerInfo_User &spectator, QString message);
void logUnattachCard(PlayerLogic *player, QString cardName); void logUnattachCard(PlayerLogic *player, QString cardName);

View file

@ -14,6 +14,11 @@
#include <QTransform> #include <QTransform>
#include <QtGlobal> #include <QtGlobal>
#include <google/protobuf/stubs/port.h> #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 <qnamespace.h>
#include <qnumeric.h> #include <qnumeric.h>
@ -115,9 +120,9 @@ PhasesToolbar::PhasesToolbar(QGraphicsItem *parent)
: QGraphicsItem(parent), width(100), height(100), ySpacing(1), symbolSize(8) : QGraphicsItem(parent), width(100), height(100), ySpacing(1), symbolSize(8)
{ {
auto *aUntapAll = new QAction(this); auto *aUntapAll = new QAction(this);
connect(aUntapAll, &QAction::triggered, this, &PhasesToolbar::untapAllRequested); connect(aUntapAll, &QAction::triggered, this, &PhasesToolbar::actUntapAll);
auto *aDrawCard = new QAction(this); auto *aDrawCard = new QAction(this);
connect(aDrawCard, &QAction::triggered, this, &PhasesToolbar::drawCardRequested); connect(aDrawCard, &QAction::triggered, this, &PhasesToolbar::actDrawCard);
PhaseButton *untapButton = new PhaseButton("untap", this, aUntapAll); PhaseButton *untapButton = new PhaseButton("untap", this, aUntapAll);
PhaseButton *upkeepButton = new PhaseButton("upkeep", this); PhaseButton *upkeepButton = new PhaseButton("upkeep", this);
@ -139,7 +144,7 @@ PhasesToolbar::PhasesToolbar(QGraphicsItem *parent)
} }
nextTurnButton = new PhaseButton("nextturn", this, nullptr, false); nextTurnButton = new PhaseButton("nextturn", this, nullptr, false);
connect(nextTurnButton, &PhaseButton::clicked, this, &PhasesToolbar::nextTurnRequested); connect(nextTurnButton, &PhaseButton::clicked, this, &PhasesToolbar::actNextTurn);
rearrangeButtons(); rearrangeButtons();
@ -260,5 +265,31 @@ void PhasesToolbar::phaseButtonClicked()
button->triggerDoubleClickAction(); button->triggerDoubleClickAction();
} }
emit setActivePhaseRequested(buttonList.indexOf(button)); 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);
} }

View file

@ -17,6 +17,14 @@
class QAction; class QAction;
class QTimer; class QTimer;
namespace google
{
namespace protobuf
{
class Message;
}
} // namespace google
class PhaseButton : public QObject, public QGraphicsItem class PhaseButton : public QObject, public QGraphicsItem
{ {
Q_OBJECT Q_OBJECT
@ -87,16 +95,11 @@ public slots:
void triggerPhaseAction(int phase); void triggerPhaseAction(int phase);
private slots: private slots:
void phaseButtonClicked(); void phaseButtonClicked();
void actNextTurn();
void actUntapAll();
void actDrawCard();
signals: signals:
/** @name Intent signals void sendGameCommand(const ::google::protobuf::Message &command, int playerId);
* The view reports what the user asked for; GameEventHandler builds the commands.
* @{
*/
void setActivePhaseRequested(int phase);
void nextTurnRequested();
void untapAllRequested();
void drawCardRequested();
/** @} */
protected: protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) override;

View file

@ -1,6 +1,6 @@
/** /**
* @file card_menu_action_type.h * @file card_menu_action_type.h
* @ingroup GameLogicPlayers * @ingroup GameMenusPlayers
*/ */
//! \todo Document this file. //! \todo Document this file.

View file

@ -3,8 +3,16 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/card_counter_settings.h" #include "../../../client/settings/card_counter_settings.h"
#include "../../../client/settings/shortcuts_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 "../../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 "../../game_scene.h"
#include "../card_menu_action_type.h"
#include "../player_graphics_item.h" #include "../player_graphics_item.h"
#include "libcockatrice/card/card_info.h" #include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/database/card_database_querier.h" #include "libcockatrice/card/database/card_database_querier.h"
@ -27,14 +35,6 @@
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/relation/card_relation.h> #include <libcockatrice/card/relation/card_relation.h>
#include <libcockatrice/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 <libcockatrice/utility/zone_names.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <utility> #include <utility>
@ -67,7 +67,7 @@ static QAction *makeAction(QObject *parent, Slot &&slot, bool checkable = false,
return a; return a;
} }
CardMenu::CardMenu(PlayerGraphicsItem *_player, CardItem *_card, bool _shortcutsActive) CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _shortcutsActive)
: player(_player), card(_card), shortcutsActive(_shortcutsActive) : player(_player), card(_card), shortcutsActive(_shortcutsActive)
{ {
const QList<PlayerLogic *> &players = player->getLogic()->getGame()->getPlayerManager()->getPlayers().values(); const QList<PlayerLogic *> &players = player->getLogic()->getGame()->getPlayerManager()->getPlayers().values();
@ -86,7 +86,7 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, CardItem *_card, bool _shortcuts
auto *gameScene = player->getGameScene(); auto *gameScene = player->getGameScene();
// Single selection resolver used by all lambdas — called at trigger time // Single selection resolver used by all lambdas — called at trigger time
auto sel = [gameScene]() { return gameScene->selectedCardStates(); }; auto sel = [gameScene]() { return gameScene->selectedCards(); };
// Unified dispatcher for card menu actions // Unified dispatcher for card menu actions
auto invoke = [actions, sel](CardMenuActionType type) { auto invoke = [actions, sel](CardMenuActionType type) {
@ -108,18 +108,18 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, CardItem *_card, bool _shortcuts
aHide = makeAction(this, [actions, sel]() { actions->actHide(sel()); }); aHide = makeAction(this, [actions, sel]() { actions->actHide(sel()); });
aReduceLifeByPower = makeAction(this, [actions, sel]() { actions->actReduceLifeByPower(sel()); }); aReduceLifeByPower = makeAction(this, [actions, sel]() { actions->actReduceLifeByPower(sel()); });
// Actions bound to the card this menu was opened for, not to the current selection // Actions that use activeCard, not selection — direct connection
aAttach = new QAction(this); aAttach = new QAction(this);
aDrawArrow = new QAction(this); aDrawArrow = new QAction(this);
aSelectAll = new QAction(this); aSelectAll = new QAction(this);
aSelectRow = new QAction(this); aSelectRow = new QAction(this);
aSelectColumn = new QAction(this); aSelectColumn = new QAction(this);
connect(aAttach, &QAction::triggered, this, &CardMenu::attachCard); connect(aAttach, &QAction::triggered, actions, &PlayerActions::actAttach);
connect(aDrawArrow, &QAction::triggered, this, &CardMenu::drawArrow); connect(aDrawArrow, &QAction::triggered, actions, &PlayerActions::actDrawArrow);
connect(aSelectAll, &QAction::triggered, this, [this, gameScene]() { gameScene->selectAllInZone(card); }); connect(aSelectAll, &QAction::triggered, actions, &PlayerActions::actSelectAll);
connect(aSelectRow, &QAction::triggered, this, [this, gameScene]() { gameScene->selectSameRow(card); }); connect(aSelectRow, &QAction::triggered, actions, &PlayerActions::actSelectRow);
connect(aSelectColumn, &QAction::triggered, this, [this, gameScene]() { gameScene->selectSameColumn(card); }); connect(aSelectColumn, &QAction::triggered, actions, &PlayerActions::actSelectColumn);
aRevealToAll = new QAction(this); aRevealToAll = new QAction(this);
@ -337,7 +337,7 @@ void CardMenu::createHandOrCustomZoneMenu(bool canModifyCard)
initContextualPlayersMenu(revealMenu, aRevealToAll); initContextualPlayersMenu(revealMenu, aRevealToAll);
connect(revealMenu, &QMenu::triggered, this, [this](QAction *action) { connect(revealMenu, &QMenu::triggered, this, [this](QAction *action) {
player->getLogic()->getPlayerActions()->actReveal(player->getGameScene()->selectedCardStates(), action); player->getLogic()->getPlayerActions()->actReveal(player->getGameScene()->selectedCards(), action);
}); });
addSeparator(); addSeparator();
@ -441,7 +441,6 @@ void CardMenu::addRelatedCardActions()
if (relatedCards.isEmpty()) { if (relatedCards.isEmpty()) {
return; return;
} }
auto *playerActions = player->getLogic()->getPlayerActions();
addSeparator(); addSeparator();
int index = 0; int index = 0;
@ -485,10 +484,9 @@ void CardMenu::addRelatedCardActions()
} }
auto *createRelated = new QAction(text, this); auto *createRelated = new QAction(text, this);
const int relatedIndex = index++; createRelated->setData(QVariant(index++));
connect(createRelated, &QAction::triggered, this, [this, playerActions, relatedIndex] { connect(createRelated, &QAction::triggered, player->getLogic()->getPlayerActions(),
playerActions->actCreateRelatedCard(card->getState(), relatedIndex); &PlayerActions::actCreateRelatedCard);
});
addAction(createRelated); addAction(createRelated);
} }
@ -497,26 +495,12 @@ void CardMenu::addRelatedCardActions()
createRelatedCards->setShortcuts( createRelatedCards->setShortcuts(
SettingsCache::instance().shortcuts().getShortcut("Player/aCreateRelatedTokens")); SettingsCache::instance().shortcuts().getShortcut("Player/aCreateRelatedTokens"));
} }
connect(createRelatedCards, &QAction::triggered, this, connect(createRelatedCards, &QAction::triggered, player->getLogic()->getPlayerActions(),
[this, playerActions] { playerActions->actCreateAllRelatedCards(card->getState()); }); &PlayerActions::actCreateAllRelatedCards);
addAction(createRelatedCards); addAction(createRelatedCards);
} }
} }
void CardMenu::attachCard()
{
if (card) {
card->drawAttachArrow();
}
}
void CardMenu::drawArrow()
{
if (card) {
card->drawArrow(Qt::red);
}
}
void CardMenu::retranslateUi() void CardMenu::retranslateUi()
{ {
aSelectAll->setText(tr("&Select All")); aSelectAll->setText(tr("&Select All"));

View file

@ -27,7 +27,7 @@ signals:
void cardInfoRequested(const CardRef &cardRef); void cardInfoRequested(const CardRef &cardRef);
public: public:
explicit CardMenu(PlayerGraphicsItem *player, CardItem *card, bool shortcutsActive); explicit CardMenu(PlayerGraphicsItem *player, const CardItem *card, bool shortcutsActive);
void removePlayer(PlayerLogic *playerToRemove); void removePlayer(PlayerLogic *playerToRemove);
void createTableMenu(bool canModifyCard); void createTableMenu(bool canModifyCard);
void createStackMenu(bool canModifyCard); void createStackMenu(bool canModifyCard);
@ -53,13 +53,11 @@ public:
private: private:
PlayerGraphicsItem *player; PlayerGraphicsItem *player;
CardItem *card; const CardItem *card;
QList<QPair<QString, int>> playersInfo; QList<QPair<QString, int>> playersInfo;
bool shortcutsActive; bool shortcutsActive;
void addRelatedCardActions(); void addRelatedCardActions();
void attachCard();
void drawArrow();
void retranslateUi(); void retranslateUi();
void initContextualPlayersMenu(QMenu *menu, QAction *allPlayersAction); void initContextualPlayersMenu(QMenu *menu, QAction *allPlayersAction);
void setShortcutsActive(); void setShortcutsActive();

View file

@ -1,13 +1,13 @@
#include "custom_zone_menu.h" #include "custom_zone_menu.h"
#include "../../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game_scene.h" #include "../../game_scene.h"
#include "../player_graphics_item.h" #include "../player_graphics_item.h"
#include <QAction> #include <QAction>
#include <QList> #include <QList>
#include <QVariant> #include <QVariant>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
CustomZoneMenu::CustomZoneMenu(PlayerGraphicsItem *_player) : player(_player) CustomZoneMenu::CustomZoneMenu(PlayerGraphicsItem *_player) : player(_player)
{ {

View file

@ -2,7 +2,13 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_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 "../../../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 "../player_graphics_item.h"
#include <QAction> #include <QAction>
@ -13,12 +19,6 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QVariant> #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> #include <libcockatrice/utility/zone_names.h>
class QWidget; class QWidget;

View file

@ -2,7 +2,14 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_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 "../../../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 "../player_graphics_item.h"
#include <QAction> #include <QAction>
@ -13,13 +20,6 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QVariant> #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> #include <libcockatrice/utility/zone_names.h>
class QWidget; class QWidget;

View file

@ -2,7 +2,14 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_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 "../../../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 "../player_graphics_item.h"
#include <QAction> #include <QAction>
@ -16,13 +23,6 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QVariant> #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; class QWidget;

View file

@ -2,13 +2,13 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_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 "../../game_scene.h"
#include "../card_menu_action_type.h"
#include "../player_graphics_item.h" #include "../player_graphics_item.h"
#include <QAction> #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")) 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) { auto invoke = [player](CardMenuActionType type) {
return [type, player]() { return [type, player]() {
player->getLogic()->getPlayerActions()->cardMenuAction(player->getGameScene()->selectedCardStates(), type); player->getLogic()->getPlayerActions()->cardMenuAction(player->getGameScene()->selectedCards(), type);
}; };
}; };

View file

@ -2,6 +2,10 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_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/hand_zone.h"
#include "../../../game_graphics/zones/pile_zone.h" #include "../../../game_graphics/zones/pile_zone.h"
#include "../../../game_graphics/zones/table_zone.h" #include "../../../game_graphics/zones/table_zone.h"
@ -22,10 +26,6 @@
#include <QMenu> #include <QMenu>
#include <QString> #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) PlayerMenu::PlayerMenu(PlayerGraphicsItem *_player) : QObject(_player), player(_player)
{ {
@ -87,24 +87,22 @@ void PlayerMenu::setMenusForGraphicItems()
} }
} }
QMenu *PlayerMenu::updateCardMenu(CardState *card) QMenu *PlayerMenu::updateCardMenu(const CardItem *card)
{ {
if (!card) { if (!card) {
emit cardMenuUpdated(nullptr); emit cardMenuUpdated(nullptr);
return nullptr; return nullptr;
} }
auto *cardItem = qobject_cast<CardItem *>(card->parent());
// If is spectator (as spectators don't need card menus), return // If is spectator (as spectators don't need card menus), return
// only update the menu if the card is actually selected // only update the menu if the card is actually selected
if ((player->getLogic()->getGame()->getPlayerManager()->isSpectator() && if ((player->getLogic()->getGame()->getPlayerManager()->isSpectator() &&
!player->getLogic()->getGame()->getPlayerManager()->isJudge()) || !player->getLogic()->getGame()->getPlayerManager()->isJudge()) ||
cardItem == nullptr || player->getGameScene()->getActiveCard() != cardItem) { player->getLogic()->getGame()->getActiveCard() != card) {
return nullptr; return nullptr;
} }
CardMenu *menu = new CardMenu(player, cardItem, shortcutsActive); CardMenu *menu = new CardMenu(player, card, shortcutsActive);
connect(menu, &CardMenu::cardInfoRequested, this, &PlayerMenu::cardInfoRequested); connect(menu, &CardMenu::cardInfoRequested, this, &PlayerMenu::cardInfoRequested);
emit cardMenuUpdated(menu); emit cardMenuUpdated(menu);

View file

@ -14,7 +14,7 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <utility> #include <utility>
class CardState; class CardItem;
class CardMenu; class CardMenu;
class PlayerGraphicsItem; class PlayerGraphicsItem;
class AbstractPlayerComponent; class AbstractPlayerComponent;
@ -43,7 +43,7 @@ signals:
public slots: public slots:
void setMenusForGraphicItems(); void setMenusForGraphicItems();
QMenu *updateCardMenu(CardState *card); QMenu *updateCardMenu(const CardItem *card);
private slots: private slots:
void refreshShortcuts(); void refreshShortcuts();

View file

@ -2,12 +2,12 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_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 "../../game_scene.h"
#include "../player_graphics_item.h" #include "../player_graphics_item.h"
#include <QAction> #include <QAction>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_logic.h>
PtMenu::PtMenu(PlayerGraphicsItem *player) : QMenu(tr("Power / toughness")) PtMenu::PtMenu(PlayerGraphicsItem *player) : QMenu(tr("Power / toughness"))
{ {
@ -15,35 +15,34 @@ PtMenu::PtMenu(PlayerGraphicsItem *player) : QMenu(tr("Power / toughness"))
aIncP = new QAction(this); aIncP = new QAction(this);
connect(aIncP, &QAction::triggered, playerActions, connect(aIncP, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actIncP(player->getGameScene()->selectedCardStates()); }); [player, playerActions] { playerActions->actIncP(player->getGameScene()->selectedCards()); });
aDecP = new QAction(this); aDecP = new QAction(this);
connect(aDecP, &QAction::triggered, playerActions, connect(aDecP, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actDecP(player->getGameScene()->selectedCardStates()); }); [player, playerActions] { playerActions->actDecP(player->getGameScene()->selectedCards()); });
aIncT = new QAction(this); aIncT = new QAction(this);
connect(aIncT, &QAction::triggered, playerActions, connect(aIncT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actIncT(player->getGameScene()->selectedCardStates()); }); [player, playerActions] { playerActions->actIncT(player->getGameScene()->selectedCards()); });
aDecT = new QAction(this); aDecT = new QAction(this);
connect(aDecT, &QAction::triggered, playerActions, connect(aDecT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actDecT(player->getGameScene()->selectedCardStates()); }); [player, playerActions] { playerActions->actDecT(player->getGameScene()->selectedCards()); });
aIncPT = new QAction(this); aIncPT = new QAction(this);
connect(aIncPT, &QAction::triggered, playerActions, connect(aIncPT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actIncPT(player->getGameScene()->selectedCardStates()); }); [player, playerActions] { playerActions->actIncPT(player->getGameScene()->selectedCards()); });
aDecPT = new QAction(this); aDecPT = new QAction(this);
connect(aDecPT, &QAction::triggered, playerActions, connect(aDecPT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actDecPT(player->getGameScene()->selectedCardStates()); }); [player, playerActions] { playerActions->actDecPT(player->getGameScene()->selectedCards()); });
aFlowP = new QAction(this); aFlowP = new QAction(this);
connect(aFlowP, &QAction::triggered, playerActions, connect(aFlowP, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actFlowP(player->getGameScene()->selectedCardStates()); }); [player, playerActions] { playerActions->actFlowP(player->getGameScene()->selectedCards()); });
aFlowT = new QAction(this); aFlowT = new QAction(this);
connect(aFlowT, &QAction::triggered, playerActions, connect(aFlowT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actFlowT(player->getGameScene()->selectedCardStates()); }); [player, playerActions] { playerActions->actFlowT(player->getGameScene()->selectedCards()); });
aSetPT = new QAction(this); aSetPT = new QAction(this);
connect(aSetPT, &QAction::triggered, playerActions, [player, playerActions] { connect(aSetPT, &QAction::triggered, playerActions,
playerActions->actRequestSetPTDialog(player->getGameScene()->selectedCardStates()); [player, playerActions] { playerActions->actRequestSetPTDialog(player->getGameScene()->selectedCards()); });
});
aResetPT = new QAction(this); aResetPT = new QAction(this);
connect(aResetPT, &QAction::triggered, playerActions, connect(aResetPT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actResetPT(player->getGameScene()->selectedCardStates()); }); [player, playerActions] { playerActions->actResetPT(player->getGameScene()->selectedCards()); });
addAction(aIncP); addAction(aIncP);
addAction(aDecP); addAction(aDecP);

View file

@ -1,6 +1,10 @@
#include "rfg_menu.h" #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 "../../../interface/widgets/menus/tearoff_menu.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../player_graphics_item.h" #include "../player_graphics_item.h"
#include <QAction> #include <QAction>
@ -8,10 +12,6 @@
#include <QMenu> #include <QMenu>
#include <QString> #include <QString>
#include <QVariant> #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> #include <libcockatrice/utility/zone_names.h>
class QWidget; class QWidget;

Some files were not shown because too many files have changed in this diff Show more