mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-28 08:52:19 -07:00
Merge f5e49a571e into 48776cfeba
This commit is contained in:
commit
5f2094bc49
97 changed files with 2628 additions and 127 deletions
1
Testing/Temporary/CTestCostData.txt
Normal file
1
Testing/Temporary/CTestCostData.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
---
|
||||||
3
Testing/Temporary/LastTest.log
Normal file
3
Testing/Temporary/LastTest.log
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
Start testing: Aug 10 22:25 EDT
|
||||||
|
----------------------------------------------------------
|
||||||
|
End testing: Aug 10 22:25 EDT
|
||||||
|
|
@ -67,6 +67,7 @@ set(cockatrice_SOURCES
|
||||||
src/game_graphics/board/card_item.cpp
|
src/game_graphics/board/card_item.cpp
|
||||||
src/game/board/card_list.cpp
|
src/game/board/card_list.cpp
|
||||||
src/game/board/card_state.cpp
|
src/game/board/card_state.cpp
|
||||||
|
src/game_graphics/board/cast_count_widget.cpp
|
||||||
src/game_graphics/board/counter_general.cpp
|
src/game_graphics/board/counter_general.cpp
|
||||||
src/game/board/counter_state.cpp
|
src/game/board/counter_state.cpp
|
||||||
src/game_graphics/board/translate_counter_name.cpp
|
src/game_graphics/board/translate_counter_name.cpp
|
||||||
|
|
@ -87,6 +88,7 @@ set(cockatrice_SOURCES
|
||||||
src/game/phase.cpp
|
src/game/phase.cpp
|
||||||
src/game_graphics/phases_toolbar.cpp
|
src/game_graphics/phases_toolbar.cpp
|
||||||
src/game_graphics/player/menu/card_menu.cpp
|
src/game_graphics/player/menu/card_menu.cpp
|
||||||
|
src/game_graphics/player/menu/command_zone_menu.cpp
|
||||||
src/game_graphics/player/menu/custom_zone_menu.cpp
|
src/game_graphics/player/menu/custom_zone_menu.cpp
|
||||||
src/game_graphics/player/menu/grave_menu.cpp
|
src/game_graphics/player/menu/grave_menu.cpp
|
||||||
src/game_graphics/player/menu/hand_menu.cpp
|
src/game_graphics/player/menu/hand_menu.cpp
|
||||||
|
|
@ -114,6 +116,8 @@ set(cockatrice_SOURCES
|
||||||
src/game_graphics/player/player_target.cpp
|
src/game_graphics/player/player_target.cpp
|
||||||
src/game/replay.cpp
|
src/game/replay.cpp
|
||||||
src/game/zones/card_zone_logic.cpp
|
src/game/zones/card_zone_logic.cpp
|
||||||
|
src/game_graphics/zones/command_zone.cpp
|
||||||
|
src/game/zones/command_zone_logic.cpp
|
||||||
src/game/zones/hand_zone_logic.cpp
|
src/game/zones/hand_zone_logic.cpp
|
||||||
src/game/zones/pile_zone_logic.cpp
|
src/game/zones/pile_zone_logic.cpp
|
||||||
src/game/zones/stack_zone_logic.cpp
|
src/game/zones/stack_zone_logic.cpp
|
||||||
|
|
|
||||||
|
|
@ -563,6 +563,9 @@ private:
|
||||||
{"Player/aMoveToBottomLibrary", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Bottom of Library"),
|
{"Player/aMoveToBottomLibrary", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Bottom of Library"),
|
||||||
parseSequenceString("Ctrl+B"),
|
parseSequenceString("Ctrl+B"),
|
||||||
ShortcutGroup::Move_selected)},
|
ShortcutGroup::Move_selected)},
|
||||||
|
{"Player/aMoveToCommandZone", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Command Zone"),
|
||||||
|
parseSequenceString(""),
|
||||||
|
ShortcutGroup::Move_selected)},
|
||||||
{"Player/aMoveToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile"),
|
{"Player/aMoveToExile", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Exile"),
|
||||||
parseSequenceString(""),
|
parseSequenceString(""),
|
||||||
ShortcutGroup::Move_selected)},
|
ShortcutGroup::Move_selected)},
|
||||||
|
|
@ -594,6 +597,18 @@ private:
|
||||||
{"Player/aViewBottomCards", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Bottom Cards of Library"),
|
{"Player/aViewBottomCards", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Bottom Cards of Library"),
|
||||||
parseSequenceString("Ctrl+Shift+W"),
|
parseSequenceString("Ctrl+Shift+W"),
|
||||||
ShortcutGroup::View)},
|
ShortcutGroup::View)},
|
||||||
|
{"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"),
|
{"Player/aCloseMostRecentZoneView", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Close Recent View"),
|
||||||
parseSequenceString("Esc"),
|
parseSequenceString("Esc"),
|
||||||
ShortcutGroup::View)},
|
ShortcutGroup::View)},
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,22 @@
|
||||||
|
|
||||||
#include <libcockatrice/utility/color.h>
|
#include <libcockatrice/utility/color.h>
|
||||||
|
|
||||||
CounterState::CounterState(int id, const QString &name, const QColor &color, int radius, int value, QObject *parent)
|
CounterState::CounterState(int _id,
|
||||||
: QObject(parent), id(id), name(name), color(color), radius(radius), value(value)
|
const QString &_name,
|
||||||
|
const QColor &_color,
|
||||||
|
int _radius,
|
||||||
|
int _value,
|
||||||
|
bool _active,
|
||||||
|
QObject *parent)
|
||||||
|
: QObject(parent), id(_id), name(_name), color(_color), radius(_radius), value(_value), active(_active)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CounterState *CounterState::fromProto(const ServerInfo_Counter &counter, QObject *parent)
|
CounterState *CounterState::fromProto(const ServerInfo_Counter &counter, QObject *parent)
|
||||||
{
|
{
|
||||||
return new CounterState(counter.id(), QString::fromStdString(counter.name()),
|
return new CounterState(counter.id(), QString::fromStdString(counter.name()),
|
||||||
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count(), parent);
|
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count(),
|
||||||
|
counter.active(), parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CounterState::setValue(int newValue)
|
void CounterState::setValue(int newValue)
|
||||||
|
|
@ -22,3 +29,12 @@ void CounterState::setValue(int newValue)
|
||||||
value = newValue;
|
value = newValue;
|
||||||
emit valueChanged(old, newValue);
|
emit valueChanged(old, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CounterState::setActive(bool newActive)
|
||||||
|
{
|
||||||
|
if (newActive == active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
active = newActive;
|
||||||
|
emit activeChanged(newActive);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,13 @@ class CounterState : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CounterState(int id, const QString &name, const QColor &color, int radius, int value, QObject *parent = nullptr);
|
CounterState(int _id,
|
||||||
|
const QString &_name,
|
||||||
|
const QColor &_color,
|
||||||
|
int _radius,
|
||||||
|
int _value,
|
||||||
|
bool _active = true,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
static CounterState *fromProto(const ServerInfo_Counter &counter, QObject *parent = nullptr);
|
static CounterState *fromProto(const ServerInfo_Counter &counter, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
|
@ -34,11 +40,20 @@ public:
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
/** @brief Returns whether this counter is active (visible and modifiable). */
|
||||||
|
bool isActive() const
|
||||||
|
{
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
void setValue(int newValue);
|
void setValue(int newValue);
|
||||||
|
/** @brief Sets the active (visible) state and emits activeChanged if it changed. */
|
||||||
|
void setActive(bool newActive);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void valueChanged(int oldValue, int newValue);
|
void valueChanged(int oldValue, int newValue);
|
||||||
|
/** @brief Emitted when the counter's active state changes. */
|
||||||
|
void activeChanged(bool newActive);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int id;
|
int id;
|
||||||
|
|
@ -46,6 +61,7 @@ private:
|
||||||
QColor color;
|
QColor color;
|
||||||
int radius;
|
int radius;
|
||||||
int value;
|
int value;
|
||||||
|
bool active; ///< Inactive counters are hidden; server rejects modification attempts
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COCKATRICE_COUNTER_STATE_H
|
#endif // COCKATRICE_COUNTER_STATE_H
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,20 @@
|
||||||
#include "../../game_graphics/zones/table_zone.h"
|
#include "../../game_graphics/zones/table_zone.h"
|
||||||
#include "../../interface/widgets/tabs/tab_game.h"
|
#include "../../interface/widgets/tabs/tab_game.h"
|
||||||
#include "../../interface/widgets/utility/get_text_with_max.h"
|
#include "../../interface/widgets/utility/get_text_with_max.h"
|
||||||
|
#include "../board/counter_state.h"
|
||||||
#include "../zones/view_zone_logic.h"
|
#include "../zones/view_zone_logic.h"
|
||||||
|
|
||||||
#include <libcockatrice/card/database/card_database_manager.h>
|
#include <libcockatrice/card/database/card_database_manager.h>
|
||||||
#include <libcockatrice/card/relation/card_relation.h>
|
#include <libcockatrice/card/relation/card_relation.h>
|
||||||
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
|
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_change_zone_properties.pb.h>
|
#include <libcockatrice/protocol/pb/command_change_zone_properties.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_create_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_create_token.pb.h>
|
#include <libcockatrice/protocol/pb/command_create_token.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_delete_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
|
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_flip_card.pb.h>
|
#include <libcockatrice/protocol/pb/command_flip_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_game_say.pb.h>
|
#include <libcockatrice/protocol/pb/command_game_say.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_inc_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_inc_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_inc_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_mulligan.pb.h>
|
#include <libcockatrice/protocol/pb/command_mulligan.pb.h>
|
||||||
|
|
@ -27,9 +31,11 @@
|
||||||
#include <libcockatrice/protocol/pb/command_shuffle.pb.h>
|
#include <libcockatrice/protocol/pb/command_shuffle.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_undo_draw.pb.h>
|
#include <libcockatrice/protocol/pb/command_undo_draw.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/context_move_card.pb.h>
|
#include <libcockatrice/protocol/pb/context_move_card.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pending_command.h>
|
||||||
#include <libcockatrice/settings/card_override_settings.h>
|
#include <libcockatrice/settings/card_override_settings.h>
|
||||||
#include <libcockatrice/settings/interface_settings.h>
|
#include <libcockatrice/settings/interface_settings.h>
|
||||||
#include <libcockatrice/utility/clamped_arithmetic.h>
|
#include <libcockatrice/utility/clamped_arithmetic.h>
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
#include <libcockatrice/utility/counter_limits.h>
|
#include <libcockatrice/utility/counter_limits.h>
|
||||||
#include <libcockatrice/utility/expression.h>
|
#include <libcockatrice/utility/expression.h>
|
||||||
#include <libcockatrice/utility/zone_names.h>
|
#include <libcockatrice/utility/zone_names.h>
|
||||||
|
|
@ -48,10 +54,10 @@ PlayerActions::PlayerActions(PlayerLogic *_player)
|
||||||
connect(moveTopCardTimer, &QTimer::timeout, [this]() { actMoveTopCardToPlay(); });
|
connect(moveTopCardTimer, &QTimer::timeout, [this]() { actMoveTopCardToPlay(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerActions::playCard(CardItem *card, bool faceDown)
|
PendingCommand *PlayerActions::prepareCardMove(CardItem *card, bool faceDown)
|
||||||
{
|
{
|
||||||
if (card == nullptr) {
|
if (card == nullptr) {
|
||||||
return;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Command_MoveCard cmd;
|
Command_MoveCard cmd;
|
||||||
|
|
@ -63,7 +69,7 @@ void PlayerActions::playCard(CardItem *card, bool faceDown)
|
||||||
|
|
||||||
ExactCard exactCard = card->getCard();
|
ExactCard exactCard = card->getCard();
|
||||||
if (!exactCard) {
|
if (!exactCard) {
|
||||||
return;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CardInfo &info = exactCard.getInfo();
|
const CardInfo &info = exactCard.getInfo();
|
||||||
|
|
@ -94,7 +100,14 @@ void PlayerActions::playCard(CardItem *card, bool faceDown)
|
||||||
cmd.set_x(gridPoint.x());
|
cmd.set_x(gridPoint.x());
|
||||||
cmd.set_y(gridPoint.y());
|
cmd.set_y(gridPoint.y());
|
||||||
}
|
}
|
||||||
sendGameCommand(cmd);
|
return prepareGameCommand(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerActions::playCard(CardItem *card, bool faceDown)
|
||||||
|
{
|
||||||
|
if (PendingCommand *pend = prepareCardMove(card, faceDown)) {
|
||||||
|
sendGameCommand(pend);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1631,6 +1644,14 @@ static bool isUnwritableRevealZone(CardZoneLogic *zone)
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerActions::playSelectedCards(QList<CardItem *> selectedCards, const bool faceDown)
|
void PlayerActions::playSelectedCards(QList<CardItem *> selectedCards, const bool faceDown)
|
||||||
|
{
|
||||||
|
playSelectedCardsImpl(selectedCards, faceDown, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerActions::playSelectedCardsImpl(
|
||||||
|
QList<CardItem *> selectedCards,
|
||||||
|
bool faceDown,
|
||||||
|
const std::function<void(PendingCommand *, const QString &)> &postPlayCallback)
|
||||||
{
|
{
|
||||||
// CardIds will get shuffled downwards when cards leave the deck.
|
// CardIds will get shuffled downwards when cards leave the deck.
|
||||||
// We need to iterate through the cards in reverse order so cardIds don't get changed out from under us as we play
|
// We need to iterate through the cards in reverse order so cardIds don't get changed out from under us as we play
|
||||||
|
|
@ -1640,11 +1661,98 @@ void PlayerActions::playSelectedCards(QList<CardItem *> selectedCards, const boo
|
||||||
|
|
||||||
for (auto &card : selectedCards) {
|
for (auto &card : selectedCards) {
|
||||||
if (card && !isUnwritableRevealZone(card->getZone()) && card->getZone()->getName() != ZoneNames::TABLE) {
|
if (card && !isUnwritableRevealZone(card->getZone()) && card->getZone()->getName() != ZoneNames::TABLE) {
|
||||||
playCard(card, faceDown);
|
const QString originalZone = card->getZone()->getName();
|
||||||
|
PendingCommand *pend = prepareCardMove(card, faceDown);
|
||||||
|
if (pend == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Connect before send: a local game processes the command synchronously inside
|
||||||
|
// sendGameCommand, firing the pend's finished signal before this call returns.
|
||||||
|
if (postPlayCallback) {
|
||||||
|
postPlayCallback(pend, originalZone);
|
||||||
|
}
|
||||||
|
sendGameCommand(pend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayerActions::actPlayAndIncrease1stCastCount(QList<CardItem *> selectedCards)
|
||||||
|
{
|
||||||
|
playAndIncreaseCastCount(selectedCards, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerActions::actPlayAndIncrease2ndCastCount(QList<CardItem *> selectedCards)
|
||||||
|
{
|
||||||
|
playAndIncreaseCastCount(selectedCards, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerActions::playAndIncreaseCastCount(QList<CardItem *> selectedCards, int index)
|
||||||
|
{
|
||||||
|
playSelectedCardsImpl(selectedCards, false, [this, index](PendingCommand *pend, const QString &originalZone) {
|
||||||
|
if (originalZone != ZoneNames::COMMAND || pend == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Gate the increment on the server accepting the move
|
||||||
|
connect(pend, &PendingCommand::finished, this,
|
||||||
|
[this, index](const Response &response, const CommandContainer &, const QVariant &) {
|
||||||
|
if (response.response_code() != Response::RespOk) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CounterState *state = player->getCastCount(index);
|
||||||
|
if (state) {
|
||||||
|
Command_IncCastCount cmd;
|
||||||
|
cmd.set_index(index);
|
||||||
|
cmd.set_delta(1);
|
||||||
|
sendGameCommand(cmd);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerActions::sendIncCounter(int counterId, int delta)
|
||||||
|
{
|
||||||
|
Command_IncCounter cmd;
|
||||||
|
cmd.set_counter_id(counterId);
|
||||||
|
cmd.set_delta(delta);
|
||||||
|
sendGameCommand(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerActions::actModifyCastCount(int index, int delta)
|
||||||
|
{
|
||||||
|
if (!CastCountIds::isValidIndex(index)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CounterState *state = player->getCastCount(index);
|
||||||
|
if (!state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Command_IncCastCount cmd;
|
||||||
|
cmd.set_index(index);
|
||||||
|
cmd.set_delta(delta);
|
||||||
|
sendGameCommand(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerActions::actToggleCastCount(int index)
|
||||||
|
{
|
||||||
|
if (!CastCountIds::isValidIndex(index)) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerActions::actPlay(QList<CardItem *> selectedCards)
|
void PlayerActions::actPlay(QList<CardItem *> selectedCards)
|
||||||
{
|
{
|
||||||
playSelectedCards(selectedCards, false);
|
playSelectedCards(selectedCards, false);
|
||||||
|
|
@ -1925,6 +2033,18 @@ void PlayerActions::cardMenuAction(QList<CardItem *> selectedCards, CardMenuActi
|
||||||
commandList.append(cmd);
|
commandList.append(cmd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case cmMoveToCommandZone: {
|
||||||
|
auto *cmd = new Command_MoveCard;
|
||||||
|
cmd->set_start_player_id(startPlayerId);
|
||||||
|
cmd->set_start_zone(startZone.toStdString());
|
||||||
|
cmd->mutable_cards_to_move()->CopyFrom(idList);
|
||||||
|
cmd->set_target_player_id(player->getPlayerInfo()->getId());
|
||||||
|
cmd->set_target_zone(ZoneNames::COMMAND);
|
||||||
|
cmd->set_x(0);
|
||||||
|
cmd->set_y(0);
|
||||||
|
commandList.append(cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case cmMoveToTable: {
|
case cmMoveToTable: {
|
||||||
// Each card needs its own command because table row, pt, and cipt vary per card
|
// Each card needs its own command because table row, pt, and cipt vary per card
|
||||||
for (const auto &card : cardList) {
|
for (const auto &card : cardList) {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <functional>
|
||||||
#include <libcockatrice/card/relation/card_relation_type.h>
|
#include <libcockatrice/card/relation/card_relation_type.h>
|
||||||
#include <libcockatrice/filters/filter_string.h>
|
#include <libcockatrice/filters/filter_string.h>
|
||||||
|
|
||||||
|
|
@ -126,6 +127,14 @@ public slots:
|
||||||
|
|
||||||
void actPlay(QList<CardItem *> selectedCards);
|
void actPlay(QList<CardItem *> selectedCards);
|
||||||
void actPlayFacedown(QList<CardItem *> selectedCards);
|
void actPlayFacedown(QList<CardItem *> selectedCards);
|
||||||
|
/** @brief Plays the selected card and increments the 1st cast count. */
|
||||||
|
void actPlayAndIncrease1stCastCount(QList<CardItem *> selectedCards);
|
||||||
|
/** @brief Plays the selected card and increments the 2nd cast count. */
|
||||||
|
void actPlayAndIncrease2ndCastCount(QList<CardItem *> 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<CardItem *> selectedCards);
|
void actHide(QList<CardItem *> selectedCards);
|
||||||
|
|
||||||
void actMoveTopCardToPlay();
|
void actMoveTopCardToPlay();
|
||||||
|
|
@ -219,6 +228,9 @@ public slots:
|
||||||
void cardMenuAction(QList<CardItem *> selectedCards, CardMenuActionType type);
|
void cardMenuAction(QList<CardItem *> selectedCards, CardMenuActionType type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/** @brief Sends an increment command for the specified counter. */
|
||||||
|
void sendIncCounter(int counterId, int delta);
|
||||||
|
|
||||||
PlayerLogic *player;
|
PlayerLogic *player;
|
||||||
|
|
||||||
int defaultNumberTopCards = 1;
|
int defaultNumberTopCards = 1;
|
||||||
|
|
@ -245,6 +257,35 @@ private:
|
||||||
|
|
||||||
void playSelectedCards(QList<CardItem *> selectedCards, bool faceDown = false);
|
void playSelectedCards(QList<CardItem *> selectedCards, bool faceDown = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Builds the move command for playing a card, returning the prepared (unsent) PendingCommand.
|
||||||
|
* @param card The card to play
|
||||||
|
* @param faceDown Whether to play the card face-down
|
||||||
|
* @return The prepared command, or nullptr if the card cannot be played.
|
||||||
|
*/
|
||||||
|
PendingCommand *prepareCardMove(CardItem *card, bool faceDown);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shared implementation for playing selected cards with an optional per-card callback.
|
||||||
|
* @param selectedCards Cards to play
|
||||||
|
* @param faceDown Whether to play cards face-down
|
||||||
|
* @param postPlayCallback Called for each card once its move is prepared but before it is sent,
|
||||||
|
* receiving the move's PendingCommand and the card's *original* zone name (the move may
|
||||||
|
* change the card's zone). Connect response handling here so it is attached before send.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
playSelectedCardsImpl(QList<CardItem *> selectedCards,
|
||||||
|
bool faceDown,
|
||||||
|
const std::function<void(PendingCommand *, const QString &)> &postPlayCallback = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Plays the selected cards and, for each that came from the command zone and whose move
|
||||||
|
* the server accepts, increments the given cast count by one.
|
||||||
|
* @param selectedCards Cards to play
|
||||||
|
* @param index The cast count index (1-5)
|
||||||
|
*/
|
||||||
|
void playAndIncreaseCastCount(QList<CardItem *> selectedCards, int index);
|
||||||
|
|
||||||
void cmdSetTopCard(Command_MoveCard &cmd);
|
void cmdSetTopCard(Command_MoveCard &cmd);
|
||||||
void cmdSetBottomCard(Command_MoveCard &cmd);
|
void cmdSetBottomCard(Command_MoveCard &cmd);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,12 @@
|
||||||
#include <libcockatrice/protocol/pb/event_attach_card.pb.h>
|
#include <libcockatrice/protocol/pb/event_attach_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_change_zone_properties.pb.h>
|
#include <libcockatrice/protocol/pb/event_change_zone_properties.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_create_arrow.pb.h>
|
#include <libcockatrice/protocol/pb/event_create_arrow.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/event_create_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_create_counter.pb.h>
|
#include <libcockatrice/protocol/pb/event_create_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_create_token.pb.h>
|
#include <libcockatrice/protocol/pb/event_create_token.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_del_counter.pb.h>
|
#include <libcockatrice/protocol/pb/event_del_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_delete_arrow.pb.h>
|
#include <libcockatrice/protocol/pb/event_delete_arrow.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/event_delete_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_destroy_card.pb.h>
|
#include <libcockatrice/protocol/pb/event_destroy_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_draw_cards.pb.h>
|
#include <libcockatrice/protocol/pb/event_draw_cards.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_dump_zone.pb.h>
|
#include <libcockatrice/protocol/pb/event_dump_zone.pb.h>
|
||||||
|
|
@ -30,6 +32,7 @@
|
||||||
#include <libcockatrice/protocol/pb/event_roll_die.pb.h>
|
#include <libcockatrice/protocol/pb/event_roll_die.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_set_card_attr.pb.h>
|
#include <libcockatrice/protocol/pb/event_set_card_attr.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_set_card_counter.pb.h>
|
#include <libcockatrice/protocol/pb/event_set_card_counter.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/event_set_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_set_counter.pb.h>
|
#include <libcockatrice/protocol/pb/event_set_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_shuffle.pb.h>
|
#include <libcockatrice/protocol/pb/event_shuffle.pb.h>
|
||||||
#include <libcockatrice/utility/color.h>
|
#include <libcockatrice/utility/color.h>
|
||||||
|
|
@ -264,13 +267,16 @@ void PlayerEventHandler::eventCreateCounter(const Event_CreateCounter &event)
|
||||||
|
|
||||||
void PlayerEventHandler::eventSetCounter(const Event_SetCounter &event)
|
void PlayerEventHandler::eventSetCounter(const Event_SetCounter &event)
|
||||||
{
|
{
|
||||||
CounterState *ctr = player->getCounters().value(event.counter_id(), nullptr);
|
CounterState *state = player->getCounters().value(event.counter_id(), nullptr);
|
||||||
if (!ctr) {
|
if (!state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int oldValue = ctr->getValue();
|
int oldValue = state->getValue();
|
||||||
ctr->setValue(event.value());
|
state->setValue(event.value());
|
||||||
emit logSetCounter(player, ctr->getName(), event.value(), oldValue);
|
|
||||||
|
if (event.value() != oldValue) {
|
||||||
|
emit logSetCounter(player, state->getId(), state->getName(), event.value(), oldValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerEventHandler::eventDelCounter(const Event_DelCounter &event)
|
void PlayerEventHandler::eventDelCounter(const Event_DelCounter &event)
|
||||||
|
|
@ -278,6 +284,21 @@ void PlayerEventHandler::eventDelCounter(const Event_DelCounter &event)
|
||||||
player->delCounter(event.counter_id());
|
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)
|
void PlayerEventHandler::eventDumpZone(const Event_DumpZone &event)
|
||||||
{
|
{
|
||||||
PlayerLogic *zoneOwner = player->getGame()->getPlayerManager()->getPlayers().value(event.zone_owner_id(), 0);
|
PlayerLogic *zoneOwner = player->getGame()->getPlayerManager()->getPlayers().value(event.zone_owner_id(), 0);
|
||||||
|
|
@ -630,6 +651,15 @@ void PlayerEventHandler::processGameEvent(GameEvent::GameEventType type,
|
||||||
case GameEvent::DEL_COUNTER:
|
case GameEvent::DEL_COUNTER:
|
||||||
eventDelCounter(event.GetExtension(Event_DelCounter::ext));
|
eventDelCounter(event.GetExtension(Event_DelCounter::ext));
|
||||||
break;
|
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:
|
case GameEvent::DUMP_ZONE:
|
||||||
eventDumpZone(event.GetExtension(Event_DumpZone::ext));
|
eventDumpZone(event.GetExtension(Event_DumpZone::ext));
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@ class Event_RollDie;
|
||||||
class Event_SetCardAttr;
|
class Event_SetCardAttr;
|
||||||
class Event_SetCardCounter;
|
class Event_SetCardCounter;
|
||||||
class Event_SetCounter;
|
class Event_SetCounter;
|
||||||
|
class Event_CreateCastCount;
|
||||||
|
class Event_DeleteCastCount;
|
||||||
|
class Event_SetCastCount;
|
||||||
class Event_Shuffle;
|
class Event_Shuffle;
|
||||||
class Event_GameLogNotice;
|
class Event_GameLogNotice;
|
||||||
|
|
||||||
|
|
@ -158,6 +161,15 @@ public:
|
||||||
/// Delete a player-level counter.
|
/// Delete a player-level counter.
|
||||||
void eventDelCounter(const Event_DelCounter &event);
|
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
|
/** @name Zone-level operations
|
||||||
|
|
@ -246,7 +258,7 @@ signals:
|
||||||
void logUnattachCard(PlayerLogic *player, QString cardName);
|
void logUnattachCard(PlayerLogic *player, QString cardName);
|
||||||
void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue);
|
void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue);
|
||||||
void logSetTapped(PlayerLogic *player, CardItem *card, bool tapped);
|
void logSetTapped(PlayerLogic *player, CardItem *card, bool tapped);
|
||||||
void logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue);
|
void logSetCounter(PlayerLogic *player, int counterId, QString counterName, int value, int oldValue);
|
||||||
void logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap);
|
void logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap);
|
||||||
void logSetPT(PlayerLogic *player, CardItem *card, QString newPT);
|
void logSetPT(PlayerLogic *player, CardItem *card, QString newPT);
|
||||||
void logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation);
|
void logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation);
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/serverinfo_zone.pb.h>
|
#include <libcockatrice/protocol/pb/serverinfo_zone.pb.h>
|
||||||
#include <libcockatrice/utility/color.h>
|
#include <libcockatrice/utility/color.h>
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
|
|
||||||
PlayerLogic::PlayerLogic(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent)
|
PlayerLogic::PlayerLogic(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent)
|
||||||
: QObject(_parent), game(_parent), playerInfo(new PlayerInfo(info, _id, _local, _judge)),
|
: QObject(_parent), game(_parent), playerInfo(new PlayerInfo(info, _id, _local, _judge)),
|
||||||
|
|
@ -48,6 +49,9 @@ void PlayerLogic::initializeZones()
|
||||||
bool visibleHand = playerInfo->getLocalOrJudge() ||
|
bool visibleHand = playerInfo->getLocalOrJudge() ||
|
||||||
(game->getPlayerManager()->isSpectator() && game->getGameMetaInfo()->spectatorsOmniscient());
|
(game->getPlayerManager()->isSpectator() && game->getGameMetaInfo()->spectatorsOmniscient());
|
||||||
addZone(new HandZoneLogic(this, ZoneNames::HAND, false, false, visibleHand, this));
|
addZone(new HandZoneLogic(this, ZoneNames::HAND, false, false, visibleHand, this));
|
||||||
|
if (game->getGameMetaInfo()->proto().enable_command_zone()) {
|
||||||
|
addZone(new CommandZoneLogic(this, ZoneNames::COMMAND, true, false, true, this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerLogic::~PlayerLogic()
|
PlayerLogic::~PlayerLogic()
|
||||||
|
|
@ -104,7 +108,9 @@ void PlayerLogic::processPlayerInfo(const ServerInfo_Player &info)
|
||||||
/* StackZone */
|
/* StackZone */
|
||||||
ZoneNames::STACK,
|
ZoneNames::STACK,
|
||||||
/* HandZone */
|
/* HandZone */
|
||||||
ZoneNames::HAND};
|
ZoneNames::HAND,
|
||||||
|
/* CommandZone */
|
||||||
|
ZoneNames::COMMAND};
|
||||||
clearCounters();
|
clearCounters();
|
||||||
emit arrowsClearedLocally();
|
emit arrowsClearedLocally();
|
||||||
|
|
||||||
|
|
@ -253,15 +259,17 @@ void PlayerLogic::setDeck(const DeckList &_deck)
|
||||||
CounterState *PlayerLogic::addCounter(const ServerInfo_Counter &counter)
|
CounterState *PlayerLogic::addCounter(const ServerInfo_Counter &counter)
|
||||||
{
|
{
|
||||||
return addCounter(counter.id(), QString::fromStdString(counter.name()),
|
return addCounter(counter.id(), QString::fromStdString(counter.name()),
|
||||||
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count());
|
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count(),
|
||||||
|
counter.active());
|
||||||
}
|
}
|
||||||
|
|
||||||
CounterState *PlayerLogic::addCounter(int id, const QString &name, const QColor &color, int radius, int value)
|
CounterState *
|
||||||
|
PlayerLogic::addCounter(int id, const QString &name, const QColor &color, int radius, int value, bool active)
|
||||||
{
|
{
|
||||||
if (counters.contains(id)) {
|
if (counters.contains(id)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto *state = new CounterState(id, name, color, radius, value, this);
|
auto *state = new CounterState(id, name, color, radius, value, active, this);
|
||||||
counters.insert(id, state);
|
counters.insert(id, state);
|
||||||
emit counterAdded(state);
|
emit counterAdded(state);
|
||||||
return state;
|
return state;
|
||||||
|
|
@ -284,6 +292,49 @@ void PlayerLogic::clearCounters()
|
||||||
}
|
}
|
||||||
qDeleteAll(counters);
|
qDeleteAll(counters);
|
||||||
counters.clear();
|
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
|
CounterState *PlayerLogic::getLifeCounter() const
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include "../../interface/widgets/menus/tearoff_menu.h"
|
#include "../../interface/widgets/menus/tearoff_menu.h"
|
||||||
#include "../board/arrow_data.h"
|
#include "../board/arrow_data.h"
|
||||||
#include "../interface/deck_loader/loaded_deck.h"
|
#include "../interface/deck_loader/loaded_deck.h"
|
||||||
|
#include "../zones/command_zone_logic.h"
|
||||||
#include "../zones/hand_zone_logic.h"
|
#include "../zones/hand_zone_logic.h"
|
||||||
#include "../zones/pile_zone_logic.h"
|
#include "../zones/pile_zone_logic.h"
|
||||||
#include "../zones/stack_zone_logic.h"
|
#include "../zones/stack_zone_logic.h"
|
||||||
|
|
@ -25,6 +26,7 @@
|
||||||
#include <libcockatrice/filters/filter_string.h>
|
#include <libcockatrice/filters/filter_string.h>
|
||||||
#include <libcockatrice/protocol/pb/card_attributes.pb.h>
|
#include <libcockatrice/protocol/pb/card_attributes.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/game_event.pb.h>
|
#include <libcockatrice/protocol/pb/game_event.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
|
||||||
#include <libcockatrice/utility/zone_names.h>
|
#include <libcockatrice/utility/zone_names.h>
|
||||||
|
|
||||||
inline Q_LOGGING_CATEGORY(PlayerLog, "player");
|
inline Q_LOGGING_CATEGORY(PlayerLog, "player");
|
||||||
|
|
@ -77,6 +79,9 @@ signals:
|
||||||
void counterAdded(CounterState *state);
|
void counterAdded(CounterState *state);
|
||||||
void counterRemoved(int counterId);
|
void counterRemoved(int counterId);
|
||||||
void rearrangeCounters();
|
void rearrangeCounters();
|
||||||
|
void castCountAdded(int index, CounterState *state);
|
||||||
|
void castCountRemoved(int index);
|
||||||
|
void castCountValueChanged(int index, int value);
|
||||||
void activeChanged(bool active);
|
void activeChanged(bool active);
|
||||||
void zoneIdChanged(int zoneId);
|
void zoneIdChanged(int zoneId);
|
||||||
void concededChanged(int playerId, bool conceded);
|
void concededChanged(int playerId, bool conceded);
|
||||||
|
|
@ -91,6 +96,10 @@ signals:
|
||||||
public slots:
|
public slots:
|
||||||
void setActive(bool _active);
|
void setActive(bool _active);
|
||||||
void onRequestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed);
|
void onRequestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed);
|
||||||
|
void requestRearrangeCounters()
|
||||||
|
{
|
||||||
|
emit rearrangeCounters();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PlayerLogic(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent);
|
PlayerLogic(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent);
|
||||||
|
|
@ -191,8 +200,21 @@ public:
|
||||||
return qobject_cast<HandZoneLogic *>(zones.value(ZoneNames::HAND));
|
return qobject_cast<HandZoneLogic *>(zones.value(ZoneNames::HAND));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Returns the command zone logic, or nullptr if not present. */
|
||||||
|
CommandZoneLogic *getCommandZone() const
|
||||||
|
{
|
||||||
|
return qobject_cast<CommandZoneLogic *>(zones.value(ZoneNames::COMMAND));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Whether this game has command zone support (determined at game creation). */
|
||||||
|
bool hasCommandZone() const
|
||||||
|
{
|
||||||
|
return getCommandZone() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
CounterState *addCounter(const ServerInfo_Counter &counter);
|
CounterState *addCounter(const ServerInfo_Counter &counter);
|
||||||
CounterState *addCounter(int id, const QString &name, const QColor &color, int radius, int value);
|
CounterState *
|
||||||
|
addCounter(int id, const QString &name, const QColor &color, int radius, int value, bool active = true);
|
||||||
void delCounter(int counterId);
|
void delCounter(int counterId);
|
||||||
void clearCounters();
|
void clearCounters();
|
||||||
|
|
||||||
|
|
@ -206,6 +228,16 @@ public:
|
||||||
*/
|
*/
|
||||||
CounterState *getLifeCounter() const;
|
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<int, CounterState *> &getCastCounts() const
|
||||||
|
{
|
||||||
|
return castCounts;
|
||||||
|
}
|
||||||
|
|
||||||
void setConceded(bool _conceded);
|
void setConceded(bool _conceded);
|
||||||
bool getConceded() const
|
bool getConceded() const
|
||||||
{
|
{
|
||||||
|
|
@ -240,6 +272,7 @@ private:
|
||||||
int zoneId;
|
int zoneId;
|
||||||
QMap<QString, CardZoneLogic *> zones;
|
QMap<QString, CardZoneLogic *> zones;
|
||||||
QMap<int, CounterState *> counters;
|
QMap<int, CounterState *> counters;
|
||||||
|
QMap<int, CounterState *> castCounts; // index (1-5) -> state
|
||||||
|
|
||||||
bool dialogSemaphore;
|
bool dialogSemaphore;
|
||||||
QList<CardItem *> cardsToDelete;
|
QList<CardItem *> cardsToDelete;
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,9 @@ QString CardZoneLogic::getTranslatedName(bool theirOwn, GrammaticalCase gc) cons
|
||||||
return (theirOwn ? tr("their graveyard", "nominative") : tr("%1's graveyard", "nominative").arg(ownerName));
|
return (theirOwn ? tr("their graveyard", "nominative") : tr("%1's graveyard", "nominative").arg(ownerName));
|
||||||
} else if (name == ZoneNames::EXILE) {
|
} else if (name == ZoneNames::EXILE) {
|
||||||
return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName));
|
return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName));
|
||||||
|
} else if (name == ZoneNames::COMMAND) {
|
||||||
|
return (theirOwn ? tr("their command zone", "nominative")
|
||||||
|
: tr("%1's command zone", "nominative").arg(ownerName));
|
||||||
} else if (name == ZoneNames::SIDEBOARD) {
|
} else if (name == ZoneNames::SIDEBOARD) {
|
||||||
switch (gc) {
|
switch (gc) {
|
||||||
case CaseLookAtZone:
|
case CaseLookAtZone:
|
||||||
|
|
|
||||||
19
cockatrice/src/game/zones/command_zone_logic.cpp
Normal file
19
cockatrice/src/game/zones/command_zone_logic.cpp
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include "command_zone_logic.h"
|
||||||
|
|
||||||
|
#include "../../game_graphics/board/card_item.h"
|
||||||
|
#include "card_zone_algorithms.h"
|
||||||
|
|
||||||
|
CommandZoneLogic::CommandZoneLogic(PlayerLogic *_player,
|
||||||
|
const QString &_name,
|
||||||
|
bool _hasCardAttr,
|
||||||
|
bool _isShufflable,
|
||||||
|
bool _contentsKnown,
|
||||||
|
QObject *parent)
|
||||||
|
: CardZoneLogic(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZoneLogic::addCardImpl(CardItem *card, int x, int /*y*/)
|
||||||
|
{
|
||||||
|
CardZoneAlgorithms::addCardToList(cards, card, x, false);
|
||||||
|
}
|
||||||
51
cockatrice/src/game/zones/command_zone_logic.h
Normal file
51
cockatrice/src/game/zones/command_zone_logic.h
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
/**
|
||||||
|
* @file command_zone_logic.h
|
||||||
|
* @ingroup GameLogicZones
|
||||||
|
* @brief Logic layer for the command zone, used for Commander format.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COCKATRICE_COMMAND_ZONE_LOGIC_H
|
||||||
|
#define COCKATRICE_COMMAND_ZONE_LOGIC_H
|
||||||
|
#include "card_zone_logic.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class CommandZoneLogic
|
||||||
|
* @brief Logic layer for managing cards in the command zone.
|
||||||
|
*
|
||||||
|
* Handles data storage and card management for the command zone in Commander format.
|
||||||
|
* Supports ordered card insertion for drag-and-drop operations.
|
||||||
|
*
|
||||||
|
* @see CommandZone for the graphics layer
|
||||||
|
* @see CardZoneLogic
|
||||||
|
*/
|
||||||
|
class CommandZoneLogic : public CardZoneLogic
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a CommandZoneLogic instance.
|
||||||
|
* @param _player The player who owns this zone
|
||||||
|
* @param _name Zone name (ZoneNames::COMMAND)
|
||||||
|
* @param _hasCardAttr Whether cards in this zone have attributes
|
||||||
|
* @param _isShufflable Whether the zone can be shuffled
|
||||||
|
* @param _contentsKnown Whether the zone contents are public knowledge
|
||||||
|
* @param parent Parent QObject
|
||||||
|
*/
|
||||||
|
CommandZoneLogic(PlayerLogic *_player,
|
||||||
|
const QString &_name,
|
||||||
|
bool _hasCardAttr,
|
||||||
|
bool _isShufflable,
|
||||||
|
bool _contentsKnown,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* @brief Adds a card at position x (y ignored). Appends if x is -1 or out of range.
|
||||||
|
* @param card Card to add
|
||||||
|
* @param x Insertion index, or -1 to append
|
||||||
|
* @param y Unused
|
||||||
|
*/
|
||||||
|
void addCardImpl(CardItem *card, int x, int y) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_COMMAND_ZONE_LOGIC_H
|
||||||
|
|
@ -29,9 +29,13 @@ AbstractCounter::AbstractCounter(CounterState *state,
|
||||||
{
|
{
|
||||||
setAcceptHoverEvents(true);
|
setAcceptHoverEvents(true);
|
||||||
|
|
||||||
connect(state, &CounterState::valueChanged, this, [this](int, int newValue) {
|
// Route through the (possibly overridden) virtual setValue so subclasses such as
|
||||||
value = newValue;
|
// CastCountWidget can clamp and refresh their tooltip on every value change.
|
||||||
update();
|
connect(state, &CounterState::valueChanged, this, [this](int, int newValue) { setValue(newValue); });
|
||||||
|
|
||||||
|
connect(state, &CounterState::activeChanged, this, [this](bool newActive) {
|
||||||
|
setActive(newActive);
|
||||||
|
player->requestRearrangeCounters();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (player->getPlayerInfo()->getLocalOrJudge()) {
|
if (player->getPlayerInfo()->getLocalOrJudge()) {
|
||||||
|
|
@ -80,6 +84,38 @@ void AbstractCounter::delCounter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AbstractCounter::setValue(int _value)
|
||||||
|
{
|
||||||
|
value = _value;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr int OVERLAY_ALPHA = 191;
|
||||||
|
static constexpr int HOVER_LIGHTEN_PERCENT = 130;
|
||||||
|
|
||||||
|
QColor AbstractCounter::getOverlayColor() const
|
||||||
|
{
|
||||||
|
QColor result = color;
|
||||||
|
result.setAlpha(OVERLAY_ALPHA);
|
||||||
|
if (hovered) {
|
||||||
|
result = result.lighter(HOVER_LIGHTEN_PERCENT);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractCounter::setActive(bool _active)
|
||||||
|
{
|
||||||
|
active = _active;
|
||||||
|
setVisible(_active);
|
||||||
|
if (menu) {
|
||||||
|
menu->setEnabled(_active);
|
||||||
|
if (!_active) {
|
||||||
|
menu->hideTearOffMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void AbstractCounter::retranslateUi()
|
void AbstractCounter::retranslateUi()
|
||||||
{
|
{
|
||||||
if (aSet) {
|
if (aSet) {
|
||||||
|
|
@ -128,7 +164,7 @@ void AbstractCounter::refreshShortcuts()
|
||||||
|
|
||||||
void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (!isUnderMouse() || !player->getPlayerInfo()->getLocalOrJudge()) {
|
if (!active || !isUnderMouse() || !player->getPlayerInfo()->getLocalOrJudge()) {
|
||||||
event->ignore();
|
event->ignore();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -159,6 +195,10 @@ void AbstractCounter::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
|
||||||
|
|
||||||
void AbstractCounter::incrementCounter()
|
void AbstractCounter::incrementCounter()
|
||||||
{
|
{
|
||||||
|
if (!active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Command_IncCounter cmd;
|
Command_IncCounter cmd;
|
||||||
cmd.set_counter_id(id);
|
cmd.set_counter_id(id);
|
||||||
cmd.set_delta(static_cast<QAction *>(sender())->data().toInt());
|
cmd.set_delta(static_cast<QAction *>(sender())->data().toInt());
|
||||||
|
|
@ -167,6 +207,10 @@ void AbstractCounter::incrementCounter()
|
||||||
|
|
||||||
void AbstractCounter::setCounter()
|
void AbstractCounter::setCounter()
|
||||||
{
|
{
|
||||||
|
if (!active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QWidget *parent = nullptr;
|
QWidget *parent = nullptr;
|
||||||
if (auto *view = scene() ? scene()->views().value(0) : nullptr) {
|
if (auto *view = scene() ? scene()->views().value(0) : nullptr) {
|
||||||
parent = view->window();
|
parent = view->window();
|
||||||
|
|
@ -181,7 +225,8 @@ void AbstractCounter::setCounter()
|
||||||
deleteLater();
|
deleteLater();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!ok) {
|
// Re-check active: exec() spins the event loop in case the counter was deactivated.
|
||||||
|
if (!ok || !active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
/**
|
/**
|
||||||
* @file abstract_counter.h
|
* @file abstract_counter.h
|
||||||
* @ingroup GameGraphicsPlayers
|
* @ingroup GameGraphicsPlayers
|
||||||
|
* @brief Abstract base for player counters displayed on the game board.
|
||||||
*/
|
*/
|
||||||
//! \todo Document this file.
|
|
||||||
|
|
||||||
#ifndef COUNTER_H
|
#ifndef COUNTER_H
|
||||||
#define COUNTER_H
|
#define COUNTER_H
|
||||||
|
|
@ -20,6 +20,7 @@ class QKeyEvent;
|
||||||
class QMenu;
|
class QMenu;
|
||||||
class QString;
|
class QString;
|
||||||
|
|
||||||
|
//! \todo Document AbstractCounter class members.
|
||||||
class AbstractCounter : public QObject, public QGraphicsItem, public AbstractPlayerComponent
|
class AbstractCounter : public QObject, public QGraphicsItem, public AbstractPlayerComponent
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
@ -35,6 +36,14 @@ protected:
|
||||||
bool hovered = false;
|
bool hovered = false;
|
||||||
bool useNameForShortcut;
|
bool useNameForShortcut;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the counter's color adjusted for overlay rendering.
|
||||||
|
*
|
||||||
|
* Applies semi-transparency and lightens the color when hovered.
|
||||||
|
* Use this for fill-based counters; SVG-based counters (GeneralCounter) bypass this.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QColor getOverlayColor() const;
|
||||||
|
|
||||||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||||
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
|
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
|
||||||
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
|
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
|
||||||
|
|
@ -61,6 +70,16 @@ public:
|
||||||
~AbstractCounter() override;
|
~AbstractCounter() override;
|
||||||
|
|
||||||
void retranslateUi() override;
|
void retranslateUi() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the counter value and triggers a visual update.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
virtual void setValue(int _value);
|
||||||
void setShortcutsActive() override;
|
void setShortcutsActive() override;
|
||||||
void setShortcutsInactive() override;
|
void setShortcutsInactive() override;
|
||||||
void delCounter();
|
void delCounter();
|
||||||
|
|
@ -93,6 +112,26 @@ public:
|
||||||
{
|
{
|
||||||
return shownInCounterArea;
|
return shownInCounterArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Returns whether this counter is shown and can be modified. */
|
||||||
|
[[nodiscard]] bool isActive() const
|
||||||
|
{
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shows or hides the counter and enables or disables its menu.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
virtual void setActive(bool _active);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool active = true; ///< Whether the counter is shown and modifiable
|
||||||
};
|
};
|
||||||
|
|
||||||
class AbstractCounterDialog : public QInputDialog
|
class AbstractCounterDialog : public QInputDialog
|
||||||
|
|
|
||||||
52
cockatrice/src/game_graphics/board/cast_count_widget.cpp
Normal file
52
cockatrice/src/game_graphics/board/cast_count_widget.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include "cast_count_widget.h"
|
||||||
|
|
||||||
|
#include "../../game/board/counter_state.h"
|
||||||
|
#include "translate_counter_name.h"
|
||||||
|
|
||||||
|
#include <QFontDatabase>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
static constexpr qreal CORNER_RADIUS = 4.0;
|
||||||
|
static constexpr qreal FONT_SIZE_RATIO = 0.6;
|
||||||
|
|
||||||
|
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 CastCountWidget::boundingRect() const
|
||||||
|
{
|
||||||
|
return QRectF(0, 0, size, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CastCountWidget::paint(QPainter *painter,
|
||||||
|
[[maybe_unused]] const QStyleOptionGraphicsItem *option,
|
||||||
|
[[maybe_unused]] QWidget *widget)
|
||||||
|
{
|
||||||
|
painter->save();
|
||||||
|
|
||||||
|
QRectF rect = boundingRect().adjusted(1, 1, -1, -1);
|
||||||
|
|
||||||
|
painter->setPen(Qt::NoPen);
|
||||||
|
painter->setBrush(getOverlayColor());
|
||||||
|
painter->drawRoundedRect(rect, CORNER_RADIUS, CORNER_RADIUS);
|
||||||
|
|
||||||
|
QFont f = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
|
||||||
|
f.setPixelSize(static_cast<int>(size * FONT_SIZE_RATIO));
|
||||||
|
f.setWeight(QFont::Bold);
|
||||||
|
painter->setFont(f);
|
||||||
|
painter->setPen(Qt::white);
|
||||||
|
painter->drawText(rect, Qt::AlignCenter, QString::number(value));
|
||||||
|
|
||||||
|
painter->restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CastCountWidget::setValue(int _value)
|
||||||
|
{
|
||||||
|
int clampedValue = qMax(0, _value);
|
||||||
|
AbstractCounter::setValue(clampedValue);
|
||||||
|
setToolTip(tr("%1: %2").arg(TranslateCounterName::getDisplayName(getName())).arg(clampedValue));
|
||||||
|
}
|
||||||
35
cockatrice/src/game_graphics/board/cast_count_widget.h
Normal file
35
cockatrice/src/game_graphics/board/cast_count_widget.h
Normal file
|
|
@ -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
|
||||||
|
|
@ -8,4 +8,9 @@ const QMap<QString, QString> TranslateCounterName::translated = {
|
||||||
{"r", QT_TRANSLATE_NOOP("TranslateCounterName", "Red")},
|
{"r", QT_TRANSLATE_NOOP("TranslateCounterName", "Red")},
|
||||||
{"g", QT_TRANSLATE_NOOP("TranslateCounterName", "Green")},
|
{"g", QT_TRANSLATE_NOOP("TranslateCounterName", "Green")},
|
||||||
{"x", QT_TRANSLATE_NOOP("TranslateCounterName", "Colorless")},
|
{"x", QT_TRANSLATE_NOOP("TranslateCounterName", "Colorless")},
|
||||||
{"storm", QT_TRANSLATE_NOOP("TranslateCounterName", "Other")}};
|
{"storm", QT_TRANSLATE_NOOP("TranslateCounterName", "Other")},
|
||||||
|
{"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")}};
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include <libcockatrice/protocol/pb/context_move_card.pb.h>
|
#include <libcockatrice/protocol/pb/context_move_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/context_mulligan.pb.h>
|
#include <libcockatrice/protocol/pb/context_mulligan.pb.h>
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
#include <libcockatrice/utility/zone_names.h>
|
#include <libcockatrice/utility/zone_names.h>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
|
@ -80,6 +81,8 @@ MessageLogWidget::getFromStr(CardZoneLogic *zone, QString cardName, int position
|
||||||
fromStr = tr(" from sideboard");
|
fromStr = tr(" from sideboard");
|
||||||
} else if (zoneName == ZoneNames::STACK) {
|
} else if (zoneName == ZoneNames::STACK) {
|
||||||
fromStr = tr(" from the stack");
|
fromStr = tr(" from the stack");
|
||||||
|
} else if (zoneName == ZoneNames::COMMAND) {
|
||||||
|
fromStr = tr(" from the command zone");
|
||||||
} else {
|
} else {
|
||||||
fromStr = tr(" from custom zone '%1'").arg(zoneName);
|
fromStr = tr(" from custom zone '%1'").arg(zoneName);
|
||||||
}
|
}
|
||||||
|
|
@ -344,6 +347,8 @@ void MessageLogWidget::logMoveCard(PlayerLogic *player,
|
||||||
} else {
|
} else {
|
||||||
finalStr = tr("%1 plays %2%3.");
|
finalStr = tr("%1 plays %2%3.");
|
||||||
}
|
}
|
||||||
|
} else if (targetZoneName == ZoneNames::COMMAND) {
|
||||||
|
finalStr = tr("%1 moves %2%3 to the command zone.");
|
||||||
} else {
|
} else {
|
||||||
fourthArg = targetZoneName;
|
fourthArg = targetZoneName;
|
||||||
if (card->getFaceDown()) {
|
if (card->getFaceDown()) {
|
||||||
|
|
@ -665,8 +670,9 @@ void MessageLogWidget::logSetCardCounter(PlayerLogic *player, QString cardName,
|
||||||
.arg(value));
|
.arg(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageLogWidget::logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue)
|
void MessageLogWidget::logSetCounter(PlayerLogic *player, int counterId, QString counterName, int value, int oldValue)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(counterId);
|
||||||
if (counterName == "life") {
|
if (counterName == "life") {
|
||||||
soundEngine->playSound("life_change");
|
soundEngine->playSound("life_change");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ public slots:
|
||||||
void logSetActivePlayer(PlayerLogic *player);
|
void logSetActivePlayer(PlayerLogic *player);
|
||||||
void logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation);
|
void logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation);
|
||||||
void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue);
|
void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue);
|
||||||
void logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue);
|
void logSetCounter(PlayerLogic *player, int counterId, QString counterName, int value, int oldValue);
|
||||||
void logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap);
|
void logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap);
|
||||||
void logSetPT(PlayerLogic *player, CardItem *card, QString newPT);
|
void logSetPT(PlayerLogic *player, CardItem *card, QString newPT);
|
||||||
void logSetSideboardLock(PlayerLogic *player, bool locked);
|
void logSetSideboardLock(PlayerLogic *player, bool locked);
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@ enum CardMenuActionType
|
||||||
cmMoveToHand,
|
cmMoveToHand,
|
||||||
cmMoveToGraveyard,
|
cmMoveToGraveyard,
|
||||||
cmMoveToExile,
|
cmMoveToExile,
|
||||||
cmMoveToTable
|
cmMoveToTable,
|
||||||
|
cmMoveToCommandZone
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COCKATRICE_CARD_MENU_ACTION_TYPE_H
|
#endif // COCKATRICE_CARD_MENU_ACTION_TYPE_H
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "../../../client/settings/card_counter_settings.h"
|
#include "../../../client/settings/card_counter_settings.h"
|
||||||
#include "../../../client/settings/shortcuts_settings.h"
|
#include "../../../client/settings/shortcuts_settings.h"
|
||||||
#include "../../../interface/widgets/tabs/tab_game.h"
|
#include "../../../interface/widgets/tabs/tab_game.h"
|
||||||
|
#include "../../board/abstract_counter.h"
|
||||||
#include "../../board/card_item.h"
|
#include "../../board/card_item.h"
|
||||||
#include "../../game/player/player_actions.h"
|
#include "../../game/player/player_actions.h"
|
||||||
#include "../../game/player/player_logic.h"
|
#include "../../game/player/player_logic.h"
|
||||||
|
|
@ -15,6 +16,7 @@
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <libcockatrice/card/database/card_database_manager.h>
|
#include <libcockatrice/card/database/card_database_manager.h>
|
||||||
#include <libcockatrice/card/relation/card_relation.h>
|
#include <libcockatrice/card/relation/card_relation.h>
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
#include <libcockatrice/utility/zone_names.h>
|
#include <libcockatrice/utility/zone_names.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -82,6 +84,10 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _sho
|
||||||
aUnattach = makeAction(this, [actions, sel]() { actions->actUnattach(sel()); });
|
aUnattach = makeAction(this, [actions, sel]() { actions->actUnattach(sel()); });
|
||||||
aSetAnnotation = makeAction(this, [actions, sel]() { actions->actRequestSetAnnotationDialog(sel()); });
|
aSetAnnotation = makeAction(this, [actions, sel]() { actions->actRequestSetAnnotationDialog(sel()); });
|
||||||
aPlay = makeAction(this, [actions, sel]() { actions->actPlay(sel()); });
|
aPlay = makeAction(this, [actions, sel]() { actions->actPlay(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()); });
|
aPlayFacedown = makeAction(this, [actions, sel]() { actions->actPlayFacedown(sel()); });
|
||||||
aHide = makeAction(this, [actions, sel]() { actions->actHide(sel()); });
|
aHide = makeAction(this, [actions, sel]() { actions->actHide(sel()); });
|
||||||
aReduceLifeByPower = makeAction(this, [actions, sel]() { actions->actReduceLifeByPower(sel()); });
|
aReduceLifeByPower = makeAction(this, [actions, sel]() { actions->actReduceLifeByPower(sel()); });
|
||||||
|
|
@ -158,6 +164,37 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _sho
|
||||||
} else if (card->getZone()->getName() == ZoneNames::EXILE ||
|
} else if (card->getZone()->getName() == ZoneNames::EXILE ||
|
||||||
card->getZone()->getName() == ZoneNames::GRAVE) {
|
card->getZone()->getName() == ZoneNames::GRAVE) {
|
||||||
createGraveyardOrExileMenu(writeableCard);
|
createGraveyardOrExileMenu(writeableCard);
|
||||||
|
} else if (card->getZone()->getName() == ZoneNames::COMMAND) {
|
||||||
|
if (writeableCard) {
|
||||||
|
addAction(aPlay);
|
||||||
|
|
||||||
|
// Only offer for single selection: a multi-select would over-count casts
|
||||||
|
const bool singleSelection = gameScene->selectedCards().size() <= 1;
|
||||||
|
|
||||||
|
if (singleSelection && player->getCastCountWidget(1)) {
|
||||||
|
addAction(aPlayAndIncrease1stCastCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (singleSelection && player->getCastCountWidget(2)) {
|
||||||
|
addAction(aPlayAndIncrease2ndCastCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No reveal submenu - command zone is public
|
||||||
|
addSeparator();
|
||||||
|
addAction(aClone);
|
||||||
|
addMenu(new MoveMenu(player, this));
|
||||||
|
addSeparator();
|
||||||
|
addAction(aAttach);
|
||||||
|
addAction(aDrawArrow);
|
||||||
|
} else {
|
||||||
|
addAction(aDrawArrow);
|
||||||
|
addSeparator();
|
||||||
|
addAction(aClone);
|
||||||
|
}
|
||||||
|
addSeparator();
|
||||||
|
addAction(aSelectAll);
|
||||||
|
addRelatedCardView();
|
||||||
|
addRelatedCardActions();
|
||||||
} else {
|
} else {
|
||||||
createHandOrCustomZoneMenu(writeableCard);
|
createHandOrCustomZoneMenu(writeableCard);
|
||||||
}
|
}
|
||||||
|
|
@ -203,7 +240,7 @@ void CardMenu::createTableMenu(bool canModifyCard)
|
||||||
}
|
}
|
||||||
addSeparator();
|
addSeparator();
|
||||||
addAction(aClone);
|
addAction(aClone);
|
||||||
addMenu(new MoveMenu(player));
|
addMenu(new MoveMenu(player, this));
|
||||||
addSeparator();
|
addSeparator();
|
||||||
addAction(aAttach);
|
addAction(aAttach);
|
||||||
if (card->getAttachedTo()) {
|
if (card->getAttachedTo()) {
|
||||||
|
|
@ -253,7 +290,7 @@ void CardMenu::createStackMenu(bool canModifyCard)
|
||||||
addAction(aPlayFacedown);
|
addAction(aPlayFacedown);
|
||||||
addSeparator();
|
addSeparator();
|
||||||
addAction(aClone);
|
addAction(aClone);
|
||||||
addMenu(new MoveMenu(player));
|
addMenu(new MoveMenu(player, this));
|
||||||
addSeparator();
|
addSeparator();
|
||||||
addAction(aAttach);
|
addAction(aAttach);
|
||||||
addAction(aDrawArrow);
|
addAction(aDrawArrow);
|
||||||
|
|
@ -282,7 +319,7 @@ void CardMenu::createGraveyardOrExileMenu(bool canModifyCard)
|
||||||
addAction(aPlayFacedown);
|
addAction(aPlayFacedown);
|
||||||
addSeparator();
|
addSeparator();
|
||||||
addAction(aClone);
|
addAction(aClone);
|
||||||
addMenu(new MoveMenu(player));
|
addMenu(new MoveMenu(player, this));
|
||||||
addSeparator();
|
addSeparator();
|
||||||
addAction(aAttach);
|
addAction(aAttach);
|
||||||
addAction(aDrawArrow);
|
addAction(aDrawArrow);
|
||||||
|
|
@ -320,7 +357,7 @@ void CardMenu::createHandOrCustomZoneMenu(bool canModifyCard)
|
||||||
|
|
||||||
addSeparator();
|
addSeparator();
|
||||||
addAction(aClone);
|
addAction(aClone);
|
||||||
addMenu(new MoveMenu(player));
|
addMenu(new MoveMenu(player, this));
|
||||||
|
|
||||||
// actions that are really wonky when done from deck or sideboard
|
// actions that are really wonky when done from deck or sideboard
|
||||||
if (card->getZone()->getName() == ZoneNames::HAND) {
|
if (card->getZone()->getName() == ZoneNames::HAND) {
|
||||||
|
|
@ -344,7 +381,7 @@ void CardMenu::createHandOrCustomZoneMenu(bool canModifyCard)
|
||||||
void CardMenu::createZonelessMenu(bool canModifyCard)
|
void CardMenu::createZonelessMenu(bool canModifyCard)
|
||||||
{
|
{
|
||||||
if (canModifyCard) {
|
if (canModifyCard) {
|
||||||
addMenu(new MoveMenu(player));
|
addMenu(new MoveMenu(player, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -488,6 +525,8 @@ void CardMenu::retranslateUi()
|
||||||
aPlay->setText(tr("&Play"));
|
aPlay->setText(tr("&Play"));
|
||||||
aHide->setText(tr("&Hide"));
|
aHide->setText(tr("&Hide"));
|
||||||
aPlayFacedown->setText(tr("Play &Face Down"));
|
aPlayFacedown->setText(tr("Play &Face Down"));
|
||||||
|
aPlayAndIncrease1stCastCount->setText(tr("Play and &Increase 1st Cast Count"));
|
||||||
|
aPlayAndIncrease2ndCastCount->setText(tr("Play and Increase &2nd Cast Count"));
|
||||||
aRevealToAll->setText(tr("&All players"));
|
aRevealToAll->setText(tr("&All players"));
|
||||||
//: Turn sideways or back again
|
//: Turn sideways or back again
|
||||||
aTap->setText(tr("&Tap / Untap"));
|
aTap->setText(tr("&Tap / Untap"));
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ public:
|
||||||
QMenu *mCardCounters;
|
QMenu *mCardCounters;
|
||||||
|
|
||||||
QAction *aPlay, *aPlayFacedown;
|
QAction *aPlay, *aPlayFacedown;
|
||||||
|
/** @brief Play actions that also increment the corresponding cast count. */
|
||||||
|
QAction *aPlayAndIncrease1stCastCount, *aPlayAndIncrease2ndCastCount;
|
||||||
QAction *aRevealToAll;
|
QAction *aRevealToAll;
|
||||||
QAction *aHide;
|
QAction *aHide;
|
||||||
QAction *aClone;
|
QAction *aClone;
|
||||||
|
|
|
||||||
168
cockatrice/src/game_graphics/player/menu/command_zone_menu.cpp
Normal file
168
cockatrice/src/game_graphics/player/menu/command_zone_menu.cpp
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
#include "command_zone_menu.h"
|
||||||
|
|
||||||
|
#include "../../../client/settings/cache_settings.h"
|
||||||
|
#include "../../../client/settings/shortcuts_settings.h"
|
||||||
|
#include "../../board/abstract_counter.h"
|
||||||
|
#include "../../game/player/player_actions.h"
|
||||||
|
#include "../../game/player/player_logic.h"
|
||||||
|
#include "../../game_scene.h"
|
||||||
|
#include "../../zones/command_zone.h"
|
||||||
|
#include "../player_graphics_item.h"
|
||||||
|
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
|
#include <libcockatrice/utility/counter_limits.h>
|
||||||
|
|
||||||
|
CommandZoneMenu::CommandZoneMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) : QMenu(playerMenu), player(_player)
|
||||||
|
{
|
||||||
|
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 < CastCountCount; ++i) {
|
||||||
|
int index = i + 1; // 1-based index
|
||||||
|
|
||||||
|
aIncrease[i] = new QAction(this);
|
||||||
|
connect(aIncrease[i], &QAction::triggered, this, [this, index]() {
|
||||||
|
if (auto *l = player->getLogic()) {
|
||||||
|
l->getPlayerActions()->actModifyCastCount(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addAction(aIncrease[i]);
|
||||||
|
|
||||||
|
aDecrease[i] = new QAction(this);
|
||||||
|
connect(aDecrease[i], &QAction::triggered, this, [this, index]() {
|
||||||
|
if (auto *l = player->getLogic()) {
|
||||||
|
l->getPlayerActions()->actModifyCastCount(index, -1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addAction(aDecrease[i]);
|
||||||
|
|
||||||
|
addSeparator();
|
||||||
|
|
||||||
|
aToggle[i] = new QAction(this);
|
||||||
|
connect(aToggle[i], &QAction::triggered, this, [this, index]() {
|
||||||
|
if (auto *l = player->getLogic()) {
|
||||||
|
l->getPlayerActions()->actToggleCastCount(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addAction(aToggle[i]);
|
||||||
|
|
||||||
|
addSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
aToggleMinimized = new QAction(this);
|
||||||
|
connect(aToggleMinimized, &QAction::triggered, this, &CommandZoneMenu::actToggleMinimized);
|
||||||
|
addAction(aToggleMinimized);
|
||||||
|
|
||||||
|
connect(this, &QMenu::aboutToShow, this, &CommandZoneMenu::updateCastCountActionStates);
|
||||||
|
}
|
||||||
|
|
||||||
|
retranslateUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZoneMenu::retranslateUi()
|
||||||
|
{
|
||||||
|
setTitle(tr("Co&mmander"));
|
||||||
|
|
||||||
|
static const char *ordinals[] = {"1st", "2nd", "3rd", "4th", "5th"};
|
||||||
|
|
||||||
|
for (int i = 0; i < CastCountCount; ++i) {
|
||||||
|
if (aIncrease[i]) {
|
||||||
|
aIncrease[i]->setText(tr("&Increase %1 Cast Count (+1)").arg(ordinals[i]));
|
||||||
|
}
|
||||||
|
if (aDecrease[i]) {
|
||||||
|
aDecrease[i]->setText(tr("&Decrease %1 Cast Count (-1)").arg(ordinals[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aToggleMinimized) {
|
||||||
|
aToggleMinimized->setText(tr("&Minimize"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZoneMenu::actToggleMinimized()
|
||||||
|
{
|
||||||
|
CommandZone *zone = player->getCommandZoneGraphicsItem();
|
||||||
|
if (zone) {
|
||||||
|
zone->toggleMinimized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZoneMenu::updateCastCountActionStates()
|
||||||
|
{
|
||||||
|
static const char *ordinals[] = {"1st", "2nd", "3rd", "4th", "5th"};
|
||||||
|
|
||||||
|
std::array<AbstractCounter *, CastCountCount> castCounts{};
|
||||||
|
for (int i = 0; i < CastCountCount; ++i) {
|
||||||
|
castCounts[i] = player->getCastCountWidget(i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int highestActive = -1;
|
||||||
|
for (int i = CastCountCount - 1; i >= 0; --i) {
|
||||||
|
if (castCounts[i]) {
|
||||||
|
highestActive = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < CastCountCount; ++i) {
|
||||||
|
AbstractCounter *counter = castCounts[i];
|
||||||
|
|
||||||
|
if (aIncrease[i]) {
|
||||||
|
aIncrease[i]->setVisible(counter && counter->getValue() < MAX_COUNTER_VALUE);
|
||||||
|
}
|
||||||
|
if (aDecrease[i]) {
|
||||||
|
aDecrease[i]->setVisible(counter && counter->getValue() > 0);
|
||||||
|
}
|
||||||
|
if (aToggle[i]) {
|
||||||
|
aToggle[i]->setText(counter ? tr("&Remove %1 Cast Count").arg(ordinals[i])
|
||||||
|
: tr("&Add %1 Cast Count").arg(ordinals[i]));
|
||||||
|
|
||||||
|
bool canAdd = !counter && (i == 0 || castCounts[i - 1]);
|
||||||
|
bool canRemove = counter && counter->getValue() == 0 && i == highestActive;
|
||||||
|
aToggle[i]->setVisible(canAdd || canRemove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aToggleMinimized) {
|
||||||
|
CommandZone *zone = player->getCommandZoneGraphicsItem();
|
||||||
|
aToggleMinimized->setText(zone && zone->isMinimized() ? tr("&Restore") : tr("&Minimize"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZoneMenu::setShortcutsActive()
|
||||||
|
{
|
||||||
|
ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts();
|
||||||
|
|
||||||
|
if (aIncrease[0]) {
|
||||||
|
aIncrease[0]->setShortcuts(shortcuts.getShortcut(inc1ShortcutKey));
|
||||||
|
}
|
||||||
|
if (aDecrease[0]) {
|
||||||
|
aDecrease[0]->setShortcuts(shortcuts.getShortcut(dec1ShortcutKey));
|
||||||
|
}
|
||||||
|
if (aIncrease[1]) {
|
||||||
|
aIncrease[1]->setShortcuts(shortcuts.getShortcut(inc2ShortcutKey));
|
||||||
|
}
|
||||||
|
if (aDecrease[1]) {
|
||||||
|
aDecrease[1]->setShortcuts(shortcuts.getShortcut(dec2ShortcutKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZoneMenu::setShortcutsInactive()
|
||||||
|
{
|
||||||
|
if (aIncrease[0]) {
|
||||||
|
aIncrease[0]->setShortcut(QKeySequence());
|
||||||
|
}
|
||||||
|
if (aDecrease[0]) {
|
||||||
|
aDecrease[0]->setShortcut(QKeySequence());
|
||||||
|
}
|
||||||
|
if (aIncrease[1]) {
|
||||||
|
aIncrease[1]->setShortcut(QKeySequence());
|
||||||
|
}
|
||||||
|
if (aDecrease[1]) {
|
||||||
|
aDecrease[1]->setShortcut(QKeySequence());
|
||||||
|
}
|
||||||
|
}
|
||||||
61
cockatrice/src/game_graphics/player/menu/command_zone_menu.h
Normal file
61
cockatrice/src/game_graphics/player/menu/command_zone_menu.h
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
* @file command_zone_menu.h
|
||||||
|
* @ingroup GameGraphicsPlayers
|
||||||
|
* @brief Submenu for command zone actions (cast counts, minimize/restore).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COCKATRICE_COMMAND_ZONE_MENU_H
|
||||||
|
#define COCKATRICE_COMMAND_ZONE_MENU_H
|
||||||
|
|
||||||
|
#include "abstract_player_component.h"
|
||||||
|
|
||||||
|
#include <QMenu>
|
||||||
|
#include <array>
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
|
|
||||||
|
class PlayerGraphicsItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class CommandZoneMenu
|
||||||
|
* @brief Player submenu for managing cast counts and command zone display.
|
||||||
|
*
|
||||||
|
* Provides actions to increment/decrement each cast count, toggle cast counts
|
||||||
|
* on/off, and minimize/restore the command zone display.
|
||||||
|
*
|
||||||
|
* @see PlayerMenu
|
||||||
|
* @see CommandZone
|
||||||
|
*/
|
||||||
|
class CommandZoneMenu : public QMenu, public AbstractPlayerComponent
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CommandZoneMenu(PlayerGraphicsItem *player, QMenu *playerMenu);
|
||||||
|
void retranslateUi() override;
|
||||||
|
void setShortcutsActive() override;
|
||||||
|
void setShortcutsInactive() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int CastCountCount = CastCountIds::Count;
|
||||||
|
|
||||||
|
std::array<QAction *, CastCountCount> aIncrease{};
|
||||||
|
std::array<QAction *, CastCountCount> aDecrease{};
|
||||||
|
std::array<QAction *, CastCountCount> aToggle{};
|
||||||
|
QAction *aToggleMinimized = nullptr;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateCastCountActionStates();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void actToggleMinimized();
|
||||||
|
|
||||||
|
private:
|
||||||
|
PlayerGraphicsItem *player;
|
||||||
|
|
||||||
|
QString inc1ShortcutKey;
|
||||||
|
QString dec1ShortcutKey;
|
||||||
|
QString inc2ShortcutKey;
|
||||||
|
QString dec2ShortcutKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_COMMAND_ZONE_MENU_H
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#include "../card_menu_action_type.h"
|
#include "../card_menu_action_type.h"
|
||||||
#include "../player_graphics_item.h"
|
#include "../player_graphics_item.h"
|
||||||
|
|
||||||
MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to"))
|
MoveMenu::MoveMenu(PlayerGraphicsItem *player, QWidget *parent) : QMenu(tr("Move to"), parent)
|
||||||
{
|
{
|
||||||
aMoveToTopLibrary = new QAction(this);
|
aMoveToTopLibrary = new QAction(this);
|
||||||
aMoveToTopLibrary->setData(cmMoveToTopLibrary);
|
aMoveToTopLibrary->setData(cmMoveToTopLibrary);
|
||||||
|
|
@ -21,6 +21,8 @@ MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to"))
|
||||||
aMoveToGraveyard->setData(cmMoveToGraveyard);
|
aMoveToGraveyard->setData(cmMoveToGraveyard);
|
||||||
aMoveToExile = new QAction(this);
|
aMoveToExile = new QAction(this);
|
||||||
aMoveToExile->setData(cmMoveToExile);
|
aMoveToExile->setData(cmMoveToExile);
|
||||||
|
aMoveToCommandZone = new QAction(this);
|
||||||
|
aMoveToCommandZone->setData(cmMoveToCommandZone);
|
||||||
|
|
||||||
auto *actions = player->getLogic()->getPlayerActions();
|
auto *actions = player->getLogic()->getPlayerActions();
|
||||||
|
|
||||||
|
|
@ -38,6 +40,7 @@ MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to"))
|
||||||
connect(aMoveToHand, &QAction::triggered, actions, invoke(cmMoveToHand));
|
connect(aMoveToHand, &QAction::triggered, actions, invoke(cmMoveToHand));
|
||||||
connect(aMoveToGraveyard, &QAction::triggered, actions, invoke(cmMoveToGraveyard));
|
connect(aMoveToGraveyard, &QAction::triggered, actions, invoke(cmMoveToGraveyard));
|
||||||
connect(aMoveToExile, &QAction::triggered, actions, invoke(cmMoveToExile));
|
connect(aMoveToExile, &QAction::triggered, actions, invoke(cmMoveToExile));
|
||||||
|
connect(aMoveToCommandZone, &QAction::triggered, actions, invoke(cmMoveToCommandZone));
|
||||||
|
|
||||||
addAction(aMoveToTopLibrary);
|
addAction(aMoveToTopLibrary);
|
||||||
addAction(aMoveToXfromTopOfLibrary);
|
addAction(aMoveToXfromTopOfLibrary);
|
||||||
|
|
@ -50,6 +53,9 @@ MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to"))
|
||||||
addAction(aMoveToGraveyard);
|
addAction(aMoveToGraveyard);
|
||||||
addSeparator();
|
addSeparator();
|
||||||
addAction(aMoveToExile);
|
addAction(aMoveToExile);
|
||||||
|
addSeparator();
|
||||||
|
addAction(aMoveToCommandZone);
|
||||||
|
aMoveToCommandZone->setVisible(player->getLogic()->hasCommandZone());
|
||||||
|
|
||||||
setShortcutsActive();
|
setShortcutsActive();
|
||||||
|
|
||||||
|
|
@ -66,6 +72,7 @@ void MoveMenu::setShortcutsActive()
|
||||||
aMoveToHand->setShortcuts(shortcuts.getShortcut("Player/aMoveToHand"));
|
aMoveToHand->setShortcuts(shortcuts.getShortcut("Player/aMoveToHand"));
|
||||||
aMoveToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveToGraveyard"));
|
aMoveToGraveyard->setShortcuts(shortcuts.getShortcut("Player/aMoveToGraveyard"));
|
||||||
aMoveToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveToExile"));
|
aMoveToExile->setShortcuts(shortcuts.getShortcut("Player/aMoveToExile"));
|
||||||
|
aMoveToCommandZone->setShortcuts(shortcuts.getShortcut("Player/aMoveToCommandZone"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MoveMenu::retranslateUi()
|
void MoveMenu::retranslateUi()
|
||||||
|
|
@ -77,4 +84,5 @@ void MoveMenu::retranslateUi()
|
||||||
aMoveToHand->setText(tr("&Hand"));
|
aMoveToHand->setText(tr("&Hand"));
|
||||||
aMoveToGraveyard->setText(tr("&Graveyard"));
|
aMoveToGraveyard->setText(tr("&Graveyard"));
|
||||||
aMoveToExile->setText(tr("&Exile"));
|
aMoveToExile->setText(tr("&Exile"));
|
||||||
|
aMoveToCommandZone->setText(tr("&Command Zone"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ class MoveMenu : public QMenu
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MoveMenu(PlayerGraphicsItem *player);
|
explicit MoveMenu(PlayerGraphicsItem *player, QWidget *parent);
|
||||||
void setShortcutsActive();
|
void setShortcutsActive();
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
|
||||||
|
|
@ -26,6 +26,7 @@ public:
|
||||||
QAction *aMoveToTable = nullptr;
|
QAction *aMoveToTable = nullptr;
|
||||||
QAction *aMoveToGraveyard = nullptr;
|
QAction *aMoveToGraveyard = nullptr;
|
||||||
QAction *aMoveToExile = nullptr;
|
QAction *aMoveToExile = nullptr;
|
||||||
|
QAction *aMoveToCommandZone = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COCKATRICE_MOVE_MENU_H
|
#endif // COCKATRICE_MOVE_MENU_H
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include "../../../game_graphics/zones/table_zone.h"
|
#include "../../../game_graphics/zones/table_zone.h"
|
||||||
#include "../../../interface/widgets/tabs/tab_game.h"
|
#include "../../../interface/widgets/tabs/tab_game.h"
|
||||||
#include "../../board/card_item.h"
|
#include "../../board/card_item.h"
|
||||||
|
#include "../../zones/command_zone.h"
|
||||||
#include "../player_graphics_item.h"
|
#include "../player_graphics_item.h"
|
||||||
#include "card_menu.h"
|
#include "card_menu.h"
|
||||||
#include "hand_menu.h"
|
#include "hand_menu.h"
|
||||||
|
|
@ -32,6 +33,13 @@ PlayerMenu::PlayerMenu(PlayerGraphicsItem *_player) : QObject(_player), player(_
|
||||||
|
|
||||||
if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) {
|
if (player->getLogic()->getPlayerInfo()->getLocalOrJudge()) {
|
||||||
sideboardMenu = addManagedMenu<SideboardMenu>(player, playerMenu);
|
sideboardMenu = addManagedMenu<SideboardMenu>(player, playerMenu);
|
||||||
|
|
||||||
|
if (player->getLogic()->hasCommandZone()) {
|
||||||
|
commandZoneMenu = addManagedMenu<CommandZoneMenu>(player, playerMenu);
|
||||||
|
} else {
|
||||||
|
commandZoneMenu = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
customZonesMenu = addManagedMenu<CustomZoneMenu>(player);
|
customZonesMenu = addManagedMenu<CustomZoneMenu>(player);
|
||||||
playerMenu->addSeparator();
|
playerMenu->addSeparator();
|
||||||
|
|
||||||
|
|
@ -40,6 +48,7 @@ PlayerMenu::PlayerMenu(PlayerGraphicsItem *_player) : QObject(_player), player(_
|
||||||
utilityMenu = createManagedComponent<UtilityMenu>(player, playerMenu);
|
utilityMenu = createManagedComponent<UtilityMenu>(player, playerMenu);
|
||||||
} else {
|
} else {
|
||||||
sideboardMenu = nullptr;
|
sideboardMenu = nullptr;
|
||||||
|
commandZoneMenu = nullptr;
|
||||||
customZonesMenu = nullptr;
|
customZonesMenu = nullptr;
|
||||||
countersMenu = nullptr;
|
countersMenu = nullptr;
|
||||||
utilityMenu = nullptr;
|
utilityMenu = nullptr;
|
||||||
|
|
@ -69,6 +78,10 @@ void PlayerMenu::setMenusForGraphicItems()
|
||||||
player->getHandZoneGraphicsItem()->setMenu(handMenu);
|
player->getHandZoneGraphicsItem()->setMenu(handMenu);
|
||||||
player->getDeckZoneGraphicsItem()->setMenu(libraryMenu, libraryMenu->aDrawCard);
|
player->getDeckZoneGraphicsItem()->setMenu(libraryMenu, libraryMenu->aDrawCard);
|
||||||
player->getSideboardZoneGraphicsItem()->setMenu(sideboardMenu);
|
player->getSideboardZoneGraphicsItem()->setMenu(sideboardMenu);
|
||||||
|
|
||||||
|
if (auto *commandZone = player->getCommandZoneGraphicsItem()) {
|
||||||
|
commandZone->setMenu(commandZoneMenu);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
#define COCKATRICE_PLAYER_MENU_H
|
#define COCKATRICE_PLAYER_MENU_H
|
||||||
|
|
||||||
#include "../../../interface/widgets/menus/tearoff_menu.h"
|
#include "../../../interface/widgets/menus/tearoff_menu.h"
|
||||||
|
#include "../../game/player/player_logic.h"
|
||||||
|
#include "command_zone_menu.h"
|
||||||
#include "custom_zone_menu.h"
|
#include "custom_zone_menu.h"
|
||||||
#include "grave_menu.h"
|
#include "grave_menu.h"
|
||||||
#include "hand_menu.h"
|
#include "hand_menu.h"
|
||||||
|
|
@ -69,6 +71,11 @@ public:
|
||||||
return utilityMenu;
|
return utilityMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] CommandZoneMenu *getCommandZoneMenu() const
|
||||||
|
{
|
||||||
|
return commandZoneMenu;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool getShortcutsActive() const
|
[[nodiscard]] bool getShortcutsActive() const
|
||||||
{
|
{
|
||||||
return shortcutsActive;
|
return shortcutsActive;
|
||||||
|
|
@ -91,6 +98,7 @@ private:
|
||||||
UtilityMenu *utilityMenu;
|
UtilityMenu *utilityMenu;
|
||||||
TallyMenu *tallyMenu;
|
TallyMenu *tallyMenu;
|
||||||
SayMenu *sayMenu;
|
SayMenu *sayMenu;
|
||||||
|
CommandZoneMenu *commandZoneMenu;
|
||||||
CustomZoneMenu *customZonesMenu;
|
CustomZoneMenu *customZonesMenu;
|
||||||
|
|
||||||
/** @brief Drives AbstractPlayerComponent lifecycle delegation. Counters are iterated separately via
|
/** @brief Drives AbstractPlayerComponent lifecycle delegation. Counters are iterated separately via
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,11 @@
|
||||||
#include "../../game/player/player_actions.h"
|
#include "../../game/player/player_actions.h"
|
||||||
#include "../../interface/widgets/tabs/tab_game.h"
|
#include "../../interface/widgets/tabs/tab_game.h"
|
||||||
#include "../board/abstract_card_item.h"
|
#include "../board/abstract_card_item.h"
|
||||||
|
#include "../board/cast_count_widget.h"
|
||||||
#include "../board/counter_general.h"
|
#include "../board/counter_general.h"
|
||||||
#include "../hand_counter.h"
|
#include "../hand_counter.h"
|
||||||
|
#include "../z_values.h"
|
||||||
|
#include "../zones/command_zone.h"
|
||||||
#include "../zones/hand_zone.h"
|
#include "../zones/hand_zone.h"
|
||||||
#include "../zones/pile_zone.h"
|
#include "../zones/pile_zone.h"
|
||||||
#include "../zones/stack_zone.h"
|
#include "../zones/stack_zone.h"
|
||||||
|
|
@ -14,6 +17,7 @@
|
||||||
|
|
||||||
#include <QGraphicsView>
|
#include <QGraphicsView>
|
||||||
#include <libcockatrice/settings/interface_settings.h>
|
#include <libcockatrice/settings/interface_settings.h>
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
|
|
||||||
PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player)
|
PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player)
|
||||||
{
|
{
|
||||||
|
|
@ -28,6 +32,8 @@ PlayerGraphicsItem::PlayerGraphicsItem(PlayerLogic *_player) : player(_player)
|
||||||
|
|
||||||
connect(player, &PlayerLogic::counterAdded, this, &PlayerGraphicsItem::onCounterAdded);
|
connect(player, &PlayerLogic::counterAdded, this, &PlayerGraphicsItem::onCounterAdded);
|
||||||
connect(player, &PlayerLogic::counterRemoved, this, &PlayerGraphicsItem::onCounterRemoved);
|
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);
|
playerMenu = new PlayerMenu(this);
|
||||||
|
|
||||||
|
|
@ -122,6 +128,15 @@ void PlayerGraphicsItem::initializeZones()
|
||||||
new HandZone(player->getHandZone(), static_cast<int>(tableZoneGraphicsItem->boundingRect().height()), this);
|
new HandZone(player->getHandZone(), static_cast<int>(tableZoneGraphicsItem->boundingRect().height()), this);
|
||||||
connect(player->getPlayerActions(), &PlayerActions::requestSortHand, handZoneGraphicsItem, &HandZone::sortHand);
|
connect(player->getPlayerActions(), &PlayerActions::requestSortHand, handZoneGraphicsItem, &HandZone::sortHand);
|
||||||
|
|
||||||
|
// Command zone (only created for commander games)
|
||||||
|
if (auto *commandZoneLogic = player->getCommandZone()) {
|
||||||
|
commandZoneGraphicsItem = new CommandZone(commandZoneLogic, ZoneSizes::COMMAND_ZONE_HEIGHT, this);
|
||||||
|
connect(commandZoneGraphicsItem, &CommandZone::minimizedChanged, this, &PlayerGraphicsItem::rearrangeZones);
|
||||||
|
connect(commandZoneGraphicsItem, &CommandZone::effectiveHeightChanged, this,
|
||||||
|
&PlayerGraphicsItem::rearrangeZones);
|
||||||
|
zoneGraphicsItems.insert(commandZoneLogic->getName(), commandZoneGraphicsItem);
|
||||||
|
}
|
||||||
|
|
||||||
connect(handZoneGraphicsItem->getLogic(), &HandZoneLogic::cardCountChanged, handCounter,
|
connect(handZoneGraphicsItem->getLogic(), &HandZoneLogic::cardCountChanged, handCounter,
|
||||||
&HandCounter::updateNumber);
|
&HandCounter::updateNumber);
|
||||||
connect(handCounter, &HandCounter::showContextMenu, handZoneGraphicsItem, &HandZone::showContextMenu);
|
connect(handCounter, &HandCounter::showContextMenu, handZoneGraphicsItem, &HandZone::showContextMenu);
|
||||||
|
|
@ -183,6 +198,36 @@ void PlayerGraphicsItem::setMirrored(bool _mirrored)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QAction *PlayerGraphicsItem::counterMenuInsertAnchor(int counterId) const
|
||||||
|
{
|
||||||
|
QMenu *countersMenu = playerMenu->getCountersMenu();
|
||||||
|
if (!countersMenu) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const QList<QAction *> inMenu = countersMenu->actions();
|
||||||
|
// QMap iterates in ascending key order, so this finds the lowest id above counterId.
|
||||||
|
for (auto it = counterWidgets.upperBound(counterId); it != counterWidgets.constEnd(); ++it) {
|
||||||
|
QMenu *menu = it.value()->getMenu();
|
||||||
|
if (menu && inMenu.contains(menu->menuAction())) {
|
||||||
|
return menu->menuAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerGraphicsItem::setCounterMenuRegistered(AbstractCounter *widget, bool registered)
|
||||||
|
{
|
||||||
|
QMenu *countersMenu = playerMenu->getCountersMenu();
|
||||||
|
if (!widget || !countersMenu || !widget->getMenu()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (registered) {
|
||||||
|
countersMenu->insertMenu(counterMenuInsertAnchor(widget->getId()), widget->getMenu());
|
||||||
|
} else {
|
||||||
|
countersMenu->removeAction(widget->getMenu()->menuAction());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerGraphicsItem::onCounterAdded(CounterState *state)
|
void PlayerGraphicsItem::onCounterAdded(CounterState *state)
|
||||||
{
|
{
|
||||||
AbstractCounter *widget;
|
AbstractCounter *widget;
|
||||||
|
|
@ -193,9 +238,7 @@ void PlayerGraphicsItem::onCounterAdded(CounterState *state)
|
||||||
}
|
}
|
||||||
counterWidgets.insert(state->getId(), widget);
|
counterWidgets.insert(state->getId(), widget);
|
||||||
|
|
||||||
if (playerMenu->getCountersMenu() && widget->getMenu()) {
|
setCounterMenuRegistered(widget, state->isActive());
|
||||||
playerMenu->getCountersMenu()->addMenu(widget->getMenu());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playerMenu->getShortcutsActive()) {
|
if (playerMenu->getShortcutsActive()) {
|
||||||
widget->setShortcutsActive();
|
widget->setShortcutsActive();
|
||||||
|
|
@ -204,21 +247,60 @@ void PlayerGraphicsItem::onCounterAdded(CounterState *state)
|
||||||
rearrangeCounters();
|
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)
|
void PlayerGraphicsItem::onCounterRemoved(int counterId)
|
||||||
{
|
{
|
||||||
auto *widget = counterWidgets.take(counterId);
|
auto *widget = counterWidgets.take(counterId);
|
||||||
if (!widget) {
|
if (!widget) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (playerMenu->getCountersMenu() && widget->getMenu()) {
|
setCounterMenuRegistered(widget, false);
|
||||||
playerMenu->getCountersMenu()->removeAction(widget->getMenu()->menuAction());
|
widget->delCounter();
|
||||||
|
rearrangeCounters();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerGraphicsItem::onCastCountRemoved(int index)
|
||||||
|
{
|
||||||
|
auto *widget = castCountWidgets.take(index);
|
||||||
|
if (!widget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (commandZoneGraphicsItem) {
|
||||||
|
commandZoneGraphicsItem->unregisterCastCount(widget);
|
||||||
}
|
}
|
||||||
widget->delCounter();
|
widget->delCounter();
|
||||||
|
|
||||||
|
if (auto *menu = playerMenu->getCommandZoneMenu()) {
|
||||||
|
menu->updateCastCountActionStates();
|
||||||
|
}
|
||||||
|
|
||||||
rearrangeCounters();
|
rearrangeCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerGraphicsItem::rearrangeCounters()
|
void PlayerGraphicsItem::rearrangeCounters()
|
||||||
{
|
{
|
||||||
|
if (commandZoneGraphicsItem) {
|
||||||
|
commandZoneGraphicsItem->rearrangeCastCounts();
|
||||||
|
}
|
||||||
|
|
||||||
qreal ySize = boundingRect().y() + 80;
|
qreal ySize = boundingRect().y() + 80;
|
||||||
constexpr qreal padding = 5;
|
constexpr qreal padding = 5;
|
||||||
for (auto *ctr : counterWidgets.values()) {
|
for (auto *ctr : counterWidgets.values()) {
|
||||||
|
|
@ -231,9 +313,24 @@ void PlayerGraphicsItem::rearrangeCounters()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AbstractCounter *PlayerGraphicsItem::getCastCountWidget(int index) const
|
||||||
|
{
|
||||||
|
return castCountWidgets.value(index, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerGraphicsItem::rearrangeZones()
|
void PlayerGraphicsItem::rearrangeZones()
|
||||||
{
|
{
|
||||||
auto base = QPointF(CardDimensions::HEIGHT_F + counterAreaWidth + 15, 0);
|
auto base = QPointF(CardDimensions::HEIGHT_F + counterAreaWidth + 15, 0);
|
||||||
|
|
||||||
|
// Calculate stack height, accounting for command zone if visible
|
||||||
|
bool commandZoneVisible = commandZoneGraphicsItem && commandZoneGraphicsItem->isVisible();
|
||||||
|
qreal tableHeight = tableZoneGraphicsItem->boundingRect().height();
|
||||||
|
qreal stackHeight = tableHeight;
|
||||||
|
if (commandZoneVisible) {
|
||||||
|
stackHeight = tableHeight - totalCommandZoneHeight();
|
||||||
|
}
|
||||||
|
stackZoneGraphicsItem->setHeight(stackHeight);
|
||||||
|
|
||||||
if (SettingsCache::instance().userInterface().getHorizontalHand()) {
|
if (SettingsCache::instance().userInterface().getHorizontalHand()) {
|
||||||
if (mirrored) {
|
if (mirrored) {
|
||||||
if (player->getHandZone()->contentsKnown()) {
|
if (player->getHandZone()->contentsKnown()) {
|
||||||
|
|
@ -244,12 +341,12 @@ void PlayerGraphicsItem::rearrangeZones()
|
||||||
handVisible = false;
|
handVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
stackZoneGraphicsItem->setPos(base);
|
positionCommandAndStackZones(base);
|
||||||
base += QPointF(stackZoneGraphicsItem->boundingRect().width(), 0);
|
base += QPointF(stackZoneGraphicsItem->boundingRect().width(), 0);
|
||||||
|
|
||||||
tableZoneGraphicsItem->setPos(base);
|
tableZoneGraphicsItem->setPos(base);
|
||||||
} else {
|
} else {
|
||||||
stackZoneGraphicsItem->setPos(base);
|
positionCommandAndStackZones(base);
|
||||||
|
|
||||||
tableZoneGraphicsItem->setPos(base.x() + stackZoneGraphicsItem->boundingRect().width(), 0);
|
tableZoneGraphicsItem->setPos(base.x() + stackZoneGraphicsItem->boundingRect().width(), 0);
|
||||||
base += QPointF(0, tableZoneGraphicsItem->boundingRect().height());
|
base += QPointF(0, tableZoneGraphicsItem->boundingRect().height());
|
||||||
|
|
@ -269,7 +366,7 @@ void PlayerGraphicsItem::rearrangeZones()
|
||||||
handZoneGraphicsItem->setPos(base);
|
handZoneGraphicsItem->setPos(base);
|
||||||
base += QPointF(handZoneGraphicsItem->boundingRect().width(), 0);
|
base += QPointF(handZoneGraphicsItem->boundingRect().width(), 0);
|
||||||
|
|
||||||
stackZoneGraphicsItem->setPos(base);
|
positionCommandAndStackZones(base);
|
||||||
base += QPointF(stackZoneGraphicsItem->boundingRect().width(), 0);
|
base += QPointF(stackZoneGraphicsItem->boundingRect().width(), 0);
|
||||||
|
|
||||||
tableZoneGraphicsItem->setPos(base);
|
tableZoneGraphicsItem->setPos(base);
|
||||||
|
|
@ -278,7 +375,6 @@ void PlayerGraphicsItem::rearrangeZones()
|
||||||
handZoneGraphicsItem->updateOrientation();
|
handZoneGraphicsItem->updateOrientation();
|
||||||
tableZoneGraphicsItem->reorganizeCards();
|
tableZoneGraphicsItem->reorganizeCards();
|
||||||
updateBoundingRect();
|
updateBoundingRect();
|
||||||
rearrangeCounters();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerGraphicsItem::updateBoundingRect()
|
void PlayerGraphicsItem::updateBoundingRect()
|
||||||
|
|
@ -298,3 +394,20 @@ void PlayerGraphicsItem::updateBoundingRect()
|
||||||
|
|
||||||
emit sizeChanged();
|
emit sizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qreal PlayerGraphicsItem::totalCommandZoneHeight() const
|
||||||
|
{
|
||||||
|
if (commandZoneGraphicsItem && commandZoneGraphicsItem->isVisible()) {
|
||||||
|
return commandZoneGraphicsItem->currentHeight();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerGraphicsItem::positionCommandAndStackZones(const QPointF &base)
|
||||||
|
{
|
||||||
|
bool commandZoneVisible = commandZoneGraphicsItem && commandZoneGraphicsItem->isVisible();
|
||||||
|
if (commandZoneVisible) {
|
||||||
|
commandZoneGraphicsItem->setPos(base);
|
||||||
|
}
|
||||||
|
stackZoneGraphicsItem->setPos(base.x(), base.y() + (commandZoneVisible ? totalCommandZoneHeight() : 0));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <QGraphicsObject>
|
#include <QGraphicsObject>
|
||||||
|
|
||||||
|
class CommandZone;
|
||||||
class HandZone;
|
class HandZone;
|
||||||
class PileZone;
|
class PileZone;
|
||||||
class PlayerDialogs;
|
class PlayerDialogs;
|
||||||
|
|
@ -112,12 +113,26 @@ public:
|
||||||
{
|
{
|
||||||
return handZoneGraphicsItem;
|
return handZoneGraphicsItem;
|
||||||
}
|
}
|
||||||
|
/** @brief Returns the command zone graphics item. */
|
||||||
|
[[nodiscard]] CommandZone *getCommandZoneGraphicsItem() const
|
||||||
|
{
|
||||||
|
return commandZoneGraphicsItem;
|
||||||
|
}
|
||||||
|
/** @brief Returns the counter widget for the given counter ID, or nullptr if not found. */
|
||||||
|
[[nodiscard]] AbstractCounter *getCounterWidget(int counterId) const
|
||||||
|
{
|
||||||
|
return counterWidgets.value(counterId, nullptr);
|
||||||
|
}
|
||||||
|
/** @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:
|
public slots:
|
||||||
void onPlayerActiveChanged(bool _active);
|
void onPlayerActiveChanged(bool _active);
|
||||||
void onCustomZoneAdded(QString customZoneName);
|
void onCustomZoneAdded(QString customZoneName);
|
||||||
void onCounterAdded(CounterState *state);
|
void onCounterAdded(CounterState *state);
|
||||||
void onCounterRemoved(int counterId);
|
void onCounterRemoved(int counterId);
|
||||||
|
void onCastCountAdded(int index, CounterState *state);
|
||||||
|
void onCastCountRemoved(int index);
|
||||||
void rearrangeCounters();
|
void rearrangeCounters();
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
|
||||||
|
|
@ -134,6 +149,7 @@ private:
|
||||||
PlayerArea *playerArea;
|
PlayerArea *playerArea;
|
||||||
PlayerTarget *playerTarget;
|
PlayerTarget *playerTarget;
|
||||||
QMap<int, AbstractCounter *> counterWidgets;
|
QMap<int, AbstractCounter *> counterWidgets;
|
||||||
|
QMap<int, AbstractCounter *> castCountWidgets;
|
||||||
QMap<QString, CardZone *> zoneGraphicsItems;
|
QMap<QString, CardZone *> zoneGraphicsItems;
|
||||||
PileZone *deckZoneGraphicsItem;
|
PileZone *deckZoneGraphicsItem;
|
||||||
PileZone *sideboardGraphicsItem;
|
PileZone *sideboardGraphicsItem;
|
||||||
|
|
@ -142,10 +158,22 @@ private:
|
||||||
TableZone *tableZoneGraphicsItem;
|
TableZone *tableZoneGraphicsItem;
|
||||||
StackZone *stackZoneGraphicsItem;
|
StackZone *stackZoneGraphicsItem;
|
||||||
HandZone *handZoneGraphicsItem;
|
HandZone *handZoneGraphicsItem;
|
||||||
|
CommandZone *commandZoneGraphicsItem = nullptr;
|
||||||
QRectF bRect;
|
QRectF bRect;
|
||||||
bool mirrored;
|
bool mirrored;
|
||||||
bool handVisible = false;
|
bool handVisible = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the menu action a counter's submenu is inserted before to keep the
|
||||||
|
* Player -> Counters menu ordered by counter id, or nullptr to append.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QAction *counterMenuInsertAnchor(int counterId) const;
|
||||||
|
/** @brief Adds or removes a counter's submenu in the Player -> Counters menu. */
|
||||||
|
void setCounterMenuRegistered(AbstractCounter *widget, bool registered);
|
||||||
|
/** @brief Returns the command zone's display height, or 0 if hidden. */
|
||||||
|
[[nodiscard]] qreal totalCommandZoneHeight() const;
|
||||||
|
/** @brief Positions the command and stack zones vertically starting from base. */
|
||||||
|
void positionCommandAndStackZones(const QPointF &base);
|
||||||
private slots:
|
private slots:
|
||||||
void updateBoundingRect();
|
void updateBoundingRect();
|
||||||
void rearrangeZones();
|
void rearrangeZones();
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ constexpr qreal OVERLAY_BASE = ZValueLayerManager::OVERLAY_BASE;
|
||||||
|
|
||||||
// Overlay layer Z-values for items that should appear above normal cards
|
// Overlay layer Z-values for items that should appear above normal cards
|
||||||
constexpr qreal HOVERED_CARD = ZValueLayerManager::overlayZValue(1.0);
|
constexpr qreal HOVERED_CARD = ZValueLayerManager::overlayZValue(1.0);
|
||||||
|
/** @brief Cast count overlay */
|
||||||
|
constexpr qreal CAST_COUNTS = ZValueLayerManager::overlayZValue(2.0);
|
||||||
constexpr qreal ARROWS = ZValueLayerManager::overlayZValue(3.0);
|
constexpr qreal ARROWS = ZValueLayerManager::overlayZValue(3.0);
|
||||||
constexpr qreal ZONE_VIEW_WIDGET = ZValueLayerManager::overlayZValue(4.0);
|
constexpr qreal ZONE_VIEW_WIDGET = ZValueLayerManager::overlayZValue(4.0);
|
||||||
constexpr qreal DRAG_ITEM = ZValueLayerManager::overlayZValue(5.0);
|
constexpr qreal DRAG_ITEM = ZValueLayerManager::overlayZValue(5.0);
|
||||||
|
|
|
||||||
187
cockatrice/src/game_graphics/zones/command_zone.cpp
Normal file
187
cockatrice/src/game_graphics/zones/command_zone.cpp
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
#include "command_zone.h"
|
||||||
|
|
||||||
|
#include "../../client/settings/cache_settings.h"
|
||||||
|
#include "../../game/player/player_actions.h"
|
||||||
|
#include "../../game/player/player_logic.h"
|
||||||
|
#include "../../interface/theme_manager.h"
|
||||||
|
#include "../board/abstract_counter.h"
|
||||||
|
#include "../board/card_drag_item.h"
|
||||||
|
#include "../board/card_item.h"
|
||||||
|
#include "../board/cast_count_widget.h"
|
||||||
|
#include "../z_values.h"
|
||||||
|
|
||||||
|
#include <QGraphicsSceneMouseEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
|
|
||||||
|
CommandZone::CommandZone(CommandZoneLogic *_logic, int _zoneHeight, QGraphicsItem *parent)
|
||||||
|
: SelectZone(_logic, parent), zoneHeight(_zoneHeight)
|
||||||
|
{
|
||||||
|
connect(themeManager, &ThemeManager::themeChanged, this, &CommandZone::updateBg);
|
||||||
|
updateBg();
|
||||||
|
setCacheMode(DeviceCoordinateCache);
|
||||||
|
setupClipContainer(ZValues::CARD_BASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandZone::~CommandZone()
|
||||||
|
{
|
||||||
|
for (AbstractCounter *ctr : castCounts) {
|
||||||
|
disconnect(ctr, &QObject::destroyed, this, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZone::updateBg()
|
||||||
|
{
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF CommandZone::boundingRect() const
|
||||||
|
{
|
||||||
|
return {0, 0, ZoneSizes::COMMAND_ZONE_WIDTH, currentHeight()};
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal CommandZone::currentHeight() const
|
||||||
|
{
|
||||||
|
return minimized ? qMax(zoneHeight * MINIMIZED_HEIGHT_RATIO, static_cast<double>(minimumHeight)) : zoneHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZone::setMinimumHeight(int height)
|
||||||
|
{
|
||||||
|
if (minimumHeight == height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The floor only affects height while minimized; skip relayout if the displayed
|
||||||
|
// 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)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareGeometryChange();
|
||||||
|
updateClipRect();
|
||||||
|
reorganizeCards();
|
||||||
|
update();
|
||||||
|
// effectiveHeightChanged repositions neighbouring zones; that path re-enters with the
|
||||||
|
// same floor and early-returns above, so it converges.
|
||||||
|
emit effectiveHeightChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CommandZone::isMinimized() const
|
||||||
|
{
|
||||||
|
return minimized;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZone::toggleMinimized()
|
||||||
|
{
|
||||||
|
minimized = !minimized;
|
||||||
|
|
||||||
|
prepareGeometryChange();
|
||||||
|
updateClipRect();
|
||||||
|
reorganizeCards();
|
||||||
|
update();
|
||||||
|
|
||||||
|
emit minimizedChanged(minimized);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZone::paint(QPainter *painter,
|
||||||
|
[[maybe_unused]] const QStyleOptionGraphicsItem *option,
|
||||||
|
[[maybe_unused]] QWidget *widget)
|
||||||
|
{
|
||||||
|
QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Command, getLogic()->getPlayer()->getZoneId());
|
||||||
|
|
||||||
|
QPointF scenePos = mapToScene(QPointF(0, 0));
|
||||||
|
painter->setBrushOrigin(-scenePos);
|
||||||
|
|
||||||
|
painter->fillRect(boundingRect(), brush);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
|
||||||
|
CardZoneLogic *startZone,
|
||||||
|
const QPoint &dropPoint)
|
||||||
|
{
|
||||||
|
if (startZone == nullptr || startZone->getPlayer() == nullptr || dragItems.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = calcDropIndexFromY(dropPoint.y(), MIN_CARD_VISIBLE);
|
||||||
|
|
||||||
|
Command_MoveCard cmd;
|
||||||
|
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||||
|
cmd.set_start_zone(startZone->getName().toStdString());
|
||||||
|
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
|
||||||
|
cmd.set_target_zone(getLogic()->getName().toStdString());
|
||||||
|
cmd.set_x(index);
|
||||||
|
cmd.set_y(0);
|
||||||
|
|
||||||
|
for (const CardDragItem *item : dragItems) {
|
||||||
|
if (item) {
|
||||||
|
auto *cardToMove = cmd.mutable_cards_to_move()->add_card();
|
||||||
|
cardToMove->set_card_id(item->getId());
|
||||||
|
if (item->isForceFaceDown()) {
|
||||||
|
cardToMove->set_face_down(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZone::reorganizeCards()
|
||||||
|
{
|
||||||
|
restoreStaleEscapedCards();
|
||||||
|
updateClipRect();
|
||||||
|
|
||||||
|
const auto &cards = getLogic()->getCards();
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto params = buildStackParams(MIN_CARD_VISIBLE);
|
||||||
|
params.allowBottomOverflow = true;
|
||||||
|
layoutCardsVertically(params);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZone::registerCastCount(AbstractCounter *counter)
|
||||||
|
{
|
||||||
|
if (!counter || castCounts.contains(counter)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
castCounts.append(counter);
|
||||||
|
connect(counter, &QObject::destroyed, this, [this, counter]() { castCounts.removeOne(counter); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZone::unregisterCastCount(AbstractCounter *counter)
|
||||||
|
{
|
||||||
|
if (!counter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
disconnect(counter, &QObject::destroyed, this, nullptr);
|
||||||
|
castCounts.removeOne(counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZone::rearrangeCastCounts()
|
||||||
|
{
|
||||||
|
qreal y = CastCountSizes::MARGIN;
|
||||||
|
|
||||||
|
for (AbstractCounter *ctr : castCounts) {
|
||||||
|
ctr->setPos(CastCountSizes::MARGIN, y);
|
||||||
|
ctr->setZValue(ZValues::CAST_COUNTS);
|
||||||
|
y += ctr->getRadius() + CastCountSizes::MARGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMinimumHeight(static_cast<int>(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandZone::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
toggleMinimized();
|
||||||
|
event->accept();
|
||||||
|
} else {
|
||||||
|
SelectZone::mouseDoubleClickEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
117
cockatrice/src/game_graphics/zones/command_zone.h
Normal file
117
cockatrice/src/game_graphics/zones/command_zone.h
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
* @file command_zone.h
|
||||||
|
* @ingroup GameGraphicsZones
|
||||||
|
* @brief Graphics layer for the command zone, used for Commander format.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COCKATRICE_COMMAND_ZONE_H
|
||||||
|
#define COCKATRICE_COMMAND_ZONE_H
|
||||||
|
|
||||||
|
#include "../../game/zones/command_zone_logic.h"
|
||||||
|
#include "../card_dimensions.h"
|
||||||
|
#include "select_zone.h"
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
class AbstractCounter;
|
||||||
|
|
||||||
|
inline Q_LOGGING_CATEGORY(CommandZoneLog, "command_zone");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace ZoneSizes
|
||||||
|
* @brief Size constants for the command zone and its sub-elements.
|
||||||
|
*/
|
||||||
|
namespace ZoneSizes
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @brief Height of the command zone (accommodates a card plus padding) */
|
||||||
|
constexpr qreal COMMAND_ZONE_HEIGHT = CardDimensions::HEIGHT + 8;
|
||||||
|
|
||||||
|
/** @brief Width of the command zone (matches stack zone) */
|
||||||
|
constexpr qreal COMMAND_ZONE_WIDTH = CardDimensions::WIDTH_F * 1.5;
|
||||||
|
|
||||||
|
} // namespace ZoneSizes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class CommandZone
|
||||||
|
* @brief Graphics layer for the command zone in Commander format games.
|
||||||
|
*
|
||||||
|
* Always visible when enabled. Uses the generic vertical stacking layout
|
||||||
|
* with bottom overflow enabled. Can be minimized via double-click (25% height,
|
||||||
|
* or the cast count floor if higher).
|
||||||
|
*
|
||||||
|
* @see SelectZone::layoutCardsVertically for the stacking algorithm
|
||||||
|
* @see CommandZoneLogic for card data management
|
||||||
|
* @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 cast count floor)
|
||||||
|
int minimumHeight = 0; ///< Floor for minimized height (e.g. to fit cast counts)
|
||||||
|
QList<AbstractCounter *> castCounts; ///< Registered cast count widgets
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a CommandZone graphics item.
|
||||||
|
* @param _logic Logic layer managing card data
|
||||||
|
* @param _zoneHeight Zone height in pixels
|
||||||
|
* @param parent Parent graphics item
|
||||||
|
*/
|
||||||
|
CommandZone(CommandZoneLogic *_logic, int _zoneHeight, QGraphicsItem *parent);
|
||||||
|
~CommandZone() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handles card drops, calculating insertion position from drop point.
|
||||||
|
* @param dragItems Cards being dragged
|
||||||
|
* @param startZone Source zone
|
||||||
|
* @param dropPoint Drop position in local coordinates
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override;
|
||||||
|
|
||||||
|
/** @brief Returns the bounding rectangle, accounting for minimized state. */
|
||||||
|
[[nodiscard]] QRectF boundingRect() const override;
|
||||||
|
/** @brief Paints the zone background using the Commander theme brush. */
|
||||||
|
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
|
||||||
|
/** @brief Repositions cards using vertical stacking with bottom overflow. */
|
||||||
|
void reorganizeCards() override;
|
||||||
|
|
||||||
|
/** @brief Toggles between full and minimized height. */
|
||||||
|
void toggleMinimized();
|
||||||
|
/** @brief Returns whether the zone is currently minimized. */
|
||||||
|
[[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 cast counts remain visible. */
|
||||||
|
void setMinimumHeight(int height);
|
||||||
|
/** @brief Registers a cast count widget for layout management. */
|
||||||
|
void registerCastCount(AbstractCounter *counter);
|
||||||
|
/**
|
||||||
|
* @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 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. cast count toggled while
|
||||||
|
* minimized). */
|
||||||
|
void effectiveHeightChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateBg();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COCKATRICE_COMMAND_ZONE_H
|
||||||
|
|
@ -94,7 +94,7 @@ int SelectZone::calcDropIndexFromY(qreal dropY, qreal minOffset) const
|
||||||
if (effectiveOffset <= 0.0) {
|
if (effectiveOffset <= 0.0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return qBound(0, qRound((dropY - start) / effectiveOffset), params.cardCount - 1);
|
return qBound(0, qRound((dropY - start) / effectiveOffset), params.cardCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectZone::restoreStaleEscapedCards()
|
void SelectZone::restoreStaleEscapedCards()
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ protected:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Computes the card index at a given y-coordinate within the zone's vertical layout.
|
* @brief Computes the card index at a given y-coordinate within the zone's vertical layout.
|
||||||
* Returns 0 if the zone has no cards or the offset is zero.
|
* Returns 0..cardCount (inclusive), where cardCount means append after the last card.
|
||||||
*/
|
*/
|
||||||
int calcDropIndexFromY(qreal dropY, qreal minOffset = 0.0) const;
|
int calcDropIndexFromY(qreal dropY, qreal minOffset = 0.0) const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ QRectF StackZone::boundingRect() const
|
||||||
void StackZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
void StackZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||||
{
|
{
|
||||||
QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Stack, getLogic()->getPlayer()->getZoneId());
|
QBrush brush = themeManager->getExtraBgBrush(ThemeManager::Stack, getLogic()->getPlayer()->getZoneId());
|
||||||
|
|
||||||
|
QPointF scenePos = mapToScene(QPointF(0, 0));
|
||||||
|
painter->setBrushOrigin(-scenePos);
|
||||||
|
|
||||||
painter->fillRect(boundingRect(), brush);
|
painter->fillRect(boundingRect(), brush);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,19 +47,7 @@ void StackZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &cards = getLogic()->getCards();
|
int index = calcDropIndexFromY(dropPoint.y(), MIN_CARD_VISIBLE);
|
||||||
int index;
|
|
||||||
if (startZone == getLogic()) {
|
|
||||||
// Reordering within the zone: use drop position
|
|
||||||
index = calcDropIndexFromY(dropPoint.y(), MIN_CARD_VISIBLE);
|
|
||||||
// Same-zone no-op: don't move a card onto itself
|
|
||||||
if (!cards.isEmpty() && cards.at(index)->getId() == dragItems.at(0)->getId()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Coming from another zone: append at end (top of stack, rendered on top)
|
|
||||||
index = static_cast<int>(cards.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
Command_MoveCard cmd;
|
Command_MoveCard cmd;
|
||||||
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,12 @@
|
||||||
#define PLAYERZONE_BG_NAME "playerzone"
|
#define PLAYERZONE_BG_NAME "playerzone"
|
||||||
#define STACKZONE_BG_NAME "stackzone"
|
#define STACKZONE_BG_NAME "stackzone"
|
||||||
#define TABLEZONE_BG_NAME "tablezone"
|
#define TABLEZONE_BG_NAME "tablezone"
|
||||||
|
#define COMMANDZONE_BG_NAME "commandzone"
|
||||||
static const QColor HANDZONE_BG_DEFAULT = QColor(80, 100, 50);
|
static const QColor HANDZONE_BG_DEFAULT = QColor(80, 100, 50);
|
||||||
static const QColor TABLEZONE_BG_DEFAULT = QColor(70, 50, 100);
|
static const QColor TABLEZONE_BG_DEFAULT = QColor(70, 50, 100);
|
||||||
static const QColor PLAYERZONE_BG_DEFAULT = QColor(200, 200, 200);
|
static const QColor PLAYERZONE_BG_DEFAULT = QColor(200, 200, 200);
|
||||||
static const QColor STACKZONE_BG_DEFAULT = QColor(113, 43, 43);
|
static const QColor STACKZONE_BG_DEFAULT = QColor(113, 43, 43);
|
||||||
|
static const QColor COMMANDZONE_BG_DEFAULT = QColor(50, 60, 80);
|
||||||
static const QStringList DEFAULT_RESOURCE_PATHS = {":/resources"};
|
static const QStringList DEFAULT_RESOURCE_PATHS = {":/resources"};
|
||||||
|
|
||||||
struct PaletteColorInfo
|
struct PaletteColorInfo
|
||||||
|
|
@ -424,6 +426,8 @@ void ThemeManager::themeChangedSlot()
|
||||||
brushes[Role::Player] = loadBrush(PLAYERZONE_BG_NAME, PLAYERZONE_BG_DEFAULT);
|
brushes[Role::Player] = loadBrush(PLAYERZONE_BG_NAME, PLAYERZONE_BG_DEFAULT);
|
||||||
|
|
||||||
brushes[Role::Stack] = loadBrush(STACKZONE_BG_NAME, STACKZONE_BG_DEFAULT);
|
brushes[Role::Stack] = loadBrush(STACKZONE_BG_NAME, STACKZONE_BG_DEFAULT);
|
||||||
|
|
||||||
|
brushes[Role::Command] = loadBrush(COMMANDZONE_BG_NAME, COMMANDZONE_BG_DEFAULT);
|
||||||
for (auto &brushCache : brushesCache) {
|
for (auto &brushCache : brushesCache) {
|
||||||
brushCache.clear();
|
brushCache.clear();
|
||||||
}
|
}
|
||||||
|
|
@ -448,6 +452,9 @@ static QString roleBgName(ThemeManager::Role role)
|
||||||
case ThemeManager::Table:
|
case ThemeManager::Table:
|
||||||
return TABLEZONE_BG_NAME;
|
return TABLEZONE_BG_NAME;
|
||||||
|
|
||||||
|
case ThemeManager::Command:
|
||||||
|
return COMMANDZONE_BG_NAME;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
return {};
|
return {};
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ public:
|
||||||
Stack,
|
Stack,
|
||||||
Table,
|
Table,
|
||||||
Player,
|
Player,
|
||||||
MaxRole = Player,
|
Command,
|
||||||
|
MaxRole = Command,
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ void DlgCreateGame::sharedCtor()
|
||||||
startingLifeTotalLabel->setBuddy(startingLifeTotalEdit);
|
startingLifeTotalLabel->setBuddy(startingLifeTotalEdit);
|
||||||
|
|
||||||
shareDecklistsOnLoadCheckBox = new QCheckBox(tr("Open decklists in lobby"));
|
shareDecklistsOnLoadCheckBox = new QCheckBox(tr("Open decklists in lobby"));
|
||||||
|
enableCommandZoneCheckBox = new QCheckBox(tr("Enable command zone"));
|
||||||
|
|
||||||
createGameAsJudgeCheckBox = new QCheckBox(tr("Create game as judge"));
|
createGameAsJudgeCheckBox = new QCheckBox(tr("Create game as judge"));
|
||||||
|
|
||||||
|
|
@ -110,8 +111,9 @@ void DlgCreateGame::sharedCtor()
|
||||||
gameSetupOptionsLayout->addWidget(startingLifeTotalLabel, 0, 0);
|
gameSetupOptionsLayout->addWidget(startingLifeTotalLabel, 0, 0);
|
||||||
gameSetupOptionsLayout->addWidget(startingLifeTotalEdit, 0, 1);
|
gameSetupOptionsLayout->addWidget(startingLifeTotalEdit, 0, 1);
|
||||||
gameSetupOptionsLayout->addWidget(shareDecklistsOnLoadCheckBox, 1, 0);
|
gameSetupOptionsLayout->addWidget(shareDecklistsOnLoadCheckBox, 1, 0);
|
||||||
|
gameSetupOptionsLayout->addWidget(enableCommandZoneCheckBox, 2, 0);
|
||||||
if (room && room->getUserInfo()->user_level() & ServerInfo_User::IsJudge) {
|
if (room && room->getUserInfo()->user_level() & ServerInfo_User::IsJudge) {
|
||||||
gameSetupOptionsLayout->addWidget(createGameAsJudgeCheckBox, 2, 0);
|
gameSetupOptionsLayout->addWidget(createGameAsJudgeCheckBox, 3, 0);
|
||||||
} else {
|
} else {
|
||||||
createGameAsJudgeCheckBox->setChecked(false);
|
createGameAsJudgeCheckBox->setChecked(false);
|
||||||
createGameAsJudgeCheckBox->setHidden(true);
|
createGameAsJudgeCheckBox->setHidden(true);
|
||||||
|
|
@ -172,6 +174,7 @@ DlgCreateGame::DlgCreateGame(TabRoom *_room, const QMap<int, QString> &_gameType
|
||||||
createGameAsSpectatorCheckBox->setChecked(SettingsCache::instance().game().getCreateGameAsSpectator());
|
createGameAsSpectatorCheckBox->setChecked(SettingsCache::instance().game().getCreateGameAsSpectator());
|
||||||
startingLifeTotalEdit->setValue(SettingsCache::instance().game().getDefaultStartingLifeTotal());
|
startingLifeTotalEdit->setValue(SettingsCache::instance().game().getDefaultStartingLifeTotal());
|
||||||
shareDecklistsOnLoadCheckBox->setChecked(SettingsCache::instance().game().getShareDecklistsOnLoad());
|
shareDecklistsOnLoadCheckBox->setChecked(SettingsCache::instance().game().getShareDecklistsOnLoad());
|
||||||
|
enableCommandZoneCheckBox->setChecked(SettingsCache::instance().game().getEnableCommandZone());
|
||||||
|
|
||||||
if (!rememberGameSettings->isChecked()) {
|
if (!rememberGameSettings->isChecked()) {
|
||||||
actReset();
|
actReset();
|
||||||
|
|
@ -205,6 +208,7 @@ DlgCreateGame::DlgCreateGame(const ServerInfo_Game &gameInfo, const QMap<int, QS
|
||||||
createGameAsSpectatorCheckBox->setEnabled(false);
|
createGameAsSpectatorCheckBox->setEnabled(false);
|
||||||
startingLifeTotalEdit->setEnabled(false);
|
startingLifeTotalEdit->setEnabled(false);
|
||||||
shareDecklistsOnLoadCheckBox->setEnabled(false);
|
shareDecklistsOnLoadCheckBox->setEnabled(false);
|
||||||
|
enableCommandZoneCheckBox->setEnabled(false);
|
||||||
|
|
||||||
descriptionEdit->setText(QString::fromStdString(gameInfo.description()));
|
descriptionEdit->setText(QString::fromStdString(gameInfo.description()));
|
||||||
maxPlayersEdit->setValue(gameInfo.max_players());
|
maxPlayersEdit->setValue(gameInfo.max_players());
|
||||||
|
|
@ -215,6 +219,7 @@ DlgCreateGame::DlgCreateGame(const ServerInfo_Game &gameInfo, const QMap<int, QS
|
||||||
spectatorsCanTalkCheckBox->setChecked(gameInfo.spectators_can_chat());
|
spectatorsCanTalkCheckBox->setChecked(gameInfo.spectators_can_chat());
|
||||||
spectatorsSeeEverythingCheckBox->setChecked(gameInfo.spectators_omniscient());
|
spectatorsSeeEverythingCheckBox->setChecked(gameInfo.spectators_omniscient());
|
||||||
shareDecklistsOnLoadCheckBox->setChecked(gameInfo.share_decklists_on_load());
|
shareDecklistsOnLoadCheckBox->setChecked(gameInfo.share_decklists_on_load());
|
||||||
|
enableCommandZoneCheckBox->setChecked(gameInfo.enable_command_zone());
|
||||||
|
|
||||||
QSet<int> types;
|
QSet<int> types;
|
||||||
for (int i = 0; i < gameInfo.game_types_size(); ++i) {
|
for (int i = 0; i < gameInfo.game_types_size(); ++i) {
|
||||||
|
|
@ -252,6 +257,7 @@ void DlgCreateGame::actReset()
|
||||||
|
|
||||||
startingLifeTotalEdit->setValue(20);
|
startingLifeTotalEdit->setValue(20);
|
||||||
shareDecklistsOnLoadCheckBox->setChecked(false);
|
shareDecklistsOnLoadCheckBox->setChecked(false);
|
||||||
|
enableCommandZoneCheckBox->setChecked(false);
|
||||||
createGameAsJudgeCheckBox->setChecked(false);
|
createGameAsJudgeCheckBox->setChecked(false);
|
||||||
|
|
||||||
QMapIterator<int, QRadioButton *> gameTypeCheckBoxIterator(gameTypeCheckBoxes);
|
QMapIterator<int, QRadioButton *> gameTypeCheckBoxIterator(gameTypeCheckBoxes);
|
||||||
|
|
@ -282,6 +288,7 @@ void DlgCreateGame::actOK()
|
||||||
cmd.set_join_as_spectator(createGameAsSpectatorCheckBox->isChecked());
|
cmd.set_join_as_spectator(createGameAsSpectatorCheckBox->isChecked());
|
||||||
cmd.set_starting_life_total(startingLifeTotalEdit->value());
|
cmd.set_starting_life_total(startingLifeTotalEdit->value());
|
||||||
cmd.set_share_decklists_on_load(shareDecklistsOnLoadCheckBox->isChecked());
|
cmd.set_share_decklists_on_load(shareDecklistsOnLoadCheckBox->isChecked());
|
||||||
|
cmd.set_enable_command_zone(enableCommandZoneCheckBox->isChecked());
|
||||||
|
|
||||||
auto _gameTypes = QString();
|
auto _gameTypes = QString();
|
||||||
QMapIterator<int, QRadioButton *> gameTypeCheckBoxIterator(gameTypeCheckBoxes);
|
QMapIterator<int, QRadioButton *> gameTypeCheckBoxIterator(gameTypeCheckBoxes);
|
||||||
|
|
@ -306,6 +313,7 @@ void DlgCreateGame::actOK()
|
||||||
SettingsCache::instance().game().setCreateGameAsSpectator(createGameAsSpectatorCheckBox->isChecked());
|
SettingsCache::instance().game().setCreateGameAsSpectator(createGameAsSpectatorCheckBox->isChecked());
|
||||||
SettingsCache::instance().game().setDefaultStartingLifeTotal(startingLifeTotalEdit->value());
|
SettingsCache::instance().game().setDefaultStartingLifeTotal(startingLifeTotalEdit->value());
|
||||||
SettingsCache::instance().game().setShareDecklistsOnLoad(shareDecklistsOnLoadCheckBox->isChecked());
|
SettingsCache::instance().game().setShareDecklistsOnLoad(shareDecklistsOnLoadCheckBox->isChecked());
|
||||||
|
SettingsCache::instance().game().setEnableCommandZone(enableCommandZoneCheckBox->isChecked());
|
||||||
SettingsCache::instance().game().setGameTypes(_gameTypes);
|
SettingsCache::instance().game().setGameTypes(_gameTypes);
|
||||||
}
|
}
|
||||||
PendingCommand *pend = room->prepareRoomCommand(cmd);
|
PendingCommand *pend = room->prepareRoomCommand(cmd);
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ private:
|
||||||
QCheckBox *spectatorsAllowedCheckBox, *spectatorsNeedPasswordCheckBox, *spectatorsCanTalkCheckBox,
|
QCheckBox *spectatorsAllowedCheckBox, *spectatorsNeedPasswordCheckBox, *spectatorsCanTalkCheckBox,
|
||||||
*spectatorsSeeEverythingCheckBox, *createGameAsJudgeCheckBox, *createGameAsSpectatorCheckBox;
|
*spectatorsSeeEverythingCheckBox, *createGameAsJudgeCheckBox, *createGameAsSpectatorCheckBox;
|
||||||
QCheckBox *shareDecklistsOnLoadCheckBox;
|
QCheckBox *shareDecklistsOnLoadCheckBox;
|
||||||
|
QCheckBox *enableCommandZoneCheckBox;
|
||||||
QDialogButtonBox *buttonBox;
|
QDialogButtonBox *buttonBox;
|
||||||
QPushButton *clearButton;
|
QPushButton *clearButton;
|
||||||
QCheckBox *rememberGameSettings;
|
QCheckBox *rememberGameSettings;
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,9 @@ DlgFilterGames::DlgFilterGames(const QMap<int, QString> &_allGameTypes,
|
||||||
hideOpenDecklistGames = new QCheckBox(tr("Hide games with forced open decklists"));
|
hideOpenDecklistGames = new QCheckBox(tr("Hide games with forced open decklists"));
|
||||||
hideOpenDecklistGames->setChecked(filters.hideOpenDecklistGames);
|
hideOpenDecklistGames->setChecked(filters.hideOpenDecklistGames);
|
||||||
|
|
||||||
|
hideCommandZoneGames = new QCheckBox(tr("Hide games with command zone enabled"));
|
||||||
|
hideCommandZoneGames->setChecked(filters.hideCommandZoneGames);
|
||||||
|
|
||||||
maxGameAgeComboBox = new QComboBox();
|
maxGameAgeComboBox = new QComboBox();
|
||||||
maxGameAgeComboBox->setEditable(false);
|
maxGameAgeComboBox->setEditable(false);
|
||||||
maxGameAgeComboBox->addItems(gameAgeMap.values());
|
maxGameAgeComboBox->addItems(gameAgeMap.values());
|
||||||
|
|
@ -122,6 +125,7 @@ DlgFilterGames::DlgFilterGames(const QMap<int, QString> &_allGameTypes,
|
||||||
restrictionsLayout->addWidget(hideIgnoredUserGames, 4, 0);
|
restrictionsLayout->addWidget(hideIgnoredUserGames, 4, 0);
|
||||||
restrictionsLayout->addWidget(hideNotBuddyCreatedGames, 5, 0);
|
restrictionsLayout->addWidget(hideNotBuddyCreatedGames, 5, 0);
|
||||||
restrictionsLayout->addWidget(hideOpenDecklistGames, 6, 0);
|
restrictionsLayout->addWidget(hideOpenDecklistGames, 6, 0);
|
||||||
|
restrictionsLayout->addWidget(hideCommandZoneGames, 7, 0);
|
||||||
|
|
||||||
auto *restrictionsGroupBox = new QGroupBox(tr("Restrictions"));
|
auto *restrictionsGroupBox = new QGroupBox(tr("Restrictions"));
|
||||||
restrictionsGroupBox->setLayout(restrictionsLayout);
|
restrictionsGroupBox->setLayout(restrictionsLayout);
|
||||||
|
|
@ -192,6 +196,7 @@ GameFilterConfigs DlgFilterGames::getFilters() const
|
||||||
hidePasswordProtectedGames->isChecked(),
|
hidePasswordProtectedGames->isChecked(),
|
||||||
hideNotBuddyCreatedGames->isChecked(),
|
hideNotBuddyCreatedGames->isChecked(),
|
||||||
hideOpenDecklistGames->isChecked(),
|
hideOpenDecklistGames->isChecked(),
|
||||||
|
hideCommandZoneGames->isChecked(),
|
||||||
gameNameFilterEdit->text(),
|
gameNameFilterEdit->text(),
|
||||||
getCreatorNameFilters(),
|
getCreatorNameFilters(),
|
||||||
getGameTypeFilter(),
|
getGameTypeFilter(),
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ private:
|
||||||
QCheckBox *hideIgnoredUserGames;
|
QCheckBox *hideIgnoredUserGames;
|
||||||
QCheckBox *hideNotBuddyCreatedGames;
|
QCheckBox *hideNotBuddyCreatedGames;
|
||||||
QCheckBox *hideOpenDecklistGames;
|
QCheckBox *hideOpenDecklistGames;
|
||||||
|
QCheckBox *hideCommandZoneGames;
|
||||||
QLineEdit *gameNameFilterEdit;
|
QLineEdit *gameNameFilterEdit;
|
||||||
QLineEdit *creatorNameFilterEdit;
|
QLineEdit *creatorNameFilterEdit;
|
||||||
QMap<int, QCheckBox *> gameTypeFilterCheckBoxes;
|
QMap<int, QCheckBox *> gameTypeFilterCheckBoxes;
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,13 @@ DlgLocalGameOptions::DlgLocalGameOptions(QWidget *parent) : QDialog(parent)
|
||||||
startingLifeTotalEdit->setValue(20);
|
startingLifeTotalEdit->setValue(20);
|
||||||
startingLifeTotalLabel->setBuddy(startingLifeTotalEdit);
|
startingLifeTotalLabel->setBuddy(startingLifeTotalEdit);
|
||||||
|
|
||||||
|
enableCommandZoneCheckBox = new QCheckBox(tr("Enable command zone"), this);
|
||||||
|
|
||||||
auto *gameSetupGrid = new QGridLayout;
|
auto *gameSetupGrid = new QGridLayout;
|
||||||
gameSetupGrid->setContentsMargins(5, 5, 5, 5);
|
gameSetupGrid->setContentsMargins(5, 5, 5, 5);
|
||||||
gameSetupGrid->addWidget(startingLifeTotalLabel, 0, 0);
|
gameSetupGrid->addWidget(startingLifeTotalLabel, 0, 0);
|
||||||
gameSetupGrid->addWidget(startingLifeTotalEdit, 0, 1);
|
gameSetupGrid->addWidget(startingLifeTotalEdit, 0, 1);
|
||||||
|
gameSetupGrid->addWidget(enableCommandZoneCheckBox, 1, 0, 1, 2);
|
||||||
gameSetupOptionsGroupBox = new QGroupBox(tr("Game setup options"), this);
|
gameSetupOptionsGroupBox = new QGroupBox(tr("Game setup options"), this);
|
||||||
gameSetupOptionsGroupBox->setLayout(gameSetupGrid);
|
gameSetupOptionsGroupBox->setLayout(gameSetupGrid);
|
||||||
|
|
||||||
|
|
@ -58,6 +61,7 @@ DlgLocalGameOptions::DlgLocalGameOptions(QWidget *parent) : QDialog(parent)
|
||||||
if (rememberSettingsCheckBox->isChecked()) {
|
if (rememberSettingsCheckBox->isChecked()) {
|
||||||
numberPlayersEdit->setValue(SettingsCache::instance().game().getLocalGameMaxPlayers());
|
numberPlayersEdit->setValue(SettingsCache::instance().game().getLocalGameMaxPlayers());
|
||||||
startingLifeTotalEdit->setValue(SettingsCache::instance().game().getLocalGameStartingLifeTotal());
|
startingLifeTotalEdit->setValue(SettingsCache::instance().game().getLocalGameStartingLifeTotal());
|
||||||
|
enableCommandZoneCheckBox->setChecked(SettingsCache::instance().game().getLocalGameEnableCommandZone());
|
||||||
}
|
}
|
||||||
|
|
||||||
setWindowTitle(tr("Local game options"));
|
setWindowTitle(tr("Local game options"));
|
||||||
|
|
@ -72,6 +76,7 @@ void DlgLocalGameOptions::actOK()
|
||||||
if (rememberSettingsCheckBox->isChecked()) {
|
if (rememberSettingsCheckBox->isChecked()) {
|
||||||
SettingsCache::instance().game().setLocalGameMaxPlayers(numberPlayersEdit->value());
|
SettingsCache::instance().game().setLocalGameMaxPlayers(numberPlayersEdit->value());
|
||||||
SettingsCache::instance().game().setLocalGameStartingLifeTotal(startingLifeTotalEdit->value());
|
SettingsCache::instance().game().setLocalGameStartingLifeTotal(startingLifeTotalEdit->value());
|
||||||
|
SettingsCache::instance().game().setLocalGameEnableCommandZone(enableCommandZoneCheckBox->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
accept();
|
accept();
|
||||||
|
|
@ -82,5 +87,6 @@ LocalGameOptions DlgLocalGameOptions::getOptions() const
|
||||||
return LocalGameOptions{
|
return LocalGameOptions{
|
||||||
.numberPlayers = numberPlayersEdit->value(),
|
.numberPlayers = numberPlayersEdit->value(),
|
||||||
.startingLifeTotal = startingLifeTotalEdit->value(),
|
.startingLifeTotal = startingLifeTotalEdit->value(),
|
||||||
|
.enableCommandZone = enableCommandZoneCheckBox->isChecked(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ struct LocalGameOptions
|
||||||
{
|
{
|
||||||
int numberPlayers = 1;
|
int numberPlayers = 1;
|
||||||
int startingLifeTotal = 20;
|
int startingLifeTotal = 20;
|
||||||
|
bool enableCommandZone = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class QCheckBox;
|
class QCheckBox;
|
||||||
|
|
@ -45,6 +46,7 @@ private:
|
||||||
QLabel *startingLifeTotalLabel;
|
QLabel *startingLifeTotalLabel;
|
||||||
QSpinBox *startingLifeTotalEdit;
|
QSpinBox *startingLifeTotalEdit;
|
||||||
|
|
||||||
|
QCheckBox *enableCommandZoneCheckBox;
|
||||||
QCheckBox *rememberSettingsCheckBox;
|
QCheckBox *rememberSettingsCheckBox;
|
||||||
|
|
||||||
QDialogButtonBox *buttonBox;
|
QDialogButtonBox *buttonBox;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ struct GameFilterConfigs
|
||||||
bool hidePasswordProtectedGames = false;
|
bool hidePasswordProtectedGames = false;
|
||||||
bool hideNotBuddyCreatedGames = false;
|
bool hideNotBuddyCreatedGames = false;
|
||||||
bool hideOpenDecklistGames = false;
|
bool hideOpenDecklistGames = false;
|
||||||
|
bool hideCommandZoneGames = false;
|
||||||
QString gameNameFilter = "";
|
QString gameNameFilter = "";
|
||||||
QStringList creatorNameFilters = {};
|
QStringList creatorNameFilters = {};
|
||||||
QSet<int> gameTypeFilter = {};
|
QSet<int> gameTypeFilter = {};
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,9 @@ QVariant GamesModel::data(const QModelIndex &index, int role) const
|
||||||
if (gameentry.share_decklists_on_load()) {
|
if (gameentry.share_decklists_on_load()) {
|
||||||
result.append(tr("open decklists"));
|
result.append(tr("open decklists"));
|
||||||
}
|
}
|
||||||
|
if (gameentry.enable_command_zone()) {
|
||||||
|
result.append(tr("command zone"));
|
||||||
|
}
|
||||||
return result.join(", ");
|
return result.join(", ");
|
||||||
}
|
}
|
||||||
case Qt::DecorationRole: {
|
case Qt::DecorationRole: {
|
||||||
|
|
@ -346,9 +349,9 @@ void GamesProxyModel::loadFilterParameters(const QMap<int, QString> &allGameType
|
||||||
setGameFilters({gameFilters.isHideBuddiesOnlyGames(), gameFilters.isHideIgnoredUserGames(),
|
setGameFilters({gameFilters.isHideBuddiesOnlyGames(), gameFilters.isHideIgnoredUserGames(),
|
||||||
gameFilters.isHideFullGames(), gameFilters.isHideGamesThatStarted(),
|
gameFilters.isHideFullGames(), gameFilters.isHideGamesThatStarted(),
|
||||||
gameFilters.isHidePasswordProtectedGames(), gameFilters.isHideNotBuddyCreatedGames(),
|
gameFilters.isHidePasswordProtectedGames(), gameFilters.isHideNotBuddyCreatedGames(),
|
||||||
gameFilters.isHideOpenDecklistGames(), gameFilters.getGameNameFilter(),
|
gameFilters.isHideOpenDecklistGames(), gameFilters.isHideCommandZoneGames(),
|
||||||
gameFilters.getCreatorNameFilters(), newGameTypeFilter, gameFilters.getMinPlayers(),
|
gameFilters.getGameNameFilter(), gameFilters.getCreatorNameFilters(), newGameTypeFilter,
|
||||||
gameFilters.getMaxPlayers(), gameFilters.getMaxGameAge(),
|
gameFilters.getMinPlayers(), gameFilters.getMaxPlayers(), gameFilters.getMaxGameAge(),
|
||||||
gameFilters.isShowOnlyIfSpectatorsCanWatch(), gameFilters.isShowSpectatorPasswordProtected(),
|
gameFilters.isShowOnlyIfSpectatorsCanWatch(), gameFilters.isShowSpectatorPasswordProtected(),
|
||||||
gameFilters.isShowOnlyIfSpectatorsCanChat(), gameFilters.isShowOnlyIfSpectatorsCanSeeHands()});
|
gameFilters.isShowOnlyIfSpectatorsCanChat(), gameFilters.isShowOnlyIfSpectatorsCanSeeHands()});
|
||||||
}
|
}
|
||||||
|
|
@ -363,6 +366,7 @@ void GamesProxyModel::saveFilterParameters(const QMap<int, QString> &allGameType
|
||||||
gameFilters.setHideIgnoredUserGames(filters.hideIgnoredUserGames);
|
gameFilters.setHideIgnoredUserGames(filters.hideIgnoredUserGames);
|
||||||
gameFilters.setHideNotBuddyCreatedGames(filters.hideNotBuddyCreatedGames);
|
gameFilters.setHideNotBuddyCreatedGames(filters.hideNotBuddyCreatedGames);
|
||||||
gameFilters.setHideOpenDecklistGames(filters.hideOpenDecklistGames);
|
gameFilters.setHideOpenDecklistGames(filters.hideOpenDecklistGames);
|
||||||
|
gameFilters.setHideCommandZoneGames(filters.hideCommandZoneGames);
|
||||||
gameFilters.setGameNameFilter(filters.gameNameFilter);
|
gameFilters.setGameNameFilter(filters.gameNameFilter);
|
||||||
gameFilters.setCreatorNameFilters(filters.creatorNameFilters);
|
gameFilters.setCreatorNameFilters(filters.creatorNameFilters);
|
||||||
|
|
||||||
|
|
@ -408,6 +412,9 @@ bool GamesProxyModel::filterAcceptsRow(int sourceRow) const
|
||||||
if (filters.hideOpenDecklistGames && game.share_decklists_on_load()) {
|
if (filters.hideOpenDecklistGames && game.share_decklists_on_load()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (filters.hideCommandZoneGames && game.enable_command_zone()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (filters.hideIgnoredUserGames &&
|
if (filters.hideIgnoredUserGames &&
|
||||||
userListProxy->isUserIgnored(QString::fromStdString(game.creator_info().name()))) {
|
userListProxy->isUserIgnored(QString::fromStdString(game.creator_info().name()))) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,7 @@ void MainWindow::startLocalGame(const LocalGameOptions &options)
|
||||||
Command_CreateGame createCommand;
|
Command_CreateGame createCommand;
|
||||||
createCommand.set_max_players(static_cast<google::protobuf::uint32>(options.numberPlayers));
|
createCommand.set_max_players(static_cast<google::protobuf::uint32>(options.numberPlayers));
|
||||||
createCommand.set_starting_life_total(options.startingLifeTotal);
|
createCommand.set_starting_life_total(options.startingLifeTotal);
|
||||||
|
createCommand.set_enable_command_zone(options.enableCommandZone);
|
||||||
mainClient->sendCommand(LocalClient::prepareRoomCommand(createCommand, 0));
|
mainClient->sendCommand(LocalClient::prepareRoomCommand(createCommand, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
BIN
cockatrice/themes/Fabric/zones/commandzone.png
Normal file
BIN
cockatrice/themes/Fabric/zones/commandzone.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 220 KiB |
BIN
cockatrice/themes/Leather/zones/commandzone.png
Normal file
BIN
cockatrice/themes/Leather/zones/commandzone.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 141 KiB |
BIN
cockatrice/themes/Plasma/zones/commandzone.png
Normal file
BIN
cockatrice/themes/Plasma/zones/commandzone.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
BIN
cockatrice/themes/VelvetMarble/zones/commandzone.jpg
Normal file
BIN
cockatrice/themes/VelvetMarble/zones/commandzone.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
|
|
@ -268,7 +268,10 @@ Client
|
||||||
**Server:**
|
**Server:**
|
||||||
- `Server_Player::cmdIncCounter`
|
- `Server_Player::cmdIncCounter`
|
||||||
- Rejects if the game has not started or the player has conceded
|
- Rejects if the game has not started or the player has conceded
|
||||||
- Updates the counter value
|
- Rejects tax counters when command zone is disabled (`RespContextError`)
|
||||||
|
- Rejects inactive tax counters (`RespContextError`)
|
||||||
|
- Rejects if counter doesn't exist (`RespNameNotFound`)
|
||||||
|
- Updates the counter value (clamped to `[minValue, maxValue]`)
|
||||||
- Emits `Event_SetCounter` only if the value changed
|
- Emits `Event_SetCounter` only if the value changed
|
||||||
|
|
||||||
**Client:**
|
**Client:**
|
||||||
|
|
@ -285,7 +288,8 @@ Client
|
||||||
**Server:**
|
**Server:**
|
||||||
- `Server_Player::cmdCreateCounter`
|
- `Server_Player::cmdCreateCounter`
|
||||||
- Rejects if the game has not started or the player has conceded
|
- Rejects if the game has not started or the player has conceded
|
||||||
- Allocates a new counter ID
|
- Rejects reserved tax counter names (`RespFunctionNotAllowed`)
|
||||||
|
- Allocates a new counter ID (starting at `CounterIds::FirstUserId`)
|
||||||
- Creates the counter
|
- Creates the counter
|
||||||
- Emits `Event_CreateCounter`
|
- Emits `Event_CreateCounter`
|
||||||
|
|
||||||
|
|
@ -302,7 +306,10 @@ Client
|
||||||
**Server:**
|
**Server:**
|
||||||
- `Server_Player::cmdSetCounter`
|
- `Server_Player::cmdSetCounter`
|
||||||
- Rejects if the game has not started or the player has conceded
|
- Rejects if the game has not started or the player has conceded
|
||||||
- Updates the counter value
|
- Rejects tax counters when command zone is disabled (`RespContextError`)
|
||||||
|
- Rejects inactive tax counters (`RespContextError`)
|
||||||
|
- Rejects if counter doesn't exist (`RespNameNotFound`)
|
||||||
|
- Updates the counter value (clamped to `[minValue, maxValue]`)
|
||||||
- Emits `Event_SetCounter` only if the value changed
|
- Emits `Event_SetCounter` only if the value changed
|
||||||
|
|
||||||
**Client:**
|
**Client:**
|
||||||
|
|
@ -319,6 +326,8 @@ Client
|
||||||
**Server:**
|
**Server:**
|
||||||
- `Server_Player::cmdDelCounter`
|
- `Server_Player::cmdDelCounter`
|
||||||
- Rejects if the game has not started or the player has conceded
|
- Rejects if the game has not started or the player has conceded
|
||||||
|
- Rejects tax counters (`RespFunctionNotAllowed`)
|
||||||
|
- Rejects if counter doesn't exist (`RespNameNotFound`)
|
||||||
- Deletes the counter
|
- Deletes the counter
|
||||||
- Emits `Event_DelCounter`
|
- Emits `Event_DelCounter`
|
||||||
|
|
||||||
|
|
@ -521,6 +530,26 @@ Client
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### `SET_COUNTER_ACTIVE` (1035)
|
||||||
|
|
||||||
|
**Purpose:** Show or hide a reserved tax counter without deleting it.
|
||||||
|
|
||||||
|
**Server:**
|
||||||
|
- `Server_Player::cmdSetCounterActive`
|
||||||
|
- Rejects if game not started (`RespGameNotStarted`)
|
||||||
|
- Rejects if player has conceded (`RespContextError`)
|
||||||
|
- Rejects for non-tax counters (`RespFunctionNotAllowed`)
|
||||||
|
- Rejects if command zone is disabled (`RespContextError`)
|
||||||
|
- Rejects if counter doesn't exist (`RespNameNotFound`)
|
||||||
|
- Rejects deactivation when counter has non-zero value (`RespContextError`)
|
||||||
|
- Emits `Event_SetCounterActive` only if the active state changed
|
||||||
|
|
||||||
|
**Client:**
|
||||||
|
- `PlayerEventHandler::eventSetCounterActive`
|
||||||
|
- Updates the counter's active state in the UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- Game commands are handled by `Server_Player`, `Server_AbstractParticipant`, or `Server_Game`, depending on the command.
|
- Game commands are handled by `Server_Player`, `Server_AbstractParticipant`, or `Server_Game`, depending on the command.
|
||||||
|
|
|
||||||
|
|
@ -476,8 +476,7 @@
|
||||||
* @ingroup GameMenus
|
* @ingroup GameMenus
|
||||||
* @brief Menus for interacting with zones.
|
* @brief Menus for interacting with zones.
|
||||||
*
|
*
|
||||||
* Provides contextual options for a CardZone such as the hand,
|
* Provides contextual options for card zones.
|
||||||
* library, graveyard, and battlefield.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -512,8 +511,7 @@
|
||||||
* @ingroup GameGraphics
|
* @ingroup GameGraphics
|
||||||
* @brief Graphical representations of zones.
|
* @brief Graphical representations of zones.
|
||||||
*
|
*
|
||||||
* Provides layout, visuals, and animations for a CardZone like the hand,
|
* Provides layout, visuals, and animations for card zones.
|
||||||
* library, battlefield, and graveyard.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -548,8 +546,7 @@
|
||||||
* @ingroup GameLogic
|
* @ingroup GameLogic
|
||||||
* @brief Logical handling of CardZones during a Game.
|
* @brief Logical handling of CardZones during a Game.
|
||||||
*
|
*
|
||||||
* Defines the rules and behaviors of zones such as the hand,
|
* Defines the rules and behaviors of card zones.
|
||||||
* battlefield, library, and graveyard.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,11 @@ public:
|
||||||
[[nodiscard]] virtual int getDefaultStartingLifeTotal() const = 0;
|
[[nodiscard]] virtual int getDefaultStartingLifeTotal() const = 0;
|
||||||
[[nodiscard]] virtual bool getShareDecklistsOnLoad() const = 0;
|
[[nodiscard]] virtual bool getShareDecklistsOnLoad() const = 0;
|
||||||
[[nodiscard]] virtual bool getRememberGameSettings() const = 0;
|
[[nodiscard]] virtual bool getRememberGameSettings() const = 0;
|
||||||
|
[[nodiscard]] virtual bool getEnableCommandZone() const = 0;
|
||||||
[[nodiscard]] virtual bool getLocalGameRememberSettings() const = 0;
|
[[nodiscard]] virtual bool getLocalGameRememberSettings() const = 0;
|
||||||
[[nodiscard]] virtual int getLocalGameMaxPlayers() const = 0;
|
[[nodiscard]] virtual int getLocalGameMaxPlayers() const = 0;
|
||||||
[[nodiscard]] virtual int getLocalGameStartingLifeTotal() const = 0;
|
[[nodiscard]] virtual int getLocalGameStartingLifeTotal() const = 0;
|
||||||
|
[[nodiscard]] virtual bool getLocalGameEnableCommandZone() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COCKATRICE_INTERFACE_GAME_SETTINGS_PROVIDER_H
|
#endif // COCKATRICE_INTERFACE_GAME_SETTINGS_PROVIDER_H
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,19 @@
|
||||||
#include <libcockatrice/protocol/pb/command_change_zone_properties.pb.h>
|
#include <libcockatrice/protocol/pb/command_change_zone_properties.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_concede.pb.h>
|
#include <libcockatrice/protocol/pb/command_concede.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_create_arrow.pb.h>
|
#include <libcockatrice/protocol/pb/command_create_arrow.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_create_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_create_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_create_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_create_token.pb.h>
|
#include <libcockatrice/protocol/pb/command_create_token.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_deck_select.pb.h>
|
#include <libcockatrice/protocol/pb/command_deck_select.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_del_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_del_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_delete_arrow.pb.h>
|
#include <libcockatrice/protocol/pb/command_delete_arrow.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_delete_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
|
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_dump_zone.pb.h>
|
#include <libcockatrice/protocol/pb/command_dump_zone.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_flip_card.pb.h>
|
#include <libcockatrice/protocol/pb/command_flip_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_game_say.pb.h>
|
#include <libcockatrice/protocol/pb/command_game_say.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_inc_card_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_inc_card_counter.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_inc_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_inc_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_inc_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_kick_from_game.pb.h>
|
#include <libcockatrice/protocol/pb/command_kick_from_game.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_leave_game.pb.h>
|
#include <libcockatrice/protocol/pb/command_leave_game.pb.h>
|
||||||
|
|
@ -36,6 +39,7 @@
|
||||||
#include <libcockatrice/protocol/pb/command_set_active_phase.pb.h>
|
#include <libcockatrice/protocol/pb/command_set_active_phase.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
|
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_set_card_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_set_card_counter.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_set_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_set_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_set_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_set_sideboard_lock.pb.h>
|
#include <libcockatrice/protocol/pb/command_set_sideboard_lock.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_set_sideboard_plan.pb.h>
|
#include <libcockatrice/protocol/pb/command_set_sideboard_plan.pb.h>
|
||||||
|
|
@ -342,6 +346,34 @@ Response::ResponseCode Server_AbstractParticipant::cmdDelCounter(const Command_D
|
||||||
return Response::RespFunctionNotAllowed;
|
return Response::RespFunctionNotAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
Response::ResponseCode Server_AbstractParticipant::cmdNextTurn(const Command_NextTurn & /*cmd*/,
|
Response::ResponseCode Server_AbstractParticipant::cmdNextTurn(const Command_NextTurn & /*cmd*/,
|
||||||
ResponseContainer & /*rc*/,
|
ResponseContainer & /*rc*/,
|
||||||
GameEventStorage & /*ges*/)
|
GameEventStorage & /*ges*/)
|
||||||
|
|
@ -525,6 +557,18 @@ Server_AbstractParticipant::processGameCommand(const GameCommand &command, Respo
|
||||||
case GameCommand::REVERSE_TURN:
|
case GameCommand::REVERSE_TURN:
|
||||||
return cmdReverseTurn(command.GetExtension(Command_ReverseTurn::ext), rc, ges);
|
return cmdReverseTurn(command.GetExtension(Command_ReverseTurn::ext), rc, ges);
|
||||||
break;
|
break;
|
||||||
|
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:
|
default:
|
||||||
return Response::RespInvalidCommand;
|
return Response::RespInvalidCommand;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,10 @@ class Command_SetSideboardPlan;
|
||||||
class Command_DeckSelect;
|
class Command_DeckSelect;
|
||||||
class Command_SetSideboardLock;
|
class Command_SetSideboardLock;
|
||||||
class Command_ChangeZoneProperties;
|
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
|
class Server_AbstractParticipant : public Server_ArrowTarget, public ServerInfo_User_Container
|
||||||
{
|
{
|
||||||
|
|
@ -172,6 +176,14 @@ public:
|
||||||
cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges);
|
cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges);
|
||||||
virtual Response::ResponseCode
|
virtual Response::ResponseCode
|
||||||
cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges);
|
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);
|
Response::ResponseCode processGameCommand(const GameCommand &command, ResponseContainer &rc, GameEventStorage &ges);
|
||||||
void sendGameEvent(const GameEventContainer &event);
|
void sendGameEvent(const GameEventContainer &event);
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,15 @@
|
||||||
|
|
||||||
#include <libcockatrice/protocol/pb/serverinfo_counter.pb.h>
|
#include <libcockatrice/protocol/pb/serverinfo_counter.pb.h>
|
||||||
|
|
||||||
Server_Counter::Server_Counter(int _id, const QString &_name, const color &_counterColor, int _radius, int _count)
|
Server_Counter::Server_Counter(int _id,
|
||||||
: id(_id), name(_name), counterColor(_counterColor), radius(_radius), count(_count)
|
const QString &_name,
|
||||||
|
const color &_counterColor,
|
||||||
|
int _radius,
|
||||||
|
int _count,
|
||||||
|
int _minValue,
|
||||||
|
int _maxValue)
|
||||||
|
: id(_id), name(_name), counterColor(_counterColor), radius(_radius), count(_count), minValue(_minValue),
|
||||||
|
maxValue(_maxValue)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,4 +21,5 @@ void Server_Counter::getInfo(ServerInfo_Counter *info)
|
||||||
info->mutable_counter_color()->CopyFrom(counterColor);
|
info->mutable_counter_color()->CopyFrom(counterColor);
|
||||||
info->set_radius(radius);
|
info->set_radius(radius);
|
||||||
info->set_count(count);
|
info->set_count(count);
|
||||||
|
info->set_active(active);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,12 @@ class ServerInfo_Counter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Server_Counter
|
* @class Server_Counter
|
||||||
* @brief Represents a player counter with overflow-safe increment arithmetic.
|
* @brief Represents a player counter with overflow-safe increment arithmetic and optional bounds.
|
||||||
*
|
*
|
||||||
* All value modifications return whether the value actually changed,
|
* All value modifications return whether the value actually changed,
|
||||||
* enabling callers to skip unnecessary network events.
|
* enabling callers to skip unnecessary network events.
|
||||||
*
|
*
|
||||||
* @note Direct assignment via setCount() does not clamp; only
|
* @note Values are clamped to [minValue, maxValue] on both setCount() and incrementCount().
|
||||||
* incrementCount() enforces int boundary saturation.
|
|
||||||
* @note Unlike card counters, player counters are never auto-removed
|
* @note Unlike card counters, player counters are never auto-removed
|
||||||
* when they reach zero - they persist with value 0.
|
* when they reach zero - they persist with value 0.
|
||||||
*/
|
*/
|
||||||
|
|
@ -47,9 +46,30 @@ protected:
|
||||||
color counterColor;
|
color counterColor;
|
||||||
int radius;
|
int radius;
|
||||||
int count;
|
int count;
|
||||||
|
int minValue; ///< Minimum allowed value (default: INT_MIN, i.e. unbounded)
|
||||||
|
int maxValue; ///< Maximum allowed value (default: INT_MAX, i.e. unbounded)
|
||||||
|
bool active = true; ///< Whether this counter is visible/active (default: true)
|
||||||
|
|
||||||
|
static constexpr int DEFAULT_MAX_VALUE = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Server_Counter(int _id, const QString &_name, const color &_counterColor, int _radius, int _count = 0);
|
/**
|
||||||
|
* @brief Constructs a counter.
|
||||||
|
* @param _id Unique counter identifier
|
||||||
|
* @param _name Display name
|
||||||
|
* @param _counterColor Counter color
|
||||||
|
* @param _radius Display radius
|
||||||
|
* @param _count Initial value (default 0)
|
||||||
|
* @param _minValue Minimum allowed value (default INT_MIN)
|
||||||
|
* @param _maxValue Maximum allowed value (default INT_MAX)
|
||||||
|
*/
|
||||||
|
Server_Counter(int _id,
|
||||||
|
const QString &_name,
|
||||||
|
const color &_counterColor,
|
||||||
|
int _radius,
|
||||||
|
int _count = 0,
|
||||||
|
int _minValue = std::numeric_limits<int>::min(),
|
||||||
|
int _maxValue = DEFAULT_MAX_VALUE);
|
||||||
~Server_Counter()
|
~Server_Counter()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -73,18 +93,31 @@ public:
|
||||||
{
|
{
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
bool isActive() const
|
||||||
|
{
|
||||||
|
return active;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @brief Sets the counter to an exact value.
|
* @brief Sets the active (visible) state of this counter.
|
||||||
* @param _count The new value (assigned directly without clamping).
|
* @param _active True to show the counter, false to hide it
|
||||||
* @return true if the value changed, false otherwise.
|
* @return true if the state changed
|
||||||
* @warning This performs raw assignment. For overflow-safe incrementing,
|
*/
|
||||||
* use incrementCount().
|
[[nodiscard]] bool setActive(bool _active)
|
||||||
|
{
|
||||||
|
bool oldActive = active;
|
||||||
|
active = _active;
|
||||||
|
return active != oldActive;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Sets the counter value, clamping to [minValue, maxValue].
|
||||||
|
* @param _count The desired new value
|
||||||
|
* @return true if the clamped value differs from the previous value
|
||||||
|
* @note For increment operations, prefer incrementCount() which handles overflow safely.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] bool setCount(int _count)
|
[[nodiscard]] bool setCount(int _count)
|
||||||
{
|
{
|
||||||
const int oldCount = count;
|
int oldCount = count;
|
||||||
count = _count;
|
count = qBound(minValue, _count, maxValue);
|
||||||
return count != oldCount;
|
return count != oldCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,12 +125,12 @@ public:
|
||||||
* @brief Increments the counter by delta with overflow-safe arithmetic.
|
* @brief Increments the counter by delta with overflow-safe arithmetic.
|
||||||
* @param delta The amount to add (may be negative for decrement).
|
* @param delta The amount to add (may be negative for decrement).
|
||||||
* @return true if the value changed, false otherwise.
|
* @return true if the value changed, false otherwise.
|
||||||
* @note Clamps result to [INT_MIN, INT_MAX] to prevent overflow.
|
* @note Clamps result to [minValue, maxValue] to prevent overflow.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] bool incrementCount(int delta)
|
[[nodiscard]] bool incrementCount(int delta)
|
||||||
{
|
{
|
||||||
const int oldCount = count;
|
const int oldCount = count;
|
||||||
count = addClamped(count, delta, std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
|
count = addClamped(count, delta, minValue, maxValue);
|
||||||
return count != oldCount;
|
return count != oldCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ Server_Game::Server_Game(const ServerInfo_User &_creatorInfo,
|
||||||
bool _spectatorsSeeEverything,
|
bool _spectatorsSeeEverything,
|
||||||
int _startingLifeTotal,
|
int _startingLifeTotal,
|
||||||
bool _shareDecklistsOnLoad,
|
bool _shareDecklistsOnLoad,
|
||||||
|
bool _enableCommandZone,
|
||||||
Server_Room *_room)
|
Server_Room *_room)
|
||||||
: QObject(), room(_room), nextPlayerId(0), hostId(0), creatorInfo(new ServerInfo_User(_creatorInfo)),
|
: QObject(), room(_room), nextPlayerId(0), hostId(0), creatorInfo(new ServerInfo_User(_creatorInfo)),
|
||||||
gameStarted(false), gameClosed(false), gameId(_gameId), password(_password), maxPlayers(_maxPlayers),
|
gameStarted(false), gameClosed(false), gameId(_gameId), password(_password), maxPlayers(_maxPlayers),
|
||||||
|
|
@ -74,9 +75,9 @@ Server_Game::Server_Game(const ServerInfo_User &_creatorInfo,
|
||||||
onlyRegistered(_onlyRegistered), spectatorsAllowed(_spectatorsAllowed),
|
onlyRegistered(_onlyRegistered), spectatorsAllowed(_spectatorsAllowed),
|
||||||
spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk),
|
spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk),
|
||||||
spectatorsSeeEverything(_spectatorsSeeEverything), startingLifeTotal(_startingLifeTotal),
|
spectatorsSeeEverything(_spectatorsSeeEverything), startingLifeTotal(_startingLifeTotal),
|
||||||
shareDecklistsOnLoad(_shareDecklistsOnLoad), inactivityCounter(0), startTimeOfThisGame(0), secondsElapsed(0),
|
shareDecklistsOnLoad(_shareDecklistsOnLoad), enableCommandZone(_enableCommandZone), inactivityCounter(0),
|
||||||
firstGameStarted(false), turnOrderReversed(false), startTime(QDateTime::currentDateTime()), pingClock(nullptr),
|
startTimeOfThisGame(0), secondsElapsed(0), firstGameStarted(false), turnOrderReversed(false),
|
||||||
gameMutex()
|
startTime(QDateTime::currentDateTime()), pingClock(nullptr), gameMutex()
|
||||||
{
|
{
|
||||||
currentReplay = new GameReplay;
|
currentReplay = new GameReplay;
|
||||||
currentReplay->set_replay_id(room->getServer()->getDatabaseInterface()->getNextReplayId());
|
currentReplay->set_replay_id(room->getServer()->getDatabaseInterface()->getNextReplayId());
|
||||||
|
|
@ -854,6 +855,7 @@ void Server_Game::getInfo(ServerInfo_Game &result) const
|
||||||
result.set_spectators_can_chat(spectatorsCanTalk);
|
result.set_spectators_can_chat(spectatorsCanTalk);
|
||||||
result.set_spectators_omniscient(spectatorsSeeEverything);
|
result.set_spectators_omniscient(spectatorsSeeEverything);
|
||||||
result.set_share_decklists_on_load(shareDecklistsOnLoad);
|
result.set_share_decklists_on_load(shareDecklistsOnLoad);
|
||||||
|
result.set_enable_command_zone(enableCommandZone);
|
||||||
result.set_spectators_count(getSpectatorCount());
|
result.set_spectators_count(getSpectatorCount());
|
||||||
result.set_start_time(startTime.toSecsSinceEpoch());
|
result.set_start_time(startTime.toSecsSinceEpoch());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ private:
|
||||||
bool spectatorsSeeEverything;
|
bool spectatorsSeeEverything;
|
||||||
int startingLifeTotal;
|
int startingLifeTotal;
|
||||||
bool shareDecklistsOnLoad;
|
bool shareDecklistsOnLoad;
|
||||||
|
bool enableCommandZone;
|
||||||
int inactivityCounter;
|
int inactivityCounter;
|
||||||
int startTimeOfThisGame, secondsElapsed;
|
int startTimeOfThisGame, secondsElapsed;
|
||||||
bool firstGameStarted;
|
bool firstGameStarted;
|
||||||
|
|
@ -106,6 +107,7 @@ public:
|
||||||
bool _spectatorsSeeEverything,
|
bool _spectatorsSeeEverything,
|
||||||
int _startingLifeTotal,
|
int _startingLifeTotal,
|
||||||
bool _shareDecklistsOnLoad,
|
bool _shareDecklistsOnLoad,
|
||||||
|
bool _enableCommandZone,
|
||||||
Server_Room *parent);
|
Server_Room *parent);
|
||||||
~Server_Game() override;
|
~Server_Game() override;
|
||||||
Server_Room *getRoom() const
|
Server_Room *getRoom() const
|
||||||
|
|
@ -173,6 +175,10 @@ public:
|
||||||
{
|
{
|
||||||
return shareDecklistsOnLoad;
|
return shareDecklistsOnLoad;
|
||||||
}
|
}
|
||||||
|
bool getEnableCommandZone() const
|
||||||
|
{
|
||||||
|
return enableCommandZone;
|
||||||
|
}
|
||||||
Response::ResponseCode
|
Response::ResponseCode
|
||||||
checkJoin(ServerInfo_User *user, const QString &_password, bool spectator, bool overrideRestrictions, bool asJudge);
|
checkJoin(ServerInfo_User *user, const QString &_password, bool spectator, bool overrideRestrictions, bool asJudge);
|
||||||
bool containsUser(const QString &userName) const;
|
bool containsUser(const QString &userName) const;
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,18 @@
|
||||||
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
|
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_change_zone_properties.pb.h>
|
#include <libcockatrice/protocol/pb/command_change_zone_properties.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_concede.pb.h>
|
#include <libcockatrice/protocol/pb/command_concede.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_create_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_create_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_create_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_deck_select.pb.h>
|
#include <libcockatrice/protocol/pb/command_deck_select.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_del_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_del_counter.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_delete_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
|
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_inc_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_inc_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_inc_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_mulligan.pb.h>
|
#include <libcockatrice/protocol/pb/command_mulligan.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_set_active_phase.pb.h>
|
#include <libcockatrice/protocol/pb/command_set_active_phase.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/command_set_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_set_counter.pb.h>
|
#include <libcockatrice/protocol/pb/command_set_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_set_sideboard_lock.pb.h>
|
#include <libcockatrice/protocol/pb/command_set_sideboard_lock.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/command_set_sideboard_plan.pb.h>
|
#include <libcockatrice/protocol/pb/command_set_sideboard_plan.pb.h>
|
||||||
|
|
@ -33,20 +37,26 @@
|
||||||
#include <libcockatrice/protocol/pb/context_deck_select.pb.h>
|
#include <libcockatrice/protocol/pb/context_deck_select.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/context_mulligan.pb.h>
|
#include <libcockatrice/protocol/pb/context_mulligan.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/context_set_sideboard_lock.pb.h>
|
#include <libcockatrice/protocol/pb/context_set_sideboard_lock.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/event_create_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_create_counter.pb.h>
|
#include <libcockatrice/protocol/pb/event_create_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_del_counter.pb.h>
|
#include <libcockatrice/protocol/pb/event_del_counter.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/event_delete_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_draw_cards.pb.h>
|
#include <libcockatrice/protocol/pb/event_draw_cards.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_game_log_notice.pb.h>
|
#include <libcockatrice/protocol/pb/event_game_log_notice.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_player_properties_changed.pb.h>
|
#include <libcockatrice/protocol/pb/event_player_properties_changed.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/event_set_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_set_counter.pb.h>
|
#include <libcockatrice/protocol/pb/event_set_counter.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/event_shuffle.pb.h>
|
#include <libcockatrice/protocol/pb/event_shuffle.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/response.pb.h>
|
#include <libcockatrice/protocol/pb/response.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/response_deck_download.pb.h>
|
#include <libcockatrice/protocol/pb/response_deck_download.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/response_dump_zone.pb.h>
|
#include <libcockatrice/protocol/pb/response_dump_zone.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/serverinfo_cast_count.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/serverinfo_player.pb.h>
|
#include <libcockatrice/protocol/pb/serverinfo_player.pb.h>
|
||||||
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||||
#include <libcockatrice/rng/rng_abstract.h>
|
#include <libcockatrice/rng/rng_abstract.h>
|
||||||
#include <libcockatrice/utility/color.h>
|
#include <libcockatrice/utility/color.h>
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
|
#include <libcockatrice/utility/counter_limits.h>
|
||||||
#include <libcockatrice/utility/string_limits.h>
|
#include <libcockatrice/utility/string_limits.h>
|
||||||
#include <libcockatrice/utility/zone_names.h>
|
#include <libcockatrice/utility/zone_names.h>
|
||||||
|
|
||||||
|
|
@ -71,7 +81,7 @@ int Server_Player::newCounterId() const
|
||||||
id = c->getId();
|
id = c->getId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return id + 1;
|
return std::max(id + 1, CounterIds::FirstUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server_Player::setupZones()
|
void Server_Player::setupZones()
|
||||||
|
|
@ -101,6 +111,12 @@ void Server_Player::setupZones()
|
||||||
addCounter(new Server_Counter(6, "x", makeColor(255, 255, 255), 20, 0));
|
addCounter(new Server_Counter(6, "x", makeColor(255, 255, 255), 20, 0));
|
||||||
addCounter(new Server_Counter(7, "storm", makeColor(255, 150, 30), 20, 0));
|
addCounter(new Server_Counter(7, "storm", makeColor(255, 150, 30), 20, 0));
|
||||||
|
|
||||||
|
// Command zone for Commander format
|
||||||
|
if (game->getEnableCommandZone()) {
|
||||||
|
addZone(new Server_CardZone(this, ZoneNames::COMMAND, false, ServerInfo_Zone::PublicZone));
|
||||||
|
castCounts.insert(1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
// Assign card ids and create deck from deck list
|
// Assign card ids and create deck from deck list
|
||||||
|
|
@ -155,6 +171,7 @@ void Server_Player::clearZones()
|
||||||
delete counter;
|
delete counter;
|
||||||
}
|
}
|
||||||
counters.clear();
|
counters.clear();
|
||||||
|
castCounts.clear();
|
||||||
|
|
||||||
lastDrawList.clear();
|
lastDrawList.clear();
|
||||||
}
|
}
|
||||||
|
|
@ -427,19 +444,29 @@ Server_Player::cmdUndoDraw(const Command_UndoDraw & /*cmd*/, ResponseContainer &
|
||||||
}
|
}
|
||||||
|
|
||||||
Response::ResponseCode
|
Response::ResponseCode
|
||||||
Server_Player::cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
Server_Player::evaluateModifyCounter(bool gameStarted, bool playerConceded, const Server_Counter *counter)
|
||||||
{
|
{
|
||||||
if (!game->getGameStarted()) {
|
if (!gameStarted) {
|
||||||
return Response::RespGameNotStarted;
|
return Response::RespGameNotStarted;
|
||||||
}
|
}
|
||||||
if (conceded) {
|
if (playerConceded) {
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
}
|
}
|
||||||
|
if (!counter) {
|
||||||
|
return Response::RespNameNotFound;
|
||||||
|
}
|
||||||
|
return Response::RespOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response::ResponseCode
|
||||||
|
Server_Player::cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
||||||
|
{
|
||||||
const int counterId = cmd.counter_id();
|
const int counterId = cmd.counter_id();
|
||||||
Server_Counter *c = counters.value(counterId, nullptr);
|
Server_Counter *c = counters.value(counterId, nullptr);
|
||||||
if (!c) {
|
|
||||||
return Response::RespNameNotFound;
|
const Response::ResponseCode authResult = evaluateModifyCounter(game->getGameStarted(), conceded, c);
|
||||||
|
if (authResult != Response::RespOk) {
|
||||||
|
return authResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool didChange = c->incrementCount(cmd.delta());
|
bool didChange = c->incrementCount(cmd.delta());
|
||||||
|
|
@ -454,17 +481,28 @@ Server_Player::cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer &
|
||||||
}
|
}
|
||||||
|
|
||||||
Response::ResponseCode
|
Response::ResponseCode
|
||||||
Server_Player::cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
Server_Player::evaluateCreateCounter(bool gameStarted, bool playerConceded, const QString & /*counterName*/)
|
||||||
{
|
{
|
||||||
if (!game->getGameStarted()) {
|
if (!gameStarted) {
|
||||||
return Response::RespGameNotStarted;
|
return Response::RespGameNotStarted;
|
||||||
}
|
}
|
||||||
if (conceded) {
|
if (playerConceded) {
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
}
|
}
|
||||||
|
return Response::RespOk;
|
||||||
|
}
|
||||||
|
|
||||||
auto *c = new Server_Counter(newCounterId(), nameFromStdString(cmd.counter_name()), cmd.counter_color(),
|
Response::ResponseCode
|
||||||
cmd.radius(), cmd.value());
|
Server_Player::cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
||||||
|
{
|
||||||
|
const QString counterName = nameFromStdString(cmd.counter_name());
|
||||||
|
|
||||||
|
const Response::ResponseCode authResult = evaluateCreateCounter(game->getGameStarted(), conceded, counterName);
|
||||||
|
if (authResult != Response::RespOk) {
|
||||||
|
return authResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *c = new Server_Counter(newCounterId(), counterName, cmd.counter_color(), cmd.radius(), cmd.value());
|
||||||
addCounter(c);
|
addCounter(c);
|
||||||
|
|
||||||
Event_CreateCounter event;
|
Event_CreateCounter event;
|
||||||
|
|
@ -482,17 +520,12 @@ Server_Player::cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContai
|
||||||
Response::ResponseCode
|
Response::ResponseCode
|
||||||
Server_Player::cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
Server_Player::cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
||||||
{
|
{
|
||||||
if (!game->getGameStarted()) {
|
|
||||||
return Response::RespGameNotStarted;
|
|
||||||
}
|
|
||||||
if (conceded) {
|
|
||||||
return Response::RespContextError;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int counterId = cmd.counter_id();
|
const int counterId = cmd.counter_id();
|
||||||
Server_Counter *c = counters.value(counterId, nullptr);
|
Server_Counter *c = counters.value(counterId, nullptr);
|
||||||
if (!c) {
|
|
||||||
return Response::RespNameNotFound;
|
const Response::ResponseCode authResult = evaluateModifyCounter(game->getGameStarted(), conceded, c);
|
||||||
|
if (authResult != Response::RespOk) {
|
||||||
|
return authResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool didChange = c->setCount(cmd.value());
|
bool didChange = c->setCount(cmd.value());
|
||||||
|
|
@ -507,20 +540,31 @@ Server_Player::cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer &
|
||||||
}
|
}
|
||||||
|
|
||||||
Response::ResponseCode
|
Response::ResponseCode
|
||||||
Server_Player::cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
Server_Player::evaluateDelCounter(bool gameStarted, bool playerConceded, const Server_Counter *counter)
|
||||||
{
|
{
|
||||||
if (!game->getGameStarted()) {
|
if (!gameStarted) {
|
||||||
return Response::RespGameNotStarted;
|
return Response::RespGameNotStarted;
|
||||||
}
|
}
|
||||||
if (conceded) {
|
if (playerConceded) {
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int counterId = cmd.counter_id();
|
|
||||||
Server_Counter *counter = counters.value(counterId, nullptr);
|
|
||||||
if (!counter) {
|
if (!counter) {
|
||||||
return Response::RespNameNotFound;
|
return Response::RespNameNotFound;
|
||||||
}
|
}
|
||||||
|
return Response::RespOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response::ResponseCode
|
||||||
|
Server_Player::cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges)
|
||||||
|
{
|
||||||
|
const int counterId = cmd.counter_id();
|
||||||
|
Server_Counter *counter = counters.value(counterId, nullptr);
|
||||||
|
|
||||||
|
const Response::ResponseCode authResult = evaluateDelCounter(game->getGameStarted(), conceded, counter);
|
||||||
|
if (authResult != Response::RespOk) {
|
||||||
|
return authResult;
|
||||||
|
}
|
||||||
|
|
||||||
counters.remove(counterId);
|
counters.remove(counterId);
|
||||||
delete counter;
|
delete counter;
|
||||||
|
|
||||||
|
|
@ -531,6 +575,187 @@ Server_Player::cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer &
|
||||||
return Response::RespOk;
|
return Response::RespOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
if (playerConceded) {
|
||||||
|
return Response::RespContextError;
|
||||||
|
}
|
||||||
|
if (!commandZoneEnabled) {
|
||||||
|
return Response::RespContextError;
|
||||||
|
}
|
||||||
|
if (!CastCountIds::isValidIndex(index)) {
|
||||||
|
return Response::RespContextError;
|
||||||
|
}
|
||||||
|
if (exists) {
|
||||||
|
return Response::RespContextError;
|
||||||
|
}
|
||||||
|
if (index > 1 && !predecessorExists) {
|
||||||
|
return Response::RespContextError;
|
||||||
|
}
|
||||||
|
return Response::RespOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response::ResponseCode Server_Player::evaluateDeleteCastCount(bool gameStarted,
|
||||||
|
bool playerConceded,
|
||||||
|
bool commandZoneEnabled,
|
||||||
|
int index,
|
||||||
|
bool exists,
|
||||||
|
int value,
|
||||||
|
bool successorExists)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response::RespOk;
|
||||||
|
}
|
||||||
|
|
||||||
Response::ResponseCode
|
Response::ResponseCode
|
||||||
Server_Player::cmdNextTurn(const Command_NextTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/)
|
Server_Player::cmdNextTurn(const Command_NextTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/)
|
||||||
{
|
{
|
||||||
|
|
@ -604,4 +829,10 @@ void Server_Player::getInfo(ServerInfo_Player *info,
|
||||||
for (Server_Counter *counter : counters) {
|
for (Server_Counter *counter : counters) {
|
||||||
counter->getInfo(info->add_counter_list());
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ class Server_Player : public Server_AbstractPlayer
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private:
|
private:
|
||||||
QMap<int, Server_Counter *> counters;
|
QMap<int, Server_Counter *> counters;
|
||||||
|
QMap<int, int> castCounts; // index (1-5) -> value
|
||||||
QList<int> lastDrawList;
|
QList<int> lastDrawList;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -21,9 +22,41 @@ public:
|
||||||
{
|
{
|
||||||
return counters;
|
return counters;
|
||||||
}
|
}
|
||||||
|
const QMap<int, int> &getCastCounts() const
|
||||||
|
{
|
||||||
|
return castCounts;
|
||||||
|
}
|
||||||
int newCounterId() const;
|
int newCounterId() const;
|
||||||
void addCounter(Server_Counter *counter);
|
void addCounter(Server_Counter *counter);
|
||||||
|
|
||||||
|
static Response::ResponseCode
|
||||||
|
evaluateDelCounter(bool gameStarted, bool playerConceded, const Server_Counter *counter);
|
||||||
|
|
||||||
|
static Response::ResponseCode
|
||||||
|
evaluateModifyCounter(bool gameStarted, bool playerConceded, const Server_Counter *counter);
|
||||||
|
|
||||||
|
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 setupZones() override;
|
||||||
void clearZones() override;
|
void clearZones() override;
|
||||||
|
|
||||||
|
|
@ -66,6 +99,16 @@ public:
|
||||||
ResponseContainer &rc,
|
ResponseContainer &rc,
|
||||||
GameEventStorage &ges) override;
|
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,
|
void getInfo(ServerInfo_Player *info,
|
||||||
Server_AbstractParticipant *playerWhosAsking,
|
Server_AbstractParticipant *playerWhosAsking,
|
||||||
bool omniscient,
|
bool omniscient,
|
||||||
|
|
|
||||||
|
|
@ -899,10 +899,11 @@ Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room
|
||||||
|
|
||||||
// When server doesn't permit registered users to exist, do not honor only-reg setting
|
// When server doesn't permit registered users to exist, do not honor only-reg setting
|
||||||
bool onlyRegisteredUsers = cmd.only_registered() && (server->permitUnregisteredUsers());
|
bool onlyRegisteredUsers = cmd.only_registered() && (server->permitUnregisteredUsers());
|
||||||
auto *game = new Server_Game(copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()),
|
auto *game =
|
||||||
cmd.max_players(), gameTypes, cmd.only_buddies(), onlyRegisteredUsers,
|
new Server_Game(copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()),
|
||||||
cmd.spectators_allowed(), cmd.spectators_need_password(), cmd.spectators_can_talk(),
|
cmd.max_players(), gameTypes, cmd.only_buddies(), onlyRegisteredUsers, cmd.spectators_allowed(),
|
||||||
cmd.spectators_see_everything(), startingLifeTotal, shareDecklistsOnLoad, room);
|
cmd.spectators_need_password(), cmd.spectators_can_talk(), cmd.spectators_see_everything(),
|
||||||
|
startingLifeTotal, shareDecklistsOnLoad, cmd.enable_command_zone(), room);
|
||||||
|
|
||||||
game->addPlayer(this, rc, asSpectator, asJudge, false);
|
game->addPlayer(this, rc, asSpectator, asJudge, false);
|
||||||
room->addGame(game);
|
room->addGame(game);
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ set(PROTO_FILES
|
||||||
command_change_zone_properties.proto
|
command_change_zone_properties.proto
|
||||||
command_concede.proto
|
command_concede.proto
|
||||||
command_create_arrow.proto
|
command_create_arrow.proto
|
||||||
|
command_create_cast_count.proto
|
||||||
command_create_counter.proto
|
command_create_counter.proto
|
||||||
command_create_token.proto
|
command_create_token.proto
|
||||||
command_deck_del.proto
|
command_deck_del.proto
|
||||||
|
|
@ -21,11 +22,13 @@ set(PROTO_FILES
|
||||||
command_deck_upload.proto
|
command_deck_upload.proto
|
||||||
command_del_counter.proto
|
command_del_counter.proto
|
||||||
command_delete_arrow.proto
|
command_delete_arrow.proto
|
||||||
|
command_delete_cast_count.proto
|
||||||
command_draw_cards.proto
|
command_draw_cards.proto
|
||||||
command_dump_zone.proto
|
command_dump_zone.proto
|
||||||
command_flip_card.proto
|
command_flip_card.proto
|
||||||
command_game_say.proto
|
command_game_say.proto
|
||||||
command_inc_card_counter.proto
|
command_inc_card_counter.proto
|
||||||
|
command_inc_cast_count.proto
|
||||||
command_inc_counter.proto
|
command_inc_counter.proto
|
||||||
command_kick_from_game.proto
|
command_kick_from_game.proto
|
||||||
command_leave_game.proto
|
command_leave_game.proto
|
||||||
|
|
@ -45,6 +48,7 @@ set(PROTO_FILES
|
||||||
command_set_active_phase.proto
|
command_set_active_phase.proto
|
||||||
command_set_card_attr.proto
|
command_set_card_attr.proto
|
||||||
command_set_card_counter.proto
|
command_set_card_counter.proto
|
||||||
|
command_set_cast_count.proto
|
||||||
command_set_counter.proto
|
command_set_counter.proto
|
||||||
command_set_sideboard_lock.proto
|
command_set_sideboard_lock.proto
|
||||||
command_set_sideboard_plan.proto
|
command_set_sideboard_plan.proto
|
||||||
|
|
@ -65,10 +69,12 @@ set(PROTO_FILES
|
||||||
event_change_zone_properties.proto
|
event_change_zone_properties.proto
|
||||||
event_connection_closed.proto
|
event_connection_closed.proto
|
||||||
event_create_arrow.proto
|
event_create_arrow.proto
|
||||||
|
event_create_cast_count.proto
|
||||||
event_create_counter.proto
|
event_create_counter.proto
|
||||||
event_create_token.proto
|
event_create_token.proto
|
||||||
event_del_counter.proto
|
event_del_counter.proto
|
||||||
event_delete_arrow.proto
|
event_delete_arrow.proto
|
||||||
|
event_delete_cast_count.proto
|
||||||
event_destroy_card.proto
|
event_destroy_card.proto
|
||||||
event_draw_cards.proto
|
event_draw_cards.proto
|
||||||
event_dump_zone.proto
|
event_dump_zone.proto
|
||||||
|
|
@ -105,6 +111,7 @@ set(PROTO_FILES
|
||||||
event_set_active_player.proto
|
event_set_active_player.proto
|
||||||
event_set_card_attr.proto
|
event_set_card_attr.proto
|
||||||
event_set_card_counter.proto
|
event_set_card_counter.proto
|
||||||
|
event_set_cast_count.proto
|
||||||
event_set_counter.proto
|
event_set_counter.proto
|
||||||
event_shuffle.proto
|
event_shuffle.proto
|
||||||
event_user_joined.proto
|
event_user_joined.proto
|
||||||
|
|
@ -147,6 +154,7 @@ set(PROTO_FILES
|
||||||
server_message.proto
|
server_message.proto
|
||||||
serverinfo_arrow.proto
|
serverinfo_arrow.proto
|
||||||
serverinfo_ban.proto
|
serverinfo_ban.proto
|
||||||
|
serverinfo_cast_count.proto
|
||||||
serverinfo_card.proto
|
serverinfo_card.proto
|
||||||
serverinfo_cardcounter.proto
|
serverinfo_cardcounter.proto
|
||||||
serverinfo_chat_message.proto
|
serverinfo_chat_message.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];
|
||||||
|
}
|
||||||
|
|
@ -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];
|
||||||
|
}
|
||||||
|
|
@ -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];
|
||||||
|
}
|
||||||
|
|
@ -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];
|
||||||
|
}
|
||||||
|
|
@ -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];
|
||||||
|
}
|
||||||
|
|
@ -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];
|
||||||
|
}
|
||||||
|
|
@ -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];
|
||||||
|
}
|
||||||
|
|
@ -175,6 +175,26 @@ message GameCommand {
|
||||||
/// Server: Server_Player::cmdReverseTurn
|
/// Server: Server_Player::cmdReverseTurn
|
||||||
/// Client: reflected via subsequent turn events
|
/// Client: reflected via subsequent turn events
|
||||||
REVERSE_TURN = 1034;
|
REVERSE_TURN = 1034;
|
||||||
|
|
||||||
|
/// 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;
|
extensions 100 to max;
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ message GameEvent {
|
||||||
CHANGE_ZONE_PROPERTIES = 2020;
|
CHANGE_ZONE_PROPERTIES = 2020;
|
||||||
REVERSE_TURN = 2021;
|
REVERSE_TURN = 2021;
|
||||||
GAME_LOG_NOTICE = 2022;
|
GAME_LOG_NOTICE = 2022;
|
||||||
|
CREATE_CAST_COUNT = 2024;
|
||||||
|
DELETE_CAST_COUNT = 2025;
|
||||||
|
SET_CAST_COUNT = 2026;
|
||||||
}
|
}
|
||||||
optional sint32 player_id = 1 [default = -1];
|
optional sint32 player_id = 1 [default = -1];
|
||||||
extensions 100 to max;
|
extensions 100 to max;
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,9 @@ message Command_CreateGame {
|
||||||
|
|
||||||
// share decklists with all players when selected
|
// share decklists with all players when selected
|
||||||
optional bool share_decklists_on_load = 14;
|
optional bool share_decklists_on_load = 14;
|
||||||
|
|
||||||
|
// enable command zone for Commander format
|
||||||
|
optional bool enable_command_zone = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Command_JoinGame {
|
message Command_JoinGame {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
message ServerInfo_CastCount {
|
||||||
|
optional sint32 index = 1;
|
||||||
|
optional sint32 value = 2;
|
||||||
|
}
|
||||||
|
|
@ -7,4 +7,5 @@ message ServerInfo_Counter {
|
||||||
optional color counter_color = 3;
|
optional color counter_color = 3;
|
||||||
optional sint32 radius = 4;
|
optional sint32 radius = 4;
|
||||||
optional sint32 count = 5;
|
optional sint32 count = 5;
|
||||||
|
optional bool active = 6 [default = true];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@ message ServerInfo_Game {
|
||||||
// decklists are sent to all players when loaded
|
// decklists are sent to all players when loaded
|
||||||
optional bool share_decklists_on_load = 15;
|
optional bool share_decklists_on_load = 15;
|
||||||
|
|
||||||
|
// command zone is enabled for Commander format
|
||||||
|
optional bool enable_command_zone = 16;
|
||||||
|
|
||||||
// the current player count
|
// the current player count
|
||||||
optional uint32 player_count = 30;
|
optional uint32 player_count = 30;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import "serverinfo_zone.proto";
|
||||||
import "serverinfo_counter.proto";
|
import "serverinfo_counter.proto";
|
||||||
import "serverinfo_arrow.proto";
|
import "serverinfo_arrow.proto";
|
||||||
import "serverinfo_playerproperties.proto";
|
import "serverinfo_playerproperties.proto";
|
||||||
|
import "serverinfo_cast_count.proto";
|
||||||
|
|
||||||
message ServerInfo_Player {
|
message ServerInfo_Player {
|
||||||
optional ServerInfo_PlayerProperties properties = 1;
|
optional ServerInfo_PlayerProperties properties = 1;
|
||||||
|
|
@ -10,4 +11,5 @@ message ServerInfo_Player {
|
||||||
repeated ServerInfo_Zone zone_list = 3;
|
repeated ServerInfo_Zone zone_list = 3;
|
||||||
repeated ServerInfo_Counter counter_list = 4;
|
repeated ServerInfo_Counter counter_list = 4;
|
||||||
repeated ServerInfo_Arrow arrow_list = 5;
|
repeated ServerInfo_Arrow arrow_list = 5;
|
||||||
|
repeated ServerInfo_CastCount cast_count_list = 6;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,17 @@ bool GameFiltersSettings::isHideOpenDecklistGames() const
|
||||||
return previous == QVariant() ? false : previous.toBool();
|
return previous == QVariant() ? false : previous.toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameFiltersSettings::setHideCommandZoneGames(bool hide)
|
||||||
|
{
|
||||||
|
setValue(hide, "hide_command_zone_games");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameFiltersSettings::isHideCommandZoneGames() const
|
||||||
|
{
|
||||||
|
QVariant previous = getValue("hide_command_zone_games");
|
||||||
|
return previous == QVariant() ? false : previous.toBool();
|
||||||
|
}
|
||||||
|
|
||||||
void GameFiltersSettings::setGameNameFilter(QString gameName)
|
void GameFiltersSettings::setGameNameFilter(QString gameName)
|
||||||
{
|
{
|
||||||
setValue(gameName, "gameNameFilter");
|
setValue(gameName, "gameNameFilter");
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ public:
|
||||||
bool isHideIgnoredUserGames() const;
|
bool isHideIgnoredUserGames() const;
|
||||||
bool isHideNotBuddyCreatedGames() const;
|
bool isHideNotBuddyCreatedGames() const;
|
||||||
bool isHideOpenDecklistGames() const;
|
bool isHideOpenDecklistGames() const;
|
||||||
|
bool isHideCommandZoneGames() const;
|
||||||
QString getGameNameFilter() const;
|
QString getGameNameFilter() const;
|
||||||
QStringList getCreatorNameFilters() const;
|
QStringList getCreatorNameFilters() const;
|
||||||
int getMinPlayers() const;
|
int getMinPlayers() const;
|
||||||
|
|
@ -37,6 +38,7 @@ public:
|
||||||
void setHideBuddiesOnlyGames(bool hide);
|
void setHideBuddiesOnlyGames(bool hide);
|
||||||
void setHideIgnoredUserGames(bool hide);
|
void setHideIgnoredUserGames(bool hide);
|
||||||
void setHideOpenDecklistGames(bool hide);
|
void setHideOpenDecklistGames(bool hide);
|
||||||
|
void setHideCommandZoneGames(bool hide);
|
||||||
void setHideFullGames(bool hide);
|
void setHideFullGames(bool hide);
|
||||||
void setHideGamesThatStarted(bool hide);
|
void setHideGamesThatStarted(bool hide);
|
||||||
void setHidePasswordProtectedGames(bool hide);
|
void setHidePasswordProtectedGames(bool hide);
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,11 @@ bool GameSettings::getRememberGameSettings() const
|
||||||
return getValue("rememberGameSettings", "game", QString(), true).toBool();
|
return getValue("rememberGameSettings", "game", QString(), true).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameSettings::getEnableCommandZone() const
|
||||||
|
{
|
||||||
|
return getValue("enablecommandzone", "game").toBool();
|
||||||
|
}
|
||||||
|
|
||||||
bool GameSettings::getLocalGameRememberSettings() const
|
bool GameSettings::getLocalGameRememberSettings() const
|
||||||
{
|
{
|
||||||
return getValue("rememberSettings", "localgameoptions").toBool();
|
return getValue("rememberSettings", "localgameoptions").toBool();
|
||||||
|
|
@ -85,6 +90,11 @@ int GameSettings::getLocalGameStartingLifeTotal() const
|
||||||
return getValue("startingLifeTotal", "localgameoptions", QString(), 20).toInt();
|
return getValue("startingLifeTotal", "localgameoptions", QString(), 20).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameSettings::getLocalGameEnableCommandZone() const
|
||||||
|
{
|
||||||
|
return getValue("enablecommandzone", "localgameoptions").toBool();
|
||||||
|
}
|
||||||
|
|
||||||
void GameSettings::setGameDescription(const QString &_gameDescription)
|
void GameSettings::setGameDescription(const QString &_gameDescription)
|
||||||
{
|
{
|
||||||
setValue(_gameDescription, "gameDescription", "game");
|
setValue(_gameDescription, "gameDescription", "game");
|
||||||
|
|
@ -150,6 +160,11 @@ void GameSettings::setRememberGameSettings(bool _rememberGameSettings)
|
||||||
setValue(_rememberGameSettings, "rememberGameSettings", "game");
|
setValue(_rememberGameSettings, "rememberGameSettings", "game");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameSettings::setEnableCommandZone(bool _enableCommandZone)
|
||||||
|
{
|
||||||
|
setValue(_enableCommandZone, "enablecommandzone", "game");
|
||||||
|
}
|
||||||
|
|
||||||
void GameSettings::setLocalGameRememberSettings(bool value)
|
void GameSettings::setLocalGameRememberSettings(bool value)
|
||||||
{
|
{
|
||||||
setValue(value, "rememberSettings", "localgameoptions");
|
setValue(value, "rememberSettings", "localgameoptions");
|
||||||
|
|
@ -164,3 +179,8 @@ void GameSettings::setLocalGameStartingLifeTotal(int value)
|
||||||
{
|
{
|
||||||
setValue(value, "startingLifeTotal", "localgameoptions");
|
setValue(value, "startingLifeTotal", "localgameoptions");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameSettings::setLocalGameEnableCommandZone(bool value)
|
||||||
|
{
|
||||||
|
setValue(value, "enablecommandzone", "localgameoptions");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,11 @@ public:
|
||||||
[[nodiscard]] int getDefaultStartingLifeTotal() const override;
|
[[nodiscard]] int getDefaultStartingLifeTotal() const override;
|
||||||
[[nodiscard]] bool getShareDecklistsOnLoad() const override;
|
[[nodiscard]] bool getShareDecklistsOnLoad() const override;
|
||||||
[[nodiscard]] bool getRememberGameSettings() const override;
|
[[nodiscard]] bool getRememberGameSettings() const override;
|
||||||
|
[[nodiscard]] bool getEnableCommandZone() const override;
|
||||||
[[nodiscard]] bool getLocalGameRememberSettings() const override;
|
[[nodiscard]] bool getLocalGameRememberSettings() const override;
|
||||||
[[nodiscard]] int getLocalGameMaxPlayers() const override;
|
[[nodiscard]] int getLocalGameMaxPlayers() const override;
|
||||||
[[nodiscard]] int getLocalGameStartingLifeTotal() const override;
|
[[nodiscard]] int getLocalGameStartingLifeTotal() const override;
|
||||||
|
[[nodiscard]] bool getLocalGameEnableCommandZone() const override;
|
||||||
|
|
||||||
void setGameDescription(const QString &_gameDescription);
|
void setGameDescription(const QString &_gameDescription);
|
||||||
void setMaxPlayers(int _maxPlayers);
|
void setMaxPlayers(int _maxPlayers);
|
||||||
|
|
@ -41,9 +43,11 @@ public:
|
||||||
void setDefaultStartingLifeTotal(int _defaultStartingLifeTotal);
|
void setDefaultStartingLifeTotal(int _defaultStartingLifeTotal);
|
||||||
void setShareDecklistsOnLoad(bool _shareDecklistsOnLoad);
|
void setShareDecklistsOnLoad(bool _shareDecklistsOnLoad);
|
||||||
void setRememberGameSettings(bool _rememberGameSettings);
|
void setRememberGameSettings(bool _rememberGameSettings);
|
||||||
|
void setEnableCommandZone(bool _enableCommandZone);
|
||||||
void setLocalGameRememberSettings(bool value);
|
void setLocalGameRememberSettings(bool value);
|
||||||
void setLocalGameMaxPlayers(int value);
|
void setLocalGameMaxPlayers(int value);
|
||||||
void setLocalGameStartingLifeTotal(int value);
|
void setLocalGameStartingLifeTotal(int value);
|
||||||
|
void setLocalGameEnableCommandZone(bool value);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GameSettings(const QString &settingPath, QObject *parent = nullptr);
|
explicit GameSettings(const QString &settingPath, QObject *parent = nullptr);
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ set(UTILITY_HEADERS
|
||||||
libcockatrice/utility/string_limits.h
|
libcockatrice/utility/string_limits.h
|
||||||
libcockatrice/utility/dice_limits.h
|
libcockatrice/utility/dice_limits.h
|
||||||
libcockatrice/utility/counter_limits.h
|
libcockatrice/utility/counter_limits.h
|
||||||
|
libcockatrice/utility/counter_ids.h
|
||||||
libcockatrice/utility/clamped_arithmetic.h
|
libcockatrice/utility/clamped_arithmetic.h
|
||||||
libcockatrice/utility/zone_names.h
|
libcockatrice/utility/zone_names.h
|
||||||
libcockatrice/utility/days_years_between.h
|
libcockatrice/utility/days_years_between.h
|
||||||
|
|
|
||||||
49
libcockatrice_utility/libcockatrice/utility/counter_ids.h
Normal file
49
libcockatrice_utility/libcockatrice/utility/counter_ids.h
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* @file counter_ids.h
|
||||||
|
* @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
|
||||||
|
#define COCKATRICE_COUNTER_IDS_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace CounterIds
|
||||||
|
* @brief Reserved counter ID ranges.
|
||||||
|
*/
|
||||||
|
namespace CounterIds
|
||||||
|
{
|
||||||
|
constexpr int FirstUserId = 8; ///< First ID available for user-created counters (0-7 are built-in)
|
||||||
|
} // namespace CounterIds
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace CastCountIds
|
||||||
|
* @brief Cast count index range and utilities.
|
||||||
|
*
|
||||||
|
* Cast counts use a separate 1-based index space (1-5), not sharing IDs with regular counters.
|
||||||
|
*/
|
||||||
|
namespace CastCountIds
|
||||||
|
{
|
||||||
|
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 true if the index is in the valid cast count range [1, 5]. */
|
||||||
|
inline bool isValidIndex(int index)
|
||||||
|
{
|
||||||
|
return index >= MinIndex && index <= MaxIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Returns the internal name for a cast count at the given index (e.g. "cast_count_1"). */
|
||||||
|
inline QString nameForIndex(int index)
|
||||||
|
{
|
||||||
|
return QString("cast_count_%1").arg(index);
|
||||||
|
}
|
||||||
|
} // namespace CastCountIds
|
||||||
|
|
||||||
|
#endif // COCKATRICE_COUNTER_IDS_H
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
*
|
*
|
||||||
* Caps an individual counter's VALUE (e.g. a +1/+1 counter at 999), not how many counters
|
* 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,
|
* 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.
|
* 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.
|
* The max of 999 is a display constraint (3-digit rendering) and a reasonable gameplay limit.
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ constexpr const char *DECK = "deck";
|
||||||
constexpr const char *SIDEBOARD = "sb";
|
constexpr const char *SIDEBOARD = "sb";
|
||||||
constexpr const char *STACK = "stack";
|
constexpr const char *STACK = "stack";
|
||||||
|
|
||||||
|
// Command zone (Commander format)
|
||||||
|
constexpr const char *COMMAND = "command";
|
||||||
|
|
||||||
} // namespace ZoneNames
|
} // namespace ZoneNames
|
||||||
|
|
||||||
#endif // ZONE_NAMES_H
|
#endif // ZONE_NAMES_H
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ target_link_libraries(
|
||||||
|
|
||||||
add_subdirectory(card_zone_algorithms)
|
add_subdirectory(card_zone_algorithms)
|
||||||
add_subdirectory(carddatabase)
|
add_subdirectory(carddatabase)
|
||||||
|
add_subdirectory(command_zone_tests)
|
||||||
add_subdirectory(loading_from_clipboard)
|
add_subdirectory(loading_from_clipboard)
|
||||||
add_subdirectory(movecard_tests)
|
add_subdirectory(movecard_tests)
|
||||||
add_subdirectory(oracle)
|
add_subdirectory(oracle)
|
||||||
|
|
|
||||||
50
tests/command_zone_tests/CMakeLists.txt
Normal file
50
tests/command_zone_tests/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
add_executable(new_counter_id_test new_counter_id_test.cpp)
|
||||||
|
|
||||||
|
if(NOT GTEST_FOUND)
|
||||||
|
add_dependencies(new_counter_id_test gtest)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
new_counter_id_test
|
||||||
|
PRIVATE libcockatrice_network_server_remote
|
||||||
|
PRIVATE libcockatrice_rng
|
||||||
|
PRIVATE Threads::Threads
|
||||||
|
PRIVATE ${GTEST_BOTH_LIBRARIES}
|
||||||
|
PRIVATE ${TEST_QT_MODULES}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(NAME new_counter_id_test COMMAND new_counter_id_test)
|
||||||
|
|
||||||
|
add_executable(counter_command_auth_test counter_command_auth_test.cpp)
|
||||||
|
|
||||||
|
if(NOT GTEST_FOUND)
|
||||||
|
add_dependencies(counter_command_auth_test gtest)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
counter_command_auth_test
|
||||||
|
PRIVATE libcockatrice_network_server_remote
|
||||||
|
PRIVATE libcockatrice_rng
|
||||||
|
PRIVATE Threads::Threads
|
||||||
|
PRIVATE ${GTEST_BOTH_LIBRARIES}
|
||||||
|
PRIVATE ${TEST_QT_MODULES}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(NAME counter_command_auth_test COMMAND counter_command_auth_test)
|
||||||
|
|
||||||
|
add_executable(setup_zones_command_zone_test setup_zones_command_zone_test.cpp)
|
||||||
|
|
||||||
|
if(NOT GTEST_FOUND)
|
||||||
|
add_dependencies(setup_zones_command_zone_test gtest)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
setup_zones_command_zone_test
|
||||||
|
PRIVATE libcockatrice_network_server_remote
|
||||||
|
PRIVATE libcockatrice_rng
|
||||||
|
PRIVATE Threads::Threads
|
||||||
|
PRIVATE ${GTEST_BOTH_LIBRARIES}
|
||||||
|
PRIVATE ${TEST_QT_MODULES}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(NAME setup_zones_command_zone_test COMMAND setup_zones_command_zone_test)
|
||||||
220
tests/command_zone_tests/counter_command_auth_test.cpp
Normal file
220
tests/command_zone_tests/counter_command_auth_test.cpp
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
/** @file counter_command_auth_test.cpp
|
||||||
|
* @brief Tests for Server_Player counter-command authorization helpers.
|
||||||
|
* @ingroup Tests
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "game/server_counter.h"
|
||||||
|
#include "game/server_player.h"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <libcockatrice/protocol/pb/color.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/response.pb.h>
|
||||||
|
#include <libcockatrice/rng/rng_abstract.h>
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
|
|
||||||
|
RNG_Abstract *rng = nullptr; // required by linked server code
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr int UserCounterId = CounterIds::FirstUserId;
|
||||||
|
|
||||||
|
Server_Counter makeCounter(int id, int count)
|
||||||
|
{
|
||||||
|
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, &counter),
|
||||||
|
Response::RespGameNotStarted);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EvaluateDelCounter, RejectsWhenPlayerConceded)
|
||||||
|
{
|
||||||
|
Server_Counter counter = makeCounter(UserCounterId, 0);
|
||||||
|
EXPECT_EQ(Server_Player::evaluateDelCounter(/*gameStarted=*/true, /*playerConceded=*/true, &counter),
|
||||||
|
Response::RespContextError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EvaluateDelCounter, RejectsMissingCounter)
|
||||||
|
{
|
||||||
|
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, &counter), Response::RespOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EvaluateCreateCounter, RejectsWhenGameNotStarted)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(Server_Player::evaluateCreateCounter(/*gameStarted=*/false, /*playerConceded=*/false, "test"),
|
||||||
|
Response::RespGameNotStarted);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EvaluateCreateCounter, RejectsWhenPlayerConceded)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(Server_Player::evaluateCreateCounter(true, /*playerConceded=*/true, "test"), Response::RespContextError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EvaluateCreateCounter, AllowsUserCounterName)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(Server_Player::evaluateCreateCounter(true, false, "poison"), Response::RespOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EvaluateModifyCounter, RejectsWhenGameNotStarted)
|
||||||
|
{
|
||||||
|
Server_Counter counter = makeCounter(UserCounterId, 0);
|
||||||
|
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, &counter),
|
||||||
|
Response::RespContextError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EvaluateModifyCounter, RejectsMissingCounter)
|
||||||
|
{
|
||||||
|
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, &counter), Response::RespOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cast count evaluation tests
|
||||||
|
|
||||||
|
TEST(EvaluateCreateCastCount, RejectsWhenGameNotStarted)
|
||||||
|
{
|
||||||
|
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(EvaluateCreateCastCount, RejectsWhenCommandZoneDisabled)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(Server_Player::evaluateCreateCastCount(true, false, /*commandZoneEnabled=*/false, 1, false, true),
|
||||||
|
Response::RespContextError);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EvaluateCreateCastCount, RejectsInvalidIndex)
|
||||||
|
{
|
||||||
|
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(EvaluateModifyCastCount, RejectsWhenGameNotStarted)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
72
tests/command_zone_tests/new_counter_id_test.cpp
Normal file
72
tests/command_zone_tests/new_counter_id_test.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
/** @file new_counter_id_test.cpp
|
||||||
|
* @brief Tests for Server_Player::newCounterId() id allocation.
|
||||||
|
* @ingroup Tests
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../movecard_tests/server_test_helpers.h"
|
||||||
|
#include "game/server_counter.h"
|
||||||
|
#include "game/server_game.h"
|
||||||
|
#include "game/server_player.h"
|
||||||
|
#include "server_room.h"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <libcockatrice/protocol/pb/color.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||||
|
#include <libcockatrice/rng/rng_abstract.h>
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
|
|
||||||
|
RNG_Abstract *rng = nullptr; // required by linked server code
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct PlayerFixture
|
||||||
|
{
|
||||||
|
ServerInfo_User user;
|
||||||
|
FakeServer server;
|
||||||
|
Server_Room room{0, 0, "", "", "", "", false, "", {}, &server};
|
||||||
|
Server_Game game{user, 1, "", "", 2, QList<int>(), false, false,
|
||||||
|
false, false, false, false, 20, false, false, &room};
|
||||||
|
Server_Player player{&game, 1, user, false, nullptr};
|
||||||
|
|
||||||
|
~PlayerFixture()
|
||||||
|
{
|
||||||
|
player.clearZones(); // owns and deletes any counters added during the test
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(NewCounterId, ReturnsFirstUserIdWhenNoCounters)
|
||||||
|
{
|
||||||
|
PlayerFixture f;
|
||||||
|
EXPECT_EQ(f.player.newCounterId(), CounterIds::FirstUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NewCounterId, SkipsReservedRangeWhenOnlyReservedCountersExist)
|
||||||
|
{
|
||||||
|
PlayerFixture f;
|
||||||
|
f.player.addCounter(new Server_Counter(3, "g", color(), 20, 0));
|
||||||
|
f.player.addCounter(new Server_Counter(5, "h", color(), 20, 0));
|
||||||
|
EXPECT_EQ(f.player.newCounterId(), CounterIds::FirstUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NewCounterId, ReturnsNextIdAboveHighestUserCounter)
|
||||||
|
{
|
||||||
|
PlayerFixture f;
|
||||||
|
f.player.addCounter(new Server_Counter(CounterIds::FirstUserId, "a", color(), 20, 0));
|
||||||
|
f.player.addCounter(new Server_Counter(CounterIds::FirstUserId + 2, "b", color(), 20, 0));
|
||||||
|
EXPECT_EQ(f.player.newCounterId(), CounterIds::FirstUserId + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NewCounterId, IgnoresReservedCountersWhenUserCountersPresent)
|
||||||
|
{
|
||||||
|
PlayerFixture f;
|
||||||
|
f.player.addCounter(new Server_Counter(5, "r", color(), 20, 0)); // reserved range
|
||||||
|
f.player.addCounter(new Server_Counter(CounterIds::FirstUserId, "user", color(), 20, 0)); // user range
|
||||||
|
EXPECT_EQ(f.player.newCounterId(), CounterIds::FirstUserId + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
116
tests/command_zone_tests/setup_zones_command_zone_test.cpp
Normal file
116
tests/command_zone_tests/setup_zones_command_zone_test.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
/** @file setup_zones_command_zone_test.cpp
|
||||||
|
* @brief Tests for the command-zone branch of Server_Player::setupZones().
|
||||||
|
* @ingroup Tests
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../movecard_tests/server_test_helpers.h"
|
||||||
|
#include "game/server_counter.h"
|
||||||
|
#include "game/server_game.h"
|
||||||
|
#include "game/server_player.h"
|
||||||
|
#include "server_room.h"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <libcockatrice/deck_list/deck_list.h>
|
||||||
|
#include <libcockatrice/protocol/pb/color.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||||
|
#include <libcockatrice/rng/rng_abstract.h>
|
||||||
|
#include <libcockatrice/utility/counter_ids.h>
|
||||||
|
#include <libcockatrice/utility/counter_limits.h>
|
||||||
|
#include <libcockatrice/utility/zone_names.h>
|
||||||
|
|
||||||
|
RNG_Abstract *rng = nullptr; // required by linked server code
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief A Server_Player whose protected deck member is populated with an empty deck.
|
||||||
|
*
|
||||||
|
* setupZones() reads deck->getCardNodes(); the base member is null by default, so this
|
||||||
|
* subclass injects an empty deck. An empty deck makes the card-loading loops no-ops,
|
||||||
|
* leaving only the zone/counter setup under test. The base destructor does not free deck
|
||||||
|
* (only prepareDestroy() does, which the test never calls), so this subclass deletes it.
|
||||||
|
*/
|
||||||
|
struct CommandZoneTestPlayer : Server_Player
|
||||||
|
{
|
||||||
|
CommandZoneTestPlayer(Server_Game *_game, const ServerInfo_User &_userInfo)
|
||||||
|
: Server_Player(_game, 1, _userInfo, false, nullptr)
|
||||||
|
{
|
||||||
|
deck = new DeckList();
|
||||||
|
}
|
||||||
|
|
||||||
|
~CommandZoneTestPlayer() override
|
||||||
|
{
|
||||||
|
delete deck;
|
||||||
|
deck = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Builds a Server_Player on a minimal fake game whose command zone is toggled by the
|
||||||
|
* constructor argument, then runs setupZones() so getCastCounts() reflects the result.
|
||||||
|
*/
|
||||||
|
struct SetupFixture
|
||||||
|
{
|
||||||
|
ServerInfo_User user;
|
||||||
|
FakeServer server;
|
||||||
|
Server_Room room{0, 0, "", "", "", "", false, "", {}, &server};
|
||||||
|
Server_Game game;
|
||||||
|
CommandZoneTestPlayer player;
|
||||||
|
|
||||||
|
explicit SetupFixture(bool commandZoneEnabled)
|
||||||
|
: game(user,
|
||||||
|
1,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
2,
|
||||||
|
QList<int>(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
20,
|
||||||
|
false,
|
||||||
|
commandZoneEnabled,
|
||||||
|
&room),
|
||||||
|
player(&game, user)
|
||||||
|
{
|
||||||
|
player.setupZones();
|
||||||
|
}
|
||||||
|
|
||||||
|
~SetupFixture()
|
||||||
|
{
|
||||||
|
player.clearZones(); // owns and deletes the counters created by setupZones()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(SetupZonesCommandZone, CreatesFirstCastCountWhenEnabled)
|
||||||
|
{
|
||||||
|
SetupFixture f(true);
|
||||||
|
const QMap<int, int> &castCounts = f.player.getCastCounts();
|
||||||
|
|
||||||
|
EXPECT_TRUE(f.player.getZones().contains(ZoneNames::COMMAND));
|
||||||
|
|
||||||
|
EXPECT_EQ(castCounts.size(), 1);
|
||||||
|
EXPECT_TRUE(castCounts.contains(1));
|
||||||
|
EXPECT_EQ(castCounts.value(1), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SetupZonesCommandZone, NoCastCountsWhenDisabled)
|
||||||
|
{
|
||||||
|
SetupFixture f(false);
|
||||||
|
const QMap<int, int> &castCounts = f.player.getCastCounts();
|
||||||
|
|
||||||
|
EXPECT_FALSE(f.player.getZones().contains(ZoneNames::COMMAND));
|
||||||
|
EXPECT_TRUE(castCounts.isEmpty());
|
||||||
|
|
||||||
|
EXPECT_TRUE(f.player.getCounters().contains(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,8 @@ TEST(ReverseCardMoveTest, MoveCardFromBottomTest)
|
||||||
// instantiate a fake server instance
|
// instantiate a fake server instance
|
||||||
FakeServer server;
|
FakeServer server;
|
||||||
Server_Room room(0, 0, "", "", "", "", false, "", {}, &server);
|
Server_Room room(0, 0, "", "", "", "", false, "", {}, &server);
|
||||||
Server_Game game(user, 1, "", "", 2, QList<int>(), false, false, false, false, false, false, 20, false, &room);
|
Server_Game game(user, 1, "", "", 2, QList<int>(), false, false, false, false, false, false, 20, false, false,
|
||||||
|
&room);
|
||||||
Server_AbstractPlayer player(&game, 1, user, false, nullptr);
|
Server_AbstractPlayer player(&game, 1, user, false, nullptr);
|
||||||
Server_CardZone deckZone(&player, ZoneNames::DECK, true, ServerInfo_Zone::PublicZone);
|
Server_CardZone deckZone(&player, ZoneNames::DECK, true, ServerInfo_Zone::PublicZone);
|
||||||
Server_CardZone exileZone(&player, ZoneNames::EXILE, true, ServerInfo_Zone::PublicZone);
|
Server_CardZone exileZone(&player, ZoneNames::EXILE, true, ServerInfo_Zone::PublicZone);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <libcockatrice/network/server/remote/game/server_counter.h>
|
#include <libcockatrice/network/server/remote/game/server_counter.h>
|
||||||
|
#include <libcockatrice/utility/counter_limits.h>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
TEST(ServerCounter, IncrementDoesNotOverflow)
|
TEST(ServerCounter, IncrementDoesNotOverflow)
|
||||||
|
|
@ -79,6 +80,47 @@ TEST(ServerCounter, MixedExtremesDoNotClamp)
|
||||||
EXPECT_EQ(c.getCount(), -1);
|
EXPECT_EQ(c.getCount(), -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ServerCounter, SetCountClampsToCustomBounds)
|
||||||
|
{
|
||||||
|
Server_Counter c(1, "test", color(), 10, 50, 0, 100);
|
||||||
|
EXPECT_TRUE(c.setCount(150));
|
||||||
|
EXPECT_EQ(c.getCount(), 100);
|
||||||
|
EXPECT_TRUE(c.setCount(-10));
|
||||||
|
EXPECT_EQ(c.getCount(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ServerCounter, IncrementClampsToCustomBounds)
|
||||||
|
{
|
||||||
|
Server_Counter c(1, "test", color(), 10, 50, 0, 100);
|
||||||
|
EXPECT_TRUE(c.incrementCount(100));
|
||||||
|
EXPECT_EQ(c.getCount(), 100);
|
||||||
|
EXPECT_FALSE(c.incrementCount(1));
|
||||||
|
EXPECT_EQ(c.getCount(), 100);
|
||||||
|
EXPECT_TRUE(c.incrementCount(-200));
|
||||||
|
EXPECT_EQ(c.getCount(), 0);
|
||||||
|
EXPECT_FALSE(c.incrementCount(-1));
|
||||||
|
EXPECT_EQ(c.getCount(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ServerCounter, CustomBoundsForCastCount)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Server_Counter c(1, "test", color(), 10, 0);
|
||||||
|
EXPECT_TRUE(c.isActive());
|
||||||
|
EXPECT_TRUE(c.setActive(false));
|
||||||
|
EXPECT_FALSE(c.isActive());
|
||||||
|
EXPECT_FALSE(c.setActive(false));
|
||||||
|
EXPECT_TRUE(c.setActive(true));
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,18 @@ TEST_F(SettingsDefaultsTest, Game_LocalGameRememberSettings_Default)
|
||||||
ASSERT_EQ(s.getLocalGameRememberSettings(), false);
|
ASSERT_EQ(s.getLocalGameRememberSettings(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SettingsDefaultsTest, Game_EnableCommandZone_Default)
|
||||||
|
{
|
||||||
|
GameSettings s(settingsPath, nullptr);
|
||||||
|
ASSERT_EQ(s.getEnableCommandZone(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SettingsDefaultsTest, Game_LocalGameEnableCommandZone_Default)
|
||||||
|
{
|
||||||
|
GameSettings s(settingsPath, nullptr);
|
||||||
|
ASSERT_EQ(s.getLocalGameEnableCommandZone(), false);
|
||||||
|
}
|
||||||
|
|
||||||
// --- UpdatesSettings ---
|
// --- UpdatesSettings ---
|
||||||
|
|
||||||
TEST_F(SettingsDefaultsTest, Updates_CheckUpdatesOnStartup_Default)
|
TEST_F(SettingsDefaultsTest, Updates_CheckUpdatesOnStartup_Default)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue