From 62b7088807ea31a9a9ea3860d2eac0160f7d5a46 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Sun, 1 Nov 2026 23:52:42 -0700 Subject: [PATCH 1/3] Reserve row label space and add battlefield zone appearance state --- cockatrice/src/game_graphics/zones/table_zone.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/cockatrice/src/game_graphics/zones/table_zone.h b/cockatrice/src/game_graphics/zones/table_zone.h index 8aa4936ad..992e150df 100644 --- a/cockatrice/src/game_graphics/zones/table_zone.h +++ b/cockatrice/src/game_graphics/zones/table_zone.h @@ -11,6 +11,7 @@ #include "../card_dimensions.h" #include "select_zone.h" +#include #include #include #include @@ -47,10 +48,10 @@ private: */ static const int MARGIN_LEFT = 20; static const int MARGIN_RIGHT = 15; - static const int MARGIN_TOP = 10; - static const int MARGIN_BOTTOM = 30; + static const int MARGIN_TOP = 30; // Space for zone labels. + static const int MARGIN_BOTTOM = 26; static const int PADDING_X = 35; - static const int PADDING_Y = 30; + static const int PADDING_Y = 50; /* Minimum width of the table zone including margins. @@ -61,7 +62,7 @@ private: Offset sizes when cards are stacked on each other in the grid */ static const int STACKED_CARD_OFFSET_X = CardDimensions::WIDTH / 3; - static const int STACKED_CARD_OFFSET_Y = PADDING_Y / 3; + static const int STACKED_CARD_OFFSET_Y = 8; // Avoid covering the next zone label. /* Width of the box line drawn in the margin around the active player's area @@ -100,6 +101,11 @@ private: bool mirrored = false; bool playmatActive = false; + QColor frontZoneColor; + QColor supportZoneColor; + QColor commandZoneColor; + QString zoneLabelFontFamily; + [[nodiscard]] bool isInverted() const; private slots: @@ -220,7 +226,8 @@ private: qreal damageShimmerAlpha = 0.0; void paintZoneOutline(QPainter *painter); - void paintLandDivider(QPainter *painter); + void paintBattlefieldRows(QPainter *painter); + void reloadBattlefieldAppearance(); /* Calculates card stack widths so mapping functions work properly From 61659a566a26164583681fabf64008c604dd059d Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Sun, 1 Nov 2026 23:52:47 -0700 Subject: [PATCH 2/3] Paint Front Support Command zone bands with optional theme colors and font --- .../src/game_graphics/zones/table_zone.cpp | 141 ++++++++++++++++-- 1 file changed, 129 insertions(+), 12 deletions(-) diff --git a/cockatrice/src/game_graphics/zones/table_zone.cpp b/cockatrice/src/game_graphics/zones/table_zone.cpp index 0dffab353..b2895c56a 100644 --- a/cockatrice/src/game_graphics/zones/table_zone.cpp +++ b/cockatrice/src/game_graphics/zones/table_zone.cpp @@ -12,6 +12,15 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -71,6 +80,7 @@ TableZone::TableZone(TableZoneLogic *_logic, bool _mirrored, QGraphicsItem *pare void TableZone::updateBg() { + reloadBattlefieldAppearance(); update(); } @@ -126,6 +136,8 @@ void TableZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*opti painter->fillRect(boundingRect(), brush); } + paintBattlefieldRows(painter); + if (active) { paintZoneOutline(painter); } else { @@ -141,7 +153,6 @@ void TableZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*opti painter->fillRect(boundingRect(), shimmerColor); } - paintLandDivider(painter); } void TableZone::onPlaymatChanged(bool active) @@ -179,21 +190,127 @@ void TableZone::paintZoneOutline(QPainter *painter) painter->fillRect(QRectF(width - BOX_LINE_WIDTH, 0, BOX_LINE_WIDTH, height), QBrush(grad1)); } -/** - Render a division line for land placement - @painter QPainter object +/** + * Draw Front, Support, and Command on Cockatrice's existing three-row table. + * This changes presentation only, not gameplay or network-zone identifiers. */ -void TableZone::paintLandDivider(QPainter *painter) +void TableZone::paintBattlefieldRows(QPainter *painter) { - // Place the line 2 grid heights down then back it off just enough to allow - // some space between a 3-card stack and the land area. - qreal separatorY = MARGIN_TOP + 2 * (CardDimensions::HEIGHT + PADDING_Y) - STACKED_CARD_OFFSET_Y / 2; - if (isInverted()) { - separatorY = height - separatorY; + painter->save(); + + const qreal rowStride = CardDimensions::HEIGHT + PADDING_Y; + const bool inverted = isInverted(); + QFont font(zoneLabelFontFamily); + font.setPixelSize(18); + painter->setFont(font); + + for (int visualRow = 0; visualRow < TableZoneLogic::ROW_COUNT; ++visualRow) { + // Table logical grid Y: 0 = Command, 1 = Support, 2 = Front. + const int row = inverted ? TableZoneLogic::ROW_COUNT - 1 - visualRow : visualRow; + QString label; + QColor color; + + switch (row) { + case 0: + label = tr("COMMAND ZONE"); + color = commandZoneColor; + break; + case 1: + label = tr("SUPPORT ZONE"); + color = supportZoneColor; + break; + default: + label = tr("FRONT ZONE"); + color = frontZoneColor; + break; + } + + const qreal top = visualRow * rowStride; + const qreal bottom = visualRow == TableZoneLogic::ROW_COUNT - 1 ? height : top + rowStride; + painter->fillRect(QRectF(0, top, width, bottom - top), color); + + const QRectF labelRect(16, top + 3, qMax(0, width - 32), 24); + painter->setPen(QColor(0, 0, 0, 210)); + painter->drawText(labelRect.translated(1, 1), Qt::AlignHCenter | Qt::AlignVCenter, label); + painter->setPen(QColor(250, 248, 244)); + painter->drawText(labelRect, Qt::AlignHCenter | Qt::AlignVCenter, label); + + if (visualRow > 0) { + painter->setPen(QPen(QColor(255, 255, 255, 95), 2)); + painter->drawLine(QPointF(0, top), QPointF(width, top)); + } + } + + painter->restore(); +} + +/** + * Each theme may provide a battlefield.ini and/or Continuumorphous font file. + * Optional assets avoid build failures when the proprietary font is absent. + */ +void TableZone::reloadBattlefieldAppearance() +{ + 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()) { + const QString iniPath = QDir(themePath).filePath(QStringLiteral("battlefield.ini")); + if (QFileInfo::exists(iniPath)) { + QSettings settings(iniPath, QSettings::IniFormat); + const auto readColor = [&settings](const QString &key, const QColor &fallback) { + const QColor parsed(settings.value(key, fallback.name(QColor::HexArgb)).toString()); + return parsed.isValid() ? parsed : fallback; + }; + frontZoneColor = readColor(QStringLiteral("Zones/Front"), frontZoneColor); + supportZoneColor = readColor(QStringLiteral("Zones/Support"), supportZoneColor); + commandZoneColor = readColor(QStringLiteral("Zones/Command"), commandZoneColor); + } + } + + QStringList fontPaths; + if (!themePath.isEmpty()) { + const QDir themeDir(themePath); + fontPaths << themeDir.filePath(QStringLiteral("fonts/Continuumorphous-Regular.ttf")) + << themeDir.filePath(QStringLiteral("fonts/Continuumorphous-Regular.otf")); + } + const QDir binaryDir(QCoreApplication::applicationDirPath()); + fontPaths << binaryDir.filePath(QStringLiteral("fonts/Continuumorphous-Regular.ttf")) + << binaryDir.filePath(QStringLiteral("fonts/Continuumorphous-Regular.otf")); + + static QHash loadedFamilies; + zoneLabelFontFamily.clear(); + for (const QString &path : fontPaths) { + if (!QFileInfo::isFile(path)) { + continue; + } + if (loadedFamilies.contains(path)) { + zoneLabelFontFamily = loadedFamilies.value(path); + } else { + const int id = QFontDatabase::addApplicationFont(path); + const QStringList families = QFontDatabase::applicationFontFamilies(id); + if (!families.isEmpty()) { + zoneLabelFontFamily = families.constFirst(); + loadedFamilies.insert(path, zoneLabelFontFamily); + } + } + if (!zoneLabelFontFamily.isEmpty()) { + break; + } + } + + if (zoneLabelFontFamily.isEmpty()) { + const QStringList installed = QFontDatabase::families(); + if (installed.contains(QStringLiteral("Continuumorphous Regular"), Qt::CaseInsensitive)) { + zoneLabelFontFamily = QStringLiteral("Continuumorphous Regular"); + } else if (installed.contains(QStringLiteral("Continuumorphous"), Qt::CaseInsensitive)) { + zoneLabelFontFamily = QStringLiteral("Continuumorphous"); + } else { + zoneLabelFontFamily = QFont().family(); + } } - painter->setPen(QColor(255, 255, 255, 40)); - painter->drawLine(QPointF(0, separatorY), QPointF(width, separatorY)); } void TableZone::handleDropEvent(const QList &dragItems, From 3f779c3bc17d8ea3215eaec483b215af6531ed0c Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Sun, 1 Nov 2026 23:52:50 -0700 Subject: [PATCH 3/3] Document battlefield colors and Continuumorphous font setup --- doc/continuumatrice-battlefield.md | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 doc/continuumatrice-battlefield.md diff --git a/doc/continuumatrice-battlefield.md b/doc/continuumatrice-battlefield.md new file mode 100644 index 000000000..130dc1f2d --- /dev/null +++ b/doc/continuumatrice-battlefield.md @@ -0,0 +1,32 @@ +# Continuumatrice battlefield style + +Cockatrice's three visual table rows are reused as **Front**, **Support**, and **Command**. This is purely a display change: the server still sees a single `table` zone. + +| Card XML `` | Position | +| --- | --- | +| `0` | Front | +| `1` | Support | +| `2` | Command | + +## Optional color customization + +Create `battlefield.ini` in the active Cockatrice theme directory: + +```ini +[Zones] +Front = #A08E343D +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. + +## Font + +To render labels using your own **Continuumorphous Regular**, copy either `Continuumorphous-Regular.ttf` or `Continuumorphous-Regular.otf` into `fonts/` inside the current theme directory, or into a `fonts/` directory beside the Continuumatrice executable. + +The font is also detected if already installed as "Continuumorphous Regular" or "Continuumorphous". If not found, a Qt default font is used. The font asset is not included in this change; no resource entry was added that would break builds when the file is missing. + +## Compatibility + +The existing drag/drop, row mapping, stacking, card counters, playmats, and multiplayer protocols are preserved. This does **not** enforce gameplay placement restrictions.