Adjust to rebase.

Took 28 minutes

Took 7 seconds

Took 2 minutes

Took 8 minutes

Took 13 seconds
This commit is contained in:
Lukas Brübach 2026-06-09 09:26:53 +02:00 committed by DawnFire42
parent 75d59e2d82
commit 8447c73ec5
No known key found for this signature in database
GPG key ID: 24BB855EE2911B33
16 changed files with 50 additions and 48 deletions

View file

@ -0,0 +1,60 @@
#include "commander_tax_counter.h"
#include "../../game/board/counter_state.h"
#include "translate_counter_name.h"
#include <QColor>
#include <QFontDatabase>
#include <QPainter>
static constexpr qreal CORNER_RADIUS = 4.0;
static constexpr qreal FONT_SIZE_RATIO = 0.6;
static constexpr int OVERLAY_ALPHA = 191;
static const QColor OVERLAY_BG_NORMAL{40, 40, 40, OVERLAY_ALPHA};
static const QColor OVERLAY_BG_HOVERED{70, 70, 70, OVERLAY_ALPHA};
CommanderTaxCounter::CommanderTaxCounter(CounterState *state, PlayerLogic *player, QGraphicsItem *parent)
: AbstractCounter(state, player, false, false, parent), size(TaxCounterSizes::TAX_COUNTER_SIZE)
{
setCacheMode(DeviceCoordinateCache);
setAcceptHoverEvents(true);
setCursor(Qt::ArrowCursor);
setToolTip(tr("%1: %2").arg(TranslateCounterName::getDisplayName(getName())).arg(getValue()));
}
QRectF CommanderTaxCounter::boundingRect() const
{
return QRectF(0, 0, size, size);
}
void CommanderTaxCounter::paint(QPainter *painter,
[[maybe_unused]] const QStyleOptionGraphicsItem *option,
[[maybe_unused]] QWidget *widget)
{
painter->save();
QRectF rect = boundingRect().adjusted(1, 1, -1, -1);
QColor bgColor = hovered ? OVERLAY_BG_HOVERED : OVERLAY_BG_NORMAL;
painter->setPen(Qt::NoPen);
painter->setBrush(bgColor);
painter->drawRoundedRect(rect, CORNER_RADIUS, CORNER_RADIUS);
QFont f = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
f.setPixelSize(static_cast<int>(size * FONT_SIZE_RATIO));
f.setWeight(QFont::Bold);
painter->setFont(f);
painter->setPen(Qt::white);
painter->drawText(rect, Qt::AlignCenter, QString::number(value));
painter->restore();
}
void CommanderTaxCounter::setValue(int _value)
{
int clampedValue = qMax(0, _value);
AbstractCounter::setValue(clampedValue);
setToolTip(tr("%1: %2").arg(TranslateCounterName::getDisplayName(getName())).arg(clampedValue));
}

View file

@ -0,0 +1,72 @@
/**
* @file commander_tax_counter.h
* @ingroup GameGraphicsPlayers
* @brief Square counter for commander tax, clamped to non-negative values.
*/
#ifndef COCKATRICE_COMMANDER_TAX_COUNTER_H
#define COCKATRICE_COMMANDER_TAX_COUNTER_H
#include "abstract_counter.h"
/**
* @namespace TaxCounterSizes
* @brief Size constants for commander tax counter layout.
*/
namespace TaxCounterSizes
{
/** @brief Size of commander tax counter icons (width and height) */
constexpr int TAX_COUNTER_SIZE = 24;
/** @brief Margin around and between tax counter icons */
constexpr int TAX_COUNTER_MARGIN = 2;
} // namespace TaxCounterSizes
/**
* @class CommanderTaxCounter
* @brief Counter for tracking commander tax in Commander format.
*
* Displays cumulative cost increase for casting a commander. The counter
* is manually adjusted by the player to track their commander tax. Values
* are clamped to >= 0.
*
* Appearance: square with rounded corners, semi-transparent background,
* positioned at top-left of command zone.
*
* Two instances per player: CounterIds::CommanderTax and CounterIds::PartnerTax.
* Each counter supports an active/inactive state (inherited from AbstractCounter):
* commander tax starts active; partner tax starts inactive until explicitly
* enabled by the player via the context menu.
*
* @see AbstractCounter
* @see AbstractCounter::setActive()
* @see CounterIds
*/
class CommanderTaxCounter : public AbstractCounter
{
Q_OBJECT
private:
int size;
public:
/**
* @brief Constructs a CommanderTaxCounter.
* @param state Counter state containing id, name, value, etc.
* @param player The player who owns this counter
* @param parent Parent graphics item (typically the command zone)
*/
CommanderTaxCounter(CounterState *state, PlayerLogic *player, QGraphicsItem *parent = nullptr);
[[nodiscard]] QRectF boundingRect() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
/**
* @brief Overrides AbstractCounter::setValue to clamp values to >= 0 and update the tooltip.
* @param _value New value (clamped if negative)
*/
void setValue(int _value) override;
};
#endif // COCKATRICE_COMMANDER_TAX_COUNTER_H

View file

@ -83,6 +83,8 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _sho
aUnattach = makeAction(this, [actions, sel]() { actions->actUnattach(sel()); });
aSetAnnotation = makeAction(this, [actions, sel]() { actions->actRequestSetAnnotationDialog(sel()); });
aPlay = makeAction(this, [actions, sel]() { actions->actPlay(sel()); });
aPlayAndIncreaseTax = makeAction(this, [actions, sel]() { actions->actPlayAndIncreaseTax(sel()); });
aPlayAndIncreasePartnerTax = makeAction(this, [actions, sel]() { actions->actPlayAndIncreasePartnerTax(sel()); });
aPlayFacedown = makeAction(this, [actions, sel]() { actions->actPlayFacedown(sel()); });
aHide = makeAction(this, [actions, sel]() { actions->actHide(sel()); });
aReduceLifeByPower = makeAction(this, [actions, sel]() { actions->actReduceLifeByPower(sel()); });
@ -94,12 +96,6 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _sho
aSelectRow = new QAction(this);
aSelectColumn = new QAction(this);
aPlayAndIncreaseTax = new QAction(this);
connect(aPlayAndIncreaseTax, &QAction::triggered, playerActions, &PlayerActions::actPlayAndIncreaseTax);
aPlayAndIncreasePartnerTax = new QAction(this);
connect(aPlayAndIncreasePartnerTax, &QAction::triggered, playerActions,
&PlayerActions::actPlayAndIncreasePartnerTax);
connect(aAttach, &QAction::triggered, actions, &PlayerActions::actAttach);
connect(aDrawArrow, &QAction::triggered, actions, &PlayerActions::actDrawArrow);
connect(aSelectAll, &QAction::triggered, actions, &PlayerActions::actSelectAll);

View file

@ -2,11 +2,11 @@
#include "../../../client/settings/cache_settings.h"
#include "../../board/abstract_counter.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../../game_scene.h"
#include "../../zones/command_zone.h"
#include "../player_actions.h"
#include "../player_graphics_item.h"
#include "../player_logic.h"
#include <libcockatrice/utility/counter_ids.h>
#include <libcockatrice/utility/zone_names.h>
@ -21,7 +21,7 @@ CommandZoneMenu::CommandZoneMenu(PlayerLogic *_player, QMenu *playerMenu) : QMen
aViewZone = new QAction(this);
connect(aViewZone, &QAction::triggered, this,
[this]() { player->getGameScene()->toggleZoneView(player, ZoneNames::COMMAND, -1); });
[this]() { emit player->requestZoneViewToggle(player, ZoneNames::COMMAND, -1, false); });
if (player->getPlayerInfo()->getLocalOrJudge()) {
addAction(aViewZone);
@ -106,10 +106,11 @@ void CommandZoneMenu::retranslateUi()
void CommandZoneMenu::actToggleMinimized()
{
CommandZone *zone = player->getGraphicsItem()->getCommandZoneGraphicsItem();
// TODO
/*CommandZone *zone = player->getGraphicsItem()->getCommandZoneGraphicsItem();
if (zone) {
zone->toggleMinimized();
}
}*/
}
void CommandZoneMenu::updateTaxCounterActionStates()

View file

@ -33,14 +33,15 @@ PlayerMenu::PlayerMenu(PlayerGraphicsItem *_player) : QObject(_player), player(_
if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) {
sideboardMenu = addManagedMenu<SideboardMenu>(player, playerMenu);
commandZoneMenu = addManagedMenu<CommandZoneMenu>(player, playerMenu);
// TODO
/*commandZoneMenu = addManagedMenu<CommandZoneMenu>(player, playerMenu);
auto updateCommandZoneMenuVisibility = [this](bool has) {
if (commandZoneMenu) {
commandZoneMenu->menuAction()->setVisible(has);
}
};
connect(player, &PlayerLogic::commandZoneSupportChanged, this, updateCommandZoneMenuVisibility);
updateCommandZoneMenuVisibility(player->hasServerCommandZone());
updateCommandZoneMenuVisibility(player->hasServerCommandZone());*/
customZonesMenu = addManagedMenu<CustomZoneMenu>(player);
playerMenu->addSeparator();

View file

@ -8,7 +8,7 @@
#define COCKATRICE_PLAYER_MENU_H
#include "../../../interface/widgets/menus/tearoff_menu.h"
#include "../player_logic.h"
#include "../../game/player/player_logic.h"
#include "command_zone_menu.h"
#include "custom_zone_menu.h"
#include "grave_menu.h"

View file

@ -6,6 +6,8 @@
#include "../board/commander_tax_counter.h"
#include "../board/counter_general.h"
#include "../hand_counter.h"
#include "../z_values.h"
#include "../zones/command_zone.h"
#include "../zones/hand_zone.h"
#include "../zones/pile_zone.h"
#include "../zones/stack_zone.h"
@ -14,9 +16,6 @@
#include "player_dialogs.h"
#include <QGraphicsView>
#include "../z_values.h"
#include "../zones/command_zone.h"
#include <libcockatrice/utility/counter_ids.h>
PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player)
@ -66,6 +65,7 @@ PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player)
connect(player, &PlayerLogic::addViewCustomZoneActionToCustomZoneMenu, this,
&PlayerGraphicsItem::onCustomZoneAdded);
connect(player, &PlayerLogic::commandZoneSupportChanged, this, &PlayerGraphicsItem::setCommandZoneVisible);
playerMenu->setMenusForGraphicItems();

