Merge branch 'master' into tooomm-sentry_bump

This commit is contained in:
tooomm 2026-11-01 17:19:32 +01:00
commit d9b0ee92ca
224 changed files with 6721 additions and 2501 deletions

View file

@ -474,6 +474,10 @@ if [[ $RUNNER_OS == Windows && ! $MAKE_NO_CLIENT ]]; then
# FileShare.None is exactly that case, so this asserts the reason the probe was # 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 # 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. # 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" probe_check="./holder-probe-check.ps1"
cat >"$probe_check" <<'POWERSHELL' cat >"$probe_check" <<'POWERSHELL'
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
@ -500,22 +504,149 @@ function Invoke-Probe([string] $guardDir) {
return @{ Code = $code; Output = $output } 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 { try {
$file = Join-Path $dir "locked.dll" $file = Join-Path $dir "locked.dll"
[IO.File]::WriteAllText($file, "x") [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") $handle = [IO.File]::Open($file, "Open", "ReadWrite", "None")
try { try {
$locked = Invoke-Probe $dir $self = Invoke-Probe $dir
} finally { } finally {
$handle.Dispose() $handle.Dispose()
} }
if ($locked.Code -ne 1) { throw "expected exit 1 for a locked file, got $($locked.Code) : $($locked.Output)" } if ($self.Code -ne 0) { throw "expected exit 0 when the asking process is the only holder, got $($self.Code) : $($self.Output)" }
if ($locked.Output -notmatch "pid\s+$PID\b") { throw "the lookup did not name this process: $($locked.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" $empty = Join-Path $dir "empty"
New-Item -ItemType Directory -Force $empty | Out-Null New-Item -ItemType Directory -Force $empty | Out-Null
$nothing = Invoke-Probe $empty $nothing = Invoke-Probe $empty
if ($nothing.Code -ne 0) { throw "expected exit 0 for a directory with nothing open, got $($nothing.Code) : $($nothing.Output)" } if ($nothing.Code -ne 0) { throw "expected exit 0 for a directory with nothing open, got $($nothing.Code) : $($nothing.Output)" }
Write-Host "holder lookup named pid $PID for a locked file and named nothing for an empty directory" # 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 { } finally {
Remove-Item -Recurse -Force $dir -ErrorAction SilentlyContinue Remove-Item -Recurse -Force $dir -ErrorAction SilentlyContinue
} }

View file

@ -584,11 +584,17 @@ 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" run: mkdir -p "${CACHE_DIR:-$GITHUB_WORKSPACE/.vcpkg-cache}"
# 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,6 +250,15 @@ 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
@ -420,6 +429,9 @@ if(WITH_ORACLE OR WITH_CLIENT)
add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_models ${CMAKE_BINARY_DIR}/libcockatrice_models) add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_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)
@ -450,5 +462,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
qt6_finalize_project() qt_finalize_project()
endif() endif()

File diff suppressed because it is too large Load diff

View file

@ -57,6 +57,29 @@ try {
} }
} }
# --- What was holding the files, read from the log as well -------------------
# The guard and the close attempt each log one line naming every holder and what
# happened to it. Carrying the last of each as event data keeps an abort caused by
# a running instance actionable without opening the attachment. Kept out of the
# message so the issue still groups on the reason alone.
$Holders = ''
$CloseResult = ''
$EndResult = ''
if (Test-Path -LiteralPath $LogPath) {
$holderLine = Select-String -LiteralPath $LogPath -Pattern 'guard: still in use by (.*)$' | Select-Object -Last 1
if ($holderLine) {
$Holders = $holderLine.Matches[0].Groups[1].Value
}
$closeLine = Select-String -LiteralPath $LogPath -Pattern '(close-holders: .*)$' | Select-Object -Last 1
if ($closeLine) {
$CloseResult = $closeLine.Matches[0].Groups[1].Value
}
$endLine = Select-String -LiteralPath $LogPath -Pattern '(end-holders: .*)$' | Select-Object -Last 1
if ($endLine) {
$EndResult = $endLine.Matches[0].Groups[1].Value
}
}
# --- Event --------------------------------------------------------------- # --- 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')
@ -75,6 +98,9 @@ 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,9 +25,7 @@ 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
@ -71,36 +69,27 @@ 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
@ -118,23 +107,11 @@ 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
@ -345,6 +322,7 @@ set(cockatrice_SOURCES
src/interface/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp src/interface/widgets/visual_deck_storage/visual_deck_storage_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
@ -450,6 +428,7 @@ 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
@ -511,7 +490,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)
qt6_add_resources(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES}) qt_add_resources(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
# Declare path variables # Declare path variables
set(ICONDIR set(ICONDIR
@ -532,7 +511,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")
qt6_add_executable( qt_add_executable(
cockatrice cockatrice
WIN32 WIN32
MACOSX_BUNDLE MACOSX_BUNDLE
@ -544,7 +523,7 @@ qt6_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")
qt6_add_shaders( qt_add_shaders(
cockatrice cockatrice
"onboarding_shaders" "onboarding_shaders"
PREFIX PREFIX
@ -556,7 +535,7 @@ qt6_add_shaders(
src/interface/widgets/onboarding/shaders/brand_plate.frag src/interface/widgets/onboarding/shaders/brand_plate.frag
) )
qt6_add_resources( qt_add_resources(
cockatrice cockatrice
"onboarding_qml" "onboarding_qml"
PREFIX PREFIX
@ -572,6 +551,7 @@ 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
@ -788,7 +768,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)
qt6_add_translations( qt_add_translations(
cockatrice cockatrice
TS_FILES TS_FILES
${cockatrice_TS} ${cockatrice_TS}
@ -798,7 +778,7 @@ if(Qt6LinguistTools_FOUND)
cockatrice_QM cockatrice_QM
) )
else() else()
qt6_add_translations(cockatrice TS_FILES ${cockatrice_TS} QM_FILES_OUTPUT_VARIABLE cockatrice_QM) qt_add_translations(cockatrice TS_FILES ${cockatrice_TS} QM_FILES_OUTPUT_VARIABLE cockatrice_QM)
endif() endif()
if(UNIX) if(UNIX)
@ -824,4 +804,4 @@ if(Qt6LinguistTools_FOUND)
endforeach() endforeach()
endif() endif()
qt6_finalize_target(cockatrice) qt_finalize_target(cockatrice)

View file

@ -244,17 +244,27 @@ 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.") +
"\n\n" + tr("To update your client, go to 'Help -> Check for Client Updates'.")); if (missingFeatures.isEmpty()) {
msgBox.setDetailedText(formatted); // Without a feature list this is either a login the client stopped itself or a
// strict server asking for a password; the reason string already says which and
// what to do. No client update can fix any of those causes, so show it instead of
// the update advice.
msgBox.setText(reasonStr.isEmpty() ? tr("The login did not complete.") : reasonStr);
} else {
QString formatted = tr("Missing Features:");
for (const QString &feature : missingFeatures) {
formatted.append(QString("\n %1 ").arg(QChar(0x2022)) + feature);
}
msgBox.setText(tr("Your client seems to be missing features this server requires for connection.") +
"\n\n" + tr("To update your client, go to 'Help -> Check for Client Updates'."));
msgBox.setDetailedText(formatted);
}
msgBox.exec(); msgBox.exec();
break; break;
} }
@ -517,14 +527,38 @@ void ConnectionController::onForgotPasswordSuccess()
SettingsCache::instance().servers().setFPPlayerName(""); SettingsCache::instance().servers().setFPPlayerName("");
} }
void ConnectionController::onForgotPasswordError() void ConnectionController::onForgotPasswordError(int responseCode)
{ {
QMessageBox::warning( // Rate limiting is retryable, so it must not clear the prefill.
dialogParent, tr("Reset Password"), bool keepPrefill = false;
tr("Failed to reset user account password, please contact the server operator to reset your password.")); QString message;
SettingsCache::instance().servers().setFPHostName(""); switch (static_cast<Response::ResponseCode>(responseCode)) {
SettingsCache::instance().servers().setFPPort(""); case Response::RespTooManyRequests:
SettingsCache::instance().servers().setFPPlayerName(""); // Retryable: keep the host/port/user prefill so the user can try again. The window
// is server-configured (an hour by default), so do not promise a shorter wait.
keepPrefill = true;
message = tr("Too many password reset attempts from your address. Please wait before "
"requesting a new reset.");
break;
case Response::RespFunctionNotAllowed:
// Also returned for an unknown user, a banned user, and an expired token or
// challenge, so the message must not claim a single cause.
message = tr("The server did not accept the reset request. Check the user name, or contact "
"the server operator to reset your password.");
break;
default:
message = tr("Failed to reset user account password, please contact the server operator to reset your "
"password.");
break;
}
QMessageBox::warning(dialogParent, tr("Reset Password"), message);
if (!keepPrefill) {
SettingsCache::instance().servers().setFPHostName("");
SettingsCache::instance().servers().setFPPort("");
SettingsCache::instance().servers().setFPPlayerName("");
}
} }
void ConnectionController::onPromptForgotPasswordReset() void ConnectionController::onPromptForgotPasswordReset()

View file

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

View file

@ -19,7 +19,6 @@ 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,13 +2,18 @@
#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 ICardPreferenceProvider class SettingsCardPreferenceProvider : public QObject, 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,6 +1,5 @@
#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"
@ -15,6 +14,7 @@
#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,6 +11,7 @@
#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

@ -1,113 +0,0 @@
#include "card_state.h"
#include <QSharedPointer>
void CardState::resetState(bool keepAnnotations)
{
attacking = false;
counters.clear();
pt.clear();
if (!keepAnnotations) {
annotation.clear();
}
attachedTo = nullptr;
}
void CardState::setZone(CardZoneLogic *_zone)
{
if (zone == _zone) {
return;
}
zone = _zone;
emit zoneChanged(this, zone);
emit stateChanged();
}
void CardState::setAttacking(bool _attacking)
{
if (attacking == _attacking) {
return;
}
attacking = _attacking;
emit attackingChanged(_attacking);
emit stateChanged();
}
void CardState::insertCounter(int id, int value)
{
counters.insert(id, value);
emit countersChanged(counters);
emit stateChanged();
}
void CardState::setCounter(int id, int value)
{
if (value) {
counters[id] = value;
} else {
counters.remove(id);
}
emit countersChanged(counters);
emit stateChanged();
}
void CardState::clearCounters()
{
counters.clear();
emit countersChanged(counters);
emit stateChanged();
}
void CardState::setAnnotation(const QString &_annotation)
{
if (annotation == _annotation) {
return;
}
annotation = _annotation;
emit annotationChanged(annotation);
emit stateChanged();
}
void CardState::setPT(const QString &_pt)
{
if (pt == _pt) {
return;
}
pt = _pt;
emit ptChanged(pt);
emit stateChanged();
}
void CardState::setDoesntUntap(bool _doesntUntap)
{
if (doesntUntap == _doesntUntap) {
return;
}
doesntUntap = _doesntUntap;
emit doesntUntapChanged(_doesntUntap);
emit stateChanged();
}
void CardState::setDestroyOnZoneChange(bool _destroyOnZoneChange)
{
if (destroyOnZoneChange == _destroyOnZoneChange) {
return;
}
destroyOnZoneChange = _destroyOnZoneChange;
emit destroyOnZoneChangeChanged(_destroyOnZoneChange);
emit stateChanged();
}
void CardState::setAttachedTo(CardItem *_attachedTo)
{
if (attachedTo == _attachedTo) {
return;
}
attachedTo = _attachedTo;
emit attachedToChanged(_attachedTo);
emit stateChanged();
}

View file

@ -1,106 +0,0 @@
#ifndef COCKATRICE_CARD_STATE_H
#define COCKATRICE_CARD_STATE_H
#include <QMap>
#include <QObject>
#include <QString>
#include <qtmetamacros.h>
class CardZoneLogic;
class CardItem;
class CardState : public QObject
{
Q_OBJECT
private:
bool attacking = false;
QMap<int, int> counters;
QString annotation;
QString pt;
bool doesntUntap = false;
bool destroyOnZoneChange = false;
CardItem *attachedTo = nullptr;
CardZoneLogic *zone = nullptr;
signals:
void stateChanged();
void attackingChanged(bool newValue);
void countersChanged(const QMap<int, int> &newCounters);
void annotationChanged(const QString &newAnnotation);
void ptChanged(const QString &newPt);
void doesntUntapChanged(bool newValue);
void destroyOnZoneChangeChanged(bool newValue);
void attachedToChanged(CardItem *newAttachedTo);
void zoneChanged(CardState *changedCard, CardZoneLogic *newZone);
public:
explicit CardState(QObject *parent, CardZoneLogic *_zone) : QObject(parent), zone(_zone)
{
}
void resetState(bool keepAnnotations);
CardZoneLogic *getZone() const
{
return zone;
}
void setZone(CardZoneLogic *_zone);
bool getAttacking() const
{
return attacking;
}
void setAttacking(bool _attacking);
const QMap<int, int> &getCounters() const
{
return counters;
}
void insertCounter(int id, int value);
void setCounter(int id, int value);
void clearCounters();
QString getAnnotation() const
{
return annotation;
}
void setAnnotation(const QString &_annotation);
QString getPT() const
{
return pt;
}
void setPT(const QString &_pt);
bool getDoesntUntap() const
{
return doesntUntap;
}
void setDoesntUntap(bool _doesntUntap);
bool getDestroyOnZoneChange() const
{
return destroyOnZoneChange;
}
void setDestroyOnZoneChange(bool _destroyOnZoneChange);
CardItem *getAttachedTo() const
{
return attachedTo;
}
void setAttachedTo(CardItem *_attachedTo);
};
#endif // COCKATRICE_CARD_STATE_H

View file

@ -1,26 +0,0 @@
/**
* @file replay.h
* @ingroup GameLogic
* @ingroup Replay
*/
//! \todo Document this file.
#ifndef COCKATRICE_REPLAY_H
#define COCKATRICE_REPLAY_H
#include "abstract_game.h"
#include <qtmetamacros.h>
class GameReplay;
class QObject;
class Replay : public AbstractGame
{
Q_OBJECT
public:
explicit Replay(QObject *_parent, const GameReplay *_replay, bool isLocalGame);
};
#endif // COCKATRICE_REPLAY_H

View file

@ -34,7 +34,6 @@
#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>
@ -43,13 +42,23 @@
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), id(_id), cardRef(cardRef), tapped(false), facedown(false), tapAngle(0), : ArrowTarget(_owner, parent), state(new CardState(this, nullptr, cardRef, _id)), tapAngle(0),
bgColor(Qt::transparent), isHovered(false), realZValue(0), pendingSelectionCollapse(false) bgColor(Qt::transparent), isHovered(false), realZValue(0)
{ {
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,
@ -67,7 +76,7 @@ AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef
AbstractCardItem::~AbstractCardItem() AbstractCardItem::~AbstractCardItem()
{ {
emit deleteCardInfoPopup(cardRef.name); emit deleteCardInfoPopup(state->getCardRef().name);
} }
QRectF AbstractCardItem::boundingRect() const QRectF AbstractCardItem::boundingRect() const
@ -90,30 +99,29 @@ void AbstractCardItem::pixmapUpdated()
emit sigPixmapUpdated(); emit sigPixmapUpdated();
} }
void AbstractCardItem::refreshCardInfo() void AbstractCardItem::onCardInfoChanged()
{ {
exactCard = CardDatabaseManager::query()->getCard(cardRef);
if (!exactCard && !cardRef.name.isEmpty()) {
CardInfo::UiAttributes attributes = {.tableRow = -1};
auto info = CardInfo::newInstance(cardRef.name, "", true, {}, {}, {}, {}, attributes);
exactCard = ExactCard(info);
}
if (exactCard) {
connect(exactCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &AbstractCardItem::pixmapUpdated);
}
cacheBgColor(); cacheBgColor();
update(); update();
} }
void AbstractCardItem::onCardRefChanged(const CardRef &oldCardRef, const CardRef & /*newCardRef*/)
{
emit deleteCardInfoPopup(oldCardRef.name);
}
void AbstractCardItem::refreshCardInfo()
{
state->refreshCardInfo();
}
/** /**
* Convenience method to get the CardInfo of the exactCard * Convenience method to get the CardInfo of the exactCard
* @return A const reference to the CardInfo, or an empty CardInfo if card was null * @return A const reference to the CardInfo, or an empty CardInfo if card was null
*/ */
const CardInfo &AbstractCardItem::getCardInfo() const const CardInfo &AbstractCardItem::getCardInfo() const
{ {
return exactCard.getInfo(); return state->getCardInfo();
} }
void AbstractCardItem::setRealZValue(qreal _zValue) void AbstractCardItem::setRealZValue(qreal _zValue)
@ -158,13 +166,13 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
QPixmap translatedPixmap; QPixmap translatedPixmap;
bool paintImage = true; bool paintImage = true;
if (facedown || cardRef.name.isEmpty()) { if (state->getFaceDown() || state->getCardRef().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, exactCard, translatedSize.toSize()); CardPictureLoader::getPixmap(translatedPixmap, state->getCard(), translatedSize.toSize());
if (translatedPixmap.isNull()) { if (translatedPixmap.isNull()) {
paintImage = false; paintImage = false;
} }
@ -185,19 +193,20 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
painter->drawPath(shape()); painter->drawPath(shape());
} }
if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() || facedown) { if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() ||
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 (facedown) { if (state->getFaceDown()) {
nameStr = "# " + QString::number(id); nameStr = "# " + QString::number(state->getId());
} else { } else {
QString prefix = ""; QString prefix = "";
if (SettingsCache::instance().debug().getShowCardId()) { if (SettingsCache::instance().debug().getShowCardId()) {
prefix = "#" + QString::number(id) + " "; prefix = "#" + QString::number(state->getId()) + " ";
} }
nameStr = prefix + CardLocalization::displayName(getCardInfo()); nameStr = prefix + CardLocalization::displayName(getCardInfo());
} }
@ -235,21 +244,6 @@ 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) {
@ -275,18 +269,17 @@ void AbstractCardItem::setHovered(bool _hovered)
void AbstractCardItem::setColor(const QString &_color) void AbstractCardItem::setColor(const QString &_color)
{ {
color = _color; state->setColor(_color);
cacheBgColor();
update();
} }
void AbstractCardItem::cacheBgColor() void AbstractCardItem::cacheBgColor()
{ {
QChar colorChar; QChar colorChar;
if (color.isEmpty()) { const QString cardColor = state->getColor();
colorChar = exactCard.getInfo().getColorChar(); if (cardColor.isEmpty()) {
colorChar = state->getCardInfo().getColorChar();
} else { } else {
colorChar = color.at(0); colorChar = cardColor.at(0);
} }
switch (colorChar.toLower().toLatin1()) { switch (colorChar.toLower().toLatin1()) {
@ -314,17 +307,12 @@ void AbstractCardItem::cacheBgColor()
} }
} }
void AbstractCardItem::setTapped(bool _tapped, bool canAnimate) void AbstractCardItem::onTappedChanged(bool newTapped, bool canAnimate)
{ {
if (tapped == _tapped) {
return;
}
tapped = _tapped;
if (SettingsCache::instance().cardsDisplay().getTapAnimation() && canAnimate) { if (SettingsCache::instance().cardsDisplay().getTapAnimation() && canAnimate) {
static_cast<GameScene *>(scene())->registerAnimationItem(this); static_cast<GameScene *>(scene())->registerAnimationItem(this);
} else { } else {
tapAngle = tapped ? 90 : 0; tapAngle = newTapped ? 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)
@ -338,12 +326,6 @@ 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();
@ -357,7 +339,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(cardRef.name); emit cardShiftClicked(state->getCardRef().name);
} else if ((event->modifiers() & Qt::ControlModifier)) { } else if ((event->modifiers() & Qt::ControlModifier)) {
setSelected(!isSelected()); setSelected(!isSelected());
} else if (!isSelected()) { } else if (!isSelected()) {
@ -371,7 +353,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(), cardRef); emit showCardInfoPopup(event->screenPos(), state->getCardRef());
} }
event->accept(); event->accept();
} }
@ -405,7 +387,7 @@ void AbstractCardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
} }
if (event->button() == Qt::MiddleButton) { if (event->button() == Qt::MiddleButton) {
emit deleteCardInfoPopup(cardRef.name); emit deleteCardInfoPopup(state->getCardRef().name);
} }
// This function ensures the parent function doesn't mess around with our selection. // This function ensures the parent function doesn't mess around with our selection.

