mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-08 17:13:57 -07:00
feat(command-zone): integrate into player system
Wire command zones into the player object hierarchy, making them functional game elements. Player class modifications: - player.h/cpp: command zone ownership and lifecycle - player_actions.h/cpp: command zone action handlers - player_graphics_item.h/cpp: command zone visual positioning This commit connects all previous command zone components to the player, enabling: - Zone creation during game setup - Zone positioning relative to other player zones - Action routing for command zone interactions - Proper cleanup on player destruction
This commit is contained in:
parent
1ae1d876b1
commit
437d2e4ed4
6 changed files with 655 additions and 44 deletions
|
|
@ -5,12 +5,16 @@
|
||||||
#include "../board/arrow_item.h"
|
#include "../board/arrow_item.h"
|
||||||
#include "../board/card_item.h"
|
#include "../board/card_item.h"
|
||||||
#include "../board/card_list.h"
|
#include "../board/card_list.h"
|
||||||
|
#include "../board/commander_tax_counter.h"
|
||||||
#include "../board/counter_general.h"
|
#include "../board/counter_general.h"
|
||||||
#include "../game_scene.h"
|
#include "../game_scene.h"
|
||||||
|
#include "../z_values.h"
|
||||||
|
#include "../zones/command_zone.h"
|
||||||
#include "../zones/hand_zone.h"
|
#include "../zones/hand_zone.h"
|
||||||
#include "../zones/pile_zone.h"
|
#include "../zones/pile_zone.h"
|
||||||
#include "../zones/stack_zone.h"
|
#include "../zones/stack_zone.h"
|
||||||
#include "../zones/table_zone.h"
|
#include "../zones/table_zone.h"
|
||||||
|
#include "../zones/zone_names.h"
|
||||||
#include "player_actions.h"
|
#include "player_actions.h"
|
||||||
#include "player_target.h"
|
#include "player_target.h"
|
||||||
|
|
||||||
|
|
@ -19,6 +23,7 @@
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
|
#include <libcockatrice/common/counter_ids.h>
|
||||||
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
|
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_set_card_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_set_card_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_create_arrow.pb.h>
|
#include <libcockatrice/protocol/pb/event_create_arrow.pb.h>
|
||||||
|
|
@ -32,7 +37,8 @@
|
||||||
Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent)
|
Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent)
|
||||||
: QObject(_parent), game(_parent), playerInfo(new PlayerInfo(info, _id, _local, _judge)),
|
: QObject(_parent), game(_parent), playerInfo(new PlayerInfo(info, _id, _local, _judge)),
|
||||||
playerEventHandler(new PlayerEventHandler(this)), playerActions(new PlayerActions(this)), active(false),
|
playerEventHandler(new PlayerEventHandler(this)), playerActions(new PlayerActions(this)), active(false),
|
||||||
conceded(false), zoneId(0), dialogSemaphore(false)
|
conceded(false), zoneId(0), dialogSemaphore(false), serverHasCommandZone(false), serverHasCompanionZone(false),
|
||||||
|
serverHasBackgroundZone(false)
|
||||||
{
|
{
|
||||||
initializeZones();
|
initializeZones();
|
||||||
|
|
||||||
|
|
@ -41,6 +47,9 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, A
|
||||||
playerMenu->setMenusForGraphicItems();
|
playerMenu->setMenusForGraphicItems();
|
||||||
|
|
||||||
connect(this, &Player::activeChanged, graphicsItem, &PlayerGraphicsItem::onPlayerActiveChanged);
|
connect(this, &Player::activeChanged, graphicsItem, &PlayerGraphicsItem::onPlayerActiveChanged);
|
||||||
|
connect(this, &Player::commandZoneSupportChanged, graphicsItem, &PlayerGraphicsItem::setCommandZonesVisible);
|
||||||
|
connect(this, &Player::companionZoneSupportChanged, graphicsItem, &PlayerGraphicsItem::setCompanionZoneVisible);
|
||||||
|
connect(this, &Player::backgroundZoneSupportChanged, graphicsItem, &PlayerGraphicsItem::setBackgroundZoneVisible);
|
||||||
|
|
||||||
connect(this, &Player::openDeckEditor, game->getTab(), &TabGame::openDeckEditor);
|
connect(this, &Player::openDeckEditor, game->getTab(), &TabGame::openDeckEditor);
|
||||||
|
|
||||||
|
|
@ -61,15 +70,19 @@ void Player::forwardActionSignalsToEventHandler()
|
||||||
|
|
||||||
void Player::initializeZones()
|
void Player::initializeZones()
|
||||||
{
|
{
|
||||||
addZone(new PileZoneLogic(this, "deck", false, true, false, this));
|
addZone(new PileZoneLogic(this, ZoneNames::DECK, false, true, false, this));
|
||||||
addZone(new PileZoneLogic(this, "grave", false, false, true, this));
|
addZone(new PileZoneLogic(this, ZoneNames::GRAVE, false, false, true, this));
|
||||||
addZone(new PileZoneLogic(this, "rfg", false, false, true, this));
|
addZone(new PileZoneLogic(this, ZoneNames::EXILE, false, false, true, this));
|
||||||
addZone(new PileZoneLogic(this, "sb", false, false, false, this));
|
addZone(new PileZoneLogic(this, ZoneNames::SIDEBOARD, false, false, false, this));
|
||||||
addZone(new TableZoneLogic(this, "table", true, false, true, this));
|
addZone(new TableZoneLogic(this, ZoneNames::TABLE, true, false, true, this));
|
||||||
addZone(new StackZoneLogic(this, "stack", true, false, true, this));
|
addZone(new StackZoneLogic(this, ZoneNames::STACK, true, false, true, this));
|
||||||
bool visibleHand = playerInfo->getLocalOrJudge() ||
|
bool visibleHand = playerInfo->getLocalOrJudge() ||
|
||||||
(game->getPlayerManager()->isSpectator() && game->getGameMetaInfo()->spectatorsOmniscient());
|
(game->getPlayerManager()->isSpectator() && game->getGameMetaInfo()->spectatorsOmniscient());
|
||||||
addZone(new HandZoneLogic(this, "hand", false, false, visibleHand, this));
|
addZone(new HandZoneLogic(this, ZoneNames::HAND, false, false, visibleHand, this));
|
||||||
|
addZone(new CommandZoneLogic(this, ZoneNames::COMMAND, true, false, true, this));
|
||||||
|
addZone(new CommandZoneLogic(this, ZoneNames::PARTNER, true, false, true, this));
|
||||||
|
addZone(new CommandZoneLogic(this, ZoneNames::COMPANION, true, false, true, this));
|
||||||
|
addZone(new CommandZoneLogic(this, ZoneNames::BACKGROUND, true, false, true, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
Player::~Player()
|
Player::~Player()
|
||||||
|
|
@ -97,6 +110,14 @@ void Player::clear()
|
||||||
clearCounters();
|
clearCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::updateZoneSupport(bool *flag, void (Player::*signal)(bool), bool found)
|
||||||
|
{
|
||||||
|
if (*flag != found) {
|
||||||
|
*flag = found;
|
||||||
|
emit(this->*signal)(found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Player::setConceded(bool _conceded)
|
void Player::setConceded(bool _conceded)
|
||||||
{
|
{
|
||||||
if (conceded != _conceded) {
|
if (conceded != _conceded) {
|
||||||
|
|
@ -125,7 +146,9 @@ void Player::processPlayerInfo(const ServerInfo_Player &info)
|
||||||
/* StackZone */
|
/* StackZone */
|
||||||
"stack",
|
"stack",
|
||||||
/* HandZone */
|
/* HandZone */
|
||||||
"hand"};
|
"hand",
|
||||||
|
/* CommandZones */
|
||||||
|
"command", "partner", "companion", "background"};
|
||||||
clearCounters();
|
clearCounters();
|
||||||
clearArrows();
|
clearArrows();
|
||||||
|
|
||||||
|
|
@ -140,7 +163,27 @@ void Player::processPlayerInfo(const ServerInfo_Player &info)
|
||||||
|
|
||||||
emit clearCustomZonesMenu();
|
emit clearCustomZonesMenu();
|
||||||
|
|
||||||
|
// Check if server has command zones by scanning the zone list
|
||||||
|
bool foundCommandZone = false;
|
||||||
|
bool foundCompanionZone = false;
|
||||||
|
bool foundBackgroundZone = false;
|
||||||
const int zoneListSize = info.zone_list_size();
|
const int zoneListSize = info.zone_list_size();
|
||||||
|
for (int i = 0; i < zoneListSize; ++i) {
|
||||||
|
QString zoneName = QString::fromStdString(info.zone_list(i).name());
|
||||||
|
if (zoneName == ZoneNames::COMMAND) {
|
||||||
|
foundCommandZone = true;
|
||||||
|
} else if (zoneName == ZoneNames::COMPANION) {
|
||||||
|
foundCompanionZone = true;
|
||||||
|
} else if (zoneName == ZoneNames::BACKGROUND) {
|
||||||
|
foundBackgroundZone = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update command zone support flags and notify graphics
|
||||||
|
updateZoneSupport(&serverHasCommandZone, &Player::commandZoneSupportChanged, foundCommandZone);
|
||||||
|
updateZoneSupport(&serverHasCompanionZone, &Player::companionZoneSupportChanged, foundCompanionZone);
|
||||||
|
updateZoneSupport(&serverHasBackgroundZone, &Player::backgroundZoneSupportChanged, foundBackgroundZone);
|
||||||
|
|
||||||
for (int i = 0; i < zoneListSize; ++i) {
|
for (int i = 0; i < zoneListSize; ++i) {
|
||||||
const ServerInfo_Zone &zoneInfo = info.zone_list(i);
|
const ServerInfo_Zone &zoneInfo = info.zone_list(i);
|
||||||
|
|
||||||
|
|
@ -156,7 +199,7 @@ void Player::processPlayerInfo(const ServerInfo_Player &info)
|
||||||
// Zones without coordinats are always treated as non-shufflable
|
// Zones without coordinats are always treated as non-shufflable
|
||||||
// PileZones, although supporting alternate hand or stack zones
|
// PileZones, although supporting alternate hand or stack zones
|
||||||
// might make sense in some scenarios.
|
// might make sense in some scenarios.
|
||||||
bool contentsKnown;
|
bool contentsKnown = false; // Default to false for unknown zone types
|
||||||
|
|
||||||
switch (zoneInfo.type()) {
|
switch (zoneInfo.type()) {
|
||||||
case ServerInfo_Zone::PrivateZone:
|
case ServerInfo_Zone::PrivateZone:
|
||||||
|
|
@ -172,6 +215,9 @@ void Player::processPlayerInfo(const ServerInfo_Player &info)
|
||||||
case ServerInfo_Zone::HiddenZone:
|
case ServerInfo_Zone::HiddenZone:
|
||||||
contentsKnown = false;
|
contentsKnown = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
zone = addZone(new PileZoneLogic(this, zoneName, false, /* isShufflable */ false, contentsKnown, this));
|
zone = addZone(new PileZoneLogic(this, zoneName, false, /* isShufflable */ false, contentsKnown, this));
|
||||||
|
|
@ -285,6 +331,20 @@ AbstractCounter *Player::addCounter(int counterId, const QString &name, QColor c
|
||||||
AbstractCounter *ctr;
|
AbstractCounter *ctr;
|
||||||
if (name == "life") {
|
if (name == "life") {
|
||||||
ctr = getGraphicsItem()->getPlayerTarget()->addCounter(counterId, name, value);
|
ctr = getGraphicsItem()->getPlayerTarget()->addCounter(counterId, name, value);
|
||||||
|
} else if (counterId == CounterIds::CommanderTax) {
|
||||||
|
QGraphicsItem *parent = graphicsItem->getCommandZoneGraphicsItem();
|
||||||
|
if (!parent) {
|
||||||
|
qCWarning(PlayerLog) << "addCounter: Cannot create CommanderTax counter - command zone not available";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ctr = new CommanderTaxCounter(this, counterId, name, ZoneSizes::TAX_COUNTER_SIZE, value, parent);
|
||||||
|
} else if (counterId == CounterIds::PartnerTax) {
|
||||||
|
QGraphicsItem *parent = graphicsItem->getPartnerZoneGraphicsItem();
|
||||||
|
if (!parent) {
|
||||||
|
qCWarning(PlayerLog) << "addCounter: Cannot create PartnerTax counter - partner zone not available";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ctr = new CommanderTaxCounter(this, counterId, name, ZoneSizes::TAX_COUNTER_SIZE, value, parent);
|
||||||
} else {
|
} else {
|
||||||
ctr = new GeneralCounter(this, counterId, name, color, radius, value, true, graphicsItem);
|
ctr = new GeneralCounter(this, counterId, name, color, radius, value, true, graphicsItem);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "../../game_graphics/board/abstract_graphics_item.h"
|
#include "../../game_graphics/board/abstract_graphics_item.h"
|
||||||
#include "../../interface/widgets/menus/tearoff_menu.h"
|
#include "../../interface/widgets/menus/tearoff_menu.h"
|
||||||
#include "../interface/deck_loader/loaded_deck.h"
|
#include "../interface/deck_loader/loaded_deck.h"
|
||||||
|
#include "../zones/logic/command_zone_logic.h"
|
||||||
#include "../zones/logic/hand_zone_logic.h"
|
#include "../zones/logic/hand_zone_logic.h"
|
||||||
#include "../zones/logic/pile_zone_logic.h"
|
#include "../zones/logic/pile_zone_logic.h"
|
||||||
#include "../zones/logic/stack_zone_logic.h"
|
#include "../zones/logic/stack_zone_logic.h"
|
||||||
|
|
@ -75,6 +76,9 @@ signals:
|
||||||
void clearCustomZonesMenu();
|
void clearCustomZonesMenu();
|
||||||
void addViewCustomZoneActionToCustomZoneMenu(QString zoneName);
|
void addViewCustomZoneActionToCustomZoneMenu(QString zoneName);
|
||||||
void resetTopCardMenuActions();
|
void resetTopCardMenuActions();
|
||||||
|
void commandZoneSupportChanged(bool hasCommandZone);
|
||||||
|
void companionZoneSupportChanged(bool hasCompanionZone);
|
||||||
|
void backgroundZoneSupportChanged(bool hasBackgroundZone);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setActive(bool _active);
|
void setActive(bool _active);
|
||||||
|
|
@ -143,7 +147,7 @@ public:
|
||||||
return zone;
|
return zone;
|
||||||
}
|
}
|
||||||
|
|
||||||
CardZoneLogic *getZone(const QString zoneName)
|
CardZoneLogic *getZone(const QString &zoneName)
|
||||||
{
|
{
|
||||||
return zones.value(zoneName);
|
return zones.value(zoneName);
|
||||||
}
|
}
|
||||||
|
|
@ -188,6 +192,26 @@ public:
|
||||||
return qobject_cast<HandZoneLogic *>(zones.value("hand"));
|
return qobject_cast<HandZoneLogic *>(zones.value("hand"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CommandZoneLogic *getCommandZone()
|
||||||
|
{
|
||||||
|
return qobject_cast<CommandZoneLogic *>(zones.value("command"));
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandZoneLogic *getPartnerZone()
|
||||||
|
{
|
||||||
|
return qobject_cast<CommandZoneLogic *>(zones.value("partner"));
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandZoneLogic *getCompanionZone()
|
||||||
|
{
|
||||||
|
return qobject_cast<CommandZoneLogic *>(zones.value("companion"));
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandZoneLogic *getBackgroundZone()
|
||||||
|
{
|
||||||
|
return qobject_cast<CommandZoneLogic *>(zones.value("background"));
|
||||||
|
}
|
||||||
|
|
||||||
AbstractCounter *addCounter(const ServerInfo_Counter &counter);
|
AbstractCounter *addCounter(const ServerInfo_Counter &counter);
|
||||||
AbstractCounter *addCounter(int counterId, const QString &name, QColor color, int radius, int value);
|
AbstractCounter *addCounter(int counterId, const QString &name, QColor color, int radius, int value);
|
||||||
void delCounter(int counterId);
|
void delCounter(int counterId);
|
||||||
|
|
@ -216,6 +240,21 @@ public:
|
||||||
return conceded;
|
return conceded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasServerCommandZone() const
|
||||||
|
{
|
||||||
|
return serverHasCommandZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasServerCompanionZone() const
|
||||||
|
{
|
||||||
|
return serverHasCompanionZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasServerBackgroundZone() const
|
||||||
|
{
|
||||||
|
return serverHasBackgroundZone;
|
||||||
|
}
|
||||||
|
|
||||||
void setGameStarted();
|
void setGameStarted();
|
||||||
|
|
||||||
void setDialogSemaphore(const bool _active)
|
void setDialogSemaphore(const bool _active)
|
||||||
|
|
@ -249,8 +288,13 @@ private:
|
||||||
QMap<int, ArrowItem *> arrows;
|
QMap<int, ArrowItem *> arrows;
|
||||||
|
|
||||||
bool dialogSemaphore;
|
bool dialogSemaphore;
|
||||||
|
bool serverHasCommandZone;
|
||||||
|
bool serverHasCompanionZone;
|
||||||
|
bool serverHasBackgroundZone;
|
||||||
QList<CardItem *> cardsToDelete;
|
QList<CardItem *> cardsToDelete;
|
||||||
|
|
||||||
|
void updateZoneSupport(bool *flag, void (Player::*signal)(bool), bool found);
|
||||||
|
|
||||||
// void eventConnectionStateChanged(const Event_ConnectionStateChanged &event);
|
// void eventConnectionStateChanged(const Event_ConnectionStateChanged &event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,21 @@
|
||||||
#include "../zones/hand_zone.h"
|
#include "../zones/hand_zone.h"
|
||||||
#include "../zones/logic/view_zone_logic.h"
|
#include "../zones/logic/view_zone_logic.h"
|
||||||
#include "../zones/table_zone.h"
|
#include "../zones/table_zone.h"
|
||||||
|
#include "../zones/zone_names.h"
|
||||||
#include "card_menu_action_type.h"
|
#include "card_menu_action_type.h"
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QPointer>
|
||||||
#include <libcockatrice/card/database/card_database_manager.h>
|
#include <libcockatrice/card/database/card_database_manager.h>
|
||||||
#include <libcockatrice/card/relation/card_relation.h>
|
#include <libcockatrice/card/relation/card_relation.h>
|
||||||
|
#include <libcockatrice/common/counter_ids.h>
|
||||||
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
|
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_change_zone_properties.pb.h>
|
#include <libcockatrice/protocol/pb/command_change_zone_properties.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_create_token.pb.h>
|
#include <libcockatrice/protocol/pb/command_create_token.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
|
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_flip_card.pb.h>
|
#include <libcockatrice/protocol/pb/command_flip_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_game_say.pb.h>
|
#include <libcockatrice/protocol/pb/command_game_say.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_inc_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_mulligan.pb.h>
|
#include <libcockatrice/protocol/pb/command_mulligan.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_reveal_cards.pb.h>
|
#include <libcockatrice/protocol/pb/command_reveal_cards.pb.h>
|
||||||
|
|
@ -32,6 +37,15 @@
|
||||||
// milliseconds in between triggers of the move top cards until action
|
// milliseconds in between triggers of the move top cards until action
|
||||||
static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100;
|
static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100;
|
||||||
|
|
||||||
|
// Lookup table for move-to-zone actions -> zone name strings
|
||||||
|
// Consolidates 4 identical switch cases into data-driven dispatch
|
||||||
|
static const QHash<CardMenuActionType, QString> moveToZoneMap = {
|
||||||
|
{cmMoveToCommandZone, ZoneNames::COMMAND},
|
||||||
|
{cmMoveToPartnerZone, ZoneNames::PARTNER},
|
||||||
|
{cmMoveToCompanionZone, ZoneNames::COMPANION},
|
||||||
|
{cmMoveToBackgroundZone, ZoneNames::BACKGROUND},
|
||||||
|
};
|
||||||
|
|
||||||
PlayerActions::PlayerActions(Player *_player) : player(_player), lastTokenTableRow(0), movingCardsUntil(false)
|
PlayerActions::PlayerActions(Player *_player) : player(_player), lastTokenTableRow(0), movingCardsUntil(false)
|
||||||
{
|
{
|
||||||
moveTopCardTimer = new QTimer(this);
|
moveTopCardTimer = new QTimer(this);
|
||||||
|
|
@ -520,7 +534,13 @@ void PlayerActions::moveOneCardUntil(CardItem *card)
|
||||||
if (isMatch && movingCardsUntilAutoPlay) {
|
if (isMatch && movingCardsUntilAutoPlay) {
|
||||||
// Directly calling playCard will deadlock, since we are already in the middle of processing an event.
|
// Directly calling playCard will deadlock, since we are already in the middle of processing an event.
|
||||||
// Use QTimer::singleShot to queue up the playCard on the event loop.
|
// Use QTimer::singleShot to queue up the playCard on the event loop.
|
||||||
QTimer::singleShot(0, this, [card, this] { playCard(card, false); });
|
// Use QPointer to safely handle the case where the card is deleted before the timer fires.
|
||||||
|
QPointer<CardItem> safeCard = card;
|
||||||
|
QTimer::singleShot(0, this, [safeCard, this] {
|
||||||
|
if (safeCard) {
|
||||||
|
playCard(safeCard, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player->getDeckZone()->getCards().empty() || !card) {
|
if (player->getDeckZone()->getCards().empty() || !card) {
|
||||||
|
|
@ -1280,7 +1300,10 @@ void PlayerActions::actIncPT(int deltaP, int deltaT)
|
||||||
|
|
||||||
QList<const ::google::protobuf::Message *> commandList;
|
QList<const ::google::protobuf::Message *> commandList;
|
||||||
for (const auto &item : player->getGameScene()->selectedItems()) {
|
for (const auto &item : player->getGameScene()->selectedItems()) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
auto *card = qgraphicsitem_cast<CardItem *>(item);
|
||||||
|
if (!card) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
QString pt = card->getPT();
|
QString pt = card->getPT();
|
||||||
const auto ptList = parsePT(pt);
|
const auto ptList = parsePT(pt);
|
||||||
QString newpt;
|
QString newpt;
|
||||||
|
|
@ -1313,7 +1336,10 @@ void PlayerActions::actResetPT()
|
||||||
int playerid = player->getPlayerInfo()->getId();
|
int playerid = player->getPlayerInfo()->getId();
|
||||||
QList<const ::google::protobuf::Message *> commandList;
|
QList<const ::google::protobuf::Message *> commandList;
|
||||||
for (const auto &item : player->getGameScene()->selectedItems()) {
|
for (const auto &item : player->getGameScene()->selectedItems()) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
auto *card = qgraphicsitem_cast<CardItem *>(item);
|
||||||
|
if (!card) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
QString ptString;
|
QString ptString;
|
||||||
if (!card->getFaceDown()) { // leave the pt empty if the card is face down
|
if (!card->getFaceDown()) { // leave the pt empty if the card is face down
|
||||||
ExactCard ec = card->getCard();
|
ExactCard ec = card->getCard();
|
||||||
|
|
@ -1380,8 +1406,8 @@ void PlayerActions::actSetPT()
|
||||||
|
|
||||||
auto sel = player->getGameScene()->selectedItems();
|
auto sel = player->getGameScene()->selectedItems();
|
||||||
for (const auto &item : sel) {
|
for (const auto &item : sel) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
auto *card = qgraphicsitem_cast<CardItem *>(item);
|
||||||
if (!card->getPT().isEmpty()) {
|
if (card && !card->getPT().isEmpty()) {
|
||||||
oldPT = card->getPT();
|
oldPT = card->getPT();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1399,7 +1425,10 @@ void PlayerActions::actSetPT()
|
||||||
|
|
||||||
QList<const ::google::protobuf::Message *> commandList;
|
QList<const ::google::protobuf::Message *> commandList;
|
||||||
for (const auto &item : sel) {
|
for (const auto &item : sel) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
auto *card = qgraphicsitem_cast<CardItem *>(item);
|
||||||
|
if (!card) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto *cmd = new Command_SetCardAttr;
|
auto *cmd = new Command_SetCardAttr;
|
||||||
QString newpt = QString();
|
QString newpt = QString();
|
||||||
if (!empty) {
|
if (!empty) {
|
||||||
|
|
@ -1498,8 +1527,8 @@ void PlayerActions::actSetAnnotation()
|
||||||
QString oldAnnotation;
|
QString oldAnnotation;
|
||||||
auto sel = player->getGameScene()->selectedItems();
|
auto sel = player->getGameScene()->selectedItems();
|
||||||
for (const auto &item : sel) {
|
for (const auto &item : sel) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
auto *card = qgraphicsitem_cast<CardItem *>(item);
|
||||||
if (!card->getAnnotation().isEmpty()) {
|
if (card && !card->getAnnotation().isEmpty()) {
|
||||||
oldAnnotation = card->getAnnotation();
|
oldAnnotation = card->getAnnotation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1519,7 +1548,10 @@ void PlayerActions::actSetAnnotation()
|
||||||
|
|
||||||
QList<const ::google::protobuf::Message *> commandList;
|
QList<const ::google::protobuf::Message *> commandList;
|
||||||
for (const auto &item : sel) {
|
for (const auto &item : sel) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
auto *card = qgraphicsitem_cast<CardItem *>(item);
|
||||||
|
if (!card) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto *cmd = new Command_SetCardAttr;
|
auto *cmd = new Command_SetCardAttr;
|
||||||
cmd->set_zone(card->getZone()->getName().toStdString());
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
||||||
cmd->set_card_id(card->getId());
|
cmd->set_card_id(card->getId());
|
||||||
|
|
@ -1544,9 +1576,8 @@ void PlayerActions::actUnattach()
|
||||||
{
|
{
|
||||||
QList<const ::google::protobuf::Message *> commandList;
|
QList<const ::google::protobuf::Message *> commandList;
|
||||||
for (QGraphicsItem *item : player->getGameScene()->selectedItems()) {
|
for (QGraphicsItem *item : player->getGameScene()->selectedItems()) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
auto *card = qgraphicsitem_cast<CardItem *>(item);
|
||||||
|
if (!card || !card->getAttachedTo()) {
|
||||||
if (!card->getAttachedTo()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1566,8 +1597,8 @@ void PlayerActions::actCardCounterTrigger()
|
||||||
switch (action->data().toInt() % 1000) {
|
switch (action->data().toInt() % 1000) {
|
||||||
case 9: { // increment counter
|
case 9: { // increment counter
|
||||||
for (const auto &item : player->getGameScene()->selectedItems()) {
|
for (const auto &item : player->getGameScene()->selectedItems()) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
auto *card = qgraphicsitem_cast<CardItem *>(item);
|
||||||
if (card->getCounters().value(counterId, 0) < MAX_COUNTERS_ON_CARD) {
|
if (card && card->getCounters().value(counterId, 0) < MAX_COUNTERS_ON_CARD) {
|
||||||
auto *cmd = new Command_SetCardCounter;
|
auto *cmd = new Command_SetCardCounter;
|
||||||
cmd->set_zone(card->getZone()->getName().toStdString());
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
||||||
cmd->set_card_id(card->getId());
|
cmd->set_card_id(card->getId());
|
||||||
|
|
@ -1580,8 +1611,8 @@ void PlayerActions::actCardCounterTrigger()
|
||||||
}
|
}
|
||||||
case 10: { // decrement counter
|
case 10: { // decrement counter
|
||||||
for (const auto &item : player->getGameScene()->selectedItems()) {
|
for (const auto &item : player->getGameScene()->selectedItems()) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
auto *card = qgraphicsitem_cast<CardItem *>(item);
|
||||||
if (card->getCounters().value(counterId, 0)) {
|
if (card && card->getCounters().value(counterId, 0)) {
|
||||||
auto *cmd = new Command_SetCardCounter;
|
auto *cmd = new Command_SetCardCounter;
|
||||||
cmd->set_zone(card->getZone()->getName().toStdString());
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
||||||
cmd->set_card_id(card->getId());
|
cmd->set_card_id(card->getId());
|
||||||
|
|
@ -1598,8 +1629,9 @@ void PlayerActions::actCardCounterTrigger()
|
||||||
|
|
||||||
int oldValue = 0;
|
int oldValue = 0;
|
||||||
if (player->getGameScene()->selectedItems().size() == 1) {
|
if (player->getGameScene()->selectedItems().size() == 1) {
|
||||||
auto *card = static_cast<CardItem *>(player->getGameScene()->selectedItems().first());
|
if (auto *card = qgraphicsitem_cast<CardItem *>(player->getGameScene()->selectedItems().first())) {
|
||||||
oldValue = card->getCounters().value(counterId, 0);
|
oldValue = card->getCounters().value(counterId, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Set counters"), tr("Number:"), oldValue,
|
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Set counters"), tr("Number:"), oldValue,
|
||||||
0, MAX_COUNTERS_ON_CARD, 1, &ok);
|
0, MAX_COUNTERS_ON_CARD, 1, &ok);
|
||||||
|
|
@ -1609,7 +1641,10 @@ void PlayerActions::actCardCounterTrigger()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &item : player->getGameScene()->selectedItems()) {
|
for (const auto &item : player->getGameScene()->selectedItems()) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
auto *card = qgraphicsitem_cast<CardItem *>(item);
|
||||||
|
if (!card) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto *cmd = new Command_SetCardCounter;
|
auto *cmd = new Command_SetCardCounter;
|
||||||
cmd->set_zone(card->getZone()->getName().toStdString());
|
cmd->set_zone(card->getZone()->getName().toStdString());
|
||||||
cmd->set_card_id(card->getId());
|
cmd->set_card_id(card->getId());
|
||||||
|
|
@ -1640,8 +1675,9 @@ void PlayerActions::playSelectedCards(const bool faceDown)
|
||||||
{
|
{
|
||||||
QList<CardItem *> selectedCards;
|
QList<CardItem *> selectedCards;
|
||||||
for (const auto &item : player->getGameScene()->selectedItems()) {
|
for (const auto &item : player->getGameScene()->selectedItems()) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
if (auto *card = qgraphicsitem_cast<CardItem *>(item)) {
|
||||||
selectedCards.append(card);
|
selectedCards.append(card);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CardIds will get shuffled downwards when cards leave the deck.
|
// CardIds will get shuffled downwards when cards leave the deck.
|
||||||
|
|
@ -1667,12 +1703,52 @@ void PlayerActions::actPlayFacedown()
|
||||||
playSelectedCards(true);
|
playSelectedCards(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerActions::actPlayAndIncreaseTax()
|
||||||
|
{
|
||||||
|
QList<CardItem *> selectedCards;
|
||||||
|
for (const auto &item : player->getGameScene()->selectedItems()) {
|
||||||
|
if (auto *card = qgraphicsitem_cast<CardItem *>(item)) {
|
||||||
|
selectedCards.append(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(selectedCards.begin(), selectedCards.end(),
|
||||||
|
[](const auto &card1, const auto &card2) { return card1->getId() > card2->getId(); });
|
||||||
|
|
||||||
|
for (auto &card : selectedCards) {
|
||||||
|
if (!card || isUnwritableRevealZone(card->getZone()) || card->getZone()->getName() == ZoneNames::TABLE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString zoneName = card->getZone()->getName();
|
||||||
|
|
||||||
|
// Only increment tax for cards from command or partner zones
|
||||||
|
if (zoneName == ZoneNames::COMMAND || zoneName == ZoneNames::PARTNER) {
|
||||||
|
// Determine the correct counter ID
|
||||||
|
int counterId = (zoneName == ZoneNames::PARTNER) ? CounterIds::PartnerTax : CounterIds::CommanderTax;
|
||||||
|
|
||||||
|
// Play the card
|
||||||
|
playCard(card, false);
|
||||||
|
|
||||||
|
// Increment the tax counter
|
||||||
|
Command_IncCounter cmd;
|
||||||
|
cmd.set_counter_id(counterId);
|
||||||
|
cmd.set_delta(1);
|
||||||
|
sendGameCommand(cmd);
|
||||||
|
} else {
|
||||||
|
// For non-command zone cards, just play normally
|
||||||
|
playCard(card, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerActions::actHide()
|
void PlayerActions::actHide()
|
||||||
{
|
{
|
||||||
for (const auto &item : player->getGameScene()->selectedItems()) {
|
for (const auto &item : player->getGameScene()->selectedItems()) {
|
||||||
auto *card = static_cast<CardItem *>(item);
|
if (auto *card = qgraphicsitem_cast<CardItem *>(item)) {
|
||||||
if (card && isUnwritableRevealZone(card->getZone())) {
|
if (isUnwritableRevealZone(card->getZone())) {
|
||||||
card->getZone()->removeCard(card);
|
card->getZone()->removeCard(card);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1776,7 +1852,14 @@ void PlayerActions::cardMenuAction()
|
||||||
QList<QGraphicsItem *> sel = player->getGameScene()->selectedItems();
|
QList<QGraphicsItem *> sel = player->getGameScene()->selectedItems();
|
||||||
QList<CardItem *> cardList;
|
QList<CardItem *> cardList;
|
||||||
while (!sel.isEmpty()) {
|
while (!sel.isEmpty()) {
|
||||||
cardList.append(qgraphicsitem_cast<CardItem *>(sel.takeFirst()));
|
CardItem *card = qgraphicsitem_cast<CardItem *>(sel.takeFirst());
|
||||||
|
if (card) {
|
||||||
|
cardList.append(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cardList.isEmpty()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<const ::google::protobuf::Message *> commandList;
|
QList<const ::google::protobuf::Message *> commandList;
|
||||||
|
|
@ -1863,7 +1946,8 @@ void PlayerActions::cardMenuAction()
|
||||||
idList.add_card()->set_card_id(i->getId());
|
idList.add_card()->set_card_id(i->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (static_cast<CardMenuActionType>(a->data().toInt())) {
|
const auto actionType = static_cast<CardMenuActionType>(a->data().toInt());
|
||||||
|
switch (actionType) {
|
||||||
case cmMoveToTopLibrary: {
|
case cmMoveToTopLibrary: {
|
||||||
auto *cmd = new Command_MoveCard;
|
auto *cmd = new Command_MoveCard;
|
||||||
cmd->set_start_player_id(startPlayerId);
|
cmd->set_start_player_id(startPlayerId);
|
||||||
|
|
@ -1944,6 +2028,58 @@ void PlayerActions::cardMenuAction()
|
||||||
commandList.append(cmd);
|
commandList.append(cmd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case cmMoveToCommandZone:
|
||||||
|
case cmMoveToPartnerZone:
|
||||||
|
case cmMoveToCompanionZone:
|
||||||
|
case cmMoveToBackgroundZone: {
|
||||||
|
const QString zoneName = moveToZoneMap.value(actionType);
|
||||||
|
if (zoneName.isEmpty()) {
|
||||||
|
qCWarning(PlayerLog) << "Unknown move-to-zone action:" << static_cast<int>(actionType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
cmd->set_target_zone(zoneName.toStdString());
|
||||||
|
cmd->set_x(0);
|
||||||
|
cmd->set_y(0);
|
||||||
|
commandList.append(cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case cmMoveToTable: {
|
||||||
|
// Process each card individually to position based on card type
|
||||||
|
for (const auto &card : cardList) {
|
||||||
|
auto *cmd = new Command_MoveCard;
|
||||||
|
cmd->set_start_player_id(startPlayerId);
|
||||||
|
cmd->set_start_zone(startZone.toStdString());
|
||||||
|
cmd->set_target_player_id(player->getPlayerInfo()->getId());
|
||||||
|
cmd->set_target_zone("table");
|
||||||
|
|
||||||
|
CardToMove *cardToMove = cmd->mutable_cards_to_move()->add_card();
|
||||||
|
cardToMove->set_card_id(card->getId());
|
||||||
|
|
||||||
|
// Determine correct row based on card type (same logic as playCardToTable)
|
||||||
|
int tableRow = 1; // default for non-creatures
|
||||||
|
ExactCard exactCard = card->getCard();
|
||||||
|
if (exactCard) {
|
||||||
|
const CardInfo &info = exactCard.getInfo();
|
||||||
|
tableRow = info.getUiAttributes().tableRow;
|
||||||
|
// default instant/sorcery cards to the noncreatures row
|
||||||
|
if (tableRow > 2) {
|
||||||
|
tableRow = 1;
|
||||||
|
}
|
||||||
|
cardToMove->set_pt(info.getPowTough().toStdString());
|
||||||
|
cardToMove->set_tapped(info.getUiAttributes().cipt);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd->set_x(-1);
|
||||||
|
cmd->set_y(TableZone::clampValidTableRow(2 - tableRow));
|
||||||
|
commandList.append(cmd);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ public slots:
|
||||||
|
|
||||||
void actPlay();
|
void actPlay();
|
||||||
void actPlayFacedown();
|
void actPlayFacedown();
|
||||||
|
void actPlayAndIncreaseTax();
|
||||||
void actHide();
|
void actHide();
|
||||||
|
|
||||||
void actMoveTopCardToPlay();
|
void actMoveTopCardToPlay();
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,21 @@
|
||||||
#include "player_graphics_item.h"
|
#include "player_graphics_item.h"
|
||||||
|
|
||||||
|
// @see tests/command_zone/counter_visibility_test.cpp - test harness duplicates visibility logic
|
||||||
|
// @see tests/command_zone/command_zone_integration_test.cpp - test harness duplicates counter positioning
|
||||||
|
|
||||||
#include "../../interface/widgets/tabs/tab_game.h"
|
#include "../../interface/widgets/tabs/tab_game.h"
|
||||||
#include "../board/abstract_card_item.h"
|
#include "../board/abstract_card_item.h"
|
||||||
|
#include "../board/commander_tax_counter.h"
|
||||||
#include "../hand_counter.h"
|
#include "../hand_counter.h"
|
||||||
|
#include "../z_values.h"
|
||||||
|
#include "../zones/command_zone.h"
|
||||||
#include "../zones/hand_zone.h"
|
#include "../zones/hand_zone.h"
|
||||||
#include "../zones/pile_zone.h"
|
#include "../zones/pile_zone.h"
|
||||||
#include "../zones/stack_zone.h"
|
#include "../zones/stack_zone.h"
|
||||||
#include "../zones/table_zone.h"
|
#include "../zones/table_zone.h"
|
||||||
|
|
||||||
|
#include <libcockatrice/common/counter_ids.h>
|
||||||
|
|
||||||
PlayerGraphicsItem::PlayerGraphicsItem(Player *_player) : player(_player)
|
PlayerGraphicsItem::PlayerGraphicsItem(Player *_player) : player(_player)
|
||||||
{
|
{
|
||||||
connect(&SettingsCache::instance(), &SettingsCache::horizontalHandChanged, this,
|
connect(&SettingsCache::instance(), &SettingsCache::horizontalHandChanged, this,
|
||||||
|
|
@ -83,6 +91,54 @@ void PlayerGraphicsItem::initializeZones()
|
||||||
handZoneGraphicsItem =
|
handZoneGraphicsItem =
|
||||||
new HandZone(player->getHandZone(), static_cast<int>(tableZoneGraphicsItem->boundingRect().height()), this);
|
new HandZone(player->getHandZone(), static_cast<int>(tableZoneGraphicsItem->boundingRect().height()), this);
|
||||||
|
|
||||||
|
// Command zones - mixed hierarchy:
|
||||||
|
// - Partner is Qt child of Command (tight coupling)
|
||||||
|
// - Companion and Background are Qt siblings (direct children of PlayerGraphicsItem)
|
||||||
|
// with logical parent-child relationships for hierarchy traversal
|
||||||
|
//
|
||||||
|
// OWNERSHIP INVARIANT: All command zones must remain Qt children of either
|
||||||
|
// CommandZone (for Partner) or PlayerGraphicsItem (for Primary, Companion, Background)
|
||||||
|
// for the duration of their lifetime. The logical parent-child relationships
|
||||||
|
// (logicalParentZone, logicalChildZones) are raw pointers that rely on this
|
||||||
|
// ownership guarantee. Do not reparent zones or change destruction order.
|
||||||
|
|
||||||
|
// Primary command zone
|
||||||
|
commandZoneGraphicsItem =
|
||||||
|
new CommandZone(player->getCommandZone(), ZoneSizes::COMMAND_ZONE_HEIGHT, CommandZoneType::Primary, this);
|
||||||
|
commandZoneGraphicsItem->setZValue(ZValues::COMMAND_ZONE);
|
||||||
|
commandZoneGraphicsItem->setVisible(false);
|
||||||
|
connect(commandZoneGraphicsItem, &CommandZone::minimizedChanged, this, &PlayerGraphicsItem::rearrangeZones);
|
||||||
|
|
||||||
|
// Partner zone is Qt child of Command (nested - preserves tight Command-Partner relationship)
|
||||||
|
partnerZoneGraphicsItem = new CommandZone(player->getPartnerZone(), ZoneSizes::COMMAND_ZONE_HEIGHT,
|
||||||
|
CommandZoneType::Partner, commandZoneGraphicsItem);
|
||||||
|
partnerZoneGraphicsItem->setZValue(ZValues::PARTNER_ZONE);
|
||||||
|
connect(partnerZoneGraphicsItem, &CommandZone::expandedChanged, this, &PlayerGraphicsItem::onZoneExpanded);
|
||||||
|
connect(partnerZoneGraphicsItem, &CommandZone::minimizedChanged, this, &PlayerGraphicsItem::rearrangeZones);
|
||||||
|
|
||||||
|
// Companion zone is SIBLING - direct child of PlayerGraphicsItem (fixes spacing bugs)
|
||||||
|
companionZoneGraphicsItem =
|
||||||
|
new CommandZone(player->getCompanionZone(), ZoneSizes::COMMAND_ZONE_HEIGHT, CommandZoneType::Companion, this);
|
||||||
|
companionZoneGraphicsItem->setZValue(ZValues::PARTNER_ZONE + 1);
|
||||||
|
companionZoneGraphicsItem->setVisible(false);
|
||||||
|
connect(companionZoneGraphicsItem, &CommandZone::expandedChanged, this, &PlayerGraphicsItem::onZoneExpanded);
|
||||||
|
connect(companionZoneGraphicsItem, &CommandZone::minimizedChanged, this, &PlayerGraphicsItem::rearrangeZones);
|
||||||
|
|
||||||
|
// Background zone is SIBLING - direct child of PlayerGraphicsItem (fixes spacing bugs)
|
||||||
|
backgroundZoneGraphicsItem =
|
||||||
|
new CommandZone(player->getBackgroundZone(), ZoneSizes::COMMAND_ZONE_HEIGHT, CommandZoneType::Background, this);
|
||||||
|
backgroundZoneGraphicsItem->setZValue(ZValues::PARTNER_ZONE + 2);
|
||||||
|
backgroundZoneGraphicsItem->setVisible(false);
|
||||||
|
connect(backgroundZoneGraphicsItem, &CommandZone::expandedChanged, this, &PlayerGraphicsItem::onZoneExpanded);
|
||||||
|
connect(backgroundZoneGraphicsItem, &CommandZone::minimizedChanged, this, &PlayerGraphicsItem::rearrangeZones);
|
||||||
|
|
||||||
|
// Set up logical parent-child relationships for sibling zones
|
||||||
|
// (used by hierarchy traversal methods like tryMinimizeAbove, shouldPreventCollapse)
|
||||||
|
companionZoneGraphicsItem->setLogicalParentZone(partnerZoneGraphicsItem);
|
||||||
|
backgroundZoneGraphicsItem->setLogicalParentZone(companionZoneGraphicsItem);
|
||||||
|
partnerZoneGraphicsItem->addLogicalChildZone(companionZoneGraphicsItem);
|
||||||
|
companionZoneGraphicsItem->addLogicalChildZone(backgroundZoneGraphicsItem);
|
||||||
|
|
||||||
connect(handZoneGraphicsItem->getLogic(), &HandZoneLogic::cardCountChanged, handCounter,
|
connect(handZoneGraphicsItem->getLogic(), &HandZoneLogic::cardCountChanged, handCounter,
|
||||||
&HandCounter::updateNumber);
|
&HandCounter::updateNumber);
|
||||||
connect(handCounter, &HandCounter::showContextMenu, handZoneGraphicsItem, &HandZone::showContextMenu);
|
connect(handCounter, &HandCounter::showContextMenu, handZoneGraphicsItem, &HandZone::showContextMenu);
|
||||||
|
|
@ -111,7 +167,6 @@ void PlayerGraphicsItem::paint(QPainter * /*painter*/,
|
||||||
|
|
||||||
void PlayerGraphicsItem::processSceneSizeChange(int newPlayerWidth)
|
void PlayerGraphicsItem::processSceneSizeChange(int newPlayerWidth)
|
||||||
{
|
{
|
||||||
// Extend table (and hand, if horizontal) to accommodate the new player width.
|
|
||||||
qreal tableWidth =
|
qreal tableWidth =
|
||||||
newPlayerWidth - CARD_HEIGHT - 15 - counterAreaWidth - stackZoneGraphicsItem->boundingRect().width();
|
newPlayerWidth - CARD_HEIGHT - 15 - counterAreaWidth - stackZoneGraphicsItem->boundingRect().width();
|
||||||
if (!SettingsCache::instance().getHorizontalHand()) {
|
if (!SettingsCache::instance().getHorizontalHand()) {
|
||||||
|
|
@ -136,10 +191,33 @@ void PlayerGraphicsItem::rearrangeCounters()
|
||||||
const qreal padding = 5;
|
const qreal padding = 5;
|
||||||
qreal ySize = boundingRect().y() + marginTop;
|
qreal ySize = boundingRect().y() + marginTop;
|
||||||
|
|
||||||
// Place objects
|
bool commandZonesVisible = commandZoneGraphicsItem->isVisible();
|
||||||
|
|
||||||
for (const auto &counter : player->getCounters()) {
|
for (const auto &counter : player->getCounters()) {
|
||||||
AbstractCounter *ctr = counter;
|
AbstractCounter *ctr = counter;
|
||||||
|
|
||||||
|
if (ctr->getId() == CounterIds::CommanderTax) {
|
||||||
|
if (commandZonesVisible) {
|
||||||
|
ctr->setPos(2, 2);
|
||||||
|
ctr->setZValue(ZValues::TAX_COUNTERS);
|
||||||
|
ctr->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ctr->setVisible(false);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctr->getId() == CounterIds::PartnerTax) {
|
||||||
|
if (commandZonesVisible && partnerZoneGraphicsItem->isExpanded()) {
|
||||||
|
ctr->setPos(2, 2);
|
||||||
|
ctr->setZValue(ZValues::TAX_COUNTERS);
|
||||||
|
ctr->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ctr->setVisible(false);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ctr->getShownInCounterArea()) {
|
if (!ctr->getShownInCounterArea()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -150,9 +228,132 @@ void PlayerGraphicsItem::rearrangeCounters()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qreal PlayerGraphicsItem::positionCommandZones(const QPointF &base, bool commandZonesVisible)
|
||||||
|
{
|
||||||
|
qreal runningY = 0;
|
||||||
|
|
||||||
|
// Position command/partner zones only if command zone is visible
|
||||||
|
if (commandZonesVisible) {
|
||||||
|
commandZoneGraphicsItem->setPos(base);
|
||||||
|
partnerZoneGraphicsItem->setPos(0, commandZoneGraphicsItem->currentHeight());
|
||||||
|
runningY = commandZoneGraphicsItem->currentHeight() + partnerZoneGraphicsItem->currentHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Companion and background are Qt siblings - position based on their own visibility
|
||||||
|
bool companionZoneEnabled = companionZoneGraphicsItem->isVisible();
|
||||||
|
bool backgroundZoneEnabled = backgroundZoneGraphicsItem->isVisible();
|
||||||
|
|
||||||
|
if (companionZoneEnabled) {
|
||||||
|
companionZoneGraphicsItem->setPos(base.x(), base.y() + runningY);
|
||||||
|
runningY += companionZoneGraphicsItem->currentHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backgroundZoneEnabled) {
|
||||||
|
backgroundZoneGraphicsItem->setPos(base.x(), base.y() + runningY);
|
||||||
|
runningY += backgroundZoneGraphicsItem->currentHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle visibility rules with availability guards:
|
||||||
|
// A zone's toggle is ONLY visible if:
|
||||||
|
// 1. The zone itself is enabled (Qt visible), AND
|
||||||
|
// 2. Either the zone is open OR it's the "next available" to open
|
||||||
|
//
|
||||||
|
// Truth table for entry point (game start, nothing open):
|
||||||
|
// | Cmd Enabled | Comp Enabled | Bg Enabled | Entry Point Toggle |
|
||||||
|
// |-------------|--------------|------------|--------------------|
|
||||||
|
// | Y | Y | Y | Partner |
|
||||||
|
// | Y | N | Y | Partner |
|
||||||
|
// | N | Y | Y | Companion |
|
||||||
|
// | N | Y | N | Companion |
|
||||||
|
// | N | N | Y | Background |
|
||||||
|
if (commandZonesVisible || companionZoneEnabled || backgroundZoneEnabled) {
|
||||||
|
bool commandZoneEnabled = commandZonesVisible;
|
||||||
|
|
||||||
|
bool partnerCollapsed = partnerZoneGraphicsItem->isCollapsed();
|
||||||
|
bool companionCollapsed = companionZoneGraphicsItem->isCollapsed();
|
||||||
|
bool backgroundCollapsed = backgroundZoneGraphicsItem->isCollapsed();
|
||||||
|
|
||||||
|
bool partnerIsOpen = !partnerCollapsed;
|
||||||
|
bool companionIsOpen = !companionCollapsed;
|
||||||
|
bool backgroundIsOpen = !backgroundCollapsed;
|
||||||
|
bool noDeeperZonesOpen = companionCollapsed && backgroundCollapsed;
|
||||||
|
|
||||||
|
// Partner toggle: visible when open OR at game start (no deeper zones open)
|
||||||
|
partnerZoneGraphicsItem->setToggleButtonVisible(commandZoneEnabled && (partnerIsOpen || noDeeperZonesOpen));
|
||||||
|
|
||||||
|
// Companion toggle: visible when open OR when it's next in cascade OR entry point
|
||||||
|
companionZoneGraphicsItem->setToggleButtonVisible(
|
||||||
|
companionZoneEnabled &&
|
||||||
|
(companionIsOpen || (partnerIsOpen && backgroundCollapsed) || (!commandZoneEnabled && noDeeperZonesOpen)));
|
||||||
|
|
||||||
|
// Background toggle: visible when open OR parent open OR skipping disabled companion
|
||||||
|
backgroundZoneGraphicsItem->setToggleButtonVisible(
|
||||||
|
backgroundZoneEnabled && (backgroundIsOpen || companionIsOpen || (partnerIsOpen && !companionZoneEnabled) ||
|
||||||
|
(!commandZoneEnabled && !companionZoneEnabled)));
|
||||||
|
}
|
||||||
|
|
||||||
|
stackZoneGraphicsItem->setPos(base + QPointF(0, runningY));
|
||||||
|
|
||||||
|
return runningY;
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerGraphicsItem::rearrangeZones()
|
void PlayerGraphicsItem::rearrangeZones()
|
||||||
{
|
{
|
||||||
auto base = QPointF(CARD_HEIGHT + counterAreaWidth + 15, 0);
|
auto base = QPointF(CARD_HEIGHT + counterAreaWidth + 15, 0);
|
||||||
|
|
||||||
|
bool commandZoneEnabled = commandZoneGraphicsItem->isVisible();
|
||||||
|
bool companionZoneEnabled = companionZoneGraphicsItem->isVisible();
|
||||||
|
bool backgroundZoneEnabled = backgroundZoneGraphicsItem->isVisible();
|
||||||
|
bool anyCommandFamilyZonesVisible = commandZoneEnabled || companionZoneEnabled || backgroundZoneEnabled;
|
||||||
|
|
||||||
|
// Calculate available stack height, accounting for command zones if present
|
||||||
|
qreal tableHeight = tableZoneGraphicsItem->boundingRect().height();
|
||||||
|
qreal stackHeight = tableHeight;
|
||||||
|
if (anyCommandFamilyZonesVisible) {
|
||||||
|
qreal totalCommandHeight = totalCommandZoneHeight();
|
||||||
|
|
||||||
|
// Safety-net auto-minimize for non-expansion triggers (window resize, etc.)
|
||||||
|
// Most overflow is now handled at expansion time by onZoneExpanded()
|
||||||
|
constexpr int MAX_MINIMIZE_ITERATIONS = 3;
|
||||||
|
int iterations = 0;
|
||||||
|
while (totalCommandHeight > tableHeight - ZoneSizes::MINIMUM_STACK_HEIGHT &&
|
||||||
|
iterations < MAX_MINIMIZE_ITERATIONS) {
|
||||||
|
bool minimized = false;
|
||||||
|
|
||||||
|
// Try command zone hierarchy first (includes partner via logical children)
|
||||||
|
if (commandZoneEnabled) {
|
||||||
|
minimized = commandZoneGraphicsItem->tryAutoMinimize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to companion/background directly when command zone is disabled.
|
||||||
|
// These zones use toggleMinimized() directly since they don't have the same
|
||||||
|
// hierarchical auto-minimize traversal that command zone provides.
|
||||||
|
if (!minimized && companionZoneEnabled && companionZoneGraphicsItem->isExpanded() &&
|
||||||
|
!companionZoneGraphicsItem->isMinimized()) {
|
||||||
|
companionZoneGraphicsItem->toggleMinimized();
|
||||||
|
minimized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!minimized && backgroundZoneEnabled && backgroundZoneGraphicsItem->isExpanded() &&
|
||||||
|
!backgroundZoneGraphicsItem->isMinimized()) {
|
||||||
|
backgroundZoneGraphicsItem->toggleMinimized();
|
||||||
|
minimized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!minimized)
|
||||||
|
break;
|
||||||
|
|
||||||
|
totalCommandHeight = totalCommandZoneHeight();
|
||||||
|
++iterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
stackHeight = tableHeight - totalCommandHeight;
|
||||||
|
if (stackHeight < ZoneSizes::MINIMUM_STACK_HEIGHT) {
|
||||||
|
stackHeight = ZoneSizes::MINIMUM_STACK_HEIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stackZoneGraphicsItem->setHeight(stackHeight);
|
||||||
|
|
||||||
if (SettingsCache::instance().getHorizontalHand()) {
|
if (SettingsCache::instance().getHorizontalHand()) {
|
||||||
if (mirrored) {
|
if (mirrored) {
|
||||||
if (player->getHandZone()->contentsKnown()) {
|
if (player->getHandZone()->contentsKnown()) {
|
||||||
|
|
@ -163,12 +364,12 @@ void PlayerGraphicsItem::rearrangeZones()
|
||||||
player->getPlayerInfo()->setHandVisible(false);
|
player->getPlayerInfo()->setHandVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
stackZoneGraphicsItem->setPos(base);
|
positionCommandZones(base, commandZoneEnabled);
|
||||||
base += QPointF(stackZoneGraphicsItem->boundingRect().width(), 0);
|
base += QPointF(stackZoneGraphicsItem->boundingRect().width(), 0);
|
||||||
|
|
||||||
tableZoneGraphicsItem->setPos(base);
|
tableZoneGraphicsItem->setPos(base);
|
||||||
} else {
|
} else {
|
||||||
stackZoneGraphicsItem->setPos(base);
|
positionCommandZones(base, commandZoneEnabled);
|
||||||
|
|
||||||
tableZoneGraphicsItem->setPos(base.x() + stackZoneGraphicsItem->boundingRect().width(), 0);
|
tableZoneGraphicsItem->setPos(base.x() + stackZoneGraphicsItem->boundingRect().width(), 0);
|
||||||
base += QPointF(0, tableZoneGraphicsItem->boundingRect().height());
|
base += QPointF(0, tableZoneGraphicsItem->boundingRect().height());
|
||||||
|
|
@ -188,7 +389,7 @@ void PlayerGraphicsItem::rearrangeZones()
|
||||||
handZoneGraphicsItem->setPos(base);
|
handZoneGraphicsItem->setPos(base);
|
||||||
base += QPointF(handZoneGraphicsItem->boundingRect().width(), 0);
|
base += QPointF(handZoneGraphicsItem->boundingRect().width(), 0);
|
||||||
|
|
||||||
stackZoneGraphicsItem->setPos(base);
|
positionCommandZones(base, commandZoneEnabled);
|
||||||
base += QPointF(stackZoneGraphicsItem->boundingRect().width(), 0);
|
base += QPointF(stackZoneGraphicsItem->boundingRect().width(), 0);
|
||||||
|
|
||||||
tableZoneGraphicsItem->setPos(base);
|
tableZoneGraphicsItem->setPos(base);
|
||||||
|
|
@ -217,4 +418,93 @@ void PlayerGraphicsItem::updateBoundingRect()
|
||||||
playerArea->setSize(CARD_HEIGHT + counterAreaWidth + 15, bRect.height());
|
playerArea->setSize(CARD_HEIGHT + counterAreaWidth + 15, bRect.height());
|
||||||
|
|
||||||
emit sizeChanged();
|
emit sizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qreal PlayerGraphicsItem::totalCommandZoneHeight() const
|
||||||
|
{
|
||||||
|
qreal total = 0;
|
||||||
|
|
||||||
|
// Command zone and partner use Qt parent-child - checking command visibility
|
||||||
|
// implicitly covers partner (Qt hides children when parent is hidden)
|
||||||
|
if (commandZoneGraphicsItem->isVisible()) {
|
||||||
|
total += commandZoneGraphicsItem->currentHeight();
|
||||||
|
if (partnerZoneGraphicsItem->isExpanded()) {
|
||||||
|
total += partnerZoneGraphicsItem->currentHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sibling zones have independent visibility - check each one
|
||||||
|
if (companionZoneGraphicsItem->isVisible()) {
|
||||||
|
total += companionZoneGraphicsItem->currentHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backgroundZoneGraphicsItem->isVisible()) {
|
||||||
|
total += backgroundZoneGraphicsItem->currentHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerGraphicsItem::onZoneExpanded(bool expanded)
|
||||||
|
{
|
||||||
|
if (!expanded) {
|
||||||
|
rearrangeZones();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *expandedZone = qobject_cast<CommandZone *>(sender());
|
||||||
|
if (expandedZone == nullptr) {
|
||||||
|
rearrangeZones();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal tableHeight = tableZoneGraphicsItem->boundingRect().height();
|
||||||
|
qreal totalHeight = totalCommandZoneHeight();
|
||||||
|
|
||||||
|
constexpr int MAX_MINIMIZE_ITERATIONS = 3;
|
||||||
|
int iterations = 0;
|
||||||
|
while (totalHeight > tableHeight - ZoneSizes::MINIMUM_STACK_HEIGHT && iterations < MAX_MINIMIZE_ITERATIONS) {
|
||||||
|
if (!expandedZone->tryMinimizeAbove())
|
||||||
|
break;
|
||||||
|
|
||||||
|
totalHeight = totalCommandZoneHeight();
|
||||||
|
++iterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
rearrangeZones();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerGraphicsItem::setCommandZonesVisible(bool visible)
|
||||||
|
{
|
||||||
|
if (commandZoneGraphicsItem) {
|
||||||
|
commandZoneGraphicsItem->setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset partner zone to collapsed state when hiding
|
||||||
|
if (!visible && partnerZoneGraphicsItem) {
|
||||||
|
partnerZoneGraphicsItem->setExpanded(false);
|
||||||
|
}
|
||||||
|
rearrangeZones();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerGraphicsItem::setCompanionZoneVisible(bool visible)
|
||||||
|
{
|
||||||
|
if (companionZoneGraphicsItem) {
|
||||||
|
companionZoneGraphicsItem->setVisible(visible);
|
||||||
|
if (!visible) {
|
||||||
|
companionZoneGraphicsItem->setExpanded(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rearrangeZones();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerGraphicsItem::setBackgroundZoneVisible(bool visible)
|
||||||
|
{
|
||||||
|
if (backgroundZoneGraphicsItem) {
|
||||||
|
backgroundZoneGraphicsItem->setVisible(visible);
|
||||||
|
if (!visible) {
|
||||||
|
backgroundZoneGraphicsItem->setExpanded(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rearrangeZones();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* @file player_graphics_item.h
|
* @file player_graphics_item.h
|
||||||
* @ingroup GameGraphicsPlayers
|
* @ingroup GameGraphicsPlayers
|
||||||
* @brief TODO: Document this.
|
* @brief Root QGraphicsObject container that owns and lays out all visual game elements for a single player.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef COCKATRICE_PLAYER_GRAPHICS_ITEM_H
|
#ifndef COCKATRICE_PLAYER_GRAPHICS_ITEM_H
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <QGraphicsObject>
|
#include <QGraphicsObject>
|
||||||
|
|
||||||
|
class CommandZone;
|
||||||
class HandZone;
|
class HandZone;
|
||||||
class PileZone;
|
class PileZone;
|
||||||
class PlayerTarget;
|
class PlayerTarget;
|
||||||
|
|
@ -18,6 +19,26 @@ class StackZone;
|
||||||
class TableZone;
|
class TableZone;
|
||||||
class ZoneViewZone;
|
class ZoneViewZone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The top-level graphics item representing a single player's entire board state in the game scene.
|
||||||
|
*
|
||||||
|
* PlayerGraphicsItem owns and manages every zone graphics item belonging to one player:
|
||||||
|
* the table (battlefield), hand, deck, graveyard, removed-from-game, sideboard, stack,
|
||||||
|
* and command/partner zones. It also owns the player avatar target widget, the background
|
||||||
|
* PlayerArea, and the hand card counter.
|
||||||
|
*
|
||||||
|
* Layout responsibilities include:
|
||||||
|
* - Positioning all zones relative to one another based on hand orientation
|
||||||
|
* (horizontal or vertical) and whether the view is mirrored (opponent's perspective).
|
||||||
|
* - Responding to scene width changes by resizing the table and hand zones proportionally.
|
||||||
|
* - Managing the 55-pixel left counter strip where life totals and other counters are displayed.
|
||||||
|
* - Showing or hiding command/partner zones when the server signals Commander format support.
|
||||||
|
*
|
||||||
|
* This item emits sizeChanged() whenever its bounding rect changes, which GameScene uses
|
||||||
|
* to reflow the positions of all players in the scene.
|
||||||
|
*
|
||||||
|
* @see PlayerArea, PlayerTarget, TableZone, HandZone, PileZone, StackZone, CommandZone
|
||||||
|
*/
|
||||||
class PlayerGraphicsItem : public QGraphicsObject
|
class PlayerGraphicsItem : public QGraphicsObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
@ -99,10 +120,29 @@ public:
|
||||||
{
|
{
|
||||||
return handZoneGraphicsItem;
|
return handZoneGraphicsItem;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] CommandZone *getCommandZoneGraphicsItem() const
|
||||||
|
{
|
||||||
|
return commandZoneGraphicsItem;
|
||||||
|
}
|
||||||
|
[[nodiscard]] CommandZone *getPartnerZoneGraphicsItem() const
|
||||||
|
{
|
||||||
|
return partnerZoneGraphicsItem;
|
||||||
|
}
|
||||||
|
[[nodiscard]] CommandZone *getCompanionZoneGraphicsItem() const
|
||||||
|
{
|
||||||
|
return companionZoneGraphicsItem;
|
||||||
|
}
|
||||||
|
[[nodiscard]] CommandZone *getBackgroundZoneGraphicsItem() const
|
||||||
|
{
|
||||||
|
return backgroundZoneGraphicsItem;
|
||||||
|
}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onPlayerActiveChanged(bool _active);
|
void onPlayerActiveChanged(bool _active);
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
void setCommandZonesVisible(bool visible);
|
||||||
|
void setCompanionZoneVisible(bool visible);
|
||||||
|
void setBackgroundZoneVisible(bool visible);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sizeChanged();
|
void sizeChanged();
|
||||||
|
|
@ -119,13 +159,53 @@ private:
|
||||||
TableZone *tableZoneGraphicsItem;
|
TableZone *tableZoneGraphicsItem;
|
||||||
StackZone *stackZoneGraphicsItem;
|
StackZone *stackZoneGraphicsItem;
|
||||||
HandZone *handZoneGraphicsItem;
|
HandZone *handZoneGraphicsItem;
|
||||||
|
CommandZone *commandZoneGraphicsItem;
|
||||||
|
CommandZone *partnerZoneGraphicsItem;
|
||||||
|
CommandZone *companionZoneGraphicsItem;
|
||||||
|
CommandZone *backgroundZoneGraphicsItem;
|
||||||
QRectF bRect;
|
QRectF bRect;
|
||||||
bool mirrored;
|
bool mirrored;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Positions command zones and calculates stack zone offset.
|
||||||
|
*
|
||||||
|
* This helper method centralizes the command zone positioning logic that
|
||||||
|
* was previously duplicated across multiple layout branches. It positions
|
||||||
|
* the command zone at the given base position, positions the partner zone
|
||||||
|
* relative to it, and returns the Y offset where the stack zone should start.
|
||||||
|
*
|
||||||
|
* @param base The position for the command zone
|
||||||
|
* @param commandZonesVisible Whether command zones are enabled
|
||||||
|
* @return Y offset from base where stack zone should be positioned
|
||||||
|
*/
|
||||||
|
qreal positionCommandZones(const QPointF &base, bool commandZonesVisible);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Calculates the total height of all visible command zones.
|
||||||
|
*
|
||||||
|
* Sums the current heights of the primary command zone plus any
|
||||||
|
* expanded partner, companion, and background zones.
|
||||||
|
*
|
||||||
|
* @return Total height in pixels of all visible command zones
|
||||||
|
*/
|
||||||
|
[[nodiscard]] qreal totalCommandZoneHeight() const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateBoundingRect();
|
void updateBoundingRect();
|
||||||
void rearrangeZones();
|
void rearrangeZones();
|
||||||
void rearrangeCounters();
|
void rearrangeCounters();
|
||||||
|
/**
|
||||||
|
* @brief Handles zone expansion, minimizing the zone above if space is needed.
|
||||||
|
*
|
||||||
|
* Uses sender() to identify which zone triggered the expansion. When a zone
|
||||||
|
* expands and there isn't enough vertical space, this slot calls tryMinimizeAbove()
|
||||||
|
* to minimize the zone above instead of the newly-opened zone.
|
||||||
|
*
|
||||||
|
* @param expanded True if the zone is expanding, false if collapsing
|
||||||
|
*/
|
||||||
|
void onZoneExpanded(bool expanded);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COCKATRICE_PLAYER_GRAPHICS_ITEM_H
|
#endif // COCKATRICE_PLAYER_GRAPHICS_ITEM_H
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue