From e49d053838d4fdda4de1faa24a4e7bc983a4f870 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Tue, 3 Nov 2026 18:51:23 -0700 Subject: [PATCH] Revert "Feature/continuumatrice product migration" --- CMakeLists.txt | 38 +- README.md | 218 +- cmake/Info.plist | 53 +- cmake/NSIS.template.in | 283 ++- cockatrice/CMakeLists.txt | 69 +- cockatrice/cockatrice.qrc | 24 +- cockatrice/continuumatrice.desktop | 12 - cockatrice/resources/cardback.svg | 612 ++--- .../continuumatrice-empty-carddatabase.xml | 8 - cockatrice/resources/continuumatrice.svg | 394 --- cockatrice/resources/hand.svg | 2232 ++++++++--------- cockatrice/resources/icons/essence/A.svg | 1 - cockatrice/resources/icons/essence/B.svg | 1 - cockatrice/resources/icons/essence/C.svg | 1 - cockatrice/resources/icons/essence/E.svg | 1 - cockatrice/resources/icons/essence/G.svg | 1 - cockatrice/resources/icons/essence/J.svg | 1 - cockatrice/resources/icons/essence/L.svg | 1 - cockatrice/resources/icons/essence/M.svg | 1 - cockatrice/resources/icons/essence/N.svg | 1 - cockatrice/resources/icons/essence/O.svg | 1 - cockatrice/resources/icons/essence/P.svg | 1 - cockatrice/resources/icons/essence/Q.svg | 1 - cockatrice/resources/icons/essence/R.svg | 1 - cockatrice/resources/icons/essence/S.svg | 1 - cockatrice/resources/icons/essence/T.svg | 1 - cockatrice/resources/icons/essence/V.svg | 1 - cockatrice/resources/icons/mana/A.png | Bin 39184 -> 0 bytes cockatrice/resources/icons/mana/A.svg | 1 - cockatrice/resources/icons/mana/B.png | Bin 11983 -> 0 bytes cockatrice/resources/icons/mana/B.svg | 48 +- cockatrice/resources/icons/mana/C.png | Bin 16929 -> 0 bytes cockatrice/resources/icons/mana/C.svg | 73 +- cockatrice/resources/icons/mana/E.png | Bin 27874 -> 0 bytes cockatrice/resources/icons/mana/E.svg | 1 - cockatrice/resources/icons/mana/G.png | Bin 13973 -> 0 bytes cockatrice/resources/icons/mana/G.svg | 48 +- cockatrice/resources/icons/mana/J.png | Bin 11518 -> 0 bytes cockatrice/resources/icons/mana/J.svg | 1 - cockatrice/resources/icons/mana/L.png | Bin 12580 -> 0 bytes cockatrice/resources/icons/mana/L.svg | 1 - cockatrice/resources/icons/mana/M.png | Bin 30485 -> 0 bytes cockatrice/resources/icons/mana/M.svg | 1 - cockatrice/resources/icons/mana/N.png | Bin 30029 -> 0 bytes cockatrice/resources/icons/mana/N.svg | 1 - cockatrice/resources/icons/mana/O.png | Bin 10264 -> 0 bytes cockatrice/resources/icons/mana/O.svg | 1 - cockatrice/resources/icons/mana/P.png | Bin 13859 -> 0 bytes cockatrice/resources/icons/mana/P.svg | 1 - cockatrice/resources/icons/mana/Q.png | Bin 13703 -> 0 bytes cockatrice/resources/icons/mana/Q.svg | 1 - cockatrice/resources/icons/mana/R.png | Bin 11146 -> 0 bytes cockatrice/resources/icons/mana/R.svg | 48 +- cockatrice/resources/icons/mana/S.png | Bin 31593 -> 0 bytes cockatrice/resources/icons/mana/S.svg | 1 - cockatrice/resources/icons/mana/T.png | Bin 10757 -> 0 bytes cockatrice/resources/icons/mana/T.svg | 1 - cockatrice/resources/icons/mana/V.png | Bin 40452 -> 0 bytes cockatrice/resources/icons/mana/V.svg | 1 - cockatrice/resources/piles/banishment.svg | 1 - cockatrice/resources/piles/graveyard.svg | 1 - cockatrice/resources/piles/limbo.svg | 8 +- cockatrice/resources/piles/oblivion.svg | 8 +- cockatrice/resources/piles/refuge.svg | 7 +- cockatrice/sounds/CMakeLists.txt | 4 +- .../remote_connection_controller.cpp | 13 +- .../network/update/client/release_channel.cpp | 12 +- .../src/client/settings/cache_settings.cpp | 11 - cockatrice/src/client/sound_engine.cpp | 2 +- .../player/essence_pool_item.cpp | 186 -- .../game_graphics/player/essence_pool_item.h | 57 - .../player/player_graphics_item.cpp | 19 +- .../player/player_graphics_item.h | 2 - .../src/game_graphics/zones/table_zone.cpp | 8 +- cockatrice/src/installer_close_server.cpp | 4 +- .../src/interface/essence_chart_colors.h | 55 - .../src/interface/intents/url_parser.cpp | 2 +- cockatrice/src/interface/intents/url_parser.h | 2 +- .../src/interface/pixel_map_generator.cpp | 5 +- cockatrice/src/interface/theme_manager.cpp | 4 +- .../additional_info/color_identity_widget.cpp | 33 +- .../additional_info/deck_color_identity.cpp | 8 +- .../additional_info/deck_color_identity.h | 5 +- .../additional_info/essence_filter_picker.cpp | 157 -- .../additional_info/essence_filter_picker.h | 47 - .../additional_info/mana_cost_widget.cpp | 26 +- .../mana_base/mana_base_config_dialog.cpp | 4 +- .../mana_base/mana_base_widget.cpp | 11 +- .../mana_curve/mana_curve_category_widget.cpp | 4 +- .../mana_curve/mana_curve_config_dialog.cpp | 2 +- .../mana_curve/mana_curve_total_widget.cpp | 4 +- .../mana_curve/mana_curve_widget.cpp | 2 +- .../mana_devotion_config_dialog.cpp | 2 +- .../mana_devotion/mana_devotion_widget.cpp | 10 +- .../mana_distribution_config_dialog.cpp | 15 +- ...ana_distribution_single_display_widget.cpp | 2 +- .../mana_distribution_widget.cpp | 19 +- .../deck_list_statistics_analyzer.cpp | 88 +- .../deck_list_statistics_analyzer.h | 2 +- .../interface/widgets/dialogs/dlg_update.cpp | 22 +- .../general/display/charts/bars/color_bar.cpp | 24 +- .../general/display/charts/pies/color_pie.cpp | 24 +- .../widgets/onboarding/first_run_wizard.cpp | 2 +- .../interface/widgets/tabs/tab_supervisor.cpp | 2 +- ...l_database_display_color_filter_widget.cpp | 216 +- ...ual_database_display_color_filter_widget.h | 4 +- ...k_preview_color_identity_filter_widget.cpp | 55 +- ...eck_preview_color_identity_filter_widget.h | 4 +- cockatrice/src/interface/window_main.cpp | 144 +- cockatrice/src/interface/window_main.h | 4 +- cockatrice/src/main.cpp | 41 +- cockatrice/themes/CMakeLists.txt | 4 +- doc/carddatabase_v4/cards.xsd | 2 +- doc/continuum-servatrice-hosting.md | 107 - doc/continuumatrice-battlefield.md | 12 +- doc/continuumatrice-card-data.md | 10 +- doc/continuumatrice-card-fields.md | 69 - doc/continuumatrice-essence.md | 111 - doc/continuumatrice-linux-install.md | 59 - doc/continuumatrice-match-essence.md | 83 - doc/continuumatrice-product-migration.md | 82 - libcockatrice_card/CMakeLists.txt | 1 - .../libcockatrice/card/card_info.cpp | 70 +- .../libcockatrice/card/card_info.h | 12 - .../card/database/card_database.cpp | 6 +- .../card/database/parser/cockatrice_xml_4.cpp | 12 +- .../libcockatrice/card/essence_types.h | 76 - .../libcockatrice/card/game_specific_terms.h | 55 +- .../libcockatrice/filters/filter_card.cpp | 6 +- .../libcockatrice/filters/filter_tree.cpp | 69 +- .../database/card_database_display_model.cpp | 25 +- .../models/database/card_database_model.cpp | 35 +- .../models/database/card_database_model.h | 10 +- .../server/remote/game/server_player.cpp | 42 +- .../settings/download_settings.cpp | 9 +- libcockatrice_utility/CMakeLists.txt | 1 - .../libcockatrice/utility/essence_counter.h | 55 - servatrice/CMakeLists.txt | 58 +- servatrice/continuum-servatrice.ini.example | 593 ----- servatrice/continuum-servatrice.sql | 424 ---- servatrice/src/main.cpp | 6 +- servatrice/src/servatrice.cpp | 8 +- servatrice/src/settingscache.cpp | 4 +- tests/CMakeLists.txt | 7 - tests/carddatabase/carddatabase_test.cpp | 61 - tests/essence_types_test.cpp | 83 - tests/model_contract/model_contract_test.cpp | 48 - .../installer-holder-probe/close_holders.cpp | 2 +- .../installer_close_name.cpp | 2 +- tools/installer-holder-probe/payload_paths.h | 2 +- 150 files changed, 2599 insertions(+), 5304 deletions(-) delete mode 100644 cockatrice/continuumatrice.desktop delete mode 100644 cockatrice/resources/continuumatrice-empty-carddatabase.xml delete mode 100644 cockatrice/resources/continuumatrice.svg delete mode 100644 cockatrice/resources/icons/essence/A.svg delete mode 100644 cockatrice/resources/icons/essence/B.svg delete mode 100644 cockatrice/resources/icons/essence/C.svg delete mode 100644 cockatrice/resources/icons/essence/E.svg delete mode 100644 cockatrice/resources/icons/essence/G.svg delete mode 100644 cockatrice/resources/icons/essence/J.svg delete mode 100644 cockatrice/resources/icons/essence/L.svg delete mode 100644 cockatrice/resources/icons/essence/M.svg delete mode 100644 cockatrice/resources/icons/essence/N.svg delete mode 100644 cockatrice/resources/icons/essence/O.svg delete mode 100644 cockatrice/resources/icons/essence/P.svg delete mode 100644 cockatrice/resources/icons/essence/Q.svg delete mode 100644 cockatrice/resources/icons/essence/R.svg delete mode 100644 cockatrice/resources/icons/essence/S.svg delete mode 100644 cockatrice/resources/icons/essence/T.svg delete mode 100644 cockatrice/resources/icons/essence/V.svg delete mode 100644 cockatrice/resources/icons/mana/A.png delete mode 100644 cockatrice/resources/icons/mana/A.svg delete mode 100644 cockatrice/resources/icons/mana/B.png delete mode 100644 cockatrice/resources/icons/mana/C.png delete mode 100644 cockatrice/resources/icons/mana/E.png delete mode 100644 cockatrice/resources/icons/mana/E.svg delete mode 100644 cockatrice/resources/icons/mana/G.png delete mode 100644 cockatrice/resources/icons/mana/J.png delete mode 100644 cockatrice/resources/icons/mana/J.svg delete mode 100644 cockatrice/resources/icons/mana/L.png delete mode 100644 cockatrice/resources/icons/mana/L.svg delete mode 100644 cockatrice/resources/icons/mana/M.png delete mode 100644 cockatrice/resources/icons/mana/M.svg delete mode 100644 cockatrice/resources/icons/mana/N.png delete mode 100644 cockatrice/resources/icons/mana/N.svg delete mode 100644 cockatrice/resources/icons/mana/O.png delete mode 100644 cockatrice/resources/icons/mana/O.svg delete mode 100644 cockatrice/resources/icons/mana/P.png delete mode 100644 cockatrice/resources/icons/mana/P.svg delete mode 100644 cockatrice/resources/icons/mana/Q.png delete mode 100644 cockatrice/resources/icons/mana/Q.svg delete mode 100644 cockatrice/resources/icons/mana/R.png delete mode 100644 cockatrice/resources/icons/mana/S.png delete mode 100644 cockatrice/resources/icons/mana/S.svg delete mode 100644 cockatrice/resources/icons/mana/T.png delete mode 100644 cockatrice/resources/icons/mana/T.svg delete mode 100644 cockatrice/resources/icons/mana/V.png delete mode 100644 cockatrice/resources/icons/mana/V.svg delete mode 100644 cockatrice/resources/piles/banishment.svg delete mode 100644 cockatrice/resources/piles/graveyard.svg delete mode 100644 cockatrice/src/game_graphics/player/essence_pool_item.cpp delete mode 100644 cockatrice/src/game_graphics/player/essence_pool_item.h delete mode 100644 cockatrice/src/interface/essence_chart_colors.h delete mode 100644 cockatrice/src/interface/widgets/cards/additional_info/essence_filter_picker.cpp delete mode 100644 cockatrice/src/interface/widgets/cards/additional_info/essence_filter_picker.h delete mode 100644 doc/continuum-servatrice-hosting.md delete mode 100644 doc/continuumatrice-card-fields.md delete mode 100644 doc/continuumatrice-essence.md delete mode 100644 doc/continuumatrice-linux-install.md delete mode 100644 doc/continuumatrice-match-essence.md delete mode 100644 doc/continuumatrice-product-migration.md delete mode 100644 libcockatrice_card/libcockatrice/card/essence_types.h delete mode 100644 libcockatrice_utility/libcockatrice/utility/essence_counter.h delete mode 100644 servatrice/continuum-servatrice.ini.example delete mode 100644 servatrice/continuum-servatrice.sql delete mode 100644 tests/essence_types_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index dbc54d7c0..677ef0c12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,14 +15,11 @@ option(WARNING_AS_ERROR "Treat warnings as errors in debug builds" ON) # Check for translation updates option(UPDATE_TRANSLATIONS "Update translations on compile" OFF) # Compile Cockatrice -option(WITH_CLIENT "Build Continuumatrice client" ON) +option(WITH_CLIENT "Build Cockatrice client" ON) # Compile Oracle -option(WITH_ORACLE "Build legacy upstream Magic-card Oracle importer (not Continuumatrice data)" OFF) -if(WITH_ORACLE) - message(FATAL_ERROR "The upstream Oracle importer fetches Magic data and cannot be distributed as Continuumatrice. Build with -DWITH_ORACLE=OFF; use your own card XML.") -endif() +option(WITH_ORACLE "Build Cockatrice card database tool (Oracle)" ON) # Compile Servatrice -option(WITH_SERVER "Build Continuum-Servatrice dedicated multiplayer server" ON) +option(WITH_SERVER "Build Cockatrice server (Servatrice)" OFF) # Compile tests option(TEST "Build tests" OFF) # Build with AddressSanitizer and UndefinedBehaviorSanitizer @@ -95,7 +92,7 @@ endif() # A project name is needed for CPack # Version can be overriden by git tags, see cmake/getversion.cmake -project("Continuumatrice" VERSION 3.2.0) +project("Cockatrice" VERSION 3.2.0) # Set release name if not provided via env/cmake var if(NOT DEFINED GIT_TAG_RELEASENAME) @@ -296,17 +293,17 @@ if(MSVC) endif() # Package builder -set(CPACK_PACKAGE_CONTACT "Continuum Universes Studio") +set(CPACK_PACKAGE_CONTACT "Zach Halpern ") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_NAME}") -set(CPACK_PACKAGE_VENDOR "Continuum Universes Studio") +set(CPACK_PACKAGE_VENDOR "Cockatrice Development Team") set(CPACK_PACKAGE_DESCRIPTION - "Continuumatrice is the Continuum Universes card-game client, independently packaged from the upstream Cockatrice project. It supports local games and compatible multiplayer servers." + "Cockatrice is an open-source, multiplatform application for playing tabletop card games over a network. The program's server design prevents users from manipulating the game for unfair advantage. The client also provides a single-player mode, which allows users to brew while offline." ) set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") -set(CPACK_PACKAGE_FILE_NAME "Continuumatrice-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}") +set(CPACK_PACKAGE_FILE_NAME "${PROJECT_VERSION_FILENAME}") if(UNIX) if(APPLE) @@ -323,10 +320,10 @@ if(UNIX) # linux if(CPACK_GENERATOR STREQUAL "RPM") set(CPACK_RPM_PACKAGE_LICENSE "GPLv2") - set(CPACK_RPM_MAIN_COMPONENT "continuumatrice") + set(CPACK_RPM_MAIN_COMPONENT "cockatrice") set(CPACK_RPM_PACKAGE_REQUIRES "protobuf, qt6-qtimageformats, qt6-qtmultimedia, qt6-qtsvg, qt6-qttools") set(CPACK_RPM_PACKAGE_GROUP "Amusements/Games") - set(CPACK_RPM_PACKAGE_URL "https://github.com/Continuum-Universes-Studio/Continuumatrice") + set(CPACK_RPM_PACKAGE_URL "http://github.com/Cockatrice/Cockatrice") # stop directories from making package conflicts set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/share/applications @@ -341,7 +338,7 @@ if(UNIX) set(CPACK_GENERATOR DEB) set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) set(CPACK_DEBIAN_PACKAGE_SECTION "games") - set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/Continuum-Universes-Studio/Continuumatrice") + set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/Cockatrice/Cockatrice") set(CPACK_DEBIAN_PACKAGE_DEPENDS "libqt6multimedia6, libqt6svg6, qt6-image-formats-plugins, qt6-qpa-plugins") set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libqt6sql6-mysql") # for connecting servatrice to a mysql db endif() @@ -373,7 +370,7 @@ elseif(WIN32) # All three applications are packaged into the same directory on Windows, so # this is the union of their Qt module lists, not just the client's: Servatrice # contributes Qt6Sql.dll and Oracle contributes nothing the client lacks, and a - # client-only manifest leaves Qt6Sql.dll unasserted even though continuum-servatrice.exe + # client-only manifest leaves Qt6Sql.dll unasserted even though servatrice.exe # is installed next to cockatrice.exe and cannot start without it. # # A statically linked Qt module ships no runtime DLL, so requiring one would @@ -445,16 +442,13 @@ if(WITH_CLIENT) endif() if(WITH_SERVER) - # Keep the inherited source tree stable while generating the native product - # build directory. The compiled program is build/continuum-servatrice/continuum-servatrice. - add_subdirectory(servatrice "${CMAKE_BINARY_DIR}/continuum-servatrice") - set(CPACK_INSTALL_CMAKE_PROJECTS "ContinuumServatrice;ContinuumServatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) + add_subdirectory(servatrice) + set(CPACK_INSTALL_CMAKE_PROJECTS "Servatrice;Servatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) endif() if(WITH_CLIENT) - # Source directory keeps its history; output no longer inherits "cockatrice". - add_subdirectory(cockatrice "${CMAKE_BINARY_DIR}/continuumatrice") - set(CPACK_INSTALL_CMAKE_PROJECTS "Continuumatrice;Continuumatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) + add_subdirectory(cockatrice) + set(CPACK_INSTALL_CMAKE_PROJECTS "Cockatrice;Cockatrice;ALL;/" ${CPACK_INSTALL_CMAKE_PROJECTS}) endif() if(WIN32 AND WITH_CLIENT) diff --git a/README.md b/README.md index 19566f51a..fa9272c0f 100644 --- a/README.md +++ b/README.md @@ -1,80 +1,188 @@ -# Continuumatrice +

-**Continuumatrice** is a standalone, open-source desktop card-game client for **Continuum Universes**, developed by **Continuum Universes Studio** and based on the Cockatrice C++/Qt project. +--- -It is **not** your normal Magic: The Gathering Cockatrice installation. Continuumatrice has an independent executable, application ID, Linux launcher, card database, deck directory, settings, cache, window branding, and URL scheme. It also supports Limbo, Refuge, and Oblivion as additional card piles. +

+ Cockatrice | + Download | + Related | + Community | + Contribute | + Build | + Run +

-> **Status:** Active fork development. Features and packaging changes in draft pull requests are not released or validated until built and tested. +--- -## What's different +

+To get started with Cockatrice ⇢ [view our webpage](https://cockatrice.github.io/) +To get support, or suggest changes to the app ⇢ [file an issue](https://github.com/Cockatrice/Cockatrice/issues) ([How?](https://github.com/Cockatrice/Cockatrice/wiki/How-to-Create-a-GitHub-Ticket-Regarding-Cockatrice))
+To learn about the app and its code ⇢ [read our code documentation](https://cockatrice.github.io/docs/) +To help with development ⇢ learn [how to contribute](#contribute) +


-- Front, Support, Command battlefield bands with theme overrides -- Limbo, Refuge, and Oblivion as real server-backed public piles -- Independent profile: `~/.local/share/Continuumatrice` and `~/.cache/Continuumatrice` by default on Linux -- Independent URL handler: `continuumatrice://` (does not claim `cockatrice://`) -- Own app icon, name and install identity; regular Cockatrice remains untouched -- No built-in Magic card import or Scryfall download list by default -- Card data supplied by your own Cockatrice XML v4-compatible files and images; the XML schema is still inherited from Cockatrice -- Matching fork client/server required for the three new pile-zone names -**Game rules are not automatically enforced** by the generic tabletop client. These piles are functional card containers, not an implementation of Continuum Universes rules themselves. +# Cockatrice -## Build on openSUSE Tumbleweed +Cockatrice is an open-source, multiplatform application for playing tabletop card games over a network. The program's server design prevents users from manipulating the game for unfair advantage. The client also provides a single-player mode, which allows users to brew while offline.

+This project uses C++ and the Qt libraries.
-Install development packages for Qt 6, Protobuf, a C++20 compiler, CMake and Ninja. Refer to [Linux installation notes](doc/continuumatrice-linux-install.md) for the dependency setup. +# Download [![Cockatrice Eternal Download Count](https://img.shields.io/github/downloads/cockatrice/cockatrice/total.svg)](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice&search=0) + +Downloads are available for **stable releases** and the current **beta version** in development. +There is no strict release schedule for either of them. +

+Latest stable release:
+[![Download Stable Release](https://img.shields.io/github/release/cockatrice/cockatrice.svg?label=version&colorB=0d7ebf "Download Latest Stable Release")](https://github.com/cockatrice/cockatrice/releases/latest) ![](https://img.shields.io/github/release-date/Cockatrice/Cockatrice.svg?label=released&colorB=0d7ebf "Release Date") [![](https://img.shields.io/github/downloads/cockatrice/cockatrice/latest/total.svg?label=downloads&colorB=0d7ebf "Number of Downloads")](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice&search=0)
+
+Latest beta version:
+[![Download Beta Release](https://img.shields.io/github/release/cockatrice/cockatrice/all.svg?label=version&colorB=f37f40 "Download Latest Beta Release")](https://github.com/cockatrice/cockatrice/releases) ![](https://img.shields.io/github/release-date-pre/Cockatrice/Cockatrice.svg?label=released&colorB=f37f40 "Release Date") [![](https://img.shields.io/github/downloads-pre/cockatrice/cockatrice/latest/total.svg?label=downloads&colorB=f37f40 "Number of Downloads")](https://tooomm.github.io/github-release-stats/?username=Cockatrice&repository=Cockatrice&search=0) [![](https://img.shields.io/github/commits-since/Cockatrice/Cockatrice/latest.svg?label=changes&colorB=f37f40 "Changes over Stable Release")](https://github.com/Cockatrice/Cockatrice/pulls?q=is%3Apr+is%3Aclosed)
+While incorporating the latest fixes and features, beta builds may not be stable or contain new bugs!
+Please report any findings and open new issues when testing them!
+
+ +# Related Repositories + +- [Magic-Token](https://github.com/Cockatrice/Magic-Token): File with MtG token data for use in Cockatrice +- [Magic-Spoiler](https://github.com/Cockatrice/Magic-Spoiler): Code to generate MtG spoiler data from [MTGJSON](https://github.com/mtgjson/mtgjson) for use in Cockatrice +- [cockatrice.github.io](https://github.com/Cockatrice/cockatrice.github.io): Code of the official Cockatrice webpage +- [io.github.Cockatrice.cockatrice](https://github.com/flathub/io.github.Cockatrice.cockatrice): Configuration of our Linux `flatpak` package hosted at [Flathub](https://flathub.org/en/apps/io.github.Cockatrice.cockatrice) +- [Webatrice](https://github.com/Cockatrice/Webatrice): Web client for Cockatrice servers (TypeScript / React) + + +# Community Resources [![Discord](https://img.shields.io/discord/314987288398659595?label=Discord&logo=discord&logoColor=white)](https://discord.gg/3Z9yzmA) + +Join our [Discord community](https://discord.gg/3Z9yzmA) to connect with other project contributors (`#dev` channel) or fellow users of the app. Come here to talk about the application, features, or just to hang out. +- [Official Website](https://cockatrice.github.io) +- [Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki) +- [Official Discord](https://discord.gg/3Z9yzmA) +- [reddit r/Cockatrice](https://reddit.com/r/cockatrice) + +> [!IMPORTANT] +> For support regarding specific servers, please contact that server's admin/mods and use their dedicated communication channels rather than contacting the team building the software. + + +# Contribute +

+ Code | + Documentation | + Translation +

+ +#### Repository Activity +![Cockatrice Repo Analytics](https://repobeats.axiom.co/api/embed/c7cec938789a5bbaeb4182a028b4dbb96db8f181.svg "Cockatrice Repo Analytics by Repobeats") + +
+Kudos to all our amazing contributors ❤️ +
+ + +
+ Made with contrib.rocks +
+ +### Code + +To commit code to the project, please review our [Contribution Guidelines](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md) first.
+We maintain two tags for contributors to easier find issues to potentially work on: +- [![Good first issue](https://img.shields.io/github/issues/cockatrice/cockatrice/Good%20First%20Issue)](https://github.com/Cockatrice/Cockatrice/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22Good%20first%20issue%22%20)
+Issues tagged in this way provide a simple way to get started. They don't require much experience to be worked on. +- [![Help wanted](https://img.shields.io/github/issues/cockatrice/cockatrice/Help%20Wanted)](https://github.com/Cockatrice/Cockatrice/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3A%22Help%20Wanted%22%20)
+This tag is used for issues that we are looking for somebody to pick up. Often this is for feature suggestions we support, but don't have the time to work on ourselves. + +For both tags, we're willing to provide help to contributors in showing them where and how they can make changes, as well as code reviews for submitted changes.
+We'll happily advice on how best to implement a feature, or we can show you where the codebase is doing something similar before you get too far along - put a note on an issue you want to discuss more on! + +You can also have a look at our `Todo List` in our [Code Documentation](https://cockatrice.github.io/docs) or search the repo for [`\todo` comments](https://github.com/search?q=repo%3ACockatrice%2FCockatrice%20%5Ctodo&type=code). + +### Documentation [![CI Docs](https://github.com/Cockatrice/Cockatrice/actions/workflows/documentation-build.yml/badge.svg?event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/documentation-build.yml?query=event%3Arelease+event%3Aworkflow_dispatch) + +There are various places where useful information for different needs are maintained: +- [Official Code Documentation](https://cockatrice.github.io/docs/) +- [Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki) `Community supported` +- [Official Webpage](https://cockatrice.github.io/) +- [Official README](https://github.com/Cockatrice/Cockatrice/blob/master/README.md) `This file` +- [Official CONTRIBUTING](https://github.com/Cockatrice/Cockatrice/blob/master/.github/CONTRIBUTING.md) + +Cockatrice tries to use the [Google Developer Documentation Style Guide](https://developers.google.com/style/) to ensure consistent documentation. +We encourage everybody to improve the documentation by suggesting edits based on this guide. + +### Translation [![Transifex Project](https://img.shields.io/badge/translate-on%20transifex-brightgreen)](https://explore.transifex.com/cockatrice/cockatrice/) + +Cockatrice uses Transifex to manage translations. You can help us bring Cockatrice and Oracle to your language and just adjust single wordings right from within your browser by visiting our [Transifex project page](https://explore.transifex.com/cockatrice/cockatrice/). The [Webatrice](https://github.com/seavor/Webatrice) web client manages its own translations in its repo.
+ +Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information about getting involved, and join a group of hundreds of others!
+ + +# Build [![CI Desktop](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml/badge.svg?branch=master&event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/desktop-build.yml?query=branch%3Amaster+event%3Apush) [![CI Docker](https://github.com/Cockatrice/Cockatrice/actions/workflows/docker-release.yml/badge.svg?branch=master&event=push)](https://github.com/Cockatrice/Cockatrice/actions/workflows/docker-release.yml?query=branch%3Amaster+event%3Apush) + +Dependencies: *(for minimum versions search our [CMake file](https://github.com/Cockatrice/Cockatrice/blob/master/CMakeLists.txt))* +- [Qt](https://www.qt.io/developers/) +- [protobuf](https://github.com/protocolbuffers/protobuf) +- [CMake](https://www.cmake.org/) + +Oracle can *optionally* use some packages to load compressed card files: +- [xz](https://tukaani.org/xz/) +- [zlib](https://www.zlib.net/) + +
+ +**Basic compilation steps:** ```bash -git clone https://github.com/Continuum-Universes-Studio/Continuumatrice.git -cd Continuumatrice - -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ - -DWITH_CLIENT=ON -DWITH_SERVER=ON -DWITH_ORACLE=OFF - -cmake --build build --target continuumatrice continuum-servatrice --parallel 16 -./build/continuumatrice/continuumatrice +mkdir build +cd build +cmake .. +make ``` -The `cockatrice` CMake target is a backwards-compatible **build alias**, not the generated executable. An old `build/cockatrice/cockatrice` binary may still exist if you compiled an earlier version: it is stale, not Continuumatrice. +You can then + - Create a Cockatrice installation inside the `release` folder: + ```bash + make install + ``` + - Or make an installation package specific to your system: + ```bash + make package + ``` -To install under an appropriate prefix: +> [!NOTE] +> Detailed compiling instructions can be found in the Cockatrice wiki at [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice) -```bash -cmake --install build -``` +
-Inspect `CMAKE_INSTALL_PREFIX` before installing, particularly on a machine that already has Cockatrice. The fork does not install or remove `/usr/bin/cockatrice` or the upstream `cockatrice.desktop`. +The following flags (with their non-default values) can be passed to `cmake`: +| Flag | Description | +| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| `-DWITH_SERVER=1` | Build Servatrice server | +| `-DWITH_CLIENT=0` | Don't build Cockatrice client | +| `-DWITH_ORACLE=0` | Don't build Oracle card database tool | +| `-DCMAKE_BUILD_TYPE=Debug` | Compile in debug mode
Enables extra logging output, debug symbols, and much more verbose compiler warnings | +| `-DWARNING_AS_ERROR=0` | Don't treat compilation warnings as errors in debug mode | +| `-DUPDATE_TRANSLATIONS=1` | Configure `make` to update the translation .ts files for new strings in the source code
**Note:** `make clean` will remove the .ts files | +| `-DTEST=1` | Enable regression tests
**Note:** `make test` to run tests, *googletest* will be downloaded if not available | -## Card catalogue +> [!TIP] +> Build code documentation ([Doxygen](https://www.doxygen.nl)) according to our [configuration](https://github.com/Cockatrice/Cockatrice/blob/master/Doxyfile) into `/docs`: +> ```bash +> doxygen Doxyfile +> ``` -The fork accepts XML version 4 for the time being. An empty `cards.xml` is created on a fresh profile; store your Continuum Universes sets in `~/.local/share/Continuumatrice/customsets/`, then reload the card database. -Images live under `~/.local/share/Continuumatrice/pics/`. Custom graphics and themed pile icons are supported; no MTG catalogue is required. +# Run -- [Card XML and assets](doc/continuumatrice-card-data.md) -- [Special piles and theme icons](doc/continuumatrice-special-piles.md) -- [Battlefield bands](doc/continuumatrice-battlefield.md) -- [Linux coexistence with Cockatrice](doc/continuumatrice-linux-install.md) +Cockatrice is the game client
+Oracle fetches card data
+Servatrice is the server
-## Independent product boundary +#### Docker -The fork **must not** use upstream Cockatrice software-update releases, automatically import Magic card databases, register `.cod` as its own file type, or interfere with the original Cockatrice installation. A native Continuumatrice card importer and full multi-platform release validation are separate engineering work. +You can build an image & deploy a Servatrice (Cockatrice server) container using [Docker](https://www.docker.com/resources/what-container/) and our Dockerfile yourself.
-Legacy class names, the `cockatrice/` source directory, `libcockatrice_*` internal libraries, Protobuf protocol, and inherited card XML root element remain for source and wire compatibility; they do not represent the installed product name. +For more details, look into our wiki section on [Setting up Servatrice](https://github.com/Cockatrice/Cockatrice/wiki/Setting-up-Servatrice#using-docker).
+You'll also find more hints on our **pre-build image** there, or the **docker-compose** file which will configure and run both a MySQL server and Servatrice. -## License and upstream attribution -Continuumatrice is derived from [Cockatrice](https://github.com/Cockatrice/Cockatrice), originally developed by the Cockatrice contributors. Existing copyright headers, contribution history and upstream license obligations are retained. See [LICENSE](LICENSE). The Continuumatrice modifications are maintained by Continuum Universes Studio. +# License [![GPLv2 License](https://img.shields.io/github/license/Cockatrice/Cockatrice.svg)](https://github.com/Cockatrice/Cockatrice/blob/master/LICENSE) -This is an independent fork, **not** an official Cockatrice release. - -## Host Continuum-Servatrice - -This repository includes the full dedicated multiplayer server, **Continuum-Servatrice**, built with the `continuum-servatrice` CMake target (default `WITH_SERVER=ON`). Its build output is `build/continuum-servatrice/continuum-servatrice`. Use the standalone `continuum_servatrice` MySQL database and the provided server-specific SQL schema/config; do not reuse MTG Servatrice's database or tables. - -[Dedicated server hosting and database instructions](doc/continuum-servatrice-hosting.md) - -## In-match Essence pools - -Each player has a server-authoritative **16-type Essence pool** (`M S O G E A Q C J B R L V T P N`), starting at zero. The match displays all balances beside the battlefield with quick gain/spend/set controls. Essence isn't generated by drawing land cards, and there is no automatic per-turn resource refill or cost spending until actual CCG gameplay rules define those mechanics. Continuum-Servatrice and the local server both use the same counter protocol and state. - -[In-match Essence resource pools and test checklist](doc/continuumatrice-match-essence.md) +Cockatrice is free software, licensed under the [GPLv2](https://github.com/Cockatrice/Cockatrice/blob/master/LICENSE). diff --git a/cmake/Info.plist b/cmake/Info.plist index 838033752..7f01befcb 100644 --- a/cmake/Info.plist +++ b/cmake/Info.plist @@ -51,18 +51,65 @@ - + + + + UTExportedTypeDeclarations + + + UTTypeIdentifier + org.cockatrice.deck + + UTTypeDescription + Cockatrice Deck + + UTTypeConformsTo + + public.data + + + UTTypeTagSpecification + + public.filename-extension + + cod + + + + + + CFBundleDocumentTypes + + + CFBundleTypeName + Cockatrice Deck + + CFBundleTypeRole + Editor + + LSHandlerRank + Default + + LSItemContentTypes + + org.cockatrice.deck + + + + + + CFBundleURLTypes CFBundleURLName - Continuumatrice URL Scheme + Cockatrice URL Scheme CFBundleURLSchemes - continuumatrice + cockatrice diff --git a/cmake/NSIS.template.in b/cmake/NSIS.template.in index f50ae33a5..923aded9d 100644 --- a/cmake/NSIS.template.in +++ b/cmake/NSIS.template.in @@ -105,9 +105,9 @@ Var SentryOutput !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP "${NSIS_SOURCE_PATH}\cmake\headerimage.bmp" !define MUI_HEADERIMAGE_UNBITMAP "${NSIS_SOURCE_PATH}\cmake\headerimage.bmp" -!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of Continuumatrice.$\r$\n$\r$\nClick Next to continue." -!define MUI_FINISHPAGE_RUN "$INSTDIR/continuumatrice.exe" -!define MUI_FINISHPAGE_RUN_TEXT "Run 'Continuumatrice' now" +!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of Cockatrice.$\r$\n$\r$\nClick Next to continue." +!define MUI_FINISHPAGE_RUN "$INSTDIR/cockatrice.exe" +!define MUI_FINISHPAGE_RUN_TEXT "Run 'Cockatrice' now" !define MUI_ICON "${NSIS_SOURCE_PATH}\cockatrice\resources\appicon.ico" !define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfReinstall @@ -141,7 +141,7 @@ Page Custom PortableModePageCreate PortableModePageLeave ; user's machine can be diagnosed after the fact. Kept in $TEMP rather than in ; $INSTDIR because the previous version's uninstaller removes the whole install ; directory part-way through an upgrade. Read it back with: -; notepad %TEMP%\Continuumatrice-installer.log +; notepad %TEMP%\Cockatrice-installer.log !macro CockLog text Push "${text}" Call CockLogWrite @@ -206,7 +206,7 @@ FunctionEnd Function .onInit -StrCpy $InstallLogPath "$TEMP\Continuumatrice-installer.log" +StrCpy $InstallLogPath "$TEMP\Cockatrice-installer.log" ; No backup has been taken yet; RestoreUserDataAfterUninstall must be a no-op ; until BackupUserDataBeforeUninstall sets this after actually copying something. StrCpy $DataBackupDir "" @@ -232,22 +232,22 @@ FileClose $LogHandle ${If} ${NSIS_IS_64_BIT} == 1 #NSIS 64bit ${IfNot} ${RunningX64} - MessageBox MB_OK|MB_ICONSTOP "This version of Continuumatrice requires a 64-bit Windows system." + MessageBox MB_OK|MB_ICONSTOP "This version of Cockatrice requires a 64-bit Windows system." Abort ${EndIf} - StrCpy $NormalDestDir "$ProgramFiles64\Continuumatrice" + StrCpy $NormalDestDir "$ProgramFiles64\Cockatrice" SetRegView 64 ${Else} #NSIS 32bit ${If} ${RunningX64} MessageBox MB_OK|MB_ICONEXCLAMATION \ - "You are about to install a 32-bit version of Continuumatrice on a 64-bit Windows system.$\n\ + "You are about to install a 32-bit version of Cockatrice on a 64-bit Windows system.$\n\ We advise you to use the correct 64-bit installer instead to get around potential issues.$\n$\n\ - Download from our webpage: https://github.com/Continuum-Universes-Studio/Continuumatrice" + Download from our webpage: https://cockatrice.github.io" ${EndIf} - StrCpy $NormalDestDir "$ProgramFiles\Continuumatrice" + StrCpy $NormalDestDir "$ProgramFiles\Cockatrice" ${EndIf} -StrCpy $PortableDestDir "$Desktop\ContinuumatricePortable" +StrCpy $PortableDestDir "$Desktop\CockatricePortable" ${GetParameters} $9 @@ -337,7 +337,7 @@ ${EndIf} ; Then make sure no *other* application instance is still running (and holding ; file locks) before the previous version is uninstalled. The probe names them ; and the user is asked to close them; nothing is force-stopped without that -; answer, because Continuumatrice may hold an unsaved deck and Oracle writes the card +; answer, because Cockatrice may hold an unsaved deck and Oracle writes the card ; database in place. On /R the old version is about to be removed, so an answer ; the probe cannot give cancels the upgrade before it starts rather than after it. ; Only needed this early for /R, where AutoUninstallIfNeeded runs below; for @@ -365,7 +365,7 @@ ${EndIf} ; The uninstaller logs to its own file: the installer truncates its log on every run, so ; sharing one would mean either side erasing the other's account of a failed upgrade. -StrCpy $InstallLogPath "$TEMP\Continuumatrice-uninstaller.log" +StrCpy $InstallLogPath "$TEMP\Cockatrice-uninstaller.log" FileOpen $LogHandle "$InstallLogPath" w FileClose $LogHandle !insertmacro CockLogUn "==================================================================" @@ -380,7 +380,7 @@ Function RequireAdmin UserInfo::GetAccountType Pop $8 ${If} $8 != "admin" - MessageBox MB_ICONSTOP "You need administrator rights to install Continuumatrice" + MessageBox MB_ICONSTOP "You need administrator rights to install Cockatrice" SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED Abort ${EndIf} @@ -471,7 +471,7 @@ SetShellVarContext all ; ; --- 32-bit uninstall --- SetRegView 32 -ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "QuietUninstallString" +ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString" ; On the /R path the updater passes /D=, so $INSTDIR already is the old ; install dir. Publish it for the backup/restore pair below. @@ -491,7 +491,7 @@ done32: ; --- 64-bit uninstall --- ${If} ${RunningX64} SetRegView 64 - ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "QuietUninstallString" + ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString" StrCpy $OldInstallDir $INSTDIR !insertmacro CockLog "uninstall (64-bit): '$R0 _?=$OldInstallDir'" @@ -516,7 +516,7 @@ FunctionEnd ; that did not get a usable _?= copies itself to %TEMP%, starts that copy and exits, so ; ExecWait returns at once while the copy keeps deleting - and it finishes after this ; window has closed, removing the version that was just installed. Waiting for -; continuumatrice.exe to disappear covers both cases, and stops with a message instead of +; cockatrice.exe to disappear covers both cases, and stops with a message instead of ; ; When the payload is still there after the wait, $OldUninstallExit decides: a non-zero ; code means the uninstaller was cancelled or failed, and the install aborts saying so. @@ -552,10 +552,10 @@ ${OrIf} $OldUninstallExit == "launch-failed" ${EndIf} StrCpy $R2 0 old_uninstall_wait: -IfFileExists "$OldInstallDir\continuumatrice.exe" 0 old_uninstall_gone +IfFileExists "$OldInstallDir\cockatrice.exe" 0 old_uninstall_gone IntOp $R2 $R2 + 1 ${If} $R2 > 60 - !insertmacro CockLog "old uninstall: $OldInstallDir still holds continuumatrice.exe after 30 s (uninstaller exit=$OldUninstallExit)" + !insertmacro CockLog "old uninstall: $OldInstallDir still holds cockatrice.exe after 30 s (uninstaller exit=$OldUninstallExit)" ; The uninstaller's own account of what it skipped or why it stopped lives in its ; separate log; copy it here so the report sent from AbortInstall carries it. Call AppendUninstallerLog @@ -574,7 +574,7 @@ ${If} $R2 > 60 ; them, which NSIS Delete passes over silently. Free them and remove what is left. Call RemoveOldPayloadLeftovers ; The removal reports success only when every payload executable is gone and the - ; DLL/Plugins purge came back clean; continuumatrice.exe alone would hide a locked + ; DLL/Plugins purge came back clean; cockatrice.exe alone would hide a locked ; oracle.exe or a stale DLL the new payload is about to be written over. ${If} $OldPayloadRemoved != "1" StrCpy $AbortReason \ @@ -594,7 +594,7 @@ old_uninstall_gone: ${If} $R2 > 0 ; ExecWait returned while the old payload was still there: that uninstaller ; copied itself to %TEMP%, and the copy is still working through its file list. - ; continuumatrice.exe is gone before the rest of it, so let the copy settle before + ; cockatrice.exe is gone before the rest of it, so let the copy settle before ; the sections below start writing files it would otherwise remove. Sleep 1000 !insertmacro CockLog "old uninstall: ExecWait returned early, settled 1 s after $R2 * 500 ms" @@ -622,20 +622,20 @@ Function RemoveOldPayloadLeftovers StrCpy $INSTDIR $OldInstallDir StrCpy $R2 0 rop_loop: - SetFileAttributes "$INSTDIR\continuumatrice.exe" NORMAL - SetFileAttributes "$INSTDIR\continuumatrice-legacy-oracle.exe" NORMAL - SetFileAttributes "$INSTDIR\continuum-servatrice.exe" NORMAL - Delete "$INSTDIR\continuumatrice.exe" - Delete "$INSTDIR\continuumatrice-legacy-oracle.exe" - Delete "$INSTDIR\continuum-servatrice.exe" + SetFileAttributes "$INSTDIR\cockatrice.exe" NORMAL + SetFileAttributes "$INSTDIR\oracle.exe" NORMAL + SetFileAttributes "$INSTDIR\servatrice.exe" NORMAL + Delete "$INSTDIR\cockatrice.exe" + Delete "$INSTDIR\oracle.exe" + Delete "$INSTDIR\servatrice.exe" Delete "$INSTDIR\qt.conf" Call RetryDllPurge ; $R0 is RetryDllPurge's verdict (1 = a DLL or the Plugins directory survived). ; Every payload executable has to be gone as well: a locked oracle.exe or - ; servatrice.exe is just as fatal to the install that follows as continuumatrice.exe. - IfFileExists "$INSTDIR\continuumatrice.exe" rop_retry - IfFileExists "$INSTDIR\continuumatrice-legacy-oracle.exe" rop_retry - IfFileExists "$INSTDIR\continuum-servatrice.exe" rop_retry + ; servatrice.exe is just as fatal to the install that follows as cockatrice.exe. + IfFileExists "$INSTDIR\cockatrice.exe" rop_retry + IfFileExists "$INSTDIR\oracle.exe" rop_retry + IfFileExists "$INSTDIR\servatrice.exe" rop_retry ${If} $R0 == 0 Goto rop_done ${EndIf} @@ -664,15 +664,15 @@ Function RemoveOldPayloadLeftovers Pop $INSTDIR FunctionEnd -; Copies the old uninstaller's log ($TEMP\Continuumatrice-uninstaller.log) into this +; Copies the old uninstaller's log ($TEMP\Cockatrice-uninstaller.log) into this ; installer's log, so the report AbortInstall sends shows what the uninstaller did. ; Uninstallers older than this log simply have no file; that is logged too. Function AppendUninstallerLog ClearErrors - FileOpen $UninstLogHandle "$TEMP\Continuumatrice-uninstaller.log" r + FileOpen $UninstLogHandle "$TEMP\Cockatrice-uninstaller.log" r ${If} ${Errors} ${OrIf} $UninstLogHandle == "" - !insertmacro CockLog "uninstaller log: not found at $TEMP\Continuumatrice-uninstaller.log" + !insertmacro CockLog "uninstaller log: not found at $TEMP\Cockatrice-uninstaller.log" Return ${EndIf} !insertmacro CockLog "uninstaller log begin ----------------------------------------" @@ -712,7 +712,7 @@ FunctionEnd ; (warnAboutDataPathsInProgramDir), which only exists after the first launch of ; the new client. ; -; The 32-bit and 64-bit uninstall blocks below share $TEMP\ContinuumatriceDataBackup. +; The 32-bit and 64-bit uninstall blocks below share $TEMP\CockatriceDataBackup. ; When both are registered with different InstallLocations, backing up the second ; directory into the first one's backup would restore the first dir's files into ; the second one. BackupUserDataBeforeUninstall therefore starts over when @@ -744,7 +744,7 @@ ${EndIf} ; installer run - $DataBackupDir is empty until the first backup of this run, ; so the second call in sequence (32-bit then 64-bit uninstall) still merges. ${If} $DataBackupDir == "" - RMDir /r "$TEMP\ContinuumatriceDataBackup" + RMDir /r "$TEMP\CockatriceDataBackup" ${EndIf} ; The 32-bit and 64-bit uninstall blocks share one backup dir. When the second ; block points at a different directory, merging would restore the first dir's @@ -756,7 +756,7 @@ ${If} $DataBackupDir != "" ${AndIf} $DataBackupForDir != "" ${AndIf} $OldInstallDir != $DataBackupForDir !insertmacro CockLog "pre-uninstall backup: old dir changed ($DataBackupForDir -> $OldInstallDir), starting a fresh backup" - RMDir /r "$TEMP\ContinuumatriceDataBackup" + RMDir /r "$TEMP\CockatriceDataBackup" StrCpy $DataBackupDir "" StrCpy $DataBackupDone "" StrCpy $LooseDecksBackupDone "" @@ -764,7 +764,7 @@ ${AndIf} $OldInstallDir != $DataBackupForDir ${EndIf} ${If} ${FileExists} "$OldInstallDir\portable.dat" ${AndIf} ${FileExists} "$OldInstallDir\data" - StrCpy $DataBackupDir "$TEMP\ContinuumatriceDataBackup" + StrCpy $DataBackupDir "$TEMP\CockatriceDataBackup" StrCpy $DataBackupDone "1" !insertmacro CockLog "pre-uninstall backup: copying $OldInstallDir\data (downloaded pics excluded)" DetailPrint "Backing up portable user data before removing previous version..." @@ -805,7 +805,7 @@ ${OrIf} ${FileExists} "$OldInstallDir\*.dec" ${OrIf} ${FileExists} "$OldInstallDir\*.dek" ${OrIf} ${FileExists} "$OldInstallDir\*.mwDeck" ${If} $DataBackupDir == "" - StrCpy $DataBackupDir "$TEMP\ContinuumatriceDataBackup" + StrCpy $DataBackupDir "$TEMP\CockatriceDataBackup" ${EndIf} StrCpy $LooseDecksBackupDone "1" !insertmacro CockLog "pre-uninstall backup: copying loose deck files" @@ -839,7 +839,7 @@ backup_subdirs_loop: ${EndIf} ${If} ${FileExists} "$OldInstallDir\$DataBackupFindName\*.*" ${If} $DataBackupDir == "" - StrCpy $DataBackupDir "$TEMP\ContinuumatriceDataBackup" + StrCpy $DataBackupDir "$TEMP\CockatriceDataBackup" ${EndIf} StrCpy $InstallSubdirsBackupDone "1" !insertmacro CockLog "pre-uninstall backup: copying subdir $OldInstallDir\$DataBackupFindName" @@ -922,7 +922,7 @@ ${AndIf} $LooseDeckNoticeShown != "1" ${If} $LooseDecksBackupDone == "1" ${OrIf} $InstallSubdirsBackupDone == "1" StrCpy $LooseDeckNoticeShown "1" - MessageBox MB_OK|MB_ICONINFORMATION "Your decks or data folders were stored inside the Continuumatrice install folder ($OldInstallDir), which the previous version's uninstaller deletes.$\n$\nThey have been restored from a backup to that folder (loose deck files ending in .txt are not backed up - move those manually). If you install to a different folder, they are moved there automatically. Please move them out of the install folder afterwards (Settings -> General -> Paths) so a future uninstall cannot delete them again." + MessageBox MB_OK|MB_ICONINFORMATION "Your decks or data folders were stored inside the Cockatrice install folder ($OldInstallDir), which the previous version's uninstaller deletes.$\n$\nThey have been restored from a backup to that folder (loose deck files ending in .txt are not backed up - move those manually). If you install to a different folder, they are moved there automatically. Please move them out of the install folder afterwards (Settings -> General -> Paths) so a future uninstall cannot delete them again." ${EndIf} ${EndIf} done: @@ -989,7 +989,7 @@ done: FunctionEnd ; --- Running instance handling --- -; Continuumatrice, Oracle and Servatrice must not be running while files are +; Cockatrice, Oracle and Servatrice must not be running while files are ; replaced or deleted. A still-running process holds locks on its .exe and ; Qt runtime DLLs, so a silent upgrade could otherwise end up with a mix of ; old and new Qt DLLs next to the new executable, failing with @@ -1030,7 +1030,7 @@ FunctionEnd ; the image lookup matches on the directory a process runs from, so it does not ; widen that set. ; -; Nothing is force-stopped without the user asking for it: Continuumatrice may be holding +; Nothing is force-stopped without the user asking for it: Cockatrice may be holding ; an unsaved deck or showing a "really leave this game?" confirmation, Oracle writes ; the card database in place and can be killed mid-write, and Servatrice may be serving ; a game. A close is a request, made automatically because that is what the user started @@ -1050,14 +1050,14 @@ FunctionEnd ; What the user does instead when they say no: the prompt's other answer stops the run, so they ; are told how to get past the same wall next time. Also used by the abort dialogs, which is ; where the answer to "why did it stop" ends up. -!define AFTER_CLOSING_GUIDANCE "Answer the confirmation Continuumatrice may be showing - open it from the notification area if it is hidden there - and use Task Manager for a program that would not end." +!define AFTER_CLOSING_GUIDANCE "Answer the confirmation Cockatrice may be showing - open it from the notification area if it is hidden there - and use Task Manager for a program that would not end." ; The escape hatch for a user who would rather not force-close anything: the same release, -; downloaded and run by hand once Continuumatrice is closed. Every abort dialog shows this and +; downloaded and run by hand once Cockatrice is closed. Every abort dialog shows this and ; offers to open the page, so an update that cannot free the files still leaves the user a ; way forward that does not end in Task Manager. -!define LATEST_RELEASE_URL "https://github.com/Continuumatrice/Continuumatrice/releases/latest" -!define MANUAL_UPDATE_GUIDANCE "To update safely instead, close Continuumatrice and download the latest installer from:$\r$\n${LATEST_RELEASE_URL}$\r$\nThen run it manually." +!define LATEST_RELEASE_URL "https://github.com/Cockatrice/Cockatrice/releases/latest" +!define MANUAL_UPDATE_GUIDANCE "To update safely instead, close Cockatrice and download the latest installer from:$\r$\n${LATEST_RELEASE_URL}$\r$\nThen run it manually." ; usage: set the variable named by the second argument to the probe's listing, then call ; this. Sets $GuardPrompt to the question the guard and the recovery path ask. @@ -1082,7 +1082,7 @@ FunctionEnd ; @param cancelWord what No gives up, "installation" or "uninstallation" !macro CockEndPrompt header listing cancelWord StrCpy $GuardPrompt "${header}$\r$\n$\r$\n${listing}" - StrCpy $GuardPrompt "$GuardPrompt$\r$\n$\r$\nChoose Yes to force close the Continuumatrice, Oracle and Servatrice programs listed above. ${END_COST}$\r$\n$\r$\nChoose No to cancel the ${cancelWord}. ${AFTER_CLOSING_GUIDANCE}" + StrCpy $GuardPrompt "$GuardPrompt$\r$\n$\r$\nChoose Yes to force close the Cockatrice, Oracle and Servatrice programs listed above. ${END_COST}$\r$\n$\r$\nChoose No to cancel the ${cancelWord}. ${AFTER_CLOSING_GUIDANCE}" !macroend ; usage: like CockEndPrompt, for a MB_YESNOCANCEL question whose No checks again. @@ -1104,7 +1104,7 @@ FunctionEnd ; @param cancelWord what Cancel gives up, "installation" or "uninstallation" !macro CockEndRecheckPrompt header listing cancelWord StrCpy $GuardPrompt "${header}$\r$\n$\r$\n${listing}" - StrCpy $GuardPrompt "$GuardPrompt$\r$\n$\r$\nChoose Yes to force close the Continuumatrice, Oracle and Servatrice programs listed above. ${END_COST}$\r$\n$\r$\nChoose No to close them yourself, then check again - answer the confirmation Continuumatrice may be showing, and open it from the notification area if it is hidden there.$\r$\n$\r$\nChoose Cancel to abort the ${cancelWord}." + StrCpy $GuardPrompt "$GuardPrompt$\r$\n$\r$\nChoose Yes to force close the Cockatrice, Oracle and Servatrice programs listed above. ${END_COST}$\r$\n$\r$\nChoose No to close them yourself, then check again - answer the confirmation Cockatrice may be showing, and open it from the notification area if it is hidden there.$\r$\n$\r$\nChoose Cancel to abort the ${cancelWord}." !macroend ; Emitted once for the installer and once for the uninstaller: the uninstaller @@ -1140,19 +1140,19 @@ Function ${prefix}RunHolderProbe FunctionEnd ; usage: set $GuardDir to the directory being replaced, then call this. Asks the running -; Continuumatrice to close, and waits for it to exit. +; Cockatrice to close, and waits for it to exit. ; ; The set is what the probe names: the processes holding a file of the version being ; replaced, plus the payload programs running from the directory itself. The probe ; closes that set by pid, which is why nothing here has to match an executable path. Only -; continuumatrice.exe is asked: Oracle writes the card database in place and Servatrice has no +; cockatrice.exe is asked: Oracle writes the card database in place and Servatrice has no ; window to ask, so both are left alone here and only ended by the explicit answer. ; ; Each instance is asked to close over the local socket it listens on, which is the same ; request as clicking its close button and reaches one whose window is hidden in the ; notification area or is sitting on its own confirmation. A client from before that socket ; is asked with WM_CLOSE on its own window instead, and a visible window is brought forward -; first. Either way the close is answered, not obeyed - Continuumatrice asks before it quits while +; first. Either way the close is answered, not obeyed - Cockatrice asks before it quits while ; a card database update is running, a game is open or a deck is unsaved - and that question ; is only useful if it is in front of the installer instead of behind it. ; @@ -1176,11 +1176,11 @@ Function ${prefix}CloseHolders ; Call un. functions, so the macro's two lines are spelled with the prefix here. Push "close-holders: probe exit=$R3 [$R4]" Call ${prefix}CockLogWrite - DetailPrint "Closing Continuumatrice: $R4" + DetailPrint "Closing Cockatrice: $R4" FunctionEnd ; usage: set $GuardDir to the directory being replaced, then call this. Ends the running -; Continuumatrice, Oracle and Servatrice instances the probe names. +; Cockatrice, Oracle and Servatrice instances the probe names. ; ; This is the only place anything is stopped without being asked to stop, so it is only ; reached after the user picked it from a prompt that says what it costs: a client that is @@ -1198,7 +1198,7 @@ Function ${prefix}EndHolders Pop $R4 Push "end-holders: probe exit=$R3 [$R4]" Call ${prefix}CockLogWrite - DetailPrint "Ending Continuumatrice, Oracle and Servatrice: $R4" + DetailPrint "Ending Cockatrice, Oracle and Servatrice: $R4" FunctionEnd !macroend @@ -1207,7 +1207,7 @@ FunctionEnd ; usage: set $GuardDir to the directory to guard, then call this. ; -; Asks Continuumatrice to close once, then asks one question with one answer that can free the +; Asks Cockatrice to close once, then asks one question with one answer that can free the ; payload. The automatic close is the same request clicking the window's close button makes, ; so the common case - the client was still shutting down - never turns into a question. The ; prompt is what remains when the close cannot reach everything: Oracle writes the card @@ -1232,7 +1232,7 @@ Function GuardAppsNotRunning Call RunHolderProbe ${If} $GuardExit == "0" !insertmacro CockLog "guard: nothing is holding the payload" - DetailPrint "No running Continuumatrice, Oracle or Servatrice instance was found." + DetailPrint "No running Cockatrice, Oracle or Servatrice instance was found." Return ${EndIf} ${If} $GuardExit == "1" @@ -1258,7 +1258,7 @@ Function GuardAppsNotRunning ; update from the official download page. Without the middle answer a user who did ; not want anything killed had no way forward but to abandon the update. guard_ask: - !insertmacro CockEndRecheckPrompt "Continuumatrice, Oracle or Servatrice is still using files of the installation:" $GuardOutput "installation" + !insertmacro CockEndRecheckPrompt "Cockatrice, Oracle or Servatrice is still using files of the installation:" $GuardOutput "installation" MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION|MB_DEFBUTTON2 "$GuardPrompt" IDYES guard_end IDNO guard_recheck Goto guard_cancel guard_recheck: @@ -1272,7 +1272,7 @@ Function GuardAppsNotRunning guard_cancel: !insertmacro CockLog "guard: cancelled by user after $GuardRecheckCount recheck(s)" StrCpy $AbortReason \ - "The installation was cancelled because Continuumatrice, Oracle or Servatrice is still running and holding files of the previous version.$\r$\n$\r$\n${AFTER_CLOSING_GUIDANCE}$\r$\n$\r$\nClose them and run the installer again." + "The installation was cancelled because Cockatrice, Oracle or Servatrice is still running and holding files of the previous version.$\r$\n$\r$\n${AFTER_CLOSING_GUIDANCE}$\r$\n$\r$\nClose them and run the installer again." ; RemoveOldPayloadLeftovers runs with the pre-uninstall backup already taken; ; restoring here keeps this abort from stranding the only copy in %TEMP%. Call RestoreUserDataAfterUninstall @@ -1296,7 +1296,7 @@ Function GuardAppsNotRunning "The installation was cancelled because these programs still hold files of the previous version and could not be ended:$\r$\n$\r$\n$GuardOutput$\r$\n$\r$\n${AFTER_CLOSING_GUIDANCE}$\r$\n$\r$\nClose them and run the installer again." ${Else} StrCpy $AbortReason \ - "The installation was cancelled because, after ending Continuumatrice, Oracle and Servatrice, the installer could not check whether they still hold files of the previous version.$\r$\n$\r$\nThe check reported: $GuardOutput$\r$\n$\r$\nClose them and run the installer again." + "The installation was cancelled because, after ending Cockatrice, Oracle and Servatrice, the installer could not check whether they still hold files of the previous version.$\r$\n$\r$\nThe check reported: $GuardOutput$\r$\n$\r$\nClose them and run the installer again." ${EndIf} Call RestoreUserDataAfterUninstall Call AbortInstall @@ -1314,15 +1314,15 @@ Function GuardAppsNotRunning ; installs anyway, Abort cancels instead of writing a payload next to files ; the guard could not rule out. !insertmacro CockLog "guard: the holder probe could not answer ($GuardExit): $GuardOutput" - DetailPrint "Could not confirm whether Continuumatrice is still running: $GuardOutput" + DetailPrint "Could not confirm whether Cockatrice is still running: $GuardOutput" MessageBox MB_ABORTRETRYIGNORE|MB_ICONEXCLAMATION|MB_DEFBUTTON1 \ - "This installer could not confirm whether Continuumatrice, Oracle or Servatrice is still running.$\r$\n$\r$\nThe check reported: $GuardOutput$\r$\n$\r$\nClose them, then choose Retry to check again. Choose Ignore to install anyway, or Abort to cancel the installation." \ + "This installer could not confirm whether Cockatrice, Oracle or Servatrice is still running.$\r$\n$\r$\nThe check reported: $GuardOutput$\r$\n$\r$\nClose them, then choose Retry to check again. Choose Ignore to install anyway, or Abort to cancel the installation." \ IDRETRY guard_check IDABORT guard_unknown_abort !insertmacro CockLog "guard: continuing without an answer from the probe" Return guard_unknown_abort: StrCpy $AbortReason \ - "The installation was cancelled because it could not confirm whether Continuumatrice, Oracle or Servatrice is still running.$\r$\n$\r$\nThe check reported: $GuardOutput$\r$\n$\r$\nPlease close them and run the installer again." + "The installation was cancelled because it could not confirm whether Cockatrice, Oracle or Servatrice is still running.$\r$\n$\r$\nThe check reported: $GuardOutput$\r$\n$\r$\nPlease close them and run the installer again." ; Same reason as the cancel path above: this abort can be reached from the ; leftover-removal guard, after the backup was taken. Call RestoreUserDataAfterUninstall @@ -1391,7 +1391,7 @@ Function AbortInstall ; forge extra lines into the artifact we send. !insertmacro CockLog "sentry send: result=$SentryResult output=$SentryOutput" ${If} $SentryResult == "0" - MessageBox MB_OK|MB_ICONINFORMATION "The installer log was sent. Thank you for helping improve Continuumatrice!" + MessageBox MB_OK|MB_ICONINFORMATION "The installer log was sent. Thank you for helping improve Cockatrice!" ${Else} MessageBox MB_OK|MB_ICONEXCLAMATION "The installer log could not be sent. You can still share it manually; it was written to:$\r$\n$InstallLogPath" ${EndIf} @@ -1425,7 +1425,7 @@ Function CloseLauncher Pop $R4 Push "close-launcher: probe exit=$R3 [$R4]" Call CockLogWrite - DetailPrint "Closing Continuumatrice: $R4" + DetailPrint "Closing Cockatrice: $R4" FunctionEnd ; Waits for the application that launched this installer to exit, using the /PID= @@ -1479,7 +1479,7 @@ Function WaitForLauncherToExit ; under a second, so at ten seconds it is either flushing a card database or - far ; more often - sitting on its own "quit anyway?" confirmation with nobody looking at ; it. Silence for a minute is what makes that read as a hung installer. The full - ; grace is spent after the first question, so a user who answers Continuumatrice's + ; grace is spent after the first question, so a user who answers Cockatrice's ; confirmation is not asked again while they do it. StrCpy $WaitSlice 10000 wait_launcher_open: @@ -1570,11 +1570,11 @@ Function WaitForLauncherToExit ; exists to prevent. Yes ends the launcher, Cancel cancels, and No keeps waiting: ; the close and the bring-forward just happened above. MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION|MB_DEFBUTTON2 \ - "Continuumatrice has not finished closing, so the installation is paused rather than replace files it is still using.$\r$\n$\r$\nIt may be waiting for you to answer its own question about an unsaved deck, an open game or a card database update.$\r$\n$\r$\nChoose Yes to end it now - an unsaved deck, a running game or an unwritten setting is lost.$\r$\n$\r$\nChoose No to keep waiting, or Cancel to cancel the installation." \ + "Cockatrice has not finished closing, so the installation is paused rather than replace files it is still using.$\r$\n$\r$\nIt may be waiting for you to answer its own question about an unsaved deck, an open game or a card database update.$\r$\n$\r$\nChoose Yes to end it now - an unsaved deck, a running game or an unwritten setting is lost.$\r$\n$\r$\nChoose No to keep waiting, or Cancel to cancel the installation." \ IDYES wait_launcher_end IDNO wait_launcher_retry !insertmacro CockLog "wait-launcher: cancelled by user" StrCpy $AbortReason \ - "The application that started this update has not exited yet, so the installation was cancelled rather than replace files it is still using.$\r$\n$\r$\nPlease close Continuumatrice and run the installer again." + "The application that started this update has not exited yet, so the installation was cancelled rather than replace files it is still using.$\r$\n$\r$\nPlease close Cockatrice and run the installer again." Call AbortInstall ; Only reached through Yes or No: AbortInstall does not return. wait_launcher_end: @@ -1618,7 +1618,7 @@ Function EnsureOldInstallNotRunning ; the directory the old install lives in. Every uninstaller shipped before ; this change had no running instance guard of its own, so the old one would ; delete files while an instance of the application is still holding them. - ReadRegStr $R3 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "InstallLocation" + ReadRegStr $R3 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "InstallLocation" ${If} $R3 == "" ; no usable record of where the old install is; $INSTDIR is the default ; install directory at this point, which is where it usually is anyway @@ -1629,19 +1629,19 @@ Function EnsureOldInstallNotRunning Call GuardAppsNotRunning FunctionEnd -; Sets $R0 to 1 if $INSTDIR looks like an existing Continuumatrice install. Used to +; Sets $R0 to 1 if $INSTDIR looks like an existing Cockatrice install. Used to ; guard the stale-runtime-DLL purge so a first-time install into a pre-existing ; directory with an unrelated "Plugins" folder is never touched, while an ; install that only ever held e.g. servatrice.exe is still purged. -Function IsExistingContinuumatriceInstall +Function IsExistingCockatriceInstall StrCpy $R0 0 - ${If} ${FileExists} "$INSTDIR\continuumatrice.exe" + ${If} ${FileExists} "$INSTDIR\cockatrice.exe" StrCpy $R0 1 ${EndIf} - ${If} ${FileExists} "$INSTDIR\continuumatrice-legacy-oracle.exe" + ${If} ${FileExists} "$INSTDIR\oracle.exe" StrCpy $R0 1 ${EndIf} - ${If} ${FileExists} "$INSTDIR\continuum-servatrice.exe" + ${If} ${FileExists} "$INSTDIR\servatrice.exe" StrCpy $R0 1 ${EndIf} ${If} ${FileExists} "$INSTDIR\uninstall.exe" @@ -1652,7 +1652,7 @@ Function IsExistingContinuumatriceInstall ; executables and uninstall.exe but silently skipped the DLLs its running ; instance had locked - which is exactly the broken install the purge is ; meant to heal, and without these markers none of the files above would be - ; there to prove that it is one. Every Qt module Continuumatrice links is part of + ; there to prove that it is one. Every Qt module Cockatrice links is part of ; the payload, so match them all: probing only Qt6Core.dll would miss a ; directory where that one file happened to be deletable and the others ; were not. Qt6*.dll rather than *.dll on purpose: this function decides @@ -2028,7 +2028,7 @@ Function RecoverFromHolders ; One question, the same one the guard asks and with the same two answers. No retry loop: ; the purge has already been tried after the close, so the only thing that can still free ; the files is ending them, and the caller's abort names whatever survives that. - !insertmacro CockEndPrompt "Files of the previous version of Continuumatrice are still in use by:" $HolderOutput "installation" + !insertmacro CockEndPrompt "Files of the previous version of Cockatrice are still in use by:" $HolderOutput "installation" MessageBox MB_YESNO|MB_ICONEXCLAMATION|MB_DEFBUTTON2 "$GuardPrompt" IDYES rfh_end !insertmacro CockLog "recover-holders: cancelled by user" Return @@ -2084,14 +2084,14 @@ Function un.GuardAppsNotRunning ; Same question and the same three answers as the installer's guard: force close, ; close by hand and check again, or abort. CockEndRecheckPrompt words it, so the two ; copies promise the same thing. - !insertmacro CockEndRecheckPrompt "Continuumatrice, Oracle or Servatrice is still using files of the installation:" $GuardOutput "uninstallation" + !insertmacro CockEndRecheckPrompt "Cockatrice, Oracle or Servatrice is still using files of the installation:" $GuardOutput "uninstallation" MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION|MB_DEFBUTTON2 "$GuardPrompt" IDYES un_guard_end IDNO un_guard_recheck !insertmacro CockLogUn "uninst-guard: cancelled by user after $GuardRecheckCount recheck(s)" ; The installer's Cancel lands in AbortInstall, which explains what to do next. The ; uninstaller has no such dialog, so say the same thing here: a bare Abort would ; close the window and leave the user with no reason and no next step. MessageBox MB_OK|MB_ICONEXCLAMATION \ - "The uninstallation was cancelled because Continuumatrice, Oracle or Servatrice is still running and holding files of the installation.$\r$\n$\r$\n${AFTER_CLOSING_GUIDANCE}$\r$\n$\r$\nClose them and run the uninstaller again." + "The uninstallation was cancelled because Cockatrice, Oracle or Servatrice is still running and holding files of the installation.$\r$\n$\r$\n${AFTER_CLOSING_GUIDANCE}$\r$\n$\r$\nClose them and run the uninstaller again." SetErrorLevel 1 Abort un_guard_recheck: @@ -2122,9 +2122,9 @@ Function un.GuardAppsNotRunning ; uninstall that hits a Restart Manager that was busy, or a payload program the user has ; not closed yet, can simply be retried. !insertmacro CockLogUn "uninst-guard: the holder probe could not answer ($GuardExit): $GuardOutput" - DetailPrint "Could not confirm whether Continuumatrice is still running: $GuardOutput" + DetailPrint "Could not confirm whether Cockatrice is still running: $GuardOutput" MessageBox MB_ABORTRETRYIGNORE|MB_ICONEXCLAMATION|MB_DEFBUTTON1 \ - "This uninstaller could not confirm whether Continuumatrice, Oracle or Servatrice is still running.$\r$\n$\r$\nThe check reported: $GuardOutput$\r$\n$\r$\nClose them, then choose Retry to check again. Choose Ignore to uninstall anyway, or Abort to cancel the uninstallation." \ + "This uninstaller could not confirm whether Cockatrice, Oracle or Servatrice is still running.$\r$\n$\r$\nThe check reported: $GuardOutput$\r$\n$\r$\nClose them, then choose Retry to check again. Choose Ignore to uninstall anyway, or Abort to cancel the uninstallation." \ IDRETRY un_guard_check IDABORT un_guard_unknown_abort !insertmacro CockLogUn "uninst-guard: continuing without an answer from the probe" Return @@ -2141,7 +2141,7 @@ ${If} $ReinstallMode = 1 ${EndIf} Call SetModeDestinationFromInstdir ; If the user clicks BACK on the directory page we will remember their mode specific directory -!insertmacro MUI_HEADER_TEXT "Install Mode" "Choose how you want to install Continuumatrice." +!insertmacro MUI_HEADER_TEXT "Install Mode" "Choose how you want to install Cockatrice." nsDialogs::Create 1018 Pop $0 ${NSD_CreateLabel} 0 10u 100% 24u "Select install mode:" @@ -2182,11 +2182,11 @@ ${If} $PortableMode = 0 # uninstall 32bit version SetRegView 32 - ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "UninstallString" + ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString" StrCmp $R0 "" done32 ${If} $ReinstallMode = 0 - MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Continuumatrice must be uninstalled before installing the new one." IDOK uninst32 + MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst32 ; Abort would only skip this page and install over the old version anyway; ; the message said the old one must go first, so Cancel means cancel. !insertmacro CockLog "componentsPagePre: cancelled at the previous-version prompt" @@ -2213,7 +2213,7 @@ ${If} $PortableMode = 0 ; the user clicks Next on it, the new version is installed, and the uninstaller ; removes everything once this window closes. /S leaves no window to be behind, ; so ExecWait is waiting for something that cannot outlive it. - ReadRegStr $R1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "QuietUninstallString" + ReadRegStr $R1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString" ${If} $R1 == "" StrCpy $OldUninstallString "$OldUninstallString /S" ${Else} @@ -2233,11 +2233,11 @@ ${If} $PortableMode = 0 ${If} ${NSIS_IS_64_BIT} == 1 SetRegView 64 - ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "UninstallString" + ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString" StrCmp $R0 "" done64 ${If} $ReinstallMode = 0 - MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Continuumatrice must be uninstalled before installing the new one." IDOK uninst64 + MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "A previous version of Cockatrice must be uninstalled before installing the new one." IDOK uninst64 !insertmacro CockLog "componentsPagePre: cancelled at the previous-version prompt" Quit ${Else} @@ -2249,7 +2249,7 @@ ${If} $PortableMode = 0 Call EnsureOldInstallNotRunning StrCpy $OldInstallDir $GuardDir ; Silent, for the reason given in the 32-bit branch above. - ReadRegStr $R1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "QuietUninstallString" + ReadRegStr $R1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString" ${If} $R1 == "" StrCpy $OldUninstallString "$OldUninstallString /S" ${Else} @@ -2287,7 +2287,7 @@ SetOutPath "$INSTDIR" ; "abseil_dll.dll is either not designed to run on Windows or it contains an ; error" for the protobuf ones. So the check has to come before the first ; Delete/RMDir and not after it: an interactive install into an existing portable -; directory with continuumatrice.exe still running would otherwise delete the old +; directory with cockatrice.exe still running would otherwise delete the old ; files under its feet. ; ; It is skipped on the /R path: .onInit already ran it before @@ -2301,20 +2301,20 @@ ${If} $ReinstallMode = 0 ${EndIf} ; Measured before either purge block, because it has to describe the directory as -; it arrived here and not what a purge left of it: IsExistingContinuumatriceInstall -; looks for continuumatrice.exe, oracle.exe, servatrice.exe, uninstall.exe, Qt6*.dll +; it arrived here and not what a purge left of it: IsExistingCockatriceInstall +; looks for cockatrice.exe, oracle.exe, servatrice.exe, uninstall.exe, Qt6*.dll ; and qt.conf, and the portable purge below deletes exactly those. Taken after ; that purge it answered "not an install" as soon as the only survivor was a file ; it does not look for - a locked abseil_dll.dll or libssl-3-x64.dll, say - and ; the abort further down was then skipped for the very directory the purge had ; just failed in. ; Remembered because the stale-runtime check below must only ever fire for a -; directory this installer recognised as a Continuumatrice install. Scanning for +; directory this installer recognised as a Cockatrice install. Scanning for ; leftovers in an unrelated directory (a first-time install into a folder that ; happens to hold some Qt5 DLLs, say) would otherwise turn those files into an ; abort reason even though the payload is about to overwrite nothing of theirs. -Call IsExistingContinuumatriceInstall -!insertmacro CockLog "section: IsExistingContinuumatriceInstall=$R0" +Call IsExistingCockatriceInstall +!insertmacro CockLog "section: IsExistingCockatriceInstall=$R0" StrCpy $IsUpgrade $R0 ${If} $PortableMode = 1 @@ -2332,9 +2332,9 @@ ${AndIf} ${FileExists} "$INSTDIR\portable.dat" RMDir /r "$INSTDIR\plugins" RMDir /r "$INSTDIR\translations" Delete "$INSTDIR\uninstall.exe" - Delete "$INSTDIR\continuumatrice.exe" - Delete "$INSTDIR\continuumatrice-legacy-oracle.exe" - Delete "$INSTDIR\continuum-servatrice.exe" + Delete "$INSTDIR\cockatrice.exe" + Delete "$INSTDIR\oracle.exe" + Delete "$INSTDIR\servatrice.exe" ; Every top-level DLL, not a list of expected ones. The payload is written ; after this block, so a DLL the new build still needs is put straight back; ; what this has to guarantee is that nothing from the previous build is left @@ -2348,8 +2348,8 @@ ${AndIf} ${FileExists} "$INSTDIR\portable.dat" StrCpy $DllPurgeRan 1 Delete "$INSTDIR\qt.conf" Delete "$INSTDIR\qdebug.txt" - Delete "$INSTDIR\continuum-servatrice.sql" - Delete "$INSTDIR\continuum-servatrice.ini.example" + Delete "$INSTDIR\servatrice.sql" + Delete "$INSTDIR\servatrice.ini.example" RMDir "$INSTDIR" !insertmacro CockLogListing "after-portable-purge" "*.dll" ${EndIf} @@ -2427,7 +2427,7 @@ ${If} $StaleRuntimeAfterPurge = 1 Call LogStaleRuntimeDiagnostics ${If} $HolderFound = 1 ; On /R the previous version is already uninstalled at this point, so an abort - ; leaves the user with no working Continuumatrice. It closes once, then offers to end + ; leaves the user with no working Cockatrice. It closes once, then offers to end ; what is left; this updates $StaleRuntimeAfterPurge with the verdict after that ; close, or after the end when the user asked for one. Call RecoverFromHolders @@ -2452,7 +2452,7 @@ ${If} $StaleRuntimeAfterPurge = 1 ; so "most likely still running" is no longer a fair guess and would send the user ; to close programs that are already closed. StrCpy $AbortReason \ - "The installation could not remove all files of a previous version of Continuumatrice, and was cancelled rather than leave a mismatched installation behind." + "The installation could not remove all files of a previous version of Cockatrice, and was cancelled rather than leave a mismatched installation behind." ${If} $HolderFound = 1 StrCpy $AbortReason "$AbortReason$\r$\n$\r$\nStill using the install directory:$\r$\n$HolderOutput$\r$\n$\r$\nClose them and run the installer again." ${ElseIf} $HolderExit == "0" @@ -2461,7 +2461,7 @@ ${If} $StaleRuntimeAfterPurge = 1 ; The check itself failed, so "no running program was found" would be a claim this ; installer cannot make. Name the failure instead, and leave the per-file codes to ; the log, where they already are. - StrCpy $AbortReason "$AbortReason$\r$\n$\r$\nThe check for a running program could not be completed: $HolderOutput$\r$\n$\r$\nClose Continuumatrice, Oracle and Servatrice and run the installer again; the installer log records what Windows reported for each file." + StrCpy $AbortReason "$AbortReason$\r$\n$\r$\nThe check for a running program could not be completed: $HolderOutput$\r$\n$\r$\nClose Cockatrice, Oracle and Servatrice and run the installer again; the installer log records what Windows reported for each file." ${EndIf} ; "Run the installer again" is not enough on an in-app update: the old version ; was uninstalled before the purge, so point at the installer that was downloaded. @@ -2494,37 +2494,39 @@ ${If} $PortableMode = 0 ; dumps on user machines that nothing consumes. ; https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps - ; Older installers did register continuumatrice.exe above (and the removed + ; Older installers did register cockatrice.exe above (and the removed ; MiniDumpWithFullMemory writer filled that folder), and that key survives this ; install, so WER would keep writing full dumps. Remove it, and clear the ; legacy dumps nothing consumes anymore. $LOCALAPPDATA resolves per shell context: ; under "all" it points at ProgramData, so switch to "current" for the per-user ; dumps folder and back afterwards; the registry delete works in either context. - DeleteRegKey HKLM "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\continuumatrice.exe" + DeleteRegKey HKLM "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\cockatrice.exe" SetShellVarContext current - RMDir /r "$LOCALAPPDATA\CrashDumps\Continuumatrice" + RMDir /r "$LOCALAPPDATA\CrashDumps\Cockatrice" SetShellVarContext all - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "DisplayIcon" "$INSTDIR\continuumatrice.exe" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "DisplayName" "Continuumatrice" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "DisplayVersion" "@CPACK_PACKAGE_VERSION_MAJOR@.@CPACK_PACKAGE_VERSION_MINOR@.@CPACK_PACKAGE_VERSION_PATCH@" - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "EstimatedSize" "$0" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "InstallLocation" "$INSTDIR" - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "NoModify" "1" - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "NoRepair" "1" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "Publisher" "Continuum Universes Studio" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "VersionMajor" "@CPACK_PACKAGE_VERSION_MAJOR@" - WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" "VersionMinor" "@CPACK_PACKAGE_VERSION_MINOR@" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayIcon" "$INSTDIR\cockatrice.exe" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayName" "Cockatrice" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayVersion" "@CPACK_PACKAGE_VERSION_MAJOR@.@CPACK_PACKAGE_VERSION_MINOR@.@CPACK_PACKAGE_VERSION_PATCH@" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "EstimatedSize" "$0" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "InstallLocation" "$INSTDIR" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "NoModify" "1" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "NoRepair" "1" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "Publisher" "Cockatrice team" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "VersionMajor" "@CPACK_PACKAGE_VERSION_MAJOR@" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "VersionMinor" "@CPACK_PACKAGE_VERSION_MINOR@" - ; Continuumatrice deliberately does not register .cod, which belongs to - ; an independent Cockatrice MTG installation. + ; --- Register .cod file type --- + WriteRegStr HKCR ".cod" "" "Cockatrice" + WriteRegStr HKCR "Cockatrice" "" "Cockatrice Deck File" + WriteRegStr HKCR "Cockatrice\shell\open\command" "" '"$INSTDIR\cockatrice.exe" "%1"' ; --- Register custom URI protocol --- - WriteRegStr HKCR "continuumatrice" "" "URL: Continuumatrice Protocol" - WriteRegStr HKCR "continuumatrice" "URL Protocol" "" - WriteRegStr HKCR "continuumatrice\shell\open\command" "" '"$INSTDIR\continuumatrice.exe" "%1"' + WriteRegStr HKCR "cockatrice" "" "URL: Cockatrice Protocol" + WriteRegStr HKCR "cockatrice" "URL Protocol" "" + WriteRegStr HKCR "cockatrice\shell\open\command" "" '"$INSTDIR\cockatrice.exe" "%1"' IfFileExists "$INSTDIR\vc_redist.x86.exe" VcRedist86Exists PastVcRedist86Check VcRedist86Exists: @@ -2550,8 +2552,8 @@ ${Else} ${EndIf} ${If} $ReinstallMode = 1 - IfFileExists "$INSTDIR\continuumatrice.exe" 0 +2 - Exec '"$INSTDIR\continuumatrice.exe"' + IfFileExists "$INSTDIR\cockatrice.exe" 0 +2 + Exec '"$INSTDIR\cockatrice.exe"' ${EndIf} !insertmacro CockLog "section: application install complete" @@ -2560,8 +2562,10 @@ SectionEnd Section "Start menu item" SecStartMenu ${If} $PortableMode = 0 SetShellVarContext all - createDirectory "$SMPROGRAMS\Continuumatrice" - createShortCut "$SMPROGRAMS\Continuumatrice\Continuumatrice.lnk" "$INSTDIR\continuumatrice.exe" + createDirectory "$SMPROGRAMS\Cockatrice" + createShortCut "$SMPROGRAMS\Cockatrice\Cockatrice.lnk" "$INSTDIR\cockatrice.exe" + createShortCut "$SMPROGRAMS\Cockatrice\Oracle.lnk" "$INSTDIR\oracle.exe" + createShortCut "$SMPROGRAMS\Cockatrice\Servatrice.lnk" "$INSTDIR\servatrice.exe" ${EndIf} SectionEnd @@ -2596,41 +2600,48 @@ Section "un.Application" UnSecApplication ; Succeeds only when no foreign files remain. RMDir "$INSTDIR" - RMDir /r "$SMPROGRAMS\Continuumatrice" + RMDir /r "$SMPROGRAMS\Cockatrice" - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Continuumatrice" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" ; Leftover WER LocalDumps key from pre-Sentry installers (see the installer note ; above): removing it here covers machines that uninstall without reinstalling. ; The legacy full-memory dumps go with it; nothing consumes them anymore. Same shell ; context note as the installer: the per-user dumps folder needs "current". - DeleteRegKey HKLM "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\continuumatrice.exe" + DeleteRegKey HKLM "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\cockatrice.exe" SetShellVarContext current - RMDir /r "$LOCALAPPDATA\CrashDumps\Continuumatrice" + RMDir /r "$LOCALAPPDATA\CrashDumps\Cockatrice" SetShellVarContext all - ; Remove only the Continuumatrice URI scheme. Never touch MTG .cod. + ; Only remove the file/protocol associations if we registered them (i.e. the + ; install was not portable) and .cod is still owned by Cockatrice, so we don't + ; clobber a .cod association installed by another application. ${If} $R1 == 0 - DeleteRegKey HKCR "continuumatrice" + ReadRegStr $0 HKCR ".cod" "" + ${If} $0 == "Cockatrice" + DeleteRegKey HKCR ".cod" + DeleteRegKey HKCR "Cockatrice" + DeleteRegKey HKCR "cockatrice" + ${EndIf} ${EndIf} SectionEnd ; unselected because it is /o Section /o "un.Configurations, decks, cards, pics" UnSecConfiguration SetShellVarContext current - DeleteRegKey HKCU "Software\Continuumatrice" + DeleteRegKey HKCU "Software\Cockatrice" - RMDir /r "$LOCALAPPDATA\Continuumatrice" + RMDir /r "$LOCALAPPDATA\Cockatrice" SectionEnd -LangString DESC_SecApplication ${LANG_ENGLISH} "Continuumatrice program files" -LangString DESC_SecStartMenu ${LANG_ENGLISH} "Create start menu items for Continuumatrice and Oracle." +LangString DESC_SecApplication ${LANG_ENGLISH} "Cockatrice program files" +LangString DESC_SecStartMenu ${LANG_ENGLISH} "Create start menu items for Cockatrice and Oracle." !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${SecApplication} $(DESC_SecApplication) !insertmacro MUI_DESCRIPTION_TEXT ${SecStartMenu} $(DESC_SecStartMenu) !insertmacro MUI_FUNCTION_DESCRIPTION_END -LangString DESC_UnSecApplication ${LANG_ENGLISH} "Continuumatrice program files and start menu items" +LangString DESC_UnSecApplication ${LANG_ENGLISH} "Cockatrice program files and start menu items" LangString DESC_UnSecConfiguration ${LANG_ENGLISH} "Configurations, decks, card database, pictures" !insertmacro MUI_UNFUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${UnSecApplication} $(DESC_UnSecApplication) diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index c3c5d708b..642d998a0 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -2,7 +2,7 @@ # # provides the cockatrice binary -project(Continuumatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") +project(Cockatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") set(cockatrice_SOURCES ${VERSION_STRING_CPP} @@ -107,7 +107,6 @@ set(cockatrice_SOURCES src/game_graphics/tally/stats_tally.cpp src/game_graphics/tally/subtype_tally.cpp src/game_graphics/tally/tally.cpp - src/game_graphics/player/essence_pool_item.cpp src/game_graphics/player/player_area.cpp src/game_graphics/player/player_dialogs.cpp src/game_graphics/player/player_graphics_item.cpp @@ -158,7 +157,6 @@ set(cockatrice_SOURCES src/interface/palette_editor/palette_editor_dialog.cpp src/interface/widgets/cards/additional_info/color_identity_widget.cpp src/interface/widgets/cards/additional_info/deck_color_identity.cpp - src/interface/widgets/cards/additional_info/essence_filter_picker.cpp src/interface/widgets/cards/additional_info/mana_cost_widget.cpp src/interface/widgets/cards/additional_info/mana_symbol_widget.cpp src/interface/widgets/cards/art_crop_attribution.cpp @@ -458,7 +456,7 @@ set(cockatrice_SOURCES add_subdirectory(sounds) add_subdirectory(themes) configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/resources/config/qtlogging.ini ${CMAKE_CURRENT_BINARY_DIR}/qtlogging.ini COPYONLY + ${CMAKE_SOURCE_DIR}/cockatrice/resources/config/qtlogging.ini ${CMAKE_BINARY_DIR}/cockatrice/qtlogging.ini COPYONLY ) set(cockatrice_RESOURCES cockatrice.qrc) @@ -511,12 +509,12 @@ set(MIMEDIR CACHE STRING "mime file destination" ) -set(COCKATRICE_MAC_QM_INSTALL_DIR "continuumatrice.app/Contents/Resources/translations") -set(COCKATRICE_UNIX_QM_INSTALL_DIR "share/continuumatrice/translations") +set(COCKATRICE_MAC_QM_INSTALL_DIR "cockatrice.app/Contents/Resources/translations") +set(COCKATRICE_UNIX_QM_INSTALL_DIR "share/cockatrice/translations") set(COCKATRICE_WIN32_QM_INSTALL_DIR "translations") qt_add_executable( - continuumatrice + cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} @@ -525,12 +523,10 @@ qt_add_executable( MANUAL_FINALIZATION ) -# Product target is Continuumatrice on every platform. The source directory and -# upstream libcockatrice_* library names remain internal implementation details. -target_precompile_headers(continuumatrice PRIVATE "${CMAKE_SOURCE_DIR}/cmake/pch/qtwidgets_pch.h") +target_precompile_headers(cockatrice PRIVATE "${CMAKE_SOURCE_DIR}/cmake/pch/qtwidgets_pch.h") qt_add_shaders( - continuumatrice + cockatrice "onboarding_shaders" PREFIX "/onboarding/shaders" @@ -542,7 +538,7 @@ qt_add_shaders( ) qt_add_resources( - continuumatrice + cockatrice "onboarding_qml" PREFIX "/onboarding/qml" @@ -553,7 +549,7 @@ qt_add_resources( ) target_link_libraries( - continuumatrice + cockatrice PUBLIC libcockatrice_card libcockatrice_deck_list libcockatrice_filters @@ -566,28 +562,27 @@ target_link_libraries( ${COCKATRICE_QT_MODULES} ) -cockatrice_target_enable_crash_reporting(continuumatrice) +cockatrice_target_enable_crash_reporting(cockatrice) if(UNIX) if(APPLE) set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}") - set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.continuum-universes.continuumatrice") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.cockatrice.${PROJECT_NAME}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}") set(MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) - set_target_properties(continuumatrice PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist) + set_target_properties(cockatrice PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist) - install(TARGETS continuumatrice BUNDLE DESTINATION ./) + install(TARGETS cockatrice BUNDLE DESTINATION ./) else() # Assume linux - install(TARGETS continuumatrice RUNTIME DESTINATION bin/) - # Install using distinct names so the normal MTG client keeps its .desktop, - # icon and .cod / cockatrice:// file associations unchanged. - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/continuumatrice.svg - DESTINATION ${ICONDIR}/hicolor/scalable/apps) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/continuumatrice.desktop DESTINATION ${DESKTOPDIR}) + install(TARGETS cockatrice RUNTIME DESTINATION bin/) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.png DESTINATION ${ICONDIR}/hicolor/48x48/apps) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cockatrice.desktop DESTINATION ${DESKTOPDIR}) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cockatrice-cod.xml DESTINATION ${MIMEDIR}) # Refresh the freedesktop databases so the file associations and scheme # handler register without requiring the user to run them manually. The @@ -606,13 +601,13 @@ if(UNIX) endif() endif() elseif(WIN32) - install(TARGETS continuumatrice RUNTIME DESTINATION ./) + install(TARGETS cockatrice RUNTIME DESTINATION ./) endif() if(APPLE) # these needs to be relative to CMAKE_INSTALL_PREFIX - set(plugin_dest_dir continuumatrice.app/Contents/Plugins) - set(qtconf_dest_dir continuumatrice.app/Contents/Resources) + set(plugin_dest_dir cockatrice.app/Contents/Plugins) + set(qtconf_dest_dir cockatrice.app/Contents/Resources) # Qt plugins: audio, iconengines, imageformats, multimedia, platforms, printsupport, styles, tls install( @@ -633,7 +628,7 @@ if(APPLE) ) install( - DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + DIRECTORY "${CMAKE_BINARY_DIR}/cockatrice/" DESTINATION ${qtconf_dest_dir}/ FILES_MATCHING PATTERN "*.ini" @@ -655,7 +650,7 @@ Data = Resources\") \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\") set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/continuumatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\") + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/cockatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\") " COMPONENT Runtime ) @@ -667,7 +662,7 @@ if(WIN32) set(qtconf_dest_dir .) install( - DIRECTORY "$/" + DIRECTORY "$/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll" @@ -701,7 +696,7 @@ if(WIN32) endforeach() install( - DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + DIRECTORY "${CMAKE_BINARY_DIR}/cockatrice/" DESTINATION ./ FILES_MATCHING PATTERN "*.ini" @@ -766,7 +761,7 @@ Data = Resources\") \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dll\") set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/continuumatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\") + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Cockatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\") " COMPONENT Runtime ) @@ -776,7 +771,7 @@ if(Qt6LinguistTools_FOUND) #Qt6 Translations happen after the executable is built up if(UPDATE_TRANSLATIONS) qt_add_translations( - continuumatrice + cockatrice TS_FILES ${cockatrice_TS} SOURCES @@ -785,7 +780,7 @@ if(Qt6LinguistTools_FOUND) cockatrice_QM ) else() - qt_add_translations(continuumatrice TS_FILES ${cockatrice_TS} QM_FILES_OUTPUT_VARIABLE cockatrice_QM) + qt_add_translations(cockatrice TS_FILES ${cockatrice_TS} QM_FILES_OUTPUT_VARIABLE cockatrice_QM) endif() if(UNIX) @@ -806,13 +801,9 @@ if(Qt6LinguistTools_FOUND) # error on Xcode. Older Qt versions already add this dependency themselves. foreach(_lrelease_target Cockatrice_lrelease cockatrice_lrelease) if(TARGET ${_lrelease_target}) - add_dependencies(continuumatrice ${_lrelease_target}) + add_dependencies(cockatrice ${_lrelease_target}) endif() endforeach() endif() -qt_finalize_target(continuumatrice) - -# Compatibility with existing CI scripts calling --target cockatrice. No -# executable named cockatrice is created by this target. -add_custom_target(cockatrice DEPENDS continuumatrice) +qt_finalize_target(cockatrice) diff --git a/cockatrice/cockatrice.qrc b/cockatrice/cockatrice.qrc index 35bfe88f4..e421553ab 100644 --- a/cockatrice/cockatrice.qrc +++ b/cockatrice/cockatrice.qrc @@ -2,8 +2,6 @@ resources/cardback.svg resources/cockatrice.svg - resources/continuumatrice.svg - resources/continuumatrice-empty-carddatabase.xml resources/cockatrice-logo-white.svg resources/hand.svg resources/hr.jpg @@ -60,22 +58,12 @@ resources/icons/update.png resources/icons/view.svg - resources/icons/essence/M.svg - resources/icons/essence/S.svg - resources/icons/essence/O.svg - resources/icons/essence/G.svg - resources/icons/essence/E.svg - resources/icons/essence/A.svg - resources/icons/essence/Q.svg - resources/icons/essence/C.svg - resources/icons/essence/J.svg - resources/icons/essence/B.svg - resources/icons/essence/R.svg - resources/icons/essence/L.svg - resources/icons/essence/V.svg - resources/icons/essence/T.svg - resources/icons/essence/P.svg - resources/icons/essence/N.svg + resources/icons/mana/B.svg + resources/icons/mana/C.svg + resources/icons/mana/G.svg + resources/icons/mana/R.svg + resources/icons/mana/U.svg + resources/icons/mana/W.svg resources/backgrounds/home.jpg resources/backgrounds/home-dark.jpg diff --git a/cockatrice/continuumatrice.desktop b/cockatrice/continuumatrice.desktop deleted file mode 100644 index d3919eb15..000000000 --- a/cockatrice/continuumatrice.desktop +++ /dev/null @@ -1,12 +0,0 @@ -[Desktop Entry] -Version=1.0 -Type=Application -Name=Continuumatrice -GenericName=Continuum Universes Card Game Client -Comment=Play Continuum Universes card games -Exec=continuumatrice %U -Icon=continuumatrice -Terminal=false -Categories=Game;CardGame; -StartupNotify=true -MimeType=x-scheme-handler/continuumatrice; diff --git a/cockatrice/resources/cardback.svg b/cockatrice/resources/cardback.svg index 4312d1f19..f92fbb621 100644 --- a/cockatrice/resources/cardback.svg +++ b/cockatrice/resources/cardback.svg @@ -2,24 +2,24 @@ + width="240" + height="340" + id="svg2" + sodipodi:version="0.32" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.0" + inkscape:export-xdpi="90" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/" + inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)" + sodipodi:docname="cardback.svg" + inkscape:export-ydpi="90" + inkscape:export-filename="cardback.svg" + xmlns="http://www.w3.org/2000/svg"> + + + + + + + inkscape:deskcolor="#505050"/> @@ -123,377 +177,137 @@ + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/cockatrice/resources/continuumatrice-empty-carddatabase.xml b/cockatrice/resources/continuumatrice-empty-carddatabase.xml deleted file mode 100644 index 3df8722c3..000000000 --- a/cockatrice/resources/continuumatrice-empty-carddatabase.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/cockatrice/resources/continuumatrice.svg b/cockatrice/resources/continuumatrice.svg deleted file mode 100644 index b0f334e12..000000000 --- a/cockatrice/resources/continuumatrice.svg +++ /dev/null @@ -1,394 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cockatrice/resources/hand.svg b/cockatrice/resources/hand.svg index 7ef625585..2ae2619c3 100644 --- a/cockatrice/resources/hand.svg +++ b/cockatrice/resources/hand.svg @@ -2,24 +2,24 @@ + inkscape:export-ydpi="90"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inkscape:window-width="1600" + inkscape:window-height="1178" + inkscape:window-x="1912" + inkscape:window-y="227" + inkscape:window-maximized="1" /> @@ -3144,1131 +3438,781 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + transform="translate(0,160)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cockatrice/resources/icons/essence/A.svg b/cockatrice/resources/icons/essence/A.svg deleted file mode 100644 index 9ef525741..000000000 --- a/cockatrice/resources/icons/essence/A.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/B.svg b/cockatrice/resources/icons/essence/B.svg deleted file mode 100644 index b5080c6e3..000000000 --- a/cockatrice/resources/icons/essence/B.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/C.svg b/cockatrice/resources/icons/essence/C.svg deleted file mode 100644 index c286757a5..000000000 --- a/cockatrice/resources/icons/essence/C.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/E.svg b/cockatrice/resources/icons/essence/E.svg deleted file mode 100644 index 82b078f32..000000000 --- a/cockatrice/resources/icons/essence/E.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/G.svg b/cockatrice/resources/icons/essence/G.svg deleted file mode 100644 index 440f84c0f..000000000 --- a/cockatrice/resources/icons/essence/G.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/J.svg b/cockatrice/resources/icons/essence/J.svg deleted file mode 100644 index a6027d25c..000000000 --- a/cockatrice/resources/icons/essence/J.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/L.svg b/cockatrice/resources/icons/essence/L.svg deleted file mode 100644 index 0b6781a23..000000000 --- a/cockatrice/resources/icons/essence/L.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/M.svg b/cockatrice/resources/icons/essence/M.svg deleted file mode 100644 index 2660a1fdd..000000000 --- a/cockatrice/resources/icons/essence/M.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/N.svg b/cockatrice/resources/icons/essence/N.svg deleted file mode 100644 index cdfde390b..000000000 --- a/cockatrice/resources/icons/essence/N.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/O.svg b/cockatrice/resources/icons/essence/O.svg deleted file mode 100644 index 2f82563b4..000000000 --- a/cockatrice/resources/icons/essence/O.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/P.svg b/cockatrice/resources/icons/essence/P.svg deleted file mode 100644 index 3981768ae..000000000 --- a/cockatrice/resources/icons/essence/P.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/Q.svg b/cockatrice/resources/icons/essence/Q.svg deleted file mode 100644 index b98f04077..000000000 --- a/cockatrice/resources/icons/essence/Q.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/R.svg b/cockatrice/resources/icons/essence/R.svg deleted file mode 100644 index fd3807714..000000000 --- a/cockatrice/resources/icons/essence/R.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/S.svg b/cockatrice/resources/icons/essence/S.svg deleted file mode 100644 index 1bb5714d5..000000000 --- a/cockatrice/resources/icons/essence/S.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/T.svg b/cockatrice/resources/icons/essence/T.svg deleted file mode 100644 index 4d9569ca9..000000000 --- a/cockatrice/resources/icons/essence/T.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/essence/V.svg b/cockatrice/resources/icons/essence/V.svg deleted file mode 100644 index dc391121c..000000000 --- a/cockatrice/resources/icons/essence/V.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/A.png b/cockatrice/resources/icons/mana/A.png deleted file mode 100644 index 26929856d858d32d65e831c4f8d845cbf1be14dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39184 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%0ACxL{mxT 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 zco \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/B.png b/cockatrice/resources/icons/mana/B.png deleted file mode 100644 index 1262ea91301aac44f15e8c7961555af9152ccd7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11983 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 WeLnHM)}92vk4B9jxqrAJ?tcMQnjF3W diff --git a/cockatrice/resources/icons/mana/B.svg b/cockatrice/resources/icons/mana/B.svg index b5080c6e3..d7e360054 100644 --- a/cockatrice/resources/icons/mana/B.svg +++ b/cockatrice/resources/icons/mana/B.svg @@ -1 +1,47 @@ - \ No newline at end of file + + + + + + + + + diff --git a/cockatrice/resources/icons/mana/C.png b/cockatrice/resources/icons/mana/C.png deleted file mode 100644 index f89acd1fd5c9b3d06fbbcb036ba557d2134718ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16929 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 \ No newline at end of file + + + + + + + + + + + + + diff --git a/cockatrice/resources/icons/mana/E.png b/cockatrice/resources/icons/mana/E.png deleted file mode 100644 index 1c02f62f7d3ffc25f3a105a75275ab577e7850f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27874 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%0D{#y@9;(jx{jmLI~>*&(b>A2kYyIc^b% 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 b{KCoaqWtECzS=wj50Ra% \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/G.png b/cockatrice/resources/icons/mana/G.png deleted file mode 100644 index d2d8442f4a1780907d4b6cb2fd915ed01d9f4873..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13973 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 \ No newline at end of file + + + + + + + + + diff --git a/cockatrice/resources/icons/mana/J.png b/cockatrice/resources/icons/mana/J.png deleted file mode 100644 index 8396dc9bc89943814978ac6058dc749c5e8bda52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11518 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 diff --git a/cockatrice/resources/icons/mana/J.svg b/cockatrice/resources/icons/mana/J.svg deleted file mode 100644 index a6027d25c..000000000 --- a/cockatrice/resources/icons/mana/J.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/L.png b/cockatrice/resources/icons/mana/L.png deleted file mode 100644 index 08d80d821dd856fbe401a999e417715921cd75f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12580 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 diff --git a/cockatrice/resources/icons/mana/L.svg b/cockatrice/resources/icons/mana/L.svg deleted file mode 100644 index 0b6781a23..000000000 --- a/cockatrice/resources/icons/mana/L.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/M.png b/cockatrice/resources/icons/mana/M.png deleted file mode 100644 index c75492f8d99540b7a0b1898591a0ba524743fb4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30485 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 JzMFdKe*lbb!8HH? diff --git a/cockatrice/resources/icons/mana/M.svg b/cockatrice/resources/icons/mana/M.svg deleted file mode 100644 index 2660a1fdd..000000000 --- a/cockatrice/resources/icons/mana/M.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/N.png b/cockatrice/resources/icons/mana/N.png deleted file mode 100644 index 0406e1ff50e83c528414264906d80f5d6af6560d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30029 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 A&;S4c diff --git a/cockatrice/resources/icons/mana/N.svg b/cockatrice/resources/icons/mana/N.svg deleted file mode 100644 index cdfde390b..000000000 --- a/cockatrice/resources/icons/mana/N.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/O.png b/cockatrice/resources/icons/mana/O.png deleted file mode 100644 index 428c04c503e7603bf668ff804857ec5fbcc90c76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10264 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 F{(t5Xdw~D| diff --git a/cockatrice/resources/icons/mana/O.svg b/cockatrice/resources/icons/mana/O.svg deleted file mode 100644 index 2f82563b4..000000000 --- a/cockatrice/resources/icons/mana/O.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/P.png b/cockatrice/resources/icons/mana/P.png deleted file mode 100644 index 7f992c0bedc696f03681d0b66e4f5bb6a9687e51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13859 zcmZvC30#cb_xO2cscBM8i9$58g=|S^F;6K%$np|Wp@gWY6!kVwn~>-wk*%@3vb{>S ztPPQgHfu#vibB$&P1DT(+{yd>{eJ)7@A-WC^vr$EJ^MNL+;i_Pmw7X_`2G0+pf$_M zVIcqqUpc^2!++v|qoeU36}WJwJyn)xCMKVJ?B?15T-m27U&Y1W!Zl6{=K^dW4#30! z{K6ro4j_0uz*kQItHS^W0mp7Hu)z-+0Zz*T0mcp?e^lt-qOCZn9yDvNqxx4(7H7zW zd6%v&LzFPf!ESNr+xqvmzHVhF##IhWXvmwFHO}qaIX?+qDLMHdV7%!>-mxj;ztc%4 zB@>$C1iu7hoTu9v^eGLhJadvxky;q`8fZAmW&G|@NBG0XyJ@#`j!536y}n`g`dP%X zaYY9PuKn!XzVMIltzF-h?qQ<=<{TZUbNQ0JP20TO6-y{cu0Ev!QGd4Sb3yR%#xNq3 ze0>fjJ8`GWEGG#7^(APuvjc_&5EDK-%?M};fHlpO2QDZ;-Sj+tHkcyN&y$|JDL7I& zi9sAL@ZB5+1l}}0;xh^WwHGx_-+6)xNu)9VjDR3vGIU}W@QrqEy7%BK1uF`vl}NCW7v#B5=~!MNNfOK_UOh z8YhDJY04q!St1pBgnnR5>k??XQeBo}PuLOuWSg>yOuTM*3IzJ0h|yCqTC5}KrTU@( zQ&tZixZ=W7;TD7AZ#+UDK1J|!uq8Z@z4%v`Hj$TZRqIGcIdh0E9~l3JuTw{MiR`~9 zWS4L$27T&3Aky5sY64UNUvhhoR^jGUnRzoAe7oZo{8=euwS2$E1CqC3=Kn2Lm) zLScJIAP67mMs)<~nGD_p1Sj`^{AV(Ps00%zC|cuxOh&XxL}aA=zb7MptF@bf5OCSordCsS} z#r2W#lR%o_%px=uRF6zC5rAU%NZTXRziiCy8G}kb^esR`@Kq#K@}W);Bx%h%J+uR@u<$RYLX;HqB84!FeD*Mmq9DUAz%=TB zgg6-y#gx?}EzBm;K12{b)fY|GtRCQ=Avv4OLVVrms%)Iq1DuufulwYCatZs79_ypD z#BeHUi5`-E8xEQ5`GOYgVc&&C2mR|lTG67cE*(f5vFN{~MJTkLiyA6428C21LOq~n z5fSPU6@nnBAs5d71wx3^8o$4|uuzHUHUB3UGjEK+WC1nvqa9zAcdLVB?%Cg}`v0U? zqV&W=>_vdV=K@013xYJ%{LUs?eHH|^8v~Pcm}nMtq|z+YIRDa#`Dd#fExMnR zWBayV5NsEx`^i%iv~|n`Dk?nXqc4xYt?#JKw(scJth-k88)Z=)p)7jguz*E~GL=QU z@25KFq#s&nO&^$c&_Fplrg7mmYPX7ymyM23O&}?J;5qw_t`DPM_Lgvj6RnehH#jD4 z#G0ZTY=J#5TXgEWtHnZI`$d7`<`zoQd%5ZD4_;YaA4Z0BQBHZ$5ucv(7FLAhd6=Si ztuJZGv$)%8CW4f=AO3l$^I@ch3vulS9~_^cBfY(!V}h~4g{W){0MrGCIH^aYI%D?! zTWsTobG>?0uj~~rO4;FITdM=l>KB=@X2Whwy@|ElMyrHcl$Y`vyoK4%>UE){O4sk?nqT|Y+gq+OaL2-UWu8D+$dXCCQ zr$#j(3v|a!#B<4}ZfVu0s>s;i=0~UDm$kTZl3?iicX}iQvb5QqFNZGB3{h^f0*SPH zpQEI}R#Lx%g)*RLS>3S=TPY;ecuLF$F0W3=RHT^7eK>hGk}9^10~7g7Kf?B6R*Iwa zgb^1a*13Los7E}azF+|`b2(7?ODm&SQ9_1=*69K^WW1=zGbw&^f5GK7BZq^~e*R2! zku!x;r!g#M=&h~~mvsVt0nV?85$rf{K33{J5K+2hr92md?8%)uPdZl@&9ku?9c8S` z1t^x^e`-?vIbvbqbc$&lhmJTKBP8kR5D;jnWBmKtBGM&@RIBG#>C4j~)p7iMBH@^z z7Yu96GHz=#g+}&*toFbW95y{tUs+mBK-iey@crA%J|m|8n$WnWc*1E0T!x^2%BM2C``a2_; zjpRoFuH9qID09}BvMZeXude;bJAW2~Cad{jMPBH$4K)QEj2Hv4!5<YmJA6kl8NjOIWQiB^%s6+wIVz`VO1@r?OHD+TZl0*!2 zE*ZqCNa!me=zz-%b4s0|jiO9-fesTEBqq*G(`Gre5jY{xZDa3EH0;OW!u?Pb&MXQm z3sN!QgOZ*xyoD*lOHUu}ufvf{4UbZk#SLGN)+F6K(3JdoXKIh4=(P*T9OSis+APAp z!EbE|gZp2z`6C%W^;1J4Q#!3hgIsYj@ap#)K*H-k5&1rQ($6|sdI-Nr&WB$OF|Ww% zzj-_*l)sGh58KP)fZvU5-&E#OGz&7DUN%Zi04D#cvhE_0fsZM?@E&^Bc}Ew@xsAx_ z!n9OJIwIaSJE~F>|F1XF-_t9G1?Jal`|E&PwZxOA5(gI;4324R`RfUyNQ}jKQe<7` z@XG6%UWer6y?Y0)Ac`)BczRctXUa84R4Zbl=;P(xwaG<02>KcJRAsal(4WH`m}~(ueaL>x90@;{`KH=FA7v=C~pgpjQ>HR8m~pGIHL5$Fm3he6Nleww{WpxK)=k zCkAW4z7#wyow%HnwM}Gp(NfBR(q2fBTYSNycSU)3THoPV(R{N#?Z~`qf7raCz!?MA zA%8uB$d;Ib1m9r|6&s%MiQkCS$XkqcHa* zp^+PPW<&BPUC2N*%Zo#9kCTARBJ|8i)s&+;{Rh-P|PVaRQ7pev5K~1+lq(7`{l-e^ULjE9%d35U{z~&3~ zxL9$?63dhyzcY>f*96Ne?xp66`AIhDpU>+J#;`21+YkCI^a=!-1!iTJ$$Rd>@3;1u7%D*jB zhjkAXm)W6=h2cThX>>p32+jF3s~cDO4Frkyhqeoi=t=hU6B{)NGG5g;2iJl-Zmv9V zeL4A_VEFXYN(s*e*Fxbx#ZC#FJardYgj~KC2)JW!N3aYXBYjV!sLC5z-2XZOdf^j@0 z{p*^^E%L|qwOb1Gs48W>Ye(Fx!>kSzOcE>#FKOu7A9vu3hoVL7KFS#N z5ZS!WVOn*Ah_;z_aI*4;a@;Xn>YB0QqM&e3_w*Oel>DFuz^O@AHamZ%`cFyOp;Tn? z3}(#6lER6xtm%){)6VAs=32_u%~gqW5#6N2EEiG~>gQdwsoLt^sy-WY2d)E-VWC#q zfxUTNemD753B+BvnED|rc#*d*TBFK?!Xk^Fg%TZKitwf^lWX6H0KCd;d5PFIkGkEB zvccEd&FFB#=5LFyOfAJAM=R0P?oHFM5Mh3yxAC|C!Si`2N{YBQ%3Sj(evc7jHN*vw zz8{pzC(9F`;q_ChGuu~&em8z7680Vi@{^R%e};0`N?e}2a?DATccdBJQ>mK~*st=Z z2;~^@Q@*l6&Yt5|v^($dV|hGZiG~{@XD7L_LXi{Ky!(yw>#Iaa;&ETV@{YD0l)5*X zt=u(DZmg+f@?AG#L~m>;TlY0stl}f|7S?b_yCTfBe$>7YHC>1i(_0rK%SXFf7j}&B zt9>Yv9?zWirTD!VN{{tc&hjks(PgaC!^%E;^(GOAtE&qUO~r-2@ZEE@>fbC~+BPuX zeDuHKGc2#N4WwRtC_iWXQEA2tG_9|HDSjcC1f0_^6Fo)a;zB#L439xWtBFK z;4)hV?#->bc`I1x*WPu|5zg5y*`xx#8<$o+KBPKT_Q&9uzz}7m_9qK&VnOy!H1p_9 zYt2~a>zmA263)3@(}6-qsWO(pvTKH@6X|>RyC-(EXig-S_$FJ$mk!3|GWYCblf+&# z6_mx0PPH5qS0K-ezoVodmApPP@fxx!kPq%}I|n4ho)OI_>>uPYUZF#uKXGht zPX4wCFIk63a3{%_Js#`WYC=V<_*SH}$##oTzP$KQEdQrMDLS1KT~qS)%Q~ZJ`G2kj z=GU-yZm5@x6Drwn2lsNg^W-Xz{<-it3$Y}YJeJ||5O8|v3aor6JC9=ZW^X1)%aQzLsH0T%=hZ(Y?9zP{&Xe(Nn}tV$jFN#C1p z2E?UjKBYVZ^DO$H4x>aMSH;jmSpD|Hu)qGn{>8PLyf0JoZK`!>t+9Q@W0JDJ|5258 za-ur(ZrJmMDB_vIm121N(T1wqm~HUqedNAa;&Xa^s3_!}G1KNInqO&`%^!C!fvngT z&k*)@TiX_0TuDwE*iV_*2Oh^qI6UYOVWOh{>GF+-g74Jn!qRsN2lOfMUYR!N_7c0l zY*^rOp!UG}(6BGt#PD&l@-(kwW;S1BH3W9+wuVMEiG_XsO`A>!p0Fg-CWzE3amTJ@ zJFSm`a+;PAlQv`@mu@(gDhVA5lnQTZtJw7f7dG)K@i){k3M@qakBF%CFWL$W1IaLB#@wLjx()_+}FwX~9 z{nB{u9m?mgNoy z=Ho4E3NBxDVQ~=vUHhvy!SuAH+iQcaFlx_6qLFp4(hq6IgD(1wV_4lS2UU-y^XLi| ztb1JL^hFzYxF?kAz-;M08yr`7mE;{nMUN)z<6H5jwJNr+t^QDTrx@EsIK&^v#xjx~ zf!nt+NP^GY4pkWMe4T!%VLDnX&NB0A4-%C{))fTqtHhcWOP3ET_Ep-Q;4-yJRD^JE zuY1S8ilz{1lnJUDRS>HBM^hPts+BZf@7(S1!tX0M2?vn%%xYDnCurLNX zybR+IAAPhCK15Miw>9=FQhhzv?;QtRu7%n90&K!&@MbKCqTx_spX z{L!37zV~n6RD`QIdChDm#Vf-Cq4pr;ROqu}fM4=Jg}Rsb53h|#eeLY6=Yq3uZ{0hY z!j{dw6O#~TJ|7rgcdhp*B(mkg@zH0~f9_FES0g%$bUXg;9P0P{+{`SZU&(pNYF(iT ziaY@8Ppq*&y$v=5xHLCK8_{^(otn8B3m4-R-{i(qL13C!kyna))D7Dnn9wa@uMW1{ zRUxso-B)aP4QGc07@*sSeAcx>PcfxH+Ny^y4n+<_AZwoe(#Q4iOy=>Ck4`SS zxHbqQ#drI*PzeVlHI=J$g%glptvN72h)Xy@GlT<|x1r{wJ9yys4Hr-*K^L-esZ9n@ zfdMR6`*Nb)1a}PV@8P0DgW}XkqXy4C(i!>2F-`2sMiHZCW79A0mN!W%8!|)8 z?@y}63{)p8yFNGhJgHSA>ycTR<;tg|dq<2^Im3A{sa?^7`0tNlfM5p)gJacz$=(3Z zwil;Ae3rx3q+;LRUK9OA3wzWZSWaZS<2nu=?@Cs1OdoCXXO-TtfiGQ6jl%jr-r#O6 z8an{tA2IJb%fl@iF+^A*)G9DW?|&>^RdXu>=>l}tEgdZ1QlqW<#Lrj}+jzDZERH$j zY8Nig;!UiLSnHeqtNY4W=JubhL9{=va2s>#I>qLz&rB7iK8+XHhc1<^n1gGYvUQo- z?J|Rk!>wpeq?ORp&aFFvp381f92k{$kd3xrhS^T-qtKO8%$-duSj?m7q-d#=(=c3a zd~jDVG`~(X5E-}mnN$TKhmFWk!r8uyLMb8rq4st%0CN96V+5E%DFMSlc6>81a_&>MF8ncn3< zJ;SOn!ny|>7C`yW6Es2sylB~$i!mycM;DD%xJm zB5krwegl~)-HNWo;^`^N%U)izuj{_I(2cP5CZyqwi2nIGUhp~o@$$#9GglC{bB|N< zH=}us>BjD+=pgTrgw^GDXDl8p4?klF%ikUH!z~Evs!qDGsl?YYonM7+YT=~X$_OGm zuiHzoZfGe5^ecpt-R+gN)pi>?>m2{#upwiU2Eq5SwXL~*a&Mcd!3o&^fCZsmYt-oI zX~+9DZnU^=h$fPZ#*LKQ*h_3E2orf6p29|`mD@&ivx_tD9oSsDlytqBt&=^WI5zg1 zpodg|Ei2211(!XL5uwj~qhIQ{TS0x+_=HVU~7)W%AzWF|6;52AW?>kh8t2M`rLNaK81_}9y7Q0_e?WjP0-&U}BU`vd~g zLJo~R{2!nRf#-C=%>P4hZxvSx8Y zVDm4L%3oEm=!FGC2O^XBph|$qye_XL@0E$_RrdbfvslO9kfemh=U*^h|4WE=M%f4f3Do8q zV9q6&!oRo|^~W)xoox}WR0b<;;Is1nN z4Os=3=L{o1$6MK8_cb7XvUVY{-dN`zPE47RxKl{UQ4&oKT!aJ>nD2=~D4xC-BL}5* zzh!nhzR(CYV2KBiM1q{lEkKzbHYa0`Lj%34*>p-$C4Mp8d&d}(oHTlj5%|=fAmLH; z=U+3F$eo1af-Spjk39-pu@KME%mIj@)R3y|ua2ZQCW6rWM3*`jz+-c|j~2+{Y85z5 zy7X@Ux29C0`hiVIgE)MNIkL^|YEhp5)2aYBpJag{ob{~o3^>Xfhk9ks1*t^2Lu!1C(LqKsa+ zmTPT#v@w@t5hQ~CV!0*T<5n(=RxPsn)ZkDHV78}^CVtMb-1w9f%-J5XnEhhmO=jzp z9k1*d7`RXk0t`aBBemL(k|o5X{MASfuB4H-(F%H{^|p9WfG!wInaV`J{^)p~MHW5N zXU|qxGH1Eayd~Y>;l-186eurKR6eQyFWg`le}1DO-1-Nvs$2!^x$;|uHIxKb{HV=3*m!bXL{kR&L@tCM~}i%0hLMLe=~9{_C{>=>~E0$;0#6N-i<4jI#;V792`c$ z>XvYGE&uDRp$&G~w_C4YhPkEI{gl@2qy=y)#MZ)gz}%d$BK<_eAxgRzIloEVpSnZ+ zp}wle1*VVPy|qxogihOC>*I(2c&J+T&pnARSrktvwOtStVn51W8&BW%2Sro~>5tWC zRIEbE#AcmGh1jQ|x2XD#RmPqNSe`$;?t0Gx=J0n-vd^E>X+z2!*x!VWsnBLzGd*q5 zQH1@p71KM^Nw}r7lw)gxLC+K@?0SYCl12)FT3$2*dC(tKWh|58f(WZyBN~cDw@cFP zJ3cvesK01`%#qgq(BME_)N}FbfbRW0AGzdo-4C=x|5W>W^iNCO(kl>rD#30IedSWl>A2IW=B4Bag?ax(yL{DFvuRHrA zvvu1KtR}sy)*&}S353(zB)?AY_)y(nDldk8JuT6a*@ z3qm=?+1oy0Ly8`8qoL#E-Ao6dSME_DLW_uhx;GSJi2&C=Hk~I;pWhdQP&mb_mv@je#kJ?$T&bJP-z=rgQBl)5S> zz)RK% z(FA7=R&GdiEmxWuhsOM5I8P;0(E5Xy{E)KiyE}7E6Bjv8u=q~+*8t`|KU0U>)-CNz z3O#rvtrN}dys2nat!3z+R$6>=>oNG|(5AO_qJqZTB4(#1)FSKK3dgDVd*pR|BblG& z8@-B%WiCPXFc1ls!|=?qM>tg}S>p~9kRTpB9PWOP zhmGhLAS-k4gJ**Su``UH1S>rQ+f$GHw8w*t zQ@m%c_9vq57(o=Ve&SS|>q!b*m4X|3e8MH_A1lS2WBBO{o-$P#3wM2M&Fy@TcIPQG z{!;Ho4u%r=*zJ}_mUAp`U_IZAJ!`@i<9aXEx;?ev-c<8nD)&F-{WH}n6qP7tN%rRO zR3+THv7}eRG+XKt(@JPGrm5_HW6_SS13Pe1BW?{J6NI*sp;qWTjDw=2qGu)DBrA%d-!D-SPZrz3_C2x_RZ(on1O}=ce za80h5*J^Bps|;?3u<_VXmEDBk>o(bk~17c*OkY5Bn)>QxEMDejK)l5bEbwgB|u zy5H<$xH}j0L)@F!53wd%xi(posE~L7h(DcIHK!8~{~4lOmU9ca<}<^ms^jIOn;D!- zvN{oc?CsNhq6@qE^WJy+O#1Ni5=DPmJ{qu)p#NO!r@VS94;#dc;q{b*WP9aRSSKQB z(&l`MA;ijk$4e&0bJZ&2kS3ll=W&)4?T>I&tSKgI#uFmNxC+Px+oS3FI=UqTywr?SD6@A zj|Lkxtkben_hARm$!=*%=$&igRp3-G9Rpvh6a^FWpQ7`eye{PGxdCuDp30PQuE80M|VC_>}nFlRHnw;try*#a(lFO}hi55%y3ckpcziu4&HxjKVH%&+>$Q;RS%2qDapiy#!2 zP?u4t+!uWUlPBTmdw+c_YhAWdo+Q)~&@;a5w#_+$&^u#F6qE!#$+@4h)+)W5EvaKk z*C2L{t?w1;PRsdauL&(qg%O@F5NerT^3?e%%9TZ0`$-%?;X|XZ&=Mkk|XqQJ>L`Q?v1SD`S8Z9YY;-_HC zjuB-&YCmw^(X{mFkT@mk>D)p5y1Mn--xyW&o5j$P15nodRRsMOsg!?h)HCbPq>K`c zfeEE8zp_`O+7jiHZ0_7ubvS{a=uK6cVVaqP0?U#J1=-8Ln(%TJRXhd73HqK>-XD*= zkuQ+#$8jPW?k6M~*qM`z-Z#%y>0yGF^>V_H*Z;g_l@|IaeUa=0Kxj=YdM;KqOLsi( z5RSIZ$F?$DyJ06-O{cN|y6PBoQN|{44MjzZ7Wz^h%U;<}?k0<{b{rklJ^CC#HAHV^#Z@6W` z!&5#O^I-Qie|ffP`VM5y%a+UNsDa~hSKNU#5#@9SbP%`Ovu-EyH#Ov!h}oEiteV>n z+@>J?l%aDLaip2k^8cYg7KHj{Hs)hLRsymExjZytvdV{G7H$DN+RsltLasil!DP1) zX7+>ILhgY;cu<~Ok^)&+Wr4i$J+@*J-|zQfRcfmQd?DLZUdUl|kQ3$-8S%^f(WzV= z+HFh{r)eqPNkQhq`(c#~iO&hYDNW5Hv?}-Ic#57{Xsgr_9vunc#g|z0^AZa8ljeAP z3?EV*3|)oWMexckeYArF%|&o=9siR@k1Pp|2f>F)t~=i2F@`aXkY#faF#JX*ZV%B{ zDhnjT`Cy*;4XulM`Co@73_GU zWjI4d;pfbQEU7v%Yszf=il;eip6FRrFIqDRbJ8*`=BI5Cc>h86Np^;Ma z)Cip860>wVHm7A+>EeS_H(4yh7xM$ZC9APN&4tZfHv@_8NKQ|}tp zao>;x@N1~6NXHf~M2TlOL&qt6lUe+hhhAf`7SWkSv!n*DAoY(FXymdP z6TFr}TP}dgcs0OlDQ#+~d^`xi2k{I2i)Oe}>k1#5Pd{KY*}s~|6&RVe zIK{4M?OLY`iMX4u)WoAA8S8hR zCX`TBy=REQbgs7s^rO{G4~wP^juG^PL8<;!P6qSsxFhzpcYCuygn{gtz7Jn|q42iY zPLP9sDC?z)&J{+wWn$2O?1N}Wk9Jcs~n&{P%$*2L z`dG<-gayj=^5$ej6k}WUNgSKlDWOz7wt4b~IebKg61c$3W zG>X2zzcPD;ztNK_4@avY(TcNX+I~^Ooq>Z5T;AI0XX^W(3f2AY!ZR#4a^q7s?PuPM z@=e?5Zl@Wm{m(kzNxO!Bw*B#ATZ80>@<(20#FqV8$Pp8U&02hDV5Tluh$m7-{C%}P zu>L*wbrFXZU+nPc?=>K;OIv*cq_KU4CT~jm$Kj-f90+*8-O#QGE*cD#?)ntCg`&u| zrYy(;w#(4edMFQzLcBu*wrA7|5WS@DY%}cSPhDj$Vmtj|;3XnztI}bCJwh2O&V0h- zCQr6}b5mjMV=7?WF1*QKj@!IA4x*M)rq7m?_yen9pdGSd#C+A-8m3JJ6!9i^=+O&T z7XLX_2(Ux6Y#R#Kxmfzg}>f0s6K879mw>#?#!aDM<^Z&_0( zcq?8R!egH6C>uEtY6p8}Li=_ECugzc)se`Vi{bnLU;uiLe8lK#R90wjxc-$_sT`+& z-w^RP#vSR%VKibu<|`h*yQS&x=yfHN-VH{%${)-GsisCi-029^P?k^hh7KO&H%5J2 zkGro)d!d)O(p}-|IRjVhySIXP-?OG!bH*zo!w?Fep_0Q7*rAvTn)K6{GUkXsR@$8|6`q|r4-SBf#(Y-(hZ!S2 zW{u0B9ZP0)VO$HSW2~JZ|L2`UxYc;YgAR2ZVuP+^`SG1oTAuUA|Dq_sCJg7?p^)ES zS@^YWqOZxkJ>v0l{2D{PmTbMbYu_DPb!rD;lUJ!PIqv?aVeaF<0}lrz1QPx=Z+zr| z=aqTdZN1#8jUjp7`RJ_`W?}td`O}%K%ae4Q$~xjKJ&HDJV8+0M$f9@E3&J~-yCzZ< zt?JdrI5-&0-_rw>Dn&AeV2>|`-BLL^Wg|;VfBc@Cx9470-Y=N9MjjoeDFKD#WZ{IW zS+MSW_XE?yC&A$&I&Ie!>+y0sykLFhD-R?_GrKDa2K-o}7+`6o`{d6YW0-xq&hD{| z1_ZC0!)rV&S{(fTN^css^g#E@y79*0*!FzmUSb@G0m*&C%Ht6#?Fd-RuoG! zYy`(xmG<}pR7MwKq9Ce-)tF9yq8Fg+pyhD@L%0XxzYQ2F03q=*utS7XddTAdHZo=qYX9;>rl|O_;iCScjVo6zF~TJV zUd_M4S)O+Q!0bSX0-b@zTYHV_2>b|O56R4QFU)9UAOIO59uQ24Nv2@f1xV~) z+=-E@kxNrpfXt=7&`eAm!Ga$hgrT9s_{0 zLXt34GM3$V#-<;X+ \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/Q.png b/cockatrice/resources/icons/mana/Q.png deleted file mode 100644 index cbdfa6496a6ab78c435ddb27a1337e70346349cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13703 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 diff --git a/cockatrice/resources/icons/mana/Q.svg b/cockatrice/resources/icons/mana/Q.svg deleted file mode 100644 index b98f04077..000000000 --- a/cockatrice/resources/icons/mana/Q.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/R.png b/cockatrice/resources/icons/mana/R.png deleted file mode 100644 index 1daf26d20f5655e3a39744bba33e942a7411ca4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11146 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ℜ>H>cgLLF7 z4#{u?TIa+@)wEm`Ks9a>;tQsCFkoO6PG92N_=TrsWYusoK9F+est8(#{qCT>?@O^a z7@@9{i@b-CnbvfrqCikY!3&V(1a}rXtIZYaCql}Y2HO>+v+q;jZ|_rp`=ptV*xnKk zN8=xoiHK52?M-m^_EBm1T+C@?GAN1@@Y77+lv(a{=Aj$Zk!D9ZSr@ERZEye2vMRNb Z \ No newline at end of file + + + + + + + + + diff --git a/cockatrice/resources/icons/mana/S.png b/cockatrice/resources/icons/mana/S.png deleted file mode 100644 index dac7037e0f0049e2449f5ee66a398940af5773e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31593 zcmZs@cT|(v6E}WC1Qa6dDkwpUh0sw90)mtvD@u<_k&YTvs!A_Hq9`i%gkFQ9bU~_g zY-s4xq!$UjO0UW92EXs`ynnps>>l0cdG6e~)9%b?Ci@s~bapqFAQu2&x9++BE t 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 diff --git a/cockatrice/resources/icons/mana/S.svg b/cockatrice/resources/icons/mana/S.svg deleted file mode 100644 index 1bb5714d5..000000000 --- a/cockatrice/resources/icons/mana/S.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/T.png b/cockatrice/resources/icons/mana/T.png deleted file mode 100644 index 16974f906c3cabdf5966b24c1415d3b472e73b91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10757 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 J?t$h-{{uh7>P-Lu diff --git a/cockatrice/resources/icons/mana/T.svg b/cockatrice/resources/icons/mana/T.svg deleted file mode 100644 index 4d9569ca9..000000000 --- a/cockatrice/resources/icons/mana/T.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/icons/mana/V.png b/cockatrice/resources/icons/mana/V.png deleted file mode 100644 index a0e8a104790ef633ac4dcc884be5df03efed729a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40452 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%0DKyR@iWJSvv^HC=gLBEO+N@Z=OV~(&>Zfd6hDJ zJLd0-ss49LZQ(7X0+r>W!~UW@Pg0S&FwxFQaae(aS01tHD>{l?C&Ql3Ck2HWrcI-1|2iqAi \ No newline at end of file diff --git a/cockatrice/resources/piles/banishment.svg b/cockatrice/resources/piles/banishment.svg deleted file mode 100644 index edecc79af..000000000 --- a/cockatrice/resources/piles/banishment.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/piles/graveyard.svg b/cockatrice/resources/piles/graveyard.svg deleted file mode 100644 index cd7174662..000000000 --- a/cockatrice/resources/piles/graveyard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cockatrice/resources/piles/limbo.svg b/cockatrice/resources/piles/limbo.svg index cd2811878..288b195cc 100644 --- a/cockatrice/resources/piles/limbo.svg +++ b/cockatrice/resources/piles/limbo.svg @@ -1 +1,7 @@ - \ No newline at end of file + + + + + + + diff --git a/cockatrice/resources/piles/oblivion.svg b/cockatrice/resources/piles/oblivion.svg index 3bd1af5a4..96e832b8e 100644 --- a/cockatrice/resources/piles/oblivion.svg +++ b/cockatrice/resources/piles/oblivion.svg @@ -1 +1,7 @@ - \ No newline at end of file + + + + + + + diff --git a/cockatrice/resources/piles/refuge.svg b/cockatrice/resources/piles/refuge.svg index 5784f02e8..8c42ef953 100644 --- a/cockatrice/resources/piles/refuge.svg +++ b/cockatrice/resources/piles/refuge.svg @@ -1 +1,6 @@ - \ No newline at end of file + + + + + + diff --git a/cockatrice/sounds/CMakeLists.txt b/cockatrice/sounds/CMakeLists.txt index 8492837c1..f81714b05 100644 --- a/cockatrice/sounds/CMakeLists.txt +++ b/cockatrice/sounds/CMakeLists.txt @@ -6,10 +6,10 @@ set(defsounds Default Legacy) if(UNIX) if(APPLE) - install(DIRECTORY ${defsounds} DESTINATION continuumatrice.app/Contents/Resources/sounds/) + install(DIRECTORY ${defsounds} DESTINATION Cockatrice.app/Contents/Resources/sounds/) else() # Assume linux - install(DIRECTORY ${defsounds} DESTINATION share/continuumatrice/sounds/) + install(DIRECTORY ${defsounds} DESTINATION share/cockatrice/sounds/) endif() elseif(WIN32) install(DIRECTORY ${defsounds} DESTINATION sounds/) diff --git a/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp b/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp index afd21e428..c702f91f1 100644 --- a/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp +++ b/cockatrice/src/client/network/connection_controller/remote_connection_controller.cpp @@ -13,7 +13,6 @@ #include "libcockatrice/protocol/pb/event_server_shutdown.pb.h" #include -#include #include #include #include @@ -218,7 +217,7 @@ void ConnectionController::onServerShutdownEvent(const Event_ServerShutdown &eve "games will be lost.\nReason for shutdown: %1", "", event.minutes()) .arg(QString::fromStdString(event.reason()))); - serverShutdownMessageBox.setIconPixmap(themePixmap(QStringLiteral("continuumatrice")).scaled(64, 64)); + serverShutdownMessageBox.setIconPixmap(themePixmap(QStringLiteral("cockatrice")).scaled(64, 64)); serverShutdownMessageBox.setText(tr("Scheduled server shutdown")); serverShutdownMessageBox.setWindowModality(Qt::ApplicationModal); serverShutdownMessageBox.setVisible(true); @@ -334,7 +333,7 @@ void ConnectionController::onLoginError(int r, case Response::RespContextError: { QMessageBox::critical(dialogParent, tr("Error"), - tr("An internal error has occurred, please close and reopen Continuumatrice before " + tr("An internal error has occurred, please close and reopen Cockatrice before " "trying again.\nIf the error persists, ensure you are running the latest " "version of the software and if needed contact the software developers.")); break; @@ -490,14 +489,14 @@ void ConnectionController::onProtocolVersionMismatch(int localVersion, int remot { if (localVersion > remoteVersion) { QMessageBox::critical(dialogParent, tr("Error"), - tr("You are trying to connect to an obsolete server. Please downgrade your Continuumatrice " + tr("You are trying to connect to an obsolete server. Please downgrade your Cockatrice " "version or connect to a suitable server.\n" "Local version is %1, remote version is %2.") .arg(localVersion) .arg(remoteVersion)); } else { QMessageBox::critical(dialogParent, tr("Error"), - tr("Your Continuumatrice client is obsolete. Please update your Continuumatrice version.\n" + tr("Your Cockatrice client is obsolete. Please update your Cockatrice version.\n" "Local version is %1, remote version is %2.") .arg(localVersion) .arg(remoteVersion)); @@ -632,7 +631,7 @@ void ConnectionController::onPromptForgotPasswordChallenge() void ConnectionController::updateWindowTitle() { - const QString appName = QCoreApplication::applicationName(); + const QString appName = QStringLiteral("Cockatrice"); QString title; switch (remoteClient->getStatus()) { @@ -654,7 +653,7 @@ void ConnectionController::updateWindowTitle() break; } case StatusLoggedIn: { - title = appName + " - " + remoteClient->getUserName() + "@" + remoteClient->peerName(); + title = remoteClient->getUserName() + "@" + remoteClient->peerName(); break; } case StatusRequestingForgotPassword: diff --git a/cockatrice/src/client/network/update/client/release_channel.cpp b/cockatrice/src/client/network/update/client/release_channel.cpp index a63bd0341..ce80a2a76 100644 --- a/cockatrice/src/client/network/update/client/release_channel.cpp +++ b/cockatrice/src/client/network/update/client/release_channel.cpp @@ -30,13 +30,13 @@ #include #endif -#define STABLERELEASE_URL "https://api.github.com/repos/Continuum-Universes-Studio/Continuumatrice/releases/latest" -#define STABLEMANUALDOWNLOAD_URL "https://github.com/Continuum-Universes-Studio/Continuumatrice/releases/latest" -#define STABLETAG_URL "https://api.github.com/repos/Continuum-Universes-Studio/Continuumatrice/git/refs/tags/" +#define STABLERELEASE_URL "https://api.github.com/repos/Cockatrice/Cockatrice/releases/latest" +#define STABLEMANUALDOWNLOAD_URL "https://github.com/Cockatrice/Cockatrice/releases/latest" +#define STABLETAG_URL "https://api.github.com/repos/Cockatrice/Cockatrice/git/refs/tags/" -#define BETARELEASE_URL "https://api.github.com/repos/Continuum-Universes-Studio/Continuumatrice/releases" -#define BETAMANUALDOWNLOAD_URL "https://github.com/Continuum-Universes-Studio/Continuumatrice/releases/" -#define BETARELEASE_CHANGESURL "https://github.com/Continuum-Universes-Studio/Continuumatrice/compare/%1...%2" +#define BETARELEASE_URL "https://api.github.com/repos/Cockatrice/Cockatrice/releases" +#define BETAMANUALDOWNLOAD_URL "https://github.com/Cockatrice/Cockatrice/releases/" +#define BETARELEASE_CHANGESURL "https://github.com/Cockatrice/Cockatrice/compare/%1...%2" #define GIT_SHORT_HASH_LEN 7 diff --git a/cockatrice/src/client/settings/cache_settings.cpp b/cockatrice/src/client/settings/cache_settings.cpp index 993140d48..08eccf1e4 100644 --- a/cockatrice/src/client/settings/cache_settings.cpp +++ b/cockatrice/src/client/settings/cache_settings.cpp @@ -256,17 +256,6 @@ void SettingsCache::loadPaths() computePath("customSets", dataPath + "/customsets/"); computeFilePath("cardDatabase", dataPath + "/cards.xml"); - - // On a new Continuumatrice profile create an empty, valid XML v4 database. - // Never download MTG data or copy the user's Cockatrice card database. - const QString initialCardDb = dataPath + "/cards.xml"; - if (!QFile::exists(initialCardDb)) { - if (!QFile::copy(QStringLiteral(":/resources/continuumatrice-empty-carddatabase.xml"), initialCardDb)) { - qCWarning(SettingsCacheLog) << "Could not create Continuumatrice starter database:" << initialCardDb; - } else { - QFile::setPermissions(initialCardDb, QFile::ReadOwner | QFile::WriteOwner); - } - } computeFilePath("tokenDatabase", dataPath + "/tokens.xml"); computeFilePath("spoilerDatabase", dataPath + "/spoiler.xml"); } diff --git a/cockatrice/src/client/sound_engine.cpp b/cockatrice/src/client/sound_engine.cpp index 7a06ddb12..4ac142cf4 100644 --- a/cockatrice/src/client/sound_engine.cpp +++ b/cockatrice/src/client/sound_engine.cpp @@ -118,7 +118,7 @@ QStringMap &SoundEngine::getAvailableThemes() #elif defined(Q_OS_WIN) "/sounds" #else // linux - "/../share/continuumatrice/sounds" + "/../share/cockatrice/sounds" #endif ); diff --git a/cockatrice/src/game_graphics/player/essence_pool_item.cpp b/cockatrice/src/game_graphics/player/essence_pool_item.cpp deleted file mode 100644 index fcd8441c3..000000000 --- a/cockatrice/src/game_graphics/player/essence_pool_item.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "essence_pool_item.h" - -#include "../../interface/pixel_map_generator.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -EssencePoolItem::EssencePoolItem(PlayerLogic *_player, QGraphicsItem *parent) - : QGraphicsObject(parent), player(_player) -{ - setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton); - setAcceptHoverEvents(true); - setZValue(5); - - connect(player, &PlayerLogic::counterAdded, this, &EssencePoolItem::attachCounter); - connect(player, &PlayerLogic::counterRemoved, this, &EssencePoolItem::detachCounter); - - // Reconnect and late view construction must paint the full snapshot, - // even when the counterAdded events happened before this item existed. - for (CounterState *state : player->getCounters()) { - attachCounter(state); - } - setToolTip(tr("Essence pool — left click +1, right click -1, Shift+left to set")); -} - -QRectF EssencePoolItem::boundingRect() const -{ - return QRectF(0, 0, Width, Height); -} - -void EssencePoolItem::attachCounter(CounterState *state) -{ - if (!state) { - return; - } - const QChar code = EssenceCounter::codeFromName(state->getName()); - if (code.isNull()) { - return; - } - counters.insert(code, state); - connect(state, &CounterState::valueChanged, this, [this](int, int, bool) { update(); }); - update(); -} - -void EssencePoolItem::detachCounter(int id) -{ - for (auto it = counters.begin(); it != counters.end();) { - if (it.value() && it.value()->getId() == id) { - it = counters.erase(it); - } else { - ++it; - } - } - update(); -} - -CounterState *EssencePoolItem::stateFor(QChar code) const -{ - return counters.value(code).data(); -} - -QChar EssencePoolItem::codeAt(const QPointF &position) const -{ - const qreal x = position.x() - Padding; - const qreal y = position.y() - HeaderHeight; - if (x < 0 || y < 0 || x >= Columns * CellWidth || y >= 4 * CellHeight) { - return QChar(); - } - const int col = static_cast(x) / CellWidth; - const int row = static_cast(y) / CellHeight; - const int index = row * Columns + col; - const QString &codes = EssenceCounter::symbols(); - return index >= 0 && index < codes.size() ? codes.at(index) : QChar(); -} - -void EssencePoolItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) -{ - painter->save(); - painter->setRenderHint(QPainter::Antialiasing, true); - painter->setPen(QPen(QColor(118, 125, 158), 1)); - painter->setBrush(QColor(16, 19, 31, 232)); - painter->drawRoundedRect(boundingRect().adjusted(0.5, 0.5, -0.5, -0.5), 7, 7); - - QFont titleFont = painter->font(); - titleFont.setBold(true); - titleFont.setPixelSize(15); - painter->setFont(titleFont); - painter->setPen(QColor(236, 238, 248)); - painter->drawText(QRectF(8, 3, Width - 16, 24), Qt::AlignCenter, tr("ESSENCE POOL")); - - QFont countFont = painter->font(); - countFont.setBold(true); - countFont.setPixelSize(14); - painter->setFont(countFont); - - const QString &codes = EssenceCounter::symbols(); - for (int i = 0; i < codes.size(); ++i) { - const QChar code = codes.at(i); - const int col = i % Columns; - const int row = i / Columns; - const qreal x = Padding + col * CellWidth; - const qreal y = HeaderHeight + row * CellHeight; - const QRectF tile(x, y, CellWidth - 3, CellHeight - 3); - - painter->setPen(QPen(QColor(85, 91, 119), 1)); - painter->setBrush(QColor(43, 47, 67, 228)); - painter->drawRoundedRect(tile, 5, 5); - - const QPixmap icon = ManaSymbolPixmapGenerator::generatePixmap(QString(code), QSize(32, 32)); - if (!icon.isNull()) { - painter->drawPixmap(QPointF(x + 9, y + 2), icon); - } else { - painter->setPen(Qt::white); - painter->drawText(QRectF(x, y + 2, CellWidth - 3, 30), Qt::AlignCenter, QString(code)); - } - - CounterState *state = stateFor(code); - const int count = state ? state->getValue() : 0; - painter->setPen(count > 0 ? QColor(255, 244, 191) : QColor(178, 182, 200)); - painter->drawText(QRectF(x, y + 33, CellWidth - 3, 18), - Qt::AlignCenter, QString::number(count)); - } - - painter->restore(); -} - -void EssencePoolItem::mousePressEvent(QGraphicsSceneMouseEvent *event) -{ - const QChar code = codeAt(event->pos()); - CounterState *state = stateFor(code); - if (!state || !player->getPlayerInfo()->getLocalOrJudge()) { - event->ignore(); - return; - } - PlayerActions *actions = player->getPlayerActions(); - if (event->button() == Qt::LeftButton && (event->modifiers() & Qt::ShiftModifier)) { - const QString label = code == QChar('N') ? tr("Colorless (N)") : - code == QChar('J') ? tr("Jasper (J)") : tr("Essence %1").arg(code); - bool accepted = false; - QWidget *parent = event->widget() ? event->widget()->window() : nullptr; - const int value = QInputDialog::getInt(parent, tr("Set Essence"), - tr("Available %1:").arg(label), - state->getValue(), 0, std::numeric_limits::max(), 1, &accepted); - if (accepted) { - actions->actSetCounter(state->getId(), value); - } - } else if (event->button() == Qt::LeftButton) { - actions->actIncrementCounter(state->getId(), 1); - } else if (event->button() == Qt::RightButton) { - if (state->getValue() > 0) { - actions->actIncrementCounter(state->getId(), -1); - } - } else { - event->ignore(); - return; - } - event->accept(); -} - -void EssencePoolItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) -{ - const QChar code = codeAt(event->pos()); - CounterState *state = stateFor(code); - if (!state) { - setToolTip(tr("Essence pool — all types start at zero")); - return; - } - const QString name = code == QChar('N') ? tr("Colorless Essence") : - code == QChar('J') ? tr("Jasper Essence") : tr("Essence %1").arg(code); - setToolTip(tr("%1: %2 available\nLeft +1 | Right -1 | Shift+Left: Set") - .arg(name).arg(state->getValue())); -} diff --git a/cockatrice/src/game_graphics/player/essence_pool_item.h b/cockatrice/src/game_graphics/player/essence_pool_item.h deleted file mode 100644 index 1b02b740d..000000000 --- a/cockatrice/src/game_graphics/player/essence_pool_item.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef CONTINUUMATRICE_ESSENCE_POOL_ITEM_H -#define CONTINUUMATRICE_ESSENCE_POOL_ITEM_H - -#include -#include -#include - -class CounterState; -class PlayerLogic; -class QGraphicsSceneMouseEvent; -class QGraphicsSceneHoverEvent; -class QPainter; -class QStyleOptionGraphicsItem; -class QWidget; - -/** - * An interactive, sixteen-type Essence resource pool attached to a player - * battlefield. All values are read from the existing server-owned CounterState - * objects; this graphics item does NOT own mutable match resource balances. - * - * Left click +1, right click -1, Shift+left to set an exact nonnegative amount. - * Only the owning player or a game judge can modify the pool. - */ -class EssencePoolItem final : public QGraphicsObject -{ - Q_OBJECT - -public: - static constexpr int Width = 224; - static constexpr int Height = 258; - - explicit EssencePoolItem(PlayerLogic *player, QGraphicsItem *parent = nullptr); - - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; - -protected: - void mousePressEvent(QGraphicsSceneMouseEvent *event) override; - void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override; - -private: - static constexpr int Columns = 4; - static constexpr int Padding = 8; - static constexpr int CellWidth = 52; - static constexpr int CellHeight = 54; - static constexpr int HeaderHeight = 29; - - [[nodiscard]] QChar codeAt(const QPointF &position) const; - [[nodiscard]] CounterState *stateFor(QChar code) const; - void attachCounter(CounterState *state); - void detachCounter(int id); - - PlayerLogic *player; - QHash> counters; -}; - -#endif // CONTINUUMATRICE_ESSENCE_POOL_ITEM_H diff --git a/cockatrice/src/game_graphics/player/player_graphics_item.cpp b/cockatrice/src/game_graphics/player/player_graphics_item.cpp index 165c5b6fc..fbc70680d 100644 --- a/cockatrice/src/game_graphics/player/player_graphics_item.cpp +++ b/cockatrice/src/game_graphics/player/player_graphics_item.cpp @@ -20,7 +20,6 @@ #include "libcockatrice/utility/card_ref.h" #include "menu/player_menu.h" #include "player_area.h" -#include "essence_pool_item.h" #include "player_dialogs.h" #include "player_target.h" @@ -41,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -53,10 +51,7 @@ namespace { -// Reserve a third, compact column for the synchronized 4x4 Essence pool. -// This keeps the 16 resource balances visible without obscuring the table. -constexpr qreal EXTRA_PILE_COLUMN_WIDTH = - CardDimensions::HEIGHT_F + 24 + EssencePoolItem::Width + 16; +constexpr qreal EXTRA_PILE_COLUMN_WIDTH = CardDimensions::HEIGHT_F + 24; } // namespace PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) @@ -108,13 +103,6 @@ PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) initializeZones(); - // Board-owned renderer only. All Essence balances live in player runtime - // CounterState and are sent/validated by the server. - essencePool = new EssencePoolItem(player, this); - const qreal essenceLeft = CardDimensions::HEIGHT_F + counterAreaWidth + 15 + 8 + - CardDimensions::HEIGHT_F + 14; - essencePool->setPos(essenceLeft, 14); - connect(player, &PlayerLogic::addViewCustomZoneActionToCustomZoneMenu, this, &PlayerGraphicsItem::onCustomZoneAdded); @@ -322,11 +310,6 @@ void PlayerGraphicsItem::setMirrored(bool _mirrored) void PlayerGraphicsItem::onCounterAdded(CounterState *state) { - // The Essence pool owns its own compact 4x4 display. Never add sixteen - // full-size generic counters down the narrow life-counter column. - if (EssenceCounter::isEssenceCounter(state->getName())) { - return; - } AbstractCounter *widget; if (state->getName() == "life") { widget = playerTarget->addCounter(state); diff --git a/cockatrice/src/game_graphics/player/player_graphics_item.h b/cockatrice/src/game_graphics/player/player_graphics_item.h index dae758745..237fccb55 100644 --- a/cockatrice/src/game_graphics/player/player_graphics_item.h +++ b/cockatrice/src/game_graphics/player/player_graphics_item.h @@ -31,7 +31,6 @@ class TableZone; class AbstractCounter; class CardZone; class CounterState; -class EssencePoolItem; class PlayerArea; class PlayerLogic; struct CardRef; @@ -161,7 +160,6 @@ private: PlayerDialogs *playerDialogs; PlayerArea *playerArea; PlayerTarget *playerTarget; - EssencePoolItem *essencePool = nullptr; QMap counterWidgets; QMap zoneGraphicsItems; PileZone *deckZoneGraphicsItem; diff --git a/cockatrice/src/game_graphics/zones/table_zone.cpp b/cockatrice/src/game_graphics/zones/table_zone.cpp index 6b9fb3a69..f643b3fd5 100644 --- a/cockatrice/src/game_graphics/zones/table_zone.cpp +++ b/cockatrice/src/game_graphics/zones/table_zone.cpp @@ -251,11 +251,9 @@ void TableZone::paintBattlefieldRows(QPainter *painter) */ void TableZone::reloadBattlefieldAppearance() { - // Canonical default battlefield palette. Theme battlefield.ini can override - // these with #RRGGBB (opaque) or #AARRGGBB (alpha) values. - frontZoneColor = QColor(QStringLiteral("#dc143c")); - supportZoneColor = QColor(QStringLiteral("#c3b091")); - commandZoneColor = QColor(QStringLiteral("#0a0e3f")); + frontZoneColor = QColor(142, 52, 61, 128); + supportZoneColor = QColor(40, 105, 129, 128); + commandZoneColor = QColor(94, 66, 140, 128); const QString themePath = themeManager->getCurrentThemePath(); if (!themePath.isEmpty()) { diff --git a/cockatrice/src/installer_close_server.cpp b/cockatrice/src/installer_close_server.cpp index ae79559f6..26f0b2c87 100644 --- a/cockatrice/src/installer_close_server.cpp +++ b/cockatrice/src/installer_close_server.cpp @@ -25,14 +25,14 @@ InstallerCloseServer::InstallerCloseServer(QObject *parent) : QObject(parent) QString InstallerCloseServer::socketNameForUser(qint64 processId, const QString &userName) { - return QStringLiteral("ContinuumatriceInstallerClose-%1-%2").arg(processId).arg(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("ContinuumatriceInstallerClose-%1").arg(processId)); + return scopedLocalServerName(QStringLiteral("CockatriceInstallerClose-%1").arg(processId)); } bool InstallerCloseServer::listen() diff --git a/cockatrice/src/interface/essence_chart_colors.h b/cockatrice/src/interface/essence_chart_colors.h deleted file mode 100644 index 91e580e89..000000000 --- a/cockatrice/src/interface/essence_chart_colors.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef CONTINUUMATRICE_ESSENCE_CHART_COLORS_H -#define CONTINUUMATRICE_ESSENCE_CHART_COLORS_H - -#include -#include -#include -#include -#include -#include -#include - -// CHART-ONLY presentation palette, NOT canon Essence colors or affiliations. -// Each type must remain distinguishable without inventing lore. N is colorless. -namespace EssenceCharts -{ -inline QColor colorFor(const QString &code) -{ - if (code == QStringLiteral("N")) { - return QColor(145, 149, 156); - } - const int index = Essence::symbols().indexOf(code); - if (index < 0) { - // These charts are also reused for card types/categories. Retain - // distinct readable visualization colors without assigning Essence lore. - const QColor recognizedColor(code); - if (recognizedColor.isValid()) { - return recognizedColor; - } - return QColor::fromHsv(static_cast(qHash(code) % 360u), 110, 195); - } - return QColor::fromHsv((index * 137) % 360, 125, 210); -} - -inline QList> ordered(const QMap &values) -{ - QList> result; - QSet used; - for (const QChar code : Essence::symbols()) { - const QString key(code); - auto it = values.constFind(key); - if (it != values.cend()) { - result.append({it.key(), it.value()}); - used.insert(key); - } - } - for (auto it = values.cbegin(); it != values.cend(); ++it) { - if (!used.contains(it.key())) { - result.append({it.key(), it.value()}); - } - } - return result; -} -} // namespace EssenceCharts - -#endif // CONTINUUMATRICE_ESSENCE_CHART_COLORS_H diff --git a/cockatrice/src/interface/intents/url_parser.cpp b/cockatrice/src/interface/intents/url_parser.cpp index ed8c0e457..e0b7bb2fb 100644 --- a/cockatrice/src/interface/intents/url_parser.cpp +++ b/cockatrice/src/interface/intents/url_parser.cpp @@ -44,7 +44,7 @@ void IntentUrlParser::handle(const QString &urlStr) { QUrl url(urlStr); - if (url.scheme() != "continuumatrice") { + if (url.scheme() != "cockatrice") { return; } diff --git a/cockatrice/src/interface/intents/url_parser.h b/cockatrice/src/interface/intents/url_parser.h index 9384616bc..9b8594275 100644 --- a/cockatrice/src/interface/intents/url_parser.h +++ b/cockatrice/src/interface/intents/url_parser.h @@ -36,7 +36,7 @@ struct PendingIntentChain }; /** - * @brief Parses continuumatrice:// links and runs them as serialized intent chains. + * @brief Parses cockatrice:// links and runs them as serialized intent chains. * * Links are parsed by action (joingame/opendeck) and translated into an intent * chain. Chains are queued and run one at a time: a document can hand multiple diff --git a/cockatrice/src/interface/pixel_map_generator.cpp b/cockatrice/src/interface/pixel_map_generator.cpp index 11a4644a6..2f5c4b6dd 100644 --- a/cockatrice/src/interface/pixel_map_generator.cpp +++ b/cockatrice/src/interface/pixel_map_generator.cpp @@ -488,10 +488,7 @@ const QPixmap &ManaSymbolPixmapGenerator::masterIcon(const QString &symbol) return it.value(); } - // Essence uses its own artwork namespace, including codes shared with MTG. - // Theme overrides are supported through icons/essence/.svg/png. - const QString asset = themeManager->assetPath(QStringLiteral("icons/essence/") + symbol); - QImageReader reader(QStringLiteral("theme:") + asset); + QImageReader reader("theme:icons/mana/" + symbol); QSize sourceSize = reader.size(); if (!sourceSize.isEmpty()) { sourceSize.scale(QSize(MASTER_ICON_SIZE, MASTER_ICON_SIZE), Qt::KeepAspectRatio); diff --git a/cockatrice/src/interface/theme_manager.cpp b/cockatrice/src/interface/theme_manager.cpp index 08cca9270..893cdc795 100644 --- a/cockatrice/src/interface/theme_manager.cpp +++ b/cockatrice/src/interface/theme_manager.cpp @@ -294,7 +294,7 @@ QString ThemeManager::assetPath(QStringView prefix) const // on Windows where UAC VirtualStore can make a system dir appear writable). bool ThemeManager::isDirReallyWritable(const QString &dirPath) { - const QString probe = QDir(dirPath).absoluteFilePath(".continuumatrice_write_test"); + const QString probe = QDir(dirPath).absoluteFilePath(".cockatrice_write_test"); QFile f(probe); if (!f.open(QIODevice::WriteOnly)) { return false; @@ -326,7 +326,7 @@ static QString systemThemesBasePath() #elif defined(Q_OS_WIN) base += "/themes"; #else // linux - base += "/../share/continuumatrice/themes"; + base += "/../share/cockatrice/themes"; #endif return base; } diff --git a/cockatrice/src/interface/widgets/cards/additional_info/color_identity_widget.cpp b/cockatrice/src/interface/widgets/cards/additional_info/color_identity_widget.cpp index 455d09877..0f68f3626 100644 --- a/cockatrice/src/interface/widgets/cards/additional_info/color_identity_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/additional_info/color_identity_widget.cpp @@ -2,7 +2,6 @@ #include "../../../../client/settings/cache_settings.h" #include "libcockatrice/card/card_info.h" -#include #include "mana_symbol_widget.h" #include @@ -42,9 +41,9 @@ ColorIdentityWidget::ColorIdentityWidget(QWidget *parent, const CardInfoPtr &car void ColorIdentityWidget::populateManaSymbolWidgets() { - // Continuum Universes supports fourteen named Essence codes plus N (colorless). - const QString &allEssences = Essence::symbols(); - QStringList symbols = parseColorIdentity(colorIdentity); + // Define the full WUBRG set (White, Blue, Black, Red, Green) + QString fullColorIdentity = "WUBRG"; + QStringList symbols = parseColorIdentity(colorIdentity); // Parse mana cost string // clear old layout QtUtils::clearLayoutRec(layout); @@ -56,8 +55,7 @@ void ColorIdentityWidget::populateManaSymbolWidgets() // populate mana symbols if (SettingsCache::instance().visualDeckStorage().getVisualDeckStorageDrawUnusedColorIdentities()) { - for (const QChar code : allEssences) { - const QString symbol(code); + for (const QString symbol : fullColorIdentity) { auto *manaSymbol = new ManaSymbolWidget(this, symbol, symbols.contains(symbol)); layout->addWidget(manaSymbol); } @@ -122,11 +120,28 @@ void ColorIdentityWidget::resizeEvent(QResizeEvent *event) } } -QStringList ColorIdentityWidget::parseColorIdentity(const QString &essenceIdentity) +QStringList ColorIdentityWidget::parseColorIdentity(const QString &manaString) { QStringList symbols; - for (const QChar code : Essence::normalizeIdentity(essenceIdentity)) { - symbols.append(QString(code)); + + // Handle split costs (e.g., "3U // 4UU") + QStringList splitCosts = manaString.split(" // "); + for (const QString &part : splitCosts) { + QRegularExpression regex(R"(\{([^}]+)\}|(\d+)|([WUBRGCSPX]))"); + QRegularExpressionMatchIterator matches = regex.globalMatch(part); + while (matches.hasNext()) { + QRegularExpressionMatch match = matches.next(); + if (match.captured(1).isEmpty()) { // If no `{}` group was captured, check other groups + if (!match.captured(2).isEmpty()) { + symbols.append(match.captured(2)); // Number match + } else { + symbols.append(match.captured(3)); // Single mana letter match + } + } else { + symbols.append(match.captured(1)); // `{}` enclosed match + } + } } + return symbols; } diff --git a/cockatrice/src/interface/widgets/cards/additional_info/deck_color_identity.cpp b/cockatrice/src/interface/widgets/cards/additional_info/deck_color_identity.cpp index 59557e667..13c185a79 100644 --- a/cockatrice/src/interface/widgets/cards/additional_info/deck_color_identity.cpp +++ b/cockatrice/src/interface/widgets/cards/additional_info/deck_color_identity.cpp @@ -1,7 +1,6 @@ #include "deck_color_identity.h" #include "libcockatrice/card/card_info.h" -#include #include #include @@ -18,7 +17,7 @@ QString getDeckColorIdentity(const DeckList &deck, const CardDatabaseQuerier *db return {}; } - QSet colorSet; // Unique Essence symbols (including N for colorless). + QSet colorSet; // A set to collect unique color symbols (e.g., W, U, B, R, G) for (const QString &cardName : cardList) { CardInfoPtr currentCard = db->getCardInfo(cardName); @@ -30,9 +29,10 @@ QString getDeckColorIdentity(const DeckList &deck, const CardDatabaseQuerier *db } } - // Canonical Essence order is shared by the database and all widgets. + // Ensure the color identity is in WUBRG order QString colorIdentity; - for (const QChar &color : Essence::symbols()) { + const QString wubrgOrder = "WUBRG"; + for (const QChar &color : wubrgOrder) { if (colorSet.contains(color)) { colorIdentity.append(color); } diff --git a/cockatrice/src/interface/widgets/cards/additional_info/deck_color_identity.h b/cockatrice/src/interface/widgets/cards/additional_info/deck_color_identity.h index 7ec204bee..04294cb1c 100644 --- a/cockatrice/src/interface/widgets/cards/additional_info/deck_color_identity.h +++ b/cockatrice/src/interface/widgets/cards/additional_info/deck_color_identity.h @@ -7,9 +7,8 @@ class CardDatabaseQuerier; class DeckList; /** - * @brief Computes the Essence identity of a deck from the defined Essence - * symbols (M,S,O,G,E,A,Q,C,J,B,R,L,V,T,P,N) of cards in main/sideboard. - * N denotes colorless. + * @brief Computes the color identity of a deck (e.g. "WUBRG") from the color + * symbols of all cards in the main deck and sideboard, ordered WUBRG. * * Shared as a free function so the deck storage previews and the deck share * dialog compute identities identically. diff --git a/cockatrice/src/interface/widgets/cards/additional_info/essence_filter_picker.cpp b/cockatrice/src/interface/widgets/cards/additional_info/essence_filter_picker.cpp deleted file mode 100644 index 17bb3039d..000000000 --- a/cockatrice/src/interface/widgets/cards/additional_info/essence_filter_picker.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "essence_filter_picker.h" - -#include "../../../pixel_map_generator.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -EssenceFilterPicker::EssenceFilterPicker(QWidget *parent) : QToolButton(parent) -{ - setPopupMode(QToolButton::InstantPopup); - setToolButtonStyle(Qt::ToolButtonTextOnly); // Keep the summary visible even without a button icon. - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - // Force a stable toolbar footprint independent of popup and font sizing. - // Modes and all 16 Essence icons are contained inside the popup. - setFixedWidth(qMax(156, fontMetrics().horizontalAdvance(tr("Essence: Any")) + 42)); - - popupMenu = new QMenu(this); - auto *panel = new QWidget(popupMenu); - panelLayout = new QVBoxLayout(panel); - panelLayout->setContentsMargins(10, 10, 10, 10); - panelLayout->setSpacing(7); - - auto *heading = new QLabel(tr("Filter by Essence"), panel); - panelLayout->addWidget(heading); - - auto *grid = new QGridLayout(); - grid->setSpacing(5); - - int index = 0; - for (const QChar essence : Essence::symbols()) { - const QString symbol(essence); - auto *button = new QToolButton(panel); - button->setCheckable(true); - button->setAutoRaise(false); - button->setFixedSize(42, 42); - button->setIcon(QIcon(ManaSymbolPixmapGenerator::generatePixmap(symbol, QSize(32, 32)))); - button->setIconSize(QSize(30, 30)); - if (essence == QChar('N')) { - button->setToolTip(tr("N — Colorless Essence")); - button->setAccessibleName(tr("Colorless Essence N")); - } else if (essence == QChar('J')) { - button->setToolTip(tr("J — Jasper Essence")); - button->setAccessibleName(tr("Jasper Essence J")); - } else { - button->setToolTip(tr("Essence %1").arg(symbol)); - button->setAccessibleName(tr("Essence %1").arg(symbol)); - } - button->setProperty("essenceCode", symbol); - connect(button, &QToolButton::toggled, this, [this, essence](bool checked) { - updateSummary(); - emit essenceToggled(essence, checked); - }); - - // Sixteen Essences form a compact 4x4 grid. - grid->addWidget(button, index / 4, index % 4); - buttons.insert(essence, button); - ++index; - } - panelLayout->addLayout(grid); - - auto *clearButton = new QPushButton(tr("Clear Essence selection"), panel); - connect(clearButton, &QPushButton::clicked, this, [this]() { - // Clear as ONE model operation, so Exact Match and Include/Exclude - // don't retain stale "not this Essence" predicates. - for (const QChar essence : Essence::symbols()) { - if (auto *button = buttons.value(essence); button && button->isChecked()) { - const QSignalBlocker blocker(button); - button->setChecked(false); - } - } - updateSummary(); - emit selectionCleared(); - }); - panelLayout->addWidget(clearButton); - - auto *panelAction = new QWidgetAction(popupMenu); - panelAction->setDefaultWidget(panel); - popupMenu->addAction(panelAction); - setMenu(popupMenu); - - retranslateUi(); -} - -void EssenceFilterPicker::addPopupControl(QWidget *control) -{ - if (!control || !panelLayout) { - return; - } - - // Insert above the Clear button, keeping the entire configuration in one - // popup rather than competing for horizontal space in the main toolbar. - panelLayout->insertWidget(panelLayout->count() - 1, control); -} - -QSet EssenceFilterPicker::activeEssences() const -{ - QSet active; - for (auto it = buttons.cbegin(); it != buttons.cend(); ++it) { - if (it.value()->isChecked()) { - active.insert(it.key()); - } - } - return active; -} - -void EssenceFilterPicker::setEssenceActive(QChar essence, bool active) -{ - auto *button = buttons.value(essence); - if (!button || button->isChecked() == active) { - return; - } - - // Restoring external filter state must not rewrite the filter model. - const QSignalBlocker blocker(button); - button->setChecked(active); - updateSummary(); -} - -void EssenceFilterPicker::retranslateUi() -{ - updateSummary(); -} - -void EssenceFilterPicker::updateSummary() -{ - QStringList selected; - const QSet active = activeEssences(); - for (const QChar essence : Essence::symbols()) { - if (active.contains(essence)) { - selected.append(QString(essence)); - } - } - - if (selected.isEmpty()) { - setText(tr("Essence: Any")); - setToolTip(tr("Filter by Essence (none selected)")); - } else if (selected.size() == 1) { - setText(tr("Essence: %1").arg(selected.first())); - setToolTip(tr("Selected Essence: %1").arg(selected.first())); - } else { - // Keep the toolbar label short; the tooltip lists the exact types. - setText(tr("Essence (%1)").arg(selected.size())); - setToolTip(tr("Selected Essence: %1").arg(selected.join(QStringLiteral(", ")))); - } -} diff --git a/cockatrice/src/interface/widgets/cards/additional_info/essence_filter_picker.h b/cockatrice/src/interface/widgets/cards/additional_info/essence_filter_picker.h deleted file mode 100644 index fbf9b3174..000000000 --- a/cockatrice/src/interface/widgets/cards/additional_info/essence_filter_picker.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef CONTINUUMATRICE_ESSENCE_FILTER_PICKER_H -#define CONTINUUMATRICE_ESSENCE_FILTER_PICKER_H - -#include -#include -#include -#include - -class QMenu; -class QVBoxLayout; -class QWidget; - -/** - * Compact, reusable multi-select Essence picker for the deck and card database - * toolbars. The menu contains all fifteen Essence symbols in a 5x3 icon grid. - * Clicking a symbol does not close the popup; programmatic synchronization - * does not emit a user-selection signal. - */ -class EssenceFilterPicker final : public QToolButton -{ - Q_OBJECT - -public: - explicit EssenceFilterPicker(QWidget *parent = nullptr); - - [[nodiscard]] QSet activeEssences() const; - void setEssenceActive(QChar essence, bool active); - // Put the host's filtering mode selector inside this popup too: - // neither toolbar should require a separate cramped control. - void addPopupControl(QWidget *control); - void retranslateUi(); - -signals: - void essenceToggled(QChar essence, bool active); - // A clear request removes ALL Essence filters, including exclusions, - // rather than creating fifteen negative filters in Exact/Exclude mode. - void selectionCleared(); - -private: - void updateSummary(); - - QHash buttons; - QMenu *popupMenu = nullptr; - QVBoxLayout *panelLayout = nullptr; -}; - -#endif // CONTINUUMATRICE_ESSENCE_FILTER_PICKER_H diff --git a/cockatrice/src/interface/widgets/cards/additional_info/mana_cost_widget.cpp b/cockatrice/src/interface/widgets/cards/additional_info/mana_cost_widget.cpp index cc10f76bf..f6efd267e 100644 --- a/cockatrice/src/interface/widgets/cards/additional_info/mana_cost_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/additional_info/mana_cost_widget.cpp @@ -1,7 +1,6 @@ #include "mana_cost_widget.h" #include "libcockatrice/card/card_info.h" -#include #include "mana_symbol_widget.h" #include @@ -52,7 +51,28 @@ void ManaCostWidget::resizeEvent(QResizeEvent *event) } } -QStringList ManaCostWidget::parseManaCost(const QString &essenceCost) +QStringList ManaCostWidget::parseManaCost(const QString &cmc) { - return Essence::parseCost(essenceCost); + QStringList symbols; + + // Handle split costs (e.g., "3U // 4UU") + QStringList splitCosts = cmc.split(" // "); + for (const QString &part : splitCosts) { + QRegularExpression regex(R"(\{([^}]+)\}|(\d+)|([WUBRGCSPX]))"); + QRegularExpressionMatchIterator matches = regex.globalMatch(part); + while (matches.hasNext()) { + QRegularExpressionMatch match = matches.next(); + if (match.captured(1).isEmpty()) { // If no `{}` group was captured, check other groups + if (!match.captured(2).isEmpty()) { + symbols.append(match.captured(2)); // Number match + } else { + symbols.append(match.captured(3)); // Single mana letter match + } + } else { + symbols.append(match.captured(1)); // `{}` enclosed match + } + } + } + + return symbols; } diff --git a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_base/mana_base_config_dialog.cpp b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_base/mana_base_config_dialog.cpp index dbd91df85..32b54c485 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_base/mana_base_config_dialog.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_base/mana_base_config_dialog.cpp @@ -57,14 +57,14 @@ ManaBaseConfigDialog::ManaBaseConfigDialog(DeckListStatisticsAnalyzer *analyzer, void ManaBaseConfigDialog::retranslateUi() { - setWindowTitle(tr("Essence Production Configuration")); + setWindowTitle(tr("Mana Base Configuration")); displayTypeLabel->setText(tr("Display type:")); displayType->clear(); displayType->addItems({tr("pie"), tr("bar"), tr("combinedBar")}); - filterLabel->setText(tr("Filter Essence types (optional):")); + filterLabel->setText(tr("Filter Colors (optional):")); buttons->button(QDialogButtonBox::Ok)->setText(tr("OK")); buttons->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); diff --git a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_base/mana_base_widget.cpp b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_base/mana_base_widget.cpp index 6cedb6234..8215cbdad 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_base/mana_base_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_base/mana_base_widget.cpp @@ -1,6 +1,5 @@ #include "mana_base_widget.h" -#include "../../../../essence_chart_colors.h" #include "../../../general/display/charts/bars/bar_widget.h" #include "../../../general/display/charts/bars/color_bar.h" #include "../../../general/display/charts/pies/color_pie.h" @@ -31,7 +30,7 @@ namespace { AnalyticsPanelWidgetRegistrar registerManaBase{ - "manaBase", ManaBaseWidget::tr("Essence Production"), + "manaBase", ManaBaseWidget::tr("Mana Base"), [](QWidget *parent, DeckListStatisticsAnalyzer *analyzer) { return new ManaBaseWidget(parent, analyzer); }}; } // anonymous namespace @@ -84,12 +83,16 @@ void ManaBaseWidget::updateDisplay() // Choose display mode if (config.displayType == "bar") { - const QList> sortedColors = EssenceCharts::ordered(mapSorted); + const QList> sortedColors = GameSpecificColors::MTG::sortManaMapWUBRGCFirst(mapSorted); + static const QHash colorMap = { + {"W", QColor(248, 231, 185)}, {"U", QColor(14, 104, 171)}, {"B", QColor(21, 11, 0)}, + {"R", QColor(211, 32, 42)}, {"G", QColor(0, 115, 62)}, {"C", QColor(150, 150, 150)}, + }; for (const auto &[color, count] : sortedColors) { QString label = QString("%1 %2 (%3)").arg(color).arg(count).arg(cardCount.value(color)); - BarWidget *bar = new BarWidget(label, count, highest, EssenceCharts::colorFor(color), this); + BarWidget *bar = new BarWidget(label, count, highest, colorMap.value(color, Qt::gray), this); barLayout->addWidget(bar); } diff --git a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_category_widget.cpp b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_category_widget.cpp index 9446e511e..be81ab97a 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_category_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_category_widget.cpp @@ -1,7 +1,7 @@ #include "mana_curve_category_widget.h" #include "../../../general/display/charts/bars/bar_chart_widget.h" -#include "../../../../essence_chart_colors.h" +#include "libcockatrice/utility/color.h" #include "libcockatrice/utility/qt_utils.h" #include "mana_curve_config.h" @@ -110,7 +110,7 @@ void ManaCurveCategoryWidget::updateDisplay(int minCmc, QVector segments; if (val > 0) { - segments.push_back({cat, val, cards, EssenceCharts::colorFor(cat)}); + segments.push_back({cat, val, cards, GameSpecificColors::MTG::colorHelper(cat)}); } catBars.push_back({QString::number(cmc), segments}); diff --git a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_config_dialog.cpp b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_config_dialog.cpp index ae4f03e42..c5498ff3e 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_config_dialog.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_config_dialog.cpp @@ -64,7 +64,7 @@ void ManaCurveConfigDialog::retranslateUi() { labelGroupBy->setText(tr("Group By:")); groupBy->setItemText(0, tr("type")); - groupBy->setItemText(1, tr("Essence")); + groupBy->setItemText(1, tr("color")); groupBy->setItemText(2, tr("subtype")); groupBy->setItemText(3, tr("power")); groupBy->setItemText(4, tr("toughness")); diff --git a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_total_widget.cpp b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_total_widget.cpp index e0fad0d83..f1d9ea8c7 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_total_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_total_widget.cpp @@ -1,7 +1,7 @@ #include "mana_curve_total_widget.h" #include "../../../general/display/charts/bars/bar_chart_widget.h" -#include "../../../../essence_chart_colors.h" +#include "libcockatrice/utility/color.h" #include "mana_curve_config.h" #include @@ -71,7 +71,7 @@ void ManaCurveTotalWidget::updateDisplay(const QString &categoryName, cards = catIt->value(cmc); } - segments.push_back({category, value, cards, EssenceCharts::colorFor(category)}); + segments.push_back({category, value, cards, GameSpecificColors::MTG::colorHelper(category)}); } } diff --git a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_widget.cpp b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_widget.cpp index f7be618e9..6d93bde54 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_widget.cpp @@ -23,7 +23,7 @@ namespace { AnalyticsPanelWidgetRegistrar registerManaCurve{ - "manaCurve", ManaCurveWidget::tr("Essence Curve"), + "manaCurve", ManaCurveWidget::tr("Mana Curve"), [](QWidget *parent, DeckListStatisticsAnalyzer *analyzer) { return new ManaCurveWidget(parent, analyzer); }}; } // anonymous namespace diff --git a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_devotion/mana_devotion_config_dialog.cpp b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_devotion/mana_devotion_config_dialog.cpp index d2e3de776..0d70fc24d 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_devotion/mana_devotion_config_dialog.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_devotion/mana_devotion_config_dialog.cpp @@ -63,7 +63,7 @@ void ManaDevotionConfigDialog::retranslateUi() displayType->setItemText(1, tr("bar")); displayType->setItemText(2, tr("combinedBar")); - labelFilters->setText(tr("Filter Essence types (optional):")); + labelFilters->setText(tr("Filter Colors (optional):")); } void ManaDevotionConfigDialog::accept() diff --git a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_devotion/mana_devotion_widget.cpp b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_devotion/mana_devotion_widget.cpp index 47ad1405d..b958a733f 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_devotion/mana_devotion_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_devotion/mana_devotion_widget.cpp @@ -1,6 +1,5 @@ #include "mana_devotion_widget.h" -#include "../../../../essence_chart_colors.h" #include "../../../general/display/charts/bars/bar_widget.h" #include "../../../general/display/charts/bars/color_bar.h" #include "../../../general/display/charts/pies/color_pie.h" @@ -30,7 +29,7 @@ namespace { AnalyticsPanelWidgetRegistrar registerManaDevotion{ - "manaDevotion", ManaDevotionWidget::tr("Essence Cost Symbols"), + "manaDevotion", ManaDevotionWidget::tr("Mana Devotion"), [](QWidget *parent, DeckListStatisticsAnalyzer *analyzer) { return new ManaDevotionWidget(parent, analyzer); }}; } // anonymous namespace @@ -83,13 +82,18 @@ void ManaDevotionWidget::updateDisplay() mapSorted.insert(QString(it.key()), it.value()); } + // Color map + QHash colors = {{'W', QColor(248, 231, 185)}, {'U', QColor(14, 104, 171)}, + {'B', QColor(21, 11, 0)}, {'R', QColor(211, 32, 42)}, + {'G', QColor(0, 115, 62)}, {'C', QColor(150, 150, 150)}}; + // Choose display mode if (config.displayType == "bar") { // One BarWidget per devotion color for (auto c : devoMap.keys()) { QString label = QString("%1 %2 (%3)").arg(c).arg(devoMap[c]).arg(cardCount.value(QString(c))); - BarWidget *bar = new BarWidget(label, devoMap[c], highest, EssenceCharts::colorFor(QString(c)), this); + BarWidget *bar = new BarWidget(label, devoMap[c], highest, colors.value(c, Qt::gray), this); barLayout->addWidget(bar); } diff --git a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_config_dialog.cpp b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_config_dialog.cpp index 0261b5c5e..24b71bae0 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_config_dialog.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_config_dialog.cpp @@ -10,18 +10,11 @@ #include #include #include -#include #include class QWidget; -static const QStringList kEssences = [] { - QStringList codes; - for (const QChar symbol : Essence::symbols()) { - codes.append(QString(symbol)); - } - return codes; -}(); +static const QStringList kColors = {"W", "U", "B", "R", "G", "C"}; ManaDistributionConfigDialog::ManaDistributionConfigDialog(DeckListStatisticsAnalyzer *analyzer, QWidget *parent) : QDialog(parent), analyzer(analyzer) @@ -40,7 +33,7 @@ ManaDistributionConfigDialog::ManaDistributionConfigDialog(DeckListStatisticsAna filterList = new QListWidget(this); filterList->setSelectionMode(QAbstractItemView::MultiSelection); - filterList->addItems(kEssences); // dynamic/fixed, no translation needed + filterList->addItems(kColors); // dynamic/fixed, no translation needed lay->addWidget(filterList); showColorRows = new QCheckBox(this); @@ -63,9 +56,9 @@ void ManaDistributionConfigDialog::retranslateUi() displayType->setItemText(0, tr("pie")); displayType->setItemText(1, tr("bar")); - labelFilters->setText(tr("Essence types:")); + labelFilters->setText(tr("Colors:")); - showColorRows->setText(tr("Show per-Essence rows")); + showColorRows->setText(tr("Show per-color rows")); // QDialogButtonBox buttons are automatically translated } diff --git a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_single_display_widget.cpp b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_single_display_widget.cpp index dfde39b27..4faa105e6 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_single_display_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_single_display_widget.cpp @@ -48,5 +48,5 @@ void ManaDistributionSingleDisplayWidget::setDevotion(int pips, int cards, int p void ManaDistributionSingleDisplayWidget::setProduction(int pips, int cards, int percent) { productionBar->setValue(percent); - productionLabel->setText(QString(tr("%1 Essence (%2 cards)")).arg(pips).arg(cards)); + productionLabel->setText(QString(tr("%1 mana (%2 cards)")).arg(pips).arg(cards)); } diff --git a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_widget.cpp b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_widget.cpp index ae81462f9..5543c64df 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_widget.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/analyzer_modules/mana_distribution/mana_distribution_widget.cpp @@ -14,25 +14,18 @@ #include #include #include -#include #include #include namespace { AnalyticsPanelWidgetRegistrar registerManaDistribution{ - "manaProdDevotion", ManaDistributionWidget::tr("Essence Production + Costs"), + "manaProdDevotion", ManaDistributionWidget::tr("Mana Production + Devotion"), [](QWidget *parent, DeckListStatisticsAnalyzer *analyzer) { return new ManaDistributionWidget(parent, analyzer); }}; } // anonymous namespace -static const QStringList kEssences = [] { - QStringList codes; - for (const QChar symbol : Essence::symbols()) { - codes.append(QString(symbol)); - } - return codes; -}(); +static const QStringList kColors = {"W", "U", "B", "R", "G", "C"}; ManaDistributionWidget::ManaDistributionWidget(QWidget *parent, DeckListStatisticsAnalyzer *analyzer) : AbstractAnalyticsPanelWidget(parent, analyzer) @@ -56,7 +49,7 @@ ManaDistributionWidget::ManaDistributionWidget(QWidget *parent, DeckListStatisti row = new QHBoxLayout(); containerLayout->addLayout(row); - for (const QString &c : kEssences) { + for (const QString &c : kColors) { auto *w = new ManaDistributionSingleDisplayWidget(c, this); row->addWidget(w); rows[c] = w; @@ -72,7 +65,7 @@ void ManaDistributionWidget::updateDisplay() const auto &prodPips = analyzer->getProductionPipCount(); const auto &prodCards = analyzer->getProductionCardCount(); - QStringList filtered = config.filters.isEmpty() ? kEssences : config.filters; + QStringList filtered = config.filters.isEmpty() ? kColors : config.filters; QMap devMap, prodMap; for (const QString &c : filtered) { @@ -96,7 +89,7 @@ void ManaDistributionWidget::updateDisplay() productionBarTop->setColors(prodMap); } - for (const QString &c : kEssences) { + for (const QString &c : kColors) { auto *w = rows.value(c); if (!w) { @@ -133,7 +126,7 @@ void ManaDistributionWidget::updateDisplay() QDialog *ManaDistributionWidget::createConfigDialog(QWidget *parent) { auto *dlg = new ManaDistributionConfigDialog(analyzer, parent); - dlg->setWindowTitle(tr("Essence Distribution Settings")); + dlg->setWindowTitle(tr("Mana Distribution Settings")); dlg->setFromConfig(config); connect(dlg, &QDialog::accepted, [this, dlg]() { diff --git a/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp b/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp index 45561719d..c2f70759e 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp +++ b/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.cpp @@ -2,7 +2,6 @@ #include "deck_list_model.h" #include "libcockatrice/card/card_info.h" -#include #include "libcockatrice/card/database/card_database_querier.h" #include "libcockatrice/deck_list/tree/deck_list_card_node.h" #include "libcockatrice/deck_list/tree/inner_deck_list_node.h" @@ -128,39 +127,68 @@ void DeckListStatisticsAnalyzer::analyze() QHash DeckListStatisticsAnalyzer::determineManaProduction(const QString &rulesText) { - QHash result; - for (const QChar code : Essence::symbols()) { - result.insert(QString(code), 0); - } + QHash manaCounts = {{"W", 0}, {"U", 0}, {"B", 0}, {"R", 0}, {"G", 0}, {"C", 0}}; - // Explicit Essence grants only. Inferring additional resources from MTG - // tap/mana rules would invent Continuum Universes gameplay semantics. - // E.g. "Add {M}" / "add {N}" both work. - static const QRegularExpression grantedEssence( - QStringLiteral(R"(\badd\s*\{([MSOGEAQCJBRLVTPN])\})"), - QRegularExpression::CaseInsensitiveOption); + QString text = rulesText.toLower(); // Normalize case for matching - auto matches = grantedEssence.globalMatch(rulesText); - while (matches.hasNext()) { - const QString code = matches.next().captured(1).toUpper(); - result[code]++; - } - return result; -} - -std::unordered_map DeckListStatisticsAnalyzer::countManaSymbols(const QString &essenceCost) -{ - std::unordered_map result; - for (const QChar code : Essence::symbols()) { - result.emplace(code.toLatin1(), 0); - } - - for (const QString &token : Essence::parseCost(essenceCost)) { - if (token.size() == 1 && Essence::isSymbol(token.at(0))) { - result[token.at(0).toLatin1()]++; + // Quick keyword-based checks for any color and colorless mana + if (text.contains("{t}: add one mana of any color") || text.contains("add one mana of any color")) { + for (const auto &color : {QStringLiteral("W"), QStringLiteral("U"), QStringLiteral("B"), QStringLiteral("R"), + QStringLiteral("G")}) { + manaCounts[color]++; } } - return result; + if (text.contains("{t}: add {c}") || text.contains("add one colorless mana")) { + manaCounts["C"]++; + } + + // Optimized regex for specific mana symbols + static const QRegularExpression specificColorRegex(R"(\{T\}:\s*Add\s*\{([WUBRG])\})"); + QRegularExpressionMatch match = specificColorRegex.match(rulesText); + if (match.hasMatch()) { + manaCounts[match.captured(1)]++; + } + + return manaCounts; +} + +std::unordered_map DeckListStatisticsAnalyzer::countManaSymbols(const QString &manaString) +{ + std::unordered_map manaCounts = {{'W', 0}, {'U', 0}, {'B', 0}, {'R', 0}, {'G', 0}}; + + int len = manaString.length(); + for (int i = 0; i < len; ++i) { + if (manaString[i] == '{') { + ++i; // Move past '{' + if (i < len && manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) { + char mana1 = manaString[i].toLatin1(); + ++i; // Move to next character + if (i < len && manaString[i] == '/') { + ++i; // Move past '/' + if (i < len && manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) { + char mana2 = manaString[i].toLatin1(); + manaCounts[mana1]++; + manaCounts[mana2]++; + } else { + // Handle cases like "{W/}" where second part is invalid + manaCounts[mana1]++; + } + } else { + manaCounts[mana1]++; + } + } + // Ensure we always skip to the closing '}' + while (i < len && manaString[i] != '}') { + ++i; + } + } + // Check if the character is a standalone mana symbol (not inside {}) + else if (manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) { + manaCounts[manaString[i].toLatin1()]++; + } + } + + return manaCounts; } // Hypergeometric probability: P(X=k) diff --git a/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.h b/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.h index 95b14d1e7..c3643d65f 100644 --- a/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.h +++ b/cockatrice/src/interface/widgets/deck_analytics/deck_list_statistics_analyzer.h @@ -152,7 +152,7 @@ private: std::unordered_map manaCurveMap; std::unordered_map manaDevotionMap; - QHash devotionPipCount; // Counts for the fifteen Continuum Universes Essence codes + QHash devotionPipCount; // W/U/B/R/G total symbols QHash devotionCardCount; // how many cards provide devotion QHash productionPipCount; // mana produced by cards diff --git a/cockatrice/src/interface/widgets/dialogs/dlg_update.cpp b/cockatrice/src/interface/widgets/dialogs/dlg_update.cpp index 8e4bca115..ca55c7b41 100644 --- a/cockatrice/src/interface/widgets/dialogs/dlg_update.cpp +++ b/cockatrice/src/interface/widgets/dialogs/dlg_update.cpp @@ -36,9 +36,9 @@ // Executable that, when it sits next to the downloaded update installer, is installed instead of // it. A packager shipping a custom build - or someone testing one - can drop the file there and -// have Continuumatrice run it rather than the official installer, without the release channel having to +// have Cockatrice run it rather than the official installer, without the release channel having to // host an installer for that build. It is a drop-in and is run with the same arguments. -static const QString UPDATE_INSTALLER_OVERRIDE = "Continuumatrice-Update-Override.exe"; +static const QString UPDATE_INSTALLER_OVERRIDE = "Cockatrice-Update-Override.exe"; DlgUpdate::DlgUpdate(QWidget *parent) : QDialog(parent) { @@ -87,7 +87,7 @@ DlgUpdate::DlgUpdate(QWidget *parent) : QDialog(parent) if (!QSslSocket::supportsSsl()) { enableUpdateButton(false); QMessageBox::critical(this, tr("Error"), - tr("Continuumatrice was not built with SSL support, therefore you cannot download updates " + tr("Cockatrice was not built with SSL support, therefore you cannot download updates " "automatically! \nPlease visit the download page to update manually.")); } @@ -161,7 +161,7 @@ void DlgUpdate::finishedUpdateCheck(bool needToUpdate, bool isCompatible, Releas // downloader themselves if there's a compatible build QMessageBox::information( window(), tr("No Update Available"), - tr("Continuumatrice is up to date!") + "

" + + tr("Cockatrice is up to date!") + "

" + tr("You are already running the latest version available in the chosen release channel.") + "
" + "" + tr("Current version") + QString(": %1
").arg(VERSION_STRING) + "" + tr("Selected release channel") + @@ -171,7 +171,7 @@ void DlgUpdate::finishedUpdateCheck(bool needToUpdate, bool isCompatible, Releas publishDate = release->getPublishDate().toString(QLocale().dateFormat(QLocale::LongFormat)); if (isCompatible) { - const QString message = tr("A new version of Continuumatrice is available!") + "

" + "" + + const QString message = tr("A new version of Cockatrice is available!") + "

" + "" + tr("New version") + QString(": %1
").arg(release->getName()) + "" + tr("Released") + QString(": %1 (
").arg(publishDate, release->getDescriptionUrl()) + @@ -185,7 +185,7 @@ void DlgUpdate::finishedUpdateCheck(bool needToUpdate, bool isCompatible, Releas } else { QMessageBox::information( window(), tr("Update Available"), - tr("A new version of Continuumatrice is available!") + "

" + "" + tr("New version") + + tr("A new version of Cockatrice is available!") + "

" + "" + tr("New version") + QString(": %1
").arg(release->getName()) + "" + tr("Released") + QString(": %1 (
").arg(publishDate, release->getDescriptionUrl()) + tr("Changelog") + ")

" + @@ -235,7 +235,7 @@ void DlgUpdate::warnInstallerIsWaiting() // would therefore swallow the installer's one and only request for the rest of its wait. auto *notification = new QMessageBox(QMessageBox::Warning, tr("Update"), tr("The update installer is already running and waits about a " - "minute for Continuumatrice to close. Continuumatrice is still busy, so " + "minute for Cockatrice to close. Cockatrice is still busy, so " "save your work and close it before then. Otherwise the " "installer gives up and the update is cancelled."), QMessageBox::Ok, parentWidget()); @@ -293,9 +293,9 @@ void DlgUpdate::downloadSuccessful(const QUrl &filepath) process.setArguments({"/R", QString("/D=%1").arg(appDir)}); #endif - // Try to open the installer. If it opens, quit Continuumatrice + // Try to open the installer. If it opens, quit Cockatrice if (process.startDetached()) { - qCInfo(DlgUpdateLog) << "Opened update installer successfully - closing Continuumatrice"; + qCInfo(DlgUpdateLog) << "Opened update installer successfully - closing Cockatrice"; // Close the main window synchronously so file locks are released before the NSIS installer // (already launched) starts replacing files. This also flushes settings and shuts down the // tabs, but only when the close is actually accepted: MainWindow may veto it for a running @@ -324,8 +324,8 @@ void DlgUpdate::downloadSuccessful(const QUrl &filepath) } else { setLabel(tr("Error")); QMessageBox::critical(this, tr("Update Error"), - tr("Continuumatrice is unable to open the installer.") + "

" + - tr("Try to update manually by closing Continuumatrice and running the installer.") + + tr("Cockatrice is unable to open the installer.") + "

" + + tr("Try to update manually by closing Cockatrice and running the installer.") + "
" + tr("Download location") + QString(": %1").arg(filepath.toLocalFile())); } } diff --git a/cockatrice/src/interface/widgets/general/display/charts/bars/color_bar.cpp b/cockatrice/src/interface/widgets/general/display/charts/bars/color_bar.cpp index 4183e1fc6..28118cc9c 100644 --- a/cockatrice/src/interface/widgets/general/display/charts/bars/color_bar.cpp +++ b/cockatrice/src/interface/widgets/general/display/charts/bars/color_bar.cpp @@ -1,6 +1,6 @@ #include "color_bar.h" -#include "../../../../../essence_chart_colors.h" +#include "libcockatrice/utility/color.h" #include #include @@ -19,14 +19,14 @@ #include ColorBar::ColorBar(const QMap &_colors, QWidget *parent) - : QWidget(parent), colors(EssenceCharts::ordered(_colors)) + : QWidget(parent), colors(GameSpecificColors::MTG::sortManaMapWUBRGCFirst(_colors)) { setMouseTracking(true); } void ColorBar::setColors(const QMap &_colors) { - colors = EssenceCharts::ordered(_colors); + colors = GameSpecificColors::MTG::sortManaMapWUBRGCFirst(_colors); update(); } @@ -150,5 +150,19 @@ QString ColorBar::tooltipForPosition(int x) const QColor ColorBar::colorFromName(const QString &name) const { - return EssenceCharts::colorFor(name); -} + static QMap map = { + {"R", QColor(220, 30, 30)}, {"G", QColor(40, 170, 40)}, {"U", QColor(40, 90, 200)}, + {"W", QColor(235, 235, 230)}, {"B", QColor(30, 30, 30)}, + }; + + if (map.contains(name)) { + return map[name]; + } + + QColor c(name); + if (!c.isValid()) { + c = Qt::gray; + } + + return c; +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/general/display/charts/pies/color_pie.cpp b/cockatrice/src/interface/widgets/general/display/charts/pies/color_pie.cpp index 7b59ee3fa..82542a663 100644 --- a/cockatrice/src/interface/widgets/general/display/charts/pies/color_pie.cpp +++ b/cockatrice/src/interface/widgets/general/display/charts/pies/color_pie.cpp @@ -1,6 +1,6 @@ #include "color_pie.h" -#include "../../../../../essence_chart_colors.h" +#include "libcockatrice/utility/color.h" #include #include @@ -20,14 +20,14 @@ #include ColorPie::ColorPie(const QMap &_colors, QWidget *parent) - : QWidget(parent), colors(EssenceCharts::ordered(_colors)) + : QWidget(parent), colors(GameSpecificColors::MTG::sortManaMapWUBRGCFirst(_colors)) { setMouseTracking(true); } void ColorPie::setColors(const QMap &_colors) { - colors = EssenceCharts::ordered(_colors); + colors = GameSpecificColors::MTG::sortManaMapWUBRGCFirst(_colors); update(); } @@ -184,5 +184,19 @@ QString ColorPie::tooltipForPoint(const QPoint &pt) const QColor ColorPie::colorFromName(const QString &name) const { - return EssenceCharts::colorFor(name); -} + static QMap map = { + {"R", QColor(220, 30, 30)}, {"G", QColor(40, 170, 40)}, {"U", QColor(40, 90, 200)}, + {"W", QColor(235, 235, 230)}, {"B", QColor(30, 30, 30)}, + }; + + if (map.contains(name)) { + return map[name]; + } + + QColor c(name); + if (!c.isValid()) { + c = Qt::gray; + } + + return c; +} \ No newline at end of file diff --git a/cockatrice/src/interface/widgets/onboarding/first_run_wizard.cpp b/cockatrice/src/interface/widgets/onboarding/first_run_wizard.cpp index f802b278c..c4298d6b6 100644 --- a/cockatrice/src/interface/widgets/onboarding/first_run_wizard.cpp +++ b/cockatrice/src/interface/widgets/onboarding/first_run_wizard.cpp @@ -213,7 +213,7 @@ void FirstRunWizard::changeEvent(QEvent *event) void FirstRunWizard::retranslateUi() { - setWindowTitle(tr("Welcome to Continuumatrice")); + setWindowTitle(tr("Welcome to Cockatrice")); backButton->setText(tr("Back")); skipButton->setText(tr("Skip")); for (FirstRunWizardPage *page : std::as_const(pages)) { diff --git a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp index c53d14d50..3316232a4 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_supervisor.cpp @@ -1511,7 +1511,7 @@ void TabSupervisor::processNotifyUserEvent(const Event_NotifyUser &event) QMessageBox::information( this, tr("Unknown Event"), tr("The server has sent you a message that your client does not understand.\nThis message might mean " - "there is a new version of Continuumatrice available or this server is running a custom or pre-release " + "there is a new version of Cockatrice available or this server is running a custom or pre-release " "version.\n\nTo update your client, go to Help -> Check for Updates.")); break; case Event_NotifyUser::IDLEWARNING: diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp index a9e850d50..464d62e4a 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.cpp @@ -1,44 +1,48 @@ #include "visual_database_display_color_filter_widget.h" -#include "../cards/additional_info/essence_filter_picker.h" +#include "../cards/additional_info/mana_symbol_widget.h" #include "filter_tree_model.h" #include "libcockatrice/filters/filter_card.h" +#include #include +#include #include +#include #include -#include #include #include -#include +#include #include +/** + * This widget provides a graphical control element for the CardFilter::Attr::AttrColor filters applied to the filter + * model. + * @param parent The Qt Widget that this widget will parent to + * @param _filterModel The filter model that this widget will manipulate + */ VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(QWidget *parent, FilterTreeModel *_filterModel) : QWidget(parent), filterModel(_filterModel), layout(new QHBoxLayout(this)) { setLayout(layout); - layout->setSpacing(3); + layout->setSpacing(5); layout->setContentsMargins(0, 0, 0, 0); - // Share the same compact Essence selection UI as Visual Deck Storage. - // A 15-icon horizontal row cannot fit the database filter toolbar. - essencePicker = new EssenceFilterPicker(this); - layout->addWidget(essencePicker); - // FlowWidget measures THIS parent widget, not its nested button. Give it - // an explicit size hint that cannot clip the picker. - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); - setMinimumWidth(essencePicker->width()); + QString fullColorIdentity = "WUBRG"; + for (const QChar &color : fullColorIdentity) { + auto *manaSymbol = new ManaSymbolWidget(this, color, false, true); + manaSymbol->setFixedWidth(25); - connect(essencePicker, &EssenceFilterPicker::essenceToggled, this, - &VisualDatabaseDisplayColorFilterWidget::handleColorToggled); + layout->addWidget(manaSymbol); - connect(essencePicker, &EssenceFilterPicker::selectionCleared, this, [this]() { - filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor); - }); + // Connect the color toggled signal + connect(manaSymbol, &ManaSymbolWidget::colorToggled, this, + &VisualDatabaseDisplayColorFilterWidget::handleColorToggled); + } modeComboBox = new QComboBox(this); - essencePicker->addPopupControl(modeComboBox); + layout->addWidget(modeComboBox); connect(modeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualDatabaseDisplayColorFilterWidget::updateFilterMode); @@ -46,83 +50,108 @@ VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(Q connect(filterModel, &FilterTreeModel::layoutChanged, this, [this]() { syncDebouncer.call([this] { syncWithFilterModel(); }); }); + // Call retranslateUi to set the initial text retranslateUi(); - syncWithFilterModel(); } void VisualDatabaseDisplayColorFilterWidget::retranslateUi() { - essencePicker->retranslateUi(); - modeComboBox->blockSignals(true); modeComboBox->clear(); + modeComboBox->addItem(tr("Exact match"), QVariant::fromValue(FilterMode::ExactMatch)); modeComboBox->addItem(tr("Includes"), QVariant::fromValue(FilterMode::Includes)); modeComboBox->addItem(tr("Include / Exclude"), QVariant::fromValue(FilterMode::IncludeExclude)); - modeComboBox->setToolTip(tr("How selected and unselected Essence types are combined in the filter")); + modeComboBox->setToolTip(tr("How selected and unselected colors are combined in the filter")); + + // Restore current mode const int index = modeComboBox->findData(QVariant::fromValue(currentMode)); if (index >= 0) { modeComboBox->setCurrentIndex(index); } + modeComboBox->blockSignals(false); } -void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar essence, bool active) +void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar color, bool active) { if (active) { - addFilter(essence); + addFilter(color); } else { - removeFilter(essence); + removeFilter(color); } } -void VisualDatabaseDisplayColorFilterWidget::addFilter(QChar essence) +void VisualDatabaseDisplayColorFilterWidget::addFilter(QChar color) { - QString essenceString(essence); - const QList filters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor); - for (const CardFilter *filter : filters) { - if (filter->term() == essenceString) { - filterModel->removeFilter(filter); + QString colorString = color; + QString typeStr; + + // Remove previous filters + + QList allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor); + QList matchingFilters; + + for (const CardFilter *filter : allColorFilters) { + if (filter->term() == color) { + matchingFilters.append(filter); } } + for (const CardFilter *filter : matchingFilters) { + filterModel->removeFilter(filter); + } + + // Add actual filter + switch (currentMode) { case FilterMode::ExactMatch: - filterModel->addFilter( - new CardFilter(essenceString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor)); + filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor)); break; + case FilterMode::Includes: + filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor)); + break; + case FilterMode::IncludeExclude: - filterModel->addFilter( - new CardFilter(essenceString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor)); + filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor)); break; } } -void VisualDatabaseDisplayColorFilterWidget::removeFilter(QChar essence) +void VisualDatabaseDisplayColorFilterWidget::removeFilter(QChar color) { - QString essenceString(essence); - const QList filters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor); - for (const CardFilter *filter : filters) { - if (filter->term() == essenceString) { - filterModel->removeFilter(filter); + QString colorString = color; + + // Remove inclusion filters + QList allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor); + QList matchingFilters; + + for (const CardFilter *filter : allColorFilters) { + if (filter->term() == color) { + matchingFilters.append(filter); } } - // The empty selection means "Any Essence", not "exclude everything". - if (essencePicker->activeEssences().isEmpty()) { - filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor); - return; + for (const CardFilter *filter : matchingFilters) { + filterModel->removeFilter(filter); } + // Add exclusion filters if the mode demands it switch (currentMode) { case FilterMode::ExactMatch: + filterModel->addFilter( + new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor)); + break; + case FilterMode::IncludeExclude: filterModel->addFilter( - new CardFilter(essenceString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor)); + new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor)); break; + case FilterMode::Includes: + // No exclusion in Includes mode break; } } @@ -141,12 +170,10 @@ void VisualDatabaseDisplayColorFilterWidget::updateFilterMode() filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor); - const QSet selected = essencePicker->activeEssences(); - // No selections should never generate 15 mutually excluding filters. - if (!selected.isEmpty()) { - for (const QChar essence : Essence::symbols()) { - handleColorToggled(essence, selected.contains(essence)); - } + const QList manaSymbolWidgets = findChildren(); + + for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) { + handleColorToggled(manaSymbolWidget->getSymbolChar(), manaSymbolWidget->isColorActive()); } filterModel->blockSignals(false); @@ -157,44 +184,79 @@ void VisualDatabaseDisplayColorFilterWidget::updateFilterMode() emit filterModeChanged(currentMode); } -void VisualDatabaseDisplayColorFilterWidget::setEssenceActive(QChar essence, bool active) +void VisualDatabaseDisplayColorFilterWidget::setManaSymbolActive(QChar color, bool active) { - essencePicker->setEssenceActive(essence, active); + QList manaSymbolWidgets = findChildren(); + + for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) { + if (manaSymbolWidget->getSymbolChar() == color) { + manaSymbolWidget->setColorActive(active); + } + } } QList VisualDatabaseDisplayColorFilterWidget::getActiveColors() { - QList result; - const QSet selected = essencePicker->activeEssences(); - for (const QChar essence : Essence::symbols()) { - if (selected.contains(essence)) { - result.append(essence); + QList activeColors; + QList manaSymbolWidgets = findChildren(); + + for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) { + if (manaSymbolWidget->isColorActive()) { + activeColors.append(manaSymbolWidget->getSymbolChar()); } } - return result; + + return activeColors; } void VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel() { - const QList filters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor); + QList allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor); - for (const QChar essence : Essence::symbols()) { - bool selected = false; - for (const CardFilter *filter : filters) { - if (filter->term() != QString(essence)) { - continue; - } - if (currentMode == FilterMode::ExactMatch) { - selected = filter->type() == CardFilter::Type::TypeAnd; - } else { - selected = filter->type() == CardFilter::Type::TypeOr; - } - if (selected) { - break; + QList manaSymbolWidgets = findChildren(); + + for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) { + bool found = false; + for (const CardFilter *filter : allColorFilters) { + if (manaSymbolWidget->getSymbolChar() == filter->term()) { + switch (currentMode) { + case FilterMode::ExactMatch: + switch (filter->type()) { + case CardFilter::Type::TypeAnd: + setManaSymbolActive(filter->term().at(0), true); + break; + default: + setManaSymbolActive(filter->term().at(0), false); + break; + } + break; + case FilterMode::Includes: + switch (filter->type()) { + case CardFilter::Type::TypeOr: + setManaSymbolActive(filter->term().at(0), true); + break; + default: + setManaSymbolActive(filter->term().at(0), false); + break; + } + break; + case FilterMode::IncludeExclude: + switch (filter->type()) { + case CardFilter::Type::TypeOr: + setManaSymbolActive(filter->term().at(0), true); + break; + default: + setManaSymbolActive(filter->term().at(0), false); + break; + } + break; + } + found = true; } } - // Signals are blocked inside the picker; loading saved filters must - // not mutate the FilterTreeModel again. - setEssenceActive(essence, selected); + + if (!found) { + setManaSymbolActive(manaSymbolWidget->getSymbolChar(), false); + } } } diff --git a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.h b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.h index b1b268444..184f1f96c 100644 --- a/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.h +++ b/cockatrice/src/interface/widgets/visual_database_display/visual_database_display_color_filter_widget.h @@ -18,7 +18,6 @@ class FilterTreeModel; class QComboBox; class QHBoxLayout; -class EssenceFilterPicker; class VisualDatabaseDisplayColorFilterCircleWidget : public QWidget { @@ -64,7 +63,7 @@ private slots: void addFilter(QChar color); void removeFilter(QChar color); void updateFilterMode(); - void setEssenceActive(QChar essence, bool active); + void setManaSymbolActive(QChar color, bool active); QList getActiveColors(); void syncWithFilterModel(); @@ -72,7 +71,6 @@ private: FilterTreeModel *filterModel; QHBoxLayout *layout; QComboBox *modeComboBox; - EssenceFilterPicker *essencePicker = nullptr; FilterMode currentMode = FilterMode::Includes; // Default mode QtUtils::Debouncer syncDebouncer{100}; }; diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp index da6a00fbb..1ac4c856c 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp +++ b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.cpp @@ -1,12 +1,12 @@ #include "deck_preview_color_identity_filter_widget.h" -#include "../../cards/additional_info/essence_filter_picker.h" +#include "../../cards/additional_info/mana_symbol_widget.h" #include "../visual_deck_storage_sort_filter_proxy_model.h" #include #include -#include #include +#include #include DeckPreviewColorIdentityFilterWidget::DeckPreviewColorIdentityFilterWidget(QWidget *parent) @@ -16,30 +16,34 @@ DeckPreviewColorIdentityFilterWidget::DeckPreviewColorIdentityFilterWidget(QWidg layout->setSpacing(5); layout->setContentsMargins(0, 0, 0, 0); - // Fifteen Essence codes don't fit as inline buttons beside sorting and - // search. The popup keeps all codes selectable without expanding the row. - essencePicker = new EssenceFilterPicker(this); - layout->addWidget(essencePicker); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); - setMinimumWidth(essencePicker->width()); + QString fullColorIdentity = "WUBRG"; + for (const QChar &color : fullColorIdentity) { + auto *manaSymbol = new ManaSymbolWidget(this, color, false, true); + manaSymbol->setFixedWidth(25); - connect(essencePicker, &EssenceFilterPicker::essenceToggled, this, - [this](QChar /*essence*/, bool /*active*/) { emit activeColorsChanged(); }); - connect(essencePicker, &EssenceFilterPicker::selectionCleared, this, - [this]() { emit activeColorsChanged(); }); + layout->addWidget(manaSymbol); + + // Initialize the activeColors map + activeColors[color] = false; + + // Connect the color toggled signal + connect(manaSymbol, &ManaSymbolWidget::colorToggled, this, + &DeckPreviewColorIdentityFilterWidget::handleColorToggled); + } toggleButton = new QPushButton(this); - essencePicker->addPopupControl(toggleButton); + layout->addWidget(toggleButton); + + // Connect the button's clicked signal connect(toggleButton, &QPushButton::clicked, this, &DeckPreviewColorIdentityFilterWidget::updateFilterMode); + // Call retranslateUi to set the initial text retranslateUi(); } void DeckPreviewColorIdentityFilterWidget::retranslateUi() { - essencePicker->retranslateUi(); - - // Preserve existing Exact / Includes / Excludes filter semantics. + // Set the toggle button text based on the current mode switch (filterMode) { case VisualDeckStorageSortFilterProxyModel::FilterMode::ExactMatch: toggleButton->setText(tr("Mode: Exact Match")); @@ -51,21 +55,32 @@ void DeckPreviewColorIdentityFilterWidget::retranslateUi() toggleButton->setText(tr("Mode: Excludes")); break; } - toggleButton->setToolTip(tr("Essence filter mode (exact match, includes, or excludes)")); + toggleButton->setToolTip(tr("Color identity filter mode (AND/OR/NOT conjunctions of filters)")); } +/** + * @brief The colors that are currently toggled on. + */ QSet DeckPreviewColorIdentityFilterWidget::getActiveColors() const { - return essencePicker->activeEssences(); + QSet activeColorSet; + for (auto it = activeColors.constBegin(); it != activeColors.constEnd(); ++it) { + if (it.value()) { + activeColorSet.insert(it.key()); + } + } + return activeColorSet; } -void DeckPreviewColorIdentityFilterWidget::handleColorToggled(QChar /*essence*/, bool /*active*/) +void DeckPreviewColorIdentityFilterWidget::handleColorToggled(QChar color, bool active) { + activeColors[color] = active; emit activeColorsChanged(); } void DeckPreviewColorIdentityFilterWidget::updateFilterMode() { + // Cycle through the modes switch (filterMode) { case VisualDeckStorageSortFilterProxyModel::FilterMode::ExactMatch: filterMode = VisualDeckStorageSortFilterProxyModel::FilterMode::Includes; @@ -78,6 +93,6 @@ void DeckPreviewColorIdentityFilterWidget::updateFilterMode() break; } - retranslateUi(); + retranslateUi(); // Update the button text emit filterModeChanged(filterMode); } diff --git a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.h b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.h index 0ccf729d3..e0c90034c 100644 --- a/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.h +++ b/cockatrice/src/interface/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_filter_widget.h @@ -9,13 +9,13 @@ #include "../visual_deck_storage_sort_filter_proxy_model.h" #include +#include #include #include #include class QHBoxLayout; class QPushButton; -class EssenceFilterPicker; class DeckPreviewColorIdentityFilterWidget : public QWidget { @@ -57,7 +57,7 @@ private slots: private: QHBoxLayout *layout; QPushButton *toggleButton; - EssenceFilterPicker *essencePicker = nullptr; + QMap activeColors; VisualDeckStorageSortFilterProxyModel::FilterMode filterMode = VisualDeckStorageSortFilterProxyModel::Includes; }; diff --git a/cockatrice/src/interface/window_main.cpp b/cockatrice/src/interface/window_main.cpp index df948bb43..d546676bc 100644 --- a/cockatrice/src/interface/window_main.cpp +++ b/cockatrice/src/interface/window_main.cpp @@ -120,17 +120,17 @@ class LocalServerInterface; -#define GITHUB_PAGES_URL "https://github.com/Continuum-Universes-Studio/Continuumatrice" +#define GITHUB_PAGES_URL "https://cockatrice.github.io" #define GITHUB_CONTRIBUTORS_URL "https://github.com/Cockatrice/Cockatrice/graphs/contributors" #define GITHUB_CONTRIBUTE_URL "https://github.com/Cockatrice/Cockatrice#" #define GITHUB_TRANSIFEX_TRANSLATORS_URL "https://github.com/Cockatrice/Cockatrice/wiki/Translator-Hall-of-Fame" #define GITHUB_TRANSLATOR_FAQ_URL "https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ" -#define GITHUB_ISSUES_URL "https://github.com/Continuum-Universes-Studio/Continuumatrice/issues" +#define GITHUB_ISSUES_URL "https://github.com/Cockatrice/Cockatrice/issues" #define GITHUB_TROUBLESHOOTING_URL "https://github.com/Cockatrice/Cockatrice/wiki/Troubleshooting" #define GITHUB_FAQ_URL "https://github.com/Cockatrice/Cockatrice/wiki/Frequently-Asked-Questions" -const QString MainWindow::appName = "Continuumatrice"; -const QStringList MainWindow::fileNameFilters = QStringList() << QObject::tr("Continuumatrice card database (*.xml)") +const QString MainWindow::appName = "Cockatrice"; +const QStringList MainWindow::fileNameFilters = QStringList() << QObject::tr("Cockatrice card database (*.xml)") << QObject::tr("All files (*.*)"); inline Q_LOGGING_CATEGORY(MainWindowLog, "main_window"); @@ -231,7 +231,7 @@ void MainWindow::actWatchReplay() { QFileDialog dlg(this, tr("Load replay")); dlg.setDirectory(SettingsCache::instance().paths().getReplaysPath()); - dlg.setNameFilters(QStringList() << QObject::tr("Continuumatrice replays (*.cor)")); + dlg.setNameFilters(QStringList() << QObject::tr("Cockatrice replays (*.cor)")); if (!dlg.exec()) { return; } @@ -292,24 +292,27 @@ void MainWindow::actExit() void MainWindow::actAbout() { QMessageBox mb( - QMessageBox::NoIcon, tr("About Continuumatrice"), - QStringLiteral("Continuumatrice
") + - tr("Continuum Universes Studio") + QStringLiteral(" — ") + tr("Version") + - QStringLiteral(" ") + QString(VERSION_STRING) + - QStringLiteral("

") + - tr("Continuumatrice is a standalone Continuum Universes card-game client.") + - QStringLiteral("
") + - tr("Source code and issues") + QStringLiteral("

") + - tr("Based on the open-source Cockatrice project. The original developers and contributors " - "retain credit for their work, and the applicable open-source license remains in force.") + - QStringLiteral("
") + - tr("Upstream Cockatrice project") + QStringLiteral(""), + QMessageBox::NoIcon, tr("About Cockatrice"), + QString("Cockatrice (" + QString::fromStdString(BUILD_ARCHITECTURE) + ")
" + + tr("Version") + QString(" %1").arg(VERSION_STRING) + "

" + + tr("Cockatrice Webpage") + "
" + "
" + tr("Project Manager:") + + "
Zach Halpern

" + "" + tr("Past Project Managers:") + + "
Gavin Bisesi
Max-Wilhelm Bruker
Marcus Schütz

" + "" + tr("Developers:") + + "
" + "" + tr("Our Developers") + "
" + + "" + tr("Help Develop!") + "

" + "" + + tr("Translators:") + "
" + "" + + tr("Our Translators") + "
" + "" + + tr("Help Translate!") + "

" + "" + tr("Support:") + "
" + "" + tr("Report an Issue") + "
" + "" + tr("Troubleshooting") + "
" + "" + tr("F.A.Q.") + "
"), QMessageBox::Ok, this); mb.setIconPixmap( - themePixmap(QStringLiteral("continuumatrice")).scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + themePixmap(QStringLiteral("cockatrice")).scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); mb.setTextInteractionFlags(Qt::TextBrowserInteraction); mb.exec(); } + void MainWindow::actTips() { if (tip != nullptr) { @@ -366,7 +369,7 @@ void MainWindow::retranslateUi() #if defined(__APPLE__) /* For OSX */ cockatriceMenu->setTitle(tr("A&ctions")); #else - cockatriceMenu->setTitle(tr("&Continuumatrice")); + cockatriceMenu->setTitle(tr("&Cockatrice")); #endif dbMenu->setTitle(tr("C&ard Database")); @@ -380,13 +383,11 @@ void MainWindow::retranslateUi() tabsMenu->setTitle(tr("Tabs")); helpMenu->setTitle(tr("&Help")); - aAbout->setText(tr("&About Continuumatrice")); + aAbout->setText(tr("&About Cockatrice")); aTips->setText(tr("&Tip of the Day")); aUpdate->setText(tr("Check for Client Updates")); - aCheckCardUpdates->setText(tr("Legacy MTG Oracle importer (disabled)")); - aCheckCardUpdates->setEnabled(false); - aCheckCardUpdatesBackground->setText(tr("Automatic MTG card updates (disabled)")); - aCheckCardUpdatesBackground->setEnabled(false); + aCheckCardUpdates->setText(tr("Check for Card Updates...")); + aCheckCardUpdatesBackground->setText(tr("Check for Card Updates (Automatic)")); aStatusBar->setText(tr("Show Status Bar")); aViewLog->setText(tr("View &Debug Log")); aOpenSettingsFolder->setText(tr("Open Settings Folder")); @@ -650,23 +651,20 @@ void MainWindow::startupConfigCheck() startLocalGame(options); } - // The fork has independent release channels. Until a Continuumatrice - // release is published, do not auto-check or run upstream Cockatrice updates. - SettingsCache::instance().updates().setCheckUpdatesOnStartup(false); + if (SettingsCache::instance().updates().getCheckUpdatesOnStartup()) { + actCheckClientUpdates(); + } + + actCheckCommanderBracketDefinitionUpdates(); - // No MTG Commander-bracket network requests in Continuumatrice. if (isCleanInstall) { // no config found, 99% new clean install qCInfo(WindowMainStartupVersionLog) << "Startup: old client version empty, assuming first start after clean install"; - SettingsCache::instance().downloads().resetToDefaultURLs(); // Empty in Continuumatrice: no MTG providers + SettingsCache::instance().downloads().resetToDefaultURLs(); // populate the download urls SettingsCache::instance().network().setClientVersion(VERSION_STRING); - // No upstream Cockatrice first-run wizard, Magic importer, or default - // server discovery. Cards are loaded from this application's own XML. - QMessageBox::information(this, tr("Welcome to Continuumatrice"), - tr("Continuumatrice uses its own card database. " - "Add your card XML files using Card Database > Add custom sets/cards " - "or place them in the Continuumatrice customsets directory.")); + actCheckServerUpdates(); + runFirstRunWizard(true); if (QString(VERSION_STRING).contains("custom", Qt::CaseInsensitive)) { SettingsCache::instance().updates().setCheckUpdatesOnStartup(false); @@ -679,7 +677,7 @@ void MainWindow::startupConfigCheck() << "Startup: old client version" << SettingsCache::instance().network().getClientVersion() << "differs, assuming first start after update"; if (SettingsCache::instance().updates().getNotifyAboutNewVersion()) { - // Do not automatically launch the upstream Magic-card Oracle importer. + alertForcedOracleRun(VERSION_STRING, true); } qCInfo(WindowMainStartupShortcutsLog) << "Migrating shortcuts after update detected."; @@ -706,8 +704,35 @@ void MainWindow::startupConfigCheck() // previous config from this version found qCInfo(WindowMainStartupVersionLog) << "Startup: found config with current version"; - // No automatic upstream card database downloads in Continuumatrice. + if (SettingsCache::instance().updates().getCardUpdateCheckRequired()) { + if (SettingsCache::instance().updates().getStartupCardUpdateCheckPromptForUpdate()) { + auto startupCardCheckDialog = new DlgStartupCardCheck(this); + if (startupCardCheckDialog->exec() == QDialog::Accepted) { + switch (startupCardCheckDialog->group->checkedId()) { + case 0: // foreground + actCheckCardUpdates(); + break; + case 1: // background + actCheckCardUpdatesBackground(); + break; + case 2: // background + always + SettingsCache::instance().updates().setStartupCardUpdateCheckPromptForUpdate(false); + SettingsCache::instance().updates().setStartupCardUpdateCheckAlwaysUpdate(true); + actCheckCardUpdatesBackground(); + break; + case 3: // don't prompt again + don't run + SettingsCache::instance().updates().setStartupCardUpdateCheckPromptForUpdate(false); + SettingsCache::instance().updates().setStartupCardUpdateCheckAlwaysUpdate(false); + break; + default: + break; + } + } + } else if (SettingsCache::instance().updates().getStartupCardUpdateCheckAlwaysUpdate()) { + actCheckCardUpdatesBackground(); + } + } // Run the tips dialog only on subsequent startups. // On the first run after an install/update the startup is already crowded enough @@ -835,12 +860,12 @@ void MainWindow::alertForcedOracleRun(const QString &version, bool isUpdate) { if (isUpdate) { QMessageBox::information(this, tr("New Version"), - tr("Congratulations on updating to Continuumatrice %1!\n" + tr("Congratulations on updating to Cockatrice %1!\n" "Oracle will now launch to update your card database.") .arg(version)); } else { - QMessageBox::information(this, tr("Continuumatrice installed"), - tr("Congratulations on installing Continuumatrice %1!\n" + QMessageBox::information(this, tr("Cockatrice installed"), + tr("Congratulations on installing Cockatrice %1!\n" "Oracle will now launch to install the initial card database.") .arg(version)); } @@ -1085,7 +1110,7 @@ void MainWindow::cardDatabaseLoadingFailed() QMessageBox msgBox; msgBox.setWindowTitle(tr("Card database")); msgBox.setIcon(QMessageBox::Question); - msgBox.setText(tr("Continuumatrice is unable to load the card database.\n" + msgBox.setText(tr("Cockatrice is unable to load the card database.\n" "Do you want to update your card database now?\n" "If unsure or first time user, choose \"Yes\"")); @@ -1168,7 +1193,7 @@ void MainWindow::cardDatabaseAllNewSetsEnabled() } QMessageBox::information( this, tr("Welcome"), - tr("This is the first time you've loaded this card database in Continuumatrice.\nAll the sets in the " + tr("Hi! It seems like you're running this version of Cockatrice for the first time.\nAll the sets in the " "card " "database have been enabled.\nRead more about changing the set order or disabling specific sets and " "consequent effects in the \"Manage Sets\" dialog.")); @@ -1182,28 +1207,16 @@ void MainWindow::cardDatabaseAllNewSetsEnabled() /* CARD UPDATER */ void MainWindow::actCheckCardUpdates() { - QMessageBox::information(this, tr("Continuumatrice card data"), - tr("The upstream Oracle importer downloads Magic card data and is disabled " - "in Continuumatrice. Import Continuum Universes XML cards using " - "Card Database > Add custom sets/cards instead.")); + createCardUpdateProcess(); } void MainWindow::actCheckCardUpdatesBackground() { - // Never run the upstream Oracle MTG importer unattended. - qCInfo(CardDatabaseUpdateLog) << "The legacy Magic Oracle importer is disabled in Continuumatrice"; + createCardUpdateProcess(true); } void MainWindow::createCardUpdateProcess(bool background) { - Q_UNUSED(background); - // Direct callers must also be unable to launch the upstream MTG importer. - QMessageBox::information(this, tr("Importer unavailable"), - tr("Continuumatrice does not run the upstream Magic Oracle importer. " - "Import Continuum Universes XML sets directly.")); - return; - - // Legacy importer implementation retained for upstream reference only. if (cardUpdateProcess) { qCDebug(CardDatabaseUpdateLog) << "A card database update is already running; asking it to come to the front"; if (requestCardUpdateRaise()) { @@ -1296,7 +1309,7 @@ void MainWindow::createCardUpdateProcess(bool background) statusBar()->showMessage(tr("Card database update running.")); if (trayIcon && SettingsCache::instance().updates().getCardDatabaseUpdateTrayNotification()) { trayIcon->showMessage(tr("Card database update started"), - tr("Continuumatrice is updating the card database in the background.")); + tr("Cockatrice is updating the card database in the background.")); } } } @@ -1438,7 +1451,7 @@ void MainWindow::cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatu bool MainWindow::requestCardUpdateRaise() { - const QString serverName = scopedLocalServerName(QStringLiteral("ContinuumatriceOracleRaise")); + const QString serverName = scopedLocalServerName(QStringLiteral("CockatriceOracleRaise")); QLocalSocket socket; socket.connectToServer(serverName); if (!socket.waitForConnected(250)) { @@ -1456,7 +1469,7 @@ void MainWindow::announceCardUpdateRunning() statusBar()->showMessage(tr("Card database update running.")); } else if (trayIcon && SettingsCache::instance().updates().getCardDatabaseUpdateTrayNotification()) { trayIcon->showMessage(tr("Card database update running"), - tr("Continuumatrice is updating the card database in the background.")); + tr("Cockatrice is updating the card database in the background.")); } } @@ -1472,8 +1485,9 @@ void MainWindow::notifyCardUpdateFinishedFromTray(bool success) void MainWindow::actCheckServerUpdates() { - // Upstream Cockatrice public servers are not Continuumatrice game servers. - qCInfo(MainWindowLog) << "Upstream public server discovery disabled in Continuumatrice."; + auto hps = new HandlePublicServers(this); + hps->downloadPublicServers(); + connect(hps, &HandlePublicServers::sigPublicServersDownloadedSuccessfully, [=]() { hps->deleteLater(); }); } void MainWindow::actCheckClientUpdates() @@ -1493,8 +1507,12 @@ void MainWindow::checkClientUpdatesFinished(bool needToUpdate, bool /* isCompati void MainWindow::actCheckCommanderBracketDefinitionUpdates() { - // Commander brackets are a Magic: The Gathering system, not Continuum Universes. - qCInfo(MainWindowLog) << "Upstream Commander bracket updates disabled in Continuumatrice."; + auto *handler = new HandleCommanderBrackets(this); + + connect(handler, &HandleCommanderBrackets::sigBracketDefinitionsDownloaded, this, + []() { qDebug() << "Bracket definitions loaded"; }); + + handler->downloadBracketDefinitions(); } void MainWindow::refreshShortcuts() diff --git a/cockatrice/src/interface/window_main.h b/cockatrice/src/interface/window_main.h index 07638fee6..9e342e4b0 100644 --- a/cockatrice/src/interface/window_main.h +++ b/cockatrice/src/interface/window_main.h @@ -224,11 +224,11 @@ public: explicit MainWindow(QWidget *parent = nullptr); void setConnectTo(QString url) { - connectTo = QUrl(QString("continuumatrice://%1").arg(url)); + connectTo = QUrl(QString("cockatrice://%1").arg(url)); } // When set, the window's own startup connection (--connect or auto-connect // on first activation) is skipped. Used for activation launches: the intent - // chain triggered by a continuumatrice:// URL owns the connection, and letting + // chain triggered by a cockatrice:// URL owns the connection, and letting // auto-connect race against it caused two connectToServer calls to tear // each other down. onUrlChainFinished() clears this and retries the startup // connection when the link's chain ended without connecting. diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index b10f9b7f5..43b5f2e83 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -69,7 +69,6 @@ #include #include #include -#include #include #include #include @@ -85,7 +84,7 @@ SoundEngine *soundEngine; QSystemTrayIcon *trayIcon; ThemeManager *themeManager; -const QString translationPrefix = "cockatrice"; // Keep inherited translation catalogue until retranslated. +const QString translationPrefix = "cockatrice"; QString translationPath; static void CockatriceLogger(QtMsgType type, const QMessageLogContext &ctx, const QString &message) @@ -138,9 +137,9 @@ QString const generateClientID() static QString redactActivationUrl(const QString &url) { // Activation URLs carry secrets in their query string (e.g. the deck share - // token); log only the scheme and the action (continuumatrice://opendeck), never + // token); log only the scheme and the action (cockatrice://opendeck), never // the parameters. - if (!url.startsWith(QStringLiteral("continuumatrice://"))) { + if (!url.startsWith(QStringLiteral("cockatrice://"))) { return url; } const QUrl parsed(url); @@ -220,7 +219,7 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); #ifdef Q_OS_MAC - UrlSchemeEventFilter cockatriceFilter(QStringList{QStringLiteral("continuumatrice")}); + UrlSchemeEventFilter cockatriceFilter(QStringList{QStringLiteral("cockatrice")}); QStringList pendingMacUrls; @@ -258,7 +257,7 @@ int main(int argc, char *argv[]) // Declared before crash-reporter init: the startup attach below needs the log tail while the // file still holds the crashed session's lines. const QString persistentLogPath = - SettingsCache::instance().getSettingsPath() + QStringLiteral("logs/continuumatrice.log"); + SettingsCache::instance().getSettingsPath() + QStringLiteral("logs/cockatrice.log"); Logger::getInstance().setPersistentLogPath(persistentLogPath); // Crash reporting: init the SDK early so a crash anywhere below is captured into the @@ -334,7 +333,7 @@ int main(int argc, char *argv[]) #elif defined(Q_OS_WIN) translationPath = qApp->applicationDirPath() + "/translations"; #else // linux - translationPath = qApp->applicationDirPath() + "/../share/continuumatrice/translations"; + translationPath = qApp->applicationDirPath() + "/../share/cockatrice/translations"; #endif // Command-line parser @@ -417,24 +416,6 @@ int main(int argc, char *argv[]) CardDatabaseManager::setCardDatabasePathProvider(&SettingsCache::instance()); CardDatabaseManager::setCardSetPriorityController(&SettingsCache::instance().cardDatabase()); - // Existing early fork profiles may still contain the upstream Scryfall / - // Gatherer defaults saved by older builds. Migrate only known MTG provider - // URLs, leaving user-authored Continuum Universes endpoints untouched. - auto &downloadSettings = SettingsCache::instance().downloads(); - const QStringList savedImageUrls = downloadSettings.getAllURLs(); - QStringList compatibleImageUrls; - for (const QString &url : savedImageUrls) { - if (!url.contains(QStringLiteral("scryfall.com"), Qt::CaseInsensitive) && - !url.contains(QStringLiteral("scryfall.io"), Qt::CaseInsensitive) && - !url.contains(QStringLiteral("gatherer.wizards.com"), Qt::CaseInsensitive)) { - compatibleImageUrls.append(url); - } - } - if (compatibleImageUrls != savedImageUrls) { - downloadSettings.setDownloadUrls(compatibleImageUrls); - qCInfo(MainLog) << "Removed inherited MTG image sources from Continuumatrice profile."; - } - qCInfo(MainLog) << "Starting main program"; // Front-load the card database before constructing the main window. The @@ -467,7 +448,7 @@ int main(int argc, char *argv[]) // auto-connect must not race against it (two connectToServer calls tear // each other down via doDisconnectFromServer). bool hasUrlActivation = std::any_of(startupFiles.begin(), startupFiles.end(), [](const QString &file) { - return file.startsWith(QStringLiteral("continuumatrice://")); + return file.startsWith(QStringLiteral("cockatrice://")); }); #ifdef Q_OS_MAC // On macOS the launch can arrive through the URL scheme instead of as a @@ -475,12 +456,12 @@ int main(int argc, char *argv[]) // window would auto-connect into the link's own connection attempt. hasUrlActivation = hasUrlActivation || std::any_of(pendingMacUrls.cbegin(), pendingMacUrls.cend(), - [](const QString &url) { return url.startsWith(QStringLiteral("continuumatrice://")); }); + [](const QString &url) { return url.startsWith(QStringLiteral("cockatrice://")); }); #endif ui.setSkipStartupAutoConnect(hasUrlActivation); auto handleActivation = [&ui](const QString &file) { - if (file.startsWith("continuumatrice://")) { + if (file.startsWith("cockatrice://")) { qCInfo(MainLog) << "Handling URL activation:" << redactActivationUrl(file); // Route through the window's persistent url parser: it serializes // link chains so activations handed over while another chain is @@ -506,12 +487,12 @@ int main(int argc, char *argv[]) } qCInfo(MainLog) << "MainWindow constructor finished"; - ui.setWindowIcon(themePixmap(QStringLiteral("continuumatrice"))); + ui.setWindowIcon(themePixmap(QStringLiteral("cockatrice"))); // Base name of the desktop file installed next to the binary, without the ".desktop" // extension. Wayland reads the window icon from it, and xdg-desktop-portal looks up // org.freedesktop.host.portal.Registry by this app ID, so it has to match the file name // exactly, down to the case. - QGuiApplication::setDesktopFileName("continuumatrice"); + QGuiApplication::setDesktopFileName("cockatrice"); SettingsCache::instance().network().setClientID(generateClientID()); diff --git a/cockatrice/themes/CMakeLists.txt b/cockatrice/themes/CMakeLists.txt index 800cc4b1d..70344d95a 100644 --- a/cockatrice/themes/CMakeLists.txt +++ b/cockatrice/themes/CMakeLists.txt @@ -6,10 +6,10 @@ set(defthemes Fabric Fusion Leather Plasma VelvetMarble System) if(UNIX) if(APPLE) - install(DIRECTORY ${defthemes} DESTINATION continuumatrice.app/Contents/Resources/themes/) + install(DIRECTORY ${defthemes} DESTINATION Cockatrice.app/Contents/Resources/themes/) else() # Assume linux - install(DIRECTORY ${defthemes} DESTINATION share/continuumatrice/themes/) + install(DIRECTORY ${defthemes} DESTINATION share/cockatrice/themes/) endif() elseif(WIN32) install(DIRECTORY ${defthemes} DESTINATION themes/) diff --git a/doc/carddatabase_v4/cards.xsd b/doc/carddatabase_v4/cards.xsd index a888edf69..df52d6327 100644 --- a/doc/carddatabase_v4/cards.xsd +++ b/doc/carddatabase_v4/cards.xsd @@ -73,7 +73,7 @@ - + diff --git a/doc/continuum-servatrice-hosting.md b/doc/continuum-servatrice-hosting.md deleted file mode 100644 index 16af80b7d..000000000 --- a/doc/continuum-servatrice-hosting.md +++ /dev/null @@ -1,107 +0,0 @@ -# Hosting Continuum-Servatrice - -**Continuum-Servatrice** is Continuumatrice's dedicated multiplayer server. It is built from the existing Servatrice C++ implementation, including accounts, lobbies, rooms, replays, game synchronization, TCP and WebSocket networking, and MySQL support. It also registers the three additional public pile zones: Limbo, Refuge, and Oblivion. - -You may run it alongside upstream Servatrice, but **never reuse the MTG server's database, schema, account tables, process name, or service configuration.** - -## Build client and server together - -On Linux with the existing development dependencies, from the repository root: - -```bash -cmake -S . -B build --fresh -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DWITH_CLIENT=ON -DWITH_SERVER=ON -DWITH_ORACLE=OFF - -cmake --build build --target continuumatrice continuum-servatrice --parallel 16 -``` - -Executable paths: - -- Client: `build/continuumatrice/continuumatrice` -- Dedicated server: `build/continuum-servatrice/continuum-servatrice` - -The inherited source directories `cockatrice/` and `servatrice/` remain for source compatibility. The **binary build directories and targets are distinct**. - -`WITH_SERVER` is `ON` by default; use `-DWITH_CLIENT=OFF -DWITH_SERVER=ON` for a dedicated server-only build (e.g., on a Linux host). This project deliberately excludes the upstream Magic Oracle importer. - -## New independent MySQL schema - -The repository includes `servatrice/continuum-servatrice.sql`. This is **not** the old `servatrice.sql`: its table identifiers use the `continuum_servatrice_` prefix matching the fork server configuration. Do not apply these SQL scripts to a production MTG Servatrice database. - -Create a separate database and limited account using MySQL. The following is an example for local MySQL (choose a real, strong password and do not commit it): - -```sql -CREATE DATABASE continuum_servatrice CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE USER 'continuum_servatrice'@'localhost' IDENTIFIED BY 'REPLACE_WITH_A_STRONG_PASSWORD'; -GRANT ALL PRIVILEGES ON continuum_servatrice.* TO 'continuum_servatrice'@'localhost'; -``` - -Execute those statements as a MySQL administrator, for example `mysql -u root -p`. Then initialize the *empty* database: - -```bash -mysql -u root -p continuum_servatrice < servatrice/continuum-servatrice.sql -``` - -This schema contains user and room tables; it is not the card catalogue. Card XML and images live on the client, and users joining the same game should use compatible Continuumatrice card databases. - -**Schema lifecycle:** The fork's new base SQL is isolated. Upstream schema migration scripts under `servatrice/migrations/` have not all been converted to the new prefix. Review/adapt each future migration against a **backup** of the fork database; never run the upstream migration scripts blindly on either product. - -## Configure server - -```bash -mkdir -p ~/.config/continuum-servatrice -cp servatrice/continuum-servatrice.ini.example ~/.config/continuum-servatrice/continuum-servatrice.ini -chmod 600 ~/.config/continuum-servatrice/continuum-servatrice.ini -``` - -Edit the private INI file, at least: - -```ini -[server] -name=Continuum-Servatrice -host=any -port=4757 -websocket_host=any -websocket_port=4758 - -[database] -type=mysql -prefix=continuum_servatrice -hostname=localhost -database=continuum_servatrice -user=continuum_servatrice -password=REPLACE_WITH_YOUR_MYSQL_PASSWORD -``` - -Those are the **fork's example ports** (TCP 4757, WebSocket 4758), separate from the usual upstream 4747/4748. The server supports other ports. If you run multiple game servers on the same host, explicitly choose distinct bound ports and independently configured databases. - -To start in the terminal: - -```bash -./build/continuum-servatrice/continuum-servatrice \ - --config "$HOME/.config/continuum-servatrice/continuum-servatrice.ini" \ - --log-to-console -``` - -A successful start requires database connectivity, a compatible schema, and available ports. Check the server's diagnostics rather than assuming a process that exits is running correctly. - -For Internet hosting, use firewall rules scoped to the ports you intend to expose, configure TLS / a secure reverse proxy for WebSockets, and avoid exposing MySQL directly to the Internet. The sample file has inherited settings for authentication, room permissions, email, and moderation; review them before public launch. - -## Client/server compatibility - -Continuumatrice supports standalone local games without a dedicated server. For remote games, use a **matching Continuum-Servatrice build**. Ordinary upstream Cockatrice servers don't define the `limbo`, `refuge`, or `oblivion` zone IDs. - -## Production verification - -- [ ] Client and server both compile from the same Git commit. -- [ ] They create `build/continuumatrice` and `build/continuum-servatrice` with correct filenames. -- [ ] Server starts with `--config` and can connect to the **independent** MySQL schema. -- [ ] Account registration/authentication, room joining, game creation and reconnect work. -- [ ] Cards move correctly into Limbo, Refuge and Oblivion for both players and after reconnect. -- [ ] TCP and WebSocket connect on the configured ports. -- [ ] No accesses to upstream MTG databases, profile directories, sockets, or HTTP update services. -- [ ] No migration script alters a production database without backup/review. -- [ ] Service hardening, logging, TLS and restart management are in place before public launch. - -**Status:** Committed source changes; dedicated server compilation, MySQL initialization, and runtime validation still need to be performed on the deployment system. diff --git a/doc/continuumatrice-battlefield.md b/doc/continuumatrice-battlefield.md index 88d20c3db..9b4fc5b37 100644 --- a/doc/continuumatrice-battlefield.md +++ b/doc/continuumatrice-battlefield.md @@ -10,18 +10,16 @@ Cockatrice's three visual table rows are reused as **Front**, **Support**, and * ## Optional color customization -The default zone colors are **Front `#dc143c` (Crimson)**, **Support `#c3b091`**, and **Command `#0a0e3f`**. - -To override the defaults, create `battlefield.ini` in your active **Continuumatrice** theme directory: +Create `battlefield.ini` in the active Cockatrice theme directory: ```ini [Zones] -Front = #dc143c -Support = #c3b091 -Command = #0a0e3f +Front = #A08E343D +Support = #A0286981 +Command = #A05E428C ``` -Qt accepts `#RRGGBB` for opaque colors and `#AARRGGBB` to specify transparency. The built-in defaults above are opaque; a theme can set translucent overrides explicitly. An existing `battlefield.ini` still takes precedence over these defaults. Reload the theme to apply changes. The default layout places Front closest to the opposing player's battlefield and Command closest to each player's hand. As in Cockatrice, the **Invert vertical coordinate** setting reverses the row placement (and its labels) when enabled. +The Qt color notation is `#AARRGGBB`, including transparency. Without this file, built-in red, teal, and purple shades are used. Reload the theme to apply changes. The default layout places Front closest to the opposing player's battlefield and Command closest to each player's hand. As in Cockatrice, the **Invert vertical coordinate** setting reverses the row placement (and its labels) when enabled. ## Font diff --git a/doc/continuumatrice-card-data.md b/doc/continuumatrice-card-data.md index a31b8d973..8e3f9a12f 100644 --- a/doc/continuumatrice-card-data.md +++ b/doc/continuumatrice-card-data.md @@ -30,13 +30,13 @@ Qt follows the XDG environment variables if set; the paths above are defaults, n ## Card database format -Continuumatrice reads its native **Continuumatrice XML v4**, and also accepts legacy Cockatrice XML v4 for import (schema `doc/carddatabase_v4/cards.xsd`). This is a database description and card list, not the rendered card images. You do not need to build new parsing logic to playtest your own cards. +Continuumatrice currently reads **Cockatrice XML v4** (schema `doc/carddatabase_v4/cards.xsd`). This is a database description and card list, not the rendered card images. You do not need to build new parsing logic to playtest your own cards. A self-contained *test-only* main database `cards.xml` can look like: ```xml - + CTEST @@ -70,7 +70,7 @@ A self-contained *test-only* main database `cards.xml` can look like: 2 - + ``` These names are examples, **not** Continuum Universes lore or playable canon. @@ -103,7 +103,3 @@ For reliable matching across reprints, the importer also recognizes names incorp 7. Check normal Cockatrice's profile is still intact and can open separately. **Magic Set Editor** is an editor for card layouts and card content, but its native project format is not Cockatrice XML v4. You will need a matching export script/template or a conversion pipeline that writes this XML and the image files. - -## Native XML root - -Continuumatrice generates and accepts ``. Existing `` files are still accepted by the parser, so older set-export pipelines can be migrated safely rather than rewritten blindly. diff --git a/doc/continuumatrice-card-fields.md b/doc/continuumatrice-card-fields.md deleted file mode 100644 index b96ec6543..000000000 --- a/doc/continuumatrice-card-fields.md +++ /dev/null @@ -1,69 +0,0 @@ -# Continuumatrice CCG card fields and numeric sorting - -This document covers **static card-definition metadata** loaded from Continuumatrice XML v4. It does not change mutable game resources, damage, armour degradation, campaign state or server match mechanics. - -## Source mapping - -| Continuumatrice field | Native XML v4 property | Field in existing Continuum MSE game | Legacy import fallback | Sorting | -| --- | --- | --- | --- | --- | -| Identity | `identity` | `alias` | `alias` | Locale-aware text | -| Attack (ATK) | `attack` | `strength` | `strength`, or Attack side of `pt` | Numeric | -| Armour | `armour` | `armor` | `armor` | Numeric | -| Health (HP) | `health` | `health` | Health side of `pt` | Numeric | -| ATK/HP | Derived from Attack and Health | MSE `strength` + `health` | `pt` via the individual stat fallbacks | Attack numeric first, then Health numeric | - -**Identity** is the CCG's **card alias**. It must not be confused with `essence` (which is the distinct Essence resource/type identity). The `Name` column still contains the actual card name. - -The XML importer already supports arbitrary text properties under ``, so adding these properties does not require a schema version change. - -## Example - -```xml - - Example Character - Static card data demonstration only. - - Character - The Guardian - 10 - 3 - 12 - M - - EXAMPLE - 0 - -``` - -The table view displays ATK/HP as `10/12`, with separate **Attack**, **Armour**, and **Health** columns. If a card has no authored stats, the combined display remains blank. When one stat is omitted, the missing side is shown as `?`. - -The database's sort dropdown discovers model columns dynamically, so the new fields are available for ascending/descending sorting. Numeric fields use numeric comparisons; the combined ATK/HP column sorts by Attack first and Health second. The former `P/T` column position is retained internally for compatibility with existing saved table views. - -## Exporter compatibility - -The matching Magic Set Editor exporter change is in `Continuum-Universes-MSE-Templates`: - -- `card.alias` → `` -- `card.strength` → `` -- `card.armor` → `` -- `card.health` → `` - -The MSE internal field identifiers are **not renamed**: that would otherwise risk breaking existing `.mse-set` files, render scripts and card styles. - -Older XML exports with `strength`, `armor`, `alias`, or `pt` remain readable. Native properties take precedence whenever present; no automatic rewriting of existing files occurs. - -## Test checklist - -```bash -cmake --build build --target continuumatrice carddatabase_test model_contract_test --parallel 16 -ctest --test-dir build -R 'CardInfoTest.ContinuumIdentityAndStatsHaveNativeAndLegacyPropertyAccess|ModelContract.ContinuumCardStatsAreSortableAndLabeled' --output-on-failure -``` - -- [ ] Import an MSE-exported set and verify Identity, Attack, Armour, Health and ATK/HP columns. -- [ ] Check Attack `9` precedes `10` and Armour `2` precedes `10`. -- [ ] Check ATK/HP sorts on Attack then Health. -- [ ] Verify legacy fields are still readable and an empty field is not assumed to mean zero. -- [ ] Verify the card details pane displays the new names, including **Armour**. -- [ ] Verify the existing Essence filter still operates independently of the Alias/Identity property. - -**Status:** Source changes committed; native compilation, tests and MSE GUI export still need local runtime verification. diff --git a/doc/continuumatrice-essence.md b/doc/continuumatrice-essence.md deleted file mode 100644 index 577544dd2..000000000 --- a/doc/continuumatrice-essence.md +++ /dev/null @@ -1,111 +0,0 @@ -# Continuumatrice Essence symbols (CCG card catalogue) - -Continuumatrice uses **Essence** for card resources, not Magic: The Gathering mana. The canonical Essence symbol order is: - -```text -M S O G E A Q C J B R L V T P N -``` - -These are **16 distinct symbols**. `N` is explicitly **colorless Essence**. The non-colorless codes use the existing MSE gemstone mappings (including **J = Jasper**), but the client does not assign their final artwork, gameplay rules or resource behavior. Do not infer anything from similarly named MTG mana letters. All Essence codes are uppercase. - -## Native Continuumatrice XML v4 - -The parser supports arbitrary XML `` entries, so no change to the existing XML v4 schema format is needed. These three native property names are defined in `libcockatrice/card/game_specific_terms.h`: - -| Property | Meaning | -| --- | --- | -| `essencecost` | Card's explicitly authored Essence cost, e.g. `{M}{S}{N}` | -| `essence` | Card's explicitly authored Essence identity/types, e.g. `MSN` | -| `essencevalue` | Optional numeric aggregate value for existing cost-curve UI; this is not calculated from cost | - -**Test-only sample**, not a playable/canonical card: - -```xml - - - - CTESTEssence Import Test - - - - Essence Sample Card - Test Essence symbol rendering only. - - Test Card - {M}{S}{N} - MSN - - CTEST - 1 - - - -``` - -Do not automatically derive `essence` or `essencevalue` from `essencecost` until gameplay rules define the correct relationship. The card database **stores metadata**, not mutable match resource balances. - -Previously authored XML ``, ``, and `` properties remain readable by the inherited `CardInfo` API for compatibility, but new Continuumatrice cards should use the native properties. Format compatibility does **not** mean MTG `W` or `U` are accepted as Essence symbols. - -## Icons and custom asset assignment - -All 16 Essence letters have small bundled SVG **placeholder** icons under: - -```text -cockatrice/resources/icons/essence/ - M.svg S.svg O.svg G.svg E.svg A.svg Q.svg C.svg J.svg B.svg - R.svg L.svg V.svg T.svg P.svg N.svg -``` - -Each is rendered using an independent `icons/essence/` asset namespace. In the currently selected Continuumatrice theme you may override any SVG with an SVG, PNG, JPG or JPEG using the same stem: - -```text -~/.local/share/Continuumatrice/themes//icons/essence/ - M.svg - S.png - N.svg -``` - -The current bundled placeholders show just the letter. **J = Jasper** (matching the existing Continuum MSE authoring templates). `N` is neutral gray; the others use a shared neutral treatment. They are **not** final Essence artwork or a commitment to elemental color assignments. The chart interface uses a separately generated *chart-only* palette so sixteen statistical series can be distinguished. - -## Features updated - -- Card database properties: native Essence cost, identity and optional value, with fallbacks for older XML. -- Card information display and deck editor column headers: `Essence`, `Essence Cost`, `Essence Value`. -- Visual Deck Storage and Visual Database filters share a compact `Essence: Any` dropdown with a 4×4 grid of all 16 icons. Select multiple icons without closing the popup, clear selections within the popup, and change filtering mode **inside that same popup**. Only one compact control occupies the toolbar. The dropdown summarizes selected Essence codes and supports N independently from C. -- Cost parser: single Essence symbols in bare form (`MSN`) or braces (`{M}{S}{N}`); numeric tokens may be preserved as generic amounts. -- Deck identity: unique, ordered Essence codes (including `N`). -- Search filters: filter for specific Essence codes, or `colorless` meaning `N`. An empty identity is not automatically `N`. -- Analytics: counts all 16 Essence cost symbols. Production counts only literal `Add {M}`-style grants to avoid guessing any resource rules. - -Hybrid, phyrexian, and other MTG-specific mana-cost syntax are **not** introduced as Essence mechanics. Nor is automatic calculation of resource costs, card affordability, or game turn rules included. - -## Compact Essence filter UI - -The former five-button MTG `WUBRG` filters are replaced in both Visual Deck Storage and Visual Database. On either screen, click `Essence: Any` to open a **4-column × 4-row** grid of the complete Essence set. Each icon can be toggled independently and the menu stays open for multiple selections. Use **Clear Essence selection** to reset. The button summarizes your selection, and the filtering mode remains available **inside** the popup (Exact Match / Includes / Excludes in deck storage, or Include / Exclude in the database). - -The picker is a single reusable Qt widget (`essence_filter_picker.h/.cpp`). This keeps both screens synchronized with `Essence::symbols()` without repeating a hand-maintained UI list. The UI uses SVG assets from `icons/essence/`, never old MTG `icons/mana/` assets. - - -## Build and verification - -From `feature/continuumatrice-essence`: - -```bash -cmake -S . -B build --fresh -G Ninja -DCMAKE_BUILD_TYPE=Release \ - -DWITH_CLIENT=ON -DWITH_SERVER=ON -DWITH_ORACLE=OFF -DTEST=ON -cmake --build build --target continuumatrice continuum-servatrice essence_types_test --parallel 16 -ctest --test-dir build -R EssenceTypesTest --output-on-failure -./build/continuumatrice/continuumatrice -``` - -Verify that all sixteen icons render, `N` loads and filters separately from `C`, the XML importer accepts `essencecost` and `essence`, and the analytics show no WUBRG assumptions. - -**Status:** Source-level implementation committed. User-side Qt compilation and visual/runtime testing remain required. - -## November UI stability fixes - -The database toolbar now keeps its Essence filtering mode **inside** the picker popup, rather than forcing two separate controls into a wrapping flow layout. Both Visual Deck Storage and Visual Database use one compact toolbar button with a selection summary and tooltip. The popup grid is unchanged (16 icons, four columns × four rows). - -When the final selected Essence is cleared, the filter becomes **Any**, including in Exact Match / Include-Exclude mode; it must not leave behind exclusions for all sixteen symbols. - -A new Continuumatrice profile starts with a valid, deliberately empty XML card catalogue. The in-memory database now treats a successful empty snapshot as `LoadStatus::Ok`, not `NotLoaded`. This allows the card browser and toolbar to initialize even before the user's first MSE card import. `carddatabase_test` covers this distinction. diff --git a/doc/continuumatrice-linux-install.md b/doc/continuumatrice-linux-install.md deleted file mode 100644 index aa95a665b..000000000 --- a/doc/continuumatrice-linux-install.md +++ /dev/null @@ -1,59 +0,0 @@ -# Running Continuumatrice alongside Cockatrice (Linux) - -Continuumatrice is an independent application. You can keep normal upstream **Cockatrice for MTG** installed and run both without mixing profiles or overwriting executables. - -## Binary and build target - -The CMake client target is now `continuumatrice` on every platform. A compatibility build target named `cockatrice` remains for older automation, but it creates **no executable** with the old name. - -```bash -git pull --ff-only origin feature/continuumatrice-product-migration -cmake -S . -B build --fresh -G Ninja -DCMAKE_BUILD_TYPE=Release -DWITH_CLIENT=ON -DWITH_SERVER=ON -DWITH_ORACLE=OFF -cmake --build build --target continuumatrice continuum-servatrice --parallel 16 -./build/continuumatrice/continuumatrice -``` - -If you previously compiled the fork under the old name, `build/cockatrice/cockatrice` can remain as an **outdated, stale executable** in the build directory. Launch `build/continuumatrice/continuumatrice` instead. Do not remove your system's normal `/usr/bin/cockatrice` application. - -The standalone executable on Linux installs as `bin/continuumatrice`. The new `continuumatrice.desktop` launcher installs alongside the normal `cockatrice.desktop`. Its application name and desktop-file ID are also `continuumatrice`, so Wayland and desktop launchers distinguish the applications. - -The fork installs its own original `continuumatrice.svg` icon, separate from Cockatrice artwork. The independent desktop launcher and `continuumatrice://` URI scheme do not take ownership of upstream `cockatrice://` links or `.cod` deck associations. - -This fork intentionally does **not** install Cockatrice's `cockatrice-cod.xml` MIME handler or register the `cockatrice://` URL scheme in the new Linux desktop file. The normal MTG client keeps its default deck file and URL associations. - -## Data isolation - -Assuming typical XDG locations (and no `portable.dat` file): - -- Data and databases: `~/.local/share/Continuumatrice/` -- Decks: `~/.local/share/Continuumatrice/decks/` -- Card images: `~/.local/share/Continuumatrice/pics/` -- Custom XML: `~/.local/share/Continuumatrice/customsets/` -- Preferences: `~/.local/share/Continuumatrice/settings/` -- Cache: `~/.cache/Continuumatrice/` -- Linux shared translations: `/share/continuumatrice/translations` -- Linux shared themes: `/share/continuumatrice/themes` - -Its per-user single-instance local socket uses a Continuumatrice-specific name. - -## System installation (optional) - -If you want a separate `continuumatrice` command and a graphical launcher, first configure the build with a prefix of your choice (e.g., `/usr/local` for a system install). Then: - -```bash -sudo cmake --install build -``` - -This uses the build's existing `CMAKE_INSTALL_PREFIX`; check `build/CMakeCache.txt` if unsure. The Linux client-specific install rules do not replace `bin/cockatrice` or `cockatrice.desktop`. - -For immediate testing, skip installation and run the executable directly from the build tree. This change has not been compiled in this environment; verify with your openSUSE compiler. - -## Cross-platform status - -This migration also gives the Windows and macOS application package and URL registrations separate names. Those packaging flows have **not** been validated on Windows or macOS; installers must not be published until installation, upgrade, and uninstall testing demonstrates that neither platform affects upstream Cockatrice. - -## Dedicated multiplayer server: Continuum-Servatrice - -Both components are built by default. The server target is `continuum-servatrice` and its binary is `build/continuum-servatrice/continuum-servatrice`. The original MTG Servatrice executable and configuration are not used. - -See [server hosting guide](continuum-servatrice-hosting.md) for the isolated MySQL schema and configuration commands. The default sample ports are TCP **4757** and WebSocket **4758**, chosen not to collide with MTG Servatrice's 4747/4748. Hosting outside your local network requires explicit firewall/reverse proxy/TLS planning. diff --git a/doc/continuumatrice-match-essence.md b/doc/continuumatrice-match-essence.md deleted file mode 100644 index 395ef2afa..000000000 --- a/doc/continuumatrice-match-essence.md +++ /dev/null @@ -1,83 +0,0 @@ -# Match Essence pools (Continuum Universes CCG) - -Continuumatrice matches provide **16 independent Essence balances per player**, using the canonical sequence: - -```text -M S O G E A Q C J B R L V T P N -``` - -**J** is Jasper; **N** is colorless. These are not Magic mana colors, and Essence is not primarily obtained by drawing land cards. - -## Behavior - -The client displays a compact **4 × 4 Essence Pool** in a reserved column beside each player's battlefield. The panel shows the user's installed `icons/essence/.svg` artwork and the current quantity, including zero. - -- **Left-click:** add one Essence of that type (gain). -- **Right-click:** remove one Essence of that type (spend), when the balance is positive. -- **Shift + left-click:** set an exact nonnegative balance through a number dialog. -- Other players and spectators can **view** the balances; only the player controlling the pool (or a judge) can modify it. - -For example, generating three Jasper Essence from a card ability currently means clicking `J` three times or setting its balance. Likewise, paying two Jasper means decrementing its balance twice. Future automated card effects should call a validated resource transaction action rather than simulating mouse clicks. - -### What this change does **not** assume - -It does not invent Essence generation rules, a land-based resource curve, turn-based Essence resets, automatic cost deduction, overdraft/Essence debt, or activation triggers. The core match tracks available resources, but **gameplay effects are still manual**. Define those rules separately before implementing them. - -## Server-authoritative state - -The existing Cockatrice-derived counter protocol already supports all the required functionality. The fork's shared `EssenceCounter` helper (`libcockatrice_utility/libcockatrice/utility/essence_counter.h`) defines stable counter names and the canonical symbol order. - -The server creates sixteen counters at game setup with IDs `1..16` and names `essence:M`, `essence:S`, ..., `essence:N`. The existing life counter retains ID `0`. These names are protocol/storage identifiers; client gameplay displays the corresponding Essence symbols rather than exposing raw IDs. - -Resource counters: -- Start at `0`, independently for every player. -- Cannot go below `0` (validated by the server for increment **and** set commands). -- Cannot be deleted or created a second time via the generic counter commands. -- Are serialized to client snapshots, replay events, and reconnect state by the existing counter protocol. -- Do not live on card ScriptableObjects, card metadata/XML, or a global static client singleton. - -`Server_Player` implements this for both the self-hosted **Continuum-Servatrice** and the embedded/local game server because they share the same player server code. **Both client and server must use a matching Continuumatrice build**. The regular MTG Cockatrice client/server do not provide the native Essence Pool display. - -## Implementation map - -| File | Responsibility | -| --- | --- | -| `libcockatrice_utility/libcockatrice/utility/essence_counter.h` | Canonical Essence order, protocol names, nonnegative values | -| `libcockatrice_card/libcockatrice/card/essence_types.h` | Card-cost parser uses the same source of symbols | -| `libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp` | Server counter initialization, updates, permanent-counter restrictions | -| `cockatrice/src/game_graphics/player/essence_pool_item.h/.cpp` | Compact graphical resource display, manual controls, server-state observation | -| `cockatrice/src/game_graphics/player/player_graphics_item.cpp` | Places Essence pool beside player's board and excludes it from the narrow ordinary-counter stack | -| `tests/essence_types_test.cpp` | Protocol-name round trips and nonnegative value utility checks | - -The panel never owns the true resource quantities. It listens for `CounterState::valueChanged` and sends normal `actIncrementCounter`/`actSetCounter` commands through `PlayerActions`. Only the server authoritatively changes the stored balance, so remote clients and replays see the same values. - -## Build and tests - -On the `feature/continuumatrice-essence-match-pools` branch: - -```bash -cmake -S . -B build -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DWITH_CLIENT=ON -DWITH_SERVER=ON -DWITH_ORACLE=OFF -DTEST=ON - -cmake --build build --target continuumatrice continuum-servatrice essence_types_test --parallel 16 - -ctest --test-dir build -R EssenceTypesTest --output-on-failure - -./build/continuumatrice/continuumatrice -``` - -## Functional acceptance checklist - -- [ ] In a new local game, each player has a 4×4 pool with exactly 16 Essence types and zero balances. -- [ ] Gaining `J` leaves `C`, `B`, and `N` unchanged; right-clicking `J` at zero cannot make it negative. -- [ ] Shift+left can set `N` (colorless) to a specific nonnegative amount. -- [ ] The normal life counter still works; no default MTG W/U/B/R/G/X/Storm counters appear. -- [ ] Both players see the same balance after a command is processed by the server. -- [ ] A spectator can view but not modify another player's Essence. -- [ ] Disconnect/reconnect restores all 16 values from the game-state snapshot. -- [ ] Replay playback restores previously recorded Essence changes. -- [ ] A remote game running Continuum-Servatrice matches the local game behavior. -- [ ] The Essence panel does not overlap the three battlefield bands or Limbo/Refuge/Oblivion piles at normal zoom. - -**Current status:** Source implementation committed. This server/client change has **not** been compiled or tested in a live match yet. The new browser and match UI need user-side smoke testing. diff --git a/doc/continuumatrice-product-migration.md b/doc/continuumatrice-product-migration.md deleted file mode 100644 index 949694ce2..000000000 --- a/doc/continuumatrice-product-migration.md +++ /dev/null @@ -1,82 +0,0 @@ -# Continuumatrice product migration and coexistence - -This is a **product fork**, not a renamed Cockatrice binary. The upstream Cockatrice runtime, MTG cards, profiles, update feeds, launchers and installers must remain independent. - -## Runtime identity - -| Component | Continuumatrice | -| --- | --- | -| CMake target / installed binary | `continuumatrice` | -| Linux build output | `build/continuumatrice/continuumatrice` (source folder remains upstream named) | -| Linux app launcher | `continuumatrice.desktop` | -| Qt application | `Continuumatrice` | -| Qt organization | `Continuum Universes Studio` | -| User data on Linux (default XDG) | `~/.local/share/Continuumatrice/` | -| User cache on Linux | `~/.cache/Continuumatrice/` | -| Single-instance socket | `ContinuumatriceSingleInstance` | -| Deep-link scheme | `continuumatrice://` | -| Card XML root (native) | `continuumatrice_carddatabase` version `4` | -| Card XML root (import compatibility) | `cockatrice_carddatabase` version `4` | -| Fork server executable (when built) | `continuum-servatrice` | -| Fork server config | `continuum-servatrice.ini` | - -Both `` and `` load, but new XML saved by the fork uses the new root. The parser format and gameplay features still derive from the Cockatrice codebase; this change is not an automatic rules engine. - -The source directories `cockatrice/` and `servatrice/`, common `libcockatrice_*` targets, and historical class names are left as *internal* implementation details to avoid needless churn and protocol breakage. They do not identify the installed product. - -## Card data - -- A new profile receives a **valid empty XML v4 starter database**, with no MTG cards. -- Put Continuum Universes card sets in `~/.local/share/Continuumatrice/customsets/`, or import from the Card Database menu. -- The default image download URL list is empty. Older fork profiles have known Scryfall/Gatherer URLs removed while custom URLs are preserved. -- The upstream MTG Oracle importer is not shipped: `WITH_ORACLE=OFF` is the default and `WITH_ORACLE=ON` is deliberately refused until there is a replacement. -- The inherited first-run Magic importer and automatic public-server/Commander-bracket fetches are disabled. -- Application release checks point only to the Continuumatrice GitHub repository; automatic update checks are off until a fork release is available. - -**An independent Continuum Universes catalogue importer remains to be implemented.** Static XML v4 import is already available. - -## System integration - -Linux: `continuumatrice` binary, independent .desktop file, own SVG icon and `continuumatrice://` handler. No takeover of `.cod` or `cockatrice://`. - -Windows: packaging script uses a separate installation directory, uninstall registry key, executable, URL protocol and process-lock detection. It must **never** remove or overwrite `cockatrice.exe` or the upstream `Uninstall\Cockatrice` key. - -macOS: separate app executable/bundle identifier and `continuumatrice://` link handling; `.cod` handler removed to avoid competition with Cockatrice. - -The Windows and macOS installers are **not yet verified**. Source and packaging changes should not be presented as validated shipping installers. - -## Build - -```bash -cmake -S . -B build --fresh -G Ninja \ - -DCMAKE_BUILD_TYPE=Release -DWITH_CLIENT=ON -DWITH_SERVER=ON -DWITH_ORACLE=OFF -cmake --build build --target continuumatrice continuum-servatrice --parallel 16 -./build/continuumatrice/continuumatrice -``` - -An internal compatibility target `cockatrice` still depends on `continuumatrice` to accommodate older CI invocations, but never emits a `cockatrice` executable. Previously compiled copies of `build/cockatrice/cockatrice` are stale. - -## Release checklist - -- [ ] Configure and compile Continuumatrice with openSUSE Qt 6 using a clean CMake cache. -- [ ] Confirm `build/continuumatrice/continuumatrice` runs and the app name and original SVG icon appear. -- [ ] Confirm old `/usr/bin/cockatrice`, `~/.local/share/Cockatrice` and MTG card images remain untouched. -- [ ] Open both clients simultaneously; confirm distinct sockets, settings, card databases, decks and caches. -- [ ] Load both legacy and new-root XML and verify custom images and Front/Support/Command mapping. -- [ ] Test Limbo, Refuge and Oblivion card moves, view windows, counters, mirrored positions and reconnect. -- [ ] Confirm startup does not request MTG artwork, Oracle updates, Commander brackets, or upstream server lists. -- [ ] Check a `continuumatrice://` link and ensure `cockatrice://` still belongs to MTG Cockatrice. -- [ ] Install and uninstall user-local Linux app; ensure no MTG desktop-file or icon collision. -- [ ] Test Windows installer in a VM with upstream Cockatrice preinstalled, including install, upgrade, uninstall and `.cod` association preservation. -- [ ] Test macOS app bundle identity and installation alongside upstream Cockatrice. -- [ ] Verify GPL requirements, notice retention and upstream attribution. - -**Status:** Committed source changes; no completed Qt compilation, package build, or runtime validation in the assistant environment. Release is blocked pending these tests. - -## Continuum-Servatrice dedicated server - -CMake uses `add_subdirectory(servatrice "${CMAKE_BINARY_DIR}/continuum-servatrice")`, retaining the inherited source files but placing generated build files and the executable in their own product folder. `WITH_SERVER=ON` is the default; `WITH_SERVER=OFF` is a deliberate opt-out for client-only builds. - -The MySQL schema `servatrice/continuum-servatrice.sql` uses `continuum_servatrice_` table prefixes, paired with `servatrice/continuum-servatrice.ini.example`. Default listening ports are 4757/4758. Existing Servatrice implementations, account/room/networking functionality and extended Limbo/Refuge/Oblivion game zones are retained. - -The fork server should be hosted against an **independent MySQL database**, not an existing upstream MTG Servatrice database. See [hosting instructions](continuum-servatrice-hosting.md). diff --git a/libcockatrice_card/CMakeLists.txt b/libcockatrice_card/CMakeLists.txt index e8bb291ab..d2e6ee83a 100644 --- a/libcockatrice_card/CMakeLists.txt +++ b/libcockatrice_card/CMakeLists.txt @@ -1,6 +1,5 @@ set(HEADERS libcockatrice/card/card_info.h - libcockatrice/card/essence_types.h libcockatrice/card/card_info_comparator.h libcockatrice/card/card_localization.h libcockatrice/card/lazy_properties_hash.h diff --git a/libcockatrice_card/libcockatrice/card/card_info.cpp b/libcockatrice_card/libcockatrice/card/card_info.cpp index 5889a69e1..90bfd3f44 100644 --- a/libcockatrice_card/libcockatrice/card/card_info.cpp +++ b/libcockatrice_card/libcockatrice/card/card_info.cpp @@ -1,7 +1,6 @@ #include "card_info.h" #include "game_specific_terms.h" -#include "essence_types.h" #include "libcockatrice/card/lazy_properties_hash.h" #include "printing/printing_info.h" #include "relation/card_relation.h" @@ -305,34 +304,17 @@ void CardInfo::setCardType(const QString &value) { setProperty(Mtg::CardType, value); } -QString CardInfo::getEssenceValue() const -{ - return hasProperty(EssenceProperties::Value) ? getProperty(EssenceProperties::Value) - : getProperty(Mtg::ConvertedManaCost); -} -QString CardInfo::getEssenceTypes() const -{ - const QString value = hasProperty(EssenceProperties::Types) - ? getProperty(EssenceProperties::Types) - : getProperty(Mtg::Colors); - return Essence::normalizeIdentity(value); -} -QString CardInfo::getEssenceCost() const -{ - return hasProperty(EssenceProperties::Cost) ? getProperty(EssenceProperties::Cost) - : getProperty(Mtg::ManaCost); -} QString CardInfo::getCmc() const { - return getEssenceValue(); + return getProperty(Mtg::ConvertedManaCost); } QString CardInfo::getColors() const { - return getEssenceTypes(); + return getProperty(Mtg::Colors); } void CardInfo::setColors(const QString &value) { - setProperty(EssenceProperties::Types, Essence::normalizeIdentity(value)); + setProperty(Mtg::Colors, value); } QString CardInfo::getLoyalty() const { @@ -344,53 +326,11 @@ QString CardInfo::getMainCardType() const } QString CardInfo::getManaCost() const { - return getEssenceCost(); -} -QString CardInfo::getIdentity() const -{ - return hasProperty(ContinuumCardProperties::Identity) ? getProperty(ContinuumCardProperties::Identity) - : getProperty(ContinuumCardProperties::Alias); -} -QString CardInfo::getAttack() const -{ - if (hasProperty(ContinuumCardProperties::Attack)) { - return getProperty(ContinuumCardProperties::Attack); - } - if (hasProperty(ContinuumCardProperties::Strength)) { - return getProperty(ContinuumCardProperties::Strength); - } - const QString legacyStats = getProperty(Mtg::PowTough); - const qsizetype slash = legacyStats.indexOf(QLatin1Char('/')); - return slash < 0 ? QString() : legacyStats.left(slash); -} -QString CardInfo::getArmour() const -{ - return hasProperty(ContinuumCardProperties::Armour) ? getProperty(ContinuumCardProperties::Armour) - : getProperty(ContinuumCardProperties::Armor); -} -QString CardInfo::getHealth() const -{ - if (hasProperty(ContinuumCardProperties::Health)) { - return getProperty(ContinuumCardProperties::Health); - } - const QString legacyStats = getProperty(Mtg::PowTough); - const qsizetype slash = legacyStats.indexOf(QLatin1Char('/')); - return slash < 0 ? QString() : legacyStats.mid(slash + 1); -} -QString CardInfo::getAtkHp() const -{ - const QString attack = getAttack(); - const QString health = getHealth(); - if (attack.isEmpty() && health.isEmpty()) { - return QString(); - } - return (attack.isEmpty() ? QStringLiteral("?") : attack) + QLatin1Char('/') + - (health.isEmpty() ? QStringLiteral("?") : health); + return getProperty(Mtg::ManaCost); } QString CardInfo::getPowTough() const { - // Compatibility accessor. Display terminology is always ATK/HP. - return getAtkHp(); + return getProperty(Mtg::PowTough); } void CardInfo::setPowTough(const QString &value) { diff --git a/libcockatrice_card/libcockatrice/card/card_info.h b/libcockatrice_card/libcockatrice/card/card_info.h index 5bd17c470..5452ef98b 100644 --- a/libcockatrice_card/libcockatrice/card/card_info.h +++ b/libcockatrice_card/libcockatrice/card/card_info.h @@ -466,24 +466,12 @@ public: /** @name Legacy/Convenience Property Accessors */ //@{ [[nodiscard]] QString getCardType() const; void setCardType(const QString &value); - // Native Continuumatrice card properties; older MTG getter names below - // remain compatibility wrappers used by the inherited model/analytics API. - [[nodiscard]] QString getEssenceCost() const; - [[nodiscard]] QString getEssenceTypes() const; - [[nodiscard]] QString getEssenceValue() const; [[nodiscard]] QString getCmc() const; [[nodiscard]] QString getColors() const; void setColors(const QString &value); [[nodiscard]] QString getLoyalty() const; [[nodiscard]] QString getMainCardType() const; [[nodiscard]] QString getManaCost() const; - // Native static card-definition stats and identity. The database exporter - // writes identity/attack/armour/health, while older XML keys are readable. - [[nodiscard]] QString getIdentity() const; - [[nodiscard]] QString getAttack() const; - [[nodiscard]] QString getArmour() const; - [[nodiscard]] QString getHealth() const; - [[nodiscard]] QString getAtkHp() const; [[nodiscard]] QString getPowTough() const; void setPowTough(const QString &value); //@} diff --git a/libcockatrice_card/libcockatrice/card/database/card_database.cpp b/libcockatrice_card/libcockatrice/card/database/card_database.cpp index 48c9669b9..ed097ef3b 100644 --- a/libcockatrice_card/libcockatrice/card/database/card_database.cpp +++ b/libcockatrice_card/libcockatrice/card/database/card_database.cpp @@ -319,11 +319,7 @@ void CardDatabase::swapInDatabaseData(CardDatabaseData data) sets = std::move(data.sets); formats = std::move(data.formats); - // A successfully parsed card database may legitimately contain no cards. - // Continuumatrice starts with an intentionally empty catalogue, which is - // still LOADED. NotLoaded is reserved for before loading and clear(). - // The UI uses this status to reveal its filters and empty results view. - loadStatus = Ok; + loadStatus = cards.isEmpty() ? NotLoaded : Ok; // inform listeners that the whole database was replaced; they should // rebuild from the live containers in a single batch instead of reacting diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp index 98c3b2fca..3ee1e808e 100644 --- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp +++ b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp @@ -32,11 +32,10 @@ class ICardSetPriorityController; -#define CONTINUUMATRICE_XML4_TAGNAME "continuumatrice_carddatabase" -#define COCKATRICE_XML4_TAGNAME "cockatrice_carddatabase" // Accepted legacy XML input +#define COCKATRICE_XML4_TAGNAME "cockatrice_carddatabase" #define COCKATRICE_XML4_TAGVER 4 #define COCKATRICE_XML4_SCHEMALOCATION \ - "https://raw.githubusercontent.com/Continuum-Universes-Studio/Continuumatrice/master/doc/carddatabase_v4/cards.xsd" + "https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v4/cards.xsd" CockatriceXml4Parser::CockatriceXml4Parser(ICardSetPriorityController *_cardSetPriorityController) : ICardDatabaseParser(_cardSetPriorityController) @@ -55,8 +54,7 @@ bool CockatriceXml4Parser::getCanParseFile(const QString &fileName, QIODevice &d QXmlStreamReader xml(&device); while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::StartElement) { - if (xml.name().toString() == CONTINUUMATRICE_XML4_TAGNAME || - xml.name().toString() == COCKATRICE_XML4_TAGNAME) { + if (xml.name().toString() == COCKATRICE_XML4_TAGNAME) { int version = xml.attributes().value("version").toString().toInt(); if (version == COCKATRICE_XML4_TAGVER) { return true; @@ -693,7 +691,7 @@ bool CockatriceXml4Parser::saveToFile(FormatRulesNameMap _formats, xml.setAutoFormatting(true); xml.writeStartDocument(); - xml.writeStartElement(CONTINUUMATRICE_XML4_TAGNAME); + xml.writeStartElement(COCKATRICE_XML4_TAGNAME); xml.writeAttribute("version", QString::number(COCKATRICE_XML4_TAGVER)); xml.writeAttribute("xmlns:xsi", COCKATRICE_XML_XSI_NAMESPACE); xml.writeAttribute("xsi:schemaLocation", COCKATRICE_XML4_SCHEMALOCATION); @@ -729,7 +727,7 @@ bool CockatriceXml4Parser::saveToFile(FormatRulesNameMap _formats, xml.writeEndElement(); } - xml.writeEndElement(); // continuumatrice_carddatabase + xml.writeEndElement(); // cockatrice_carddatabase xml.writeEndDocument(); return true; diff --git a/libcockatrice_card/libcockatrice/card/essence_types.h b/libcockatrice_card/libcockatrice/card/essence_types.h deleted file mode 100644 index cc0246157..000000000 --- a/libcockatrice_card/libcockatrice/card/essence_types.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef CONTINUUMATRICE_ESSENCE_TYPES_H -#define CONTINUUMATRICE_ESSENCE_TYPES_H - -#include -#include -#include -#include -#include -#include - -namespace Essence -{ -// Canonical Continuum Universes CCG essence symbols. N is colorless. -// The other codes have no names, colors or game mechanics assigned here. -inline const QString &symbols() -{ - return EssenceCounter::symbols(); -} - -inline bool isSymbol(const QChar symbol) -{ - return symbols().contains(symbol); -} - -// Stable ordering for the card's explicitly recorded Essence identity. This -// is a list of distinct codes, NOT a derived resource cost or mana color. -inline QString normalizeIdentity(const QString &input) -{ - const QString uppercase = input.toUpper(); - QString normalized; - for (const QChar code : symbols()) { - if (uppercase.contains(code)) { - normalized.append(code); - } - } - return normalized; -} - -// Costs may contain bare or braced symbols, e.g. MSN or {M}{S}{N}. -// Decimal quantities are passed through as generic amounts for display. -// Hybrid/phrexian/other MTG-specific syntax is NOT defined as Essence. -inline QStringList parseCost(const QString &cost) -{ - static const QRegularExpression regex(QStringLiteral(R"(\{([^}]+)\}|(\d+)|([MSOGEAQCJBRLVTPN]))")); - QStringList result; - auto matches = regex.globalMatch(cost); - while (matches.hasNext()) { - const auto match = matches.next(); - if (!match.captured(1).isEmpty()) { - const QString token = match.captured(1); - if (token.size() == 1 && isSymbol(token.at(0))) { - result.append(token); - } else { - // Numeric quantities may also be written in braces: {2}{N}. - bool isNumber = !token.isEmpty(); - for (const QChar ch : token) { - if (!ch.isDigit()) { - isNumber = false; - break; - } - } - if (isNumber) { - result.append(token); - } - } - } else if (!match.captured(2).isEmpty()) { - result.append(match.captured(2)); - } else if (!match.captured(3).isEmpty()) { - result.append(match.captured(3)); - } - } - return result; -} -} // namespace Essence - -#endif // CONTINUUMATRICE_ESSENCE_TYPES_H diff --git a/libcockatrice_card/libcockatrice/card/game_specific_terms.h b/libcockatrice_card/libcockatrice/card/game_specific_terms.h index 69e8bdc0e..e9160e514 100644 --- a/libcockatrice_card/libcockatrice/card/game_specific_terms.h +++ b/libcockatrice_card/libcockatrice/card/game_specific_terms.h @@ -16,26 +16,6 @@ * Note: Mtg = "Maybe that game" */ -// These remain only for reading older Cockatrice XML. New cards use Essence. -namespace EssenceProperties -{ -inline const QString Cost = QStringLiteral("essencecost"); -inline const QString Types = QStringLiteral("essence"); -inline const QString Value = QStringLiteral("essencevalue"); -} - -// Continuum Universes CCG card-definition fields, not mutable match stats. -namespace ContinuumCardProperties -{ -inline const QString Identity = QStringLiteral("identity"); -inline const QString Alias = QStringLiteral("alias"); // Earlier exports may use this name. -inline const QString Attack = QStringLiteral("attack"); -inline const QString Strength = QStringLiteral("strength"); // Legacy MSE field ID. -inline const QString Armour = QStringLiteral("armour"); -inline const QString Armor = QStringLiteral("armor"); // Legacy American spelling. -inline const QString Health = QStringLiteral("health"); -} - namespace Mtg { QString const CardType("type"); @@ -51,38 +31,26 @@ QString const ColorIdentity("coloridentity"); inline static const QString getNicePropertyName(QString key) { - if (key == EssenceProperties::Cost || key == ManaCost) { - return QCoreApplication::translate("Essence", "Essence Cost"); - } - if (key == EssenceProperties::Types || key == Colors || key == ColorIdentity) { - return QCoreApplication::translate("Essence", "Essence"); - } - if (key == EssenceProperties::Value || key == ConvertedManaCost) { - return QCoreApplication::translate("Essence", "Essence Value"); - } - if (key == ContinuumCardProperties::Identity || key == ContinuumCardProperties::Alias) { - return QCoreApplication::translate("Continuum", "Identity"); - } - if (key == ContinuumCardProperties::Attack || key == ContinuumCardProperties::Strength) { - return QCoreApplication::translate("Continuum", "Attack"); - } - if (key == ContinuumCardProperties::Armour || key == ContinuumCardProperties::Armor) { - return QCoreApplication::translate("Continuum", "Armour"); - } - if (key == ContinuumCardProperties::Health) { - return QCoreApplication::translate("Continuum", "Health"); - } if (key == CardType) { return QCoreApplication::translate("Mtg", "Card Type"); } + if (key == ConvertedManaCost) { + return QCoreApplication::translate("Mtg", "Mana Value"); + } + if (key == Colors) { + return QCoreApplication::translate("Mtg", "Color(s)"); + } if (key == Loyalty) { return QCoreApplication::translate("Mtg", "Loyalty"); } if (key == MainCardType) { return QCoreApplication::translate("Mtg", "Main Card Type"); } + if (key == ManaCost) { + return QCoreApplication::translate("Mtg", "Mana Cost"); + } if (key == PowTough) { - return QCoreApplication::translate("Continuum", "ATK/HP"); + return QCoreApplication::translate("Mtg", "P/T"); } if (key == Side) { return QCoreApplication::translate("Mtg", "Side"); @@ -90,6 +58,9 @@ inline static const QString getNicePropertyName(QString key) if (key == Layout) { return QCoreApplication::translate("Mtg", "Layout"); } + if (key == ColorIdentity) { + return QCoreApplication::translate("Mtg", "Color Identity"); + } return key; } } // namespace Mtg diff --git a/libcockatrice_filters/libcockatrice/filters/filter_card.cpp b/libcockatrice_filters/libcockatrice/filters/filter_card.cpp index b35b730b9..26cddd53c 100644 --- a/libcockatrice_filters/libcockatrice/filters/filter_card.cpp +++ b/libcockatrice_filters/libcockatrice/filters/filter_card.cpp @@ -68,15 +68,15 @@ const QString CardFilter::attrName(Attr a) case AttrType: return tr("Type"); case AttrColor: - return tr("Essence"); + return tr("Color"); case AttrText: return tr("Text"); case AttrSet: return tr("Set"); case AttrManaCost: - return tr("Essence Cost"); + return tr("Mana Cost"); case AttrCmc: - return tr("Essence Value"); + return tr("Mana Value"); case AttrRarity: return tr("Rarity"); case AttrPow: diff --git a/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp b/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp index 8bc0cdf24..f12f87181 100644 --- a/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp +++ b/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp @@ -2,7 +2,6 @@ #include "filter_card.h" #include "libcockatrice/card/card_localization.h" -#include #include "libcockatrice/card/printing/printing_info.h" #include "libcockatrice/card/set/card_set.h" @@ -178,16 +177,23 @@ FilterItem::FilterItem(QString trm, FilterItemList *parent) : p(parent), term(st switch (attr()) { case CardFilter::AttrColor: { - // The old MTG color-name conversions are invalid for Essence. - // N is explicitly colorless; an unspecified identity is not N. - QString converted = term.trimmed().toUpper(); - if (converted == QStringLiteral("COLORLESS") || converted == QStringLiteral("COLOURLESS")) { - converted = QStringLiteral("N"); - } - converted.remove(QLatin1Char(' ')); - converted.remove(QLatin1Char(',')); - converted.remove(QLatin1Char('{')); - converted.remove(QLatin1Char('}')); + QString converted = term.trimmed(); + + converted.replace("green", "g", Qt::CaseInsensitive); + converted.replace("grn", "g", Qt::CaseInsensitive); + converted.replace("blue", "u", Qt::CaseInsensitive); + converted.replace("blu", "u", Qt::CaseInsensitive); + converted.replace("black", "b", Qt::CaseInsensitive); + converted.replace("blk", "b", Qt::CaseInsensitive); + converted.replace("red", "r", Qt::CaseInsensitive); + converted.replace("white", "w", Qt::CaseInsensitive); + converted.replace("wht", "w", Qt::CaseInsensitive); + converted.replace("colorless", "c", Qt::CaseInsensitive); + converted.replace("colourless", "c", Qt::CaseInsensitive); + converted.replace("none", "c", Qt::CaseInsensitive); + + converted.replace(QString(" "), QString(""), Qt::CaseInsensitive); + convertedColorTerm = converted; break; } @@ -234,7 +240,8 @@ FilterItem::FilterItem(QString trm, FilterItemList *parent) : p(parent), term(st break; } case CardFilter::AttrManaCost: - sortedManaCostTerm = Essence::parseCost(term.toUpper()).join(QString()); + sortedManaCostTerm = term.toUpper(); + // Sort the mana cost so it will be easy to find std::sort(sortedManaCostTerm.begin(), sortedManaCostTerm.end()); break; case CardFilter::AttrLoyalty: @@ -353,16 +360,25 @@ bool FilterItem::acceptSubType(const CardInfoPtr &info) const bool FilterItem::acceptColor(const CardInfoPtr &info) const { - if (convertedColorTerm.isEmpty()) { - return false; + const QString colors = info->getColors(); + + // Colorless card filter + if (convertedColorTerm.toLower() == "c" && colors.length() < 1) { + return true; } - const QString essence = info->getEssenceTypes(); - for (const QChar code : convertedColorTerm) { - if (!Essence::isSymbol(code) || !essence.contains(code)) { - return false; + + /* + * This is a tricky part, if the filter has multiple colors in it, like UGW, + * then we should match all of them to the card's colors + */ + int match_count = 0; + for (const QChar &color : convertedColorTerm) { + if (colors.contains(color, Qt::CaseInsensitive)) { + match_count++; } } - return true; + + return match_count == convertedColorTerm.length(); } bool FilterItem::acceptText(const CardInfoPtr &info, const CardSearchLanguage &searchLanguage) const @@ -405,13 +421,14 @@ bool FilterItem::acceptSet(const CardInfoPtr &info) const bool FilterItem::acceptManaCost(const CardInfoPtr &info) const { - if (sortedManaCostTerm.isEmpty()) { - return false; - } - for (const QString &costPart : info->getManaCost().split(QStringLiteral("//"))) { - QString sortedCost = Essence::parseCost(costPart).join(QString()); - std::sort(sortedCost.begin(), sortedCost.end()); - if (sortedCost.contains(sortedManaCostTerm)) { + // Try to seperate the mana cost in case it's a split card + // if it's not a split card the loop will run only once + for (const QString &manaCostPart : info->getManaCost().split("//")) { + QString fullManaCost = manaCostPart; + std::sort(fullManaCost.begin(), fullManaCost.end()); + + // If the partial is found in the full, return true + if (fullManaCost.contains(sortedManaCostTerm)) { return true; } } diff --git a/libcockatrice_models/libcockatrice/models/database/card_database_display_model.cpp b/libcockatrice_models/libcockatrice/models/database/card_database_display_model.cpp index f14f8be3b..9ec0afd9c 100644 --- a/libcockatrice_models/libcockatrice/models/database/card_database_display_model.cpp +++ b/libcockatrice_models/libcockatrice/models/database/card_database_display_model.cpp @@ -90,18 +90,11 @@ bool CardDatabaseDisplayModel::lessThan(const QModelIndex &left, const QModelInd if (isRightType && (!isLeftType || rightString.size() == cardName.size())) { return false; } - } else if (left.column() == right.column() && - (left.column() == CardDatabaseModel::AttackColumn || - left.column() == CardDatabaseModel::ArmourColumn || - left.column() == CardDatabaseModel::HealthColumn)) { - // Compare numeric card statistics as numbers: 9 sorts before 10. - // Values containing expressions retain the existing numeric-prefix - // comparator instead of being forced into an integer type. - return lessThanNumerically(leftString, rightString) < 0; - } else if (right.column() == CardDatabaseModel::AtkHpColumn && - left.column() == CardDatabaseModel::AtkHpColumn) { - // ATK/HP sorts by Attack first, then Health, both numerically. - // Operate on views to avoid allocations during large database sorts. + } else if (right.column() == CardDatabaseModel::PTColumn && left.column() == CardDatabaseModel::PTColumn) { + // The comparator runs O(n log n) times while the view is sorted, so + // splitting every power/toughness string into a QStringList allocates + // a lot for data that has exactly one separator. Work on views and + // only split the two halves out when both strings are well formed. const QStringView leftPT = leftString; const QStringView rightPT = rightString; const qsizetype leftSlash = leftPT.indexOf(u'/'); @@ -110,11 +103,11 @@ bool CardDatabaseDisplayModel::lessThan(const QModelIndex &left, const QModelInd const bool rightIsWellFormed = rightSlash >= 0 && rightPT.indexOf(u'/', rightSlash + 1) < 0; if (leftIsWellFormed && rightIsWellFormed) { - const int attackComparison = lessThanNumerically(leftPT.left(leftSlash), rightPT.left(rightSlash)); - if (attackComparison != 0) { - return attackComparison < 0; + const int powerComparison = lessThanNumerically(leftPT.left(leftSlash), rightPT.left(rightSlash)); + if (powerComparison != 0) { + return powerComparison < 0; } - // Attack equal, compare Health + // power equal, check toughness return lessThanNumerically(leftPT.mid(leftSlash + 1), rightPT.mid(rightSlash + 1)) < 0; } } diff --git a/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp b/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp index 5e6a402cc..a83b7c83d 100644 --- a/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp +++ b/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp @@ -11,6 +11,7 @@ class QObject; +#define CARDDBMODEL_COLUMNS 6 CardDatabaseModel::CardDatabaseModel(CardDatabase *_db, bool _showOnlyCardsFromEnabledSets, QObject *parent) : QAbstractListModel(parent), db(_db), showOnlyCardsFromEnabledSets(_showOnlyCardsFromEnabledSets) @@ -37,12 +38,12 @@ int CardDatabaseModel::columnCount(const QModelIndex &parent) const { // Card rows are leaves, so a valid parent holds no columns. Reporting the // row width for a card index would claim child cells that do not exist. - return parent.isValid() ? 0 : ColumnCount; + return parent.isValid() ? 0 : CARDDBMODEL_COLUMNS; } QVariant CardDatabaseModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() >= cardList.size() || index.column() >= ColumnCount || + if (!index.isValid() || index.row() >= cardList.size() || index.column() >= CARDDBMODEL_COLUMNS || (role != Qt::DisplayRole && role != SortRole)) { return QVariant(); } @@ -58,18 +59,10 @@ QVariant CardDatabaseModel::data(const QModelIndex &index, int role) const : card->getManaCost(); case CardTypeColumn: return card->getCardType(); - case AtkHpColumn: - return card->getAtkHp(); + case PTColumn: + return card->getPowTough(); case ColorColumn: return card->getColors(); - case IdentityColumn: - return card->getIdentity(); - case AttackColumn: - return card->getAttack(); - case ArmourColumn: - return card->getArmour(); - case HealthColumn: - return card->getHealth(); default: return QVariant(); } @@ -89,21 +82,13 @@ QVariant CardDatabaseModel::headerData(int section, Qt::Orientation orientation, case SetListColumn: return QString(tr("Sets")); case ManaCostColumn: - return QString(tr("Essence Cost")); + return QString(tr("Mana cost")); case CardTypeColumn: return QString(tr("Card type")); - case AtkHpColumn: - return QString(tr("ATK/HP")); + case PTColumn: + return QString(tr("P/T")); case ColorColumn: - return QString(tr("Essence")); - case IdentityColumn: - return QString(tr("Identity")); - case AttackColumn: - return QString(tr("Attack")); - case ArmourColumn: - return QString(tr("Armour")); - case HealthColumn: - return QString(tr("Health")); + return QString(tr("Color(s)")); default: return QVariant(); } @@ -116,7 +101,7 @@ void CardDatabaseModel::cardInfoChanged(const CardInfoPtr &card) return; } - emit dataChanged(index(row, 0), index(row, ColumnCount - 1)); + emit dataChanged(index(row, 0), index(row, CARDDBMODEL_COLUMNS - 1)); } bool CardDatabaseModel::checkCardHasAtLeastOneEnabledSet(const CardInfoPtr &card) const diff --git a/libcockatrice_models/libcockatrice/models/database/card_database_model.h b/libcockatrice_models/libcockatrice/models/database/card_database_model.h index 49a268bdd..208240109 100644 --- a/libcockatrice_models/libcockatrice/models/database/card_database_model.h +++ b/libcockatrice_models/libcockatrice/models/database/card_database_model.h @@ -28,15 +28,9 @@ public: NameColumn, SetListColumn, ManaCostColumn, - AtkHpColumn, // Formerly P/T; keep column index for saved table layouts. + PTColumn, CardTypeColumn, - ColorColumn, - IdentityColumn, - AttackColumn, - ArmourColumn, - HealthColumn, - ColumnCount, - PTColumn = AtkHpColumn // Compatibility alias for inherited callers. + ColorColumn }; enum Role { diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp index a90fec42b..b8ee7c6c2 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp @@ -57,7 +57,6 @@ #include #include #include -#include #include #include #include @@ -111,16 +110,13 @@ void Server_Player::setupZones() addZone(new Server_CardZone(this, ZoneNames::OBLIVION, false, ServerInfo_Zone::PublicZone)); addCounter(new Server_Counter(0, "life", makeColor(255, 255, 255), 25, game->getStartingLifeTotal())); - // Continuum Universes CCG: every player has an independent sixteen-type - // Essence pool. It starts at zero and can change without drawing lands. - // These counters are the authoritative game-state source: the existing - // counter protocol automatically handles spectators, replays and reconnect. - // Stable ids 1..16 correspond to M,S,O,G,E,A,Q,C,J,B,R,L,V,T,P,N. - int essenceId = 1; - for (const QChar code : EssenceCounter::symbols()) { - addCounter(new Server_Counter(essenceId++, EssenceCounter::name(code), - makeColor(168, 172, 189), 18, 0)); - } + addCounter(new Server_Counter(1, "w", makeColor(255, 255, 150), 20, 0)); + addCounter(new Server_Counter(2, "u", makeColor(150, 150, 255), 20, 0)); + addCounter(new Server_Counter(3, "b", makeColor(150, 150, 150), 20, 0)); + addCounter(new Server_Counter(4, "r", makeColor(250, 150, 150), 20, 0)); + addCounter(new Server_Counter(5, "g", makeColor(150, 255, 150), 20, 0)); + addCounter(new Server_Counter(6, "x", makeColor(255, 255, 255), 20, 0)); + addCounter(new Server_Counter(7, "storm", makeColor(255, 150, 30), 20, 0)); // ------------------------------------------------------------------ @@ -484,14 +480,7 @@ Server_Player::cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer & return Response::RespNameNotFound; } - const int oldValue = c->getCount(); - // Essence is an available-resource balance, never a debt counter. - // Leave ordinary player counters unchanged. - (void)c->incrementCount(cmd.delta()); - if (EssenceCounter::isEssenceCounter(c->getName()) && c->getCount() < 0) { - (void)c->setCount(EssenceCounter::clampBalance(c->getCount())); - } - const bool didChange = c->getCount() != oldValue; + bool didChange = c->incrementCount(cmd.delta()); if (didChange) { Event_SetCounter event; event.set_counter_id(c->getId()); @@ -512,13 +501,7 @@ Server_Player::cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContai return Response::RespContextError; } - const QString requestedName = nameFromStdString(cmd.counter_name()); - // The canonical pool is unique, present for every player, and may not - // be forged or replaced using a generic "create counter" command. - if (EssenceCounter::isEssenceCounter(requestedName)) { - return Response::RespContextError; - } - auto *c = new Server_Counter(newCounterId(), requestedName, cmd.counter_color(), + auto *c = new Server_Counter(newCounterId(), nameFromStdString(cmd.counter_name()), cmd.counter_color(), cmd.radius(), cmd.value()); addCounter(c); @@ -550,8 +533,7 @@ Server_Player::cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer & return Response::RespNameNotFound; } - const int newValue = EssenceCounter::isEssenceCounter(c->getName()) ? EssenceCounter::clampBalance(cmd.value()) : cmd.value(); - const bool didChange = c->setCount(newValue); + bool didChange = c->setCount(cmd.value()); if (didChange) { Event_SetCounter event; event.set_counter_id(c->getId()); @@ -577,10 +559,6 @@ Server_Player::cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer & if (!counter) { return Response::RespNameNotFound; } - // Canonical Essence counters are permanent for the duration of a match. - if (EssenceCounter::isEssenceCounter(counter->getName())) { - return Response::RespContextError; - } counters.remove(counterId); delete counter; diff --git a/libcockatrice_settings/libcockatrice/settings/download_settings.cpp b/libcockatrice_settings/libcockatrice/settings/download_settings.cpp index 9a8884e7b..84a6a7509 100644 --- a/libcockatrice_settings/libcockatrice/settings/download_settings.cpp +++ b/libcockatrice_settings/libcockatrice/settings/download_settings.cpp @@ -10,9 +10,12 @@ class QObject; -// Continuumatrice has no MTG card image providers by default. Only explicit -// user-configured URLs or local card art belong in its profile. -const QStringList DownloadSettings::DEFAULT_DOWNLOAD_URLS = {}; +const QStringList DownloadSettings::DEFAULT_DOWNLOAD_URLS = { + "https://cards.scryfall.io/large/!prop:side!/!set:uuid_substr_0_1!/!set:uuid_substr_1_1!/!set:uuid!.jpg", + "https://api.scryfall.com/cards/!set:uuid!?format=image&face=!prop:side!&lang=!sflang!", + "https://api.scryfall.com/cards/multiverse/!set:muid!?format=image&lang=!sflang!", + "https://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!set:muid!&type=card", + "https://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card"}; const QString DownloadSettings::SCRYFALL_NAMED_LOCALIZED_URL = "https://api.scryfall.com/cards/named?fuzzy=!localizedName!&lang=!sflang!&format=image&face=!prop:side!"; diff --git a/libcockatrice_utility/CMakeLists.txt b/libcockatrice_utility/CMakeLists.txt index 362e66095..8d2cb5b92 100644 --- a/libcockatrice_utility/CMakeLists.txt +++ b/libcockatrice_utility/CMakeLists.txt @@ -19,7 +19,6 @@ set(UTILITY_HEADERS libcockatrice/utility/cryptoutil.h libcockatrice/utility/debouncer.h libcockatrice/utility/expression.h - libcockatrice/utility/essence_counter.h libcockatrice/utility/installer_close_protocol.h libcockatrice/utility/levenshtein.h libcockatrice/utility/local_server_name.h diff --git a/libcockatrice_utility/libcockatrice/utility/essence_counter.h b/libcockatrice_utility/libcockatrice/utility/essence_counter.h deleted file mode 100644 index 5a82af32b..000000000 --- a/libcockatrice_utility/libcockatrice/utility/essence_counter.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef CONTINUUMATRICE_ESSENCE_COUNTER_H -#define CONTINUUMATRICE_ESSENCE_COUNTER_H - -#include -#include - - -namespace EssenceCounter -{ -// A single authoritative resource-code order shared by card metadata, the -// client match display, and the dedicated/local multiplayer servers. -// This is a protocol identity list, not a definition of gameplay generation. -inline const QString &symbols() -{ - static const QString value = QStringLiteral("MSOGEAQCJBRLVTPN"); - return value; -} - -inline bool isSymbol(QChar code) -{ - return symbols().contains(code); -} - -// Names are serialized by Cockatrice's existing ServerInfo_Counter protocol. -// Keep these stable for replays, game snapshots, and reconnects. -inline QString name(QChar code) -{ - return isSymbol(code) ? QStringLiteral("essence:") + code : QString(); -} - -inline QChar codeFromName(const QString &counterName) -{ - constexpr qsizetype prefixSize = 8; // "essence:" - if (counterName.size() != prefixSize + 1 || - !counterName.startsWith(QStringLiteral("essence:"))) { - return QChar(); - } - const QChar code = counterName.at(prefixSize); - return isSymbol(code) ? code : QChar(); -} - -// Resource balances cannot be negative. The caller handles overflow-safe -// addition separately; this function only clamps the final amount. -inline int clampBalance(int amount) -{ - return amount < 0 ? 0 : amount; -} - -inline bool isEssenceCounter(const QString &counterName) -{ - return !codeFromName(counterName).isNull(); -} -} // namespace EssenceCounter - -#endif // CONTINUUMATRICE_ESSENCE_COUNTER_H diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index 6eb8a1dbd..9d8dd46d2 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -2,7 +2,7 @@ # # provides the servatrice binary -project(ContinuumServatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") +project(Servatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") set(servatrice_SOURCES src/email_parser.cpp @@ -49,7 +49,11 @@ if(CMAKE_HOST_SYSTEM MATCHES "FreeBSD") endif() if(APPLE) - # Headless server: no inherited Cockatrice .icns bundle branding. + set(MACOSX_BUNDLE_ICON_FILE appicon.icns) + set_source_files_properties( + ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources + ) + set(servatrice_SOURCES ${servatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) endif(APPLE) qt_add_resources(servatrice_RESOURCES_RCC ${servatrice_RESOURCES}) @@ -80,7 +84,7 @@ if(${MYSQL_CLIENT_LIBRARIES} MATCHES "NOTFOUND") FALSE CACHE INTERNAL "" ) - message(STATUS "MySQL connector NOT FOUND: Continuum-Servatrice won't be able to connect to a MySQL server") + message(STATUS "MySQL connector NOT FOUND: Servatrice won't be able to connect to a MySQL server") unset(MYSQL_CLIENT_LIBRARIES) else() set(MYSQLCLIENT_FOUND @@ -102,20 +106,18 @@ set(DESKTOPDIR ) # Build servatrice binary and link it -add_executable(continuum-servatrice MACOSX_BUNDLE ${servatrice_MOC_SRCS} ${servatrice_RESOURCES_RCC} ${servatrice_SOURCES}) -# Internally keep the original source tree and Source/Protocol type names. -# This target produces only the native Continuum-Servatrice executable. +add_executable(servatrice MACOSX_BUNDLE ${servatrice_MOC_SRCS} ${servatrice_RESOURCES_RCC} ${servatrice_SOURCES}) -target_precompile_headers(continuum-servatrice PRIVATE "${CMAKE_SOURCE_DIR}/cmake/pch/qtcore_pch.h") +target_precompile_headers(servatrice PRIVATE "${CMAKE_SOURCE_DIR}/cmake/pch/qtcore_pch.h") if(CMAKE_HOST_SYSTEM MATCHES "FreeBSD") target_link_libraries( - continuum-servatrice libcockatrice_deck_list libcockatrice_network_server_remote Threads::Threads ${SERVATRICE_QT_MODULES} + servatrice libcockatrice_deck_list libcockatrice_network_server_remote Threads::Threads ${SERVATRICE_QT_MODULES} ${LIBEXECINFO_LIBRARY} ) else() target_link_libraries( - continuum-servatrice libcockatrice_deck_list libcockatrice_network_server_remote Threads::Threads ${SERVATRICE_QT_MODULES} + servatrice libcockatrice_deck_list libcockatrice_network_server_remote Threads::Threads ${SERVATRICE_QT_MODULES} ) endif() @@ -123,34 +125,35 @@ endif() if(UNIX) if(APPLE) set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}") - set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.continuum-universes.continuum-servatrice") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.cockatrice.${PROJECT_NAME}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}") set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) - install(TARGETS continuum-servatrice BUNDLE DESTINATION ./) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/continuum-servatrice.ini.example DESTINATION ./continuum-servatrice.app/Contents/Resources/) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/continuum-servatrice.sql DESTINATION ./continuum-servatrice.app/Contents/Resources/) + install(TARGETS servatrice BUNDLE DESTINATION ./) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.ini.example DESTINATION ./servatrice.app/Contents/Resources/) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.sql DESTINATION ./servatrice.app/Contents/Resources/) else() # Assume linux - install(TARGETS continuum-servatrice RUNTIME DESTINATION bin/) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/continuum-servatrice.ini.example DESTINATION share/continuum-servatrice/) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/continuum-servatrice.sql DESTINATION share/continuum-servatrice/) + install(TARGETS servatrice RUNTIME DESTINATION bin/) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.ini.example DESTINATION share/servatrice/) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.sql DESTINATION share/servatrice/) - # No upstream Servatrice desktop/icon registration; it would collide with - # a separate MTG server installation. The server is managed via CLI. + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/servatrice.png DESTINATION ${ICONDIR}/hicolor/48x48/apps) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/servatrice.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.desktop DESTINATION ${DESKTOPDIR}) endif() elseif(WIN32) - install(TARGETS continuum-servatrice RUNTIME DESTINATION ./) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/continuum-servatrice.ini.example DESTINATION ./) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/continuum-servatrice.sql DESTINATION ./) + install(TARGETS servatrice RUNTIME DESTINATION ./) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.ini.example DESTINATION ./) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.sql DESTINATION ./) endif() if(APPLE) # these needs to be relative to CMAKE_INSTALL_PREFIX - set(plugin_dest_dir continuum-servatrice.app/Contents/Plugins) - set(qtconf_dest_dir continuum-servatrice.app/Contents/Resources) + set(plugin_dest_dir servatrice.app/Contents/Plugins) + set(qtconf_dest_dir servatrice.app/Contents/Resources) # Qt plugins: platforms, sqldrivers/mysql, tls (Qt6) install( @@ -180,7 +183,7 @@ Translations = Resources/translations\") \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\") set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/continuum-servatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\") + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/servatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\") " COMPONENT Runtime ) @@ -192,7 +195,7 @@ if(WIN32) set(qtconf_dest_dir .) install( - DIRECTORY "$/" + DIRECTORY "$/" DESTINATION ./ FILES_MATCHING PATTERN "*.dll" @@ -240,11 +243,8 @@ Translations = Resources/translations\") \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dll\") set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/continuum-servatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\") + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Servatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\") " COMPONENT Runtime ) endif() - -# Compatibility build target for existing CI commands. No 'servatrice' executable is emitted. -add_custom_target(servatrice DEPENDS continuum-servatrice) diff --git a/servatrice/continuum-servatrice.ini.example b/servatrice/continuum-servatrice.ini.example deleted file mode 100644 index 9d53e21cb..000000000 --- a/servatrice/continuum-servatrice.ini.example +++ /dev/null @@ -1,593 +0,0 @@ -; Continuum-Servatrice configuration file -; -; This is the main configuration file for Continuum-Servatrice; while using a configuration is not mandatory, -; you may want to customize some aspects of your continuum-servatrice instance, like its name, port or the way -; users can authenticate to the server. -; -; Can be passed to continuum-servatrice with --config /path/to/continuum-servatrice.ini - -[server] - -; This is the name that continuum-servatrice exposes to the users; the default value is pretty boring -name="My Continuum-Servatrice server" - -; Multiple continuum-servatrice servers can run on the same host using the same database; each server instance -; must have a different id; the default id is 1 -id=1 - -; The IP address continuum-servatrice will listen on for clients; defaults to "any" to listen on all the interfaces, -; a specific IPv4/v6 addresses can be used, eg for localhost-only 127.0.0.1 for IPv4 or ::1 for IPv6. - -host=any - -; The TCP port number Continuum-Servatrice will listen on for clients; default is 4757; -; Will be removed in the future, use websocket connection instead -port=4757 - -; Continuum-Servatrice can scale up to serve big number of users using more than one parallel thread of execution; -; If your server is hosting a lot of players and they frequently report of being unable to login or -; long delays (lag), you may want to try increasing this value; default is 1. -; Set to 0 to disable the tcp server. -number_pools=1 - -; Continuum-Servatrice can listen for clients on websockets, too. Multiple connection pools are available but -; unfortunately, due to a Qt limitation, they must run in the same execution thread. -; Set to 0 to disable the websocket server. -websocket_number_pools=1 - -; The IP address continuum-servatrice will listen on for websockets clients; defaults to "any" -websocket_host=any - -; The TCP port number continuum-servatrice will listen on for websockets clients; default is 4758 -websocket_port=4758 - -; If websockets are served through a reverse proxy (e.g. nginx), the real client address can be -; read from an HTTP header such as X-Real-IP or X-Forwarded-For. A comma-separated X-Forwarded-For -; list resolves to its rightmost entry, which is the one the proxy appended. The header is ONLY -; trusted when the websocket connection itself originates from an address listed in -; security/trusted_sources (default 127.0.0.1,::1), so that clients cannot spoof their address to -; bypass bans or rate limits. -web_socket_ip_header= - -; When database is enabled, continuum-servatrice writes the server status in the "update" database table; this -; setting defines every how many milliseconds continuum-servatrice will update its status; default is 15000 (15 secs) -statusupdate=15000 - -; Do you want continuum-servatrice to write important events and errors to a logfile? Default is 1 (yes). -writelog=1 - -; Choose a name for the log file, if enabled; you can specify an absolute path or a path relative to -; the continuum-servatrice executable; the default file name is server.log (in the same path as continuum-servatrice) -; Note: When running continuum-servatrice under windows you will need to use double backslashes between folder locations -; [ex: C:\\Temp\\server.log ] -logfile=continuum-servatrice.log - -; You may want to log only certain messages in the logfile. The default log level is extremely verbose. -; This setting should contain a comma-separated list of strings that will be selectively logged. -; All other lines will be excluded from the log. Default is empty; example: "Registration,_Login,foobar" -logfilters="" - -; Set the time interval in seconds that continuum-servatrice will use to communicate with each connected client -; to verify the client has not timed out. Defaults is 1 seconds -clientkeepalive=1 - -; Maximum time in seconds a player can stay inactive with there client not even responding to pings, before is -; considered disconnected; default is 15 -max_player_inactivity_time=15 - -; More modern clients generate client IDs based on specific client side information. Enable this option to -; require that clients report the client ID in order to log into the server. Default is false -requireclientid=false - -; You can limit the types of clients that connect to the server by requiring different features be available -; on the client. This setting can contain a comma-seperated list of features. if any of the features -; listed in this line are not available on the client the client will be denied access to the server upon -; attempting to log in. Example: "client_id,client_ver,websocket" -requiredfeatures="" - -; You can define custom warnings that users are sent when the moderation staff uses the right client warn user -; menu option. This list is comma seperated that each item will appear in the drop down list for staff members -; to choose from. Each entry may optionally carry a recommended starting intervention level (see the moderator -; guide) by appending "|" and the level number. Entries without an explicit level default to intervention -; level 1. Example: "Flaming,Foul Language" -officialwarnings="Abusive Language|1,Calling Out User|1,Causing Drama|1,Cheating|2,Disrespecting Staff|1,Disrupting a Draft|1,Inappropriate Avatar|3,Inappropriate Game Name|1,Kicking Without Valid Reason|1,Spamming|1,Targeted Harassment|2" - -; Maximum time in seconds a player can stay connected but idle. Default is 3600 (0 = disabled) -; Clients will be notified at the 90% time period of pending disconnection if they do not take action. -idleclienttimeout=3600 - -[authentication] - -; Continuum-Servatrice can authenticate users connecting. It currently supports 3 different authentication methods: -; * none: no authentication, accept every user; -; * password: require users to specify a common password to log in; -; * sql: authenticate users against the "users" table of the database; -; Please note that only the "sql" method permits to have registered users and store their data on the server. -method=none - -; if the chosen authentication method is password, here you can define the password your users will use to log in -password=123456 - -; Accept only registered users? default is false (accept unregistered users) -regonly=false - -[users] - -; The minimum length a username can be -minnamelength=6 - -; The maximum length a username can be -maxnamelength=12 - -; If a username should be allowed to contain lowercase chars [a-z] -allowlowercase=true - -; If a username should be allowed to conatain uppercase chars [A-Z] -allowuppercase=true - -; If a username should be allowed to contain numbers [0-9] -allownumerics=true - -; Define punctuation allowed in usernames -allowedpunctuation=_.- - -; If a username can begin with punctuation defined in allowedpunctuation -allowpunctuationprefix=false - -; Disallow usernames containing these words. This list is comma seperated, e.g. -; "admin,user,name" -disallowedwords="admin" - -; Overwrite the words shown to the user when they enter a wrong username, -; use \n to start a new line. Neither the real wordlist nor the disallowed -; expressions will be sent to the user if this is set. -; In the old versions of the client this list will be prefaced with -; "can not contain any of the following words:" -; example: -;displaydisallowedwords="no attempts at impersonating staff\nno unparliamentary language\nno references to controversial figures\nstaff reserves the right to remove accounts deemed inappropriate" -; Setting it to nothing will simply hide the list: -;displaydisallowedwords= - -; Disallow usernames matching these regular expressions. This list is comma -; separated, e.g. "\\w+\\d+,\\d{2}user", hence you cannot use commas in your -; expressions. Backslashes must be escaped, so `\w+\d+` becomes `\\w+\\d+`. -; WARNING: Complex expressions can be harmful to performance. Please make sure -; your expressions are considered well formed. See this page for info: -; http://www.regular-expressions.info/catastrophic.html -disallowedregexp="" - -; Define minimum password length -; Default 6. -minpasswordlength = 6 - -[registration] - -; Continuum-Servatrice can process registration requests to add new users on the fly. -; Enable this feature? Default false. -;enabled=false - -; Require users to provide an email address in order to register. Default true. -;requireemail=true - -; Require email activation. Newly registered users will receive an activation token by email, -; and will be required to input back this token on cockatrice at the first login to get their -; account activated. Default true. -;requireemailactivation=true - -; Set this number to the maximum number of accounts any one user can use to create new accounts -; using the same email address. 0 = Unlimited number of accounts (default). -;maxaccountsperemail=0 - -; You can prevent users from using certain mail domains for registration. This setting contains a -; comma-seperated list of email provider domains that you would like to prevent users from using -; during registration. Comparison's are implicit, so placing an entry such as mail.com will also -; prevent users from registering accounts with providers such as gmail.com and hotmail.com -; Example: "10minutemail.com,gmail.com" -;emailproviderblacklist="" - -; You can require users to only use certain email domains for registration. This setting is a -; comma-separated list of email provider domains that you have explicitly audited and require -; the use of in order to create an account. Comparison's are explicit, so you must specify the -; domain in completion, such as gmail.com and hotmail.com. Email whitelist is checked before -; Email blacklist is checked, so an email cannot be in both setting configurations. -; Example: "gmail.com,hotmail.com,icloud.com" -;emailproviderwhitelist="" - -[forgotpassword] - -; Continuum-Servatrice can process reset password requests allowing users to reset their account -; passwords in the event they forget it. Should this feature be enabled? Default: false. -; enable=false - -; Reset password request should not be allowed to stay valid forever. This settings -; informs continuum-servatrice how long a players reset password reset token is valid for (in minutes). -; Default: 60 -; tokenlife=60 - -; Continuum-Servatrice can challenge users that are making reset password requests to answer -; questions in regards to their account to help validate they are the true owner of the account. -; Should this feature be enabled? Default: false -; enablechallenge=false - -; Email subject for the reset password emails -; subject="Continuumatrice reset password token" - -; Reset password email body. You can use these tags here: %username %token -; They will be substituted with the actual values in the email -; -; body="Hi %username,\r\nthanks for reaching out to us with your password reset request for our Continuumatrice server.\r\nHere's your unique token in order to reset your account password in the app:\r\n\r\n%token\r\n\r\nHappy gaming!" - - -[smtp] - -; Enable the internal smtp client to send registration emails. If you would like to -; use some other method to send email activation tokens set this value to false. Otherwise -; setting it to true (default) the server will send canned generated emails containing -; activation tokens for you during update intervals. Setting this to false will require -; you to either manually activate user accounts or manually send users the activation token -; by whatever means. -enableinternalsmtpclient=true - -; Connectin type: currently supported method are "tcp" and "ssl"; tls is autodetected if available -connection=tcp - -; Accept all certificates: in ssl mode, enable this if your server is using an invalid/self signed certificate -acceptallcerts=false; - -; Hostname or IP addres of the smtp server -host=localhost - -; Smtp port number of the smtp server. Usual values are 25 or 587 for tcp, 465 for ssl -port=25 - -; Username: this typically matches the "from" email address -username=root@localhost - -; Password for the username -password=foobar - -; Sender email address: the "from" email address -email=root@localhost - -; Sender email name -name="Continuumatrice server" - -; Maximum number of attempts to deliver a given e-mail before giving up. -; If the smtp server is unreachable or repeatedly rejects a message, the server -; will retry with an exponentially growing delay up to this many times, then drop -; the message from the queue and log the failure. Default: 5 -maxretries=5 - -; Initial retry delay in seconds between delivery attempts. Doubles with each -; failed attempt (capped at 30 minutes). Default: 60 -retrydelay=60 - -; Email subject -subject="Continuumatrice server account activation token" - -; Email body. You can use these tags here: %username %token -; They will be substituted with the actual values in the email -; -body="Hi %username, thank our for registering on our Continuumatrice server\r\nHere's the activation token you need to supply for activating your account:\r\n\r\n%token\r\n\r\nHappy gaming!" - -[database] - -; Database type. Valid values are: -; * none: no database; -; * mysql: mysql or compatible database; -type=none - -; Prefix used in he database for table names; default is continuum_servatrice -prefix=continuum_servatrice - -; Database connection parameter: server hostname or IP -hostname=localhost - -; Database connection parameter: database name -database=continuum_servatrice - -; Database connection parameter: database user -user=continuum_servatrice - -; Database connection parameter: database user's password -password=foobar - -[rooms] - -; A continuum-servatrice server can expose to the users different "rooms" to chat and create games. Rooms can be defined -; with two different methods: -; config: rooms are defined in this configuration (see the following example) -; sql: rooms are defined in the "rooms" table of the database -method=config - -; Example configuration for a server with rooms configured in the configuration file. Number of rooms defined -roomlist\size=1 - -; Room name for the room number 1 -roomlist\1\name="General room" - -; Room description for the room number 1 -roomlist\1\description="Play anything here." - -; Rooms can restrict the level of user that can join. Current supported options are none, registered, moderator, administrator. -; Default is none. -roomlist\1\permissionlevel=none - -; Rooms can restrict the permission level of users that can join, Currnetly supported options are none, privileged, vip, and donator. -; Default is none. -roomlist\1\privilegelevel=none - -; Wether to make users autojoin this room when connected to the server -roomlist\1\autojoin=true - -; Message displayed to each user when he joins room number 1 -roomlist\1\joinmessage="This message is only here to show that rooms can have a join message." - -; The number of chat history messages to save that gets presented to a user joining the room -roomlist\1\chathistorysize=100 - -; Number of game types allowed (defined) in the room number 1 -roomlist\1\game_types\size=3 - -; Name of the three game types for the room number 1 -roomlist\1\game_types\1\name="GameType1" -roomlist\1\game_types\2\name="GameType2" -roomlist\1\game_types\3\name="GameType3" - -; Optional chat channels for room number 1, in addition to the implicit Main -; channel every room has. Each channel needs a stable ASCII id, a display name -; (server data, like the room name) and an access level. Supported levels, from -; least to most restrictive, are: public, registered, judge, moderator, admin -; and developer. Access is evaluated against the member's user flags, not as a -; ladder: a moderator channel admits moderators (admins hold the moderator -; flag alongside the admin flag, so they are admitted too), an admin channel -; admits only admins, and a developer channel admits only developers and -; admins. In particular, developers cannot read moderator or admin channels. -; A judge channel additionally admits moderators. -; Public channels are visible to every room member. -; -; Write access defaults to the read level, so by default a channel is readable -; and writable by the same members. Set writeaccess higher for read-mostly -; channels (e.g. a developer news feed readable by everyone but writable only -; by developers). Members who can read but not write see the channel with a -; disabled message input. -; -; banner is an optional static notice shown at the top of the channel, similar -; to the room joinmessage on Main. Empty or unset = none. -roomlist\1\channels\size=3 -roomlist\1\channels\1\id=looking-for-players -roomlist\1\channels\1\displayname="Looking for players" -roomlist\1\channels\1\access=public -roomlist\1\channels\1\banner="Find opponents here. Be friendly!" -roomlist\1\channels\2\id=moderators -roomlist\1\channels\2\displayname="Moderators" -roomlist\1\channels\2\access=moderator -roomlist\1\channels\3\id=developer-news -roomlist\1\channels\3\displayname="Developer news" -roomlist\1\channels\3\access=public -roomlist\1\channels\3\writeaccess=developer -roomlist\1\channels\3\banner="Official announcements from the developers." - - -[game] - -; Maximum time in seconds all players in a game can stay inactive before the game is automatically closed; -; default is 120 -max_game_inactivity_time=120 - -; All actions during a game are recorded and stored in the database as a replay that all participants of -; the game can go back to and review after the game is closed. This can require a fairly large amount of -; storage to save all the information. Disable this option to prevent the storing of replay data in -; the database. Default value is true. -store_replays=true - -; Allow users to create a new game and join it as a judge. The host will be able to execute any action on -; the cards of every player. This is needed in order to support some games (eg. Werewolf). -; Default off to prevent abuse on servers that are mostly running other games. -allow_create_as_judge=false - -[security] -; You may want to restrict the number of users that can connect to your server at any given time. -enable_max_user_limit=false - -; Maximum number of users that can connect to the server, default is 500. -max_users_total=500 - -; Maximum number of users that can connect to the server using a tcp connection, default is 500. -max_users_tcp=500 - -; Maximum number of users that can connect to the server using a websocket connection, default is 500. -max_users_websocket=500 - -; Maximum number of users that can connect from the same IP address; useful to avoid bots, default is 4 -max_users_per_address=4 - -; Maximum number of failed login attempts from a single IP address before that -; address is temporarily locked out. Default is 5; set to 0 to disable. -max_login_attempts_per_ip=5 - -; Length in seconds of the sliding window used for the login lockout. Default is 900 (15 minutes). -login_attempt_window_seconds=900 - -; Maximum number of account registrations from a single IP address within the window below. -; Default is 2; set to 0 to disable. -max_registrations_per_ip=2 - -; Length in seconds of the registration window. Default is 3600 (1 hour). -registration_window_seconds=3600 - -; Maximum number of forgot-password requests from a single IP address within the window below. -; Default is 3; set to 0 to disable. -max_forgot_password_requests_per_ip=3 - -; Length in seconds of the forgot-password window. Default is 3600 (1 hour). -forgot_password_window_seconds=3600 -; How strictly new authentication features are enforced. Possible values: -; * legacy: accounts that still use the legacy 1000-round SHA-512 hash keep logging in with it -; (they are served the legacy salt, never a challenge-response nonce) and are never -; auto-migrated to scrypt. Already-migrated scrypt rows keep logging in via -; challenge-response. New credentials may use either format; -; * mixed: accept both legacy hashes and challenge-response authentication (default); -; * strict: only accept challenge-response authentication from clients that support it, -; and reject plain password submissions. Legacy accounts are migrated to scrypt -; automatically on their next successful login. Strict only constrains -; registered SQL accounts: guest/unregistered logins and servers with -; authentication/method set to none or password are unaffected, since -; they have no stored verifier to challenge against. -; -; Note: anyone holding only the legacy salt+hash of a not-yet-migrated account -; (e.g. from a wire capture of an old client, or an old database dump) can log in -; with it and then set a brand new password of their choosing. Before -; challenge-response a captured hash yielded a session, but changing the password -; still required the plaintext old password. There is no way around this without -; a plaintext proof at migration time, so not-yet-migrated rows should be treated -; accordingly. -; -; Challenge-response verifiers are scrypt (RFC 7914, N=32768, r=8, p=1) stored as -; "$scrypt$$$

$$". The stored verifier is password-equivalent: -; plaintext passwords never reach the client configuration and never go over the wire, but -; a database dump yields credentials that can answer a login challenge directly. -authentication_strictness=mixed - -; Optional key that seals the legacy password backup (64 hex characters). -; Migrating an account to scrypt normally strands old clients, which can only present -; the legacy hash. With a key configured, the server keeps that hash encrypted next to -; the scrypt verifier, so old clients can still log in during the window below without -; the database holding a fast-crackable hash in the clear. -; Generate with: openssl rand -hex 32 -; If unset there is no downgrade path at all: a migrated account keeps only a scrypt -; verifier, and an administrator's password reset stores a verifier too, so pre-challenge -; clients cannot log in with the temporary password either. Set the key if you still serve -; them. Rotating the key invalidates existing backups (they become unusable immediately; -; expired ones are still purged). -legacy_backup_key= - -; Days a legacy password backup may go unused before it is purged from the database. -; Logging in with an old client refreshes the window; backups that are never touched -; age out. Default 180. -legacy_backup_ttl_days=180 - -; You may want to allow an unlimited number of users from a trusted source. This setting can contain a -; comma-separed list of IP addresses which will allow an unlimited number of connections from each of the -; IP addresses listed (ignoring the max_users_per_address). Entries must be exact IP addresses; hostnames, prefixes -; and subnets are ignored. Default is "127.0.0.1,::1"; example: "192.73.233.244,81.4.100.74" -trusted_sources="127.0.0.1,::1" - -; Continuum-Servatrice can avoid users from flooding rooms with large number of messages in an interval of time. -; This setting defines the length in seconds of the considered interval; default is 10 -message_counting_interval=10 - -; Maximum size in characters of all messages in an interval before new messages gets dropped; default is 1000 -max_message_size_per_interval=1000 - -; Maximum number of messages in an interval before new messages gets dropped; default is 10 -max_message_count_per_interval=10 - -; Maximum number of games a single user can create; default is 5; set to -1 to disable; 0 disallows game creation -max_games_per_user=5 - -; Continuum-Servatrice can avoid users from flooding games with large number of game commands in an interval of time. -; This setting defines the length in seconds of the considered interval; default is 10 -command_counting_interval=10 - -; Maximum number of game commands in an interval before new commands gets dropped; default is 20 -max_command_count_per_interval=20 - -[reporting] -; Maximum number of user reports a single user can file per day; default is 10; set to 0 to disable the limit -max_reports_per_day=10 - -; Maximum number of report comments a single user can post per hour; default is 30; set to 0 to disable the limit -max_comments_per_hour=30 - -[metrics] -; Command containers that take longer than this many milliseconds are logged -; as slow commands. Set to 0 to disable the log line. A latency spike produces -; one warning per slow container with no rate limiting of its own -- a bad -; patch can briefly flood the log, which is how you notice it. -slow_command_ms=500 - -; Each socket pool thread runs a watchdog heartbeat. If a heartbeat arrives -; this many milliseconds late, the stall is logged and exposed as -; continuum-servatrice_eventloop_* metrics in the Developer tab. Set to 0 to disable -; the watchdogs. -stall_warn_ms=2000 - -[logging] -; Admin/Moderators can query the stored logs for information when looking up reports by various players. This -; option can allow or disallow them from doing so. -; !!NOTE!! Enabling this feature puts a very high CPU and DISK load on the server, enable with caution. -enablelogquery=false - -; Continuum-Servatrice can log user messages to the database table cockatrice_log. -; These messages can come from different sources; each source can be enabled separately. - -; Log user messages inside chat rooms -log_user_msg_room=false - -; Log user messages inside games -log_user_msg_game=false - -; Log user messages in private chats -log_user_msg_chat=false - -; Log user messages coming from other servers in the network -log_user_msg_isl=false - -[audit] - -; Continuum-Servatrice can record certain actions being performed in the database for server operators to better understand -; if some one may be abusing application functionality. Enabling auditing will allow continuum-servatrice to record any -; of the below enabled audit functionality to be recorded. -; Default: true -enable_audit=true - -; Continuum-Servatrice can record when users attempt a new account registration. Should we enable auditing for this action? -; Default: true -enable_registration_audit=true - -; Continuum-Servatrice can record when a users attempts to reset the account password. Should we enable auditing for this action? -; Default: true -enable_forgotpassword_audit=true - - -; EXPERIMENTAL - NOT WORKING YET -; The following settings are relative to the server network functionality, that is not yet complete. -; Avoid enabling it unless you are willing to test it and help its development. - -[servernetwork] - -; Continuum-Servatrice servers can connect themselves and build a network. This settins enable the ability of continuum-servatrice -; of waiting for other server's connections and connect to other servers. Other servers can be defined in the -; "servers" table of the database. Default is 0 (disabled) -active=0 - -; The TCP port number Continuum-Servatrice will listen on for other servers; default is 14757 -port=14757 - -; Server-to-server communication needs a valid certificate in PEM format. Enter its filename in this setting -ssl_cert=ssl_cert.pem - -; Filename of the private key for the server-to-server certificate -ssl_key=ssl_key.pem - - -[deck_share] - -; How many days a created deck share link remains valid before it expires. -; Default: 7 -expiry_days=7 - -; How often (in minutes) the server checks for and removes expired deck shares. -; A value of 0 disables the automatic cleanup. -; Default: 60 -cleanup_interval=60 - -; Maximum number of decks a single share link can contain. -; Default: 50 -max_decks_per_share=50 - -; Maximum number of share links a single user may create per day. -; A value of 0 disables the limit. -; Default: 50 -max_shares_per_day=50 diff --git a/servatrice/continuum-servatrice.sql b/servatrice/continuum-servatrice.sql deleted file mode 100644 index bea8036ed..000000000 --- a/servatrice/continuum-servatrice.sql +++ /dev/null @@ -1,424 +0,0 @@ --- Schema file for Continuum-Servatrice database. - --- This schema file is using the default table prefix "continuum_servatrice", --- to match the "prefix=continuum_servatrice" default setting in continuum-servatrice.ini - -SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; - - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; - --- Every time the database schema changes, the schema version number --- must be incremented. Also remember to update the corresponding --- number in servatrice/src/servatrice_database_interface.h - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_schema_version` ( - `version` int(7) unsigned NOT NULL, - PRIMARY KEY (`version`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -INSERT INTO continuum_servatrice_schema_version VALUES(40); - --- users and user data tables -CREATE TABLE IF NOT EXISTS `continuum_servatrice_users` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - -- Bitfield of staff levels: 1 = admin (implies moderator), 2 = moderator, - -- 4 = judge, 8 = developer. Operators set these by hand with - -- "UPDATE continuum_servatrice_users SET admin = ...". - `admin` tinyint(1) NOT NULL, - `name` varchar(35) NOT NULL, - `realname` varchar(255) NOT NULL, - `password_sha512` varchar(255) NOT NULL, - `password_legacy_backup` varchar(255) NOT NULL DEFAULT '', - `password_legacy_backup_used_at` datetime DEFAULT NULL, - `email` varchar(255) NOT NULL, - `country` char(2) NOT NULL, - `avatar_bmp` mediumblob NOT NULL, - `registrationDate` datetime NOT NULL, - `active` tinyint(1) NOT NULL, - `token` binary(16), - `clientid` varchar(15) NOT NULL, - `adminnotes` mediumtext NOT NULL, - `privlevel` enum("NONE","VIP","DONATOR") NOT NULL, - `privlevelStartDate` datetime NOT NULL, - `privlevelEndDate` datetime NOT NULL, - `passwordLastChangedDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `force_password_change` tinyint(1) NOT NULL DEFAULT 0, - `leftPawnColorOverride` varchar(255), - `rightPawnColorOverride` varchar(255), - `card_art_params` TEXT DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`), - KEY `token` (`token`), - KEY `email` (`email`), - INDEX `idx_admin` (`admin`), - INDEX `idx_active` (`active`), - INDEX `idx_privlevel` (`privlevel`), - INDEX `idx_clientid` (`clientid`), - INDEX `idx_pawnColorOverrides` (`leftPawnColorOverride`, `rightPawnColorOverride`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_decklist_files` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - `id_folder` int(7) unsigned zerofill NOT NULL, - `id_user` int(7) unsigned NULL, - `name` varchar(50) NOT NULL, - `upload_time` datetime NOT NULL, - `content` text NOT NULL, - `is_public` tinyint(1) NOT NULL DEFAULT 0, - `banner_card_name` varchar(255) NULL, - `banner_card_provider` varchar(32) NULL, - `color_identity` varchar(5) NULL, - `tags` text NULL, - PRIMARY KEY (`id`), - KEY `FolderPlusUser` (`id_folder`,`id_user`), - FOREIGN KEY(`id_user`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - --- Temporary deck shares: a named bundle of decks that can be fetched by --- anyone who knows the (unguessable) token, until the share expires. -CREATE TABLE IF NOT EXISTS `continuum_servatrice_deck_share` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - `token` varchar(64) COLLATE utf8mb4_bin NOT NULL, - `name` varchar(64) NOT NULL, - `created_by` int(7) unsigned NULL, - `created_at` datetime NOT NULL, - `expires_at` datetime NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `token` (`token`), - KEY `expires_at` (`expires_at`), - FOREIGN KEY(`created_by`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - --- Individual decks inside a share bundle. Content is materialized at share --- time so expiring/deleting a share can cascade cleanly. The metadata columns --- are nullable because Qt's MySQL driver binds empty QStrings as NULL when --- using prepared statements. -CREATE TABLE IF NOT EXISTS `continuum_servatrice_deck_share_item` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - `share_id` int(7) unsigned zerofill NOT NULL, - `name` varchar(50) NOT NULL, - `tags` text NULL, - `banner_card` varchar(255) NULL, - `game_format` varchar(50) NULL, - `color_identity` varchar(5) NULL, - `content` text NOT NULL, - `position` int(7) NOT NULL, - PRIMARY KEY (`id`), - KEY `share_id` (`share_id`), - FOREIGN KEY(`share_id`) REFERENCES `continuum_servatrice_deck_share`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_decklist_folders` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - `id_parent` int(7) unsigned zerofill NOT NULL, - `id_user` int(7) unsigned NULL, - `name` varchar(30) NOT NULL, - `is_public` tinyint(1) NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - KEY `ParentPlusUser` (`id_parent`,`id_user`), - FOREIGN KEY(`id_user`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_ignorelist` ( - `id_user1` int(7) unsigned NOT NULL, - `id_user2` int(7) unsigned NOT NULL, - UNIQUE KEY `key` (`id_user1`, `id_user2`), - FOREIGN KEY(`id_user1`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY(`id_user2`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_buddylist` ( - `id_user1` int(7) unsigned NOT NULL, - `id_user2` int(7) unsigned NOT NULL, - UNIQUE KEY `key` (`id_user1`, `id_user2`), - FOREIGN KEY(`id_user1`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY(`id_user2`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - --- rooms -CREATE TABLE IF NOT EXISTS `continuum_servatrice_rooms` ( - `id` int(7) unsigned NOT NULL auto_increment, - `name` varchar(50) NOT NULL, - `descr` varchar(255) NOT NULL, - `permissionlevel` enum('NONE','REGISTERED','MODERATOR','ADMINISTRATOR') NOT NULL, - `privlevel` enum('NONE','PRIVILEGED','VIP','DONATOR') NOT NULL, - `auto_join` tinyint(1) default 0, - `join_message` varchar(255) NOT NULL, - `chat_history_size` int(4) NOT NULL, - `id_server` tinyint(3) NOT NULL DEFAULT 1, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_rooms_gametypes` ( - `id_room` int(7) unsigned NOT NULL, - `name` varchar(50) NOT NULL, - `id_server` tinyint(3) NOT NULL DEFAULT 1, - FOREIGN KEY(`id_room`) REFERENCES `continuum_servatrice_rooms`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_rooms_channels` ( - `id_room` int(7) unsigned NOT NULL, - `id_server` tinyint(3) NOT NULL DEFAULT 1, - `channel_id` varchar(50) NOT NULL, - `display_name` varchar(50) NOT NULL, - `access_level` ENUM('public','registered','judge','moderator','admin','developer') NOT NULL DEFAULT 'public', - `write_access_level` ENUM('public','registered','judge','moderator','admin','developer') NULL DEFAULT NULL, - `banner_message` varchar(255) NOT NULL DEFAULT '', - PRIMARY KEY (`id_room`, `id_server`, `channel_id`), - FOREIGN KEY(`id_room`) REFERENCES `continuum_servatrice_rooms`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - --- games -CREATE TABLE IF NOT EXISTS `continuum_servatrice_games` ( - `room_name` varchar(255) NOT NULL, - `id` int(7) unsigned NOT NULL auto_increment, - `descr` varchar(50) default NULL, - `creator_name` varchar(35) NOT NULL, - `password` tinyint(1) NOT NULL, - `game_types` varchar(255) NOT NULL, - `player_count` tinyint(3) NOT NULL, - `time_started` datetime default NULL, - `time_finished` datetime default NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_games_players` ( - `id_game` int(7) unsigned zerofill NOT NULL, - `player_name` varchar(35) NOT NULL, - FOREIGN KEY(`id_game`) REFERENCES `continuum_servatrice_games`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - --- Note: an empty row with id_game = NULL is created when the game is created, --- and then updated when the game ends with the full replay data. -CREATE TABLE IF NOT EXISTS `continuum_servatrice_replays` ( - `id` int(7) NOT NULL AUTO_INCREMENT, - `id_game` int(7) unsigned NULL, - `duration` int(7) NOT NULL, - `replay` mediumblob NOT NULL, - PRIMARY KEY (`id`), - FOREIGN KEY(`id_game`) REFERENCES `continuum_servatrice_games`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_replays_access` ( - `id_game` int(7) unsigned NOT NULL, - `id_player` int(7) unsigned NOT NULL, - `replay_name` varchar(255) NOT NULL, - `do_not_hide` tinyint(1) NOT NULL, - KEY `id_player` (`id_player`), - FOREIGN KEY(`id_game`) REFERENCES `continuum_servatrice_games`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY(`id_player`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - --- server administration - --- Note: unused table -CREATE TABLE IF NOT EXISTS `continuum_servatrice_servers` ( - `id` mediumint(8) unsigned NOT NULL, - `ssl_cert` text NOT NULL, - `hostname` varchar(255) NOT NULL, - `address` varchar(255) NOT NULL, - `game_port` mediumint(8) unsigned NOT NULL, - `control_port` mediumint(9) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_uptime` ( - `id_server` tinyint(3) NOT NULL, - `timest` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `uptime` int(11) NOT NULL, - `users_count` int(11) NOT NULL, - `mods_count` int(11) NOT NULL DEFAULT 0, - `mods_list` TEXT, - `games_count` int(11) NOT NULL, - `rx_bytes` int(11) NOT NULL, - `tx_bytes` int(11) NOT NULL, - PRIMARY KEY (`timest`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_servermessages` ( - `id_server` tinyint(3) not null default 1, - `timest` datetime NOT NULL default '0000-00-00 00:00:00', - `message` text, - PRIMARY KEY (`timest`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_sessions` ( - `id` int(9) NOT NULL AUTO_INCREMENT, - `user_name` varchar(35) NOT NULL, - `id_server` tinyint(3) NOT NULL, - `ip_address` varchar(45) NOT NULL, - `start_time` datetime NOT NULL, - `end_time` datetime DEFAULT NULL, - `clientid` varchar(15) NOT NULL, - `connection_type` ENUM('tcp', 'websocket'), - PRIMARY KEY (`id`), - KEY `username` (`user_name`), - INDEX `idx_start_time` (`start_time`), - INDEX `idx_clientid` (`clientid`), - INDEX `idx_ip_address` (`ip_address`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - --- server moderation -CREATE TABLE IF NOT EXISTS `continuum_servatrice_bans` ( - `user_name` varchar(35) NOT NULL, - `ip_address` varchar(45) NOT NULL, - `id_admin` int(7) unsigned zerofill NOT NULL, - `time_from` datetime NOT NULL, - `minutes` int(6) NOT NULL, - `reason` text NOT NULL, - `visible_reason` text NOT NULL, - `clientid` varchar(15) NOT NULL, - PRIMARY KEY (`user_name`,`time_from`), - KEY `time_from` (`time_from`,`ip_address`), - KEY `ip_address` (`ip_address`), - INDEX `idx_user_name` (`user_name`), - FOREIGN KEY(`id_admin`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_warnings` ( - `user_id` int(7) unsigned NOT NULL, - `user_name` varchar(35) NOT NULL, - `mod_name` varchar(35) NOT NULL, - `reason` text NOT NULL, - `time_of` datetime NOT NULL, - `clientid` varchar(15) NOT NULL, - PRIMARY KEY (`user_id`,`time_of`), - INDEX `idx_time_of` (`time_of`), - INDEX `idx_user_name` (`user_name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_reports` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `reporter_id` int(7) unsigned NULL, - `reporter_name` varchar(35) NOT NULL, - `reported_user_id` int(7) unsigned NULL, - `reported_user_name` varchar(35) NOT NULL, - `game_id` int(7) unsigned NULL, - `room_id` int(7) unsigned NULL, - `category` varchar(255) NOT NULL, - `description` text NOT NULL, - `chat_log` mediumtext NULL, - `created_at` datetime NOT NULL, - `resolution_time` datetime NULL, - `status` enum('open','assigned','resolved','dismissed') NOT NULL DEFAULT 'open', - `assigned_to` int(7) unsigned NULL, - `resolved_by` int(7) unsigned NULL, - `resolution_note` text, - `notified` tinyint(1) NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - INDEX `idx_status` (`status`), - INDEX `idx_created` (`created_at`), - INDEX `idx_reporter_id_created` (`reporter_id`, `created_at`), - INDEX `idx_reported_user_name` (`reported_user_name`), - INDEX `idx_status_created` (`status`, `created_at`), - FOREIGN KEY (`reporter_id`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, - FOREIGN KEY (`reported_user_id`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, - FOREIGN KEY (`assigned_to`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, - FOREIGN KEY (`resolved_by`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_report_comments` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `report_id` int(11) unsigned NOT NULL, - `author_name` varchar(35) NOT NULL, - `author_id` int(7) unsigned NULL, - `comment_text` text NOT NULL, - `created_at` datetime NOT NULL, - `is_moderator` tinyint(1) NOT NULL DEFAULT 0, - `notified` tinyint(1) NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - INDEX `idx_report_id` (`report_id`), - INDEX `idx_notified` (`notified`), - FOREIGN KEY (`report_id`) REFERENCES `continuum_servatrice_reports`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY (`author_id`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_log` ( - `log_time` datetime NOT NULL, - `sender_id` int(7) unsigned NULL, - `sender_name` varchar(35) NOT NULL, - `sender_ip` varchar(45) NOT NULL, - `log_message` text NOT NULL, - `target_type` ENUM('room', 'game', 'chat', 'room_channel'), - `target_id` int(7) NULL, - `target_name` varchar(50) NOT NULL, - `channel` varchar(50) NULL, - KEY `sender_name` (`sender_name`), - KEY `sender_ip` (`sender_ip`), - KEY `target_id` (`target_id`), - KEY `target_name` (`target_name`), - INDEX `idx_log_time` (`log_time`), - FOREIGN KEY(`sender_id`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE - -- No FK on target_id, it can be zero -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_activation_emails` ( - `name` varchar(35) NOT NULL, - FOREIGN KEY(`name`) REFERENCES `continuum_servatrice_users`(`name`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_user_analytics` ( - `id` int(7) unsigned zerofill NOT NULL, - `client_ver` varchar(35) NOT NULL, - `last_login` datetime NOT NULL, - `notes` varchar(255) NOT NULL, - PRIMARY KEY (`id`), - INDEX `idx_last_login` (`last_login`), - FOREIGN KEY(`id`) REFERENCES `continuum_servatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_donations` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `username` varchar(35) DEFAULT NULL, - `email` varchar(255) DEFAULT NULL, - `payment_pre_fee` double DEFAULT NULL, - `payment_post_fee` double DEFAULT NULL, - `term_length` int(11) DEFAULT NULL, - `date` varchar(255) DEFAULT NULL, - `pp_type` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_forgot_password` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - `name` varchar(35) NOT NULL, - `requestDate` datetime NOT NULL default '0000-00-00 00:00:00', - `emailed` tinyint(1) NOT NULL default 0, - PRIMARY KEY (`id`), - KEY `user_name` (`name`), - INDEX `idx_emailed` (`emailed`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_audit` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - `id_server` tinyint(3) NOT NULL, - `name` varchar(35) NOT NULL, - `ip_address` varchar(45) NOT NULL, - `clientid` varchar(15) NOT NULL, - `incidentDate` datetime NOT NULL default '0000-00-00 00:00:00', - `action` varchar(35) NOT NULL, - `results` ENUM('fail', 'success') NOT NULL DEFAULT 'fail', - `details` varchar(255) NOT NULL, - PRIMARY KEY (`id`), - KEY `user_name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `continuum_servatrice_card_art_name_rules` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `card_name` varchar(255) NOT NULL, - `card_provider_id` varchar(255) NOT NULL, - `mode` enum('ALLOW','DENY') NOT NULL, - `reason` varchar(255) DEFAULT NULL, - `created_by` int(7) unsigned DEFAULT NULL, - `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uniq_provider_card_name` (`card_provider_id`, `card_name`), - KEY `idx_mode` (`mode`), - FOREIGN KEY (`created_by`) REFERENCES `continuum_servatrice_users`(`id`) - ON DELETE SET NULL - ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci; diff --git a/servatrice/src/main.cpp b/servatrice/src/main.cpp index bbad3463d..4d513d026 100644 --- a/servatrice/src/main.cpp +++ b/servatrice/src/main.cpp @@ -130,8 +130,8 @@ void myMessageOutput2(QtMsgType /*type*/, const QMessageLogContext &, const QStr int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); - QCoreApplication::setOrganizationName("Continuum Universes Studio"); - QCoreApplication::setApplicationName("Continuum-Servatrice"); + QCoreApplication::setOrganizationName("Cockatrice"); + QCoreApplication::setApplicationName("Servatrice"); QCoreApplication::setApplicationVersion(VERSION_STRING); QCommandLineParser parser; @@ -188,7 +188,7 @@ int main(int argc, char *argv[]) rng = new RNG_SFMT(CryptoUtil::randomUInt64()); - std::cerr << "Continuum-Servatrice " << VERSION_STRING << " starting." << std::endl; + std::cerr << "Servatrice " << VERSION_STRING << " starting." << std::endl; std::cerr << "-------------------------" << std::endl; if (testRandom) { diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 4ec0f36c0..afe028f32 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -1270,9 +1270,9 @@ QString Servatrice::getDBTypeString() const QString Servatrice::getDBPrefixString() const { if (QProcessEnvironment::systemEnvironment().contains("DATABASE_URL")) { - return {"continuum_servatrice"}; + return {"cockatrice"}; } - return settingsCache->value("database/prefix", "continuum_servatrice").toString(); + return settingsCache->value("database/prefix").toString(); } QString Servatrice::getDBHostNameString() const @@ -1419,7 +1419,7 @@ QHostAddress Servatrice::getServerTCPHost() const int Servatrice::getServerTCPPort() const { - return settingsCache->value("server/port", 4757).toInt(); + return settingsCache->value("server/port", 4747).toInt(); } int Servatrice::getNumberOfWebSocketPools() const @@ -1442,7 +1442,7 @@ int Servatrice::getServerWebSocketPort() const if (QProcessEnvironment::systemEnvironment().contains("PORT")) { return QProcessEnvironment::systemEnvironment().value("PORT").toInt(); } - return settingsCache->value("server/websocket_port", 4758).toInt(); + return settingsCache->value("server/websocket_port", 4748).toInt(); } bool Servatrice::getISLNetworkEnabled() const diff --git a/servatrice/src/settingscache.cpp b/servatrice/src/settingscache.cpp index 9344e2527..4ecac7319 100644 --- a/servatrice/src/settingscache.cpp +++ b/servatrice/src/settingscache.cpp @@ -27,7 +27,7 @@ SettingsCache::SettingsCache(const QString &fileName, QSettings::Format format, QString SettingsCache::guessConfigurationPath() { - const QString fileName = "continuum-servatrice.ini"; + const QString fileName = "servatrice.ini"; if (QFile::exists(qApp->applicationDirPath() + "/portable.dat")) { qDebug() << "Portable mode enabled"; return fileName; @@ -43,7 +43,7 @@ QString SettingsCache::guessConfigurationPath() #ifdef Q_OS_UNIX // /etc - guessFileName = "/etc/continuum-servatrice/" + fileName; + guessFileName = "/etc/servatrice/" + fileName; if (QFile::exists(guessFileName)) { return guessFileName; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 478c32aec..6acd225c2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -172,13 +172,6 @@ endfunction() # --- Unit tests ------------------------------------------------------------------ cockatrice_add_test(NAME expression_test SOURCES expression_test.cpp LIBS libcockatrice_utility) -# XML v3/v4 exporters in libcockatrice_card reference VERSION_STRING. -# Link the same generated version implementation used by the client/server. -cockatrice_add_test( - NAME essence_types_test - SOURCES ${VERSION_STRING_CPP} essence_types_test.cpp - LIBS libcockatrice_card -) cockatrice_add_test(NAME clamped_arithmetic_test SOURCES clamped_arithmetic_test.cpp LIBS libcockatrice_utility) cockatrice_add_test(NAME test_age_formatting SOURCES test_age_formatting.cpp LIBS libcockatrice_utility) cockatrice_add_test(NAME user_data_backup_test SOURCES user_data_backup_test.cpp LIBS libcockatrice_utility) diff --git a/tests/carddatabase/carddatabase_test.cpp b/tests/carddatabase/carddatabase_test.cpp index 833365345..22cbe9074 100644 --- a/tests/carddatabase/carddatabase_test.cpp +++ b/tests/carddatabase/carddatabase_test.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -36,7 +35,6 @@ #include #include #include -#include namespace { @@ -67,65 +65,6 @@ TEST(CardDatabaseTest, LoadXml) ASSERT_EQ(NotLoaded, db->getLoadStatus()) << "Incorrect status after clear"; } -TEST(CardDatabaseTest, EmptyLoadedSnapshotIsReadyNotLoading) -{ - NoopCardPreferenceProvider prefs; - TestCardDatabasePathProvider pathProvider; - NoopCardSetPriorityController controller; - CardDatabase db(nullptr, &prefs, &pathProvider, &controller); - - EXPECT_EQ(NotLoaded, db.getLoadStatus()); - - // A newly bootstrapped Continuumatrice profile contains a valid, but empty, - // XML v4 catalogue. Successful swap must not trap the visual browser on - // "Loading database..." when there are zero cards. - CardDatabaseData emptySnapshot; - db.swapInDatabaseData(std::move(emptySnapshot)); - EXPECT_TRUE(db.getCardList().isEmpty()); - EXPECT_EQ(Ok, db.getLoadStatus()); - - db.clear(); - EXPECT_EQ(NotLoaded, db.getLoadStatus()); -} - -TEST(CardInfoTest, ContinuumIdentityAndStatsHaveNativeAndLegacyPropertyAccess) -{ - auto native = CardInfo::newInstance("Native Continuum Card"); - native->setProperty("identity", "The Shadow Queen"); - native->setProperty("attack", "10"); - native->setProperty("armour", "3"); - native->setProperty("health", "12"); - - EXPECT_EQ(native->getIdentity(), "The Shadow Queen"); - EXPECT_EQ(native->getAttack(), "10"); - EXPECT_EQ(native->getArmour(), "3"); - EXPECT_EQ(native->getHealth(), "12"); - EXPECT_EQ(native->getAtkHp(), "10/12"); - EXPECT_EQ(native->getPowTough(), "10/12"); // Legacy accessor remains useful. - - auto legacyMse = CardInfo::newInstance("Legacy Continuum Card"); - legacyMse->setProperty("alias", "The Guardian"); - legacyMse->setProperty("strength", "9"); - legacyMse->setProperty("armor", "4"); - legacyMse->setProperty("health", "8"); - EXPECT_EQ(legacyMse->getIdentity(), "The Guardian"); - EXPECT_EQ(legacyMse->getAttack(), "9"); - EXPECT_EQ(legacyMse->getArmour(), "4"); - EXPECT_EQ(legacyMse->getHealth(), "8"); - EXPECT_EQ(legacyMse->getAtkHp(), "9/8"); - - auto oldPt = CardInfo::newInstance("Old Imported Card"); - oldPt->setProperty(Mtg::PowTough, "2/7"); - EXPECT_EQ(oldPt->getAttack(), "2"); - EXPECT_EQ(oldPt->getHealth(), "7"); - EXPECT_EQ(oldPt->getAtkHp(), "2/7"); - - // Missing values should not be represented as false zeroes. - auto blank = CardInfo::newInstance("No Stats"); - EXPECT_TRUE(blank->getAtkHp().isEmpty()); - EXPECT_TRUE(blank->getArmour().isEmpty()); -} - TEST(CardDatabaseTest, LazyPropertiesHashStreamsBlobAccessors) { QHash props; diff --git a/tests/essence_types_test.cpp b/tests/essence_types_test.cpp deleted file mode 100644 index 7a9cdf3d7..000000000 --- a/tests/essence_types_test.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -TEST(EssenceTypesTest, HasExactlySixteenCanonicalSymbols) -{ - EXPECT_EQ(Essence::symbols(), QStringLiteral("MSOGEAQCJBRLVTPN")); - EXPECT_EQ(Essence::symbols().size(), 16); - EXPECT_TRUE(Essence::isSymbol(QChar('N'))); // N is colorless. - EXPECT_TRUE(Essence::isSymbol(QChar('J'))); // J is Jasper, between C and B. - EXPECT_FALSE(Essence::isSymbol(QChar('W'))); // MTG white is not Essence. - EXPECT_FALSE(Essence::isSymbol(QChar('U'))); // MTG blue is not Essence. -} - -TEST(EssenceTypesTest, NetworkCounterNamesHaveStableRoundTrip) -{ - EXPECT_EQ(Essence::symbols(), EssenceCounter::symbols()); - int position = 1; // Counter id 0 is the existing life counter. - for (const QChar code : EssenceCounter::symbols()) { - const QString networkName = EssenceCounter::name(code); - EXPECT_TRUE(networkName.startsWith(QStringLiteral("essence:"))); - EXPECT_EQ(EssenceCounter::codeFromName(networkName), code); - EXPECT_TRUE(EssenceCounter::isEssenceCounter(networkName)); - ++position; - } - EXPECT_EQ(position, 17); - EXPECT_TRUE(EssenceCounter::name(QChar('W')).isEmpty()); - EXPECT_FALSE(EssenceCounter::isEssenceCounter(QStringLiteral("essence:W"))); - EXPECT_FALSE(EssenceCounter::isEssenceCounter(QStringLiteral("essence:MM"))); - EXPECT_FALSE(EssenceCounter::isEssenceCounter(QStringLiteral("life"))); - EXPECT_EQ(EssenceCounter::clampBalance(-1), 0); - EXPECT_EQ(EssenceCounter::clampBalance(-900), 0); - EXPECT_EQ(EssenceCounter::clampBalance(0), 0); - EXPECT_EQ(EssenceCounter::clampBalance(19), 19); -} - -TEST(EssenceTypesTest, ParsesAllBracedAndUnbracedTypes) -{ - const QString raw = QStringLiteral("{M}{S}{O}{G}{E}{A}{Q}{C}{J}{B}{R}{L}{V}{T}{P}{N}"); - EXPECT_EQ(Essence::parseCost(raw).join(QString()), Essence::symbols()); - EXPECT_EQ(Essence::parseCost(QStringLiteral("MSN")).join(QString()), QStringLiteral("MSN")); - EXPECT_EQ(Essence::parseCost(QStringLiteral("{C}{J}{B}{N}")).join(QString()), QStringLiteral("CJBN")); - EXPECT_EQ(Essence::parseCost(QStringLiteral("{2}{N}{M}")).join(QStringLiteral("|")), - QStringLiteral("2|N|M")); -} - -TEST(EssenceTypesTest, RejectsUnknownLegacyAndHybridCodes) -{ - EXPECT_TRUE(Essence::parseCost(QStringLiteral("{W}{U}{Z}{M/N}")).isEmpty()); - EXPECT_EQ(Essence::parseCost(QStringLiteral("{N}")), QStringList{QStringLiteral("N")}); -} - -TEST(EssenceTypesTest, NormalizesUniqueIdentityInCanonicalOrder) -{ - EXPECT_EQ(Essence::normalizeIdentity(QStringLiteral("NSSMMO")), QStringLiteral("MSON")); - EXPECT_EQ(Essence::normalizeIdentity(QStringLiteral("N")), QStringLiteral("N")); - EXPECT_EQ(Essence::normalizeIdentity(QStringLiteral("BJNJC")), QStringLiteral("CJBN")); - EXPECT_EQ(Essence::normalizeIdentity(QStringLiteral("WU")), QString()); -} - -TEST(EssenceTypesTest, NativePropertiesTakePrecedenceOverLegacyFallbacks) -{ - const QHash props = { - {QStringLiteral("essencecost"), QStringLiteral("{M}{N}")}, - {QStringLiteral("essence"), QStringLiteral("NM")}, - {QStringLiteral("essencevalue"), QStringLiteral("2")}, - {QStringLiteral("manacost"), QStringLiteral("{W}{U}")}, - {QStringLiteral("colors"), QStringLiteral("WU")}, - {QStringLiteral("cmc"), QStringLiteral("9")} - }; - - const CardInfoPtr card = CardInfo::newInstance(QStringLiteral("Essence Test"), - QString(), false, props, {}, {}, {}, {}); - ASSERT_TRUE(card); - EXPECT_EQ(card->getManaCost(), QStringLiteral("{M}{N}")); - EXPECT_EQ(card->getEssenceTypes(), QStringLiteral("MN")); - EXPECT_EQ(card->getColors(), QStringLiteral("MN")); - EXPECT_EQ(card->getCmc(), QStringLiteral("2")); -} diff --git a/tests/model_contract/model_contract_test.cpp b/tests/model_contract/model_contract_test.cpp index d3fced1e4..aefa26f3d 100644 --- a/tests/model_contract/model_contract_test.cpp +++ b/tests/model_contract/model_contract_test.cpp @@ -103,54 +103,6 @@ TEST(ModelContract, CardDatabaseModelAndDisplayProxyKeepContract) EXPECT_LT(display.rowCount(), source.rowCount()); } -TEST(ModelContract, ContinuumCardStatsAreSortableAndLabeled) -{ - NoopCardPreferenceProvider prefs; - TestCardDatabasePathProvider paths; - NoopCardSetPriorityController setPriority; - CardDatabase database(nullptr, &prefs, &paths, &setPriority); - - auto swift = CardInfo::newInstance("Swift"); - swift->setProperty("identity", "Astra"); - swift->setProperty("attack", "9"); - swift->setProperty("armour", "10"); - swift->setProperty("health", "2"); - - auto sturdy = CardInfo::newInstance("Sturdy"); - sturdy->setProperty("identity", "Zephyr"); - sturdy->setProperty("attack", "10"); - sturdy->setProperty("armour", "2"); - sturdy->setProperty("health", "12"); - - database.addCard(swift); - database.addCard(sturdy); - - CardDatabaseModel source(&database, false); - CardDatabaseDisplayModel display; - display.setSourceModel(&source); - - ASSERT_EQ(source.columnCount(), CardDatabaseModel::ColumnCount); - ASSERT_EQ(display.rowCount(), 2); - EXPECT_EQ(source.headerData(CardDatabaseModel::AtkHpColumn, Qt::Horizontal).toString(), "ATK/HP"); - EXPECT_EQ(source.headerData(CardDatabaseModel::IdentityColumn, Qt::Horizontal).toString(), "Identity"); - EXPECT_EQ(source.headerData(CardDatabaseModel::AttackColumn, Qt::Horizontal).toString(), "Attack"); - EXPECT_EQ(source.headerData(CardDatabaseModel::ArmourColumn, Qt::Horizontal).toString(), "Armour"); - EXPECT_EQ(source.headerData(CardDatabaseModel::HealthColumn, Qt::Horizontal).toString(), "Health"); - - const auto firstName = [&display] { return display.index(0, CardDatabaseModel::NameColumn).data().toString(); }; - - display.sort(CardDatabaseModel::AttackColumn, Qt::AscendingOrder); - EXPECT_EQ(firstName(), "Swift") << "9 ATK should precede 10 ATK"; - display.sort(CardDatabaseModel::ArmourColumn, Qt::AscendingOrder); - EXPECT_EQ(firstName(), "Sturdy") << "2 Armour should precede 10 Armour"; - display.sort(CardDatabaseModel::HealthColumn, Qt::AscendingOrder); - EXPECT_EQ(firstName(), "Swift") << "2 HP should precede 12 HP"; - display.sort(CardDatabaseModel::AtkHpColumn, Qt::AscendingOrder); - EXPECT_EQ(firstName(), "Swift") << "ATK/HP should sort attack and health numerically"; - display.sort(CardDatabaseModel::IdentityColumn, Qt::DescendingOrder); - EXPECT_EQ(firstName(), "Sturdy") << "Identity should sort text from the alias/identity property"; -} - TEST(ModelContract, SetsModelKeepsContract) { CardDatabase database(nullptr, new NoopCardPreferenceProvider(), new TestCardDatabasePathProvider(), diff --git a/tools/installer-holder-probe/close_holders.cpp b/tools/installer-holder-probe/close_holders.cpp index 60ef1e878..24fe7f0e7 100644 --- a/tools/installer-holder-probe/close_holders.cpp +++ b/tools/installer-holder-probe/close_holders.cpp @@ -223,7 +223,7 @@ bool waitForExit(const std::vector &processes, unsigned long waitMillise bool isCockatriceImage(const std::wstring &imageName) { - constexpr wchar_t COCKATRICE_IMAGE[] = L"continuumatrice.exe"; + constexpr wchar_t COCKATRICE_IMAGE[] = L"cockatrice.exe"; return CompareStringOrdinal(imageName.c_str(), -1, COCKATRICE_IMAGE, -1, TRUE) == CSTR_EQUAL; } diff --git a/tools/installer-holder-probe/installer_close_name.cpp b/tools/installer-holder-probe/installer_close_name.cpp index b0e3bd345..74659e151 100644 --- a/tools/installer-holder-probe/installer_close_name.cpp +++ b/tools/installer-holder-probe/installer_close_name.cpp @@ -4,7 +4,7 @@ std::wstring installerCloseSocketName(unsigned long processId, const std::wstring &userName) { - std::wstring name = L"ContinuumatriceInstallerClose-"; + std::wstring name = L"CockatriceInstallerClose-"; name += std::to_wstring(processId); name += L"-"; name += userName; diff --git a/tools/installer-holder-probe/payload_paths.h b/tools/installer-holder-probe/payload_paths.h index 0571035a4..ea63732f5 100644 --- a/tools/installer-holder-probe/payload_paths.h +++ b/tools/installer-holder-probe/payload_paths.h @@ -13,7 +13,7 @@ * running, which is what makes this list the evidence the two process lookups cannot * provide - see payloadExecutablesInUse(). */ -inline constexpr const wchar_t *PAYLOAD_PROGRAMS[] = {L"continuumatrice.exe", L"continuumatrice-legacy-oracle.exe", L"continuum-servatrice.exe"}; +inline constexpr const wchar_t *PAYLOAD_PROGRAMS[] = {L"cockatrice.exe", L"oracle.exe", L"servatrice.exe"}; /** * @brief The last name in @p path: what follows its last separator, or @p path itself when