View file

@ -16,8 +16,7 @@
#include <QSizeF> #include <QSizeF>
#include <QString> #include <QString>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/card/printing/exact_card.h> #include <libcockatrice/game/board/card_state.h>
#include <libcockatrice/utility/card_ref.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class PlayerLogic; class PlayerLogic;
@ -29,13 +28,8 @@ class AbstractCardItem : public ArrowTarget, public IAnimatedItem
{ {
Q_OBJECT Q_OBJECT
protected: protected:
ExactCard exactCard; CardState *state;
int id;
CardRef cardRef;
bool tapped;
bool facedown;
int tapAngle; int tapAngle;
QString color;
QColor bgColor; QColor bgColor;
private: private:
@ -45,6 +39,9 @@ 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();
@ -80,31 +77,38 @@ 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 exactCard; return state->getCard();
} }
const CardInfo &getCardInfo() const; const CardInfo &getCardInfo() const;
int getId() const int getId() const
{ {
return id; return state->getId();
} }
void setId(int _id) void setId(int _id)
{ {
id = _id; state->setId(_id);
} }
QString getName() const QString getName() const
{ {
return cardRef.name; return state->getCardRef().name;
} }
QString getProviderId() const QString getProviderId() const
{ {
return cardRef.providerId; return state->getCardRef().providerId;
}
void setCardRef(const CardRef &_cardRef)
{
state->setCardRef(_cardRef);
} }
void setCardRef(const CardRef &_cardRef);
CardRef getCardRef() const CardRef getCardRef() const
{ {
return cardRef; return state->getCardRef();
} }
qreal getRealZValue() const qreal getRealZValue() const
{ {
@ -116,27 +120,33 @@ public:
{ {
return isHovered; return isHovered;
} }
QString getColor() const [[nodiscard]] QString getColor() const
{ {
return color; return state->getColor();
} }
void setColor(const QString &_color); void setColor(const QString &_color);
bool getTapped() const bool getTapped() const
{ {
return tapped; return state->getTapped();
}
void setTapped(bool _tapped, bool canAnimate = false)
{
state->setTapped(_tapped, canAnimate);
} }
void setTapped(bool _tapped, bool canAnimate = false);
bool getFaceDown() const bool getFaceDown() const
{ {
return facedown; return state->getFaceDown();
}
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(cardRef.name); emit deleteCardInfoPopup(state->getCardRef().name);
} }
/** @brief Default: no per-tick animation. Subclasses override to animate. */ /** @brief Default: no per-tick animation. Subclasses override to animate. */

View file

@ -2,10 +2,6 @@
#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"
@ -23,8 +19,10 @@
#include <QString> #include <QString>
#include <QVariant> #include <QVariant>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/protocol/pb/command_inc_counter.pb.h> #include <libcockatrice/game/board/counter_state.h>
#include <libcockatrice/protocol/pb/command_set_counter.pb.h> #include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/utility/expression.h> #include <libcockatrice/utility/expression.h>
#include <qnamespace.h> #include <qnamespace.h>
@ -151,10 +149,7 @@ void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
menu->exec(event->screenPos()); menu->exec(event->screenPos());
} }
} else { } else {
Command_IncCounter cmd; player->getPlayerActions()->actIncrementCounter(id, event->button() == Qt::LeftButton ? 1 : -1);
cmd.set_counter_id(id);
cmd.set_delta(event->button() == Qt::LeftButton ? 1 : -1);
player->getPlayerActions()->sendGameCommand(cmd);
} }
event->accept(); event->accept();
} }
@ -172,10 +167,7 @@ void AbstractCounter::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
void AbstractCounter::incrementCounter() void AbstractCounter::incrementCounter()
{ {
Command_IncCounter cmd; player->getPlayerActions()->actIncrementCounter(id, static_cast<QAction *>(sender())->data().toInt());
cmd.set_counter_id(id);
cmd.set_delta(static_cast<QAction *>(sender())->data().toInt());
player->getPlayerActions()->sendGameCommand(cmd);
} }
void AbstractCounter::setCounter() void AbstractCounter::setCounter()
@ -199,10 +191,7 @@ void AbstractCounter::setCounter()
} }
Expression exp(value); Expression exp(value);
Command_SetCounter cmd; player->getPlayerActions()->actSetCounter(id, static_cast<int>(exp.parse(dlg.textValue())));
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,11 +2,6 @@
#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"
@ -27,12 +22,12 @@
#include <QTransform> #include <QTransform>
#include <QtGlobal> #include <QtGlobal>
#include <QtMath> #include <QtMath>
#include <libcockatrice/card/card_info.h> #include <libcockatrice/game/board/arrow_data.h>
#include <libcockatrice/protocol/pb/command_attach_card.pb.h> #include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/protocol/pb/command_create_arrow.pb.h> #include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/settings/cards_display_settings.h> #include <libcockatrice/settings/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>
@ -426,19 +421,19 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
CardZoneLogic *startZone = startCard->getZone(); CardZoneLogic *startZone = startCard->getZone();
Command_CreateArrow cmd; ArrowData arrow;
cmd.mutable_arrow_color()->CopyFrom(convertQColorToColor(data->color)); arrow.color = data->color;
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); arrow.startPlayerId = startZone->getPlayer()->getPlayerInfo()->getId();
cmd.set_start_zone(startZone->getName().toStdString()); arrow.startZone = startZone->getName();
cmd.set_start_card_id(startCard->getId()); arrow.startCardId = startCard->getId();
if (auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem)) { if (auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem)) {
CardZoneLogic *targetZone = targetCard->getZone(); CardZoneLogic *targetZone = targetCard->getZone();
cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId()); arrow.targetPlayerId = targetZone->getPlayer()->getPlayerInfo()->getId();
cmd.set_target_zone(targetZone->getName().toStdString()); arrow.targetZone = targetZone->getName();
cmd.set_target_card_id(targetCard->getId()); arrow.targetCardId = targetCard->getId();
} else if (auto *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem)) { } else if (auto *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem)) {
cmd.set_target_player_id(targetPlayer->getOwner()->getPlayerInfo()->getId()); arrow.targetPlayerId = targetPlayer->getOwner()->getPlayerInfo()->getId();
} else { } else {
delArrow(); delArrow();
return; return;
@ -447,22 +442,9 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
// if the card is in hand then we will move the card to stack or table as part of drawing the arrow // if 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);
}
} }
if (deleteInPhase != 0) { player->getPlayerActions()->createArrow(startCard->getState(), arrow, deleteInPhase);
cmd.set_delete_in_phase(deleteInPhase);
}
player->getPlayerActions()->sendGameCommand(cmd);
} }
delArrow(); delArrow();
@ -554,7 +536,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) {
attachCards(startCard, targetCard); player->getPlayerActions()->attachCards(startCard->getState(), targetCard->getState());
} }
} }
@ -563,23 +545,3 @@ 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,7 +1,6 @@
#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"
@ -18,6 +17,7 @@
#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,7 +131,6 @@ 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,9 +1,9 @@
#include "arrow_registry.h" #include "arrow_registry.h"
#include "../game_graphics/board/arrow_item.h" #include "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,6 +4,7 @@
#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,17 +1,6 @@
#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"
@ -41,6 +30,17 @@
#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,8 +53,9 @@ CardItem::CardItem(PlayerLogic *_owner,
const CardRef &cardRef, const CardRef &cardRef,
int _cardid, int _cardid,
CardZoneLogic *_zone) CardZoneLogic *_zone)
: AbstractCardItem(parent, cardRef, _owner, _cardid), state(new CardState(this, _zone)), dragItem(nullptr) : AbstractCardItem(parent, cardRef, _owner, _cardid), dragItem(nullptr)
{ {
state->setZone(_zone);
owner->addCard(this); owner->addCard(this);
connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, this, [this](int counterId) { connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, this, [this](int counterId) {
@ -62,25 +63,44 @@ 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) {
if (owner->getGame()->getActiveCard() == this) { auto *gameScene = qobject_cast<GameScene *>(scene());
if (gameScene && gameScene->getActiveCard() == this) {
emit owner->requestCardMenuUpdate(nullptr); emit owner->requestCardMenuUpdate(nullptr);
owner->getGame()->setActiveCard(nullptr); gameScene->setActiveCard(nullptr);
} }
owner = nullptr; owner = nullptr;
} }
while (!attachedCards.isEmpty()) { const QList<CardState *> &attachedStates = state->getAttachedCards();
attachedCards.first()->setZone(nullptr); // so that it won't try to call reorganizeCards() while (!attachedStates.isEmpty()) {
attachedCards.first()->setAttachedTo(nullptr); auto *attachedItem = qobject_cast<CardItem *>(attachedStates.first()->parent());
if (attachedItem == nullptr) {
break;
}
attachedItem->setZone(nullptr); // so that it won't try to call reorganizeCards()
attachedItem->setAttachedTo(nullptr);
} }
if (state->getAttachedTo() != nullptr) { if (state->getAttachedTo() != nullptr) {
state->getAttachedTo()->removeAttachedCard(this); state->getAttachedTo()->removeAttachedCard(state);
state->setAttachedTo(nullptr); state->setAttachedTo(nullptr);
} }
} }
@ -127,7 +147,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() == exactCard.getInfo().getPowTough()) { if (!getFaceDown() && state->getPT() == state->getCardInfo().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
@ -209,36 +229,13 @@ void CardItem::setPT(const QString &_pt)
void CardItem::setAttachedTo(CardItem *_attachedTo) void CardItem::setAttachedTo(CardItem *_attachedTo)
{ {
if (state->getAttachedTo() != nullptr) { CardState *newParent = _attachedTo == nullptr ? nullptr : _attachedTo->getState();
state->getAttachedTo()->removeAttachedCard(this); state->setAttachedTo(newParent);
}
gridPoint.setX(-1); // If the zone is being torn down, it might already be null by the time a card tries to un-attach all its attached
state->setAttachedTo(_attachedTo); // cards
if (state->getAttachedTo() != nullptr) { if (newParent != nullptr ? newParent->getZone() == nullptr : state->getZone() == nullptr) {
// If the zone is being torn down, it might already be null by the time a card tries to un-attach all its deleteLater();
// 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();
} }
} }
@ -248,13 +245,6 @@ 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)
@ -390,7 +380,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
// Use the buttonDownPos to align the hot spot with the position when // Use the buttonDownPos to align the hot spot with the position when
// the user originally clicked // the user originally clicked
createDragItem(id, event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown); createDragItem(state->getId(), event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown);
dragItem->grabMouse(); dragItem->grabMouse();
int childIndex = 0; int childIndex = 0;
@ -433,42 +423,11 @@ void CardItem::playCard(bool faceDown)
emit playSelected(this); emit playSelected(this);
} }
} else { } else {
state->getZone()->getPlayer()->getPlayerActions()->playCard(this, faceDown); state->getZone()->getPlayer()->getPlayerActions()->playCard(state, faceDown);
} }
} }
} }
QVariantList CardItem::parsePT(const QString &pt)
{
QVariantList ptList = QVariantList();
if (!pt.isEmpty()) {
int sep = pt.indexOf('/');
if (sep == 0) {
ptList.append(QVariant(pt.mid(1))); // cut off starting '/' and take full string
} else {
int start = 0;
for (;;) {
QString item = pt.mid(start, sep - start);
if (item.isEmpty()) {
ptList.append(QVariant(QString()));
} else if (item[0] == '+') {
ptList.append(QVariant(item.mid(1).toInt())); // add as int
} else if (item[0] == '-') {
ptList.append(QVariant(item.toInt())); // add as int
} else {
ptList.append(QVariant(item)); // add as qstring
}
if (sep == -1) {
break;
}
start = sep + 1;
sep = pt.indexOf('/', start);
}
}
}
return ptList;
}
/** /**
* @brief returns true if the zone is a unwritable reveal zone view (eg a card reveal window). Will return false if zone * @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.
@ -493,7 +452,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(this); state->getZone()->removeCard(state);
} }
} else { } else {
playCard(shiftHeld); playCard(shiftHeld);
@ -537,16 +496,16 @@ bool CardItem::animationEvent()
{ {
int rotation = ROTATION_DEGREES_PER_FRAME; int rotation = ROTATION_DEGREES_PER_FRAME;
bool animationIncomplete = true; bool animationIncomplete = true;
if (!tapped) { if (!state->getTapped()) {
rotation *= -1; rotation *= -1;
} }
tapAngle += rotation; tapAngle += rotation;
if (tapped && (tapAngle > 90)) { if (state->getTapped() && (tapAngle > 90)) {
tapAngle = 90; tapAngle = 90;
animationIncomplete = false; animationIncomplete = false;
} }
if (!tapped && (tapAngle < 0)) { if (!state->getTapped() && (tapAngle < 0)) {
tapAngle = 0; tapAngle = 0;
animationIncomplete = false; animationIncomplete = false;
} }
@ -567,7 +526,9 @@ QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value)
bool selected = value.toBool(); bool selected = value.toBool();
if (selected) { if (selected) {
owner->getGame()->setActiveCard(this); if (auto *gameScene = qobject_cast<GameScene *>(scene())) {
gameScene->setActiveCard(this);
}
} }
emit selectionChanged(this, selected); emit selectionChanged(this, selected);

View file

@ -7,7 +7,6 @@
#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"
@ -16,6 +15,7 @@
#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,11 +33,7 @@ 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);
@ -60,10 +56,6 @@ 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();
@ -72,15 +64,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 gridPoint; return state->getGridPoint();
} }
void setGridPoint(const QPoint &_gridPoint) void setGridPoint(const QPoint &_gridPoint)
{ {
gridPoint = _gridPoint; state->setGridPoint(_gridPoint);
} }
[[nodiscard]] QPoint getGridPos() const [[nodiscard]] QPoint getGridPos() const
{ {
return gridPoint; return state->getGridPoint();
} }
[[nodiscard]] PlayerLogic *getOwner() const [[nodiscard]] PlayerLogic *getOwner() const
{ {
@ -123,22 +115,22 @@ public:
{ {
state->setDestroyOnZoneChange(_destroy); state->setDestroyOnZoneChange(_destroy);
} }
[[nodiscard]] CardItem *getAttachedTo() const [[nodiscard]] CardState *getAttachedTo() const
{ {
return state->getAttachedTo(); return state->getAttachedTo();
} }
void setAttachedTo(CardItem *_attachedTo); void setAttachedTo(CardItem *_attachedTo);
void addAttachedCard(CardItem *card) void addAttachedCard(CardState *card)
{ {
attachedCards.append(card); state->addAttachedCard(card);
} }
void removeAttachedCard(CardItem *card) void removeAttachedCard(CardState *card)
{ {
attachedCards.removeOne(card); state->removeAttachedCard(card);
} }
[[nodiscard]] const QList<CardItem *> &getAttachedCards() const [[nodiscard]] const QList<CardState *> &getAttachedCards() const
{ {
return attachedCards; return state->getAttachedCards();
} }
void resetState(bool keepAnnotations = false); void resetState(bool keepAnnotations = false);
void processCardInfo(const ServerInfo_Card &_info); void processCardInfo(const ServerInfo_Card &_info);
@ -150,26 +142,6 @@ 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,13 +2,8 @@
#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"
@ -33,12 +28,12 @@
#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/protocol/pb/command_deck_select.pb.h> #include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/protocol/pb/command_ready_start.pb.h> #include <libcockatrice/game/game_event_handler.h>
#include <libcockatrice/protocol/pb/command_set_playmat.pb.h> #include <libcockatrice/game/game_state.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>
@ -314,9 +309,7 @@ void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck)
return; return;
} }
Command_DeckSelect cmd; PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareDeckSelect(deckString);
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);
@ -345,15 +338,7 @@ void DeckViewContainer::resolveAndSendPlaymat()
lastResolvedPlaymat = resolved; lastResolvedPlaymat = resolved;
Command_SetPlaymat playmatCmd; PendingCommand *playmatPend = parentGame->getGame()->getGameEventHandler()->prepareSetPlaymat(resolved);
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
@ -384,9 +369,7 @@ void DeckViewContainer::loadRemoteDeck()
{ {
DlgLoadRemoteDeck dlg(parentGame->getGame()->getClientForPlayer(playerId), this); DlgLoadRemoteDeck dlg(parentGame->getGame()->getClientForPlayer(playerId), this);
if (dlg.exec()) { if (dlg.exec()) {
Command_DeckSelect cmd; PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareDeckSelectId(dlg.getDeckId());
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);
} }
@ -446,28 +429,17 @@ void DeckViewContainer::forceStart()
return; return;
} }
Command_ReadyStart cmd; parentGame->getGame()->getGameEventHandler()->sendForceStart(playerId);
cmd.set_force_start(true);
cmd.set_ready(true);
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
} }
void DeckViewContainer::sideboardLockButtonClicked() void DeckViewContainer::sideboardLockButtonClicked()
{ {
Command_SetSideboardLock cmd; parentGame->getGame()->getGameEventHandler()->sendSetSideboardLock(sideboardLockButton->getState(), playerId);
cmd.set_locked(sideboardLockButton->getState());
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
} }
void DeckViewContainer::sideboardPlanChanged() void DeckViewContainer::sideboardPlanChanged()
{ {
Command_SetSideboardPlan cmd; parentGame->getGame()->getGameEventHandler()->sendSetSideboardPlan(deckView->getSideboardPlan(), playerId);
const QList<MoveCard_ToZone> &newPlan = deckView->getSideboardPlan();
for (const auto &i : newPlan) {
cmd.add_move_list()->CopyFrom(i);
}
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
} }
/** /**
@ -479,9 +451,7 @@ void DeckViewContainer::sendReadyStartCommand(bool ready)
resolveAndSendPlaymat(); resolveAndSendPlaymat();
} }
Command_ReadyStart cmd; parentGame->getGame()->getGameEventHandler()->sendReadyStart(ready, playerId);
cmd.set_ready(ready);
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
} }
/** /**

View file

@ -0,0 +1,13 @@
#include "dlg_annotation.h"
#include <QKeyEvent>
void AnnotationDialog::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Return && event->modifiers() & Qt::ControlModifier) {
event->accept();
accept();
return;
}
QInputDialog::keyPressEvent(event);
}

View file

@ -0,0 +1,22 @@
/**
* @file dlg_annotation.h
* @ingroup GameDialogs
*/
#ifndef COCKATRICE_DLG_ANNOTATION_H
#define COCKATRICE_DLG_ANNOTATION_H
#include <QInputDialog>
class AnnotationDialog : public QInputDialog
{
Q_OBJECT
void keyPressEvent(QKeyEvent *e) override;
public:
explicit AnnotationDialog(QWidget *parent = nullptr) : QInputDialog(parent)
{
}
};
#endif // COCKATRICE_DLG_ANNOTATION_H

View file