View file

@ -0,0 +1,177 @@
#include "command_zone.h"
#include "../../client/settings/cache_settings.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../../interface/theme_manager.h"
#include "../board/card_drag_item.h"
#include "../board/card_item.h"
#include "../board/commander_tax_counter.h"
#include "../z_values.h"
#include "select_zone.h"
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
#include <libcockatrice/utility/counter_ids.h>
CommandZone::CommandZone(CommandZoneLogic *_logic, int _zoneHeight, QGraphicsItem *parent)
: SelectZone(_logic, parent), zoneHeight(_zoneHeight)
{
connect(themeManager, &ThemeManager::themeChanged, this, &CommandZone::updateBg);
updateBg();
setCacheMode(DeviceCoordinateCache);
setupClipContainer(ZValues::CARD_BASE);
}
void CommandZone::updateBg()
{
update();
}
QRectF CommandZone::boundingRect() const
{
return {0, 0, ZoneSizes::COMMAND_ZONE_WIDTH, currentHeight()};
}
qreal CommandZone::currentHeight() const
{
return minimized ? qMax(zoneHeight * MINIMIZED_HEIGHT_RATIO, static_cast<double>(minimumHeight)) : zoneHeight;
}
void CommandZone::setMinimumHeight(int height)
{
if (minimumHeight == height) {
return;
}
minimumHeight = height;
prepareGeometryChange();
updateClipRect();
reorganizeCards();
update();
// NOTE: Do NOT emit minimizedChanged here. The minimized STATE has not changed,
// only the minimum height constraint. Emitting here causes an infinite loop:
// rearrangeZones -> rearrangeCounters -> rearrangeTaxCounters -> setMinimumHeight
// -> minimizedChanged -> rearrangeZones (loop!)
}
bool CommandZone::isMinimized() const
{
return minimized;
}
void CommandZone::toggleMinimized()
{
minimized = !minimized;
prepareGeometryChange();
updateClipRect();
reorganizeCards();
update();
emit minimizedChanged(minimized);
}
void CommandZone::paint(QPainter *painter,
[[maybe_unused]] const QStyleOptionGraphicsItem *option,
[[maybe_unused]] QWidget *widget)
{
QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Command, getLogic()->getPlayer()->getZoneId());
QPointF scenePos = mapToScene(QPointF(0, 0));
painter->setBrushOrigin(-scenePos);
painter->fillRect(boundingRect(), brush);
}
void CommandZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
CardZoneLogic *startZone,
const QPoint &dropPoint)
{
if (startZone == nullptr || startZone->getPlayer() == nullptr || dragItems.isEmpty()) {
return;
}
int index = calcDropIndexFromY(dropPoint.y(), MIN_CARD_VISIBLE);
// Same-zone no-op: don't move a card onto itself
const auto &cards = getLogic()->getCards();
if (!cards.isEmpty() && startZone == getLogic() && cards.at(index)->getId() == dragItems.at(0)->getId()) {
return;
}
Command_MoveCard cmd;
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
cmd.set_start_zone(startZone->getName().toStdString());
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
cmd.set_target_zone(getLogic()->getName().toStdString());
cmd.set_x(index);
cmd.set_y(0);
for (const CardDragItem *item : dragItems) {
if (item) {
auto *cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(item->getId());
if (item->isForceFaceDown()) {
cardToMove->set_face_down(true);
}
}
}
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
}
void CommandZone::reorganizeCards()
{
restoreStaleEscapedCards();
updateClipRect();
const auto &cards = getLogic()->getCards();
if (cards.isEmpty()) {
update();
return;
}
auto params = buildStackParams(MIN_CARD_VISIBLE);
params.allowBottomOverflow = true;
layoutCardsVertically(params);
update();
}
void CommandZone::rearrangeTaxCounters()
{
// TODO
/*bool commandZoneVisible = isVisible();
int activeTaxCounterCount = 0;
auto *graphicsItem = getLogic()->getPlayer()->getGraphicsItem();
if (!graphicsItem) {
return;
}
for (AbstractCounter *ctr : graphicsItem->getTaxCounterWidgets()) {
qreal y = TaxCounterSizes::TAX_COUNTER_MARGIN +
activeTaxCounterCount * (TaxCounterSizes::TAX_COUNTER_SIZE + TaxCounterSizes::TAX_COUNTER_MARGIN);
ctr->setPos(TaxCounterSizes::TAX_COUNTER_MARGIN, y);
ctr->setZValue(ZValues::TAX_COUNTERS);
bool visible = commandZoneVisible && ctr->isActive();
ctr->setVisible(visible);
if (visible) {
++activeTaxCounterCount;
}
}
int minHeight = activeTaxCounterCount * (TaxCounterSizes::TAX_COUNTER_SIZE + TaxCounterSizes::TAX_COUNTER_MARGIN) +
TaxCounterSizes::TAX_COUNTER_MARGIN;
setMinimumHeight(minHeight);*/
}
void CommandZone::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
toggleMinimized();
event->accept();
} else {
SelectZone::mouseDoubleClickEvent(event);
}
}

