diff --git a/cockatrice/src/client/settings/shortcuts_settings.h b/cockatrice/src/client/settings/shortcuts_settings.h index 95155b8d1..b849ea10f 100644 --- a/cockatrice/src/client/settings/shortcuts_settings.h +++ b/cockatrice/src/client/settings/shortcuts_settings.h @@ -577,6 +577,9 @@ private: {"Player/aMoveToTable", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Battlefield"), parseSequenceString(""), ShortcutGroup::Move_selected)}, + {"Player/aMoveToCommandZone", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Command Zone"), + parseSequenceString(""), + ShortcutGroup::Move_selected)}, {"Player/aViewHand", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Hand"), parseSequenceString(""), ShortcutGroup::View)}, {"Player/aViewGraveyard", @@ -588,6 +591,8 @@ private: {"Player/aViewSideboard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Sideboard"), parseSequenceString("Ctrl+F3"), ShortcutGroup::View)}, + {"Player/aViewCommandZone", + ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Command Zone"), parseSequenceString(""), ShortcutGroup::View)}, {"Player/aViewTopCards", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Top Cards of Library"), parseSequenceString("Ctrl+W"), ShortcutGroup::View)}, diff --git a/cockatrice/src/game_graphics/board/abstract_counter.cpp b/cockatrice/src/game_graphics/board/abstract_counter.cpp index 46eb7cba3..cee2202e6 100644 --- a/cockatrice/src/game_graphics/board/abstract_counter.cpp +++ b/cockatrice/src/game_graphics/board/abstract_counter.cpp @@ -28,10 +28,7 @@ AbstractCounter::AbstractCounter(CounterState *state, { setAcceptHoverEvents(true); - connect(state, &CounterState::valueChanged, this, [this](int, int newValue) { - value = newValue; - update(); - }); + connect(state, &CounterState::valueChanged, this, [this](int, int newValue) { setValue(newValue); }); connect(state, &CounterState::activeChanged, this, [this](bool newActive) { setActive(newActive); diff --git a/cockatrice/src/game_graphics/zones/command_zone.h b/cockatrice/src/game_graphics/zones/command_zone.h index 52d4f79a6..2f0d8b011 100644 --- a/cockatrice/src/game_graphics/zones/command_zone.h +++ b/cockatrice/src/game_graphics/zones/command_zone.h @@ -36,11 +36,11 @@ constexpr qreal COMMAND_ZONE_WIDTH = CardDimensions::WIDTH_F * 1.5; * @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. + * Always visible when enabled. Cards are laid out by the shared + * SelectZone::layoutCardsVertically. Can be minimized to 25% height via + * double-click. * + * @see SelectZone::layoutCardsVertically for the card layout * @see CommandZoneLogic for card data management * @see CommanderTaxCounter for the tax counter overlay */ @@ -79,7 +79,7 @@ public: [[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. */ + /** @brief Repositions cards via the shared SelectZone vertical layout (allows bottom overflow). */ void reorganizeCards() override; /** @brief Toggles between full and 25% minimized height. */ diff --git a/cockatrice/src/game_graphics/zones/select_zone.h b/cockatrice/src/game_graphics/zones/select_zone.h index 7408f29b6..e08c57407 100644 --- a/cockatrice/src/game_graphics/zones/select_zone.h +++ b/cockatrice/src/game_graphics/zones/select_zone.h @@ -119,9 +119,9 @@ protected: void layoutCardsVertically(const StackLayoutParams ¶ms); // -- Clip container -- - // The clip container mechanism is available for future zones that need visual clipping - // (e.g., zones too short to fit a full card). To enable: call setupClipContainer() in the - // zone's constructor, and set allowBottomOverflow=true in layout params. + // Provides visual clipping for zones too short to fit a full card (e.g. when minimized); + // used by CommandZone. To enable: call setupClipContainer() in the zone's constructor and + // set allowBottomOverflow=true in layout params. /** * @brief Restores any cards that were hover-escaped but whose hover state was not properly cleaned up. diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_card.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_card.cpp index 8781a5788..fdd44485c 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_card.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_card.cpp @@ -139,10 +139,8 @@ bool Server_Card::setCounter(int _id, int value, Event_SetCardCounter *event) bool Server_Card::incrementCounter(int counterId, int delta, Event_SetCardCounter *event) { const int oldValue = counters.value(counterId, 0); - const auto result = static_cast(oldValue) + static_cast(delta); // Clamp to [0, MAX_COUNTER_VALUE] for card counters - const int newValue = - static_cast(qBound(static_cast(0), result, static_cast(MAX_COUNTER_VALUE))); + const int newValue = addClamped(oldValue, delta, 0, MAX_COUNTER_VALUE); if (newValue == oldValue) { return false; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_counter.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_counter.h index 490a5c725..35a434580 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_counter.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_counter.h @@ -22,6 +22,7 @@ #include #include +#include #include class ServerInfo_Counter; @@ -128,11 +129,8 @@ public: */ [[nodiscard]] bool incrementCount(int delta) { - const auto result = static_cast(count) + static_cast(delta); - const int clamped = - static_cast(qBound(static_cast(minValue), result, static_cast(maxValue))); - int oldCount = count; - count = clamped; + const int oldCount = count; + count = addClamped(count, delta, minValue, maxValue); return count != oldCount; } diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp index 2aadf0d8c..74730f519 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp @@ -549,8 +549,11 @@ Server_Player::cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer & const int counterId = cmd.counter_id(); - if (isCommandZoneCounterBlocked(counterId)) { - return Response::RespContextError; + // Reserved tax counters are server-managed system counters and must never be + // deleted by a client. When the command zone is disabled they don't exist, so + // a lookup would fail anyway; when it's enabled they must persist for the game. + if (CounterIds::isTaxCounter(counterId)) { + return Response::RespFunctionNotAllowed; } Server_Counter *counter = counters.value(counterId, nullptr); diff --git a/libcockatrice_utility/libcockatrice/utility/trice_limits.h b/libcockatrice_utility/libcockatrice/utility/trice_limits.h index 4b35a546e..a50dea301 100644 --- a/libcockatrice_utility/libcockatrice/utility/trice_limits.h +++ b/libcockatrice_utility/libcockatrice/utility/trice_limits.h @@ -2,6 +2,8 @@ #define TRICE_LIMITS_H #include +#include +#include // max size for short strings, like names and things that are generally a single phrase constexpr int MAX_NAME_LENGTH = 0xff; @@ -21,6 +23,18 @@ constexpr uint MAXIMUM_DICE_TO_ROLL = 100; // Server enforces these bounds; client may also check for UX optimization. constexpr int MAX_COUNTER_VALUE = 999; +/** + * @brief Overflow-safe clamped addition: returns value + delta bounded to [minValue, maxValue]. + * + * Uses a 64-bit intermediate so the addition itself cannot overflow int. Shared by the + * counter arithmetic in Server_Card and Server_Counter so both stay in sync. + */ +inline int addClamped(int value, int delta, int minValue, int maxValue) +{ + const auto result = static_cast(value) + static_cast(delta); + return static_cast(qBound(static_cast(minValue), result, static_cast(maxValue))); +} + // optimized functions to get qstrings that are at most that long static inline QString nameFromStdString(const std::string &_string) {