@ -11,6 +11,7 @@
#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;
@ -25,17 +26,6 @@ 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,6 +11,7 @@
#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;
@ -21,13 +22,6 @@ 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,18 +1,10 @@
#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"
@ -40,6 +32,15 @@
#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>
@ -112,6 +113,15 @@ void GameScene::retranslateUi()
} }
} }
QList<CardState *> GameScene::selectedCardStates() const
{
QList<CardState *> selectedStates;
for (auto *card : selectedCards()) {
selectedStates.append(card->getState());
}
return selectedStates;
}
QList<CardItem *> GameScene::selectedCards() const QList<CardItem *> GameScene::selectedCards() const
{ {
QList<CardItem *> selectedCards; QList<CardItem *> selectedCards;
@ -124,6 +134,67 @@ 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);
@ -134,12 +205,12 @@ void GameScene::onCardSelectionChanged(AbstractCardItem *abstractCard, bool sele
auto *owner = card->getOwner(); auto *owner = card->getOwner();
if (selected) { if (selected) {
owner->requestCardMenuUpdate(card); owner->requestCardMenuUpdate(card->getState());
return; return;
} }
if (selectedItems().isEmpty()) { if (selectedItems().isEmpty()) {
owner->getGame()->setActiveCard(nullptr); setActiveCard(nullptr);
owner->requestCardMenuUpdate(nullptr); owner->requestCardMenuUpdate(nullptr);
} }
} }
@ -158,9 +229,9 @@ void GameScene::onCardRightClicked(AbstractCardItem *abstractCard, QPoint screen
return; return;
} }
card->getOwner()->getGame()->setActiveCard(card); setActiveCard(card);
if (auto *menu = view->getPlayerMenu()->updateCardMenu(card)) { if (auto *menu = view->getPlayerMenu()->updateCardMenu(card->getState())) {
menu->popup(screenPos); menu->popup(screenPos);
} }
} }
@ -173,7 +244,7 @@ void GameScene::playSelected(AbstractCardItem *card)
if (!card->getOwner()) { if (!card->getOwner()) {
return; return;
} }
card->getOwner()->getPlayerActions()->actPlay(selectedCards()); card->getOwner()->getPlayerActions()->actPlay(selectedCardStates());
} }
void GameScene::playSelectedFaceDown(AbstractCardItem *card) void GameScene::playSelectedFaceDown(AbstractCardItem *card)
@ -184,7 +255,7 @@ void GameScene::playSelectedFaceDown(AbstractCardItem *card)
if (!card->getOwner()) { if (!card->getOwner()) {
return; return;
} }
card->getOwner()->getPlayerActions()->actPlayFacedown(selectedCards()); card->getOwner()->getPlayerActions()->actPlayFacedown(selectedCardStates());
} }
void GameScene::hideSelected(AbstractCardItem *card) void GameScene::hideSelected(AbstractCardItem *card)
@ -195,7 +266,7 @@ void GameScene::hideSelected(AbstractCardItem *card)
if (!card->getOwner()) { if (!card->getOwner()) {
return; return;
} }
card->getOwner()->getPlayerActions()->actHide(selectedCards()); card->getOwner()->getPlayerActions()->actHide(selectedCardStates());
} }
/** /**
@ -230,6 +301,8 @@ void GameScene::addPlayer(PlayerLogic *player)
[this, id = player->getPlayerInfo()->getId()]() { clearArrowsForPlayerLocally(id); }); [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();
} }
@ -533,7 +606,11 @@ void GameScene::addArrow(QSharedPointer<ArrowData> data)
return; return;
} }
CardItem *startCard = startZone->getCard(data->startCardId); CardState *startCardState = startZone->getCard(data->startCardId);
if (!startCardState) {
return;
}
CardItem *startCard = qobject_cast<CardItem *>(startCardState->parent());
if (!startCard) { if (!startCard) {
return; return;
} }
@ -544,7 +621,8 @@ 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) {
targetItem = zone->getCard(data->targetCardId); CardState *targetCardState = zone->getCard(data->targetCardId);
targetItem = targetCardState == nullptr ? nullptr : qobject_cast<CardItem *>(targetCardState->parent());
} }
} }
if (!targetItem) { if (!targetItem) {
@ -572,11 +650,15 @@ void GameScene::requestArrowDeletion(int playerId, int arrowId)
} }
} }
void GameScene::onCardZoneChanged(CardItem *card, bool sameZone) void GameScene::onCardZoneChanged(CardState *card, bool sameZone)
{ {
auto *item = qobject_cast<CardItem *>(card->parent());
if (!item) {
return;
}
QList<ArrowItem *> toDelete; QList<ArrowItem *> toDelete;
for (auto *arrow : arrowRegistry.all()) { for (auto *arrow : arrowRegistry.all()) {
if (arrow->getStartItem() == card || arrow->getTargetItem() == card) { if (arrow->getStartItem() == item || arrow->getTargetItem() == item) {
if (sameZone) { if (sameZone) {
arrow->updatePath(); arrow->updatePath();
} else { } else {
@ -589,6 +671,25 @@ void GameScene::onCardZoneChanged(CardItem *card, bool sameZone)
} }
} }
void GameScene::onCardViewRefreshRequested(CardState *card, bool zoneChanged, PlayerLogic *newOwner)
{
auto *item = qobject_cast<CardItem *>(card->parent());
if (!item) {
return;
}
if (zoneChanged) {
item->deleteCardInfoPopup();
}
item->deleteDragItem();
if (zoneChanged) {
item->setBeingPointedAt(false);
item->setHovered(false);
}
if (newOwner) {
item->setOwner(newOwner);
}
}
void GameScene::clearArrowsForPlayer(int playerId) void GameScene::clearArrowsForPlayer(int playerId)
{ {
for (int arrowId : arrowRegistry.idsForPlayer(playerId)) { for (int arrowId : arrowRegistry.idsForPlayer(playerId)) {

View file

@ -1,7 +1,9 @@
#ifndef GAMESCENE_H #ifndef GAMESCENE_H
#define GAMESCENE_H #define GAMESCENE_H
#include "../game/arrow_registry.h" #include "animated_item.h"
#include "board/arrow_item.h"
#include "board/arrow_registry.h"
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QHash> #include <QHash>
@ -16,6 +18,8 @@
#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
@ -39,6 +43,7 @@ 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;
@ -69,6 +74,7 @@ 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>
@ -107,6 +113,22 @@ 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.
@ -261,7 +283,15 @@ 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(CardItem *card, bool sameZone); void onCardZoneChanged(CardState *card, bool sameZone);
/**
* @brief Refreshes the view of a card that the server just moved.
*
* @param card The card that moved.
* @param zoneChanged Whether the card crossed a zone boundary.
* @param newOwner The player the card now belongs to, or nullptr to leave the owner alone.
*/
void onCardViewRefreshRequested(CardState *card, bool zoneChanged, PlayerLogic *newOwner);
protected: protected:
/** @brief Handles hover updates. */ /** @brief Handles hover updates. */

View file

@ -1,7 +1,5 @@
#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>
@ -14,6 +12,8 @@
#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,16 +3,6 @@
#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"
@ -24,6 +14,16 @@
#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,
CardItem *card, CardState *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, CardItem *card, QString newAnnotation) void MessageLogWidget::logSetAnnotation(PlayerLogic *player, CardState *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, CardItem *card, bool doesntUntap) void MessageLogWidget::logSetDoesntUntap(PlayerLogic *player, CardState *card, bool doesntUntap)
{ {
QString str; QString str;
if (doesntUntap) { if (doesntUntap) {
@ -717,7 +717,7 @@ void MessageLogWidget::logSetDoesntUntap(PlayerLogic *player, CardItem *card, bo
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(card->getName()))); appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(card->getName())));
} }
void MessageLogWidget::logSetPT(PlayerLogic *player, CardItem *card, QString newPT) void MessageLogWidget::logSetPT(PlayerLogic *player, CardState *card, QString newPT)
{ {
if (currentContext == MessageContext_MoveCard) { if (currentContext == MessageContext_MoveCard) {
return; return;
@ -755,7 +755,7 @@ void MessageLogWidget::logSetSideboardLock(PlayerLogic *player, bool locked)
} }
} }
void MessageLogWidget::logSetTapped(PlayerLogic *player, CardItem *card, bool tapped) void MessageLogWidget::logSetTapped(PlayerLogic *player, CardState *card, bool tapped)
{ {
if (currentContext == MessageContext_MoveCard) { if (currentContext == MessageContext_MoveCard) {
return; return;

View file

@ -14,6 +14,7 @@
#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;
@ -76,7 +77,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,
CardItem *card, CardState *card,
CardZoneLogic *startZone, CardZoneLogic *startZone,
int oldX, int oldX,
CardZoneLogic *targetZone, CardZoneLogic *targetZone,
@ -97,13 +98,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, CardItem *card, QString newAnnotation); void logSetAnnotation(PlayerLogic *player, CardState *card, QString newAnnotation);
void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue); void 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, CardItem *card, bool doesntUntap); void logSetDoesntUntap(PlayerLogic *player, CardState *card, bool doesntUntap);
void logSetPT(PlayerLogic *player, CardItem *card, QString newPT); void logSetPT(PlayerLogic *player, CardState *card, QString newPT);
void logSetSideboardLock(PlayerLogic *player, bool locked); void logSetSideboardLock(PlayerLogic *player, bool locked);
void logSetTapped(PlayerLogic *player, CardItem *card, bool tapped); void logSetTapped(PlayerLogic *player, CardState *card, bool tapped);
void logShuffle(PlayerLogic *player, CardZoneLogic *zone, int start, int end); void 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,11 +14,6 @@
#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>
@ -120,9 +115,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::actUntapAll); connect(aUntapAll, &QAction::triggered, this, &PhasesToolbar::untapAllRequested);
auto *aDrawCard = new QAction(this); auto *aDrawCard = new QAction(this);
connect(aDrawCard, &QAction::triggered, this, &PhasesToolbar::actDrawCard); connect(aDrawCard, &QAction::triggered, this, &PhasesToolbar::drawCardRequested);
PhaseButton *untapButton = new PhaseButton("untap", this, aUntapAll); PhaseButton *untapButton = new PhaseButton("untap", this, aUntapAll);
PhaseButton *upkeepButton = new PhaseButton("upkeep", this); PhaseButton *upkeepButton = new PhaseButton("upkeep", this);
@ -144,7 +139,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::actNextTurn); connect(nextTurnButton, &PhaseButton::clicked, this, &PhasesToolbar::nextTurnRequested);
rearrangeButtons(); rearrangeButtons();
@ -265,31 +260,5 @@ void PhasesToolbar::phaseButtonClicked()
button->triggerDoubleClickAction(); button->triggerDoubleClickAction();
} }
Command_SetActivePhase cmd; emit setActivePhaseRequested(buttonList.indexOf(button));
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,14 +17,6 @@
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
@ -95,11 +87,16 @@ 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:
void sendGameCommand(const ::google::protobuf::Message &command, int playerId); /** @name Intent signals
* The view reports what the user asked for; GameEventHandler builds the commands.
* @{
*/
void setActivePhaseRequested(int phase);
void nextTurnRequested();
void untapAllRequested();
void drawCardRequested();
/** @} */
protected: protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) override;

View file

@ -3,16 +3,8 @@
#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"
@ -35,6 +27,14 @@
#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, const CardItem *_card, bool _shortcutsActive) CardMenu::CardMenu(PlayerGraphicsItem *_player, 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, const CardItem *_card, bool _sho
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->selectedCards(); }; auto sel = [gameScene]() { return gameScene->selectedCardStates(); };
// 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, const CardItem *_card, bool _sho
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 that use activeCard, not selection — direct connection // Actions bound to the card this menu was opened for, not to the current selection
aAttach = new QAction(this); 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, actions, &PlayerActions::actAttach); connect(aAttach, &QAction::triggered, this, &CardMenu::attachCard);
connect(aDrawArrow, &QAction::triggered, actions, &PlayerActions::actDrawArrow); connect(aDrawArrow, &QAction::triggered, this, &CardMenu::drawArrow);
connect(aSelectAll, &QAction::triggered, actions, &PlayerActions::actSelectAll); connect(aSelectAll, &QAction::triggered, this, [this, gameScene]() { gameScene->selectAllInZone(card); });
connect(aSelectRow, &QAction::triggered, actions, &PlayerActions::actSelectRow); connect(aSelectRow, &QAction::triggered, this, [this, gameScene]() { gameScene->selectSameRow(card); });
connect(aSelectColumn, &QAction::triggered, actions, &PlayerActions::actSelectColumn); connect(aSelectColumn, &QAction::triggered, this, [this, gameScene]() { gameScene->selectSameColumn(card); });
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()->selectedCards(), action); player->getLogic()->getPlayerActions()->actReveal(player->getGameScene()->selectedCardStates(), action);
}); });
addSeparator(); addSeparator();
@ -441,6 +441,7 @@ void CardMenu::addRelatedCardActions()
if (relatedCards.isEmpty()) { if (relatedCards.isEmpty()) {
return; return;
} }
auto *playerActions = player->getLogic()->getPlayerActions();
addSeparator(); addSeparator();
int index = 0; int index = 0;
@ -484,9 +485,10 @@ void CardMenu::addRelatedCardActions()
} }
auto *createRelated = new QAction(text, this); auto *createRelated = new QAction(text, this);
createRelated->setData(QVariant(index++)); const int relatedIndex = index++;
connect(createRelated, &QAction::triggered, player->getLogic()->getPlayerActions(), connect(createRelated, &QAction::triggered, this, [this, playerActions, relatedIndex] {
&PlayerActions::actCreateRelatedCard); playerActions->actCreateRelatedCard(card->getState(), relatedIndex);
});
addAction(createRelated); addAction(createRelated);
} }
@ -495,12 +497,26 @@ void CardMenu::addRelatedCardActions()
createRelatedCards->setShortcuts( createRelatedCards->setShortcuts(
SettingsCache::instance().shortcuts().getShortcut("Player/aCreateRelatedTokens")); SettingsCache::instance().shortcuts().getShortcut("Player/aCreateRelatedTokens"));
} }
connect(createRelatedCards, &QAction::triggered, player->getLogic()->getPlayerActions(), connect(createRelatedCards, &QAction::triggered, this,
&PlayerActions::actCreateAllRelatedCards); [this, playerActions] { playerActions->actCreateAllRelatedCards(card->getState()); });
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, const CardItem *card, bool shortcutsActive); explicit CardMenu(PlayerGraphicsItem *player, 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,11 +53,13 @@ public:
private: private:
PlayerGraphicsItem *player; PlayerGraphicsItem *player;
const CardItem *card; 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,13 +2,7 @@
#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>
@ -19,6 +13,12 @@
#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,14 +2,7 @@
#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>
@ -20,6 +13,13 @@
#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,14 +2,7 @@
#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>
@ -23,6 +16,13 @@
#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()->selectedCards(), type); player->getLogic()->getPlayerActions()->cardMenuAction(player->getGameScene()->selectedCardStates(), type);
}; };
}; };

View file

@ -2,10 +2,6 @@
#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"
@ -26,6 +22,10 @@
#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,22 +87,24 @@ void PlayerMenu::setMenusForGraphicItems()
} }
} }
QMenu *PlayerMenu::updateCardMenu(const CardItem *card) QMenu *PlayerMenu::updateCardMenu(CardState *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()) ||
player->getLogic()->getGame()->getActiveCard() != card) { cardItem == nullptr || player->getGameScene()->getActiveCard() != cardItem) {
return nullptr; return nullptr;
} }
CardMenu *menu = new CardMenu(player, card, shortcutsActive); CardMenu *menu = new CardMenu(player, cardItem, 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 CardItem; class CardState;
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(const CardItem *card); QMenu *updateCardMenu(CardState *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,34 +15,35 @@ 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()->selectedCards()); }); [player, playerActions] { playerActions->actIncP(player->getGameScene()->selectedCardStates()); });
aDecP = new QAction(this); aDecP = new QAction(this);
connect(aDecP, &QAction::triggered, playerActions, connect(aDecP, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actDecP(player->getGameScene()->selectedCards()); }); [player, playerActions] { playerActions->actDecP(player->getGameScene()->selectedCardStates()); });
aIncT = new QAction(this); aIncT = new QAction(this);
connect(aIncT, &QAction::triggered, playerActions, connect(aIncT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actIncT(player->getGameScene()->selectedCards()); }); [player, playerActions] { playerActions->actIncT(player->getGameScene()->selectedCardStates()); });
aDecT = new QAction(this); aDecT = new QAction(this);
connect(aDecT, &QAction::triggered, playerActions, connect(aDecT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actDecT(player->getGameScene()->selectedCards()); }); [player, playerActions] { playerActions->actDecT(player->getGameScene()->selectedCardStates()); });
aIncPT = new QAction(this); aIncPT = new QAction(this);
connect(aIncPT, &QAction::triggered, playerActions, connect(aIncPT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actIncPT(player->getGameScene()->selectedCards()); }); [player, playerActions] { playerActions->actIncPT(player->getGameScene()->selectedCardStates()); });
aDecPT = new QAction(this); aDecPT = new QAction(this);
connect(aDecPT, &QAction::triggered, playerActions, connect(aDecPT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actDecPT(player->getGameScene()->selectedCards()); }); [player, playerActions] { playerActions->actDecPT(player->getGameScene()->selectedCardStates()); });
aFlowP = new QAction(this); aFlowP = new QAction(this);
connect(aFlowP, &QAction::triggered, playerActions, connect(aFlowP, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actFlowP(player->getGameScene()->selectedCards()); }); [player, playerActions] { playerActions->actFlowP(player->getGameScene()->selectedCardStates()); });
aFlowT = new QAction(this); aFlowT = new QAction(this);
connect(aFlowT, &QAction::triggered, playerActions, connect(aFlowT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actFlowT(player->getGameScene()->selectedCards()); }); [player, playerActions] { playerActions->actFlowT(player->getGameScene()->selectedCardStates()); });
aSetPT = new QAction(this); aSetPT = new QAction(this);
connect(aSetPT, &QAction::triggered, playerActions, connect(aSetPT, &QAction::triggered, playerActions, [player, playerActions] {
[player, playerActions] { playerActions->actRequestSetPTDialog(player->getGameScene()->selectedCards()); }); playerActions->actRequestSetPTDialog(player->getGameScene()->selectedCardStates());
});
aResetPT = new QAction(this); aResetPT = new QAction(this);
connect(aResetPT, &QAction::triggered, playerActions, connect(aResetPT, &QAction::triggered, playerActions,
[player, playerActions] { playerActions->actResetPT(player->getGameScene()->selectedCards()); }); [player, playerActions] { playerActions->actResetPT(player->getGameScene()->selectedCardStates()); });
addAction(aIncP); addAction(aIncP);
addAction(aDecP); addAction(aDecP);

View file

@ -1,10 +1,6 @@
#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>
@ -12,6 +8,10 @@
#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;

View file

@ -1,14 +1,14 @@
#include "say_menu.h" #include "say_menu.h"
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../player_graphics_item.h" #include "../player_graphics_item.h"
#include <QAction> #include <QAction>
#include <QKeySequence> #include <QKeySequence>
#include <QList> #include <QList>
#include <QString> #include <QString>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/settings/message_settings.h> #include <libcockatrice/settings/message_settings.h>
SayMenu::SayMenu(PlayerGraphicsItem *_player) : player(_player) SayMenu::SayMenu(PlayerGraphicsItem *_player) : player(_player)

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_info.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>
#include <QKeySequence> #include <QKeySequence>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
SideboardMenu::SideboardMenu(PlayerGraphicsItem *player, QMenu *playerMenu) : QMenu(playerMenu) SideboardMenu::SideboardMenu(PlayerGraphicsItem *player, QMenu *playerMenu) : QMenu(playerMenu)
{ {

View file

@ -2,9 +2,6 @@
#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_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 "libcockatrice/card/card_info.h" #include "libcockatrice/card/card_info.h"
@ -14,6 +11,9 @@
#include <QSet> #include <QSet>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h> #include <libcockatrice/deck_list/tree/deck_list_card_node.h>
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h> #include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
UtilityMenu::UtilityMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) : QMenu(playerMenu), player(_player) UtilityMenu::UtilityMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) : QMenu(playerMenu), player(_player)
{ {
@ -44,7 +44,7 @@ UtilityMenu::UtilityMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) : QMenu
aIncrementAllCardCounters = new QAction(this); aIncrementAllCardCounters = new QAction(this);
connect(aIncrementAllCardCounters, &QAction::triggered, playerActions, [this]() { connect(aIncrementAllCardCounters, &QAction::triggered, playerActions, [this]() {
player->getLogic()->getPlayerActions()->actIncrementAllCardCounters( player->getLogic()->getPlayerActions()->actIncrementAllCardCounters(
player->getGameScene()->selectedCards()); player->getGameScene()->selectedCardStates());
}); });
createPredefinedTokenMenu = new QMenu(QString()); createPredefinedTokenMenu = new QMenu(QString());

View file

@ -2,11 +2,10 @@
#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 "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../../interface/widgets/utility/get_text_with_max.h" #include "../../interface/widgets/utility/get_text_with_max.h"
#include "../board/abstract_counter.h" #include "../board/abstract_counter.h"
#include "../board/card_item.h" #include "../board/card_item.h"
#include "../dialogs/dlg_annotation.h"
#include "../dialogs/dlg_create_token.h" #include "../dialogs/dlg_create_token.h"
#include "../dialogs/dlg_move_top_cards_until.h" #include "../dialogs/dlg_move_top_cards_until.h"
#include "../dialogs/dlg_roll_dice.h" #include "../dialogs/dlg_roll_dice.h"
@ -17,6 +16,8 @@
#include <QInputDialog> #include <QInputDialog>
#include <QLineEdit> #include <QLineEdit>
#include <libcockatrice/card/relation/card_relation.h> #include <libcockatrice/card/relation/card_relation.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/utility/string_limits.h> #include <libcockatrice/utility/string_limits.h>
PlayerDialogs::PlayerDialogs(PlayerGraphicsItem *_player, PlayerActions *_playerActions) PlayerDialogs::PlayerDialogs(PlayerGraphicsItem *_player, PlayerActions *_playerActions)
@ -34,7 +35,7 @@ PlayerDialogs::PlayerDialogs(PlayerGraphicsItem *_player, PlayerActions *_player
&PlayerDialogs::onShuffleBottomDialogRequested); &PlayerDialogs::onShuffleBottomDialogRequested);
connect(playerActions, &PlayerActions::requestMulliganDialog, this, &PlayerDialogs::onMulliganDialogRequested); connect(playerActions, &PlayerActions::requestMulliganDialog, this, &PlayerDialogs::onMulliganDialogRequested);
connect(playerActions, &PlayerActions::startingHandSizeChosen, this, &PlayerDialogs::onStartingHandSizeChosen);
connect(playerActions, &PlayerActions::requestDrawCardsDialog, this, &PlayerDialogs::onDrawCardsDialogRequested); connect(playerActions, &PlayerActions::requestDrawCardsDialog, this, &PlayerDialogs::onDrawCardsDialogRequested);
connect(playerActions, &PlayerActions::requestMoveTopCardsToDialog, this, connect(playerActions, &PlayerActions::requestMoveTopCardsToDialog, this,
@ -126,6 +127,11 @@ void PlayerDialogs::onMulliganDialogRequested(int startSize, int handSize, int d
} }
} }
void PlayerDialogs::onStartingHandSizeChosen(int number)
{
SettingsCache::instance().userInterface().setStartingHandSize(number);
}
void PlayerDialogs::onDrawCardsDialogRequested(int defaultNumberTopCards, int deckSize) void PlayerDialogs::onDrawCardsDialogRequested(int defaultNumberTopCards, int deckSize)
{ {
bool ok; bool ok;
@ -196,7 +202,7 @@ void PlayerDialogs::onRollDieDialogRequested()
playerActions->actRollDie(dlg.getDieSideCount(), dlg.getDiceToRollCount()); playerActions->actRollDie(dlg.getDieSideCount(), dlg.getDiceToRollCount());
} }
void PlayerDialogs::onCreateRelatedFromRelationDialogRequested(const CardItem *sourceCard, void PlayerDialogs::onCreateRelatedFromRelationDialogRequested(const CardState *sourceCard,
const CardRelation *cardRelation) const CardRelation *cardRelation)
{ {
if (sourceCard == nullptr || cardRelation == nullptr) { if (sourceCard == nullptr || cardRelation == nullptr) {
@ -251,14 +257,14 @@ void PlayerDialogs::onMoveCardXCardsFromTopDialogRequested(int defaultNumberTopC
number -= 1; // indexes start at 0 number -= 1; // indexes start at 0
if (ok) { if (ok) {
playerActions->actMoveCardXCardsFromTop(player->getGameScene()->selectedCards(), number); playerActions->actMoveCardXCardsFromTop(player->getGameScene()->selectedCardStates(), number);
} }
} }
void PlayerDialogs::onSetPTDialogRequested(const QString &oldPT) void PlayerDialogs::onSetPTDialogRequested(const QString &oldPT)
{ {
bool ok; bool ok;
auto cards = player->getGameScene()->selectedCards(); auto cards = player->getGameScene()->selectedCardStates();
emit requestDialogSemaphore(true); emit requestDialogSemaphore(true);
QString pt = getTextWithMax(dialogParent(), tr("Change power/toughness"), tr("Change stats to:"), QLineEdit::Normal, QString pt = getTextWithMax(dialogParent(), tr("Change power/toughness"), tr("Change stats to:"), QLineEdit::Normal,
oldPT, &ok); oldPT, &ok);
@ -273,7 +279,7 @@ void PlayerDialogs::onSetPTDialogRequested(const QString &oldPT)
void PlayerDialogs::onSetAnnotationDialogRequested(const QString &oldAnnotation) void PlayerDialogs::onSetAnnotationDialogRequested(const QString &oldAnnotation)
{ {
auto cards = player->getGameScene()->selectedCards(); auto cards = player->getGameScene()->selectedCardStates();
emit requestDialogSemaphore(true); emit requestDialogSemaphore(true);
AnnotationDialog *dialog = new AnnotationDialog(dialogParent()); AnnotationDialog *dialog = new AnnotationDialog(dialogParent());
dialog->setOptions(QInputDialog::UsePlainTextEditForTextInput); dialog->setOptions(QInputDialog::UsePlainTextEditForTextInput);
@ -291,7 +297,7 @@ void PlayerDialogs::onSetAnnotationDialogRequested(const QString &oldAnnotation)
void PlayerDialogs::onSetCardCounterDialogRequested(int counterId, const QString &oldValueForDlg) void PlayerDialogs::onSetCardCounterDialogRequested(int counterId, const QString &oldValueForDlg)
{ {
auto cards = player->getGameScene()->selectedCards(); auto cards = player->getGameScene()->selectedCardStates();
emit requestDialogSemaphore(true); emit requestDialogSemaphore(true);
auto &cardCounterSettings = SettingsCache::instance().cardCounters(); auto &cardCounterSettings = SettingsCache::instance().cardCounters();

View file

@ -17,6 +17,7 @@
class CardItem; class CardItem;
class CardRelation; class CardRelation;
class CardState;
class PlayerActions; class PlayerActions;
class QWidget; class QWidget;
struct MoveTopCardsUntilOptions; struct MoveTopCardsUntilOptions;
@ -52,12 +53,13 @@ public slots:
bool faceDown); bool faceDown);
void onDrawBottomCardsDialogRequested(int defaultNumberBottomCards, int maxCards); void onDrawBottomCardsDialogRequested(int defaultNumberBottomCards, int maxCards);
void onRollDieDialogRequested(); void onRollDieDialogRequested();
void onCreateRelatedFromRelationDialogRequested(const CardItem *sourceCard, const CardRelation *cardRelation); void onCreateRelatedFromRelationDialogRequested(const CardState *sourceCard, const CardRelation *cardRelation);
void onCreateTokenDialogRequested(const QStringList &predefinedTokens); void onCreateTokenDialogRequested(const QStringList &predefinedTokens);
void onMoveCardXCardsFromTopDialogRequested(int defaultNumberTopCardsToPlaceBelow, int deckSize); void onMoveCardXCardsFromTopDialogRequested(int defaultNumberTopCardsToPlaceBelow, int deckSize);
void onSetPTDialogRequested(const QString &oldPT); void onSetPTDialogRequested(const QString &oldPT);
void onSetAnnotationDialogRequested(const QString &oldAnnotation); void onSetAnnotationDialogRequested(const QString &oldAnnotation);
void onSetCardCounterDialogRequested(int counterId, const QString &oldValueForDlg); void onSetCardCounterDialogRequested(int counterId, const QString &oldValueForDlg);
void onStartingHandSizeChosen(int number);
private: private:
PlayerGraphicsItem *player; PlayerGraphicsItem *player;

View file

@ -1,15 +1,6 @@
#include "player_graphics_item.h" #include "player_graphics_item.h"
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../game/board/counter_state.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/hand_zone_logic.h"
#include "../../game/zones/pile_zone_logic.h"
#include "../../game/zones/stack_zone_logic.h"
#include "../../game/zones/table_zone_logic.h"
#include "../../interface/card_picture_loader/card_picture_loader.h" #include "../../interface/card_picture_loader/card_picture_loader.h"
#include "../../interface/widgets/cards/art_crop_attribution.h" #include "../../interface/widgets/cards/art_crop_attribution.h"
#include "../../interface/widgets/cards/card_art_utils.h" #include "../../interface/widgets/cards/card_art_utils.h"
@ -45,6 +36,15 @@
#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/playmat_resolver.h> #include <libcockatrice/deck_list/playmat_resolver.h>
#include <libcockatrice/game/board/counter_state.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/hand_zone_logic.h>
#include <libcockatrice/game/zones/pile_zone_logic.h>
#include <libcockatrice/game/zones/stack_zone_logic.h>
#include <libcockatrice/game/zones/table_zone_logic.h>
#include <libcockatrice/settings/interface_settings.h> #include <libcockatrice/settings/interface_settings.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <qnumeric.h> #include <qnumeric.h>
@ -177,6 +177,22 @@ void PlayerGraphicsItem::initializeZones()
void PlayerGraphicsItem::onCustomZoneAdded(QString customZoneName) void PlayerGraphicsItem::onCustomZoneAdded(QString customZoneName)
{ {
// Custom zones have no board view, but their cards still need a graphics owner.
// Without one, PileZone::onCardAdded never runs and cards moved from the table
// or hand into a custom zone stay drawn at their old spot. Give them a hidden
// pile zone so added cards are reparented to (0, 0) and hidden.
CardZoneLogic *logic = player->getZone(customZoneName);
if (auto *pileLogic = qobject_cast<PileZoneLogic *>(logic)) {
auto *hiddenZone = new PileZone(pileLogic, this);
hiddenZone->setVisible(false);
hiddenZone->setPos(0, 0);
zoneGraphicsItems.insert(customZoneName, hiddenZone);
// Hide any cards that arrived before the graphics item existed.
for (CardState *state : pileLogic->getCards()) {
hiddenZone->onCardAdded(state);
}
return;
}
zoneGraphicsItems.insert(customZoneName, nullptr); // Custom zone view goes here, if we ever implement it. zoneGraphicsItems.insert(customZoneName, nullptr); // Custom zone view goes here, if we ever implement it.
} }

View file

@ -1,8 +1,6 @@
#include "player_target.h" #include "player_target.h"
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../interface/pixel_map_generator.h" #include "../../interface/pixel_map_generator.h"
#include "../board/abstract_counter.h" #include "../board/abstract_counter.h"
#include "../board/abstract_graphics_item.h" #include "../board/abstract_graphics_item.h"
@ -30,6 +28,8 @@
#include <QTransform> #include <QTransform>
#include <QtGlobal> #include <QtGlobal>
#include <QtMath> #include <QtMath>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h> #include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <qnumeric.h> #include <qnumeric.h>

View file

@ -16,7 +16,7 @@ static int sumPowers(const QList<CardItem *> &cards)
// calculate total power; // calculate total power;
int total = 0; int total = 0;
for (auto card : cards) { for (auto card : cards) {
QVariantList parsed = CardItem::parsePT(card->getPT()); QVariantList parsed = CardState::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);
@ -44,7 +44,7 @@ static int sumToughness(const QList<CardItem *> &cards)
{ {
int total = 0; int total = 0;
for (auto card : cards) { for (auto card : cards) {
QVariantList parsed = CardItem::parsePT(card->getPT()); QVariantList parsed = CardState::parsePT(card->getPT());
if (parsed.size() == 2) { if (parsed.size() == 2) {
int toughness = parsed.at(1).toInt(); // toInt will default to 0 if it's not an int int toughness = parsed.at(1).toInt(); // toInt will default to 0 if it's not an int
total += qMax(toughness, 0); total += qMax(toughness, 0);

View file

@ -1,13 +1,13 @@
#include "card_zone.h" #include "card_zone.h"
#include "../../game/board/card_list.h"
#include "../../game/zones/card_zone_logic.h"
#include "../board/abstract_graphics_item.h" #include "../board/abstract_graphics_item.h"
#include "../board/card_item.h" #include "../board/card_item.h"
#include <QAction> #include <QAction>
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QMenu> #include <QMenu>
#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;
@ -18,23 +18,60 @@ CardZone::CardZone(CardZoneLogic *_logic, QGraphicsItem *parent)
: AbstractGraphicsItem(parent), menu(nullptr), doubleClickAction(0), logic(_logic) : AbstractGraphicsItem(parent), menu(nullptr), doubleClickAction(0), logic(_logic)
{ {
connect(logic, &CardZoneLogic::retranslateUi, this, &CardZone::retranslateUi); connect(logic, &CardZoneLogic::retranslateUi, this, &CardZone::retranslateUi);
// Invalidation is connected before any consumer so a handler that reads cardItems()
// during the same signal emission always sees a stale flag and rebuilds.
connect(logic, &CardZoneLogic::cardAdded, this, &CardZone::invalidateItems);
connect(logic, &CardZoneLogic::cardCountChanged, this, &CardZone::invalidateItems);
connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::invalidateItems);
connect(logic, &CardZoneLogic::cardAdded, this, &CardZone::onCardAdded); connect(logic, &CardZoneLogic::cardAdded, this, &CardZone::onCardAdded);
connect(logic, &CardZoneLogic::createCardRequested, this, &CardZone::onCreateCardRequested);
connect(logic, &CardZoneLogic::setGraphicsVisibility, this, [this](bool v) { this->setVisible(v); }); connect(logic, &CardZoneLogic::setGraphicsVisibility, this, [this](bool v) { this->setVisible(v); });
connect(logic, &CardZoneLogic::updateGraphics, this, [this]() { update(); }); connect(logic, &CardZoneLogic::updateGraphics, this, [this]() { update(); });
connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::reorganizeCards); connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::reorganizeCards);
} }
void CardZone::onCardAdded(CardItem *addedCard) void CardZone::invalidateItems()
{ {
addedCard->setParentItem(this); itemsDirty = true;
addedCard->setVisible(true); }
addedCard->update();
const QList<CardItem *> &CardZone::cardItems() const
{
if (itemsDirty) {
items.clear();
for (auto *cardState : getLogic()->getCards()) {
if (auto *cardItem = qobject_cast<CardItem *>(cardState->parent())) {
items.append(cardItem);
}
}
itemsDirty = false;
}
return items;
}
void CardZone::onCardAdded(CardState *addedCard)
{
auto *addedItem = addedCard == nullptr ? nullptr : qobject_cast<CardItem *>(addedCard->parent());
if (addedItem == nullptr) {
return;
}
addedItem->setParentItem(this);
addedItem->setVisible(true);
addedItem->update();
}
void CardZone::onCreateCardRequested(const ServerInfo_Card &cardInfo, bool reorganize)
{
CardRef cardRef = {QString::fromStdString(cardInfo.name()), QString::fromStdString(cardInfo.provider_id())};
auto *card = new CardItem(getLogic()->getPlayer(), nullptr, cardRef, cardInfo.id());
card->processCardInfo(cardInfo);
getLogic()->addCard(card->getState(), reorganize, cardInfo.x(), cardInfo.y());
} }
void CardZone::retranslateUi() void CardZone::retranslateUi()
{ {
for (int i = 0; i < getLogic()->getCards().size(); ++i) { for (auto *card : cardItems()) {
getLogic()->getCards()[i]->retranslateUi(); card->retranslateUi();
} }
} }

View file

@ -16,13 +16,17 @@
class CardDragItem; class CardDragItem;
class CardItem; class CardItem;
class CardState;
class CardZoneLogic; class CardZoneLogic;
class QAction; class QAction;
class QGraphicsItem; class QGraphicsItem;
class QMenu; class QMenu;
class QPoint; class QPoint;
class ServerInfo_Card;
template <typename T> class QList; template <typename T> class QList;
class CardItem;
inline Q_LOGGING_CATEGORY(CardZoneLog, "card_zone"); inline Q_LOGGING_CATEGORY(CardZoneLog, "card_zone");
/** /**
@ -55,7 +59,9 @@ public slots:
* Virtual so subclasses (e.g. SelectZone) can override parenting behavior — the Qt signal * Virtual so subclasses (e.g. SelectZone) can override parenting behavior — the Qt signal
* connection in CardZone's constructor dispatches through the vtable. * connection in CardZone's constructor dispatches through the vtable.
*/ */
virtual void onCardAdded(CardItem *addedCard); virtual void onCardAdded(CardState *addedCard);
/** @brief Builds the card item for @p cardInfo and hands it to this zone's logic. */
void onCreateCardRequested(const ServerInfo_Card &cardInfo, bool reorganize);
public: public:
enum enum
@ -76,6 +82,15 @@ public:
return logic; return logic;
} }
/**
* @brief The CardItems backing getLogic()->getCards(), in zone order.
*
* Game logic talks about cards as states; this is the graphics side's view of the same list.
* It is rebuilt on demand after any content change, so views never reach into the logic's
* container themselves and never observe a stale one.
*/
[[nodiscard]] const QList<CardItem *> &cardItems() const;
void setMenu(QMenu *_menu, QAction *_doubleClickAction = 0) void setMenu(QMenu *_menu, QAction *_doubleClickAction = 0)
{ {
menu = _menu; menu = _menu;
@ -83,7 +98,12 @@ public:
} }
private: private:
/** @brief Marks the cached card items stale. Connected to every content-changing signal. */
void invalidateItems();
CardZoneLogic *logic; CardZoneLogic *logic;
mutable QList<CardItem *> items;
mutable bool itemsDirty = true;
}; };
#endif #endif

View file

@ -1,11 +1,6 @@
#include "hand_zone.h" #include "hand_zone.h"
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/hand_zone_logic.h"
#include "../../interface/theme_manager.h" #include "../../interface/theme_manager.h"
#include "../board/card_drag_item.h" #include "../board/card_drag_item.h"
#include "../board/card_item.h" #include "../board/card_item.h"
@ -18,7 +13,11 @@
#include <QPointF> #include <QPointF>
#include <QRectF> #include <QRectF>
#include <QString> #include <QString>
#include <libcockatrice/protocol/pb/command_move_card.pb.h> #include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/hand_zone_logic.h>
#include <libcockatrice/settings/interface_settings.h> #include <libcockatrice/settings/interface_settings.h>
#include <string> #include <string>
@ -48,8 +47,8 @@ void HandZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
QPoint point = dropPoint + scenePos().toPoint(); QPoint point = dropPoint + scenePos().toPoint();
int x = -1; int x = -1;
if (SettingsCache::instance().userInterface().getHorizontalHand()) { if (SettingsCache::instance().userInterface().getHorizontalHand()) {
for (x = 0; x < getLogic()->getCards().size(); x++) { for (x = 0; x < cardItems().size(); x++) {
if (point.x() < static_cast<CardItem *>(getLogic()->getCards().at(x))->scenePos().x()) { if (point.x() < cardItems().at(x)->scenePos().x()) {
break; break;
} }
} }
@ -58,19 +57,13 @@ void HandZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
x = calcDropIndexFromY(dropPoint.y(), !sameZone); x = calcDropIndexFromY(dropPoint.y(), !sameZone);
} }
Command_MoveCard cmd; QList<CardMoveRequest> cards;
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cards.reserve(dragItems.size());
cmd.set_start_zone(startZone->getName().toStdString()); for (const CardDragItem *item : dragItems) {
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId()); cards.append(CardMoveRequest{item->getId()});
cmd.set_target_zone(getLogic()->getName().toStdString());
cmd.set_x(x);
cmd.set_y(-1);
for (int i = 0; i < dragItems.size(); ++i) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId());
} }
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); getLogic()->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), x, -1, cards);
} }
QRectF HandZone::boundingRect() const QRectF HandZone::boundingRect() const
@ -90,23 +83,23 @@ void HandZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio
void HandZone::reorganizeCards() void HandZone::reorganizeCards()
{ {
if (!getLogic()->getCards().isEmpty()) { if (!cardItems().isEmpty()) {
const int cardCount = getLogic()->getCards().size(); const int cardCount = cardItems().size();
if (SettingsCache::instance().userInterface().getHorizontalHand()) { if (SettingsCache::instance().userInterface().getHorizontalHand()) {
bool leftJustified = SettingsCache::instance().userInterface().getLeftJustified(); bool leftJustified = SettingsCache::instance().userInterface().getLeftJustified();
qreal cardWidth = getLogic()->getCards().at(0)->boundingRect().width(); qreal cardWidth = cardItems().at(0)->boundingRect().width();
const int xPadding = leftJustified ? cardWidth * 1.4 : 5; const int xPadding = leftJustified ? cardWidth * 1.4 : 5;
qreal totalWidth = qreal totalWidth =
leftJustified ? boundingRect().width() - (1 * xPadding) - 5 : boundingRect().width() - 2 * xPadding; leftJustified ? boundingRect().width() - (1 * xPadding) - 5 : boundingRect().width() - 2 * xPadding;
if (cardCount == 1) { if (cardCount == 1) {
CardItem *c = getLogic()->getCards().at(0); CardItem *c = cardItems().at(0);
qreal xPosition = leftJustified ? xPadding : xPadding + (totalWidth - cardWidth) / 2; qreal xPosition = leftJustified ? xPadding : xPadding + (totalWidth - cardWidth) / 2;
c->setPos(xPosition, 5); c->setPos(xPosition, 5);
c->setRealZValue(0); c->setRealZValue(0);
} else { } else {
for (int i = 0; i < cardCount; i++) { for (int i = 0; i < cardCount; i++) {
CardItem *c = getLogic()->getCards().at(i); CardItem *c = cardItems().at(i);
// If the total width of the cards is smaller than the available width, // If the total width of the cards is smaller than the available width,
// the cards do not need to overlap and are displayed in the center of the area. // the cards do not need to overlap and are displayed in the center of the area.
if (cardWidth * cardCount > totalWidth) { if (cardWidth * cardCount > totalWidth) {
@ -131,7 +124,7 @@ void HandZone::reorganizeCards()
void HandZone::sortHand(const QList<CardList::SortOption> &options) void HandZone::sortHand(const QList<CardList::SortOption> &options)
{ {
if (getLogic()->getCards().isEmpty()) { if (cardItems().isEmpty()) {
return; return;
} }

View file

@ -7,10 +7,10 @@
#ifndef HANDZONE_H #ifndef HANDZONE_H
#define HANDZONE_H #define HANDZONE_H
#include "../../game/board/card_list.h"
#include "select_zone.h" #include "select_zone.h"
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/game/board/card_list.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class HandZoneLogic; class HandZoneLogic;

View file

@ -1,12 +1,6 @@
#include "pile_zone.h" #include "pile_zone.h"
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../game/board/card_list.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/pile_zone_logic.h"
#include "../board/card_drag_item.h" #include "../board/card_drag_item.h"
#include "../board/card_item.h" #include "../board/card_item.h"
#include "../card_dimensions.h" #include "../card_dimensions.h"
@ -24,7 +18,12 @@
#include <QString> #include <QString>
#include <QTransform> #include <QTransform>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/protocol/pb/command_move_card.pb.h> #include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/pile_zone_logic.h>
#include <libcockatrice/settings/cards_display_settings.h> #include <libcockatrice/settings/cards_display_settings.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <string> #include <string>
@ -67,36 +66,35 @@ void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio
{ {
painter->drawPath(shape()); painter->drawPath(shape());
if (!getLogic()->getCards().isEmpty()) { if (!cardItems().isEmpty()) {
getLogic()->getCards().at(0)->paintPicture(painter, getLogic()->getCards().at(0)->getTranslatedSize(painter), cardItems().at(0)->paintPicture(painter, cardItems().at(0)->getTranslatedSize(painter), 90);
90);
} }
painter->translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F); painter->translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F);
painter->rotate(-90); painter->rotate(-90);
painter->translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F); painter->translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F);
paintNumberEllipse(getLogic()->getCards().size(), 28, Qt::white, -1, -1, painter); paintNumberEllipse(cardItems().size(), 28, Qt::white, -1, -1, painter);
} }
void PileZone::handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneLogic *startZone, const QPoint &) void PileZone::handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneLogic *startZone, const QPoint &)
{ {
Command_MoveCard cmd; QList<CardMoveRequest> cards;
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cards.reserve(dragItems.size());
cmd.set_start_zone(startZone->getName().toStdString()); for (const CardDragItem *item : dragItems) {
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId()); cards.append(CardMoveRequest{item->getId(), item->isForceFaceDown()});
cmd.set_target_zone(getLogic()->getName().toStdString());
cmd.set_x(0);
cmd.set_y(0);
for (int i = 0; i < dragItems.size(); ++i) {
auto cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(dragItems[i]->getId());
if (dragItems[i]->isForceFaceDown()) {
cardToMove->set_face_down(true);
}
} }
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); getLogic()->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), 0, 0, cards);
}
void PileZone::onCardAdded(CardState *addedCard)
{
CardZone::onCardAdded(addedCard);
auto *addedItem = addedCard == nullptr ? nullptr : qobject_cast<CardItem *>(addedCard->parent());
if (addedItem) {
addedItem->setPos(0, 0);
addedItem->setVisible(false);
}
} }
void PileZone::reorganizeCards() void PileZone::reorganizeCards()
@ -126,15 +124,14 @@ void PileZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
return; return;
} }
if (getLogic()->getCards().isEmpty()) { if (cardItems().isEmpty()) {
return; return;
} }
bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier); bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier);
bool bottomCard = event->modifiers().testFlag(Qt::ControlModifier); bool bottomCard = event->modifiers().testFlag(Qt::ControlModifier);
CardItem *card = bottomCard ? getLogic()->getCards().last() : getLogic()->getCards().first(); CardItem *card = bottomCard ? cardItems().last() : cardItems().first();
const int cardid = const int cardid = getLogic()->contentsKnown() ? card->getId() : (bottomCard ? cardItems().size() - 1 : 0);
getLogic()->contentsKnown() ? card->getId() : (bottomCard ? getLogic()->getCards().size() - 1 : 0);
CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), forceFaceDown); CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), forceFaceDown);
drag->grabMouse(); drag->grabMouse();
setCursor(Qt::OpenHandCursor); setCursor(Qt::OpenHandCursor);
@ -147,8 +144,8 @@ void PileZone::mouseReleaseEvent(QGraphicsSceneMouseEvent * /*event*/)
void PileZone::hoverEnterEvent(QGraphicsSceneHoverEvent *event) void PileZone::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{ {
if (!getLogic()->getCards().isEmpty()) { if (!cardItems().isEmpty()) {
getLogic()->getCards()[0]->processHoverEvent(); cardItems()[0]->processHoverEvent();
} }
QGraphicsItem::hoverEnterEvent(event); QGraphicsItem::hoverEnterEvent(event);
} }

