diff --git a/Testing/Temporary/CTestCostData.txt b/Testing/Temporary/CTestCostData.txt new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/Testing/Temporary/CTestCostData.txt @@ -0,0 +1 @@ +--- diff --git a/Testing/Temporary/LastTest.log b/Testing/Temporary/LastTest.log new file mode 100644 index 000000000..6f9e41de7 --- /dev/null +++ b/Testing/Temporary/LastTest.log @@ -0,0 +1,3 @@ +Start testing: Aug 10 22:25 EDT +---------------------------------------------------------- +End testing: Aug 10 22:25 EDT diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index d549221ed..a73448655 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -67,7 +67,7 @@ set(cockatrice_SOURCES src/game_graphics/board/card_item.cpp src/game/board/card_list.cpp src/game/board/card_state.cpp - src/game_graphics/board/commander_tax_counter.cpp + src/game_graphics/board/cast_count_widget.cpp src/game_graphics/board/counter_general.cpp src/game/board/counter_state.cpp src/game_graphics/board/translate_counter_name.cpp diff --git a/cockatrice/src/client/settings/shortcuts_settings.h b/cockatrice/src/client/settings/shortcuts_settings.h index 34a85078b..4f4984235 100644 --- a/cockatrice/src/client/settings/shortcuts_settings.h +++ b/cockatrice/src/client/settings/shortcuts_settings.h @@ -597,18 +597,18 @@ private: {"Player/aViewBottomCards", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Bottom Cards of Library"), parseSequenceString("Ctrl+Shift+W"), ShortcutGroup::View)}, - {"Player/aAddCommanderTax", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Increase 1st Tax"), - parseSequenceString(""), - ShortcutGroup::Player_Counters)}, - {"Player/aRemoveCommanderTax", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Decrease 1st Tax"), - parseSequenceString(""), - ShortcutGroup::Player_Counters)}, - {"Player/aAddPartnerTax", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Increase 2nd Tax"), - parseSequenceString(""), - ShortcutGroup::Player_Counters)}, - {"Player/aRemovePartnerTax", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Decrease 2nd Tax"), - parseSequenceString(""), - ShortcutGroup::Player_Counters)}, + {"Player/aIncrease1stCastCount", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Increase 1st Cast Count"), + parseSequenceString(""), + ShortcutGroup::Player_Counters)}, + {"Player/aDecrease1stCastCount", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Decrease 1st Cast Count"), + parseSequenceString(""), + ShortcutGroup::Player_Counters)}, + {"Player/aIncrease2ndCastCount", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Increase 2nd Cast Count"), + parseSequenceString(""), + ShortcutGroup::Player_Counters)}, + {"Player/aDecrease2ndCastCount", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Decrease 2nd Cast Count"), + parseSequenceString(""), + ShortcutGroup::Player_Counters)}, {"Player/aCloseMostRecentZoneView", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Close Recent View"), parseSequenceString("Esc"), ShortcutGroup::View)}, diff --git a/cockatrice/src/game/player/player_actions.cpp b/cockatrice/src/game/player/player_actions.cpp index 1954426ab..d9f416e08 100644 --- a/cockatrice/src/game/player/player_actions.cpp +++ b/cockatrice/src/game/player/player_actions.cpp @@ -14,10 +14,13 @@ #include #include #include +#include #include +#include #include #include #include +#include #include #include #include @@ -25,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -1674,32 +1676,34 @@ void PlayerActions::playSelectedCardsImpl( } } -void PlayerActions::actPlayAndIncrease1stTax(QList selectedCards) +void PlayerActions::actPlayAndIncrease1stCastCount(QList selectedCards) { - playAndIncreaseTax(selectedCards, CounterIds::TaxCounter1); + playAndIncreaseCastCount(selectedCards, 1); } -void PlayerActions::actPlayAndIncrease2ndTax(QList selectedCards) +void PlayerActions::actPlayAndIncrease2ndCastCount(QList selectedCards) { - playAndIncreaseTax(selectedCards, CounterIds::TaxCounter2); + playAndIncreaseCastCount(selectedCards, 2); } -void PlayerActions::playAndIncreaseTax(QList selectedCards, int counterId) +void PlayerActions::playAndIncreaseCastCount(QList selectedCards, int index) { - playSelectedCardsImpl(selectedCards, false, [this, counterId](PendingCommand *pend, const QString &originalZone) { + playSelectedCardsImpl(selectedCards, false, [this, index](PendingCommand *pend, const QString &originalZone) { if (originalZone != ZoneNames::COMMAND || pend == nullptr) { return; } - // Gate the tax increment on the server accepting the move, so a rejected move - // (the card couldn't legally leave the command zone) never inflates the tax. + // Gate the increment on the server accepting the move connect(pend, &PendingCommand::finished, this, - [this, counterId](const Response &response, const CommandContainer &, const QVariant &) { + [this, index](const Response &response, const CommandContainer &, const QVariant &) { if (response.response_code() != Response::RespOk) { return; } - CounterState *state = player->getCounters().value(counterId, nullptr); - if (state && state->isActive()) { - sendIncCounter(counterId, 1); + CounterState *state = player->getCastCount(index); + if (state) { + Command_IncCastCount cmd; + cmd.set_index(index); + cmd.set_delta(1); + sendGameCommand(cmd); } }); }); @@ -1713,29 +1717,40 @@ void PlayerActions::sendIncCounter(int counterId, int delta) sendGameCommand(cmd); } -void PlayerActions::actModifyTaxCounter(int counterId, int delta) +void PlayerActions::actModifyCastCount(int index, int delta) { - CounterState *state = player->getCounters().value(counterId, nullptr); - if (!state || !state->isActive() || !CounterIds::isTaxCounter(counterId)) { + if (!CastCountIds::isValidIndex(index)) { return; } - sendIncCounter(counterId, delta); + CounterState *state = player->getCastCount(index); + if (!state) { + return; + } + Command_IncCastCount cmd; + cmd.set_index(index); + cmd.set_delta(delta); + sendGameCommand(cmd); } -void PlayerActions::actToggleTaxCounter(int counterId) +void PlayerActions::actToggleCastCount(int index) { - CounterState *state = player->getCounters().value(counterId, nullptr); - if (!state || !CounterIds::isTaxCounter(counterId)) { + if (!CastCountIds::isValidIndex(index)) { return; } - // Prevent disabling a counter with tax accumulated; player must reset to 0 first - if (state->isActive() && state->getValue() != 0) { - return; + CounterState *state = player->getCastCount(index); + if (state) { + // Delete only allowed when value is 0 + if (state->getValue() != 0) { + return; + } + Command_DeleteCastCount cmd; + cmd.set_index(index); + sendGameCommand(cmd); + } else { + Command_CreateCastCount cmd; + cmd.set_index(index); + sendGameCommand(cmd); } - Command_SetCounterActive cmd; - cmd.set_counter_id(counterId); - cmd.set_active(!state->isActive()); - sendGameCommand(cmd); } void PlayerActions::actPlay(QList selectedCards) diff --git a/cockatrice/src/game/player/player_actions.h b/cockatrice/src/game/player/player_actions.h index a722e42d3..12b5ab2a6 100644 --- a/cockatrice/src/game/player/player_actions.h +++ b/cockatrice/src/game/player/player_actions.h @@ -127,14 +127,14 @@ public slots: void actPlay(QList selectedCards); void actPlayFacedown(QList selectedCards); - /** @brief Plays the selected card and increments the 1st tax counter. */ - void actPlayAndIncrease1stTax(QList selectedCards); - /** @brief Plays the selected card and increments the 2nd tax counter. */ - void actPlayAndIncrease2ndTax(QList selectedCards); - /** @brief Modifies a tax counter by delta if it is active. */ - void actModifyTaxCounter(int counterId, int delta); - /** @brief Toggles a tax counter's active state (only if inactive or value is 0). */ - void actToggleTaxCounter(int counterId); + /** @brief Plays the selected card and increments the 1st cast count. */ + void actPlayAndIncrease1stCastCount(QList selectedCards); + /** @brief Plays the selected card and increments the 2nd cast count. */ + void actPlayAndIncrease2ndCastCount(QList selectedCards); + /** @brief Modifies a cast count by delta. */ + void actModifyCastCount(int index, int delta); + /** @brief Toggles a cast count's existence (create if missing, delete if value is 0). */ + void actToggleCastCount(int index); void actHide(QList selectedCards); void actMoveTopCardToPlay(); @@ -280,11 +280,11 @@ private: /** * @brief Plays the selected cards and, for each that came from the command zone and whose move - * the server accepts, increments the given (active) tax counter by one. + * the server accepts, increments the given cast count by one. * @param selectedCards Cards to play - * @param counterId The tax counter to increment (CounterIds::TaxCounter1 through TaxCounter5) + * @param index The cast count index (1-5) */ - void playAndIncreaseTax(QList selectedCards, int counterId); + void playAndIncreaseCastCount(QList selectedCards, int index); void cmdSetTopCard(Command_MoveCard &cmd); void cmdSetBottomCard(Command_MoveCard &cmd); diff --git a/cockatrice/src/game/player/player_event_handler.cpp b/cockatrice/src/game/player/player_event_handler.cpp index 6e931e839..e04bc6723 100644 --- a/cockatrice/src/game/player/player_event_handler.cpp +++ b/cockatrice/src/game/player/player_event_handler.cpp @@ -15,10 +15,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -30,8 +32,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -277,20 +279,26 @@ void PlayerEventHandler::eventSetCounter(const Event_SetCounter &event) } } -void PlayerEventHandler::eventSetCounterActive(const Event_SetCounterActive &event) -{ - CounterState *state = player->getCounters().value(event.counter_id(), nullptr); - if (!state) { - return; - } - state->setActive(event.active()); -} - void PlayerEventHandler::eventDelCounter(const Event_DelCounter &event) { player->delCounter(event.counter_id()); } +void PlayerEventHandler::eventCreateCastCount(const Event_CreateCastCount &event) +{ + player->addCastCount(event.index()); +} + +void PlayerEventHandler::eventDeleteCastCount(const Event_DeleteCastCount &event) +{ + player->delCastCount(event.index()); +} + +void PlayerEventHandler::eventSetCastCount(const Event_SetCastCount &event) +{ + player->setCastCountValue(event.index(), event.value()); +} + void PlayerEventHandler::eventDumpZone(const Event_DumpZone &event) { PlayerLogic *zoneOwner = player->getGame()->getPlayerManager()->getPlayers().value(event.zone_owner_id(), 0); @@ -640,12 +648,18 @@ void PlayerEventHandler::processGameEvent(GameEvent::GameEventType type, case GameEvent::SET_COUNTER: eventSetCounter(event.GetExtension(Event_SetCounter::ext)); break; - case GameEvent::SET_COUNTER_ACTIVE: - eventSetCounterActive(event.GetExtension(Event_SetCounterActive::ext)); - break; case GameEvent::DEL_COUNTER: eventDelCounter(event.GetExtension(Event_DelCounter::ext)); break; + case GameEvent::CREATE_CAST_COUNT: + eventCreateCastCount(event.GetExtension(Event_CreateCastCount::ext)); + break; + case GameEvent::DELETE_CAST_COUNT: + eventDeleteCastCount(event.GetExtension(Event_DeleteCastCount::ext)); + break; + case GameEvent::SET_CAST_COUNT: + eventSetCastCount(event.GetExtension(Event_SetCastCount::ext)); + break; case GameEvent::DUMP_ZONE: eventDumpZone(event.GetExtension(Event_DumpZone::ext)); break; diff --git a/cockatrice/src/game/player/player_event_handler.h b/cockatrice/src/game/player/player_event_handler.h index 3d2dc7ce5..f0f53cd32 100644 --- a/cockatrice/src/game/player/player_event_handler.h +++ b/cockatrice/src/game/player/player_event_handler.h @@ -48,7 +48,9 @@ class Event_RollDie; class Event_SetCardAttr; class Event_SetCardCounter; class Event_SetCounter; -class Event_SetCounterActive; +class Event_CreateCastCount; +class Event_DeleteCastCount; +class Event_SetCastCount; class Event_Shuffle; class Event_GameLogNotice; @@ -156,12 +158,18 @@ public: /// Set a player-level counter value. void eventSetCounter(const Event_SetCounter &event); - /** @brief Show or hide a player-level counter without deleting it. */ - void eventSetCounterActive(const Event_SetCounterActive &event); - /// Delete a player-level counter. void eventDelCounter(const Event_DelCounter &event); + /// Create a cast count. + void eventCreateCastCount(const Event_CreateCastCount &event); + + /// Delete a cast count. + void eventDeleteCastCount(const Event_DeleteCastCount &event); + + /// Set a cast count value. + void eventSetCastCount(const Event_SetCastCount &event); + /** @} */ /** @name Zone-level operations diff --git a/cockatrice/src/game/player/player_logic.cpp b/cockatrice/src/game/player/player_logic.cpp index 715777c65..e3dd7e86c 100644 --- a/cockatrice/src/game/player/player_logic.cpp +++ b/cockatrice/src/game/player/player_logic.cpp @@ -292,6 +292,49 @@ void PlayerLogic::clearCounters() } qDeleteAll(counters); counters.clear(); + + for (int index : castCounts.keys()) { + emit castCountRemoved(index); + } + qDeleteAll(castCounts); + castCounts.clear(); +} + +CounterState *PlayerLogic::addCastCount(int index) +{ + if (castCounts.contains(index)) { + return nullptr; + } + QString name = CastCountIds::nameForIndex(index); + auto *state = new CounterState(index, name, QColor(128, 128, 128), 20, 0, true, this); + castCounts.insert(index, state); + emit castCountAdded(index, state); + return state; +} + +void PlayerLogic::delCastCount(int index) +{ + auto *state = castCounts.take(index); + if (!state) { + return; + } + emit castCountRemoved(index); + state->deleteLater(); +} + +void PlayerLogic::setCastCountValue(int index, int value) +{ + auto *state = castCounts.value(index, nullptr); + if (!state) { + return; + } + state->setValue(value); + emit castCountValueChanged(index, value); +} + +CounterState *PlayerLogic::getCastCount(int index) const +{ + return castCounts.value(index, nullptr); } CounterState *PlayerLogic::getLifeCounter() const diff --git a/cockatrice/src/game/player/player_logic.h b/cockatrice/src/game/player/player_logic.h index a98446c9e..86881d82b 100644 --- a/cockatrice/src/game/player/player_logic.h +++ b/cockatrice/src/game/player/player_logic.h @@ -79,6 +79,9 @@ signals: void counterAdded(CounterState *state); void counterRemoved(int counterId); void rearrangeCounters(); + void castCountAdded(int index, CounterState *state); + void castCountRemoved(int index); + void castCountValueChanged(int index, int value); void activeChanged(bool active); void zoneIdChanged(int zoneId); void concededChanged(int playerId, bool conceded); @@ -225,6 +228,16 @@ public: */ CounterState *getLifeCounter() const; + // Cast count management + CounterState *addCastCount(int index); + void delCastCount(int index); + void setCastCountValue(int index, int value); + CounterState *getCastCount(int index) const; + const QMap &getCastCounts() const + { + return castCounts; + } + void setConceded(bool _conceded); bool getConceded() const { @@ -259,6 +272,7 @@ private: int zoneId; QMap zones; QMap counters; + QMap castCounts; // index (1-5) -> state bool dialogSemaphore; QList cardsToDelete; diff --git a/cockatrice/src/game_graphics/board/abstract_counter.cpp b/cockatrice/src/game_graphics/board/abstract_counter.cpp index b70185c6b..e9745b7a6 100644 --- a/cockatrice/src/game_graphics/board/abstract_counter.cpp +++ b/cockatrice/src/game_graphics/board/abstract_counter.cpp @@ -30,7 +30,7 @@ AbstractCounter::AbstractCounter(CounterState *state, setAcceptHoverEvents(true); // Route through the (possibly overridden) virtual setValue so subclasses such as - // CommanderTaxCounter can clamp and refresh their tooltip on every value change. + // CastCountWidget can clamp and refresh their tooltip on every value change. connect(state, &CounterState::valueChanged, this, [this](int, int newValue) { setValue(newValue); }); connect(state, &CounterState::activeChanged, this, [this](bool newActive) { diff --git a/cockatrice/src/game_graphics/board/abstract_counter.h b/cockatrice/src/game_graphics/board/abstract_counter.h index 29ea7ab11..f4d31ba79 100644 --- a/cockatrice/src/game_graphics/board/abstract_counter.h +++ b/cockatrice/src/game_graphics/board/abstract_counter.h @@ -74,7 +74,7 @@ public: /** * @brief Sets the counter value and triggers a visual update. * - * Virtual to allow subclass display customization (e.g., CommanderTaxCounter tooltip updates). + * Virtual to allow subclass display customization (e.g., CastCountWidget tooltip updates). * Overflow protection is handled server-side, not in client counter classes. * * @param _value The new counter value @@ -122,9 +122,9 @@ public: /** * @brief Shows or hides the counter and enables or disables its menu. * - * Sole owner of both, so container layout code only positions counters and may read - * isActive(). Qt AND-s visibility with the parent item's, so an active counter nested in a - * hidden zone (e.g. a tax counter in the command zone) still does not render. + * Sole owner of both visibility and menu state, so container layout code only positions + * counters and may read isActive(). Note that Qt AND-s visibility with the parent item's, + * so an active counter nested in a hidden parent still does not render. * * @param _active True to show and enable the counter, false to hide and disable it */ diff --git a/cockatrice/src/game_graphics/board/commander_tax_counter.cpp b/cockatrice/src/game_graphics/board/cast_count_widget.cpp similarity index 73% rename from cockatrice/src/game_graphics/board/commander_tax_counter.cpp rename to cockatrice/src/game_graphics/board/cast_count_widget.cpp index cb8610216..91198b67e 100644 --- a/cockatrice/src/game_graphics/board/commander_tax_counter.cpp +++ b/cockatrice/src/game_graphics/board/cast_count_widget.cpp @@ -1,32 +1,30 @@ -#include "commander_tax_counter.h" +#include "cast_count_widget.h" #include "../../game/board/counter_state.h" #include "translate_counter_name.h" -#include #include #include static constexpr qreal CORNER_RADIUS = 4.0; static constexpr qreal FONT_SIZE_RATIO = 0.6; -CommanderTaxCounter::CommanderTaxCounter(CounterState *state, PlayerLogic *player, QGraphicsItem *parent) +CastCountWidget::CastCountWidget(CounterState *state, PlayerLogic *player, QGraphicsItem *parent) : AbstractCounter(state, player, false, false, parent), size(state->getRadius()) { setCacheMode(DeviceCoordinateCache); setAcceptHoverEvents(true); - setToolTip(tr("%1: %2").arg(TranslateCounterName::getDisplayName(getName())).arg(getValue())); } -QRectF CommanderTaxCounter::boundingRect() const +QRectF CastCountWidget::boundingRect() const { return QRectF(0, 0, size, size); } -void CommanderTaxCounter::paint(QPainter *painter, - [[maybe_unused]] const QStyleOptionGraphicsItem *option, - [[maybe_unused]] QWidget *widget) +void CastCountWidget::paint(QPainter *painter, + [[maybe_unused]] const QStyleOptionGraphicsItem *option, + [[maybe_unused]] QWidget *widget) { painter->save(); @@ -46,7 +44,7 @@ void CommanderTaxCounter::paint(QPainter *painter, painter->restore(); } -void CommanderTaxCounter::setValue(int _value) +void CastCountWidget::setValue(int _value) { int clampedValue = qMax(0, _value); AbstractCounter::setValue(clampedValue); diff --git a/cockatrice/src/game_graphics/board/cast_count_widget.h b/cockatrice/src/game_graphics/board/cast_count_widget.h new file mode 100644 index 000000000..cac266137 --- /dev/null +++ b/cockatrice/src/game_graphics/board/cast_count_widget.h @@ -0,0 +1,35 @@ +/** + * @file cast_count_widget.h + * @ingroup GameGraphicsBoard + * @brief Displays a single cast count overlay counter in the command zone. + */ + +#ifndef COCKATRICE_CAST_COUNT_WIDGET_H +#define COCKATRICE_CAST_COUNT_WIDGET_H + +#include "abstract_counter.h" + +namespace CastCountSizes +{ +constexpr int MARGIN = 2; +} // namespace CastCountSizes + +/** + * @class CastCountWidget + * @brief A compact counter widget for tracking cast counts in the command zone. + */ +class CastCountWidget : public AbstractCounter +{ + Q_OBJECT +private: + int size; + +public: + CastCountWidget(CounterState *state, PlayerLogic *player, QGraphicsItem *parent = nullptr); + + [[nodiscard]] QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + void setValue(int _value) override; +}; + +#endif // COCKATRICE_CAST_COUNT_WIDGET_H diff --git a/cockatrice/src/game_graphics/board/commander_tax_counter.h b/cockatrice/src/game_graphics/board/commander_tax_counter.h deleted file mode 100644 index efb070d9a..000000000 --- a/cockatrice/src/game_graphics/board/commander_tax_counter.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @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 Layout constants for commander tax counters. - */ -namespace TaxCounterSizes -{ - -/** @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 the number of times the commander has been cast from the command - * zone. Can be adjusted manually via +1/-1 menu actions, or automatically - * incremented when using "Play and Increase Tax" on an accepted cast from - * the command zone. 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; ///< Width and height of the counter in pixels - -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 diff --git a/cockatrice/src/game_graphics/board/translate_counter_name.cpp b/cockatrice/src/game_graphics/board/translate_counter_name.cpp index b0fa84e72..1178aea63 100644 --- a/cockatrice/src/game_graphics/board/translate_counter_name.cpp +++ b/cockatrice/src/game_graphics/board/translate_counter_name.cpp @@ -1,7 +1,5 @@ #include "translate_counter_name.h" -#include - const QMap TranslateCounterName::translated = { {"life", QT_TRANSLATE_NOOP("TranslateCounterName", "Life")}, {"w", QT_TRANSLATE_NOOP("TranslateCounterName", "White")}, @@ -11,8 +9,8 @@ const QMap TranslateCounterName::translated = { {"g", QT_TRANSLATE_NOOP("TranslateCounterName", "Green")}, {"x", QT_TRANSLATE_NOOP("TranslateCounterName", "Colorless")}, {"storm", QT_TRANSLATE_NOOP("TranslateCounterName", "Other")}, - {CounterNames::TaxCounter1, QT_TRANSLATE_NOOP("TranslateCounterName", "1st Tax")}, - {CounterNames::TaxCounter2, QT_TRANSLATE_NOOP("TranslateCounterName", "2nd Tax")}, - {CounterNames::TaxCounter3, QT_TRANSLATE_NOOP("TranslateCounterName", "3rd Tax")}, - {CounterNames::TaxCounter4, QT_TRANSLATE_NOOP("TranslateCounterName", "4th Tax")}, - {CounterNames::TaxCounter5, QT_TRANSLATE_NOOP("TranslateCounterName", "5th Tax")}}; + {"cast_count_1", QT_TRANSLATE_NOOP("TranslateCounterName", "1st Cast Count")}, + {"cast_count_2", QT_TRANSLATE_NOOP("TranslateCounterName", "2nd Cast Count")}, + {"cast_count_3", QT_TRANSLATE_NOOP("TranslateCounterName", "3rd Cast Count")}, + {"cast_count_4", QT_TRANSLATE_NOOP("TranslateCounterName", "4th Cast Count")}, + {"cast_count_5", QT_TRANSLATE_NOOP("TranslateCounterName", "5th Cast Count")}}; diff --git a/cockatrice/src/game_graphics/log/message_log_widget.cpp b/cockatrice/src/game_graphics/log/message_log_widget.cpp index d161b70f2..c2d83345a 100644 --- a/cockatrice/src/game_graphics/log/message_log_widget.cpp +++ b/cockatrice/src/game_graphics/log/message_log_widget.cpp @@ -672,27 +672,11 @@ void MessageLogWidget::logSetCardCounter(PlayerLogic *player, QString cardName, void MessageLogWidget::logSetCounter(PlayerLogic *player, int counterId, QString counterName, int value, int oldValue) { + Q_UNUSED(counterId); if (counterName == "life") { soundEngine->playSound("life_change"); } - if (CounterIds::isTaxCounter(counterId)) { - QString playerName = sanitizeHtml(player->getPlayerInfo()->getName()); - QString valueStr = QString("%1").arg(value); - int delta = value - oldValue; - QString deltaStr = QString::number(delta); - QString counterDisplayName = TranslateCounterName::getDisplayName(counterName); - QString taxLabel = QString("%1").arg(sanitizeHtml(counterDisplayName)); - if (value > oldValue) { - // delta > 0 here; the format string supplies the leading '+' - appendHtmlServerMessage(tr("%1 increases %2 to %3 (+%4).").arg(playerName, taxLabel, valueStr, deltaStr)); - } else { - // delta < 0 here; deltaStr already carries the '-' sign - appendHtmlServerMessage(tr("%1 decreases %2 to %3 (%4).").arg(playerName, taxLabel, valueStr, deltaStr)); - } - return; - } - QString counterDisplayName = TranslateCounterName::getDisplayName(counterName); appendHtmlServerMessage(tr("%1 sets counter %2 to %3 (%4%5).") .arg(sanitizeHtml(player->getPlayerInfo()->getName())) diff --git a/cockatrice/src/game_graphics/player/menu/card_menu.cpp b/cockatrice/src/game_graphics/player/menu/card_menu.cpp index 3cf026ab2..687b1557c 100644 --- a/cockatrice/src/game_graphics/player/menu/card_menu.cpp +++ b/cockatrice/src/game_graphics/player/menu/card_menu.cpp @@ -84,8 +84,10 @@ 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()); }); - aPlayAndIncrease1stTax = makeAction(this, [actions, sel]() { actions->actPlayAndIncrease1stTax(sel()); }); - aPlayAndIncrease2ndTax = makeAction(this, [actions, sel]() { actions->actPlayAndIncrease2ndTax(sel()); }); + aPlayAndIncrease1stCastCount = + makeAction(this, [actions, sel]() { actions->actPlayAndIncrease1stCastCount(sel()); }); + aPlayAndIncrease2ndCastCount = + makeAction(this, [actions, sel]() { actions->actPlayAndIncrease2ndCastCount(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()); }); @@ -166,16 +168,15 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _sho if (writeableCard) { addAction(aPlay); - // Only offer for single selection: a multi-select would over-count casts by - // bumping one commander's tax counter once per command-zone card. + // Only offer for single selection: a multi-select would over-count casts const bool singleSelection = gameScene->selectedCards().size() <= 1; - if (singleSelection && player->getTaxCounterIfActive(CounterIds::TaxCounter1)) { - addAction(aPlayAndIncrease1stTax); + if (singleSelection && player->getCastCountWidget(1)) { + addAction(aPlayAndIncrease1stCastCount); } - if (singleSelection && player->getTaxCounterIfActive(CounterIds::TaxCounter2)) { - addAction(aPlayAndIncrease2ndTax); + if (singleSelection && player->getCastCountWidget(2)) { + addAction(aPlayAndIncrease2ndCastCount); } // No reveal submenu - command zone is public @@ -524,8 +525,8 @@ void CardMenu::retranslateUi() aPlay->setText(tr("&Play")); aHide->setText(tr("&Hide")); aPlayFacedown->setText(tr("Play &Face Down")); - aPlayAndIncrease1stTax->setText(tr("Play and &Increase 1st Tax")); - aPlayAndIncrease2ndTax->setText(tr("Play and Increase &2nd Tax")); + aPlayAndIncrease1stCastCount->setText(tr("Play and &Increase 1st Cast Count")); + aPlayAndIncrease2ndCastCount->setText(tr("Play and Increase &2nd Cast Count")); aRevealToAll->setText(tr("&All players")); //: Turn sideways or back again aTap->setText(tr("&Tap / Untap")); diff --git a/cockatrice/src/game_graphics/player/menu/card_menu.h b/cockatrice/src/game_graphics/player/menu/card_menu.h index a645cb03c..dbaf1a7bf 100644 --- a/cockatrice/src/game_graphics/player/menu/card_menu.h +++ b/cockatrice/src/game_graphics/player/menu/card_menu.h @@ -32,8 +32,8 @@ public: QMenu *mCardCounters; QAction *aPlay, *aPlayFacedown; - /** @brief Play actions that also increment the corresponding tax counter. */ - QAction *aPlayAndIncrease1stTax, *aPlayAndIncrease2ndTax; + /** @brief Play actions that also increment the corresponding cast count. */ + QAction *aPlayAndIncrease1stCastCount, *aPlayAndIncrease2ndCastCount; QAction *aRevealToAll; QAction *aHide; QAction *aClone; diff --git a/cockatrice/src/game_graphics/player/menu/command_zone_menu.cpp b/cockatrice/src/game_graphics/player/menu/command_zone_menu.cpp index d6e73b26f..527d4d2b2 100644 --- a/cockatrice/src/game_graphics/player/menu/command_zone_menu.cpp +++ b/cockatrice/src/game_graphics/player/menu/command_zone_menu.cpp @@ -14,42 +14,41 @@ CommandZoneMenu::CommandZoneMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) : QMenu(playerMenu), player(_player) { - // Shortcuts only for first two tax counters (matching legacy behavior) - incTax1ShortcutKey = QStringLiteral("Player/aAddCommanderTax"); - decTax1ShortcutKey = QStringLiteral("Player/aRemoveCommanderTax"); - incTax2ShortcutKey = QStringLiteral("Player/aAddPartnerTax"); - decTax2ShortcutKey = QStringLiteral("Player/aRemovePartnerTax"); + inc1ShortcutKey = QStringLiteral("Player/aIncrease1stCastCount"); + dec1ShortcutKey = QStringLiteral("Player/aDecrease1stCastCount"); + inc2ShortcutKey = QStringLiteral("Player/aIncrease2ndCastCount"); + dec2ShortcutKey = QStringLiteral("Player/aDecrease2ndCastCount"); PlayerLogic *logic = player->getLogic(); if (logic && logic->getPlayerInfo()->getLocalOrJudge()) { - for (int i = 0; i < TaxCounterCount; ++i) { - int counterId = CounterIds::taxCounterIdFromIndex(i); + for (int i = 0; i < CastCountCount; ++i) { + int index = i + 1; // 1-based index - aIncreaseTax[i] = new QAction(this); - connect(aIncreaseTax[i], &QAction::triggered, this, [this, counterId]() { + aIncrease[i] = new QAction(this); + connect(aIncrease[i], &QAction::triggered, this, [this, index]() { if (auto *l = player->getLogic()) { - l->getPlayerActions()->actModifyTaxCounter(counterId, 1); + l->getPlayerActions()->actModifyCastCount(index, 1); } }); - addAction(aIncreaseTax[i]); + addAction(aIncrease[i]); - aDecreaseTax[i] = new QAction(this); - connect(aDecreaseTax[i], &QAction::triggered, this, [this, counterId]() { + aDecrease[i] = new QAction(this); + connect(aDecrease[i], &QAction::triggered, this, [this, index]() { if (auto *l = player->getLogic()) { - l->getPlayerActions()->actModifyTaxCounter(counterId, -1); + l->getPlayerActions()->actModifyCastCount(index, -1); } }); - addAction(aDecreaseTax[i]); + addAction(aDecrease[i]); addSeparator(); - aToggleTax[i] = new QAction(this); - connect(aToggleTax[i], &QAction::triggered, this, [this, counterId]() { + aToggle[i] = new QAction(this); + connect(aToggle[i], &QAction::triggered, this, [this, index]() { if (auto *l = player->getLogic()) { - l->getPlayerActions()->actToggleTaxCounter(counterId); + l->getPlayerActions()->actToggleCastCount(index); } }); - addAction(aToggleTax[i]); + addAction(aToggle[i]); addSeparator(); } @@ -58,7 +57,7 @@ CommandZoneMenu::CommandZoneMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) connect(aToggleMinimized, &QAction::triggered, this, &CommandZoneMenu::actToggleMinimized); addAction(aToggleMinimized); - connect(this, &QMenu::aboutToShow, this, &CommandZoneMenu::updateTaxCounterActionStates); + connect(this, &QMenu::aboutToShow, this, &CommandZoneMenu::updateCastCountActionStates); } retranslateUi(); @@ -70,14 +69,13 @@ void CommandZoneMenu::retranslateUi() static const char *ordinals[] = {"1st", "2nd", "3rd", "4th", "5th"}; - for (int i = 0; i < TaxCounterCount; ++i) { - if (aIncreaseTax[i]) { - aIncreaseTax[i]->setText(tr("&Increase %1 Tax (+1)").arg(ordinals[i])); + for (int i = 0; i < CastCountCount; ++i) { + if (aIncrease[i]) { + aIncrease[i]->setText(tr("&Increase %1 Cast Count (+1)").arg(ordinals[i])); } - if (aDecreaseTax[i]) { - aDecreaseTax[i]->setText(tr("&Decrease %1 Tax (-1)").arg(ordinals[i])); + if (aDecrease[i]) { + aDecrease[i]->setText(tr("&Decrease %1 Cast Count (-1)").arg(ordinals[i])); } - // Toggle action labels are derived dynamically in updateTaxCounterActionStates() } if (aToggleMinimized) { @@ -93,44 +91,39 @@ void CommandZoneMenu::actToggleMinimized() } } -void CommandZoneMenu::updateTaxCounterActionStates() +void CommandZoneMenu::updateCastCountActionStates() { static const char *ordinals[] = {"1st", "2nd", "3rd", "4th", "5th"}; - // Collect all tax counter states - std::array taxCounters{}; - for (int i = 0; i < TaxCounterCount; ++i) { - taxCounters[i] = player->getTaxCounterIfActive(CounterIds::taxCounterIdFromIndex(i)); + std::array castCounts{}; + for (int i = 0; i < CastCountCount; ++i) { + castCounts[i] = player->getCastCountWidget(i + 1); } - // Find highest active tax counter index int highestActive = -1; - for (int i = TaxCounterCount - 1; i >= 0; --i) { - if (taxCounters[i]) { + for (int i = CastCountCount - 1; i >= 0; --i) { + if (castCounts[i]) { highestActive = i; break; } } - for (int i = 0; i < TaxCounterCount; ++i) { - AbstractCounter *counter = taxCounters[i]; + for (int i = 0; i < CastCountCount; ++i) { + AbstractCounter *counter = castCounts[i]; - if (aIncreaseTax[i]) { - aIncreaseTax[i]->setVisible(counter && counter->getValue() < MAX_COUNTER_VALUE); + if (aIncrease[i]) { + aIncrease[i]->setVisible(counter && counter->getValue() < MAX_COUNTER_VALUE); } - if (aDecreaseTax[i]) { - aDecreaseTax[i]->setVisible(counter && counter->getValue() > 0); + if (aDecrease[i]) { + aDecrease[i]->setVisible(counter && counter->getValue() > 0); } - if (aToggleTax[i]) { - aToggleTax[i]->setText(counter ? tr("&Remove %1 Tax").arg(ordinals[i]) - : tr("&Add %1 Tax").arg(ordinals[i])); + if (aToggle[i]) { + aToggle[i]->setText(counter ? tr("&Remove %1 Cast Count").arg(ordinals[i]) + : tr("&Add %1 Cast Count").arg(ordinals[i])); - // Toggle visible if: - // - Counter doesn't exist and previous counter is active (can add next in sequence) - // - Counter exists with value 0 and is the highest active (can remove last in sequence) - bool canAdd = !counter && (i == 0 || taxCounters[i - 1]); + bool canAdd = !counter && (i == 0 || castCounts[i - 1]); bool canRemove = counter && counter->getValue() == 0 && i == highestActive; - aToggleTax[i]->setVisible(canAdd || canRemove); + aToggle[i]->setVisible(canAdd || canRemove); } } @@ -144,34 +137,32 @@ void CommandZoneMenu::setShortcutsActive() { ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); - // Only first two tax counters have shortcuts - if (aIncreaseTax[0]) { - aIncreaseTax[0]->setShortcuts(shortcuts.getShortcut(incTax1ShortcutKey)); + if (aIncrease[0]) { + aIncrease[0]->setShortcuts(shortcuts.getShortcut(inc1ShortcutKey)); } - if (aDecreaseTax[0]) { - aDecreaseTax[0]->setShortcuts(shortcuts.getShortcut(decTax1ShortcutKey)); + if (aDecrease[0]) { + aDecrease[0]->setShortcuts(shortcuts.getShortcut(dec1ShortcutKey)); } - if (aIncreaseTax[1]) { - aIncreaseTax[1]->setShortcuts(shortcuts.getShortcut(incTax2ShortcutKey)); + if (aIncrease[1]) { + aIncrease[1]->setShortcuts(shortcuts.getShortcut(inc2ShortcutKey)); } - if (aDecreaseTax[1]) { - aDecreaseTax[1]->setShortcuts(shortcuts.getShortcut(decTax2ShortcutKey)); + if (aDecrease[1]) { + aDecrease[1]->setShortcuts(shortcuts.getShortcut(dec2ShortcutKey)); } } void CommandZoneMenu::setShortcutsInactive() { - // Only first two tax counters have shortcuts - if (aIncreaseTax[0]) { - aIncreaseTax[0]->setShortcut(QKeySequence()); + if (aIncrease[0]) { + aIncrease[0]->setShortcut(QKeySequence()); } - if (aDecreaseTax[0]) { - aDecreaseTax[0]->setShortcut(QKeySequence()); + if (aDecrease[0]) { + aDecrease[0]->setShortcut(QKeySequence()); } - if (aIncreaseTax[1]) { - aIncreaseTax[1]->setShortcut(QKeySequence()); + if (aIncrease[1]) { + aIncrease[1]->setShortcut(QKeySequence()); } - if (aDecreaseTax[1]) { - aDecreaseTax[1]->setShortcut(QKeySequence()); + if (aDecrease[1]) { + aDecrease[1]->setShortcut(QKeySequence()); } } diff --git a/cockatrice/src/game_graphics/player/menu/command_zone_menu.h b/cockatrice/src/game_graphics/player/menu/command_zone_menu.h index de2fabdb6..09b7fceca 100644 --- a/cockatrice/src/game_graphics/player/menu/command_zone_menu.h +++ b/cockatrice/src/game_graphics/player/menu/command_zone_menu.h @@ -1,7 +1,7 @@ /** * @file command_zone_menu.h - * @ingroup GameMenusZones - * @brief Context menu for command zone right-click actions. + * @ingroup GameGraphicsPlayers + * @brief Submenu for command zone actions (cast counts, minimize/restore). */ #ifndef COCKATRICE_COMMAND_ZONE_MENU_H @@ -17,10 +17,10 @@ class PlayerGraphicsItem; /** * @class CommandZoneMenu - * @brief Context menu for the command zone. + * @brief Player submenu for managing cast counts and command zone display. * - * Appears when right-clicking on the command zone. Provides actions for - * adjusting tax counters (up to 5) and toggling minimized state. + * Provides actions to increment/decrement each cast count, toggle cast counts + * on/off, and minimize/restore the command zone display. * * @see PlayerMenu * @see CommandZone @@ -36,15 +36,15 @@ public: void setShortcutsInactive() override; private: - static constexpr int TaxCounterCount = CounterIds::TaxCounterCount; + static constexpr int CastCountCount = CastCountIds::Count; - std::array aIncreaseTax{}; - std::array aDecreaseTax{}; - std::array aToggleTax{}; + std::array aIncrease{}; + std::array aDecrease{}; + std::array aToggle{}; QAction *aToggleMinimized = nullptr; public slots: - void updateTaxCounterActionStates(); + void updateCastCountActionStates(); private slots: void actToggleMinimized(); @@ -52,11 +52,10 @@ private slots: private: PlayerGraphicsItem *player; - // Shortcuts only for first two tax counters - QString incTax1ShortcutKey; - QString decTax1ShortcutKey; - QString incTax2ShortcutKey; - QString decTax2ShortcutKey; + QString inc1ShortcutKey; + QString dec1ShortcutKey; + QString inc2ShortcutKey; + QString dec2ShortcutKey; }; #endif // COCKATRICE_COMMAND_ZONE_MENU_H diff --git a/cockatrice/src/game_graphics/player/player_graphics_item.cpp b/cockatrice/src/game_graphics/player/player_graphics_item.cpp index 8cc47d469..e63f7f5b4 100644 --- a/cockatrice/src/game_graphics/player/player_graphics_item.cpp +++ b/cockatrice/src/game_graphics/player/player_graphics_item.cpp @@ -3,7 +3,7 @@ #include "../../game/player/player_actions.h" #include "../../interface/widgets/tabs/tab_game.h" #include "../board/abstract_card_item.h" -#include "../board/commander_tax_counter.h" +#include "../board/cast_count_widget.h" #include "../board/counter_general.h" #include "../hand_counter.h" #include "../z_values.h" @@ -32,6 +32,8 @@ PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player) connect(player, &PlayerLogic::counterAdded, this, &PlayerGraphicsItem::onCounterAdded); connect(player, &PlayerLogic::counterRemoved, this, &PlayerGraphicsItem::onCounterRemoved); + connect(player, &PlayerLogic::castCountAdded, this, &PlayerGraphicsItem::onCastCountAdded); + connect(player, &PlayerLogic::castCountRemoved, this, &PlayerGraphicsItem::onCastCountRemoved); playerMenu = new PlayerMenu(this); @@ -231,34 +233,12 @@ void PlayerGraphicsItem::onCounterAdded(CounterState *state) AbstractCounter *widget; if (state->getName() == "life") { widget = playerTarget->addCounter(state); - } else if (CounterIds::isTaxCounter(state->getId())) { - if (!commandZoneGraphicsItem) { - qWarning() << "Cannot create tax counter" << state->getName() << "- command zone not available"; - return; - } - // Qt parent (commandZoneGraphicsItem) owns widget; counterWidgets map holds reference - // for lookup; CommandZone::registerTaxCounter connects QObject::destroyed for cleanup - widget = new CommanderTaxCounter(state, player, commandZoneGraphicsItem); - widget->setActive(state->isActive()); - commandZoneGraphicsItem->registerTaxCounter(widget); - - if (auto *menu = playerMenu->getCommandZoneMenu()) { - connect(state, &CounterState::activeChanged, menu, &CommandZoneMenu::updateTaxCounterActionStates); - connect(state, &CounterState::valueChanged, menu, &CommandZoneMenu::updateTaxCounterActionStates); - } } else { widget = new GeneralCounter(state, player, true, this); } counterWidgets.insert(state->getId(), widget); - // A counter's submenu follows its isActive() state: while inactive the counter is hidden and - // the server rejects every modification. Only tax counters go inactive today. setCounterMenuRegistered(widget, state->isActive()); - connect(state, &CounterState::activeChanged, this, [this, counterId = state->getId()](bool newActive) { - if (AbstractCounter *counter = getCounterWidget(counterId)) { - setCounterMenuRegistered(counter, newActive); - } - }); if (playerMenu->getShortcutsActive()) { widget->setShortcutsActive(); @@ -267,6 +247,25 @@ void PlayerGraphicsItem::onCounterAdded(CounterState *state) rearrangeCounters(); } +void PlayerGraphicsItem::onCastCountAdded(int index, CounterState *state) +{ + if (!commandZoneGraphicsItem) { + qWarning() << "Cannot create cast count" << index << "- command zone not available"; + return; + } + + auto *widget = new CastCountWidget(state, player, commandZoneGraphicsItem); + commandZoneGraphicsItem->registerCastCount(widget); + castCountWidgets.insert(index, widget); + + if (auto *menu = playerMenu->getCommandZoneMenu()) { + menu->updateCastCountActionStates(); + connect(state, &CounterState::valueChanged, menu, &CommandZoneMenu::updateCastCountActionStates); + } + + rearrangeCounters(); +} + void PlayerGraphicsItem::onCounterRemoved(int counterId) { auto *widget = counterWidgets.take(counterId); @@ -274,25 +273,37 @@ void PlayerGraphicsItem::onCounterRemoved(int counterId) return; } setCounterMenuRegistered(widget, false); - if (commandZoneGraphicsItem && CounterIds::isTaxCounter(widget->getId())) { - commandZoneGraphicsItem->unregisterTaxCounter(widget); + widget->delCounter(); + rearrangeCounters(); +} + +void PlayerGraphicsItem::onCastCountRemoved(int index) +{ + auto *widget = castCountWidgets.take(index); + if (!widget) { + return; + } + if (commandZoneGraphicsItem) { + commandZoneGraphicsItem->unregisterCastCount(widget); } widget->delCounter(); + + if (auto *menu = playerMenu->getCommandZoneMenu()) { + menu->updateCastCountActionStates(); + } + rearrangeCounters(); } void PlayerGraphicsItem::rearrangeCounters() { if (commandZoneGraphicsItem) { - commandZoneGraphicsItem->rearrangeTaxCounters(); + commandZoneGraphicsItem->rearrangeCastCounts(); } qreal ySize = boundingRect().y() + 80; constexpr qreal padding = 5; for (auto *ctr : counterWidgets.values()) { - if (CounterIds::isTaxCounter(ctr->getId())) { - continue; - } if (!ctr->getShownInCounterArea()) { continue; } @@ -302,10 +313,9 @@ void PlayerGraphicsItem::rearrangeCounters() } } -AbstractCounter *PlayerGraphicsItem::getTaxCounterIfActive(int counterId) const +AbstractCounter *PlayerGraphicsItem::getCastCountWidget(int index) const { - AbstractCounter *counter = getCounterWidget(counterId); - return (counter && counter->isActive()) ? counter : nullptr; + return castCountWidgets.value(index, nullptr); } void PlayerGraphicsItem::rearrangeZones() diff --git a/cockatrice/src/game_graphics/player/player_graphics_item.h b/cockatrice/src/game_graphics/player/player_graphics_item.h index 9561792e5..b19ae7a0d 100644 --- a/cockatrice/src/game_graphics/player/player_graphics_item.h +++ b/cockatrice/src/game_graphics/player/player_graphics_item.h @@ -123,14 +123,16 @@ public: { return counterWidgets.value(counterId, nullptr); } - /** @brief Returns the tax counter if it exists and is active, or nullptr otherwise. */ - [[nodiscard]] AbstractCounter *getTaxCounterIfActive(int counterId) const; + /** @brief Returns the cast count widget for the given index (1-5), or nullptr if not found. */ + [[nodiscard]] AbstractCounter *getCastCountWidget(int index) const; public slots: void onPlayerActiveChanged(bool _active); void onCustomZoneAdded(QString customZoneName); void onCounterAdded(CounterState *state); void onCounterRemoved(int counterId); + void onCastCountAdded(int index, CounterState *state); + void onCastCountRemoved(int index); void rearrangeCounters(); void retranslateUi(); @@ -147,6 +149,7 @@ private: PlayerArea *playerArea; PlayerTarget *playerTarget; QMap counterWidgets; + QMap castCountWidgets; QMap zoneGraphicsItems; PileZone *deckZoneGraphicsItem; PileZone *sideboardGraphicsItem; diff --git a/cockatrice/src/game_graphics/z_values.h b/cockatrice/src/game_graphics/z_values.h index 8847c2233..c07d3d03e 100644 --- a/cockatrice/src/game_graphics/z_values.h +++ b/cockatrice/src/game_graphics/z_values.h @@ -34,8 +34,8 @@ 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); -/** @brief Commander tax counter overlay */ -constexpr qreal TAX_COUNTERS = ZValueLayerManager::overlayZValue(2.0); +/** @brief Cast count overlay */ +constexpr qreal CAST_COUNTS = ZValueLayerManager::overlayZValue(2.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); diff --git a/cockatrice/src/game_graphics/zones/command_zone.cpp b/cockatrice/src/game_graphics/zones/command_zone.cpp index 423fb8045..7db27e13f 100644 --- a/cockatrice/src/game_graphics/zones/command_zone.cpp +++ b/cockatrice/src/game_graphics/zones/command_zone.cpp @@ -7,7 +7,7 @@ #include "../board/abstract_counter.h" #include "../board/card_drag_item.h" #include "../board/card_item.h" -#include "../board/commander_tax_counter.h" +#include "../board/cast_count_widget.h" #include "../z_values.h" #include @@ -26,7 +26,7 @@ CommandZone::CommandZone(CommandZoneLogic *_logic, int _zoneHeight, QGraphicsIte CommandZone::~CommandZone() { - for (AbstractCounter *ctr : taxCounters) { + for (AbstractCounter *ctr : castCounts) { disconnect(ctr, &QObject::destroyed, this, nullptr); } } @@ -52,7 +52,7 @@ void CommandZone::setMinimumHeight(int height) return; } // The floor only affects height while minimized; skip relayout if the displayed - // height is unchanged (e.g. a tax counter toggled while expanded). + // height is unchanged (e.g. a cast count toggled while expanded). const qreal oldEffectiveHeight = currentHeight(); minimumHeight = height; if (qFuzzyCompare(1.0 + currentHeight(), 1.0 + oldEffectiveHeight)) { @@ -145,38 +145,32 @@ void CommandZone::reorganizeCards() update(); } -void CommandZone::registerTaxCounter(AbstractCounter *counter) +void CommandZone::registerCastCount(AbstractCounter *counter) { - if (!counter || taxCounters.contains(counter)) { + if (!counter || castCounts.contains(counter)) { return; } - taxCounters.append(counter); - connect(counter, &QObject::destroyed, this, [this, counter]() { taxCounters.removeOne(counter); }); + castCounts.append(counter); + connect(counter, &QObject::destroyed, this, [this, counter]() { castCounts.removeOne(counter); }); } -void CommandZone::unregisterTaxCounter(AbstractCounter *counter) +void CommandZone::unregisterCastCount(AbstractCounter *counter) { if (!counter) { return; } disconnect(counter, &QObject::destroyed, this, nullptr); - taxCounters.removeOne(counter); + castCounts.removeOne(counter); } -void CommandZone::rearrangeTaxCounters() +void CommandZone::rearrangeCastCounts() { - qreal y = TaxCounterSizes::TAX_COUNTER_MARGIN; + qreal y = CastCountSizes::MARGIN; - for (AbstractCounter *ctr : taxCounters) { - ctr->setPos(TaxCounterSizes::TAX_COUNTER_MARGIN, y); - ctr->setZValue(ZValues::TAX_COUNTERS); - // Visibility is owned solely by AbstractCounter::setActive() (the counter's own flag), - // which Qt AND-s with this CommandZone's visibility via child-visibility propagation - // (tax counters are graphics children of the zone). This function only handles layout, - // so it stacks and measures by isActive() alone. - if (ctr->isActive()) { - y += ctr->getRadius() + TaxCounterSizes::TAX_COUNTER_MARGIN; - } + for (AbstractCounter *ctr : castCounts) { + ctr->setPos(CastCountSizes::MARGIN, y); + ctr->setZValue(ZValues::CAST_COUNTS); + y += ctr->getRadius() + CastCountSizes::MARGIN; } setMinimumHeight(static_cast(y)); diff --git a/cockatrice/src/game_graphics/zones/command_zone.h b/cockatrice/src/game_graphics/zones/command_zone.h index 4fe57ba56..8b309669b 100644 --- a/cockatrice/src/game_graphics/zones/command_zone.h +++ b/cockatrice/src/game_graphics/zones/command_zone.h @@ -38,21 +38,21 @@ constexpr qreal COMMAND_ZONE_WIDTH = CardDimensions::WIDTH_F * 1.5; * * Always visible when enabled. Uses the generic vertical stacking layout * with bottom overflow enabled. Can be minimized via double-click (25% height, - * or the tax-counter floor if higher). + * or the cast count floor if higher). * * @see SelectZone::layoutCardsVertically for the stacking algorithm * @see CommandZoneLogic for card data management - * @see CommanderTaxCounter for the tax counter overlay + * @see CastCountWidget for the cast count overlay */ class CommandZone : public SelectZone { Q_OBJECT private: static constexpr double MINIMIZED_HEIGHT_RATIO = 0.25; - int zoneHeight; ///< Full height in pixels when expanded - bool minimized = false; ///< Whether zone is collapsed (25% height, or the tax-counter floor) - int minimumHeight = 0; ///< Floor for minimized height (e.g. to fit tax counters) - QList taxCounters; ///< Registered tax counter widgets + int zoneHeight; ///< Full height in pixels when expanded + bool minimized = false; ///< Whether zone is collapsed (25% height, or the cast count floor) + int minimumHeight = 0; ///< Floor for minimized height (e.g. to fit cast counts) + QList castCounts; ///< Registered cast count widgets public: /** @@ -86,24 +86,24 @@ public: [[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. */ + /** @brief Sets the minimum height floor, e.g. to ensure cast counts remain visible. */ void setMinimumHeight(int height); - /** @brief Registers a tax counter widget for layout management. */ - void registerTaxCounter(AbstractCounter *counter); + /** @brief Registers a cast count widget for layout management. */ + void registerCastCount(AbstractCounter *counter); /** - * @brief Unregisters a tax counter widget from layout management. + * @brief Unregisters a cast count widget from layout management. * * Call on removal: the widget outlives it via deleteLater(), so unregister synchronously * to keep it out of the next layout pass. */ - void unregisterTaxCounter(AbstractCounter *counter); - /** @brief Lays out visible tax counters vertically in the top-left corner of the command zone. */ - void rearrangeTaxCounters(); + void unregisterCastCount(AbstractCounter *counter); + /** @brief Lays out visible cast counts vertically in the top-left corner of the command zone. */ + void rearrangeCastCounts(); signals: /** @brief Emitted when the zone's minimized state changes. */ void minimizedChanged(bool isMinimized); - /** @brief Emitted when display height changes without a minimized-state change (e.g. tax counter toggled while + /** @brief Emitted when display height changes without a minimized-state change (e.g. cast count toggled while * minimized). */ void effectiveHeightChanged(); diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp index bc3ab9b9b..f5fe4c5c2 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp @@ -13,16 +13,19 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -36,8 +39,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -343,9 +346,30 @@ Response::ResponseCode Server_AbstractParticipant::cmdDelCounter(const Command_D return Response::RespFunctionNotAllowed; } -Response::ResponseCode Server_AbstractParticipant::cmdSetCounterActive(const Command_SetCounterActive & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) +Response::ResponseCode Server_AbstractParticipant::cmdCreateCastCount(const Command_CreateCastCount & /*cmd*/, + ResponseContainer & /*rc*/, + GameEventStorage & /*ges*/) +{ + return Response::RespFunctionNotAllowed; +} + +Response::ResponseCode Server_AbstractParticipant::cmdDeleteCastCount(const Command_DeleteCastCount & /*cmd*/, + ResponseContainer & /*rc*/, + GameEventStorage & /*ges*/) +{ + return Response::RespFunctionNotAllowed; +} + +Response::ResponseCode Server_AbstractParticipant::cmdIncCastCount(const Command_IncCastCount & /*cmd*/, + ResponseContainer & /*rc*/, + GameEventStorage & /*ges*/) +{ + return Response::RespFunctionNotAllowed; +} + +Response::ResponseCode Server_AbstractParticipant::cmdSetCastCount(const Command_SetCastCount & /*cmd*/, + ResponseContainer & /*rc*/, + GameEventStorage & /*ges*/) { return Response::RespFunctionNotAllowed; } @@ -533,8 +557,17 @@ Server_AbstractParticipant::processGameCommand(const GameCommand &command, Respo case GameCommand::REVERSE_TURN: return cmdReverseTurn(command.GetExtension(Command_ReverseTurn::ext), rc, ges); break; - case GameCommand::SET_COUNTER_ACTIVE: - return cmdSetCounterActive(command.GetExtension(Command_SetCounterActive::ext), rc, ges); + case GameCommand::CREATE_CAST_COUNT: + return cmdCreateCastCount(command.GetExtension(Command_CreateCastCount::ext), rc, ges); + break; + case GameCommand::DELETE_CAST_COUNT: + return cmdDeleteCastCount(command.GetExtension(Command_DeleteCastCount::ext), rc, ges); + break; + case GameCommand::INC_CAST_COUNT: + return cmdIncCastCount(command.GetExtension(Command_IncCastCount::ext), rc, ges); + break; + case GameCommand::SET_CAST_COUNT: + return cmdSetCastCount(command.GetExtension(Command_SetCastCount::ext), rc, ges); break; default: return Response::RespInvalidCommand; diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h index 14de20ed0..c3ad8d067 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h @@ -41,7 +41,6 @@ class Command_Judge; class Command_IncCounter; class Command_CreateCounter; class Command_SetCounter; -class Command_SetCounterActive; class Command_DelCounter; class Command_NextTurn; class Command_SetActivePhase; @@ -53,6 +52,10 @@ class Command_SetSideboardPlan; class Command_DeckSelect; class Command_SetSideboardLock; class Command_ChangeZoneProperties; +class Command_CreateCastCount; +class Command_DeleteCastCount; +class Command_IncCastCount; +class Command_SetCastCount; class Server_AbstractParticipant : public Server_ArrowTarget, public ServerInfo_User_Container { @@ -162,8 +165,6 @@ public: virtual Response::ResponseCode cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); virtual Response::ResponseCode - cmdSetCounterActive(const Command_SetCounterActive &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode cmdNextTurn(const Command_NextTurn &cmd, ResponseContainer &rc, GameEventStorage &ges); virtual Response::ResponseCode cmdSetActivePhase(const Command_SetActivePhase &cmd, ResponseContainer &rc, GameEventStorage &ges); @@ -175,6 +176,14 @@ public: cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges); virtual Response::ResponseCode cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges); + virtual Response::ResponseCode + cmdCreateCastCount(const Command_CreateCastCount &cmd, ResponseContainer &rc, GameEventStorage &ges); + virtual Response::ResponseCode + cmdDeleteCastCount(const Command_DeleteCastCount &cmd, ResponseContainer &rc, GameEventStorage &ges); + virtual Response::ResponseCode + cmdIncCastCount(const Command_IncCastCount &cmd, ResponseContainer &rc, GameEventStorage &ges); + virtual Response::ResponseCode + cmdSetCastCount(const Command_SetCastCount &cmd, ResponseContainer &rc, GameEventStorage &ges); Response::ResponseCode processGameCommand(const GameCommand &command, ResponseContainer &rc, GameEventStorage &ges); void sendGameEvent(const GameEventContainer &event); 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 01434ee40..5e88f3670 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp @@ -18,33 +18,39 @@ #include #include #include +#include #include #include #include +#include #include +#include #include #include #include #include +#include #include -#include #include #include #include #include #include #include +#include #include #include +#include #include #include #include +#include #include -#include #include #include #include #include +#include #include #include #include @@ -75,7 +81,6 @@ int Server_Player::newCounterId() const id = c->getId(); } } - // Skip reserved IDs 0-9 even in non-Commander games to prevent collision return std::max(id + 1, CounterIds::FirstUserId); } @@ -109,15 +114,7 @@ void Server_Player::setupZones() // Command zone for Commander format if (game->getEnableCommandZone()) { addZone(new Server_CardZone(this, ZoneNames::COMMAND, false, ServerInfo_Zone::PublicZone)); - for (int i = 0; i < CounterIds::TaxCounterCount; ++i) { - int id = CounterIds::taxCounterIdFromIndex(i); - const char *name = CounterNames::forId(id); - auto *counter = new Server_Counter(id, name, makeColor(128, 128, 128), 20, 0, 0, MAX_COUNTER_VALUE); - if (i > 0) { - (void)counter->setActive(false); - } - addCounter(counter); - } + castCounts.insert(1, 0); } // ------------------------------------------------------------------ @@ -174,6 +171,7 @@ void Server_Player::clearZones() delete counter; } counters.clear(); + castCounts.clear(); lastDrawList.clear(); } @@ -445,11 +443,8 @@ Server_Player::cmdUndoDraw(const Command_UndoDraw & /*cmd*/, ResponseContainer & return retVal; } -Response::ResponseCode Server_Player::evaluateModifyCounter(bool gameStarted, - bool playerConceded, - bool commandZoneEnabled, - int counterId, - const Server_Counter *counter) +Response::ResponseCode +Server_Player::evaluateModifyCounter(bool gameStarted, bool playerConceded, const Server_Counter *counter) { if (!gameStarted) { return Response::RespGameNotStarted; @@ -457,17 +452,6 @@ Response::ResponseCode Server_Player::evaluateModifyCounter(bool gameStarted, if (playerConceded) { return Response::RespContextError; } - if (CounterIds::isTaxCounter(counterId)) { - // Tax counters are server-managed: they only exist in Commander games, and an - // inactive (hidden) tax counter must stay at zero. Block modification in either - // case so the value can never diverge from what players can see. - if (!commandZoneEnabled) { - return Response::RespContextError; - } - if (counter && !counter->isActive()) { - return Response::RespContextError; - } - } if (!counter) { return Response::RespNameNotFound; } @@ -480,8 +464,7 @@ Server_Player::cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer & const int counterId = cmd.counter_id(); Server_Counter *c = counters.value(counterId, nullptr); - const Response::ResponseCode authResult = - evaluateModifyCounter(game->getGameStarted(), conceded, game->getEnableCommandZone(), counterId, c); + const Response::ResponseCode authResult = evaluateModifyCounter(game->getGameStarted(), conceded, c); if (authResult != Response::RespOk) { return authResult; } @@ -498,7 +481,7 @@ Server_Player::cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer & } Response::ResponseCode -Server_Player::evaluateCreateCounter(bool gameStarted, bool playerConceded, const QString &counterName) +Server_Player::evaluateCreateCounter(bool gameStarted, bool playerConceded, const QString & /*counterName*/) { if (!gameStarted) { return Response::RespGameNotStarted; @@ -506,12 +489,6 @@ Server_Player::evaluateCreateCounter(bool gameStarted, bool playerConceded, cons if (playerConceded) { return Response::RespContextError; } - // Reserved system counter names (commander/partner tax) are how clients identify - // server-managed tax counters for rendering and logging; a client must not be able - // to spoof one via a user-created counter. - if (CounterNames::isTaxCounter(counterName)) { - return Response::RespFunctionNotAllowed; - } return Response::RespOk; } @@ -546,8 +523,7 @@ Server_Player::cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer & const int counterId = cmd.counter_id(); Server_Counter *c = counters.value(counterId, nullptr); - const Response::ResponseCode authResult = - evaluateModifyCounter(game->getGameStarted(), conceded, game->getEnableCommandZone(), counterId, c); + const Response::ResponseCode authResult = evaluateModifyCounter(game->getGameStarted(), conceded, c); if (authResult != Response::RespOk) { return authResult; } @@ -564,7 +540,7 @@ Server_Player::cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer & } Response::ResponseCode -Server_Player::evaluateDelCounter(bool gameStarted, bool playerConceded, int counterId, const Server_Counter *counter) +Server_Player::evaluateDelCounter(bool gameStarted, bool playerConceded, const Server_Counter *counter) { if (!gameStarted) { return Response::RespGameNotStarted; @@ -572,12 +548,6 @@ Server_Player::evaluateDelCounter(bool gameStarted, bool playerConceded, int cou if (playerConceded) { 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; - } if (!counter) { return Response::RespNameNotFound; } @@ -590,7 +560,7 @@ Server_Player::cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer & const int counterId = cmd.counter_id(); Server_Counter *counter = counters.value(counterId, nullptr); - const Response::ResponseCode authResult = evaluateDelCounter(game->getGameStarted(), conceded, counterId, counter); + const Response::ResponseCode authResult = evaluateDelCounter(game->getGameStarted(), conceded, counter); if (authResult != Response::RespOk) { return authResult; } @@ -605,14 +575,14 @@ Server_Player::cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer & return Response::RespOk; } -Response::ResponseCode Server_Player::evaluateSetCounterActive(bool gameStarted, - bool playerConceded, - bool commandZoneEnabled, - int counterId, - const Server_Counter *counter, - bool requestedActive, - const Server_Counter *predecessorCounter, - const Server_Counter *successorCounter) +// Cast count validation and command handlers + +Response::ResponseCode Server_Player::evaluateCreateCastCount(bool gameStarted, + bool playerConceded, + bool commandZoneEnabled, + int index, + bool exists, + bool predecessorExists) { if (!gameStarted) { return Response::RespGameNotStarted; @@ -620,53 +590,166 @@ Response::ResponseCode Server_Player::evaluateSetCounterActive(bool gameStarted, if (playerConceded) { return Response::RespContextError; } - if (!CounterIds::isTaxCounter(counterId)) { - return Response::RespFunctionNotAllowed; - } if (!commandZoneEnabled) { return Response::RespContextError; } - if (!counter) { - return Response::RespNameNotFound; - } - // Prevent disabling a counter with tax accumulated; player must reset to 0 first - if (!requestedActive && counter->getCount() != 0) { + if (!CastCountIds::isValidIndex(index)) { return Response::RespContextError; } - // Enforce ordering: can only activate if predecessor is active - if (requestedActive && predecessorCounter && !predecessorCounter->isActive()) { + if (exists) { return Response::RespContextError; } - // Enforce ordering: can only deactivate if successor is inactive - if (!requestedActive && successorCounter && successorCounter->isActive()) { + if (index > 1 && !predecessorExists) { return Response::RespContextError; } return Response::RespOk; } -Response::ResponseCode Server_Player::cmdSetCounterActive(const Command_SetCounterActive &cmd, - ResponseContainer & /*rc*/, - GameEventStorage &ges) +Response::ResponseCode Server_Player::evaluateDeleteCastCount(bool gameStarted, + bool playerConceded, + bool commandZoneEnabled, + int index, + bool exists, + int value, + bool successorExists) { - const int counterId = cmd.counter_id(); - Server_Counter *c = counters.value(counterId, nullptr); + if (!gameStarted) { + return Response::RespGameNotStarted; + } + if (playerConceded) { + return Response::RespContextError; + } + if (!commandZoneEnabled) { + return Response::RespContextError; + } + if (!CastCountIds::isValidIndex(index)) { + return Response::RespContextError; + } + if (!exists) { + return Response::RespNameNotFound; + } + if (value != 0) { + return Response::RespContextError; + } + if (successorExists) { + return Response::RespContextError; + } + return Response::RespOk; +} - int predecessorId = CounterIds::taxCounterIdFromIndex(CounterIds::taxCounterIndex(counterId) - 1); - int successorId = CounterIds::taxCounterIdFromIndex(CounterIds::taxCounterIndex(counterId) + 1); - Server_Counter *predecessor = predecessorId >= 0 ? counters.value(predecessorId, nullptr) : nullptr; - Server_Counter *successor = successorId >= 0 ? counters.value(successorId, nullptr) : nullptr; +Response::ResponseCode Server_Player::evaluateModifyCastCount(bool gameStarted, + bool playerConceded, + bool commandZoneEnabled, + int index, + bool exists) +{ + if (!gameStarted) { + return Response::RespGameNotStarted; + } + if (playerConceded) { + return Response::RespContextError; + } + if (!commandZoneEnabled) { + return Response::RespContextError; + } + if (!CastCountIds::isValidIndex(index)) { + return Response::RespContextError; + } + if (!exists) { + return Response::RespNameNotFound; + } + return Response::RespOk; +} - const Response::ResponseCode authResult = - evaluateSetCounterActive(game->getGameStarted(), conceded, game->getEnableCommandZone(), counterId, c, - cmd.active(), predecessor, successor); +Response::ResponseCode +Server_Player::cmdCreateCastCount(const Command_CreateCastCount &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) +{ + const int index = cmd.index(); + bool exists = castCounts.contains(index); + bool predecessorExists = (index == 1) || castCounts.contains(index - 1); + + const Response::ResponseCode authResult = evaluateCreateCastCount( + game->getGameStarted(), conceded, game->getEnableCommandZone(), index, exists, predecessorExists); if (authResult != Response::RespOk) { return authResult; } - if (c->setActive(cmd.active())) { - Event_SetCounterActive event; - event.set_counter_id(c->getId()); - event.set_active(c->isActive()); + castCounts.insert(index, 0); + + Event_CreateCastCount event; + event.set_index(index); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; +} + +Response::ResponseCode +Server_Player::cmdDeleteCastCount(const Command_DeleteCastCount &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) +{ + const int index = cmd.index(); + bool exists = castCounts.contains(index); + int value = exists ? castCounts.value(index) : 0; + bool successorExists = castCounts.contains(index + 1); + + const Response::ResponseCode authResult = evaluateDeleteCastCount( + game->getGameStarted(), conceded, game->getEnableCommandZone(), index, exists, value, successorExists); + if (authResult != Response::RespOk) { + return authResult; + } + + castCounts.remove(index); + + Event_DeleteCastCount event; + event.set_index(index); + ges.enqueueGameEvent(event, playerId); + + return Response::RespOk; +} + +Response::ResponseCode +Server_Player::cmdIncCastCount(const Command_IncCastCount &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) +{ + const int index = cmd.index(); + bool exists = castCounts.contains(index); + + const Response::ResponseCode authResult = + evaluateModifyCastCount(game->getGameStarted(), conceded, game->getEnableCommandZone(), index, exists); + if (authResult != Response::RespOk) { + return authResult; + } + + int oldValue = castCounts.value(index); + int newValue = qBound(0, oldValue + cmd.delta(), MAX_COUNTER_VALUE); + if (newValue != oldValue) { + castCounts.insert(index, newValue); + Event_SetCastCount event; + event.set_index(index); + event.set_value(newValue); + ges.enqueueGameEvent(event, playerId); + } + + return Response::RespOk; +} + +Response::ResponseCode +Server_Player::cmdSetCastCount(const Command_SetCastCount &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) +{ + const int index = cmd.index(); + bool exists = castCounts.contains(index); + + const Response::ResponseCode authResult = + evaluateModifyCastCount(game->getGameStarted(), conceded, game->getEnableCommandZone(), index, exists); + if (authResult != Response::RespOk) { + return authResult; + } + + int oldValue = castCounts.value(index); + int newValue = qBound(0, cmd.value(), MAX_COUNTER_VALUE); + if (newValue != oldValue) { + castCounts.insert(index, newValue); + Event_SetCastCount event; + event.set_index(index); + event.set_value(newValue); ges.enqueueGameEvent(event, playerId); } @@ -746,4 +829,10 @@ void Server_Player::getInfo(ServerInfo_Player *info, for (Server_Counter *counter : counters) { counter->getInfo(info->add_counter_list()); } + + for (auto it = castCounts.constBegin(); it != castCounts.constEnd(); ++it) { + auto *castCount = info->add_cast_count_list(); + castCount->set_index(it.key()); + castCount->set_value(it.value()); + } } diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h index 2513896fa..921eaf555 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h @@ -8,6 +8,7 @@ class Server_Player : public Server_AbstractPlayer Q_OBJECT private: QMap counters; + QMap castCounts; // index (1-5) -> value QList lastDrawList; public: @@ -21,90 +22,40 @@ public: { return counters; } + const QMap &getCastCounts() const + { + return castCounts; + } int newCounterId() const; void addCounter(Server_Counter *counter); - /** @name Counter command authorization - * Decision logic extracted from the corresponding cmd* handlers so it can be unit-tested - * in isolation. Each function takes all relevant state as parameters and touches no - * instance members, hence static. - * @{ - */ - - /** - * @brief Decide whether a client may delete a counter. - * - * Reserved tax counters are server-managed and may never be deleted by a client. - * - * @param gameStarted Whether the game has started - * @param playerConceded Whether the player has conceded - * @param counterId ID of the counter to delete - * @param counter Counter with id counterId, or nullptr if the player has no such counter. - * @return Response::RespOk if permitted, otherwise the error response for the client. - */ static Response::ResponseCode - evaluateDelCounter(bool gameStarted, bool playerConceded, int counterId, const Server_Counter *counter); + evaluateDelCounter(bool gameStarted, bool playerConceded, const Server_Counter *counter); - /** - * @brief Decide whether a client may change a counter's active (visible) state. - * - * Only reserved tax counters can be toggled, and one holding a non-zero value must be reset - * to zero before it can be deactivated. - * - * @param gameStarted Whether the game has started - * @param playerConceded Whether the player has conceded - * @param commandZoneEnabled Whether command zone is enabled for this game - * @param counterId ID of the counter to toggle - * @param counter Counter with id counterId, or nullptr if the player has no such counter. - * @param requestedActive Active state the client asked for. - * @param predecessorCounter Tax counter that must be active before this one can be activated (nullptr if none). - * @param successorCounter Tax counter that must be inactive before this one can be deactivated (nullptr if none). - * @return Response::RespOk if permitted, otherwise the error response for the client. - */ - static Response::ResponseCode evaluateSetCounterActive(bool gameStarted, - bool playerConceded, - bool commandZoneEnabled, - int counterId, - const Server_Counter *counter, - bool requestedActive, - const Server_Counter *predecessorCounter, - const Server_Counter *successorCounter); + static Response::ResponseCode + evaluateModifyCounter(bool gameStarted, bool playerConceded, const Server_Counter *counter); - /** - * @brief Decide whether a client may change a counter's value. - * - * Shared by cmdIncCounter and cmdSetCounter. Reserved tax counters may only be modified - * inside a Commander game and only while active, so an inactive (hidden) tax counter can - * never accumulate a value behind the scenes. - * - * @param gameStarted Whether the game has started - * @param playerConceded Whether the player has conceded - * @param commandZoneEnabled Whether command zone is enabled for this game - * @param counterId ID of the counter to modify - * @param counter Counter with id counterId, or nullptr if the player has no such counter. - * @return Response::RespOk if permitted, otherwise the error response for the client. - */ - static Response::ResponseCode evaluateModifyCounter(bool gameStarted, - bool playerConceded, - bool commandZoneEnabled, - int counterId, - const Server_Counter *counter); - - /** - * @brief Decide whether a client may create a counter with the given name. - * - * Reserved system counter names (commander/partner tax) are rejected to prevent - * clients from spoofing server-managed tax counters. - * - * @param gameStarted Whether the game has started - * @param playerConceded Whether the player has conceded - * @param counterName Name requested for the new counter - * @return Response::RespOk if permitted, otherwise the error response for the client. - */ static Response::ResponseCode evaluateCreateCounter(bool gameStarted, bool playerConceded, const QString &counterName); - /** @} */ + // Cast count validation + static Response::ResponseCode evaluateCreateCastCount(bool gameStarted, + bool playerConceded, + bool commandZoneEnabled, + int index, + bool exists, + bool predecessorExists); + + static Response::ResponseCode evaluateDeleteCastCount(bool gameStarted, + bool playerConceded, + bool commandZoneEnabled, + int index, + bool exists, + int value, + bool successorExists); + + static Response::ResponseCode + evaluateModifyCastCount(bool gameStarted, bool playerConceded, bool commandZoneEnabled, int index, bool exists); void setupZones() override; void clearZones() override; @@ -139,8 +90,6 @@ public: Response::ResponseCode cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override; Response::ResponseCode - cmdSetCounterActive(const Command_SetCounterActive &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode cmdNextTurn(const Command_NextTurn &cmd, ResponseContainer &rc, GameEventStorage &ges) override; Response::ResponseCode cmdSetActivePhase(const Command_SetActivePhase &cmd, ResponseContainer &rc, GameEventStorage &ges) override; @@ -150,6 +99,16 @@ public: ResponseContainer &rc, GameEventStorage &ges) override; + // Cast count commands + Response::ResponseCode + cmdCreateCastCount(const Command_CreateCastCount &cmd, ResponseContainer &rc, GameEventStorage &ges) override; + Response::ResponseCode + cmdDeleteCastCount(const Command_DeleteCastCount &cmd, ResponseContainer &rc, GameEventStorage &ges) override; + Response::ResponseCode + cmdIncCastCount(const Command_IncCastCount &cmd, ResponseContainer &rc, GameEventStorage &ges) override; + Response::ResponseCode + cmdSetCastCount(const Command_SetCastCount &cmd, ResponseContainer &rc, GameEventStorage &ges) override; + void getInfo(ServerInfo_Player *info, Server_AbstractParticipant *playerWhosAsking, bool omniscient, diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt index fb397544d..364b575f6 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/CMakeLists.txt @@ -10,6 +10,7 @@ set(PROTO_FILES command_change_zone_properties.proto command_concede.proto command_create_arrow.proto + command_create_cast_count.proto command_create_counter.proto command_create_token.proto command_deck_del.proto @@ -21,11 +22,13 @@ set(PROTO_FILES command_deck_upload.proto command_del_counter.proto command_delete_arrow.proto + command_delete_cast_count.proto command_draw_cards.proto command_dump_zone.proto command_flip_card.proto command_game_say.proto command_inc_card_counter.proto + command_inc_cast_count.proto command_inc_counter.proto command_kick_from_game.proto command_leave_game.proto @@ -45,8 +48,8 @@ set(PROTO_FILES command_set_active_phase.proto command_set_card_attr.proto command_set_card_counter.proto + command_set_cast_count.proto command_set_counter.proto - command_set_counter_active.proto command_set_sideboard_lock.proto command_set_sideboard_plan.proto command_shuffle.proto @@ -66,10 +69,12 @@ set(PROTO_FILES event_change_zone_properties.proto event_connection_closed.proto event_create_arrow.proto + event_create_cast_count.proto event_create_counter.proto event_create_token.proto event_del_counter.proto event_delete_arrow.proto + event_delete_cast_count.proto event_destroy_card.proto event_draw_cards.proto event_dump_zone.proto @@ -106,8 +111,8 @@ set(PROTO_FILES event_set_active_player.proto event_set_card_attr.proto event_set_card_counter.proto + event_set_cast_count.proto event_set_counter.proto - event_set_counter_active.proto event_shuffle.proto event_user_joined.proto event_user_left.proto @@ -149,6 +154,7 @@ set(PROTO_FILES server_message.proto serverinfo_arrow.proto serverinfo_ban.proto + serverinfo_cast_count.proto serverinfo_card.proto serverinfo_cardcounter.proto serverinfo_chat_message.proto diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_create_cast_count.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_create_cast_count.proto new file mode 100644 index 000000000..15173db9a --- /dev/null +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/command_create_cast_count.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; +import "game_commands.proto"; + +message Command_CreateCastCount { + extend GameCommand { + optional Command_CreateCastCount ext = 1036; + } + optional sint32 index = 1 [default = 1]; +} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_delete_cast_count.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_delete_cast_count.proto new file mode 100644 index 000000000..96982a1d3 --- /dev/null +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/command_delete_cast_count.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; +import "game_commands.proto"; + +message Command_DeleteCastCount { + extend GameCommand { + optional Command_DeleteCastCount ext = 1037; + } + optional sint32 index = 1 [default = 1]; +} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_inc_cast_count.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_inc_cast_count.proto new file mode 100644 index 000000000..e3fa723f8 --- /dev/null +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/command_inc_cast_count.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; +import "game_commands.proto"; + +message Command_IncCastCount { + extend GameCommand { + optional Command_IncCastCount ext = 1038; + } + optional sint32 index = 1 [default = 1]; + optional sint32 delta = 2 [default = 1]; +} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_cast_count.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_cast_count.proto new file mode 100644 index 000000000..34c656449 --- /dev/null +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_cast_count.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; +import "game_commands.proto"; + +message Command_SetCastCount { + extend GameCommand { + optional Command_SetCastCount ext = 1039; + } + optional sint32 index = 1 [default = 1]; + optional sint32 value = 2 [default = 0]; +} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_counter_active.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_counter_active.proto deleted file mode 100644 index 232fd69d5..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_set_counter_active.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "game_commands.proto"; -message Command_SetCounterActive { - extend GameCommand { - optional Command_SetCounterActive ext = 1035; - } - optional sint32 counter_id = 1 [default = -1]; - optional bool active = 2 [default = true]; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/event_create_cast_count.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/event_create_cast_count.proto new file mode 100644 index 000000000..1d92becbc --- /dev/null +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/event_create_cast_count.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; +import "game_event.proto"; + +message Event_CreateCastCount { + extend GameEvent { + optional Event_CreateCastCount ext = 2024; + } + optional sint32 index = 1 [default = 1]; +} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/event_delete_cast_count.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/event_delete_cast_count.proto new file mode 100644 index 000000000..da4a9938e --- /dev/null +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/event_delete_cast_count.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; +import "game_event.proto"; + +message Event_DeleteCastCount { + extend GameEvent { + optional Event_DeleteCastCount ext = 2025; + } + optional sint32 index = 1 [default = 1]; +} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/event_set_cast_count.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/event_set_cast_count.proto new file mode 100644 index 000000000..c9a2b2adc --- /dev/null +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/event_set_cast_count.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; +import "game_event.proto"; + +message Event_SetCastCount { + extend GameEvent { + optional Event_SetCastCount ext = 2026; + } + optional sint32 index = 1 [default = 1]; + optional sint32 value = 2 [default = 0]; +} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/event_set_counter_active.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/event_set_counter_active.proto deleted file mode 100644 index 27dc5fc7f..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/event_set_counter_active.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -import "game_event.proto"; - -message Event_SetCounterActive { - extend GameEvent { - optional Event_SetCounterActive ext = 2023; - } - optional sint32 counter_id = 1 [default = -1]; - optional bool active = 2 [default = true]; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto index 9292230a3..0bd9b85c0 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/game_commands.proto @@ -176,10 +176,25 @@ message GameCommand { /// Client: reflected via subsequent turn events REVERSE_TURN = 1034; - /// Show or hide a reserved tax counter without deleting it. - /// Server: Server_Player::cmdSetCounterActive - /// Client: PlayerEventHandler::eventSetCounterActive - SET_COUNTER_ACTIVE = 1035; + /// Create a cast count at the specified index. + /// Server: Server_Player::cmdCreateCastCount + /// Client: PlayerEventHandler::eventCreateCastCount + CREATE_CAST_COUNT = 1036; + + /// Delete a cast count at the specified index. + /// Server: Server_Player::cmdDeleteCastCount + /// Client: PlayerEventHandler::eventDeleteCastCount + DELETE_CAST_COUNT = 1037; + + /// Increment or decrement a cast count. + /// Server: Server_Player::cmdIncCastCount + /// Client: PlayerEventHandler::eventSetCastCount + INC_CAST_COUNT = 1038; + + /// Set a cast count to a specific value. + /// Server: Server_Player::cmdSetCastCount + /// Client: PlayerEventHandler::eventSetCastCount + SET_CAST_COUNT = 1039; } extensions 100 to max; diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/game_event.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/game_event.proto index 3b829ebc6..fdd8143c4 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/game_event.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/game_event.proto @@ -34,7 +34,9 @@ message GameEvent { CHANGE_ZONE_PROPERTIES = 2020; REVERSE_TURN = 2021; GAME_LOG_NOTICE = 2022; - SET_COUNTER_ACTIVE = 2023; + CREATE_CAST_COUNT = 2024; + DELETE_CAST_COUNT = 2025; + SET_CAST_COUNT = 2026; } optional sint32 player_id = 1 [default = -1]; extensions 100 to max; diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_cast_count.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_cast_count.proto new file mode 100644 index 000000000..7298dd43a --- /dev/null +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_cast_count.proto @@ -0,0 +1,6 @@ +syntax = "proto2"; + +message ServerInfo_CastCount { + optional sint32 index = 1; + optional sint32 value = 2; +} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_player.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_player.proto index 69cd4498a..7309b4fcf 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_player.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_player.proto @@ -3,6 +3,7 @@ import "serverinfo_zone.proto"; import "serverinfo_counter.proto"; import "serverinfo_arrow.proto"; import "serverinfo_playerproperties.proto"; +import "serverinfo_cast_count.proto"; message ServerInfo_Player { optional ServerInfo_PlayerProperties properties = 1; @@ -10,4 +11,5 @@ message ServerInfo_Player { repeated ServerInfo_Zone zone_list = 3; repeated ServerInfo_Counter counter_list = 4; repeated ServerInfo_Arrow arrow_list = 5; + repeated ServerInfo_CastCount cast_count_list = 6; } diff --git a/libcockatrice_utility/libcockatrice/utility/counter_ids.h b/libcockatrice_utility/libcockatrice/utility/counter_ids.h index a5c03f314..46d5e9027 100644 --- a/libcockatrice_utility/libcockatrice/utility/counter_ids.h +++ b/libcockatrice_utility/libcockatrice/utility/counter_ids.h @@ -1,7 +1,10 @@ /** * @file counter_ids.h - * @ingroup Core - * @brief Shared counter IDs and names for system counters (e.g. tax counters). + * @ingroup Utility + * @brief Counter ID ranges and cast count index utilities. + * + * Defines the boundary between built-in counters (life, mana colors, storm) and + * user-created counters, plus helpers for the cast count system used in Commander. */ #ifndef COCKATRICE_COUNTER_IDS_H @@ -11,95 +14,36 @@ /** * @namespace CounterIds - * @brief Shared counter IDs used by both client and server. - * - * Single source of truth: included directly by both sides, so they cannot drift. - * - * Reserved counter IDs for system counters: - * IDs 0-7: Standard player counters (life, mana colors, storm) - * IDs 8-12: Tax counters (1st through 5th) - * IDs 13+: Available for user-created counters (FirstUserId) - * - * newCounterId() always returns >= FirstUserId to prevent user counters - * from colliding with reserved IDs, even in non-Commander games. + * @brief Reserved counter ID ranges. */ namespace CounterIds { -constexpr int TaxCounter1 = 8; ///< 1st tax counter -constexpr int TaxCounter2 = 9; ///< 2nd tax counter -constexpr int TaxCounter3 = 10; ///< 3rd tax counter -constexpr int TaxCounter4 = 11; ///< 4th tax counter -constexpr int TaxCounter5 = 12; ///< 5th tax counter -constexpr int FirstUserId = 13; ///< First ID available for user-created counters - -constexpr int FirstTaxCounterId = TaxCounter1; -constexpr int LastTaxCounterId = TaxCounter5; -constexpr int TaxCounterCount = LastTaxCounterId - FirstTaxCounterId + 1; - -/** @brief Returns true if the given ID is a reserved tax counter. */ -inline bool isTaxCounter(int id) -{ - return id >= FirstTaxCounterId && id <= LastTaxCounterId; -} - -/** @brief Returns the tax counter index (0-based) for display, or -1 if not a tax counter. */ -inline int taxCounterIndex(int id) -{ - if (!isTaxCounter(id)) { - return -1; - } - return id - FirstTaxCounterId; -} - -/** @brief Returns the tax counter ID for the given 0-based index, or -1 if out of range. */ -inline int taxCounterIdFromIndex(int index) -{ - if (index < 0 || index >= TaxCounterCount) { - return -1; - } - return FirstTaxCounterId + index; -} +constexpr int FirstUserId = 8; ///< First ID available for user-created counters (0-7 are built-in) } // namespace CounterIds /** - * @namespace CounterNames - * @brief Reserved counter names for server-managed tax counters. + * @namespace CastCountIds + * @brief Cast count index range and utilities. * - * Used to reject user-created counters that would spoof system counters. + * Cast counts use a separate 1-based index space (1-5), not sharing IDs with regular counters. */ -namespace CounterNames +namespace CastCountIds { -constexpr const char *TaxCounter1 = "1st_tax_counter"; -constexpr const char *TaxCounter2 = "2nd_tax_counter"; -constexpr const char *TaxCounter3 = "3rd_tax_counter"; -constexpr const char *TaxCounter4 = "4th_tax_counter"; -constexpr const char *TaxCounter5 = "5th_tax_counter"; +constexpr int MinIndex = 1; ///< Minimum valid cast count index +constexpr int MaxIndex = 5; ///< Maximum valid cast count index +constexpr int Count = MaxIndex; ///< Total number of cast count slots -/** @brief Returns the name for the given tax counter ID, or nullptr if not a tax counter. */ -inline const char *forId(int id) +/** @brief Returns true if the index is in the valid cast count range [1, 5]. */ +inline bool isValidIndex(int index) { - switch (id) { - case CounterIds::TaxCounter1: - return TaxCounter1; - case CounterIds::TaxCounter2: - return TaxCounter2; - case CounterIds::TaxCounter3: - return TaxCounter3; - case CounterIds::TaxCounter4: - return TaxCounter4; - case CounterIds::TaxCounter5: - return TaxCounter5; - default: - return nullptr; - } + return index >= MinIndex && index <= MaxIndex; } -/** @brief Returns true if the given name is a reserved tax counter name. */ -inline bool isTaxCounter(const QString &name) +/** @brief Returns the internal name for a cast count at the given index (e.g. "cast_count_1"). */ +inline QString nameForIndex(int index) { - return name == TaxCounter1 || name == TaxCounter2 || name == TaxCounter3 || name == TaxCounter4 || - name == TaxCounter5; + return QString("cast_count_%1").arg(index); } -} // namespace CounterNames +} // namespace CastCountIds #endif // COCKATRICE_COUNTER_IDS_H diff --git a/libcockatrice_utility/libcockatrice/utility/counter_limits.h b/libcockatrice_utility/libcockatrice/utility/counter_limits.h index 1343dbb3f..8a34a2948 100644 --- a/libcockatrice_utility/libcockatrice/utility/counter_limits.h +++ b/libcockatrice_utility/libcockatrice/utility/counter_limits.h @@ -6,7 +6,7 @@ * * Caps an individual counter's VALUE (e.g. a +1/+1 counter at 999), not how many counters * something holds. Applies to counters that are constrained to a non-negative display range, - * such as card counters and commander tax. Unbounded counters (e.g. a player's life total) + * such as card counters and cast counts. Unbounded counters (e.g. a player's life total) * do not use this limit and may go negative, saturating only at the int range. * * The max of 999 is a display constraint (3-digit rendering) and a reasonable gameplay limit. diff --git a/tests/command_zone_tests/counter_command_auth_test.cpp b/tests/command_zone_tests/counter_command_auth_test.cpp index 92c0ee45f..63cbbc590 100644 --- a/tests/command_zone_tests/counter_command_auth_test.cpp +++ b/tests/command_zone_tests/counter_command_auth_test.cpp @@ -18,156 +18,35 @@ namespace { constexpr int UserCounterId = CounterIds::FirstUserId; -Server_Counter makeCounter(int id, int count, bool active = true) +Server_Counter makeCounter(int id, int count) { - Server_Counter c(id, "c", color(), 20, count); - (void)c.setActive(active); - return c; + return Server_Counter(id, "c", color(), 20, count); } } // namespace TEST(EvaluateDelCounter, RejectsWhenGameNotStarted) { Server_Counter counter = makeCounter(UserCounterId, 0); - EXPECT_EQ( - Server_Player::evaluateDelCounter(/*gameStarted=*/false, /*playerConceded=*/false, UserCounterId, &counter), - Response::RespGameNotStarted); + EXPECT_EQ(Server_Player::evaluateDelCounter(/*gameStarted=*/false, /*playerConceded=*/false, &counter), + Response::RespGameNotStarted); } TEST(EvaluateDelCounter, RejectsWhenPlayerConceded) { Server_Counter counter = makeCounter(UserCounterId, 0); - EXPECT_EQ(Server_Player::evaluateDelCounter(/*gameStarted=*/true, /*playerConceded=*/true, UserCounterId, &counter), + EXPECT_EQ(Server_Player::evaluateDelCounter(/*gameStarted=*/true, /*playerConceded=*/true, &counter), Response::RespContextError); } -TEST(EvaluateDelCounter, RejectsTaxCounters) -{ - Server_Counter tax1 = makeCounter(CounterIds::TaxCounter1, 0); - EXPECT_EQ(Server_Player::evaluateDelCounter(true, false, CounterIds::TaxCounter1, &tax1), - Response::RespFunctionNotAllowed); - - Server_Counter tax2 = makeCounter(CounterIds::TaxCounter2, 0); - EXPECT_EQ(Server_Player::evaluateDelCounter(true, false, CounterIds::TaxCounter2, &tax2), - Response::RespFunctionNotAllowed); -} - TEST(EvaluateDelCounter, RejectsMissingCounter) { - EXPECT_EQ(Server_Player::evaluateDelCounter(true, false, UserCounterId, nullptr), Response::RespNameNotFound); + EXPECT_EQ(Server_Player::evaluateDelCounter(true, false, nullptr), Response::RespNameNotFound); } TEST(EvaluateDelCounter, AllowsDeletingUserCounter) { Server_Counter counter = makeCounter(UserCounterId, 7); - EXPECT_EQ(Server_Player::evaluateDelCounter(true, false, UserCounterId, &counter), Response::RespOk); -} - -TEST(EvaluateDelCounter, GameNotStartedTakesPrecedenceOverTaxGuard) -{ - Server_Counter tax1 = makeCounter(CounterIds::TaxCounter1, 0); - EXPECT_EQ(Server_Player::evaluateDelCounter(false, false, CounterIds::TaxCounter1, &tax1), - Response::RespGameNotStarted); -} - -TEST(EvaluateSetCounterActive, RejectsWhenGameNotStarted) -{ - Server_Counter counter = makeCounter(CounterIds::TaxCounter1, 0); - EXPECT_EQ(Server_Player::evaluateSetCounterActive(/*gameStarted=*/false, /*playerConceded=*/false, - /*commandZoneEnabled=*/true, CounterIds::TaxCounter1, &counter, - /*requestedActive=*/true, nullptr, nullptr), - Response::RespGameNotStarted); -} - -TEST(EvaluateSetCounterActive, RejectsWhenPlayerConceded) -{ - Server_Counter counter = makeCounter(CounterIds::TaxCounter1, 0); - EXPECT_EQ(Server_Player::evaluateSetCounterActive(true, /*playerConceded=*/true, true, CounterIds::TaxCounter1, - &counter, true, nullptr, nullptr), - Response::RespContextError); -} - -TEST(EvaluateSetCounterActive, RejectsNonTaxCounter) -{ - Server_Counter counter = makeCounter(UserCounterId, 0); - EXPECT_EQ( - Server_Player::evaluateSetCounterActive(true, false, true, UserCounterId, &counter, true, nullptr, nullptr), - Response::RespFunctionNotAllowed); -} - -TEST(EvaluateSetCounterActive, RejectsWhenCommandZoneDisabled) -{ - Server_Counter counter = makeCounter(CounterIds::TaxCounter1, 0); - EXPECT_EQ(Server_Player::evaluateSetCounterActive(true, false, /*commandZoneEnabled=*/false, - CounterIds::TaxCounter1, &counter, true, nullptr, nullptr), - Response::RespContextError); -} - -TEST(EvaluateSetCounterActive, RejectsMissingCounter) -{ - EXPECT_EQ(Server_Player::evaluateSetCounterActive(true, false, true, CounterIds::TaxCounter1, nullptr, true, - nullptr, nullptr), - Response::RespNameNotFound); -} - -TEST(EvaluateSetCounterActive, RejectsDisablingWhenTaxAccumulated) -{ - Server_Counter counter = makeCounter(CounterIds::TaxCounter1, 3); - EXPECT_EQ(Server_Player::evaluateSetCounterActive(true, false, true, CounterIds::TaxCounter1, &counter, - /*requestedActive=*/false, nullptr, nullptr), - Response::RespContextError); -} - -TEST(EvaluateSetCounterActive, AllowsEnablingWithAccumulatedTax) -{ - Server_Counter counter = makeCounter(CounterIds::TaxCounter1, 3); - EXPECT_EQ(Server_Player::evaluateSetCounterActive(true, false, true, CounterIds::TaxCounter1, &counter, - /*requestedActive=*/true, nullptr, nullptr), - Response::RespOk); -} - -TEST(EvaluateSetCounterActive, AllowsDisablingWhenCounterIsZero) -{ - Server_Counter counter = makeCounter(CounterIds::TaxCounter1, 0); - EXPECT_EQ(Server_Player::evaluateSetCounterActive(true, false, true, CounterIds::TaxCounter1, &counter, - /*requestedActive=*/false, nullptr, nullptr), - Response::RespOk); -} - -TEST(EvaluateSetCounterActive, AllowsEnabling2ndTaxWhen1stIsActive) -{ - Server_Counter tax1 = makeCounter(CounterIds::TaxCounter1, 0, true); - Server_Counter tax2 = makeCounter(CounterIds::TaxCounter2, 0, false); - EXPECT_EQ(Server_Player::evaluateSetCounterActive(true, false, true, CounterIds::TaxCounter2, &tax2, - /*requestedActive=*/true, &tax1, nullptr), - Response::RespOk); -} - -TEST(EvaluateSetCounterActive, RejectsEnabling2ndTaxWhen1stIsInactive) -{ - Server_Counter tax1 = makeCounter(CounterIds::TaxCounter1, 0, false); - Server_Counter tax2 = makeCounter(CounterIds::TaxCounter2, 0, false); - EXPECT_EQ(Server_Player::evaluateSetCounterActive(true, false, true, CounterIds::TaxCounter2, &tax2, - /*requestedActive=*/true, &tax1, nullptr), - Response::RespContextError); -} - -TEST(EvaluateSetCounterActive, AllowsDisabling1stTaxWhen2ndIsInactive) -{ - Server_Counter tax1 = makeCounter(CounterIds::TaxCounter1, 0, true); - Server_Counter tax2 = makeCounter(CounterIds::TaxCounter2, 0, false); - EXPECT_EQ(Server_Player::evaluateSetCounterActive(true, false, true, CounterIds::TaxCounter1, &tax1, - /*requestedActive=*/false, nullptr, &tax2), - Response::RespOk); -} - -TEST(EvaluateSetCounterActive, RejectsDisabling1stTaxWhen2ndIsActive) -{ - Server_Counter tax1 = makeCounter(CounterIds::TaxCounter1, 0, true); - Server_Counter tax2 = makeCounter(CounterIds::TaxCounter2, 0, true); - EXPECT_EQ(Server_Player::evaluateSetCounterActive(true, false, true, CounterIds::TaxCounter1, &tax1, - /*requestedActive=*/false, nullptr, &tax2), - Response::RespContextError); + EXPECT_EQ(Server_Player::evaluateDelCounter(true, false, &counter), Response::RespOk); } TEST(EvaluateCreateCounter, RejectsWhenGameNotStarted) @@ -181,20 +60,6 @@ TEST(EvaluateCreateCounter, RejectsWhenPlayerConceded) EXPECT_EQ(Server_Player::evaluateCreateCounter(true, /*playerConceded=*/true, "test"), Response::RespContextError); } -TEST(EvaluateCreateCounter, RejectsTaxCounterNames) -{ - EXPECT_EQ(Server_Player::evaluateCreateCounter(true, false, CounterNames::TaxCounter1), - Response::RespFunctionNotAllowed); - EXPECT_EQ(Server_Player::evaluateCreateCounter(true, false, CounterNames::TaxCounter2), - Response::RespFunctionNotAllowed); - EXPECT_EQ(Server_Player::evaluateCreateCounter(true, false, CounterNames::TaxCounter3), - Response::RespFunctionNotAllowed); - EXPECT_EQ(Server_Player::evaluateCreateCounter(true, false, CounterNames::TaxCounter4), - Response::RespFunctionNotAllowed); - EXPECT_EQ(Server_Player::evaluateCreateCounter(true, false, CounterNames::TaxCounter5), - Response::RespFunctionNotAllowed); -} - TEST(EvaluateCreateCounter, AllowsUserCounterName) { EXPECT_EQ(Server_Player::evaluateCreateCounter(true, false, "poison"), Response::RespOk); @@ -203,61 +68,151 @@ TEST(EvaluateCreateCounter, AllowsUserCounterName) TEST(EvaluateModifyCounter, RejectsWhenGameNotStarted) { Server_Counter counter = makeCounter(UserCounterId, 0); - EXPECT_EQ(Server_Player::evaluateModifyCounter(/*gameStarted=*/false, /*playerConceded=*/false, true, UserCounterId, - &counter), + EXPECT_EQ(Server_Player::evaluateModifyCounter(/*gameStarted=*/false, /*playerConceded=*/false, &counter), Response::RespGameNotStarted); } TEST(EvaluateModifyCounter, RejectsWhenPlayerConceded) { Server_Counter counter = makeCounter(UserCounterId, 0); - EXPECT_EQ(Server_Player::evaluateModifyCounter(true, /*playerConceded=*/true, true, UserCounterId, &counter), + EXPECT_EQ(Server_Player::evaluateModifyCounter(true, /*playerConceded=*/true, &counter), Response::RespContextError); } TEST(EvaluateModifyCounter, RejectsMissingCounter) { - EXPECT_EQ(Server_Player::evaluateModifyCounter(true, false, true, UserCounterId, nullptr), - Response::RespNameNotFound); + EXPECT_EQ(Server_Player::evaluateModifyCounter(true, false, nullptr), Response::RespNameNotFound); } TEST(EvaluateModifyCounter, AllowsUserCounter) { Server_Counter counter = makeCounter(UserCounterId, 5); - EXPECT_EQ(Server_Player::evaluateModifyCounter(true, false, true, UserCounterId, &counter), Response::RespOk); + EXPECT_EQ(Server_Player::evaluateModifyCounter(true, false, &counter), Response::RespOk); } -TEST(EvaluateModifyCounter, RejectsTaxCounterWhenCommandZoneDisabled) +// Cast count evaluation tests + +TEST(EvaluateCreateCastCount, RejectsWhenGameNotStarted) { - Server_Counter counter = makeCounter(CounterIds::TaxCounter1, 0); - EXPECT_EQ(Server_Player::evaluateModifyCounter(true, false, /*commandZoneEnabled=*/false, CounterIds::TaxCounter1, - &counter), + EXPECT_EQ(Server_Player::evaluateCreateCastCount(/*gameStarted=*/false, /*playerConceded=*/false, + /*commandZoneEnabled=*/true, 1, /*exists=*/false, + /*predecessorExists=*/true), + Response::RespGameNotStarted); +} + +TEST(EvaluateCreateCastCount, RejectsWhenPlayerConceded) +{ + EXPECT_EQ(Server_Player::evaluateCreateCastCount(true, /*playerConceded=*/true, true, 1, false, true), Response::RespContextError); } -TEST(EvaluateModifyCounter, RejectsInactiveTaxCounter) +TEST(EvaluateCreateCastCount, RejectsWhenCommandZoneDisabled) { - Server_Counter counter = makeCounter(CounterIds::TaxCounter2, 0, false); - EXPECT_EQ(Server_Player::evaluateModifyCounter(true, false, /*commandZoneEnabled=*/true, CounterIds::TaxCounter2, - &counter), + EXPECT_EQ(Server_Player::evaluateCreateCastCount(true, false, /*commandZoneEnabled=*/false, 1, false, true), Response::RespContextError); } -TEST(EvaluateModifyCounter, AllowsActiveTaxCounter) +TEST(EvaluateCreateCastCount, RejectsInvalidIndex) { - Server_Counter counter = makeCounter(CounterIds::TaxCounter1, 0, true); - EXPECT_EQ(Server_Player::evaluateModifyCounter(true, false, /*commandZoneEnabled=*/true, CounterIds::TaxCounter1, - &counter), + EXPECT_EQ(Server_Player::evaluateCreateCastCount(true, false, true, 0, false, false), Response::RespContextError); + EXPECT_EQ(Server_Player::evaluateCreateCastCount(true, false, true, 6, false, false), Response::RespContextError); +} + +TEST(EvaluateCreateCastCount, RejectsExistingIndex) +{ + EXPECT_EQ(Server_Player::evaluateCreateCastCount(true, false, true, 1, /*exists=*/true, true), + Response::RespContextError); +} + +TEST(EvaluateCreateCastCount, RejectsGapInSequence) +{ + EXPECT_EQ( + Server_Player::evaluateCreateCastCount(true, false, true, 3, /*exists=*/false, /*predecessorExists=*/false), + Response::RespContextError); +} + +TEST(EvaluateCreateCastCount, AllowsNextInSequence) +{ + EXPECT_EQ( + Server_Player::evaluateCreateCastCount(true, false, true, 2, /*exists=*/false, /*predecessorExists=*/true), + Response::RespOk); +} + +TEST(EvaluateDeleteCastCount, RejectsWhenGameNotStarted) +{ + EXPECT_EQ(Server_Player::evaluateDeleteCastCount(/*gameStarted=*/false, /*playerConceded=*/false, + /*commandZoneEnabled=*/true, 1, /*exists=*/true, /*value=*/0, + /*successorExists=*/false), + Response::RespGameNotStarted); +} + +TEST(EvaluateDeleteCastCount, RejectsWhenPlayerConceded) +{ + EXPECT_EQ(Server_Player::evaluateDeleteCastCount(true, /*playerConceded=*/true, true, 1, true, 0, false), + Response::RespContextError); +} + +TEST(EvaluateDeleteCastCount, RejectsWhenCommandZoneDisabled) +{ + EXPECT_EQ(Server_Player::evaluateDeleteCastCount(true, false, /*commandZoneEnabled=*/false, 1, true, 0, false), + Response::RespContextError); +} + +TEST(EvaluateDeleteCastCount, RejectsMissingIndex) +{ + EXPECT_EQ(Server_Player::evaluateDeleteCastCount(true, false, true, 1, /*exists=*/false, 0, false), + Response::RespNameNotFound); +} + +TEST(EvaluateDeleteCastCount, RejectsNonZeroValue) +{ + EXPECT_EQ(Server_Player::evaluateDeleteCastCount(true, false, true, 1, true, /*value=*/5, false), + Response::RespContextError); +} + +TEST(EvaluateDeleteCastCount, RejectsNonHighestIndex) +{ + EXPECT_EQ(Server_Player::evaluateDeleteCastCount(true, false, true, 1, true, 0, /*successorExists=*/true), + Response::RespContextError); +} + +TEST(EvaluateDeleteCastCount, AllowsHighestWithZeroValue) +{ + EXPECT_EQ(Server_Player::evaluateDeleteCastCount(true, false, true, 2, /*exists=*/true, /*value=*/0, + /*successorExists=*/false), Response::RespOk); } -TEST(EvaluateModifyCounter, RejectsMissingTaxCounter) +TEST(EvaluateModifyCastCount, RejectsWhenGameNotStarted) { - EXPECT_EQ(Server_Player::evaluateModifyCounter(true, false, /*commandZoneEnabled=*/true, CounterIds::TaxCounter1, - nullptr), + EXPECT_EQ(Server_Player::evaluateModifyCastCount(/*gameStarted=*/false, /*playerConceded=*/false, + /*commandZoneEnabled=*/true, 1, /*exists=*/true), + Response::RespGameNotStarted); +} + +TEST(EvaluateModifyCastCount, RejectsWhenPlayerConceded) +{ + EXPECT_EQ(Server_Player::evaluateModifyCastCount(true, /*playerConceded=*/true, true, 1, true), + Response::RespContextError); +} + +TEST(EvaluateModifyCastCount, RejectsWhenCommandZoneDisabled) +{ + EXPECT_EQ(Server_Player::evaluateModifyCastCount(true, false, /*commandZoneEnabled=*/false, 1, true), + Response::RespContextError); +} + +TEST(EvaluateModifyCastCount, RejectsMissingIndex) +{ + EXPECT_EQ(Server_Player::evaluateModifyCastCount(true, false, true, 1, /*exists=*/false), Response::RespNameNotFound); } +TEST(EvaluateModifyCastCount, AllowsExistingIndex) +{ + EXPECT_EQ(Server_Player::evaluateModifyCastCount(true, false, true, 1, /*exists=*/true), Response::RespOk); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/tests/command_zone_tests/new_counter_id_test.cpp b/tests/command_zone_tests/new_counter_id_test.cpp index dfd2239b7..a3245ea5d 100644 --- a/tests/command_zone_tests/new_counter_id_test.cpp +++ b/tests/command_zone_tests/new_counter_id_test.cpp @@ -49,14 +49,6 @@ TEST(NewCounterId, SkipsReservedRangeWhenOnlyReservedCountersExist) EXPECT_EQ(f.player.newCounterId(), CounterIds::FirstUserId); } -TEST(NewCounterId, SkipsTaxCounterIds) -{ - PlayerFixture f; - f.player.addCounter(new Server_Counter(CounterIds::TaxCounter1, "tax1", color(), 0, 0)); - f.player.addCounter(new Server_Counter(CounterIds::TaxCounter2, "tax2", color(), 0, 0)); - EXPECT_EQ(f.player.newCounterId(), CounterIds::FirstUserId); -} - TEST(NewCounterId, ReturnsNextIdAboveHighestUserCounter) { PlayerFixture f; diff --git a/tests/command_zone_tests/setup_zones_command_zone_test.cpp b/tests/command_zone_tests/setup_zones_command_zone_test.cpp index 16f9fb9ef..ad4ba5cd6 100644 --- a/tests/command_zone_tests/setup_zones_command_zone_test.cpp +++ b/tests/command_zone_tests/setup_zones_command_zone_test.cpp @@ -47,7 +47,7 @@ struct CommandZoneTestPlayer : Server_Player /** * @brief Builds a Server_Player on a minimal fake game whose command zone is toggled by the - * constructor argument, then runs setupZones() so getCounters() reflects the result. + * constructor argument, then runs setupZones() so getCastCounts() reflects the result. */ struct SetupFixture { @@ -86,55 +86,27 @@ struct SetupFixture }; } // namespace -TEST(SetupZonesCommandZone, CreatesAllTaxCountersWhenEnabled) +TEST(SetupZonesCommandZone, CreatesFirstCastCountWhenEnabled) { SetupFixture f(true); - const QMap &counters = f.player.getCounters(); + const QMap &castCounts = f.player.getCastCounts(); EXPECT_TRUE(f.player.getZones().contains(ZoneNames::COMMAND)); - for (int i = 0; i < CounterIds::TaxCounterCount; ++i) { - int id = CounterIds::taxCounterIdFromIndex(i); - ASSERT_TRUE(counters.contains(id)); - } - - const Server_Counter *tax1 = counters.value(CounterIds::TaxCounter1); - EXPECT_TRUE(tax1->isActive()); - EXPECT_EQ(tax1->getCount(), 0); - - for (int i = 1; i < CounterIds::TaxCounterCount; ++i) { - int id = CounterIds::taxCounterIdFromIndex(i); - const Server_Counter *counter = counters.value(id); - EXPECT_FALSE(counter->isActive()); - EXPECT_EQ(counter->getCount(), 0); - } + EXPECT_EQ(castCounts.size(), 1); + EXPECT_TRUE(castCounts.contains(1)); + EXPECT_EQ(castCounts.value(1), 0); } -TEST(SetupZonesCommandZone, TaxCountersUseBounds) -{ - SetupFixture f(true); - Server_Counter *tax1 = f.player.getCounters().value(CounterIds::TaxCounter1); - ASSERT_NE(tax1, nullptr); - - EXPECT_TRUE(tax1->setCount(MAX_COUNTER_VALUE + 1000)); - EXPECT_EQ(tax1->getCount(), MAX_COUNTER_VALUE); - EXPECT_TRUE(tax1->setCount(-1)); - EXPECT_EQ(tax1->getCount(), 0); -} - -TEST(SetupZonesCommandZone, NoTaxCountersWhenDisabled) +TEST(SetupZonesCommandZone, NoCastCountsWhenDisabled) { SetupFixture f(false); - const QMap &counters = f.player.getCounters(); + const QMap &castCounts = f.player.getCastCounts(); EXPECT_FALSE(f.player.getZones().contains(ZoneNames::COMMAND)); + EXPECT_TRUE(castCounts.isEmpty()); - for (int i = 0; i < CounterIds::TaxCounterCount; ++i) { - int id = CounterIds::taxCounterIdFromIndex(i); - EXPECT_FALSE(counters.contains(id)); - } - - EXPECT_TRUE(counters.contains(0)); + EXPECT_TRUE(f.player.getCounters().contains(0)); } int main(int argc, char **argv) diff --git a/tests/server_counter_test.cpp b/tests/server_counter_test.cpp index bde632a27..691281927 100644 --- a/tests/server_counter_test.cpp +++ b/tests/server_counter_test.cpp @@ -102,13 +102,13 @@ TEST(ServerCounter, IncrementClampsToCustomBounds) EXPECT_EQ(c.getCount(), 0); } -TEST(ServerCounter, CustomBoundsForCommanderTax) +TEST(ServerCounter, CustomBoundsForCastCount) { - Server_Counter taxCounter(1, "tax", color(), 20, 0, 0, MAX_COUNTER_VALUE); - EXPECT_TRUE(taxCounter.setCount(1000)); - EXPECT_EQ(taxCounter.getCount(), MAX_COUNTER_VALUE); - EXPECT_TRUE(taxCounter.setCount(-5)); - EXPECT_EQ(taxCounter.getCount(), 0); + Server_Counter castCount(1, "cast_count", color(), 20, 0, 0, MAX_COUNTER_VALUE); + EXPECT_TRUE(castCount.setCount(1000)); + EXPECT_EQ(castCount.getCount(), MAX_COUNTER_VALUE); + EXPECT_TRUE(castCount.setCount(-5)); + EXPECT_EQ(castCount.getCount(), 0); } TEST(ServerCounter, SetActiveReportsChangeOnce)