From 4e2229be98357f81ddbcf1f1e1928272a2a2bcba Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:30:30 -0700 Subject: [PATCH 01/29] Orient Front and Command labels toward the opponent --- cockatrice/src/game_graphics/zones/table_zone.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cockatrice/src/game_graphics/zones/table_zone.cpp b/cockatrice/src/game_graphics/zones/table_zone.cpp index c857a5a67..f643b3fd5 100644 --- a/cockatrice/src/game_graphics/zones/table_zone.cpp +++ b/cockatrice/src/game_graphics/zones/table_zone.cpp @@ -206,23 +206,23 @@ void TableZone::paintBattlefieldRows(QPainter *painter) painter->setFont(font); for (int visualRow = 0; visualRow < TableZoneLogic::ROW_COUNT; ++visualRow) { - // Table logical grid Y: 0 = Command, 1 = Support, 2 = Front. + // Logical grid Y: 0 = Front, 1 = Support, 2 = Command. const int row = inverted ? TableZoneLogic::ROW_COUNT - 1 - visualRow : visualRow; QString label; QColor color; switch (row) { case 0: - label = tr("COMMAND ZONE"); - color = commandZoneColor; + label = tr("FRONT ZONE"); + color = frontZoneColor; break; case 1: label = tr("SUPPORT ZONE"); color = supportZoneColor; break; default: - label = tr("FRONT ZONE"); - color = frontZoneColor; + label = tr("COMMAND ZONE"); + color = commandZoneColor; break; } From d11db291e1db81d0fec5bdfa840137dbf5166ddc Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:30:32 -0700 Subject: [PATCH 02/29] Align tablerow placement with Front Support Command order --- .../libcockatrice/game/zones/table_zone_logic.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libcockatrice_game/libcockatrice/game/zones/table_zone_logic.cpp b/libcockatrice_game/libcockatrice/game/zones/table_zone_logic.cpp index 834794268..f1a896ed2 100644 --- a/libcockatrice_game/libcockatrice/game/zones/table_zone_logic.cpp +++ b/libcockatrice_game/libcockatrice/game/zones/table_zone_logic.cpp @@ -52,5 +52,6 @@ int TableZoneLogic::tableRowToGridY(int tableRow) if (tableRow > 2) { tableRow = 1; } - return clampValidTableRow(2 - tableRow); + // Continuumatrice rows are ordered Front (0), Support (1), Command (2). + return clampValidTableRow(tableRow); } From 4a201aa5b660277a48f9d5a310976ae445155d63 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:30:34 -0700 Subject: [PATCH 03/29] Document Continuumatrice table row indices --- .../libcockatrice/game/zones/table_zone_logic.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libcockatrice_game/libcockatrice/game/zones/table_zone_logic.h b/libcockatrice_game/libcockatrice/game/zones/table_zone_logic.h index c828d8b91..c60a49be4 100644 --- a/libcockatrice_game/libcockatrice/game/zones/table_zone_logic.h +++ b/libcockatrice_game/libcockatrice/game/zones/table_zone_logic.h @@ -29,7 +29,7 @@ public: bool _contentsKnown, QObject *parent = nullptr); - /** @brief Number of rows in the table zone grid (0=creatures, 1=noncreatures, 2=lands). */ + /** @brief Number of rows in the table zone grid (0=Front, 1=Support, 2=Command). */ static const int ROW_COUNT = 3; /** @@ -38,9 +38,9 @@ public: static int clampValidTableRow(const int row); /** - * Converts a card's logical table row (0=creatures, 1=noncreatures, 2=lands) - * to the corresponding grid Y coordinate. Cards with tableRow > 2 (e.g., - * instants/sorceries) default to the noncreatures row. + * Converts a card's logical table row (0=Front, 1=Support, 2=Command) + * to the corresponding grid Y coordinate. Rows greater than 2 + * fall back to the middle (Support) row. */ static int tableRowToGridY(int tableRow); From 8bc8317c57811fa4a6c998e4e8e231ecee683012 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:30:36 -0700 Subject: [PATCH 04/29] Clarify visual battlefield orientation and card metadata --- doc/continuumatrice-battlefield.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/continuumatrice-battlefield.md b/doc/continuumatrice-battlefield.md index 130dc1f2d..9b4fc5b37 100644 --- a/doc/continuumatrice-battlefield.md +++ b/doc/continuumatrice-battlefield.md @@ -19,7 +19,7 @@ Support = #A0286981 Command = #A05E428C ``` -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 row order follows the player's mirrored/inverted layout, so Front always faces the battlefield center. +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 @@ -29,4 +29,4 @@ The font is also detected if already installed as "Continuumorphous Regular" or ## Compatibility -The existing drag/drop, row mapping, stacking, card counters, playmats, and multiplayer protocols are preserved. This does **not** enforce gameplay placement restrictions. +The existing drag/drop, stacking, card counters, playmats, and multiplayer protocols are preserved. The automatic `tablerow` placement is explicitly mapped `0 -> Front`, `1 -> Support`, `2 -> Command` (not Cockatrice's reversed Magic-oriented mapping). This does **not** enforce gameplay placement restrictions. From 2dab3d0f40cba80e509e9951a5639d67da286a54 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:41:03 -0700 Subject: [PATCH 05/29] Give Continuumatrice its own Qt profile and data/cache namespace --- cockatrice/src/main.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index 829a8ac42..43b5f2e83 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -238,11 +238,13 @@ int main(int argc, char *argv[]) app.addLibraryPath(app.applicationDirPath() + "/plugins"); #endif - // These values are only used by the settings loader/saver - // Wrong or outdated values are kept to not break things - QCoreApplication::setOrganizationName("Cockatrice"); - QCoreApplication::setOrganizationDomain("cockatrice.github.io"); - QCoreApplication::setApplicationName("Cockatrice"); + // Continuumatrice must never use Cockatrice's profile. These identifiers are + // set before SettingsCache is initialized, so Qt's AppLocalDataLocation, + // CacheLocation and legacy QSettings migration use independent paths. + // Keep the application name stable: changing it again would move profiles. + QCoreApplication::setOrganizationName("Continuum Universes Studio"); + QCoreApplication::setOrganizationDomain("continuum-universes.com"); + QCoreApplication::setApplicationName("Continuumatrice"); QCoreApplication::setApplicationVersion(VERSION_STRING); #ifdef Q_OS_MAC @@ -336,7 +338,7 @@ int main(int argc, char *argv[]) // Command-line parser QCommandLineParser parser; - parser.setApplicationDescription("Cockatrice Client"); + parser.setApplicationDescription("Continuumatrice Client"); parser.addHelpOption(); parser.addVersionOption(); From f54dcc3e54bce8bf473e272643a9b6cbc01d5ab5 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:41:05 -0700 Subject: [PATCH 06/29] Isolate Continuumatrice single-instance socket from Cockatrice --- cockatrice/src/single_instance_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/single_instance_manager.cpp b/cockatrice/src/single_instance_manager.cpp index 08294d72c..693ce96da 100644 --- a/cockatrice/src/single_instance_manager.cpp +++ b/cockatrice/src/single_instance_manager.cpp @@ -27,7 +27,7 @@ bool SingleInstanceManager::tryRun(const QStringList &filesToSend) { // Scope the socket name to the current user so one user's instance cannot // hijack another user's session. - serverName = scopedLocalServerName(QStringLiteral("CockatriceSingleInstance")); + serverName = scopedLocalServerName(QStringLiteral("ContinuumatriceSingleInstance")); // Hand off to an already-running primary instance if one exists. Never steal // the socket of a busy primary: it is alive and will act on the payload. From 826da7ede2804a0c8b1bad70aa888b8be9d52df5 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:41:08 -0700 Subject: [PATCH 07/29] Document isolated profile and XML4 card/art workflow --- doc/continuumatrice-card-data.md | 105 +++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 doc/continuumatrice-card-data.md diff --git a/doc/continuumatrice-card-data.md b/doc/continuumatrice-card-data.md new file mode 100644 index 000000000..8e3f9a12f --- /dev/null +++ b/doc/continuumatrice-card-data.md @@ -0,0 +1,105 @@ +# Continuumatrice: isolated profile and card database + +Continuumatrice intentionally uses its own Qt application identity: +- Application: `Continuumatrice` +- Organization: `Continuum Universes Studio` +- Organization domain: `continuum-universes.com` + +The identity is assigned in `cockatrice/src/main.cpp` **before** `SettingsCache` runs. +The single-instance local socket is also renamed so a running Cockatrice client does not intercept Continuumatrice launches. + +## Default Linux paths + +For the usual openSUSE / XDG environment (`XDG_DATA_HOME` and `XDG_CACHE_HOME` unset): + +| Purpose | Default | +| --- | --- | +| Application data | `~/.local/share/Continuumatrice/` | +| Preferences | `~/.local/share/Continuumatrice/settings/` | +| Primary database | `~/.local/share/Continuumatrice/cards.xml` | +| Additional custom XML databases | `~/.local/share/Continuumatrice/customsets/` | +| Decks | `~/.local/share/Continuumatrice/decks/` | +| Downloaded/local images | `~/.local/share/Continuumatrice/pics/` | +| Local custom images | `~/.local/share/Continuumatrice/pics/CUSTOM/` | +| User themes | `~/.local/share/Continuumatrice/themes/` | +| Cache | `~/.cache/Continuumatrice/` | + +Qt follows the XDG environment variables if set; the paths above are defaults, not mandatory constants. A `portable.dat` next to the binary activates Cockatrice's existing portable mode and stores data/cache next to the executable instead. No old Cockatrice data is automatically copied or deleted. + +**Note:** The client's executable and file-association/URL scheme are still called Cockatrice. This patch isolates profiles, not every branding or installer integration. Avoid running the upstream Oracle Magic-card downloader against this profile until you intentionally configure it; that is a separate task. + +## Card database format + +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 + Continuumatrice Test Cards + + + + + Example Front Card + Test card for checking battlefield placement. + + Unit + + CTEST + 0 + + + Example Support Card + + Unit + + CTEST + 1 + + + Example Command Card + + Unit + + CTEST + 2 + + + +``` + +These names are examples, **not** Continuum Universes lore or playable canon. + +For a real card catalogue, generate XML from the card design/export pipeline and store it under `cards.xml` (main database), or under `customsets/*.xml` (additional sets). **A readable main `cards.xml` is needed for the loader to proceed to the optional custom sets**, so do not use custom sets exclusively without one. The loader collects custom-set files recursively and loads them in alphabetical order. + +The XML `` maps to: +- `0` = Front +- `1` = Support +- `2` = Command + +By default, cards are manually positioned and moved: this client does not evaluate card rules. + +## Images + +Card images are separate from card metadata: +- Put a local image in `pics/CUSTOM/` with the **same corrected card name** as its XML name (such as `Example Front Card.png`). Custom pictures are indexed recursively. Or +- Provide a `picurl` URL attribute on the printing's `` tag and let the client download it. + +For reliable matching across reprints, the importer also recognizes names incorporating the set code, collector number and printing/provider ID. The existing loader supports various filename schemes and file extensions. + +## Loading and testing + +1. Update and rebuild the fork. +2. Launch Continuumatrice and verify its `Continuumatrice` folder is created. +3. Quit the client before writing a new `cards.xml` or image files. +4. Place your own XML and images in the new profile. +5. Relaunch or reload the card database in the client. +6. Ensure your set is enabled; test dragging cards and checking Front/Support/Command placement. +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. From 5ccfbf7a23d5af1fc9d1b327836646ef72eb65f6 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:51:48 -0700 Subject: [PATCH 08/29] Continuumatrice: add Limbo Refuge Oblivion support to zone_names.h --- libcockatrice_utility/libcockatrice/utility/zone_names.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libcockatrice_utility/libcockatrice/utility/zone_names.h b/libcockatrice_utility/libcockatrice/utility/zone_names.h index d1463de6a..8adc427d7 100644 --- a/libcockatrice_utility/libcockatrice/utility/zone_names.h +++ b/libcockatrice_utility/libcockatrice/utility/zone_names.h @@ -9,6 +9,9 @@ namespace ZoneNames constexpr const char *TABLE = "table"; constexpr const char *GRAVE = "grave"; constexpr const char *EXILE = "rfg"; // "removed from game" +constexpr const char *LIMBO = "limbo"; +constexpr const char *REFUGE = "refuge"; +constexpr const char *OBLIVION = "oblivion"; constexpr const char *HAND = "hand"; constexpr const char *DECK = "deck"; constexpr const char *SIDEBOARD = "sb"; From 034b7189e3154c2b86fd99a004a99261ee09260e Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:51:50 -0700 Subject: [PATCH 09/29] Continuumatrice: add Limbo Refuge Oblivion support to player_logic.cpp --- libcockatrice_game/libcockatrice/game/player/player_logic.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libcockatrice_game/libcockatrice/game/player/player_logic.cpp b/libcockatrice_game/libcockatrice/game/player/player_logic.cpp index 96bd07164..4d00a2843 100644 --- a/libcockatrice_game/libcockatrice/game/player/player_logic.cpp +++ b/libcockatrice_game/libcockatrice/game/player/player_logic.cpp @@ -54,6 +54,9 @@ void PlayerLogic::initializeZones() addZone(new PileZoneLogic(this, ZoneNames::DECK, false, true, false, this)); addZone(new PileZoneLogic(this, ZoneNames::GRAVE, false, false, true, this)); addZone(new PileZoneLogic(this, ZoneNames::EXILE, false, false, true, this)); + addZone(new PileZoneLogic(this, ZoneNames::LIMBO, false, false, true, this)); + addZone(new PileZoneLogic(this, ZoneNames::REFUGE, false, false, true, this)); + addZone(new PileZoneLogic(this, ZoneNames::OBLIVION, false, false, true, this)); addZone(new PileZoneLogic(this, ZoneNames::SIDEBOARD, false, false, false, this)); addZone(new TableZoneLogic(this, ZoneNames::TABLE, true, false, true, this)); addZone(new StackZoneLogic(this, ZoneNames::STACK, true, false, true, this)); @@ -111,6 +114,7 @@ void PlayerLogic::processPlayerInfo(const ServerInfo_Player &info) { static QSet builtinZones{/* PileZones */ ZoneNames::DECK, ZoneNames::GRAVE, ZoneNames::EXILE, ZoneNames::SIDEBOARD, + ZoneNames::LIMBO, ZoneNames::REFUGE, ZoneNames::OBLIVION, /* TableZone */ ZoneNames::TABLE, /* StackZone */ From 61faf612a1b904ac1081efcc8ec6272e6bf32c37 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:52:02 -0700 Subject: [PATCH 10/29] Continuumatrice: add Limbo Refuge Oblivion support to player_logic.h --- .../libcockatrice/game/player/player_logic.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libcockatrice_game/libcockatrice/game/player/player_logic.h b/libcockatrice_game/libcockatrice/game/player/player_logic.h index d6c4f5187..758bae237 100644 --- a/libcockatrice_game/libcockatrice/game/player/player_logic.h +++ b/libcockatrice_game/libcockatrice/game/player/player_logic.h @@ -178,6 +178,21 @@ public: return qobject_cast(zones.value(ZoneNames::EXILE)); } + PileZoneLogic *getLimboZone() + { + return qobject_cast(zones.value(ZoneNames::LIMBO)); + } + + PileZoneLogic *getRefugeZone() + { + return qobject_cast(zones.value(ZoneNames::REFUGE)); + } + + PileZoneLogic *getOblivionZone() + { + return qobject_cast(zones.value(ZoneNames::OBLIVION)); + } + PileZoneLogic *getSideboardZone() { return qobject_cast(zones.value(ZoneNames::SIDEBOARD)); From 2d467955fc5e9e59e895ae9f14324a183638e78f Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:52:05 -0700 Subject: [PATCH 11/29] Continuumatrice: add Limbo Refuge Oblivion support to server_player.cpp --- .../libcockatrice/network/server/remote/game/server_player.cpp | 3 +++ 1 file changed, 3 insertions(+) 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 949d04b4b..63614b9fd 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp @@ -105,6 +105,9 @@ void Server_Player::setupZones() addZone(new Server_CardZone(this, ZoneNames::STACK, false, ServerInfo_Zone::PublicZone)); addZone(new Server_CardZone(this, ZoneNames::GRAVE, false, ServerInfo_Zone::PublicZone)); addZone(new Server_CardZone(this, ZoneNames::EXILE, false, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, ZoneNames::LIMBO, false, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, ZoneNames::REFUGE, false, ServerInfo_Zone::PublicZone)); + addZone(new Server_CardZone(this, ZoneNames::OBLIVION, false, ServerInfo_Zone::PublicZone)); addCounter(new Server_Counter(0, "life", makeColor(255, 255, 255), 25, game->getStartingLifeTotal())); addCounter(new Server_Counter(1, "w", makeColor(255, 255, 150), 20, 0)); From e23b5eb27ea9cfc25b730589cc7570dec9e61d13 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:52:07 -0700 Subject: [PATCH 12/29] Continuumatrice: add Limbo Refuge Oblivion support to card_menu_action_type.h --- .../libcockatrice/game/player/card_menu_action_type.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libcockatrice_game/libcockatrice/game/player/card_menu_action_type.h b/libcockatrice_game/libcockatrice/game/player/card_menu_action_type.h index e5c4cd84e..24e2dabcb 100644 --- a/libcockatrice_game/libcockatrice/game/player/card_menu_action_type.h +++ b/libcockatrice_game/libcockatrice/game/player/card_menu_action_type.h @@ -22,7 +22,11 @@ enum CardMenuActionType cmMoveToHand, cmMoveToGraveyard, cmMoveToExile, - cmMoveToTable + cmMoveToTable, + // Continuumatrice piles: append to preserve existing action values. + cmMoveToLimbo, + cmMoveToRefuge, + cmMoveToOblivion }; #endif // COCKATRICE_CARD_MENU_ACTION_TYPE_H From 1670137064536e2214d472be3a9870713aab1b34 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:52:21 -0700 Subject: [PATCH 13/29] Continuumatrice: add Limbo Refuge Oblivion support to player_actions.cpp --- .../game/player/player_actions.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libcockatrice_game/libcockatrice/game/player/player_actions.cpp b/libcockatrice_game/libcockatrice/game/player/player_actions.cpp index 90823fec6..966b145af 100644 --- a/libcockatrice_game/libcockatrice/game/player/player_actions.cpp +++ b/libcockatrice_game/libcockatrice/game/player/player_actions.cpp @@ -1974,6 +1974,23 @@ void PlayerActions::cardMenuAction(QList selectedCards, CardMenuAct commandList.append(cmd); break; } + case cmMoveToLimbo: + case cmMoveToRefuge: + case cmMoveToOblivion: { + auto *cmd = new Command_MoveCard; + cmd->set_start_player_id(startPlayerId); + cmd->set_start_zone(startZone.toStdString()); + cmd->mutable_cards_to_move()->CopyFrom(idList); + cmd->set_target_player_id(player->getPlayerInfo()->getId()); + const char *targetZone = type == cmMoveToLimbo ? ZoneNames::LIMBO + : type == cmMoveToRefuge ? ZoneNames::REFUGE + : ZoneNames::OBLIVION; + cmd->set_target_zone(targetZone); + cmd->set_x(0); + cmd->set_y(0); + commandList.append(cmd); + break; + } case cmMoveToTable: { // Each card needs its own command because table row, pt, and cipt vary per card for (const auto &card : cardList) { From 384a3dc0369606c030cd406a929368055bc3ed71 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:52:24 -0700 Subject: [PATCH 14/29] Continuumatrice: add Limbo Refuge Oblivion support to move_menu.h --- cockatrice/src/game_graphics/player/menu/move_menu.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cockatrice/src/game_graphics/player/menu/move_menu.h b/cockatrice/src/game_graphics/player/menu/move_menu.h index f1f239abd..b2aee973e 100644 --- a/cockatrice/src/game_graphics/player/menu/move_menu.h +++ b/cockatrice/src/game_graphics/player/menu/move_menu.h @@ -29,6 +29,9 @@ public: QAction *aMoveToTable = nullptr; QAction *aMoveToGraveyard = nullptr; QAction *aMoveToExile = nullptr; + QAction *aMoveToLimbo = nullptr; + QAction *aMoveToRefuge = nullptr; + QAction *aMoveToOblivion = nullptr; }; #endif // COCKATRICE_MOVE_MENU_H From abf975033332ede54ff7be1f378cb0c6e45659ee Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:52:26 -0700 Subject: [PATCH 15/29] Continuumatrice: add Limbo Refuge Oblivion support to move_menu.cpp --- .../src/game_graphics/player/menu/move_menu.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cockatrice/src/game_graphics/player/menu/move_menu.cpp b/cockatrice/src/game_graphics/player/menu/move_menu.cpp index b81c4619b..81ede71a4 100644 --- a/cockatrice/src/game_graphics/player/menu/move_menu.cpp +++ b/cockatrice/src/game_graphics/player/menu/move_menu.cpp @@ -25,6 +25,12 @@ MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to")) aMoveToGraveyard->setData(cmMoveToGraveyard); aMoveToExile = new QAction(this); aMoveToExile->setData(cmMoveToExile); + aMoveToLimbo = new QAction(this); + aMoveToLimbo->setData(cmMoveToLimbo); + aMoveToRefuge = new QAction(this); + aMoveToRefuge->setData(cmMoveToRefuge); + aMoveToOblivion = new QAction(this); + aMoveToOblivion->setData(cmMoveToOblivion); auto *actions = player->getLogic()->getPlayerActions(); @@ -42,6 +48,9 @@ MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to")) connect(aMoveToHand, &QAction::triggered, actions, invoke(cmMoveToHand)); connect(aMoveToGraveyard, &QAction::triggered, actions, invoke(cmMoveToGraveyard)); connect(aMoveToExile, &QAction::triggered, actions, invoke(cmMoveToExile)); + connect(aMoveToLimbo, &QAction::triggered, actions, invoke(cmMoveToLimbo)); + connect(aMoveToRefuge, &QAction::triggered, actions, invoke(cmMoveToRefuge)); + connect(aMoveToOblivion, &QAction::triggered, actions, invoke(cmMoveToOblivion)); addAction(aMoveToTopLibrary); addAction(aMoveToXfromTopOfLibrary); @@ -54,6 +63,10 @@ MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to")) addAction(aMoveToGraveyard); addSeparator(); addAction(aMoveToExile); + addSeparator(); + addAction(aMoveToLimbo); + addAction(aMoveToRefuge); + addAction(aMoveToOblivion); setShortcutsActive(); @@ -81,4 +94,7 @@ void MoveMenu::retranslateUi() aMoveToHand->setText(tr("&Hand")); aMoveToGraveyard->setText(tr("&Graveyard")); aMoveToExile->setText(tr("&Exile")); + aMoveToLimbo->setText(tr("Limbo")); + aMoveToRefuge->setText(tr("Refuge")); + aMoveToOblivion->setText(tr("Oblivion")); } From f87b4eeeac3a0418afca281afa94ce4254746197 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:52:40 -0700 Subject: [PATCH 16/29] Continuumatrice: add Limbo Refuge Oblivion support to card_menu.cpp --- cockatrice/src/game_graphics/player/menu/card_menu.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/game_graphics/player/menu/card_menu.cpp b/cockatrice/src/game_graphics/player/menu/card_menu.cpp index 9f00c0cba..f00a53520 100644 --- a/cockatrice/src/game_graphics/player/menu/card_menu.cpp +++ b/cockatrice/src/game_graphics/player/menu/card_menu.cpp @@ -178,7 +178,10 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, CardItem *_card, bool _shortcuts } else if (card->getZone()->getName() == ZoneNames::STACK) { createStackMenu(writeableCard); } else if (card->getZone()->getName() == ZoneNames::EXILE || - card->getZone()->getName() == ZoneNames::GRAVE) { + card->getZone()->getName() == ZoneNames::GRAVE || + card->getZone()->getName() == ZoneNames::LIMBO || + card->getZone()->getName() == ZoneNames::REFUGE || + card->getZone()->getName() == ZoneNames::OBLIVION) { createGraveyardOrExileMenu(writeableCard); } else { createHandOrCustomZoneMenu(writeableCard); From d6070c5c962fdf97f6edc5b435d33846aeb11fac Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:55:24 -0700 Subject: [PATCH 17/29] Continuumatrice: render interactive public piles with themed icons --- cockatrice/src/game_graphics/zones/pile_zone.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/game_graphics/zones/pile_zone.h b/cockatrice/src/game_graphics/zones/pile_zone.h index fa510ad52..777fcd071 100644 --- a/cockatrice/src/game_graphics/zones/pile_zone.h +++ b/cockatrice/src/game_graphics/zones/pile_zone.h @@ -9,6 +9,8 @@ #include "card_zone.h" +#include +#include #include class PileZoneLogic; @@ -21,6 +23,11 @@ class QGraphicsItem; class PileZone : public CardZone { Q_OBJECT +private: + QString customDisplayName; + QString customIconAsset; + QPixmap customIconPixmap; + void reloadCustomIcon(); private slots: void callUpdate() { @@ -28,7 +35,8 @@ private slots: } public: - PileZone(PileZoneLogic *_logic, QGraphicsItem *parent); + PileZone(PileZoneLogic *_logic, QGraphicsItem *parent, + const QString &displayName = {}, const QString &iconAsset = {}); [[nodiscard]] QRectF boundingRect() const override; [[nodiscard]] QPainterPath shape() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; From 29dd239cd2ae744ddf1c9c8aecb6f4df00dda45c Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:56:27 -0700 Subject: [PATCH 18/29] Continuumatrice: render interactive public piles with themed icons --- .../src/game_graphics/zones/pile_zone.cpp | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/game_graphics/zones/pile_zone.cpp b/cockatrice/src/game_graphics/zones/pile_zone.cpp index 08af241b6..3b1589be6 100644 --- a/cockatrice/src/game_graphics/zones/pile_zone.cpp +++ b/cockatrice/src/game_graphics/zones/pile_zone.cpp @@ -1,11 +1,16 @@ #include "pile_zone.h" #include "../../client/settings/cache_settings.h" +#include "../../interface/theme_manager.h" #include "../board/card_drag_item.h" #include "../board/card_item.h" #include "../card_dimensions.h" #include +#include +#include +#include +#include #include #include #include @@ -28,12 +33,19 @@ #include #include -PileZone::PileZone(PileZoneLogic *_logic, QGraphicsItem *parent) : CardZone(_logic, parent) +PileZone::PileZone(PileZoneLogic *_logic, QGraphicsItem *parent, + const QString &displayName, const QString &iconAsset) + : CardZone(_logic, parent), customDisplayName(displayName), customIconAsset(iconAsset) { setCacheMode(DeviceCoordinateCache); // Do not move this line to the parent constructor! setAcceptHoverEvents(true); setCursor(Qt::OpenHandCursor); + if (!customIconAsset.isEmpty()) { + connect(themeManager, &ThemeManager::themeChanged, this, &PileZone::reloadCustomIcon); + reloadCustomIcon(); + } + setTransform(QTransform() .translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F) .rotate(90) @@ -48,6 +60,18 @@ PileZone::PileZone(PileZoneLogic *_logic, QGraphicsItem *parent) : CardZone(_log }); } +void PileZone::reloadCustomIcon() +{ + // Try the active theme's piles/ asset first, then the bundled SVG. + const QString path = QStringLiteral("piles/") + customIconAsset; + customIconPixmap = QPixmap(QStringLiteral("theme:") + themeManager->assetPath(path)); + if (customIconPixmap.isNull()) { + customIconPixmap = + QPixmap(QStringLiteral(":/resources/piles/") + customIconAsset + QStringLiteral(".svg")); + } + update(); +} + QRectF PileZone::boundingRect() const { return QRectF(0, 0, CardDimensions::WIDTH_F, CardDimensions::HEIGHT_F); @@ -74,6 +98,28 @@ void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio painter->rotate(-90); painter->translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F); paintNumberEllipse(cardItems().size(), 28, Qt::white, -1, -1, painter); + + if (!customDisplayName.isEmpty()) { + painter->save(); + // Undo the pile's 90-degree scene rotation to make the badge readable. + painter->translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F); + painter->rotate(-90); + painter->translate(-CardDimensions::HEIGHT_HALF_F, -CardDimensions::WIDTH_HALF_F); + + const qreal labelWidth = CardDimensions::HEIGHT_F; + painter->fillRect(QRectF(2, 2, labelWidth - 4, 29), QColor(16, 18, 28, 220)); + if (!customIconPixmap.isNull()) { + painter->drawPixmap(QRect(7, 6, 21, 21), customIconPixmap); + } + QFont font = painter->font(); + font.setPixelSize(12); + font.setBold(true); + painter->setFont(font); + painter->setPen(Qt::white); + painter->drawText(QRectF(31, 3, labelWidth - 34, 26), Qt::AlignLeft | Qt::AlignVCenter, + customDisplayName.toUpper()); + painter->restore(); + } } void PileZone::handleDropEvent(const QList &dragItems, CardZoneLogic *startZone, const QPoint &) From f9f0901f09058c08377f732662f38635211bf37a Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:56:29 -0700 Subject: [PATCH 19/29] Continuumatrice: render interactive public piles with themed icons --- .../game_graphics/player/player_graphics_item.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cockatrice/src/game_graphics/player/player_graphics_item.h b/cockatrice/src/game_graphics/player/player_graphics_item.h index e1ca0a036..237fccb55 100644 --- a/cockatrice/src/game_graphics/player/player_graphics_item.h +++ b/cockatrice/src/game_graphics/player/player_graphics_item.h @@ -114,6 +114,18 @@ public: { return rfgZoneGraphicsItem; } + [[nodiscard]] PileZone *getLimboZoneGraphicsItem() const + { + return limboZoneGraphicsItem; + } + [[nodiscard]] PileZone *getRefugeZoneGraphicsItem() const + { + return refugeZoneGraphicsItem; + } + [[nodiscard]] PileZone *getOblivionZoneGraphicsItem() const + { + return oblivionZoneGraphicsItem; + } [[nodiscard]] TableZone *getTableZoneGraphicsItem() const { return tableZoneGraphicsItem; @@ -154,6 +166,9 @@ private: PileZone *sideboardGraphicsItem; PileZone *graveyardZoneGraphicsItem; PileZone *rfgZoneGraphicsItem; + PileZone *limboZoneGraphicsItem; + PileZone *refugeZoneGraphicsItem; + PileZone *oblivionZoneGraphicsItem; TableZone *tableZoneGraphicsItem; StackZone *stackZoneGraphicsItem; HandZone *handZoneGraphicsItem; From 2afbd86ad1208002decf1db75ebd8ac1a1f9567e Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:56:32 -0700 Subject: [PATCH 20/29] Continuumatrice: render interactive public piles with themed icons --- .../player/player_graphics_item.cpp | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/cockatrice/src/game_graphics/player/player_graphics_item.cpp b/cockatrice/src/game_graphics/player/player_graphics_item.cpp index 0805dbb5b..57b2b6ce2 100644 --- a/cockatrice/src/game_graphics/player/player_graphics_item.cpp +++ b/cockatrice/src/game_graphics/player/player_graphics_item.cpp @@ -49,6 +49,11 @@ #include #include +namespace +{ +constexpr qreal EXTRA_PILE_COLUMN_WIDTH = CardDimensions::HEIGHT_F + 24; +} // namespace + PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) { connect(&SettingsCache::instance().userInterface(), &InterfaceSettings::horizontalHandChanged, this, @@ -152,6 +157,16 @@ void PlayerGraphicsItem::initializeZones() rfgZoneGraphicsItem = new PileZone(player->getRfgZone(), this); rfgZoneGraphicsItem->setPos(base + QPointF(0, 2 * h + h2 + 10)); + // Three public piles in a second column, beside the legacy piles. + const qreal extraPileX = CardDimensions::HEIGHT_F + counterAreaWidth + 15 + 8; + limboZoneGraphicsItem = new PileZone(player->getLimboZone(), this, tr("Limbo"), QStringLiteral("limbo")); + limboZoneGraphicsItem->setPos(QPointF(extraPileX, base.y())); + refugeZoneGraphicsItem = new PileZone(player->getRefugeZone(), this, tr("Refuge"), QStringLiteral("refuge")); + refugeZoneGraphicsItem->setPos(QPointF(extraPileX, base.y() + h + h2 + 10)); + oblivionZoneGraphicsItem = new PileZone(player->getOblivionZone(), this, tr("Oblivion"), + QStringLiteral("oblivion")); + oblivionZoneGraphicsItem->setPos(QPointF(extraPileX, base.y() + 2 * h + h2 + 10)); + tableZoneGraphicsItem = new TableZone(player->getTableZone(), mirrored, this); connect(this, &PlayerGraphicsItem::mirroredChanged, tableZoneGraphicsItem, &TableZone::setMirrored); @@ -169,6 +184,9 @@ void PlayerGraphicsItem::initializeZones() zoneGraphicsItems.insert(player->getDeckZone()->getName(), deckZoneGraphicsItem); zoneGraphicsItems.insert(player->getGraveZone()->getName(), graveyardZoneGraphicsItem); zoneGraphicsItems.insert(player->getRfgZone()->getName(), rfgZoneGraphicsItem); + zoneGraphicsItems.insert(player->getLimboZone()->getName(), limboZoneGraphicsItem); + zoneGraphicsItems.insert(player->getRefugeZone()->getName(), refugeZoneGraphicsItem); + zoneGraphicsItems.insert(player->getOblivionZone()->getName(), oblivionZoneGraphicsItem); zoneGraphicsItems.insert(player->getSideboardZone()->getName(), sideboardGraphicsItem); zoneGraphicsItems.insert(player->getTableZone()->getName(), tableZoneGraphicsItem); zoneGraphicsItems.insert(player->getStackZone()->getName(), stackZoneGraphicsItem); @@ -203,7 +221,7 @@ QRectF PlayerGraphicsItem::boundingRect() const qreal PlayerGraphicsItem::getMinimumWidth() const { - qreal result = tableZoneGraphicsItem->getMinimumWidth() + CardDimensions::HEIGHT_F + 15 + counterAreaWidth + + qreal result = tableZoneGraphicsItem->getMinimumWidth() + CardDimensions::HEIGHT_F + 15 + counterAreaWidth + EXTRA_PILE_COLUMN_WIDTH + stackZoneGraphicsItem->boundingRect().width(); if (!SettingsCache::instance().userInterface().getHorizontalHand()) { result += handZoneGraphicsItem->boundingRect().width(); @@ -345,7 +363,7 @@ void PlayerGraphicsItem::rearrangeCounters() void PlayerGraphicsItem::rearrangeZones() { - auto base = QPointF(CardDimensions::HEIGHT_F + counterAreaWidth + 15, 0); + auto base = QPointF(CardDimensions::HEIGHT_F + counterAreaWidth + 15 + EXTRA_PILE_COLUMN_WIDTH, 0); if (SettingsCache::instance().userInterface().getHorizontalHand()) { if (mirrored) { if (player->getHandZone()->contentsKnown()) { @@ -396,7 +414,7 @@ void PlayerGraphicsItem::rearrangeZones() void PlayerGraphicsItem::updateBoundingRect() { prepareGeometryChange(); - qreal width = CardDimensions::HEIGHT_F + 15 + counterAreaWidth + stackZoneGraphicsItem->boundingRect().width(); + qreal width = CardDimensions::HEIGHT_F + 15 + counterAreaWidth + EXTRA_PILE_COLUMN_WIDTH + stackZoneGraphicsItem->boundingRect().width(); if (SettingsCache::instance().userInterface().getHorizontalHand()) { qreal handHeight = handVisible ? handZoneGraphicsItem->boundingRect().height() : 0; bRect = QRectF(0, 0, width + tableZoneGraphicsItem->boundingRect().width(), From f964b6b24f3d18b484cc2db755396895170c94f3 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:56:35 -0700 Subject: [PATCH 21/29] Continuumatrice: render interactive public piles with themed icons --- .../game_graphics/player/menu/player_menu.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cockatrice/src/game_graphics/player/menu/player_menu.cpp b/cockatrice/src/game_graphics/player/menu/player_menu.cpp index 582586ac2..0ecd6f21b 100644 --- a/cockatrice/src/game_graphics/player/menu/player_menu.cpp +++ b/cockatrice/src/game_graphics/player/menu/player_menu.cpp @@ -20,12 +20,14 @@ #include "tally_menu.h" #include "utility_menu.h" +#include #include #include #include #include #include #include +#include PlayerMenu::PlayerMenu(PlayerGraphicsItem *_player) : QObject(_player), player(_player) { @@ -45,6 +47,19 @@ PlayerMenu::PlayerMenu(PlayerGraphicsItem *_player) : QObject(_player), player(_ graveMenu = addManagedMenu(player, playerMenu); rfgMenu = addManagedMenu(player, playerMenu); + // Context menus with double-click view action, following Exile's pattern. + const auto addPublicPileMenu = [this, _player](const QString &title, const QString &zone) { + auto *menu = playerMenu->addMenu(title); + auto *view = menu->addAction(tr("View %1").arg(title)); + connect(view, &QAction::triggered, this, [_player, zone]() { + _player->getLogic()->onRequestZoneViewToggle(zone, -1, false); + }); + return menu; + }; + limboMenu = addPublicPileMenu(tr("Limbo"), QString::fromLatin1(ZoneNames::LIMBO)); + refugeMenu = addPublicPileMenu(tr("Refuge"), QString::fromLatin1(ZoneNames::REFUGE)); + oblivionMenu = addPublicPileMenu(tr("Oblivion"), QString::fromLatin1(ZoneNames::OBLIVION)); + if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { sideboardMenu = addManagedMenu(player, playerMenu); customZonesMenu = addManagedMenu(player); @@ -80,6 +95,9 @@ void PlayerMenu::setMenusForGraphicItems() player->getTableZoneGraphicsItem()->setMenu(playerMenu); player->getGraveyardZoneGraphicsItem()->setMenu(graveMenu, graveMenu->aViewGraveyard); player->getRfgZoneGraphicsItem()->setMenu(rfgMenu, rfgMenu->aViewRfg); + player->getLimboZoneGraphicsItem()->setMenu(limboMenu, limboMenu->actions().constFirst()); + player->getRefugeZoneGraphicsItem()->setMenu(refugeMenu, refugeMenu->actions().constFirst()); + player->getOblivionZoneGraphicsItem()->setMenu(oblivionMenu, oblivionMenu->actions().constFirst()); if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) { player->getHandZoneGraphicsItem()->setMenu(handMenu); player->getDeckZoneGraphicsItem()->setMenu(libraryMenu, libraryMenu->aDrawCard); From 21f574ca7cf721f5ea53b1d1c230a4e96641a0ab Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:56:37 -0700 Subject: [PATCH 22/29] Continuumatrice: render interactive public piles with themed icons --- cockatrice/src/game_graphics/player/menu/player_menu.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cockatrice/src/game_graphics/player/menu/player_menu.h b/cockatrice/src/game_graphics/player/menu/player_menu.h index e21a689de..48c12be5c 100644 --- a/cockatrice/src/game_graphics/player/menu/player_menu.h +++ b/cockatrice/src/game_graphics/player/menu/player_menu.h @@ -92,6 +92,9 @@ private: SideboardMenu *sideboardMenu; GraveyardMenu *graveMenu; RfgMenu *rfgMenu; + QMenu *limboMenu = nullptr; + QMenu *refugeMenu = nullptr; + QMenu *oblivionMenu = nullptr; UtilityMenu *utilityMenu; TallyMenu *tallyMenu; SayMenu *sayMenu; From 3df441195d783498e595cd0e0f9f65c2f4ac79e1 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:57:29 -0700 Subject: [PATCH 23/29] Continuumatrice: adjust extra pile geometry and add built-in icon resources --- cockatrice/src/game_graphics/player/player_graphics_item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cockatrice/src/game_graphics/player/player_graphics_item.cpp b/cockatrice/src/game_graphics/player/player_graphics_item.cpp index 57b2b6ce2..fbc70680d 100644 --- a/cockatrice/src/game_graphics/player/player_graphics_item.cpp +++ b/cockatrice/src/game_graphics/player/player_graphics_item.cpp @@ -290,7 +290,7 @@ void PlayerGraphicsItem::processSceneSizeChange(int newPlayerWidth) { // Extend table (and hand, if horizontal) to accommodate the new player width. qreal tableWidth = newPlayerWidth - CardDimensions::HEIGHT_F - 15 - counterAreaWidth - - stackZoneGraphicsItem->boundingRect().width(); + EXTRA_PILE_COLUMN_WIDTH - stackZoneGraphicsItem->boundingRect().width(); if (!SettingsCache::instance().userInterface().getHorizontalHand()) { tableWidth -= handZoneGraphicsItem->boundingRect().width(); } From dbb044771bbd82d2415c7704fe8ec7921e027f2d Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:57:32 -0700 Subject: [PATCH 24/29] Continuumatrice: adjust extra pile geometry and add built-in icon resources --- cockatrice/src/game_graphics/zones/pile_zone.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cockatrice/src/game_graphics/zones/pile_zone.cpp b/cockatrice/src/game_graphics/zones/pile_zone.cpp index 3b1589be6..eeb6af987 100644 --- a/cockatrice/src/game_graphics/zones/pile_zone.cpp +++ b/cockatrice/src/game_graphics/zones/pile_zone.cpp @@ -94,10 +94,12 @@ void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio cardItems().at(0)->paintPicture(painter, cardItems().at(0)->getTranslatedSize(painter), 90); } + painter->save(); painter->translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F); painter->rotate(-90); painter->translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F); paintNumberEllipse(cardItems().size(), 28, Qt::white, -1, -1, painter); + painter->restore(); if (!customDisplayName.isEmpty()) { painter->save(); From 71398ac33c47369404fd93499ea57409e989a188 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:58:27 -0700 Subject: [PATCH 25/29] Continuumatrice: adjust extra pile geometry and add built-in icon resources --- cockatrice/cockatrice.qrc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cockatrice/cockatrice.qrc b/cockatrice/cockatrice.qrc index 4ec3d2385..e421553ab 100644 --- a/cockatrice/cockatrice.qrc +++ b/cockatrice/cockatrice.qrc @@ -5,6 +5,9 @@ resources/cockatrice-logo-white.svg resources/hand.svg resources/hr.jpg + resources/piles/limbo.svg + resources/piles/refuge.svg + resources/piles/oblivion.svg resources/icons/arrow_bottom_green.svg resources/icons/arrow_down_green.svg From 09e6b54869495df27a0c3d54a64059594c5a6d35 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:58:29 -0700 Subject: [PATCH 26/29] Add fallback limbo pile SVG icon --- cockatrice/resources/piles/limbo.svg | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 cockatrice/resources/piles/limbo.svg diff --git a/cockatrice/resources/piles/limbo.svg b/cockatrice/resources/piles/limbo.svg new file mode 100644 index 000000000..288b195cc --- /dev/null +++ b/cockatrice/resources/piles/limbo.svg @@ -0,0 +1,7 @@ + + + + + + + From 453ef0533443bf2f8cd5ce895fc10509cb6bb08f Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:58:31 -0700 Subject: [PATCH 27/29] Add fallback refuge pile SVG icon --- cockatrice/resources/piles/refuge.svg | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 cockatrice/resources/piles/refuge.svg diff --git a/cockatrice/resources/piles/refuge.svg b/cockatrice/resources/piles/refuge.svg new file mode 100644 index 000000000..8c42ef953 --- /dev/null +++ b/cockatrice/resources/piles/refuge.svg @@ -0,0 +1,6 @@ + + + + + + From dd9aca86683fd386a2cf97c6d5f9beab5273b7bb Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 01:58:35 -0700 Subject: [PATCH 28/29] Add fallback oblivion pile SVG icon --- cockatrice/resources/piles/oblivion.svg | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 cockatrice/resources/piles/oblivion.svg diff --git a/cockatrice/resources/piles/oblivion.svg b/cockatrice/resources/piles/oblivion.svg new file mode 100644 index 000000000..96e832b8e --- /dev/null +++ b/cockatrice/resources/piles/oblivion.svg @@ -0,0 +1,7 @@ + + + + + + + From f4c78264b811e9df9e14ec2507f03d14f5c2385d Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 02:00:33 -0700 Subject: [PATCH 29/29] Document Limbo Refuge Oblivion pile behavior, icon overrides, and testing --- doc/continuumatrice-special-piles.md | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 doc/continuumatrice-special-piles.md diff --git a/doc/continuumatrice-special-piles.md b/doc/continuumatrice-special-piles.md new file mode 100644 index 000000000..64de4e480 --- /dev/null +++ b/doc/continuumatrice-special-piles.md @@ -0,0 +1,54 @@ +# Limbo, Refuge and Oblivion piles + +Continuumatrice adds **three persistent public card piles per player** alongside the existing Deck, Hand, Graveyard, Exile, Sideboard, Table and Stack zones. + +| Display name | Stable wire-zone name | Visibility | Interactions | +| --- | --- | --- | --- | +| Limbo | `limbo` | Public | Drop cards, move selected cards, view contents | +| Refuge | `refuge` | Public | Drop cards, move selected cards, view contents | +| Oblivion | `oblivion` | Public | Drop cards, move selected cards, view contents | + +These use existing `PileZoneLogic`, `PileZone`, server `Server_CardZone` and string-based protocol zone names. **The piles do not enforce gameplay rules** (e.g., what gets sent to Limbo or whether Oblivion is permanent). Nothing automatically sends cards to them; users move cards through drag/drop or the card **Move to** menu. + +The three new piles appear in a second, compact column, aligned with the existing deck, graveyard and exile piles. The table and hand shift to allow the extra column without overlap. Double-click a new pile to open the standard zone viewer; right-click for a **View** menu. The card count and the pile name/icon are shown even when the pile is nonempty. + +## Icons and asset overrides + +The fork bundles original fallback SVG icons: + +- `cockatrice/resources/piles/limbo.svg` +- `cockatrice/resources/piles/refuge.svg` +- `cockatrice/resources/piles/oblivion.svg` + +They are registered in `cockatrice/cockatrice.qrc`; you do not need to manually copy them to run the built client. + +To assign your own art **without recompiling**, put a matching asset in the **currently selected Continuumatrice theme**: + +```text +~/.local/share/Continuumatrice/themes//piles/ + limbo.svg + refuge.svg + oblivion.svg +``` + +The theme can use `.png`, `.jpg`, `.jpeg` or `.svg`, and the existing theme manager also supports scheme-specific file variants such as `limbo-dark.svg`. The theme's asset takes precedence; the bundled SVG is a fallback. Reload/change the theme to refresh the icons. The icons are cosmetic; all card data remains in the database and the runtime zone state. + +## Multiplayer compatibility + +The new piles are created in **both** client-side `PlayerLogic` and server-side `Server_Player::setupZones`. They therefore work with the fork's **local game** and a **Continuumatrice-built server**. An unmodified upstream Cockatrice server does not create these zones and may reject card moves to `limbo`, `refuge` or `oblivion`. Use a matched client/server build for multiplayer. + +No new protobuf messages are needed; the existing `Command_MoveCard` uses zone-name strings. Older upstream clients do not have dedicated icons/menus for these piles. + +## Manual verification (after rebuild) + +1. Launch a local game with two players and confirm all three new piles appear on each side. +2. Confirm the new icons and labels are distinct and the board does not overlap the old piles, stack, hand or table. +3. Move one card from the hand/table to each pile using **Move to**, and then by dragging. +4. Double-click or right-click each pile to view its card list, then move a card back to the table or hand. +5. Check the pile counters and name badges are legible when cards are present. +6. Rejoin/reconnect a game (and try a replay) to ensure pile contents are reconstructed from server zones. +7. Test both mirrored player orientations and the horizontal-hand setting at different window sizes. +8. Override an icon with an asset in the selected theme and reload the theme. +9. If using a remote server, ensure that server also has this fork's zone definitions. + +Build locally with `cmake --build build --target cockatrice --parallel 16`. This change has **not** been compiled or visually run in this environment; the compilation and runtime checks above are essential before merging.