View file

@ -33,6 +33,7 @@ public:
[[nodiscard]] QPainterPath shape() const override; [[nodiscard]] QPainterPath shape() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
void reorganizeCards() override; void reorganizeCards() override;
void onCardAdded(CardState *addedCard) override;
void void
handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override; handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override;

View file

@ -1,8 +1,6 @@
#include "select_zone.h" #include "select_zone.h"
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../game/board/card_list.h"
#include "../../game/zones/card_zone_logic.h"
#include "../board/card_item.h" #include "../board/card_item.h"
#include "../game_scene.h" #include "../game_scene.h"
@ -17,6 +15,8 @@
#include <QTransform> #include <QTransform>
#include <QtGlobal> #include <QtGlobal>
#include <QtMath> #include <QtMath>
#include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/settings/cards_display_settings.h> #include <libcockatrice/settings/cards_display_settings.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <qnumeric.h> #include <qnumeric.h>
@ -85,7 +85,7 @@ SelectZone *SelectZone::findOwningSelectZone(const QGraphicsItem *card)
SelectZone::StackLayoutParams SelectZone::buildStackParams(qreal minOffset) const SelectZone::StackLayoutParams SelectZone::buildStackParams(qreal minOffset) const
{ {
const auto &cards = getLogic()->getCards(); const auto &cards = cardItems();
if (cards.isEmpty()) { if (cards.isEmpty()) {
return {0, boundingRect().height(), 0.0, 0.0, minOffset}; return {0, boundingRect().height(), 0.0, 0.0, minOffset};
} }
@ -97,7 +97,7 @@ SelectZone::StackLayoutParams SelectZone::buildStackParams(qreal minOffset) cons
int SelectZone::calcDropIndexFromY(qreal dropY, bool allowCountExpand, qreal minOffset) const int SelectZone::calcDropIndexFromY(qreal dropY, bool allowCountExpand, qreal minOffset) const
{ {
const auto &cards = getLogic()->getCards(); const auto &cards = cardItems();
if (cards.isEmpty()) { if (cards.isEmpty()) {
return 0; return 0;
} }
@ -115,7 +115,7 @@ void SelectZone::restoreStaleEscapedCards()
if (!cardClipContainer) { if (!cardClipContainer) {
return; return;
} }
for (auto *card : getLogic()->getCards()) { for (auto *card : cardItems()) {
// A card parented to the zone (instead of the clip container) should // A card parented to the zone (instead of the clip container) should
// only occur while it is actively hovered. If hover cleanup was // only occur while it is actively hovered. If hover cleanup was
// missed, reparent it back so clipping resumes. // missed, reparent it back so clipping resumes.
@ -127,7 +127,7 @@ void SelectZone::restoreStaleEscapedCards()
void SelectZone::layoutCardsVertically(const StackLayoutParams &params) void SelectZone::layoutCardsVertically(const StackLayoutParams &params)
{ {
const auto &cards = getLogic()->getCards(); const auto &cards = cardItems();
if (cards.isEmpty() || params.cardCount <= 0) { if (cards.isEmpty() || params.cardCount <= 0) {
return; return;
} }
@ -162,9 +162,9 @@ SelectZone::~SelectZone()
if (cardClipContainer) { if (cardClipContainer) {
// Reparent any hover-escaped cards back to the clip container so Qt's // Reparent any hover-escaped cards back to the clip container so Qt's
// parent-child tree is consistent for destruction. setParentItem() does // parent-child tree is consistent for destruction. setParentItem() does
// not invalidate getLogic()->getCards() (it modifies the graphics tree, // not invalidate cardItems() — it only moves the item in the graphics
// not the zone's logical card list). // tree, it never changes the zone's contents.
for (auto *card : getLogic()->getCards()) { for (auto *card : cardItems()) {
if (card && card->parentItem() == this) { if (card && card->parentItem() == this) {
card->setParentItem(cardClipContainer); card->setParentItem(cardClipContainer);
} }
@ -172,12 +172,13 @@ SelectZone::~SelectZone()
} }
} }
void SelectZone::onCardAdded(CardItem *addedCard) void SelectZone::onCardAdded(CardState *addedCard)
{ {
if (cardClipContainer && addedCard) { auto *addedItem = addedCard == nullptr ? nullptr : qobject_cast<CardItem *>(addedCard->parent());
addedCard->setParentItem(cardClipContainer); if (cardClipContainer && addedItem) {
addedCard->setVisible(true); addedItem->setParentItem(cardClipContainer);
addedCard->update(); addedItem->setVisible(true);
addedItem->update();
} else { } else {
CardZone::onCardAdded(addedCard); CardZone::onCardAdded(addedCard);
} }
@ -247,7 +248,7 @@ void SelectZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
} }
QRectF selectionRect = QRectF(selectionOrigin, pos).normalized(); QRectF selectionRect = QRectF(selectionOrigin, pos).normalized();
for (auto card : getLogic()->getCards()) { for (auto card : cardItems()) {
if (card->getAttachedTo() && card->getAttachedTo()->getZone() != getLogic()) { if (card->getAttachedTo() && card->getAttachedTo()->getZone() != getLogic()) {
continue; continue;
} }

View file

@ -35,7 +35,7 @@ public:
SelectZone(CardZoneLogic *logic, QGraphicsItem *parent = nullptr); SelectZone(CardZoneLogic *logic, QGraphicsItem *parent = nullptr);
~SelectZone() override; ~SelectZone() override;
void onCardAdded(CardItem *addedCard) override; void onCardAdded(CardState *addedCard) override;
/** /**
* @brief Temporarily reparents a card from the clip container to this zone so hover scaling is visible beyond clip * @brief Temporarily reparents a card from the clip container to this zone so hover scaling is visible beyond clip

View file

@ -1,11 +1,5 @@
#include "stack_zone.h" #include "stack_zone.h"
#include "../../game/board/card_list.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/stack_zone_logic.h"
#include "../../interface/theme_manager.h" #include "../../interface/theme_manager.h"
#include "../board/card_drag_item.h" #include "../board/card_drag_item.h"
#include "../board/card_item.h" #include "../board/card_item.h"
@ -19,7 +13,12 @@
#include <QPoint> #include <QPoint>
#include <QRectF> #include <QRectF>
#include <QString> #include <QString>
#include <libcockatrice/protocol/pb/command_move_card.pb.h> #include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/stack_zone_logic.h>
#include <qnumeric.h> #include <qnumeric.h>
#include <string> #include <string>
@ -73,31 +72,21 @@ void StackZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
int index = calcDropIndexFromY(dropPoint.y(), !sameZone, MIN_CARD_VISIBLE); int index = calcDropIndexFromY(dropPoint.y(), !sameZone, MIN_CARD_VISIBLE);
if (sameZone) { if (sameZone) {
// Same-zone no-op: don't move a card onto itself // Same-zone no-op: don't move a card onto itself
const auto &cards = getLogic()->getCards(); const auto &cards = cardItems();
if (!cards.isEmpty() && cards.at(index)->getId() == dragItems.at(0)->getId()) { if (!cards.isEmpty() && cards.at(index)->getId() == dragItems.at(0)->getId()) {
return; return;
} }
} }
Command_MoveCard cmd; QList<CardMoveRequest> cards;
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cards.reserve(dragItems.size());
cmd.set_start_zone(startZone->getName().toStdString());
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
cmd.set_target_zone(getLogic()->getName().toStdString());
cmd.set_x(index);
cmd.set_y(0);
for (const CardDragItem *item : dragItems) { for (const CardDragItem *item : dragItems) {
if (item) { if (item) {
auto *cardToMove = cmd.mutable_cards_to_move()->add_card(); cards.append(CardMoveRequest{item->getId(), item->isForceFaceDown()});
cardToMove->set_card_id(item->getId());
if (item->isForceFaceDown()) {
cardToMove->set_face_down(true);
}
} }
} }
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); getLogic()->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), index, 0, cards);
} }
void StackZone::setHeight(qreal newHeight) void StackZone::setHeight(qreal newHeight)
@ -113,7 +102,7 @@ void StackZone::setHeight(qreal newHeight)
void StackZone::reorganizeCards() void StackZone::reorganizeCards()
{ {
if (!getLogic()->getCards().isEmpty()) { if (!cardItems().isEmpty()) {
const auto params = buildStackParams(MIN_CARD_VISIBLE); const auto params = buildStackParams(MIN_CARD_VISIBLE);
layoutCardsVertically(params); layoutCardsVertically(params);
} }

View file

@ -1,12 +1,6 @@
#include "table_zone.h" #include "table_zone.h"
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../game/board/card_list.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/table_zone_logic.h"
#include "../../interface/theme_manager.h" #include "../../interface/theme_manager.h"
#include "../board/abstract_card_item.h" #include "../board/abstract_card_item.h"
#include "../board/card_drag_item.h" #include "../board/card_drag_item.h"
@ -32,8 +26,12 @@
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <libcockatrice/card/card_info.h> #include <libcockatrice/card/card_info.h>
#include <libcockatrice/protocol/pb/command_move_card.pb.h> #include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h> #include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/table_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 <string> #include <string>
@ -62,7 +60,8 @@ TableZone::TableZone(TableZoneLogic *_logic, bool _mirrored, QGraphicsItem *pare
updateBg(); updateBg();
height = MARGIN_TOP + MARGIN_BOTTOM + TABLEROWS * CardDimensions::HEIGHT + (TABLEROWS - 1) * PADDING_Y; height = MARGIN_TOP + MARGIN_BOTTOM + TableZoneLogic::ROW_COUNT * CardDimensions::HEIGHT +
(TableZoneLogic::ROW_COUNT - 1) * PADDING_Y;
width = MIN_WIDTH; width = MIN_WIDTH;
currentMinimumWidth = width; currentMinimumWidth = width;
@ -208,29 +207,20 @@ void TableZone::handleDropEventByGrid(const QList<CardDragItem *> &dragItems,
CardZoneLogic *startZone, CardZoneLogic *startZone,
const QPoint &gridPoint) const QPoint &gridPoint)
{ {
Command_MoveCard cmd; QList<CardMoveRequest> cards;
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cards.reserve(dragItems.size());
cmd.set_start_zone(startZone->getName().toStdString()); for (const CardDragItem *item : dragItems) {
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId()); CardMoveRequest card{item->getId(), item->isForceFaceDown()};
cmd.set_target_zone(getLogic()->getName().toStdString());
cmd.set_x(gridPoint.x());
cmd.set_y(gridPoint.y());
for (const auto &item : dragItems) {
CardToMove *ctm = cmd.mutable_cards_to_move()->add_card();
ctm->set_card_id(item->getId());
if (item->isForceFaceDown()) {
ctm->set_face_down(true);
}
if (startZone->getName() != getLogic()->getName() && !item->isForceFaceDown()) { if (startZone->getName() != getLogic()->getName() && !item->isForceFaceDown()) {
const auto &card = item->getItem()->getCard(); const auto &cardInfo = item->getItem()->getCard();
if (card) { if (cardInfo) {
ctm->set_pt(card.getInfo().getPowTough().toStdString()); card.pt = cardInfo.getInfo().getPowTough();
} }
} }
cards.append(card);
} }
startZone->getPlayer()->getPlayerActions()->sendGameCommand(cmd); startZone->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), gridPoint.x(), gridPoint.y(), cards);
} }
void TableZone::reorganizeCards() void TableZone::reorganizeCards()
@ -238,8 +228,8 @@ void TableZone::reorganizeCards()
// Calculate card stack widths so mapping functions work properly // Calculate card stack widths so mapping functions work properly
computeCardStackWidths(); computeCardStackWidths();
for (int i = 0; i < getLogic()->getCards().size(); ++i) { for (int i = 0; i < cardItems().size(); ++i) {
QPoint gridPoint = getLogic()->getCards()[i]->getGridPos(); QPoint gridPoint = cardItems()[i]->getGridPos();
if (gridPoint.x() == -1) { if (gridPoint.x() == -1) {
continue; continue;
} }
@ -248,21 +238,24 @@ void TableZone::reorganizeCards()
qreal x = mapPoint.x(); qreal x = mapPoint.x();
qreal y = mapPoint.y(); qreal y = mapPoint.y();
int numberAttachedCards = getLogic()->getCards()[i]->getAttachedCards().size(); int numberAttachedCards = cardItems()[i]->getAttachedCards().size();
qreal actualX = x + numberAttachedCards * STACKED_CARD_OFFSET_X; qreal actualX = x + numberAttachedCards * STACKED_CARD_OFFSET_X;
qreal actualY = y; qreal actualY = y;
if (numberAttachedCards) { if (numberAttachedCards) {
actualY += 15; actualY += 15;
} }
getLogic()->getCards()[i]->setPos(actualX, actualY); cardItems()[i]->setPos(actualX, actualY);
getLogic()->getCards()[i]->setRealZValue(ZValues::tableCardZValue(actualX, actualY)); cardItems()[i]->setRealZValue(ZValues::tableCardZValue(actualX, actualY));
QListIterator<CardItem *> attachedCardIterator(getLogic()->getCards()[i]->getAttachedCards()); QListIterator<CardState *> attachedCardIterator(cardItems()[i]->getAttachedCards());
int j = 0; int j = 0;
while (attachedCardIterator.hasNext()) { while (attachedCardIterator.hasNext()) {
++j; ++j;
CardItem *attachedCard = attachedCardIterator.next(); CardItem *attachedCard = qobject_cast<CardItem *>(attachedCardIterator.next()->parent());
if (attachedCard == nullptr) {
continue;
}
qreal childX = actualX - j * STACKED_CARD_OFFSET_X; qreal childX = actualX - j * STACKED_CARD_OFFSET_X;
qreal childY = y + 5; qreal childY = y + 5;
attachedCard->setPos(childX, childY); attachedCard->setPos(childX, childY);
@ -276,35 +269,18 @@ void TableZone::reorganizeCards()
void TableZone::toggleTapped() void TableZone::toggleTapped()
{ {
QList<QGraphicsItem *> selectedItemsRaw = scene()->selectedItems(); QList<CardState *> selectedCards;
QList<QGraphicsItem *> selectedItems; for (auto *item : scene()->selectedItems()) {
auto *card = qgraphicsitem_cast<CardItem *>(item);
auto isCardOnTable = [](const QGraphicsItem *item) { if (card && card->getZone()->getName() == ZoneNames::TABLE) {
if (auto card = qgraphicsitem_cast<const CardItem *>(item)) { selectedCards.append(card->getState());
return card->getZone()->getName() == ZoneNames::TABLE;
}
return false;
};
std::copy_if(selectedItemsRaw.begin(), selectedItemsRaw.end(), std::back_inserter(selectedItems), isCardOnTable);
bool tapAll = std::any_of(selectedItems.begin(), selectedItems.end(), [](const QGraphicsItem *item) {
return !qgraphicsitem_cast<const CardItem *>(item)->getTapped();
});
QList<const ::google::protobuf::Message *> cmdList;
for (const auto &selectedItem : selectedItems) {
CardItem *temp = qgraphicsitem_cast<CardItem *>(selectedItem);
if (temp->getTapped() != tapAll) {
Command_SetCardAttr *cmd = new Command_SetCardAttr;
cmd->set_zone(getLogic()->getName().toStdString());
cmd->set_card_id(temp->getId());
cmd->set_attribute(AttrTapped);
cmd->set_attr_value(tapAll ? "1" : "0");
cmdList.append(cmd);
} }
} }
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(
getLogic()->getPlayer()->getPlayerActions()->prepareGameCommand(cmdList)); const bool tapAll = std::any_of(selectedCards.begin(), selectedCards.end(),
[](const CardState *card) { return !card->getTapped(); });
getLogic()->getPlayer()->getPlayerActions()->actToggleTapped(getLogic()->getName(), selectedCards, tapAll);
} }
void TableZone::resizeToContents() void TableZone::resizeToContents()
@ -312,9 +288,9 @@ void TableZone::resizeToContents()
int xMax = 0; int xMax = 0;
// Find rightmost card position, which includes the left margin amount. // Find rightmost card position, which includes the left margin amount.
for (int i = 0; i < getLogic()->getCards().size(); ++i) { for (int i = 0; i < cardItems().size(); ++i) {
if (getLogic()->getCards()[i]->pos().x() > xMax) { if (cardItems()[i]->pos().x() > xMax) {
xMax = (int)getLogic()->getCards()[i]->pos().x(); xMax = (int)cardItems()[i]->pos().x();
} }
} }
@ -335,9 +311,9 @@ void TableZone::resizeToContents()
CardItem *TableZone::getCardFromGrid(const QPoint &gridPoint) const CardItem *TableZone::getCardFromGrid(const QPoint &gridPoint) const
{ {
for (int i = 0; i < getLogic()->getCards().size(); i++) { for (int i = 0; i < cardItems().size(); i++) {
if (getLogic()->getCards().at(i)->getGridPoint() == gridPoint) { if (cardItems().at(i)->getGridPoint() == gridPoint) {
return getLogic()->getCards().at(i); return cardItems().at(i);
} }
} }
return 0; return 0;
@ -354,8 +330,8 @@ void TableZone::computeCardStackWidths()
// Each card stack is three grid points worth of card locations. // Each card stack is three grid points worth of card locations.
// First pass: compute the number of cards at each card stack. // First pass: compute the number of cards at each card stack.
QMap<int, int> cardStackCount; QMap<int, int> cardStackCount;
for (int i = 0; i < getLogic()->getCards().size(); ++i) { for (int i = 0; i < cardItems().size(); ++i) {
const QPoint &gridPoint = getLogic()->getCards()[i]->getGridPos(); const QPoint &gridPoint = cardItems()[i]->getGridPos();
if (gridPoint.x() == -1) { if (gridPoint.x() == -1) {
continue; continue;
} }
@ -366,8 +342,8 @@ void TableZone::computeCardStackWidths()
// Second pass: compute the width at each card stack. // Second pass: compute the width at each card stack.
cardStackWidth.clear(); cardStackWidth.clear();
for (int i = 0; i < getLogic()->getCards().size(); ++i) { for (int i = 0; i < cardItems().size(); ++i) {
const QPoint &gridPoint = getLogic()->getCards()[i]->getGridPos(); const QPoint &gridPoint = cardItems()[i]->getGridPos();
if (gridPoint.x() == -1) { if (gridPoint.x() == -1) {
continue; continue;
} }
@ -375,8 +351,8 @@ void TableZone::computeCardStackWidths()
const int key = getCardStackMapKey(gridPoint.x() / 3, gridPoint.y()); const int key = getCardStackMapKey(gridPoint.x() / 3, gridPoint.y());
const int stackCount = cardStackCount.value(key, 0); const int stackCount = cardStackCount.value(key, 0);
if (stackCount == 1) { if (stackCount == 1) {
cardStackWidth.insert(key, CardDimensions::WIDTH + getLogic()->getCards()[i]->getAttachedCards().size() * cardStackWidth.insert(key, CardDimensions::WIDTH +
STACKED_CARD_OFFSET_X); cardItems()[i]->getAttachedCards().size() * STACKED_CARD_OFFSET_X);
} else { } else {
cardStackWidth.insert(key, CardDimensions::WIDTH + (stackCount - 1) * STACKED_CARD_OFFSET_X); cardStackWidth.insert(key, CardDimensions::WIDTH + (stackCount - 1) * STACKED_CARD_OFFSET_X);
} }
@ -397,7 +373,7 @@ QPointF TableZone::mapFromGrid(QPoint gridPoint) const
} }
if (isInverted()) { if (isInverted()) {
gridPoint.setY(TABLEROWS - 1 - gridPoint.y()); gridPoint.setY(TableZoneLogic::ROW_COUNT - 1 - gridPoint.y());
} }
// Start with margin plus stacked card offset // Start with margin plus stacked card offset
@ -423,10 +399,10 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const
const int gridPointHeight = CardDimensions::HEIGHT + PADDING_Y; const int gridPointHeight = CardDimensions::HEIGHT + PADDING_Y;
int gridPointY = (y + PADDING_Y / 2) / gridPointHeight; int gridPointY = (y + PADDING_Y / 2) / gridPointHeight;
gridPointY = clampValidTableRow(gridPointY); gridPointY = TableZoneLogic::clampValidTableRow(gridPointY);
if (isInverted()) { if (isInverted()) {
gridPointY = TABLEROWS - 1 - gridPointY; gridPointY = TableZoneLogic::ROW_COUNT - 1 - gridPointY;
} }
// Calculating the x-coordinate of the grid space requires adding up the // Calculating the x-coordinate of the grid space requires adding up the
@ -471,22 +447,3 @@ QPointF TableZone::closestGridPoint(const QPointF &point)
} }
return mapFromGrid(gridPoint); return mapFromGrid(gridPoint);
} }
int TableZone::clampValidTableRow(const int row)
{
if (row < 0) {
return 0;
}
if (row >= TABLEROWS) {
return TABLEROWS - 1;
}
return row;
}
int TableZone::tableRowToGridY(int tableRow)
{
if (tableRow > 2) {
tableRow = 1;
}
return clampValidTableRow(2 - tableRow);
}

View file

@ -17,6 +17,7 @@
#include <QPoint> #include <QPoint>
#include <QPointF> #include <QPointF>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/game/zones/table_zone_logic.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class CardDragItem; class CardDragItem;
@ -25,7 +26,6 @@ class CardZoneLogic;
class QColor; class QColor;
class QGraphicsItem; class QGraphicsItem;
class QPainter; class QPainter;
class TableZoneLogic;
template <typename T> class QList; template <typename T> class QList;
/** /**
@ -42,8 +42,6 @@ signals:
void sizeChanged(); void sizeChanged();
private: private:
static const int TABLEROWS = 3;
/* /*
Margins between table edges and cards, paddings between cards Margins between table edges and cards, paddings between cards
*/ */
@ -181,15 +179,6 @@ public:
QPointF closestGridPoint(const QPointF &point) override; QPointF closestGridPoint(const QPointF &point) override;
static int clampValidTableRow(const int row);
/**
* Converts a card's logical table row (0=creatures, 1=noncreatures, 2=lands)
* to the corresponding grid Y coordinate. Cards with tableRow > 2 (e.g.,
* instants/sorceries) default to the noncreatures row.
*/
static int tableRowToGridY(int tableRow);
/** /**
Resizes the TableZone in case CardItems are within or Resizes the TableZone in case CardItems are within or
outside of the TableZone constraints. outside of the TableZone constraints.

View file

@ -1,11 +1,6 @@
#include "view_zone.h" #include "view_zone.h"
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/view_zone_logic.h"
#include "../board/card_drag_item.h" #include "../board/card_drag_item.h"
#include "../board/card_item.h" #include "../board/card_item.h"
#include "../card_dimensions.h" #include "../card_dimensions.h"
@ -26,8 +21,11 @@
#include <QtMath> #include <QtMath>
#include <functional> #include <functional>
#include <libcockatrice/card/card_localization.h> #include <libcockatrice/card/card_localization.h>
#include <libcockatrice/protocol/pb/command_dump_zone.pb.h> #include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/protocol/pb/command_move_card.pb.h> #include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/view_zone_logic.h>
#include <libcockatrice/protocol/pb/response_dump_zone.pb.h> #include <libcockatrice/protocol/pb/response_dump_zone.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h> #include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
#include <libcockatrice/protocol/pending_command.h> #include <libcockatrice/protocol/pending_command.h>
@ -42,21 +40,23 @@ class QGraphicsItem;
ZoneViewZone::ZoneViewZone(ZoneViewZoneLogic *_logic, QGraphicsItem *parent) ZoneViewZone::ZoneViewZone(ZoneViewZoneLogic *_logic, QGraphicsItem *parent)
: SelectZone(_logic, parent), bRect(QRectF()), minRows(0), groupBy(CardList::NoSort), sortBy(CardList::NoSort) : SelectZone(_logic, parent), bRect(QRectF()), minRows(0), groupBy(CardList::NoSort), sortBy(CardList::NoSort)
{ {
if (!(qobject_cast<ZoneViewZoneLogic *>(getLogic())->getRevealZone() && auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(getLogic());
!qobject_cast<ZoneViewZoneLogic *>(getLogic())->getWriteableRevealZone())) { if (!(viewLogic->getRevealZone() && !viewLogic->getWriteableRevealZone())) {
qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->getViews().append(this); viewLogic->getOriginalZone()->getViews().append(viewLogic);
} }
connect(_logic, &ZoneViewZoneLogic::closeView, this, &ZoneViewZone::close); connect(_logic, &ZoneViewZoneLogic::closeView, this, &ZoneViewZone::close);
} }
void ZoneViewZone::addToViews() void ZoneViewZone::addToViews()
{ {
qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->getViews().append(this); auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(getLogic());
viewLogic->getOriginalZone()->getViews().append(viewLogic);
} }
void ZoneViewZone::removeFromViews() void ZoneViewZone::removeFromViews()
{ {
qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->getViews().removeOne(this); auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(getLogic());
viewLogic->getOriginalZone()->getViews().removeOne(viewLogic);
} }
/** /**
@ -66,9 +66,9 @@ void ZoneViewZone::removeFromViews()
void ZoneViewZone::close() void ZoneViewZone::close()
{ {
emit closed(); emit closed();
if (!(qobject_cast<ZoneViewZoneLogic *>(getLogic())->getRevealZone() && auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(getLogic());
!qobject_cast<ZoneViewZoneLogic *>(getLogic())->getWriteableRevealZone())) { if (!(viewLogic->getRevealZone() && !viewLogic->getWriteableRevealZone())) {
qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->getViews().removeOne(this); viewLogic->getOriginalZone()->getViews().removeOne(viewLogic);
} }
deleteLater(); deleteLater();
} }
@ -95,28 +95,27 @@ void ZoneViewZone::initializeCards(const QList<const ServerInfo_Card *> &cardLis
auto copy = new CardItem(getLogic()->getPlayer(), this, cardRef, card->id()); auto copy = new CardItem(getLogic()->getPlayer(), this, cardRef, card->id());
copy->setFaceDown(card->face_down()); copy->setFaceDown(card->face_down());
getLogic()->addCard(copy, false, i); getLogic()->addCard(copy->getState(), false, i);
} }
reorganizeCards(); reorganizeCards();
} else if (!qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->contentsKnown()) { } else if (!qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->contentsKnown()) {
Command_DumpZone cmd; auto *viewLogic = qobject_cast<ZoneViewZoneLogic *>(getLogic());
cmd.set_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId()); PendingCommand *pend = viewLogic->prepareZoneDump();
cmd.set_zone_name(getLogic()->getName().toStdString()); if (pend != nullptr) {
cmd.set_number_cards(numberCards); // A local game answers synchronously, so the handler has to be connected before the
cmd.set_is_reversed(qobject_cast<ZoneViewZoneLogic *>(getLogic())->getIsReversed()); // command is sent or the response would be delivered to nobody.
connect(pend, &PendingCommand::finished, this, &ZoneViewZone::zoneDumpReceived);
PendingCommand *pend = getLogic()->getPlayer()->getPlayerActions()->prepareGameCommand(cmd); viewLogic->sendZoneDump(pend);
connect(pend, &PendingCommand::finished, this, &ZoneViewZone::zoneDumpReceived); }
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(pend);
} else { } else {
const CardList &c = qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->getCards(); const CardList &c = qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->getCards();
int number = numberCards == -1 ? c.size() : (numberCards < c.size() ? numberCards : c.size()); int number = numberCards == -1 ? c.size() : (numberCards < c.size() ? numberCards : c.size());
for (int i = 0; i < number; i++) { for (int i = 0; i < number; i++) {
CardItem *card = c.at(i); CardState *card = c.at(i);
auto copy = new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId()); auto copy = new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId());
copy->setFaceDown(card->getFaceDown()); copy->setFaceDown(card->getFaceDown());
getLogic()->addCard(copy, false, i); getLogic()->addCard(copy->getState(), false, i);
} }
reorganizeCards(); reorganizeCards();
} }
@ -132,7 +131,7 @@ void ZoneViewZone::zoneDumpReceived(const Response &r)
auto cardProviderId = QString::fromStdString(cardInfo.provider_id()); auto cardProviderId = QString::fromStdString(cardInfo.provider_id());
auto card = new CardItem(getLogic()->getPlayer(), this, {cardName, cardProviderId}, cardInfo.id(), getLogic()); auto card = new CardItem(getLogic()->getPlayer(), this, {cardName, cardProviderId}, cardInfo.id(), getLogic());
card->setFaceDown(cardInfo.face_down()); card->setFaceDown(cardInfo.face_down());
getLogic()->rawInsertCard(card, i); getLogic()->rawInsertCard(card->getState(), i);
} }
qobject_cast<ZoneViewZoneLogic *>(getLogic())->updateCardIds(ZoneViewZoneLogic::INITIALIZE); qobject_cast<ZoneViewZoneLogic *>(getLogic())->updateCardIds(ZoneViewZoneLogic::INITIALIZE);
@ -146,11 +145,11 @@ void ZoneViewZone::zoneDumpReceived(const Response &r)
void ZoneViewZone::reorganizeCards() void ZoneViewZone::reorganizeCards()
{ {
// filter cards // filter cards
CardList cardsToDisplay = CardList(getLogic()->getCards().getContentsKnown()); CardList cardsToDisplay = CardList(getLogic()->contentsKnown());
for (auto card : getLogic()->getCards()) { for (auto card : cardItems()) {
if (filterString.check(card->getCard().getCardPtr())) { if (filterString.check(card->getCard().getCardPtr())) {
card->show(); card->show();
cardsToDisplay.append(card); cardsToDisplay.append(card->getState());
} else { } else {
card->hide(); card->hide();
} }
@ -218,7 +217,8 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca
const auto extractor = CardList::getExtractorFor(pileOption); const auto extractor = CardList::getExtractorFor(pileOption);
for (int i = 0; i < cardCount; i++) { for (int i = 0; i < cardCount; i++) {
CardItem *c = cards.at(i); CardState *c = cards.at(i);
CardItem *cItem = qobject_cast<CardItem *>(c->parent());
QString columnProp = extractor(c); QString columnProp = extractor(c);
if (i) { // if not the first card if (i) { // if not the first card
@ -233,8 +233,8 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca
lastColumnProp = columnProp; lastColumnProp = columnProp;
qreal x = col * CardDimensions::WIDTH_F; qreal x = col * CardDimensions::WIDTH_F;
qreal y = row * CardDimensions::HEIGHT_F / 3; qreal y = row * CardDimensions::HEIGHT_F / 3;
c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); cItem->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y);
c->setRealZValue(i); cItem->setRealZValue(i);
longestRow = qMax(row, longestRow); longestRow = qMax(row, longestRow);
} }
@ -259,11 +259,11 @@ ZoneViewZone::GridSize ZoneViewZone::positionCardsForDisplay(CardList &cards, Ca
qCDebug(ViewZoneLog) << "reorganizeCards: rows=" << rows << "cols=" << cols; qCDebug(ViewZoneLog) << "reorganizeCards: rows=" << rows << "cols=" << cols;
for (int i = 0; i < cardCount; i++) { for (int i = 0; i < cardCount; i++) {
CardItem *c = cards.at(i); auto *cItem = qobject_cast<CardItem *>(cards.at(i)->parent());
qreal x = (i / rows) * CardDimensions::WIDTH_F; qreal x = (i / rows) * CardDimensions::WIDTH_F;
qreal y = (i % rows) * CardDimensions::HEIGHT_F / 3; qreal y = (i % rows) * CardDimensions::HEIGHT_F / 3;
c->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y); cItem->setPos(HORIZONTAL_PADDING + x, VERTICAL_PADDING + y);
c->setRealZValue(i); cItem->setRealZValue(i);
} }
return GridSize{rows, qMax(cols, 1)}; return GridSize{rows, qMax(cols, 1)};
@ -301,24 +301,13 @@ void ZoneViewZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
CardZoneLogic *startZone, CardZoneLogic *startZone,
const QPoint & /*dropPoint*/) const QPoint & /*dropPoint*/)
{ {
Command_MoveCard cmd; QList<CardMoveRequest> cards;
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId()); cards.reserve(dragItems.size());
cmd.set_start_zone(startZone->getName().toStdString());
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
cmd.set_target_zone(getLogic()->getName().toStdString());
cmd.set_x(0);
cmd.set_y(0);
cmd.set_is_reversed(qobject_cast<ZoneViewZoneLogic *>(getLogic())->getIsReversed());
for (int i = 0; i < dragItems.size(); ++i) { for (int i = 0; i < dragItems.size(); ++i) {
auto cardToMove = cmd.mutable_cards_to_move()->add_card(); cards.append(CardMoveRequest{dragItems[i]->getId(), dragItems[i]->isForceFaceDown()});
cardToMove->set_card_id(dragItems[i]->getId());
if (dragItems[i]->isForceFaceDown()) {
cardToMove->set_face_down(true);
}
} }
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd); getLogic()->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), 0, 0, cards);
} }
void ZoneViewZone::setGeometry(const QRectF &rect) void ZoneViewZone::setGeometry(const QRectF &rect)

View file

@ -7,7 +7,6 @@
#ifndef ZONEVIEWERZONE_H #ifndef ZONEVIEWERZONE_H
#define ZONEVIEWERZONE_H #define ZONEVIEWERZONE_H
#include "../../game/board/card_list.h"
#include "select_zone.h" #include "select_zone.h"
#include <QGraphicsLayoutItem> #include <QGraphicsLayoutItem>
@ -17,6 +16,7 @@
#include <QSizeF> #include <QSizeF>
#include <QString> #include <QString>
#include <libcockatrice/filters/filter_string.h> #include <libcockatrice/filters/filter_string.h>
#include <libcockatrice/game/board/card_list.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>

View file

@ -1,13 +1,7 @@
#include "view_zone_widget.h" #include "view_zone_widget.h"
#include "../../client/settings/cache_settings.h" #include "../../client/settings/cache_settings.h"
#include "../../client/translation.h"
#include "../../filters/syntax_help.h" #include "../../filters/syntax_help.h"
#include "../../game/board/card_list.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/view_zone_logic.h"
#include "../../interface/pixel_map_generator.h" #include "../../interface/pixel_map_generator.h"
#include "../card_dimensions.h" #include "../card_dimensions.h"
#include "../z_values.h" #include "../z_values.h"
@ -45,7 +39,12 @@
#include <QVariant> #include <QVariant>
#include <QWidget> #include <QWidget>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/protocol/pb/command_shuffle.pb.h> #include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/translation.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/view_zone_logic.h>
#include <libcockatrice/settings/cards_display_settings.h> #include <libcockatrice/settings/cards_display_settings.h>
#include <libcockatrice/settings/interface_settings.h> #include <libcockatrice/settings/interface_settings.h>
#include <qnamespace.h> #include <qnamespace.h>
@ -178,9 +177,10 @@ ZoneViewWidget::ZoneViewWidget(PlayerLogic *_player,
vbox->addItem(zoneHBox); vbox->addItem(zoneHBox);
zone = new ZoneViewZone(new ZoneViewZoneLogic(player, _origZone, numberCards, _revealZone, _writeableRevealZone, zone =
_isReversed, zoneContainer), new ZoneViewZone(new ZoneViewZoneLogic(player, _origZone, numberCards, _revealZone, _writeableRevealZone,
zoneContainer); _isReversed, &SettingsCache::instance().userInterface(), zoneContainer),
zoneContainer);
connect(zone, &ZoneViewZone::wheelEventReceived, scrollBarProxy, &ScrollableGraphicsProxyWidget::recieveWheelEvent); connect(zone, &ZoneViewZone::wheelEventReceived, scrollBarProxy, &ScrollableGraphicsProxyWidget::recieveWheelEvent);
retranslateUi(); retranslateUi();
@ -570,7 +570,7 @@ void ZoneViewWidget::closeEvent(QCloseEvent *event)
// manually call zone->close in order to remove it from the origZones views // manually call zone->close in order to remove it from the origZones views
zone->close(); zone->close();
if (shuffleCheckBox.isChecked()) { if (shuffleCheckBox.isChecked()) {
player->getPlayerActions()->sendGameCommand(Command_Shuffle()); player->getPlayerActions()->actShuffle();
} }
zoneDeleted(); zoneDeleted();
event->accept(); event->accept();

View file

@ -0,0 +1,81 @@
#include "installer_close_server.h"
#include <QCoreApplication>
#include <QLocalServer>
#include <QLocalSocket>
#include <QTimer>
#include <libcockatrice/utility/installer_close_protocol.h>
#include <libcockatrice/utility/local_server_name.h>
namespace
{
/// The one line the probe writes, from the header both ends include. A QByteArray view of
/// the shared literal, so the client cannot drift from the probe's copy.
const QByteArray CLOSE_COMMAND = QByteArray::fromRawData(INSTALLER_CLOSE_COMMAND, sizeof(INSTALLER_CLOSE_COMMAND) - 1);
/// How long a connection that never sends the command may stay open. The probe writes and
/// closes at once, so this only reaps a stray client; it must not run while a close request
/// is being handled, which is why it is a timer on the socket and not a disconnected slot.
constexpr int STRAY_CONNECTION_MILLISECONDS = 5000;
} // namespace
InstallerCloseServer::InstallerCloseServer(QObject *parent) : QObject(parent)
{
}
QString InstallerCloseServer::socketNameForUser(qint64 processId, const QString &userName)
{
return QStringLiteral("CockatriceInstallerClose-%1-%2").arg(processId).arg(userName);
}
QString InstallerCloseServer::socketName(qint64 processId)
{
// The same scoping helper the single-instance socket uses, so the probe only has to
// reproduce one rule (USER, then USERNAME, then the home directory name).
return scopedLocalServerName(QStringLiteral("CockatriceInstallerClose-%1").arg(processId));
}
bool InstallerCloseServer::listen()
{
if (server != nullptr) {
return server->isListening();
}
name = socketName(QCoreApplication::applicationPid());
server = new QLocalServer(this);
// A crashed instance can leave a Unix socket file behind; on Windows this is a no-op.
QLocalServer::removeServer(name);
if (!server->listen(name)) {
delete server;
server = nullptr;
return false;
}
connect(server, &QLocalServer::newConnection, this, [this]() {
while (QLocalSocket *socket = server->nextPendingConnection()) {
// No disconnected -> deleteLater() slot: the close request can spin a nested
// event loop, the probe disconnects while it runs, and a deferred delete would
// then free the socket from inside its own readyRead emission. The reaper covers
// a client that never sends the line; the handler below deletes after the emit.
auto *reaper = new QTimer(socket);
reaper->setSingleShot(true);
connect(reaper, &QTimer::timeout, socket, &QObject::deleteLater);
reaper->start(STRAY_CONNECTION_MILLISECONDS);
connect(socket, &QLocalSocket::readyRead, this, [this, socket, reaper]() {
if (!socket->canReadLine()) {
return;
}
if (socket->readLine() != CLOSE_COMMAND) {
return;
}
// Stop the reaper before the emit: the receiver may close the window and run
// a nested event loop for as long as the user takes, and the reaper must not
// free the socket from inside that loop while this handler still holds it.
reaper->stop();
emit closeRequested();
socket->disconnectFromServer();
socket->deleteLater();
});
}
});
return true;
}

View file

@ -0,0 +1,50 @@
#ifndef COCKATRICE_INSTALLER_CLOSE_SERVER_H
#define COCKATRICE_INSTALLER_CLOSE_SERVER_H
#include <QObject>
#include <QString>
class QLocalServer;
/**
* @brief Listens for the installer's request to close this instance gracefully.
*
* The Windows installer cannot reach an instance whose window is hidden in the notification
* area with WM_CLOSE, and it must not kill a process that may hold an unsaved deck. It
* connects to this local socket instead and writes one line; the instance then closes the
* same way its own close button does - saving settings and answering its own confirmation,
* never bypassing it.
*
* The socket name carries the process id, so every instance has its own and an installer
* that found several can ask each one. It is built by installerCloseSocketName() in
* tools/installer-holder-probe/installer_close_name.h; the two must stay identical.
*/
class InstallerCloseServer : public QObject
{
Q_OBJECT
public:
explicit InstallerCloseServer(QObject *parent = nullptr);
/**
* @brief Starts listening. A failure is not fatal: the installer falls back to
* WM_CLOSE, which is what older clients answer.
*/
bool listen();
/// The socket name this process listens on. The installer's probe builds the same
/// string from the process id and the account name.
static QString socketName(qint64 processId);
/// The name for an explicit account, exposed so the probe's name helper and this one
/// can be tested against each other.
static QString socketNameForUser(qint64 processId, const QString &userName);
signals:
void closeRequested();
private:
QLocalServer *server = nullptr;
QString name;
};
#endif // COCKATRICE_INSTALLER_CLOSE_SERVER_H

View file

@ -7,13 +7,14 @@
#ifndef DECK_LOADER_H #ifndef DECK_LOADER_H
#define DECK_LOADER_H #define DECK_LOADER_H
#include "deck_file_format.h"
#include "loaded_deck.h"
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QObject> #include <QObject>
#include <QPrinter>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QTextCursor>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <optional> #include <optional>
#include <qtmetamacros.h> #include <qtmetamacros.h>

View file

@ -1,14 +1,14 @@
#include "intent_open_local_deck.h" #include "intent_open_local_deck.h"
#include "../deck_loader/deck_file_format.h"
#include "../deck_loader/deck_loader.h" #include "../deck_loader/deck_loader.h"
#include "../deck_loader/loaded_deck.h"
#include "../widgets/tabs/tab_supervisor.h" #include "../widgets/tabs/tab_supervisor.h"
#include "intent_wait_for_database_load.h" #include "intent_wait_for_database_load.h"
#include "libcockatrice/card/database/card_database.h" #include "libcockatrice/card/database/card_database.h"
#include "libcockatrice/card/database/card_database_loader.h" #include "libcockatrice/card/database/card_database_loader.h"
#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 <optional> #include <optional>
IntentOpenLocalDeck::IntentOpenLocalDeck(TabSupervisor *_tabSupervisor, const QString &_file) IntentOpenLocalDeck::IntentOpenLocalDeck(TabSupervisor *_tabSupervisor, const QString &_file)

View file

@ -1,7 +1,6 @@
#ifndef COCKATRICE_INTENT_OPEN_SHARED_DECK_H #ifndef COCKATRICE_INTENT_OPEN_SHARED_DECK_H
#define COCKATRICE_INTENT_OPEN_SHARED_DECK_H #define COCKATRICE_INTENT_OPEN_SHARED_DECK_H
#include "../deck_loader/loaded_deck.h"
#include "contexts/context_open_deck.h" #include "contexts/context_open_deck.h"
#include "intent.h" #include "intent.h"
@ -9,6 +8,7 @@
#include <QMap> #include <QMap>
#include <QScopedPointer> #include <QScopedPointer>
#include <QString> #include <QString>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <memory> #include <memory>
#include <qtmetamacros.h> #include <qtmetamacros.h>

View file

@ -1,7 +1,6 @@
#include "deck_state_manager.h" #include "deck_state_manager.h"
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../deck_loader/loaded_deck.h"
#include "deck_list_model.h" #include "deck_list_model.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"
@ -18,6 +17,7 @@
#include <QVariant> #include <QVariant>
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list_history_manager.h> #include <libcockatrice/deck_list/deck_list_history_manager.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h> #include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
#include <libcockatrice/settings/cards_display_settings.h> #include <libcockatrice/settings/cards_display_settings.h>
#include <qnamespace.h> #include <qnamespace.h>

View file

@ -1,7 +1,7 @@
#ifndef COCKATRICE_DECK_STATE_MANAGER_H #ifndef COCKATRICE_DECK_STATE_MANAGER_H
#define COCKATRICE_DECK_STATE_MANAGER_H #define COCKATRICE_DECK_STATE_MANAGER_H
#include "../../deck_loader/loaded_deck.h" #include "deck_list_model.h"
#include <QModelIndex> #include <QModelIndex>
#include <QObject> #include <QObject>
@ -10,6 +10,7 @@
#include <QStringList> #include <QStringList>
#include <functional> #include <functional>
#include <libcockatrice/deck_list/deck_list.h> #include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class DeckListHistoryManager; class DeckListHistoryManager;

View file

@ -1,7 +1,6 @@
#include "dlg_convert_deck_to_cod_format.h" #include "dlg_convert_deck_to_cod_format.h"
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../deck_loader/deck_file_format.h"
#include <QCheckBox> #include <QCheckBox>
#include <QDialogButtonBox> #include <QDialogButtonBox>
@ -12,6 +11,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QObject> #include <QObject>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <libcockatrice/deck_list/deck_file_format.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/qt_utils.h>

View file

@ -10,8 +10,10 @@
#include "libcockatrice/deck_list/deck_list.h" #include "libcockatrice/deck_list/deck_list.h"
#include <QCheckBox>
#include <QDialog> #include <QDialog>
#include <QString> #include <QString>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class QPlainTextEdit; class QPlainTextEdit;

View file

@ -2,9 +2,7 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../../interface/widgets/tabs/tab_supervisor.h" #include "../../../interface/widgets/tabs/tab_supervisor.h"
#include "../../deck_loader/deck_file_format.h"
#include "../../deck_loader/deck_loader.h" #include "../../deck_loader/deck_loader.h"
#include "../../deck_loader/loaded_deck.h"
#include "../../pixel_map_generator.h" #include "../../pixel_map_generator.h"
#include "../../theme_config.h" #include "../../theme_config.h"
#include "../../theme_manager.h" #include "../../theme_manager.h"
@ -57,6 +55,8 @@
#include <QtGlobal> #include <QtGlobal>
#include <algorithm> #include <algorithm>
#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/network/client/remote/remote_client.h> #include <libcockatrice/network/client/remote/remote_client.h>
#include <libcockatrice/settings/appearance_settings.h> #include <libcockatrice/settings/appearance_settings.h>
#include <libcockatrice/settings/paths_settings.h> #include <libcockatrice/settings/paths_settings.h>

View file

@ -5,6 +5,7 @@
#include "../../settings_page/general_settings_page.h" #include "../../settings_page/general_settings_page.h"
#include "../first_run_wizard_page.h" #include "../first_run_wizard_page.h"
#include "libcockatrice/settings/personal_settings.h" #include "libcockatrice/settings/personal_settings.h"
#include "libcockatrice/utility/translation_loader.h"
#include <QApplication> #include <QApplication>
#include <QChar> #include <QChar>
@ -30,7 +31,7 @@ WelcomePage::WelcomePage(QWidget *parent) : FirstRunWizardPage(parent)
languageLabel = new QLabel(this); languageLabel = new QLabel(this);
langCombo = new QComboBox(this); langCombo = new QComboBox(this);
for (const QString &code : GeneralSettingsPage::findQmFiles()) { for (const QString &code : GeneralSettingsPage::findQmFiles()) {
langCombo->addItem(GeneralSettingsPage::languageName(code), code); langCombo->addItem(TranslationLoader::languageDisplayName(code), code);
} }
QString current = SettingsCache::instance().personal().getLang(); QString current = SettingsCache::instance().personal().getLang();

View file

@ -1,13 +1,13 @@
#include "replay_manager.h" #include "replay_manager.h"
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../../game/player/event_processing_options.h"
#include "libcockatrice/protocol/pb/game_event.pb.h" #include "libcockatrice/protocol/pb/game_event.pb.h"
#include "libcockatrice/protocol/pb/game_event_container.pb.h" #include "libcockatrice/protocol/pb/game_event_container.pb.h"
#include "libcockatrice/protocol/pb/game_replay.pb.h" #include "libcockatrice/protocol/pb/game_replay.pb.h"
#include <QTimer> #include <QTimer>
#include <algorithm> #include <algorithm>
#include <libcockatrice/game/player/event_processing_options.h>
#include <libcockatrice/protocol/get_pb_extension.h> #include <libcockatrice/protocol/get_pb_extension.h>
#include <libcockatrice/settings/interface_settings.h> #include <libcockatrice/settings/interface_settings.h>
#include <qnumeric.h> #include <qnumeric.h>

View file

@ -1,11 +1,10 @@
#ifndef COCKATRICE_REPLAY_MANAGER_H #ifndef COCKATRICE_REPLAY_MANAGER_H
#define COCKATRICE_REPLAY_MANAGER_H #define COCKATRICE_REPLAY_MANAGER_H
#include "../../../game/player/event_processing_options.h"
#include <QList> #include <QList>
#include <QObject> #include <QObject>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/game/player/event_processing_options.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class GameReplay; class GameReplay;

View file

@ -8,9 +8,8 @@
#ifndef REPLAY_WIDGET_H #ifndef REPLAY_WIDGET_H
#define REPLAY_WIDGET_H #define REPLAY_WIDGET_H
#include "../../../game/player/event_processing_options.h"
#include <QWidget> #include <QWidget>
#include <libcockatrice/game/player/event_processing_options.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class ReplayManager; class ReplayManager;

View file

@ -1,8 +1,5 @@
#include "user_context_menu.h" #include "user_context_menu.h"
#include "../../../../game/abstract_game.h"
#include "../../../../game/game_event_handler.h"
#include "../../../../game/game_meta_info.h"
#include "../../dialogs/dlg_report_user.h" #include "../../dialogs/dlg_report_user.h"
#include "../../interface/widgets/tabs/tab_supervisor.h" #include "../../interface/widgets/tabs/tab_supervisor.h"
#include "../chat_view/chat_view.h" #include "../chat_view/chat_view.h"
@ -37,6 +34,9 @@
#include <QTableWidget> #include <QTableWidget>
#include <QTableWidgetItem> #include <QTableWidgetItem>
#include <QWidget> #include <QWidget>
#include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/game/game_event_handler.h>
#include <libcockatrice/game/game_meta_info.h>
#include <libcockatrice/network/client/abstract/abstract_client.h> #include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/command_kick_from_game.pb.h> #include <libcockatrice/protocol/pb/command_kick_from_game.pb.h>
#include <libcockatrice/protocol/pb/commands.pb.h> #include <libcockatrice/protocol/pb/commands.pb.h>

View file

@ -8,7 +8,6 @@
#include "update/client/release_channel.h" #include "update/client/release_channel.h"
#include <QByteArray> #include <QByteArray>
#include <QCoreApplication>
#include <QDate> #include <QDate>
#include <QDebug> #include <QDebug>
#include <QFile> #include <QFile>
@ -22,7 +21,6 @@
#include <QPushButton> #include <QPushButton>
#include <QSharedPointer> #include <QSharedPointer>
#include <QSizePolicy> #include <QSizePolicy>
#include <QTranslator>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QVariant> #include <QVariant>
#include <QVariantList> #include <QVariantList>
@ -51,18 +49,24 @@ enum startupCardUpdateCheckBehaviorIndex
GeneralSettingsPage::GeneralSettingsPage() GeneralSettingsPage::GeneralSettingsPage()
{ {
// language settings // language settings
QStringList languageCodes = findQmFiles(); for (const QString &code : findQmFiles()) {
for (const QString &code : languageCodes) { languageBox.addItem(TranslationLoader::languageDisplayName(code), code);
QString langName = languageName(code);
languageBox.addItem(langName, code);
} }
QString setLanguage = QCoreApplication::translate("i18n", DEFAULT_LANG_NAME); // Select the language the client runs with: its own entry, the base language for a variant without a
int index = languageBox.findText(setLanguage, Qt::MatchExactly); // file of its own (de_CH -> de), and the default language for an unset setting, which names no file.
if (index == -1) { const QString configuredLanguage = SettingsCache::instance().personal().getLang();
qWarning() << "could not find language" << setLanguage; int index = languageBox.findData(configuredLanguage);
} else { if (index < 0) {
index = languageBox.findData(configuredLanguage.section('_', 0, 0));
}
if (index < 0) {
index = languageBox.findData(TranslationLoader::defaultLanguageCode());
}
if (index >= 0) {
languageBox.setCurrentIndex(index); languageBox.setCurrentIndex(index);
} else {
qCWarning(GeneralSettingsPageLog) << "could not find language" << configuredLanguage;
} }
advertiseTranslationPageLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); advertiseTranslationPageLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse);
@ -96,12 +100,12 @@ GeneralSettingsPage::GeneralSettingsPage()
auto *languageGrid = new QGridLayout; auto *languageGrid = new QGridLayout;
languageGrid->addWidget(&languageLabel, 0, 0); languageGrid->addWidget(&languageLabel, 0, 0);
languageGrid->addWidget(&languageBox, 0, 1); languageGrid->addWidget(&languageBox, 0, 1);
languageGrid->addWidget(&cardLanguageLabel, 1, 0); languageGrid->addWidget(&advertiseTranslationPageLabel, 1, 1, Qt::AlignRight);
languageGrid->addWidget(&cardLanguageBox, 1, 1); languageGrid->addWidget(&cardLanguageLabel, 2, 0);
languageGrid->addWidget(&cardLanguageNoteLabel, 2, 1); languageGrid->addWidget(&cardLanguageBox, 2, 1);
languageGrid->addWidget(&cardSearchLanguageLabel, 3, 0); languageGrid->addWidget(&cardLanguageNoteLabel, 3, 1);
languageGrid->addWidget(&cardSearchLanguageBox, 3, 1); languageGrid->addWidget(&cardSearchLanguageLabel, 4, 0);
languageGrid->addWidget(&advertiseTranslationPageLabel, 4, 1, Qt::AlignRight); languageGrid->addWidget(&cardSearchLanguageBox, 4, 1);
cardLanguageNoteLabel.setWordWrap(true); cardLanguageNoteLabel.setWordWrap(true);
cardLanguageNoteLabel.setAlignment(Qt::AlignLeft | Qt::AlignVCenter); cardLanguageNoteLabel.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
@ -384,18 +388,6 @@ QStringList GeneralSettingsPage::findQmFiles()
TranslationLoader::applicationTranslationPaths(translationPath)); TranslationLoader::applicationTranslationPaths(translationPath));
} }
QString GeneralSettingsPage::languageName(const QString &lang)
{
QTranslator qTranslator;
// Only the language name is needed here, so the file is loaded without logging a line per
// language: findQmFiles() asked for languages whose files exist in the first place.
TranslationLoader::loadFrom(qTranslator, translationPrefix + "_" + lang,
TranslationLoader::applicationTranslationPaths(translationPath));
return qTranslator.translate("i18n", DEFAULT_LANG_NAME);
}
void GeneralSettingsPage::deckPathButtonClicked() void GeneralSettingsPage::deckPathButtonClicked()
{ {
QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), deckPathEdit->text()); QString path = QFileDialog::getExistingDirectory(this, tr("Choose path"), deckPathEdit->text());
@ -576,7 +568,7 @@ void GeneralSettingsPage::retranslateUi()
const auto &settings = SettingsCache::instance(); const auto &settings = SettingsCache::instance();
languageGroupBox->setTitle(tr("Language settings")); languageGroupBox->setTitle(tr("Language settings"));
languageLabel.setText(tr("Language:")); languageLabel.setText(tr("Interface language:"));
cardLanguageBox.setItemText(0, tr("English")); cardLanguageBox.setItemText(0, tr("English"));
cardLanguageLabel.setText(tr("Card text & images language:")); cardLanguageLabel.setText(tr("Card text & images language:"));
cardLanguageNoteLabel.setText( cardLanguageNoteLabel.setText(

View file

@ -26,7 +26,6 @@ public:
void retranslateUi() override; void retranslateUi() override;
static QStringList findQmFiles(); static QStringList findQmFiles();
static QString languageName(const QString &lang);
signals: signals:
/// Request to re-import the card database with the newly selected card language /// Request to re-import the card database with the newly selected card language

View file

@ -11,9 +11,7 @@
#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 "../../deck_loader/deck_file_format.h"
#include "../../deck_loader/deck_loader.h" #include "../../deck_loader/deck_loader.h"
#include "../../deck_loader/loaded_deck.h"
#include "../cards/additional_info/deck_color_identity.h" #include "../cards/additional_info/deck_color_identity.h"
#include "../cards/card_info_frame_widget.h" #include "../cards/card_info_frame_widget.h"
#include "../client/network/interfaces/deck_stats_interface.h" #include "../client/network/interfaces/deck_stats_interface.h"
@ -55,6 +53,8 @@
#include <QUrl> #include <QUrl>
#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/deck_list/deck_file_format.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/models/database/card_database_model.h> #include <libcockatrice/models/database/card_database_model.h>
#include <libcockatrice/network/client/abstract/abstract_client.h> #include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/command_deck_upload.pb.h> #include <libcockatrice/protocol/pb/command_deck_upload.pb.h>

View file

@ -2,7 +2,6 @@
#include "../../../../../../client/settings/cache_settings.h" #include "../../../../../../client/settings/cache_settings.h"
#include "../../../../../deck_loader/card_node_function.h" #include "../../../../../deck_loader/card_node_function.h"
#include "../../../../../deck_loader/loaded_deck.h"
#include "../../../../cards/deck_card_zone_display_widget.h" #include "../../../../cards/deck_card_zone_display_widget.h"
#include "../../../../visual_deck_editor/visual_deck_display_options_widget.h" #include "../../../../visual_deck_editor/visual_deck_display_options_widget.h"
#include "../../../../visual_deck_editor/visual_deck_editor_widget.h" #include "../../../../visual_deck_editor/visual_deck_editor_widget.h"
@ -30,6 +29,7 @@
#include <QVariant> #include <QVariant>
#include <functional> #include <functional>
#include <libcockatrice/card/import/card_name_normalizer.h> #include <libcockatrice/card/import/card_name_normalizer.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/settings/cards_display_settings.h> #include <libcockatrice/settings/cards_display_settings.h>
#include <libcockatrice/utility/qt_utils.h> #include <libcockatrice/utility/qt_utils.h>
#include <qlogging.h> #include <qlogging.h>

View file

@ -1,7 +1,6 @@
#include "tab_edhrec_main.h" #include "tab_edhrec_main.h"
#include "../../../../../client/settings/cache_settings.h" #include "../../../../../client/settings/cache_settings.h"
#include "../../../../deck_loader/loaded_deck.h"
#include "../../../cards/card_size_widget.h" #include "../../../cards/card_size_widget.h"
#include "../../../quick_settings/settings_button_widget.h" #include "../../../quick_settings/settings_button_widget.h"
#include "../../../utility/completer_utils.h" #include "../../../utility/completer_utils.h"
@ -28,6 +27,7 @@
#include <QRegularExpression> #include <QRegularExpression>
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/database/card_database_querier.h> #include <libcockatrice/card/database/card_database_querier.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/models/database/card/card_completer_proxy_model.h> #include <libcockatrice/models/database/card/card_completer_proxy_model.h>
#include <libcockatrice/models/database/card/card_search_model.h> #include <libcockatrice/models/database/card/card_search_model.h>
#include <libcockatrice/models/database/card_database_display_model.h> #include <libcockatrice/models/database/card_database_display_model.h>

View file

@ -1,9 +1,7 @@
#include "tab_deck_storage.h" #include "tab_deck_storage.h"
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../deck_loader/deck_file_format.h"
#include "../../deck_loader/deck_loader.h" #include "../../deck_loader/deck_loader.h"
#include "../../deck_loader/loaded_deck.h"
#include "../../pixel_map_generator.h" #include "../../pixel_map_generator.h"
#include "../cards/additional_info/deck_color_identity.h" #include "../cards/additional_info/deck_color_identity.h"
#include "../deck_share/deck_share_utils.h" #include "../deck_share/deck_share_utils.h"
@ -49,7 +47,9 @@
#include <QtGlobal> #include <QtGlobal>
#include <algorithm> #include <algorithm>
#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/deck_list.h> #include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/protocol/pb/command_deck_del.pb.h> #include <libcockatrice/protocol/pb/command_deck_del.pb.h>
#include <libcockatrice/protocol/pb/command_deck_del_dir.pb.h> #include <libcockatrice/protocol/pb/command_deck_del_dir.pb.h>
#include <libcockatrice/protocol/pb/command_deck_download.pb.h> #include <libcockatrice/protocol/pb/command_deck_download.pb.h>

View file

@ -2,16 +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 "../../../database/interface/settings_card_preference_provider.h"
#include "../../../game/game_event_handler.h"
#include "../../../game/game_meta_info.h"
#include "../../../game/game_state.h"
#include "../../../game/player/player_info.h"
#include "../../../game/player/player_manager.h"
#include "../../../game_graphics/board/abstract_card_item.h" #include "../../../game_graphics/board/abstract_card_item.h"
#include "../game/game.h" #include "../game_graphics/board/arrow_item.h"
#include "../game/player/player_logic.h" #include "../game_graphics/board/card_item.h"
#include "../game/replay.h"
#include "../game_graphics/deckview/deck_view_container.h" #include "../game_graphics/deckview/deck_view_container.h"
#include "../game_graphics/deckview/tabbed_deck_view_container.h" #include "../game_graphics/deckview/tabbed_deck_view_container.h"
#include "../game_graphics/game_scene.h" #include "../game_graphics/game_scene.h"
@ -71,6 +65,15 @@
#include <QWidget> #include <QWidget>
#include <QtGlobal> #include <QtGlobal>
#include <libcockatrice/card/database/card_database_manager.h> #include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/game/game.h>
#include <libcockatrice/game/game_event_handler.h>
#include <libcockatrice/game/game_meta_info.h>
#include <libcockatrice/game/game_state.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/replay.h>
#include <libcockatrice/network/client/abstract/abstract_client.h> #include <libcockatrice/network/client/abstract/abstract_client.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>
@ -97,7 +100,8 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay)
: Tab(_tabSupervisor), sayLabel(nullptr), sayEdit(nullptr) : Tab(_tabSupervisor), sayLabel(nullptr), sayEdit(nullptr)
{ {
// THIS CTOR IS USED ON REPLAY // THIS CTOR IS USED ON REPLAY
game = new Replay(this, _replay, tabSupervisor->getIsLocalGame()); game = new Replay(this, _replay, tabSupervisor->getIsLocalGame(), &SettingsCache::instance().userInterface(),
new SettingsCardPreferenceProvider(this));
createCardInfoDock(true); createCardInfoDock(true);
createPlayerListDock(true); createPlayerListDock(true);
@ -141,7 +145,8 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor,
: Tab(_tabSupervisor), userListProxy(_tabSupervisor->getUserListManager()) : Tab(_tabSupervisor), userListProxy(_tabSupervisor->getUserListManager())
{ {
// THIS CTOR IS USED ON GAMES // THIS CTOR IS USED ON GAMES
game = new Game(this, tabSupervisor->getIsLocalGame(), _clients, event, _roomGameTypes); game = new Game(this, tabSupervisor->getIsLocalGame(), _clients, event, _roomGameTypes,
&SettingsCache::instance().userInterface(), new SettingsCardPreferenceProvider(this));
createCardInfoDock(); createCardInfoDock();
createPlayerListDock(); createPlayerListDock();
@ -1276,8 +1281,14 @@ void TabGame::createPlayAreaWidget(bool bReplay)
{ {
phasesToolbar = new PhasesToolbar; phasesToolbar = new PhasesToolbar;
if (!bReplay) { if (!bReplay) {
connect(phasesToolbar, &PhasesToolbar::sendGameCommand, game->getGameEventHandler(), connect(phasesToolbar, &PhasesToolbar::setActivePhaseRequested, game->getGameEventHandler(),
qOverload<const ::google::protobuf::Message &, int>(&GameEventHandler::sendGameCommand)); &GameEventHandler::handleActivePhaseChanged);
connect(phasesToolbar, &PhasesToolbar::nextTurnRequested, game->getGameEventHandler(),
&GameEventHandler::handleNextTurn);
connect(phasesToolbar, &PhasesToolbar::untapAllRequested, game->getGameEventHandler(),
&GameEventHandler::handleUntapAll);
connect(phasesToolbar, &PhasesToolbar::drawCardRequested, game->getGameEventHandler(),
&GameEventHandler::handleDrawCard);
} }
scene = new GameScene(phasesToolbar, this); scene = new GameScene(phasesToolbar, this);
connect(game->getPlayerManager(), &PlayerManager::playerConceded, scene, &GameScene::rearrange); connect(game->getPlayerManager(), &PlayerManager::playerConceded, scene, &GameScene::rearrange);

View file

@ -2,7 +2,6 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../deck_loader/deck_loader.h" #include "../../deck_loader/deck_loader.h"
#include "../../deck_loader/loaded_deck.h"
#include "../cards/card_size_widget.h" #include "../cards/card_size_widget.h"
#include "../general/layout_containers/flow_widget.h" #include "../general/layout_containers/flow_widget.h"
#include "../visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.h" #include "../visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.h"
@ -28,6 +27,7 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QWidget> #include <QWidget>
#include <functional> #include <functional>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/network/client/abstract/abstract_client.h> #include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/command_deck_download_public.pb.h> #include <libcockatrice/protocol/pb/command_deck_download_public.pb.h>
#include <libcockatrice/protocol/pb/response.pb.h> #include <libcockatrice/protocol/pb/response.pb.h>

View file

@ -2,10 +2,6 @@
#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_meta_info.h"
#include "../../deck_loader/loaded_deck.h"
#include "../../intents/contexts/context_connect_to_server.h" #include "../../intents/contexts/context_connect_to_server.h"
#include "../../intents/contexts/context_join_game.h" #include "../../intents/contexts/context_join_game.h"
#include "../../intents/contexts/context_join_room.h" #include "../../intents/contexts/context_join_room.h"
@ -63,6 +59,10 @@
#include <QStyleOption> #include <QStyleOption>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include <QTabBar> #include <QTabBar>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/game/game_event_handler.h>
#include <libcockatrice/game/game_meta_info.h>
#include <libcockatrice/network/client/abstract/abstract_client.h> #include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/network/client/remote/remote_client.h> #include <libcockatrice/network/client/remote/remote_client.h>
#include <libcockatrice/protocol/pb/event_game_joined.pb.h> #include <libcockatrice/protocol/pb/event_game_joined.pb.h>

View file

@ -2,7 +2,6 @@
#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 "../../../deck_loader/loaded_deck.h"
#include "../../deck_editor/deck_editor_card_database_dock_widget.h" #include "../../deck_editor/deck_editor_card_database_dock_widget.h"
#include "../../deck_editor/deck_editor_card_info_dock_widget.h" #include "../../deck_editor/deck_editor_card_info_dock_widget.h"
#include "../../deck_editor/deck_editor_deck_dock_widget.h" #include "../../deck_editor/deck_editor_deck_dock_widget.h"
@ -44,6 +43,7 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QWidget> #include <QWidget>
#include <functional> #include <functional>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/models/deck_list/deck_list_model.h> #include <libcockatrice/models/deck_list/deck_list_model.h>
#include <libcockatrice/settings/deck_editor_settings.h> #include <libcockatrice/settings/deck_editor_settings.h>
#include <libcockatrice/settings/layouts_settings.h> #include <libcockatrice/settings/layouts_settings.h>
@ -61,8 +61,8 @@ TabDeckEditorVisual::TabDeckEditorVisual(TabSupervisor *_tabSupervisor) : Abstra
{ {
setObjectName("TabDeckEditorVisual"); setObjectName("TabDeckEditorVisual");
createCentralFrame();
createMenus(); createMenus();
createCentralFrame();
installEventFilter(this); installEventFilter(this);
@ -105,7 +105,6 @@ void TabDeckEditorVisual::createCentralFrame()
centralFrame->addWidget(tabContainer); centralFrame->addWidget(tabContainer);
setCentralWidget(centralWidget); setCentralWidget(centralWidget);
setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
} }
/** @brief Updates the visual deck, analytics, and sample hand after a deck change. */ /** @brief Updates the visual deck, analytics, and sample hand after a deck change. */

View file

@ -1,9 +1,7 @@
#include "tab_deck_storage_visual.h" #include "tab_deck_storage_visual.h"
#include "../../../../client/settings/cache_settings.h" #include "../../../../client/settings/cache_settings.h"
#include "../../../deck_loader/deck_file_format.h"
#include "../../../deck_loader/deck_loader.h" #include "../../../deck_loader/deck_loader.h"
#include "../../../deck_loader/loaded_deck.h"
#include "../../cards/additional_info/deck_color_identity.h" #include "../../cards/additional_info/deck_color_identity.h"
#include "../../deck_share/deck_share_utils.h" #include "../../deck_share/deck_share_utils.h"
#include "../../deck_share/share_bar_widget.h" #include "../../deck_share/share_bar_widget.h"
@ -17,7 +15,9 @@
#include <QWidget> #include <QWidget>
#include <QtGlobal> #include <QtGlobal>
#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/deck_list.h> #include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/network/client/abstract/abstract_client.h> #include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/command_deck_share_create.pb.h> #include <libcockatrice/protocol/pb/command_deck_share_create.pb.h>
#include <libcockatrice/protocol/pb/response.pb.h> #include <libcockatrice/protocol/pb/response.pb.h>

View file

@ -1,9 +1,7 @@
#include "deck_preview_deck_tags_display_widget.h" #include "deck_preview_deck_tags_display_widget.h"
#include "../../../../client/settings/cache_settings.h" #include "../../../../client/settings/cache_settings.h"
#include "../../../deck_loader/deck_file_format.h"
#include "../../../deck_loader/deck_loader.h" #include "../../../deck_loader/deck_loader.h"
#include "../../../deck_loader/loaded_deck.h"
#include "../../general/layout_containers/flow_widget.h" #include "../../general/layout_containers/flow_widget.h"
#include "deck_preview_tag_addition_widget.h" #include "deck_preview_tag_addition_widget.h"
#include "deck_preview_tag_dialog.h" #include "deck_preview_tag_dialog.h"
@ -14,7 +12,10 @@
#include <QDir> #include <QDir>
#include <QDirIterator> #include <QDirIterator>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QPointer>
#include <QSizePolicy> #include <QSizePolicy>
#include <libcockatrice/deck_list/deck_file_format.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/settings/paths_settings.h> #include <libcockatrice/settings/paths_settings.h>
#include <libcockatrice/utility/qt_utils.h> #include <libcockatrice/utility/qt_utils.h>
#include <optional> #include <optional>
@ -117,8 +118,13 @@ void DeckPreviewDeckTagsDisplayWidget::openTagEditDlg()
void DeckPreviewDeckTagsDisplayWidget::execTagDialog(const QStringList &knownTags) void DeckPreviewDeckTagsDisplayWidget::execTagDialog(const QStringList &knownTags)
{ {
QPointer<DeckPreviewDeckTagsDisplayWidget> self(this);
DeckPreviewTagDialog dialog(knownTags, currentTags); DeckPreviewTagDialog dialog(knownTags, currentTags);
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
// Guard against the widget being destroyed during dialog.exec()'s nested event loop.
if (!self) {
return;
}
QStringList updatedTags = dialog.getActiveTags(); QStringList updatedTags = dialog.getActiveTags();
if (updatedTags != currentTags) { if (updatedTags != currentTags) {
setTags(updatedTags); setTags(updatedTags);

View file

@ -3,7 +3,6 @@
#include "../../../../client/settings/cache_settings.h" #include "../../../../client/settings/cache_settings.h"
#include "../../../../interface/widgets/dialogs/dlg_convert_deck_to_cod_format.h" #include "../../../../interface/widgets/dialogs/dlg_convert_deck_to_cod_format.h"
#include "../../../deck_loader/deck_loader.h" #include "../../../deck_loader/deck_loader.h"
#include "../../../deck_loader/loaded_deck.h"
#include "../../cards/additional_info/color_identity_widget.h" #include "../../cards/additional_info/color_identity_widget.h"
#include "../../cards/deck_preview_card_picture_widget.h" #include "../../cards/deck_preview_card_picture_widget.h"
#include "../visual_deck_storage_model.h" #include "../visual_deck_storage_model.h"
@ -45,6 +44,7 @@
#include <compare> #include <compare>
#include <functional> #include <functional>
#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/settings/visual_deck_storage_settings.h> #include <libcockatrice/settings/visual_deck_storage_settings.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <utility> #include <utility>

View file

@ -1,8 +1,6 @@
#include "visual_deck_storage_model.h" #include "visual_deck_storage_model.h"
#include "../../deck_loader/deck_file_format.h"
#include "../../deck_loader/deck_loader.h" #include "../../deck_loader/deck_loader.h"
#include "../../deck_loader/loaded_deck.h"
#include "../cards/additional_info/deck_color_identity.h" #include "../cards/additional_info/deck_color_identity.h"
#include "libcockatrice/deck_list/deck_list.h" #include "libcockatrice/deck_list/deck_list.h"
@ -21,6 +19,8 @@
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include <compare> #include <compare>
#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/utility/card_ref.h> #include <libcockatrice/utility/card_ref.h>
#include <optional> #include <optional>
#include <utility> #include <utility>

View file

@ -13,7 +13,6 @@
#ifndef VISUAL_DECK_STORAGE_MODEL_H #ifndef VISUAL_DECK_STORAGE_MODEL_H
#define VISUAL_DECK_STORAGE_MODEL_H #define VISUAL_DECK_STORAGE_MODEL_H
#include "../../deck_loader/loaded_deck.h"
#include "libcockatrice/utility/card_ref.h" #include "libcockatrice/utility/card_ref.h"
#include <QAbstractListModel> #include <QAbstractListModel>
@ -24,6 +23,9 @@
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QVector> #include <QVector>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <optional>
#include <qnamespace.h> #include <qnamespace.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>

View file

@ -1,7 +1,6 @@
#include "visual_deck_storage_sort_filter_proxy_model.h" #include "visual_deck_storage_sort_filter_proxy_model.h"
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../deck_loader/loaded_deck.h"
#include "../../filters/deck_filter_string.h" #include "../../filters/deck_filter_string.h"
#include "visual_deck_storage_model.h" #include "visual_deck_storage_model.h"
@ -16,6 +15,7 @@
#include <algorithm> #include <algorithm>
#include <compare> #include <compare>
#include <libcockatrice/card/card_localization.h> #include <libcockatrice/card/card_localization.h>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/settings/cards_display_settings.h> #include <libcockatrice/settings/cards_display_settings.h>
VisualDeckStorageSortFilterProxyModel::VisualDeckStorageSortFilterProxyModel(QObject *parent) VisualDeckStorageSortFilterProxyModel::VisualDeckStorageSortFilterProxyModel(QObject *parent)

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