diff --git a/cockatrice/src/game/board/abstract_card_drag_item.cpp b/cockatrice/src/game/board/abstract_card_drag_item.cpp index c961cbcb6..c9a964048 100644 --- a/cockatrice/src/game/board/abstract_card_drag_item.cpp +++ b/cockatrice/src/game/board/abstract_card_drag_item.cpp @@ -1,6 +1,7 @@ #include "abstract_card_drag_item.h" #include "../../client/settings/cache_settings.h" +#include "../z_values.h" #include #include @@ -18,13 +19,13 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, { if (parentDrag) { parentDrag->addChildDrag(this); - setZValue(2000000007 + hotSpot.x() * 1000000 + hotSpot.y() * 1000 + 1000); + setZValue(ZValues::childDragZValue(hotSpot.x(), hotSpot.y())); connect(parentDrag, &QObject::destroyed, this, &AbstractCardDragItem::deleteLater); } else { hotSpot = QPointF{qBound(0.0, hotSpot.x(), static_cast(CARD_WIDTH - 1)), qBound(0.0, hotSpot.y(), static_cast(CARD_HEIGHT - 1))}; setCursor(Qt::ClosedHandCursor); - setZValue(2000000007); + setZValue(ZValues::DRAG_ITEM); } if (item->getTapped()) setTransform(QTransform() diff --git a/cockatrice/src/game/board/abstract_card_item.cpp b/cockatrice/src/game/board/abstract_card_item.cpp index d5538011d..f8365c3d6 100644 --- a/cockatrice/src/game/board/abstract_card_item.cpp +++ b/cockatrice/src/game/board/abstract_card_item.cpp @@ -3,6 +3,7 @@ #include "../../client/settings/cache_settings.h" #include "../../interface/card_picture_loader/card_picture_loader.h" #include "../game_scene.h" +#include "../z_values.h" #include #include @@ -215,7 +216,7 @@ void AbstractCardItem::setHovered(bool _hovered) if (_hovered) processHoverEvent(); isHovered = _hovered; - setZValue(_hovered ? 2000000004 : realZValue); + setZValue(_hovered ? ZValues::HOVERED_CARD : realZValue); setScale(_hovered && SettingsCache::instance().getScaleCards() ? 1.1 : 1); setTransformOriginPoint(_hovered ? CARD_WIDTH / 2 : 0, _hovered ? CARD_HEIGHT / 2 : 0); update(); diff --git a/cockatrice/src/game/board/arrow_item.cpp b/cockatrice/src/game/board/arrow_item.cpp index 1352b3a05..257d96f8a 100644 --- a/cockatrice/src/game/board/arrow_item.cpp +++ b/cockatrice/src/game/board/arrow_item.cpp @@ -5,6 +5,7 @@ #include "../player/player.h" #include "../player/player_actions.h" #include "../player/player_target.h" +#include "../z_values.h" #include "../zones/card_zone.h" #include "card_item.h" @@ -23,7 +24,7 @@ ArrowItem::ArrowItem(Player *_player, int _id, ArrowTarget *_startItem, ArrowTar : QGraphicsItem(), player(_player), id(_id), startItem(_startItem), targetItem(_targetItem), targetLocked(false), color(_color), fullColor(true) { - setZValue(2000000005); + setZValue(ZValues::ARROWS); if (startItem) startItem->addArrowFrom(this); diff --git a/cockatrice/src/game/z_value_layer_manager.h b/cockatrice/src/game/z_value_layer_manager.h new file mode 100644 index 000000000..303edb8a5 --- /dev/null +++ b/cockatrice/src/game/z_value_layer_manager.h @@ -0,0 +1,136 @@ +/** + * @file z_value_layer_manager.h + * @ingroup GameGraphics + * @brief Semantic Z-value layer management for game scene rendering. + * + * This file provides a structured approach to Z-value allocation in the game scene. + * Z-values in Qt determine stacking order - higher values render on top of lower values. + * + * ## Layer Architecture + * + * The game scene is organized into three conceptual layers: + * + * 1. **Zone Layer (0-999)**: Zone backgrounds, containers, and static elements + * - Zone backgrounds (0.5-1.0) + * - Cards within zones (1.0 base + index) + * + * 2. **Card Layer (1-40,000,000)**: Dynamic card rendering on the table zone + * - Cards use formula: (actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100 + * - Maximum card Z-value: ~40,000,000 (with 3 rows, actualY <= ~289) + * + * 3. **Overlay Layer (2,000,000,000+)**: UI elements that must appear above all cards + * - Hovered cards (+1) + * - Arrows (+3) + * - Zone views (+4) + * - Drag items (+5, +6) + * - Top UI elements (+7) + * + * ## Design Rationale + * + * The large gap between card Z-values (max ~40M) and overlay base (2B) provides + * safety margin for future table zone expansions while ensuring overlays always + * render above cards regardless of table position. + * + * ## Usage + * + * Prefer using the semantic constants from ZValues namespace: + * @code + * card->setZValue(ZValues::HOVERED_CARD); + * arrow->setZValue(ZValues::ARROWS); + * @endcode + * + * Use validation functions to verify card Z-values during development: + * @code + * Q_ASSERT(ZValueLayerManager::isValidCardZValue(cardZ)); + * @endcode + */ + +#ifndef Z_VALUE_LAYER_MANAGER_H +#define Z_VALUE_LAYER_MANAGER_H + +#include + +/** + * @namespace ZValueLayerManager + * @brief Utilities for Z-value validation and layer management. + */ +namespace ZValueLayerManager +{ + +/** + * @enum Layer + * @brief Semantic layer identifiers for Z-value allocation. + * + * These represent conceptual rendering layers, not actual Z-values. + * Use the corresponding ZValues constants for actual rendering. + */ +enum class Layer +{ + /// Zone-level elements like backgrounds and containers + Zone, + /// Cards rendered in zones (uses sequential Z-values) + Card, + /// Temporary UI elements like hovered cards and drag items + Overlay +}; + +/** + * @brief Maximum Z-value a card can have on the table zone. + * + * Based on table zone formula: (actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100 + * With maximum 3 rows and CARD_HEIGHT ~96, actualY <= ~289. + * Maximum: (289 + 96) * 100000 + 100 * 100 = 38,510,000 + * + * We use 40,000,000 as a safe upper bound with margin. + */ +constexpr qreal CARD_Z_VALUE_MAX = 40000000.0; + +/** + * @brief Base Z-value for overlay elements. + * + * Must exceed CARD_Z_VALUE_MAX to ensure overlays render above all cards. + * The 50x margin (2B vs 40M) provides safety for future expansion. + */ +constexpr qreal OVERLAY_BASE = 2000000000.0; + +/** + * @brief Validates that a Z-value is within the valid card range. + * + * Cards should have Z-values between CARD_BASE (1.0) and CARD_Z_VALUE_MAX. + * Values outside this range may interfere with overlay rendering. + * + * @param zValue The Z-value to validate + * @return true if the Z-value is valid for a card + */ +[[nodiscard]] constexpr bool isValidCardZValue(qreal zValue) +{ + return zValue >= 1.0 && zValue <= CARD_Z_VALUE_MAX; +} + +/** + * @brief Validates that a Z-value is in the overlay layer. + * + * Overlay elements should have Z-values at or above OVERLAY_BASE. + * + * @param zValue The Z-value to validate + * @return true if the Z-value is valid for an overlay element + */ +[[nodiscard]] constexpr bool isOverlayZValue(qreal zValue) +{ + return zValue >= OVERLAY_BASE; +} + +/** + * @brief Returns the Z-value for a specific overlay element. + * + * @param offset Offset from OVERLAY_BASE (0-7 for current elements) + * @return The absolute Z-value for the overlay element + */ +[[nodiscard]] constexpr qreal overlayZValue(qreal offset) +{ + return OVERLAY_BASE + offset; +} + +} // namespace ZValueLayerManager + +#endif // Z_VALUE_LAYER_MANAGER_H diff --git a/cockatrice/src/game/z_values.h b/cockatrice/src/game/z_values.h new file mode 100644 index 000000000..2c7fe9066 --- /dev/null +++ b/cockatrice/src/game/z_values.h @@ -0,0 +1,83 @@ +#ifndef Z_VALUES_H +#define Z_VALUES_H + +#include "z_value_layer_manager.h" + +/** + * @file z_values.h + * @ingroup GameGraphics + * @brief Centralized Z-value constants for rendering layer order. + * + * Z-values in Qt determine stacking order. Higher values render on top. + * These constants define the visual layering hierarchy for the game scene. + * + * ## Layer Architecture + * + * See z_value_layer_manager.h for detailed documentation on the three-layer + * architecture (Zone, Card, Overlay) and the rationale for Z-value choices. + * + * ## Quick Reference + * + * | Layer | Z-Value Range | Purpose | + * |----------|------------------|-----------------------------------| + * | Zone | 0.5 - 1.0 | Zone backgrounds, containers | + * | Card | 1.0 - 40,000,000 | Cards on table (position-based) | + * | Overlay | 2,000,000,000+ | UI elements above all cards | + */ + +namespace ZValues +{ + +// Expose base for callers that need it +constexpr qreal OVERLAY_BASE = ZValueLayerManager::OVERLAY_BASE; + +// Overlay layer Z-values for items that should appear above normal cards +constexpr qreal HOVERED_CARD = ZValueLayerManager::overlayZValue(1.0); +constexpr qreal ARROWS = ZValueLayerManager::overlayZValue(3.0); +constexpr qreal ZONE_VIEW_WIDGET = ZValueLayerManager::overlayZValue(4.0); +constexpr qreal DRAG_ITEM = ZValueLayerManager::overlayZValue(5.0); +constexpr qreal DRAG_ITEM_CHILD = ZValueLayerManager::overlayZValue(6.0); +constexpr qreal TOP_UI = ZValueLayerManager::overlayZValue(7.0); + +/** + * @brief Compute Z-value for child drag items based on hotspot position. + * + * When dragging multiple cards together, each child card needs a unique Z-value + * to prevent Z-fighting (flickering/flashing). The Z-values are derived from + * their position when grabbed to conserve original stacking. The formula encodes + * 2D coordinates into a single value where X has higher weight, ensuring + * deterministic visual stacking. + * + * @param hotSpotX The X coordinate of the grab position + * @param hotSpotY The Y coordinate of the grab position + * @return Unique Z-value for the child drag item + */ +[[nodiscard]] constexpr qreal childDragZValue(qreal hotSpotX, qreal hotSpotY) +{ + return DRAG_ITEM_CHILD + hotSpotX * 1000000 + hotSpotY * 1000 + 1000; +} + +/** + * @brief Compute Z-value for cards on the table zone based on position. + * + * Cards lower on the table (higher Y) render above cards higher up, + * and cards to the right (higher X) render above cards to the left. + * This creates natural visual stacking for overlapping cards. + * + * @param x The X coordinate of the card position + * @param y The Y coordinate of the card position + * @return Z-value for the card's table position + */ +[[nodiscard]] constexpr qreal tableCardZValue(qreal x, qreal y) +{ + constexpr qreal CARD_HEIGHT_FOR_Z = 102.0; + return (y + CARD_HEIGHT_FOR_Z) * 100000.0 + (x + 1) * 100.0; +} + +// Card layering (general architecture, not command-zone specific) +constexpr qreal CARD_BASE = 1.0; +constexpr qreal CARD_MAX = ZValueLayerManager::CARD_Z_VALUE_MAX; + +} // namespace ZValues + +#endif // Z_VALUES_H diff --git a/cockatrice/src/game/zones/table_zone.cpp b/cockatrice/src/game/zones/table_zone.cpp index ca1052d20..33c867c43 100644 --- a/cockatrice/src/game/zones/table_zone.cpp +++ b/cockatrice/src/game/zones/table_zone.cpp @@ -7,6 +7,7 @@ #include "../board/card_item.h" #include "../player/player.h" #include "../player/player_actions.h" +#include "../z_values.h" #include "logic/table_zone_logic.h" #include @@ -169,7 +170,7 @@ void TableZone::reorganizeCards() actualY += 15; getLogic()->getCards()[i]->setPos(actualX, actualY); - getLogic()->getCards()[i]->setRealZValue((actualY + CARD_HEIGHT) * 100000 + (actualX + 1) * 100); + getLogic()->getCards()[i]->setRealZValue(ZValues::tableCardZValue(actualX, actualY)); QListIterator attachedCardIterator(getLogic()->getCards()[i]->getAttachedCards()); int j = 0; @@ -179,7 +180,7 @@ void TableZone::reorganizeCards() qreal childX = actualX - j * STACKED_CARD_OFFSET_X; qreal childY = y + 5; attachedCard->setPos(childX, childY); - attachedCard->setRealZValue((childY + CARD_HEIGHT) * 100000 + (childX + 1) * 100); + attachedCard->setRealZValue(ZValues::tableCardZValue(childX, childY)); } } diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index 3ea4eb119..0ad3b10f1 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -7,6 +7,7 @@ #include "../game_scene.h" #include "../player/player.h" #include "../player/player_actions.h" +#include "../z_values.h" #include "view_zone.h" #include @@ -47,7 +48,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, { setAcceptHoverEvents(true); setAttribute(Qt::WA_DeleteOnClose); - setZValue(2000000006); + setZValue(ZValues::ZONE_VIEW_WIDGET); setFlag(ItemIgnoresTransformations); QGraphicsLinearLayout *vbox = new QGraphicsLinearLayout(Qt::Vertical); @@ -71,7 +72,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, QGraphicsProxyWidget *searchEditProxy = new QGraphicsProxyWidget; searchEditProxy->setWidget(&searchEdit); - searchEditProxy->setZValue(2000000007); + searchEditProxy->setZValue(ZValues::DRAG_ITEM); vbox->addItem(searchEditProxy); // top row @@ -80,13 +81,13 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, // groupBy options QGraphicsProxyWidget *groupBySelectorProxy = new QGraphicsProxyWidget; groupBySelectorProxy->setWidget(&groupBySelector); - groupBySelectorProxy->setZValue(2000000008); + groupBySelectorProxy->setZValue(ZValues::TOP_UI); hTopRow->addItem(groupBySelectorProxy); // sortBy options QGraphicsProxyWidget *sortBySelectorProxy = new QGraphicsProxyWidget; sortBySelectorProxy->setWidget(&sortBySelector); - sortBySelectorProxy->setZValue(2000000007); + sortBySelectorProxy->setZValue(ZValues::DRAG_ITEM); hTopRow->addItem(sortBySelectorProxy); vbox->addItem(hTopRow);