View file

@ -0,0 +1,103 @@
/**
* @file command_zone.h
* @ingroup GameGraphicsZones
* @brief Graphics layer for the command zone, used for Commander format.
*/
#ifndef COCKATRICE_COMMAND_ZONE_H
#define COCKATRICE_COMMAND_ZONE_H
#include "../../game/zones/command_zone_logic.h"
#include "../card_dimensions.h"
#include "select_zone.h"
#include <QLoggingCategory>
inline Q_LOGGING_CATEGORY(CommandZoneLog, "command_zone");
/**
* @namespace ZoneSizes
* @brief Size constants for the command zone and its sub-elements.
*/
namespace ZoneSizes
{
/** @brief Height of the command zone (accommodates a card plus padding) */
constexpr qreal COMMAND_ZONE_HEIGHT = CardDimensions::HEIGHT + 8;
/** @brief Width of the command zone (matches stack zone) */
constexpr qreal COMMAND_ZONE_WIDTH = CardDimensions::WIDTH_F * 1.5;
} // namespace ZoneSizes
/**
* @class CommandZone
* @brief Graphics layer for the command zone in Commander format games.
*
* Always visible when enabled. Supports multiple cards using a zigzag
* horizontal stacking pattern: single cards display centered, multiple
* cards alternate left-right with vertical overlap compression.
* Can be minimized to 25% height via double-click.
*
* @see CommandZoneLogic for card data management
* @see CommanderTaxCounter for the tax counter overlay
*/
class CommandZone : public SelectZone
{
Q_OBJECT
public:
static constexpr qreal MINIMUM_STACKING_HEIGHT = 50.0;
private:
static constexpr double MINIMIZED_HEIGHT_RATIO = 0.25;
int zoneHeight; ///< Full height in pixels when expanded
bool minimized = false; ///< Whether zone is at 25% height
int minimumHeight = 0; ///< Floor for minimized height (e.g. to fit tax counters)
public:
/**
* @brief Constructs a CommandZone graphics item.
* @param _logic Logic layer managing card data
* @param _zoneHeight Zone height in pixels
* @param parent Parent graphics item
*/
CommandZone(CommandZoneLogic *_logic, int _zoneHeight, QGraphicsItem *parent);
/**
* @brief Handles card drops, calculating insertion position from drop point.
* @param dragItems Cards being dragged
* @param startZone Source zone
* @param dropPoint Drop position in local coordinates
*/
void
handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override;
/** @brief Returns the bounding rectangle, accounting for minimized state. */
[[nodiscard]] QRectF boundingRect() const override;
/** @brief Paints the zone background using the Commander theme brush. */
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
/** @brief Repositions cards using zigzag horizontal stacking with overlap compression. */
void reorganizeCards() override;
/** @brief Toggles between full and 25% minimized height. */
void toggleMinimized();
[[nodiscard]] bool isMinimized() const;
/** @brief Returns the current display height (full or minimized). */
[[nodiscard]] qreal currentHeight() const;
/** @brief Sets the minimum height floor, e.g. to ensure tax counters remain visible. */
void setMinimumHeight(int height);
/** @brief Lays out visible tax counters vertically in the top-left corner of the command zone. */
void rearrangeTaxCounters();
signals:
/** @brief Emitted when the zone toggles between minimized and expanded states. */
void minimizedChanged(bool isMinimized);
protected:
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
private slots:
void updateBg();
};
#endif // COCKATRICE_COMMAND_ZONE_H