[Refactor] Move game logic into libcockatrice_game

This commit is contained in:
Lukas Brübach 2026-10-07 11:11:39 +02:00 • committed by GitHub
parent 710dfabea2
commit 7556f6465a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
91 changed files with 388 additions and 179 deletions

View file

@ -1,13 +0,0 @@
#ifndef TRANSLATION_H
#define TRANSLATION_H
enum GrammaticalCase
{
CaseNominative,
CaseLookAtZone,
CaseTopCardsOfZone,
CaseRevealZone,
CaseShuffleZone
};
#endif

View file

@ -1,62 +0,0 @@
#include "abstract_game.h"
#include "game_event_handler.h"
#include "game_meta_info.h"
#include "game_state.h"
#include "libcockatrice/protocol/pb/game_replay.pb.h"
#include "player/player_info.h"
#include "player/player_logic.h"
#include "player/player_manager.h"
#include "zones/card_zone_logic.h"
#include <QList>
#include <QMap>
class AbstractClient;
AbstractGame::AbstractGame(QObject *_parent) : QObject(_parent)
{
gameMetaInfo = new GameMetaInfo(this);
gameEventHandler = new GameEventHandler(this);
}
bool AbstractGame::isHost() const
{
return gameState->getHostId() == playerManager->getLocalPlayerId();
}
AbstractClient *AbstractGame::getClientForPlayer(int playerId) const
{
if (gameState->getClients().size() > 1) {
if (playerId == -1) {
playerId = playerManager->getActiveLocalPlayer(gameState->getActivePlayer())->getPlayerInfo()->getId();
}
return gameState->getClients().at(playerId);
} else if (gameState->getClients().isEmpty()) {
return nullptr;
} else {
return gameState->getClients().first();
}
}
void AbstractGame::loadReplay(const GameReplay *replay)
{
gameMetaInfo->setFromProto(replay->game_info());
gameMetaInfo->setSpectatorsOmniscient(true);
}
CardState *AbstractGame::getCard(int playerId, const QString &zoneName, int cardId) const
{
PlayerLogic *player = playerManager->getPlayer(playerId);
if (!player) {
return nullptr;
}
CardZoneLogic *zone = player->getZones().value(zoneName, 0);
if (!zone) {
return nullptr;
}
return zone->getCard(cardId);
}

View file

@ -1,63 +0,0 @@
/**
* @file abstract_game.h
* @ingroup GameLogic
*/
//! \todo Document this file.
#ifndef COCKATRICE_ABSTRACT_GAME_H
#define COCKATRICE_ABSTRACT_GAME_H
#include <QObject>
#include <QString>
#include <qtmetamacros.h>
class AbstractClient;
class CardState;
class GameEventHandler;
class GameMetaInfo;
class GameReplay;
class GameState;
class PlayerManager;
class CardState;
class AbstractGame : public QObject
{
Q_OBJECT
public:
explicit AbstractGame(QObject *parent);
GameMetaInfo *gameMetaInfo;
GameState *gameState;
GameEventHandler *gameEventHandler;
PlayerManager *playerManager;
GameMetaInfo *getGameMetaInfo()
{
return gameMetaInfo;
}
GameState *getGameState() const
{
return gameState;
}
GameEventHandler *getGameEventHandler() const
{
return gameEventHandler;
}
PlayerManager *getPlayerManager() const
{
return playerManager;
}
bool isHost() const;
AbstractClient *getClientForPlayer(int playerId) const;
void loadReplay(const GameReplay *replay);
CardState *getCard(int playerId, const QString &zoneName, int cardId) const;
};
#endif // COCKATRICE_ABSTRACT_GAME_H

View file

@ -1,24 +0,0 @@
#include "arrow_data.h"
#include "libcockatrice/protocol/pb/serverinfo_arrow.pb.h"
#include "libcockatrice/utility/color.h"
ArrowData ArrowData::fromProto(const ServerInfo_Arrow &arrow, int creatorId, bool isLocalCreator)
{
ArrowData data;
data.creatorId = creatorId;
data.isLocalCreator = isLocalCreator;
data.id = arrow.id();
data.startPlayerId = arrow.start_player_id();
data.startZone = QString::fromStdString(arrow.start_zone());
data.startCardId = arrow.start_card_id();
data.targetPlayerId = arrow.target_player_id();
data.color = convertColorToQColor(arrow.arrow_color());
if (arrow.has_target_zone()) {
data.targetZone = QString::fromStdString(arrow.target_zone());
data.targetCardId = arrow.target_card_id();
}
return data;
}

View file

@ -1,30 +0,0 @@
#ifndef COCKATRICE_ARROW_DATA_H
#define COCKATRICE_ARROW_DATA_H
#include <QColor>
#include <QString>
class ServerInfo_Arrow;
struct ArrowData
{
int creatorId = -1;
bool isLocalCreator = false;
int id = -1;
int startPlayerId = -1;
QString startZone = "";
int startCardId = -1;
int targetPlayerId = -1;
QString targetZone = "";
int targetCardId = -1;
QColor color = "";
static ArrowData fromProto(const ServerInfo_Arrow &arrow, int creatorId, bool isLocalCreator);
bool isPlayerTargeted() const
{
return targetZone.isEmpty();
}
};
#endif // COCKATRICE_ARROW_DATA_H

View file

@ -1,163 +0,0 @@
#include "card_list.h"
#include "card_state.h"
#include <QChar>
#include <QDebug>
#include <QMessageLogger>
#include <QSharedPointer>
#include <algorithm>
#include <compare>
#include <libcockatrice/card/card_info.h>
CardList::CardList(bool _contentsKnown) : QList<CardState *>(), contentsKnown(_contentsKnown)
{
}
/**
* @brief Finds the CardState with the given id in the list.
* If contentsKnown is false, then this just returns the first element of the list.
*
* @param cardId The id of the card to find.
*
* @returns A pointer to the CardState, or a nullptr if not found.
*/
CardState *CardList::findCard(const int cardId) const
{
if (!contentsKnown && !empty()) {
return at(0);
} else {
for (auto *cardState : *this) {
if (cardState->getId() == cardId) {
return cardState;
}
}
}
return nullptr;
}
/**
* @brief sorts the list by using string comparison on properties extracted from the CardState
* The cards are compared using each property in order.
* If two cards have the same value for a property, then the next property in the list is used.
*
* @param option the option to compare the cards by, in order of usage.
*/
void CardList::sortBy(const QList<SortOption> &option)
{
// early return if we know we won't be sorting
if (option.isEmpty()) {
return;
}
auto comparator = [&option](CardState *a, CardState *b) {
for (auto prop : option) {
auto extractor = getExtractorFor(prop);
QString t1 = extractor(a);
QString t2 = extractor(b);
if (t1 != t2) {
return t1 < t2;
}
}
return false;
};
std::sort(begin(), end(), comparator);
}
/**
* Creates a String for the card such that when sorting cards using that string, it will result in the
* following sort order:
* - Unrecognized colors
* - Land cards
* - Colorless cards
* - Monocolor cards, in wubrg order
* - Monocolor cards of any custom colors
* - 2C cards (no internal order)
* - 3C cards (no internal order)
* - 4C cards (no internal order)
* - 5C cards (no internal order)
*
* @param c The card info
* @param appendAtEnd For multicolor cards, whether to also append the entire color string at the end.
*/
static QString getColorSortString(const CardInfo &c, bool appendAtEnd)
{
QString colors = c.getColors();
switch (colors.size()) {
case 0: {
if (c.getCardType().contains("Land")) {
return "a_land";
} else {
return "b_colorless";
}
}
case 1:
// force wubrg order
switch (colors.at(0).toLatin1()) {
case 'W':
return "c_W";
case 'U':
return "d_U";
case 'B':
return "e_B";
case 'R':
return "f_R";
case 'G':
return "g_G";
default:
// handle any custom colors
return QString("h_%1").arg(colors.at(0));
}
default:
return QString("i%1_%2").arg(colors.size()).arg(appendAtEnd ? colors : "");
}
}
/**
* @brief returns the function that extracts the given property from the CardState.
*/
std::function<QString(CardState *)> CardList::getExtractorFor(SortOption option)
{
switch (option) {
case NoSort:
return [](CardState *) { return ""; };
case SortByMainType:
return [](CardState *c) { return c->getCardInfo().getMainCardType(); };
case SortByManaValue:
// getCmc returns the int as a string. We pad with 0's so that string comp also works on it
return [](CardState *c) { return c->getCard() ? c->getCardInfo().getCmc().rightJustified(4, '0') : ""; };
case SortByColorGrouping:
return [](CardState *c) { return c->getCard() ? getColorSortString(c->getCardInfo(), false) : ""; };
case SortByName:
return [](CardState *c) { return c->getName(); };
case SortByType:
return [](CardState *c) { return c->getCardInfo().getCardType(); };
case SortByManaCost:
return [](CardState *c) {
if (!c->getCard()) {
return QString();
}
auto info = c->getCardInfo();
// calculation copied from CardDatabaseModel.
// we pad the cmc and also append the mana cost to the end so same cmc cards still have a sort order
return QString("%1%2").arg(info.getCmc(), 4, QChar('0')).arg(info.getManaCost());
};
case SortByColors:
return [](CardState *c) { return c->getCard() ? getColorSortString(c->getCardInfo(), true) : ""; };
case SortByPt:
// do the same padding trick as above
return
[](CardState *c) { return c->getCard() ? c->getCardInfo().getPowTough().rightJustified(10, '0') : ""; };
case SortBySet:
return [](CardState *c) { return c->getCardInfo().getSetsNames(); };
case SortByPrinting:
return [](CardState *c) { return c->getProviderId(); };
}
// this line should never be reached
qCWarning(CardListLog) << "cardlist.cpp: Could not find extractor for SortOption" << option;
return [](CardState *) { return ""; };
}

View file

@ -1,57 +0,0 @@
/**
* @file card_list.h
* @ingroup GameLogicCards
*/
//! \todo Document this file.
#ifndef CARDLIST_H
#define CARDLIST_H
#include <QList>
#include <QLoggingCategory>
#include <QString>
#include <functional>
inline Q_LOGGING_CATEGORY(CardListLog, "card_list");
class CardState;
class CardList : public QList<CardState *>
{
protected:
bool contentsKnown;
public:
enum SortOption
{
NoSort,
// Options that are used by groupBy
// Should partition all cards into a reasonable number of buckets
SortByMainType,
SortByManaValue,
SortByColorGrouping,
// Options that are used by sortBy
// We don't care about buckets; we want as many distinct values as possible.
SortByName,
SortByType,
SortByManaCost,
SortByColors,
SortByPt,
SortBySet,
SortByPrinting
};
explicit CardList(bool _contentsKnown);
CardState *findCard(const int cardId) const;
bool getContentsKnown() const
{
return contentsKnown;
}
void sortBy(const QList<SortOption> &options);
static std::function<QString(CardState *)> getExtractorFor(SortOption option);
};
#endif

View file

@ -1,211 +0,0 @@
#include "card_state.h"
#include <QSharedPointer>
#include <libcockatrice/card/card_info.h>
#include <libcockatrice/card/database/card_database_manager.h>
void CardState::resetState(bool keepAnnotations)
{
attacking = false;
counters.clear();
pt.clear();
if (!keepAnnotations) {
annotation.clear();
}
attachedTo = nullptr;
attachedCards.clear();
setTapped(false, false);
setDoesntUntap(false);
emit stateReset();
}
void CardState::setColor(const QString &_color)
{
color = _color;
emit colorChanged();
}
void CardState::deleteView()
{
emit viewDeleteRequested();
}
void CardState::setCardRef(const CardRef &_cardRef)
{
if (cardRef == _cardRef) {
return;
}
const CardRef oldCardRef = cardRef;
if (exactCard) {
disconnect(exactCard.getCardPtr().data(), nullptr, this, nullptr);
}
cardRef = _cardRef;
emit cardRefChanged(oldCardRef, cardRef);
refreshCardInfo();
}
void CardState::refreshCardInfo()
{
exactCard = CardDatabaseManager::query()->getCard(cardRef);
if (!exactCard && !cardRef.name.isEmpty()) {
CardInfo::UiAttributes attributes = {.tableRow = -1};
auto info = CardInfo::newInstance(cardRef.name, "", true, {}, {}, {}, {}, attributes);
exactCard = ExactCard(info);
}
if (exactCard) {
connect(exactCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &CardState::cardPixmapUpdated);
}
emit cardInfoChanged();
}
void CardState::setTapped(bool _tapped, bool canAnimate)
{
if (tapped == _tapped) {
return;
}
tapped = _tapped;
emit tappedChanged(tapped, canAnimate);
emit stateChanged();
}
void CardState::setFaceDown(bool _facedown)
{
facedown = _facedown;
emit facedownChanged(facedown);
emit stateChanged();
}
QVariantList CardState::parsePT(const QString &pt)
{
QVariantList ptList = QVariantList();
if (!pt.isEmpty()) {
int sep = pt.indexOf('/');
if (sep == 0) {
ptList.append(QVariant(pt.mid(1))); // cut off starting '/' and take full string
} else {
int start = 0;
for (;;) {
QString item = pt.mid(start, sep - start);
if (item.isEmpty()) {
ptList.append(QVariant(QString()));
} else if (item[0] == '+') {
ptList.append(QVariant(item.mid(1).toInt())); // add as int
} else if (item[0] == '-') {
ptList.append(QVariant(item.toInt())); // add as int
} else {
ptList.append(QVariant(item)); // add as qstring
}
if (sep == -1) {
break;
}
start = sep + 1;
sep = pt.indexOf('/', start);
}
}
}
return ptList;
}
void CardState::setZone(CardZoneLogic *_zone)
{
if (zone == _zone) {
return;
}
zone = _zone;
emit zoneChanged(this, zone);
emit stateChanged();
}
void CardState::setAttacking(bool _attacking)
{
if (attacking == _attacking) {
return;
}
attacking = _attacking;
emit attackingChanged(_attacking);
emit stateChanged();
}
void CardState::insertCounter(int id, int value)
{
counters.insert(id, value);
emit countersChanged(counters);
emit stateChanged();
}
void CardState::setCounter(int id, int value)
{
if (value) {
counters[id] = value;
} else {
counters.remove(id);
}
emit countersChanged(counters);
emit stateChanged();
}
void CardState::clearCounters()
{
counters.clear();
emit countersChanged(counters);
emit stateChanged();
}
void CardState::setAnnotation(const QString &_annotation)
{
if (annotation == _annotation) {
return;
}
annotation = _annotation;
emit annotationChanged(annotation);
emit stateChanged();
}
void CardState::setPT(const QString &_pt)
{
if (pt == _pt) {
return;
}
pt = _pt;
emit ptChanged(pt);
emit stateChanged();
}
void CardState::setDoesntUntap(bool _doesntUntap)
{
if (doesntUntap == _doesntUntap) {
return;
}
doesntUntap = _doesntUntap;
emit doesntUntapChanged(_doesntUntap);
emit stateChanged();
}
void CardState::setDestroyOnZoneChange(bool _destroyOnZoneChange)
{
if (destroyOnZoneChange == _destroyOnZoneChange) {
return;
}
destroyOnZoneChange = _destroyOnZoneChange;
emit destroyOnZoneChangeChanged(_destroyOnZoneChange);
emit stateChanged();
}
void CardState::setAttachedTo(CardState *_attachedTo)
{
if (attachedTo == _attachedTo) {
return;
}
attachedTo = _attachedTo;
emit attachedToChanged(_attachedTo);
emit stateChanged();
}

View file

@ -1,243 +0,0 @@
#ifndef COCKATRICE_CARD_STATE_H
#define COCKATRICE_CARD_STATE_H
#include <QList>
#include <QMap>
#include <QObject>
#include <QPoint>
#include <QString>
#include <QVariant>
#include <libcockatrice/card/printing/exact_card.h>
#include <libcockatrice/utility/card_ref.h>
#include <qtmetamacros.h>
class CardZoneLogic;
class CardState : public QObject
{
Q_OBJECT
private:
int id = -1;
CardRef cardRef;
ExactCard exactCard;
bool tapped = false;
bool facedown = false;
QPoint gridPoint;
bool attacking = false;
QMap<int, int> counters;
QString annotation;
QString pt;
bool doesntUntap = false;
bool destroyOnZoneChange = false;
QString color;
CardState *attachedTo = nullptr;
QList<CardState *> attachedCards;
CardZoneLogic *zone = nullptr;
signals:
void stateChanged();
void cardRefChanged(const CardRef &oldCardRef, const CardRef &newCardRef);
void cardInfoChanged();
void cardPixmapUpdated();
void tappedChanged(bool newTapped, bool canAnimate);
void facedownChanged(bool newFaceDown);
void stateReset();
void colorChanged();
void attackingChanged(bool newValue);
void countersChanged(const QMap<int, int> &newCounters);
void annotationChanged(const QString &newAnnotation);
void ptChanged(const QString &newPt);
void doesntUntapChanged(bool newValue);
void destroyOnZoneChangeChanged(bool newValue);
void attachedToChanged(CardState *newAttachedTo);
void zoneChanged(CardState *changedCard, CardZoneLogic *newZone);
void viewDeleteRequested();
public:
explicit CardState(QObject *parent, CardZoneLogic *_zone = nullptr, const CardRef &_cardRef = {}, int _id = -1)
: QObject(parent), id(_id), cardRef(_cardRef), zone(_zone)
{
}
void resetState(bool keepAnnotations);
/**
* @brief Asks the card view owning this state to tear itself down.
*
* The state never holds a card item directly, so it cannot delete one. It asks instead and lets the view run
* its own teardown, which detaches attachments, drops it from the active card slot and unregisters its
* animations before the graphics object goes away.
*/
void deleteView();
int getId() const
{
return id;
}
void setId(int _id)
{
id = _id;
}
const CardRef &getCardRef() const
{
return cardRef;
}
void setCardRef(const CardRef &_cardRef);
QString getName() const
{
return cardRef.name;
}
QString getProviderId() const
{
return cardRef.providerId;
}
ExactCard getCard() const
{
return exactCard;
}
const CardInfo &getCardInfo() const
{
return exactCard.getInfo();
}
void refreshCardInfo();
bool getTapped() const
{
return tapped;
}
void setTapped(bool _tapped, bool canAnimate = false);
bool getFaceDown() const
{
return facedown;
}
void setFaceDown(bool _facedown);
const QPoint &getGridPoint() const
{
return gridPoint;
}
void setGridPoint(const QPoint &_gridPoint)
{
gridPoint = _gridPoint;
}
CardZoneLogic *getZone() const
{
return zone;
}
void setZone(CardZoneLogic *_zone);
bool getAttacking() const
{
return attacking;
}
void setAttacking(bool _attacking);
const QMap<int, int> &getCounters() const
{
return counters;
}
void insertCounter(int id, int value);
void setCounter(int id, int value);
void clearCounters();
QString getAnnotation() const
{
return annotation;
}
void setAnnotation(const QString &_annotation);
QString getPT() const
{
return pt;
}
void setPT(const QString &_pt);
/**
* @brief Parses a string representing a p/t in order to extract the values from it.
*
* If the string contains '/', the string will be split at the '/' and each side will be parsed separately,
* which means the result list will have two elements.
*
* If '/' is not found, then the entire string is parsed together, which means the result list will
* have a single element.
*
* If either side of the split is empty, there will also only be a single element in the result list.
*
* This function will attempt to parse each substring as an int first, handling plus and minus prefixes.
* If successful, it will put the parsed value into the QVariant as an int.
* If failed, it will just put the substring into the QVariant as a QString.
*
* @param pt The p/t string
* @return A QVariantList that can contain one or two elements, where each QVariant can be either int or QString
*/
static QVariantList parsePT(const QString &pt);
[[nodiscard]] QString getColor() const
{
return color;
}
void setColor(const QString &_color);
bool getDoesntUntap() const
{
return doesntUntap;
}
void setDoesntUntap(bool _doesntUntap);
bool getDestroyOnZoneChange() const
{
return destroyOnZoneChange;
}
void setDestroyOnZoneChange(bool _destroyOnZoneChange);
CardState *getAttachedTo() const
{
return attachedTo;
}
void setAttachedTo(CardState *_attachedTo);
void addAttachedCard(CardState *card)
{
attachedCards.append(card);
}
void removeAttachedCard(CardState *card)
{
attachedCards.removeOne(card);
}
const QList<CardState *> &getAttachedCards() const
{
return attachedCards;
}
};
#endif // COCKATRICE_CARD_STATE_H

View file

@ -1,26 +0,0 @@
#include "counter_state.h"
#include "libcockatrice/protocol/pb/serverinfo_counter.pb.h"
#include <libcockatrice/utility/color.h>
CounterState::CounterState(int id, const QString &name, const QColor &color, int radius, int value, QObject *parent)
: QObject(parent), id(id), name(name), color(color), radius(radius), value(value)
{
}
CounterState *CounterState::fromProto(const ServerInfo_Counter &counter, QObject *parent)
{
return new CounterState(counter.id(), QString::fromStdString(counter.name()),
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count(), parent);
}
void CounterState::setValue(int newValue, bool skipDamageAnimation)
{
if (newValue == value) {
return;
}
int old = value;
value = newValue;
emit valueChanged(old, newValue, skipDamageAnimation);
}

View file

@ -1,66 +0,0 @@
#ifndef COCKATRICE_COUNTER_STATE_H
#define COCKATRICE_COUNTER_STATE_H
#include <QColor>
#include <QObject>
#include <QString>
#include <qtmetamacros.h>
class ServerInfo_Counter;
class CounterState : public QObject
{
Q_OBJECT
public:
CounterState(int id, const QString &name, const QColor &color, int radius, int value, QObject *parent = nullptr);
static CounterState *fromProto(const ServerInfo_Counter &counter, QObject *parent = nullptr);
int getId() const
{
return id;
}
QString getName() const
{
return name;
}
QColor getColor() const
{
return color;
}
int getRadius() const
{
return radius;
}
int getValue() const
{
return value;
}
/**
* @brief Set the counter value.
* @param newValue The new value.
* @param skipDamageAnimation When true, valueChanged is emitted with skipDamageAnimation=true, letting views
* suppress damage-related feedback (e.g. battlefield shimmer, life counter flash) for values set during replay
* rewinds.
*/
void setValue(int newValue, bool skipDamageAnimation = false);
signals:
/**
* @brief Emitted whenever the value changes.
* @param oldValue The previous value.
* @param newValue The new value.
* @param skipDamageAnimation True when the change should not trigger damage/life-change feedback in views.
*/
void valueChanged(int oldValue, int newValue, bool skipDamageAnimation);
private:
int id;
QString name;
QColor color;
int radius;
int value;
};
#endif // COCKATRICE_COUNTER_STATE_H

View file

@ -1,28 +0,0 @@
#include "game.h"
#include "game_meta_info.h"
#include "game_state.h"
#include "player/player_manager.h"
#include <QList>
#include <QMap>
#include <QString>
#include <libcockatrice/protocol/pb/event_game_joined.pb.h>
class AbstractClient;
class QObject;
Game::Game(QObject *_parent,
bool isLocalGame,
QList<AbstractClient *> &_clients,
const Event_GameJoined &event,
const QMap<int, QString> &_roomGameTypes)
: AbstractGame(_parent)
{
gameMetaInfo->setFromProto(event.game_info());
gameMetaInfo->setRoomGameTypes(_roomGameTypes);
gameState = new GameState(this, 0, event.host_id(), isLocalGame, _clients, false, event.resuming(), -1, false);
connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged);
playerManager = new PlayerManager(this, event.player_id(), event.judge(), event.spectator());
gameMetaInfo->setStarted(false);
}

View file

@ -1,33 +0,0 @@
/**
* @file game.h
* @ingroup GameLogic
*/
//! \todo Document this file.
#ifndef COCKATRICE_GAME_H
#define COCKATRICE_GAME_H
#include "abstract_game.h"
#include <qtmetamacros.h>
class AbstractClient;
class Event_GameJoined;
class QObject;
class QString;
template <class Key, class T> class QMap;
template <typename T> class QList;
class Game : public AbstractGame
{
Q_OBJECT
public:
Game(QObject *parent,
bool isLocalGame,
QList<AbstractClient *> &_clients,
const Event_GameJoined &event,
const QMap<int, QString> &_roomGameTypes);
};
#endif // COCKATRICE_GAME_H

View file

@ -1,669 +0,0 @@
#include "game_event_handler.h"
#include "abstract_game.h"
#include "game_meta_info.h"
#include "game_state.h"
#include "libcockatrice/protocol/pb/commands.pb.h"
#include "libcockatrice/protocol/pb/game_commands.pb.h"
#include "libcockatrice/protocol/pb/game_event.pb.h"
#include "libcockatrice/protocol/pb/game_event_context.pb.h"
#include "libcockatrice/protocol/pb/response.pb.h"
#include "libcockatrice/protocol/pb/serverinfo_game.pb.h"
#include "libcockatrice/protocol/pb/serverinfo_player.pb.h"
#include "libcockatrice/protocol/pb/serverinfo_playerproperties.pb.h"
#include "libcockatrice/protocol/pb/serverinfo_user.pb.h"
#include "player/player_event_handler.h"
#include "player/player_info.h"
#include "player/player_logic.h"
#include "player/player_manager.h"
#include <QList>
#include <QMap>
#include <QMapIterator>
#include <QMessageLogger>
#include <QSharedPointer>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include <google/protobuf/stubs/port.h>
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/get_pb_extension.h>
#include <libcockatrice/protocol/pb/command_concede.pb.h>
#include <libcockatrice/protocol/pb/command_deck_select.pb.h>
#include <libcockatrice/protocol/pb/command_delete_arrow.pb.h>
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
#include <libcockatrice/protocol/pb/command_game_say.pb.h>
#include <libcockatrice/protocol/pb/command_leave_game.pb.h>
#include <libcockatrice/protocol/pb/command_next_turn.pb.h>
#include <libcockatrice/protocol/pb/command_ready_start.pb.h>
#include <libcockatrice/protocol/pb/command_reverse_turn.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_playmat.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/context_connection_state_changed.pb.h>
#include <libcockatrice/protocol/pb/context_deck_select.pb.h>
#include <libcockatrice/protocol/pb/event_game_closed.pb.h>
#include <libcockatrice/protocol/pb/event_game_host_changed.pb.h>
#include <libcockatrice/protocol/pb/event_game_say.pb.h>
#include <libcockatrice/protocol/pb/event_game_state_changed.pb.h>
#include <libcockatrice/protocol/pb/event_join.pb.h>
#include <libcockatrice/protocol/pb/event_kicked.pb.h>
#include <libcockatrice/protocol/pb/event_leave.pb.h>
#include <libcockatrice/protocol/pb/event_player_properties_changed.pb.h>
#include <libcockatrice/protocol/pb/event_reverse_turn.pb.h>
#include <libcockatrice/protocol/pb/event_set_active_phase.pb.h>
#include <libcockatrice/protocol/pb/event_set_active_player.pb.h>
#include <libcockatrice/protocol/pb/game_event_container.pb.h>
#include <libcockatrice/protocol/pending_command.h>
#include <libcockatrice/utility/zone_names.h>
#include <string>
#include <utility>
GameEventHandler::GameEventHandler(AbstractGame *_game) : QObject(_game), game(_game)
{
}
void GameEventHandler::sendGameCommand(PendingCommand *pend, int playerId)
{
AbstractClient *client = game->getClientForPlayer(playerId);
if (!client) {
return;
}
connect(pend, &PendingCommand::finished, this, &GameEventHandler::commandFinished);
client->sendCommand(pend);
}
void GameEventHandler::sendGameCommand(const google::protobuf::Message &command, int playerId)
{
AbstractClient *client = game->getClientForPlayer(playerId);
if (!client) {
return;
}
PendingCommand *pend = prepareGameCommand(command);
connect(pend, &PendingCommand::finished, this, &GameEventHandler::commandFinished);
client->sendCommand(pend);
}
void GameEventHandler::commandFinished(const Response &response)
{
if (response.response_code() == Response::RespChatFlood) {
emit gameFlooded();
}
}
PendingCommand *GameEventHandler::prepareGameCommand(const ::google::protobuf::Message &cmd)
{
CommandContainer cont;
cont.set_game_id(static_cast<google::protobuf::uint32>(game->getGameMetaInfo()->gameId()));
GameCommand *c = cont.add_game_command();
c->GetReflection()->MutableMessage(c, cmd.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(cmd);
return new PendingCommand(cont);
}
PendingCommand *GameEventHandler::prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList)
{
CommandContainer cont;
cont.set_game_id(static_cast<google::protobuf::uint32>(game->getGameMetaInfo()->gameId()));
for (auto i : cmdList) {
GameCommand *c = cont.add_game_command();
c->GetReflection()->MutableMessage(c, i->GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(*i);
delete i;
}
return new PendingCommand(cont);
}
void GameEventHandler::processGameEventContainer(const GameEventContainer &cont,
AbstractClient *client,
EventProcessingOptions options)
{
const GameEventContext &context = cont.context();
emit containerProcessingStarted(context);
const int eventListSize = cont.event_list_size();
for (int i = 0; i < eventListSize; ++i) {
const GameEvent &event = cont.event_list(i);
const int playerId = event.player_id();
const auto eventType = static_cast<GameEvent::GameEventType>(getPbExtension(event));
if (cont.has_forced_by_judge()) {
auto id = cont.forced_by_judge();
PlayerLogic *judgep = game->getPlayerManager()->getPlayers().value(id, nullptr);
if (judgep) {
emit setContextJudgeName(judgep->getPlayerInfo()->getName());
} else if (game->getPlayerManager()->getSpectators().contains(id)) {
emit setContextJudgeName(
QString::fromStdString(game->getPlayerManager()->getSpectators().value(id).name()));
}
}
if (game->getPlayerManager()->getSpectators().contains(playerId)) {
switch (eventType) {
case GameEvent::GAME_SAY:
eventSpectatorSay(event.GetExtension(Event_GameSay::ext), playerId, context);
break;
case GameEvent::LEAVE:
eventSpectatorLeave(event.GetExtension(Event_Leave::ext), playerId, context);
break;
default:
break;
}
} else {
if ((game->getGameState()->getClients().size() > 1) && (playerId != -1)) {
if (game->getGameState()->getClients().at(playerId) != client) {
continue;
}
}
switch (eventType) {
case GameEvent::GAME_STATE_CHANGED:
eventGameStateChanged(event.GetExtension(Event_GameStateChanged::ext), playerId, context);
break;
case GameEvent::PLAYER_PROPERTIES_CHANGED:
eventPlayerPropertiesChanged(event.GetExtension(Event_PlayerPropertiesChanged::ext), playerId,
context);
break;
case GameEvent::JOIN:
eventJoin(event.GetExtension(Event_Join::ext), playerId, context);
break;
case GameEvent::LEAVE:
eventLeave(event.GetExtension(Event_Leave::ext), playerId, context);
break;
case GameEvent::KICKED:
eventKicked(event.GetExtension(Event_Kicked::ext), playerId, context);
break;
case GameEvent::GAME_HOST_CHANGED:
eventGameHostChanged(event.GetExtension(Event_GameHostChanged::ext), playerId, context);
break;
case GameEvent::GAME_CLOSED:
eventGameClosed(event.GetExtension(Event_GameClosed::ext), playerId, context);
break;
case GameEvent::SET_ACTIVE_PLAYER:
eventSetActivePlayer(event.GetExtension(Event_SetActivePlayer::ext), playerId, context);
break;
case GameEvent::SET_ACTIVE_PHASE:
eventSetActivePhase(event.GetExtension(Event_SetActivePhase::ext), playerId, context);
break;
case GameEvent::REVERSE_TURN:
eventReverseTurn(event.GetExtension(Event_ReverseTurn::ext), playerId, context);
break;
default: {
PlayerLogic *player = game->getPlayerManager()->getPlayers().value(playerId, 0);
if (!player) {
qCWarning(GameEventHandlerLog) << "unhandled game event: invalid player id";
break;
}
player->getPlayerEventHandler()->processGameEvent(eventType, event, context, options);
emitUserEvent();
}
}
}
}
emit containerProcessingDone();
}
void GameEventHandler::handleNextTurn()
{
sendGameCommand(Command_NextTurn());
}
void GameEventHandler::handleReverseTurn()
{
sendGameCommand(Command_ReverseTurn());
}
void GameEventHandler::handleActiveLocalPlayerConceded()
{
sendGameCommand(Command_Concede());
}
void GameEventHandler::handleActiveLocalPlayerUnconceded()
{
sendGameCommand(Command_Unconcede());
}
void GameEventHandler::handleActivePhaseChanged(int phase)
{
Command_SetActivePhase cmd;
cmd.set_phase(static_cast<google::protobuf::uint32>(phase));
sendGameCommand(cmd);
}
void GameEventHandler::handleUntapAll()
{
Command_SetCardAttr cmd;
cmd.set_zone(ZoneNames::TABLE);
cmd.set_attribute(AttrTapped);
cmd.set_attr_value("0");
sendGameCommand(cmd);
}
void GameEventHandler::handleDrawCard()
{
Command_DrawCards cmd;
cmd.set_number(1);
sendGameCommand(cmd);
}
PendingCommand *GameEventHandler::prepareDeckSelect(const QString &deckList)
{
Command_DeckSelect cmd;
cmd.set_deck(deckList.toStdString());
return prepareGameCommand(cmd);
}
PendingCommand *GameEventHandler::prepareDeckSelectId(int deckId)
{
Command_DeckSelect cmd;
cmd.set_deck_id(deckId);
return prepareGameCommand(cmd);
}
void GameEventHandler::sendSetPlaymat(const PlaymatInfo &playmat, int playerId)
{
Command_SetPlaymat cmd;
auto *pp = cmd.mutable_playmat_params();
pp->set_card_name(playmat.card.name.toStdString());
pp->set_card_provider_id(playmat.card.providerId.toStdString());
pp->set_margin_pct_l(playmat.params.marginPctL);
pp->set_margin_pct_r(playmat.params.marginPctR);
pp->set_vertical_offset(playmat.params.verticalOffset);
pp->set_zoom(playmat.params.zoom);
sendGameCommand(prepareGameCommand(cmd), playerId);
}
void GameEventHandler::sendReadyStart(bool ready, int playerId)
{
Command_ReadyStart cmd;
cmd.set_ready(ready);
sendGameCommand(cmd, playerId);
}
void GameEventHandler::sendForceStart(int playerId)
{
Command_ReadyStart cmd;
cmd.set_force_start(true);
cmd.set_ready(true);
sendGameCommand(cmd, playerId);
}
void GameEventHandler::sendSetSideboardLock(bool locked, int playerId)
{
Command_SetSideboardLock cmd;
cmd.set_locked(locked);
sendGameCommand(cmd, playerId);
}
void GameEventHandler::sendSetSideboardPlan(const QList<MoveCard_ToZone> &plan, int playerId)
{
Command_SetSideboardPlan cmd;
for (const auto &zone : plan) {
cmd.add_move_list()->CopyFrom(zone);
}
sendGameCommand(cmd, playerId);
}
void GameEventHandler::handleGameLeft()
{
sendGameCommand(Command_LeaveGame());
}
void GameEventHandler::handleChatMessageSent(const QString &chatMessage)
{
Command_GameSay cmd;
cmd.set_message(chatMessage.toStdString());
sendGameCommand(cmd);
}
void GameEventHandler::handleArrowDeletion(int creatorId, int arrowId)
{
Command_DeleteArrow cmd;
cmd.set_arrow_id(arrowId);
auto preparedCommand = prepareGameCommand(cmd);
connect(preparedCommand, &PendingCommand::finished, this, [creatorId, arrowId, this](const Response &response) {
handleArrowDeletionFinished(response, creatorId, arrowId);
});
sendGameCommand(preparedCommand);
}
void GameEventHandler::handleArrowDeletionFinished(const Response &response, int creatorId, int arrowId)
{
// The server confirms the arrow no longer exists whether it deleted it itself
// (RespOk, followed by an Event_DeleteArrow broadcast) or never had it
// (RespNameNotFound). In both cases the local copy has to go. deleteArrow is
// a no-op if the arrow was already removed by the event broadcast.
if (response.response_code() == Response::RespOk || response.response_code() == Response::RespNameNotFound) {
emit arrowDeleted(creatorId, arrowId);
}
}
void GameEventHandler::eventSpectatorSay(const Event_GameSay &event,
int eventPlayerId,
const GameEventContext & /*context*/)
{
const ServerInfo_User &userInfo = game->getPlayerManager()->getSpectators().value(eventPlayerId);
emit logSpectatorSay(userInfo, QString::fromStdString(event.message()));
}
void GameEventHandler::eventSpectatorLeave(const Event_Leave &event,
int eventPlayerId,
const GameEventContext & /*context*/)
{
emit logSpectatorLeave(game->getPlayerManager()->getSpectatorName(eventPlayerId), getLeaveReason(event.reason()));
emit spectatorLeft(eventPlayerId);
game->getPlayerManager()->removeSpectator(eventPlayerId);
}
void GameEventHandler::eventGameStateChanged(const Event_GameStateChanged &event,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
const int playerListSize = event.player_list_size();
QVector<QPair<int, QPair<QString, QString>>> opponentDecksToDisplay;
for (int i = 0; i < playerListSize; ++i) {
const ServerInfo_Player &playerInfo = event.player_list(i);
const ServerInfo_PlayerProperties &prop = playerInfo.properties();
const int playerId = prop.player_id();
QString playerName = QString::fromStdString(prop.user_info().name());
emit addPlayerToAutoCompleteList("@" + playerName);
if (prop.spectator()) {
if (!game->getPlayerManager()->getSpectators().contains(playerId)) {
game->getPlayerManager()->addSpectator(playerId, prop);
emit spectatorJoined(prop);
}
} else {
PlayerLogic *player = game->getPlayerManager()->getPlayers().value(playerId, 0);
if (!player) {
player = game->getPlayerManager()->addPlayer(playerId, prop.user_info());
emit playerJoined(prop);
}
player->processPlayerInfo(playerInfo);
// Extract playmat from player properties for opponent display
if (prop.has_playmat_params()) {
player->setPlaymatFromProperties(prop);
}
if (player->getPlayerInfo()->getLocal()) {
emit localPlayerDeckSelected(player, playerId, playerInfo);
} else {
if (!game->getGameMetaInfo()->proto().share_decklists_on_load()) {
continue;
}
if (!playerInfo.has_deck_list()) {
continue;
}
opponentDecksToDisplay.append(
qMakePair(playerId, qMakePair(playerName, QString::fromStdString(playerInfo.deck_list()))));
}
}
}
processCardAttachmentsForPlayers(event);
emit remotePlayersDecksSelected(opponentDecksToDisplay);
game->getGameState()->setGameTime(event.seconds_elapsed());
if (event.game_started() && !game->getGameMetaInfo()->started()) {
game->getGameState()->setResuming(!game->getGameState()->isGameStateKnown());
game->getGameMetaInfo()->setStarted(event.game_started());
if (game->getGameState()->isGameStateKnown()) {
emit logGameStart();
}
game->getGameState()->setActivePlayer(event.active_player_id());
game->getGameState()->setCurrentPhase(event.active_phase());
} else if (!event.game_started() && game->getGameMetaInfo()->started()) {
game->getGameState()->setCurrentPhase(-1);
game->getGameState()->setActivePlayer(-1);
game->getGameMetaInfo()->setStarted(false);
emit gameStopped();
}
game->getGameState()->setGameStateKnown(true);
emitUserEvent();
}
void GameEventHandler::processCardAttachmentsForPlayers(const Event_GameStateChanged &event)
{
for (int i = 0; i < event.player_list_size(); ++i) {
const ServerInfo_Player &playerInfo = event.player_list(i);
const ServerInfo_PlayerProperties &prop = playerInfo.properties();
if (!prop.spectator()) {
PlayerLogic *player = game->getPlayerManager()->getPlayers().value(prop.player_id(), 0);
if (!player) {
continue;
}
player->processCardAttachment(playerInfo);
}
}
}
void GameEventHandler::eventPlayerPropertiesChanged(const Event_PlayerPropertiesChanged &event,
int eventPlayerId,
const GameEventContext &context)
{
PlayerLogic *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0);
if (!player) {
return;
}
const ServerInfo_PlayerProperties &prop = event.player_properties();
emit playerPropertiesChanged(prop, eventPlayerId);
// Update playmat from player properties
if (prop.has_playmat_params()) {
player->setPlaymatFromProperties(prop);
}
const auto contextType = static_cast<GameEventContext::ContextType>(getPbExtension(context));
switch (contextType) {
case GameEventContext::READY_START: {
bool ready = prop.ready_start();
if (player->getPlayerInfo()->getLocal()) {
emit localPlayerReadyStateChanged(player->getPlayerInfo()->getId(), ready);
}
if (ready) {
emit logReadyStart(player);
} else {
emit logNotReadyStart(player);
}
break;
}
case GameEventContext::CONCEDE: {
player->setConceded(true);
QMapIterator<int, PlayerLogic *> playerIterator(game->getPlayerManager()->getPlayers());
while (playerIterator.hasNext()) {
playerIterator.next().value()->updateZones();
}
emit logConcede(eventPlayerId);
break;
}
case GameEventContext::UNCONCEDE: {
player->setConceded(false);
QMapIterator<int, PlayerLogic *> playerIterator(game->getPlayerManager()->getPlayers());
while (playerIterator.hasNext()) {
playerIterator.next().value()->updateZones();
}
emit logUnconcede(eventPlayerId);
break;
}
case GameEventContext::DECK_SELECT: {
Context_DeckSelect deckSelect = context.GetExtension(Context_DeckSelect::ext);
emit logDeckSelect(player, QString::fromStdString(deckSelect.deck_hash()), deckSelect.sideboard_size());
if (game->getGameMetaInfo()->proto().share_decklists_on_load() && deckSelect.has_deck_list() &&
eventPlayerId != game->getPlayerManager()->getLocalPlayerId()) {
emit remotePlayerDeckSelected(QString::fromStdString(deckSelect.deck_list()), eventPlayerId,
player->getPlayerInfo()->getName());
}
break;
}
case GameEventContext::SET_SIDEBOARD_LOCK: {
if (player->getPlayerInfo()->getLocal()) {
emit localPlayerSideboardLocked(player->getPlayerInfo()->getId(), prop.sideboard_locked());
}
emit logSideboardLockSet(player, prop.sideboard_locked());
break;
}
case GameEventContext::CONNECTION_STATE_CHANGED: {
emit logConnectionStateChanged(player, prop.ping_seconds() != -1);
break;
}
default:;
}
}
void GameEventHandler::eventJoin(const Event_Join &event, int /*eventPlayerId*/, const GameEventContext & /*context*/)
{
const ServerInfo_PlayerProperties &playerInfo = event.player_properties();
const int playerId = playerInfo.player_id();
QString playerName = QString::fromStdString(playerInfo.user_info().name());
emit addPlayerToAutoCompleteList(playerName);
PlayerManager *playerManager = game->getPlayerManager();
if (playerManager->getPlayers().contains(playerId) || playerManager->getSpectators().contains(playerId)) {
return;
}
if (playerInfo.spectator()) {
playerManager->addSpectator(playerId, playerInfo);
emit logJoinSpectator(playerName);
emit spectatorJoined(playerInfo);
} else {
PlayerLogic *newPlayer = game->getPlayerManager()->addPlayer(playerId, playerInfo.user_info());
emit logJoinPlayer(newPlayer);
emit playerJoined(playerInfo);
emitUserEvent();
}
}
QString GameEventHandler::getLeaveReason(Event_Leave::LeaveReason reason)
{
switch (reason) {
case Event_Leave::USER_KICKED:
return tr("kicked by game host or moderator");
break;
case Event_Leave::USER_LEFT:
return tr("player left the game");
break;
case Event_Leave::USER_DISCONNECTED:
return tr("player disconnected from server");
break;
case Event_Leave::OTHER:
default:
return tr("reason unknown");
break;
}
}
void GameEventHandler::eventLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext & /*context*/)
{
PlayerLogic *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0);
if (!player) {
return;
}
player->clear();
emit playerLeft(eventPlayerId);
emit logLeave(player, getLeaveReason(event.reason()));
game->getPlayerManager()->removePlayer(eventPlayerId);
player->deleteLater();
// Rearrange all remaining zones so that attachment relationship updates take place
QMapIterator<int, PlayerLogic *> playerIterator(game->getPlayerManager()->getPlayers());
while (playerIterator.hasNext()) {
playerIterator.next().value()->updateZones();
}
emitUserEvent();
}
void GameEventHandler::eventKicked(const Event_Kicked & /*event*/,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
emit gameClosed();
emit logKicked();
emit playerKicked();
emitUserEvent();
}
void GameEventHandler::eventReverseTurn(const Event_ReverseTurn &event,
int eventPlayerId,
const GameEventContext & /*context*/)
{
PlayerLogic *player = game->getPlayerManager()->getPlayers().value(eventPlayerId, 0);
if (!player) {
return;
}
emit logTurnReversed(player, event.reversed());
}
void GameEventHandler::eventGameHostChanged(const Event_GameHostChanged & /*event*/,
int eventPlayerId,
const GameEventContext & /*context*/)
{
game->getGameState()->setHostId(eventPlayerId);
}
void GameEventHandler::eventGameClosed(const Event_GameClosed & /*event*/,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
game->getGameMetaInfo()->setStarted(false);
game->getGameState()->setGameClosed(true);
emit gameClosed();
emit logGameClosed();
emitUserEvent();
}
void GameEventHandler::eventSetActivePlayer(const Event_SetActivePlayer &event,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
game->getGameState()->setActivePlayer(event.active_player_id());
PlayerLogic *player = game->getPlayerManager()->getPlayer(event.active_player_id());
if (!player) {
return;
}
emit logActivePlayer(player);
emitUserEvent();
}
void GameEventHandler::eventSetActivePhase(const Event_SetActivePhase &event,
int /*eventPlayerId*/,
const GameEventContext & /*context*/)
{
const int phase = event.phase();
if (game->getGameState()->getCurrentPhase() != phase) {
emit logActivePhaseChanged(phase);
}
game->getGameState()->setCurrentPhase(phase);
emitUserEvent();
}

View file

@ -1,453 +0,0 @@
/**
* @file game_event_handler.h
* @ingroup GameLogic
* @brief Game-level command sender and event dispatcher.
*
* GameEventHandler sends commands initiated by the local client to the server
* and processes incoming game-wide events. It bridges the networking layer
* (protobuf events received via AbstractClient) with the game model and UI
* (GameState, PlayerManager, logging, widgets).
*
* Player-scoped events are forwarded to PlayerEventHandler instances, while
* spectator and global game events are handled directly here.
*/
#ifndef COCKATRICE_GAME_EVENT_HANDLER_H
#define COCKATRICE_GAME_EVENT_HANDLER_H
#include "player/event_processing_options.h"
#include <QLoggingCategory>
#include <QObject>
#include <QPair>
#include <QString>
#include <QVector>
#include <libcockatrice/protocol/pb/event_leave.pb.h>
#include <qtmetamacros.h>
// IWYU pragma: keep
// GameEventContext, ServerInfo_Player and ServerInfo_User are passed by value
// to signals and slots, so the moc-generated code needs the complete types.
#include <libcockatrice/protocol/pb/game_event_context.pb.h>
#include <libcockatrice/protocol/pb/move_card_to_zone.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_player.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <libcockatrice/utility/playmat_params.h>
class AbstractClient;
class AbstractGame;
class GameEventContainer;
class GameEventContext;
class PendingCommand;
class PlayerLogic;
class Response;
class Event_GameStateChanged;
class Event_PlayerPropertiesChanged;
class Event_Join;
class Event_GameHostChanged;
class Event_GameClosed;
class Event_SetActivePlayer;
class Event_SetActivePhase;
class Event_GameSay;
class Event_Kicked;
class Event_ReverseTurn;
class Event_Ping;
class ServerInfo_PlayerProperties;
namespace google
{
namespace protobuf
{
class Message;
} // namespace protobuf
} // namespace google
template <typename T> class QList;
inline Q_LOGGING_CATEGORY(GameEventHandlerLog, "game_event_handler");
/**
* @class GameEventHandler
* @brief Central dispatcher for game-wide commands and events.
*
* This class owns no game state itself. Instead, it:
* - Sends commands to the server on behalf of local players
* - Receives and dispatches server-side game events
* - Updates the game model indirectly via Player, GameState, and PlayerManager
* - Emits high-level signals for UI updates and logging
*/
class GameEventHandler : public QObject
{
Q_OBJECT
private:
/** Pointer to the owning game instance. */
AbstractGame *game;
public:
/** @name Construction
* Lifecycle and ownership.
* @{
*/
/**
* @brief Construct a GameEventHandler.
*
* The handler is owned by the AbstractGame instance and uses it to
* access the game state, players, and network clients.
*
* @param _game Owning game instance (also used as QObject parent).
*/
explicit GameEventHandler(AbstractGame *_game);
/** @} */
/** @name Outgoing game commands
* Commands initiated locally and sent to the server.
*
* These methods construct and send protobuf commands corresponding
* to user actions in the UI.
* @{
*/
/** @brief Request advancing the game to the next turn. */
void handleNextTurn();
/** @brief Request reversing the current turn order. */
void handleReverseTurn();
/** @brief Concede the game for the currently active local player. */
void handleActiveLocalPlayerConceded();
/** @brief Undo a previous concede for the active local player. */
void handleActiveLocalPlayerUnconceded();
/**
* @brief Set the active phase of the game.
*
* Typically triggered by the active player selecting a new phase.
*
* @param phase Phase identifier.
*/
void handleActivePhaseChanged(int phase);
/** @brief Request untapping every permanent on the table. */
void handleUntapAll();
/** @brief Request drawing a single card. */
void handleDrawCard();
/** @brief Leave the current game session. */
void handleGameLeft();
/**
* @brief Send a chat message to all players and spectators.
*
* @param chatMessage Message text.
*/
void handleChatMessageSent(const QString &chatMessage);
/**
* @brief Delete an existing arrow.
*
* @param arrowId Unique identifier of the arrow to delete.
*/
void handleArrowDeletion(int creatorId, int arrowId);
void handleArrowDeletionFinished(const Response &response, int creatorId, int arrowId);
/**
* @brief Prepare a deck selection from its native deck list text.
*
* The returned command is not sent yet, so the caller can connect to
* PendingCommand::finished before handing it to sendGameCommand().
*
* @param deckList Serialized deck list.
* @return The pending command awaiting the deck download response.
*/
PendingCommand *prepareDeckSelect(const QString &deckList);
/**
* @brief Prepare a deck selection for a deck stored on the server.
*
* The returned command is not sent yet, so the caller can connect to
* PendingCommand::finished before handing it to sendGameCommand().
*
* @param deckId Server-side deck identifier.
* @return The pending command awaiting the deck download response.
*/
PendingCommand *prepareDeckSelectId(int deckId);
/**
* @brief Send a resolved playmat for a player.
*
* @param playmat Resolved playmat card and positioning parameters.
* @param playerId Player the playmat belongs to.
*/
void sendSetPlaymat(const PlaymatInfo &playmat, int playerId);
/**
* @brief Set a player's ready state.
*
* @param ready Whether the player is ready.
* @param playerId Player the state applies to.
*/
void sendReadyStart(bool ready, int playerId);
/**
* @brief Force-start the game, kicking every player who is not ready.
*
* @param playerId Player issuing the force start.
*/
void sendForceStart(int playerId);
/**
* @brief Lock or unlock a player's sideboard.
*
* @param locked Whether the sideboard is locked.
* @param playerId Player the lock applies to.
*/
void sendSetSideboardLock(bool locked, int playerId);
/**
* @brief Send a player's sideboard plan.
*
* @param plan Cards to move between zones when leaving sideboarding.
* @param playerId Player the plan belongs to.
*/
void sendSetSideboardPlan(const QList<MoveCard_ToZone> &plan, int playerId);
/** @} */
/** @name Incoming event processing
* Entry points for server-sent events.
* @{
*/
/**
* @brief Process a container of game events received from the server.
*
* This is the main dispatch function for incoming game events.
* Events are routed to spectator handlers, game-level handlers,
* or forwarded to PlayerEventHandler instances as appropriate.
*
* @param cont Game event container from the server.
* @param client Client that received the container.
* @param options Processing flags (e.g. silent, replay).
*/
void
processGameEventContainer(const GameEventContainer &cont, AbstractClient *client, EventProcessingOptions options);
/** @} */
/** @name Command preparation helpers
* Internal helpers for building command containers.
* @{
*/
/**
* @brief Wrap a single protobuf command in a PendingCommand.
*
* @param cmd Protobuf command message.
* @return Newly allocated PendingCommand (caller takes ownership).
*/
PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd);
/**
* @brief Wrap multiple protobuf commands in a single PendingCommand.
*
* Ownership of the messages in cmdList is transferred to the handler.
*
* @param cmdList List of protobuf command messages.
* @return Newly allocated PendingCommand.
*/
PendingCommand *prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList);
/** @} */
/** @name Spectator event handlers
* Events originating from spectators.
* @{
*/
/**
* @brief Handle a spectator chat message.
*/
void eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, const GameEventContext &context);
/**
* @brief Handle a spectator leaving the game.
*/
void eventSpectatorLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
/** @} */
/** @name Game state event handlers
* Events that affect global game state.
* @{
*/
/**
* @brief Handle a full game state update from the server.
*
* Used during game startup, reconnection, and resynchronization.
*/
void eventGameStateChanged(const Event_GameStateChanged &event, int eventPlayerId, const GameEventContext &context);
/**
* @brief Update card attachment relationships for all players.
*
* Called after a game state update to ensure attachments are resolved
* consistently across all zones.
*/
void processCardAttachmentsForPlayers(const Event_GameStateChanged &event);
/** @brief Handle a change in game host. */
void eventGameHostChanged(const Event_GameHostChanged &event, int eventPlayerId, const GameEventContext &context);
/** @brief Handle the game being closed by the server. */
void eventGameClosed(const Event_GameClosed &event, int eventPlayerId, const GameEventContext &context);
/** @brief Handle a change of the active player. */
void eventSetActivePlayer(const Event_SetActivePlayer &event, int eventPlayerId, const GameEventContext &context);
/** @brief Handle a change of the active phase. */
void eventSetActivePhase(const Event_SetActivePhase &event, int eventPlayerId, const GameEventContext &context);
/** @brief Handle a turn reversal event. */
void eventReverseTurn(const Event_ReverseTurn &event, int eventPlayerId, const GameEventContext &context);
/** @brief Handle ping / latency updates. */
void eventPing(const Event_Ping &event, int eventPlayerId, const GameEventContext &context);
/** @} */
/** @name Player lifecycle and property handlers
* Events related to players joining, leaving, or changing state.
* @{
*/
/**
* @brief Handle updates to a player's properties.
*
* Includes readiness, concede state, deck selection, sideboard lock,
* and connection state changes.
*/
void eventPlayerPropertiesChanged(const Event_PlayerPropertiesChanged &event,
int eventPlayerId,
const GameEventContext &context);
/** @brief Handle a player or spectator joining the game. */
void eventJoin(const Event_Join &event, int eventPlayerId, const GameEventContext &context);
/** @brief Handle a player leaving the game. */
void eventLeave(const Event_Leave &event, int eventPlayerId, const GameEventContext &context);
/** @brief Handle the local player being kicked from the game. */
void eventKicked(const Event_Kicked &event, int eventPlayerId, const GameEventContext &context);
/**
* @brief Convert a leave reason enum to a human-readable string.
*/
QString getLeaveReason(Event_Leave::LeaveReason reason);
/** @} */
public slots:
/** @name Command dispatch slots
* Low-level command transmission.
* @{
*/
/**
* @brief Send a prepared PendingCommand.
*
* @param pend Pending command to send.
* @param playerId Player whose client should send the command.
*/
void sendGameCommand(PendingCommand *pend, int playerId = -1);
/**
* @brief Send a single protobuf command.
*
* @param command Protobuf command message.
* @param playerId Player whose client should send the command.
*/
void sendGameCommand(const ::google::protobuf::Message &command, int playerId = -1);
/**
* @brief Called when a PendingCommand finishes execution.
*
* Used to detect server-side errors such as chat flood protection.
*/
void commandFinished(const Response &response);
/** @} */
signals:
/** @name Core state signals
* @{
*/
void emitUserEvent();
void containerProcessingStarted(GameEventContext context);
void containerProcessingDone();
void gameFlooded();
void setContextJudgeName(QString judgeName);
/** @} */
/** @name Player and spectator signals
* @{
*/
void addPlayerToAutoCompleteList(QString playerName);
void localPlayerDeckSelected(PlayerLogic *localPlayer, int playerId, ServerInfo_Player playerInfo);
void remotePlayerDeckSelected(QString deckList, int playerId, QString playerName);
void remotePlayersDecksSelected(QVector<QPair<int, QPair<QString, QString>>> opponentDecks);
void localPlayerSideboardLocked(int playerId, bool sideboardLocked);
void localPlayerReadyStateChanged(int playerId, bool ready);
/** @} */
/** @name Game flow signals
* @{
*/
void gameStopped();
void gameClosed();
void playerPropertiesChanged(const ServerInfo_PlayerProperties &prop, int playerId);
void playerJoined(const ServerInfo_PlayerProperties &playerInfo);
void playerLeft(int leavingPlayerId);
void playerKicked();
void spectatorJoined(const ServerInfo_PlayerProperties &spectatorInfo);
void spectatorLeft(int leavingSpectatorId);
void arrowDeleted(int creatorId, int arrowId);
/** @} */
/** @name Logging signals
* Signals consumed by MessageLogWidget.
* @{
*/
void logSpectatorSay(ServerInfo_User userInfo, QString message);
void logSpectatorLeave(QString name, QString reason);
void logGameStart();
void logReadyStart(PlayerLogic *player);
void logNotReadyStart(PlayerLogic *player);
void logDeckSelect(PlayerLogic *player, QString deckHash, int sideboardSize);
void logSideboardLockSet(PlayerLogic *player, bool sideboardLocked);
void logConnectionStateChanged(PlayerLogic *player, bool connected);
void logJoinSpectator(QString spectatorName);
void logJoinPlayer(PlayerLogic *player);
void logLeave(PlayerLogic *player, QString reason);
void logKicked();
void logTurnReversed(PlayerLogic *player, bool reversed);
void logGameClosed();
void logActivePlayer(PlayerLogic *activePlayer);
void logActivePhaseChanged(int activePhase);
void logConcede(int playerId);
void logUnconcede(int playerId);
/** @} */
};
#endif // COCKATRICE_GAME_EVENT_HANDLER_H

View file

@ -1,7 +0,0 @@
#include "game_meta_info.h"
#include "abstract_game.h"
GameMetaInfo::GameMetaInfo(AbstractGame *parent) : QObject(parent)
{
}

View file

@ -1,116 +0,0 @@
/**
* @file game_meta_info.h
* @ingroup GameLogic
*/
//! \todo Document this file.
#ifndef GAME_META_INFO_H
#define GAME_META_INFO_H
#include <QMap>
#include <QObject>
#include <QString>
#include <libcockatrice/protocol/pb/serverinfo_game.pb.h>
#include <qtmetamacros.h>
// Translation layer class to expose protobuf safely and hook it up to Qt Signals.
// This class de-couples the domain object (i.e. the GameMetaInfo) from the network object.
// If the network object changes, only this class needs to be adjusted.
class AbstractGame;
class GameMetaInfo : public QObject
{
Q_OBJECT
public:
explicit GameMetaInfo(AbstractGame *parent);
QMap<int, QString> roomGameTypes;
// Populate from protobuf (e.g., after network message)
void setFromProto(const ServerInfo_Game &gi)
{
gameInfo_.CopyFrom(gi);
}
const ServerInfo_Game &proto() const
{
return gameInfo_;
}
// High-level getters that avoid exposing protobuf directly
int gameId() const
{
return gameInfo_.game_id();
}
int maxPlayers() const
{
return gameInfo_.max_players();
}
QString description() const
{
return QString::fromStdString(gameInfo_.description());
}
bool started() const
{
return gameInfo_.started();
}
bool spectatorsOmniscient() const
{
return gameInfo_.spectators_omniscient();
}
bool spectatorsCanChat() const
{
return gameInfo_.spectators_can_chat();
}
int gameTypesSize() const
{
return gameInfo_.game_types_size();
}
int gameTypeIdAt(int index) const
{
return gameInfo_.game_types(index);
}
QMap<int, QString> getRoomGameTypes() const
{
return roomGameTypes;
}
void setRoomGameTypes(QMap<int, QString> _roomGameTypes)
{
roomGameTypes = _roomGameTypes;
}
QString findRoomGameType(int index)
{
return roomGameTypes.find(gameInfo_.game_types(index)).value();
}
public slots:
void setStarted(bool s)
{
if (gameInfo_.started() == s) {
return;
}
gameInfo_.set_started(s);
emit startedChanged(s);
}
void setSpectatorsOmniscient(bool v)
{
if (gameInfo_.spectators_omniscient() == v) {
return;
}
gameInfo_.set_spectators_omniscient(v);
emit spectatorsOmniscienceChanged(v);
}
signals:
void startedChanged(bool started);
void spectatorsOmniscienceChanged(bool omniscient);
private:
ServerInfo_Game gameInfo_;
};
#endif // GAME_META_INFO_H

View file

@ -1,50 +0,0 @@
#include "game_state.h"
#include "abstract_game.h"
#include <QChar>
#include <QTimer>
class AbstractClient;
GameState::GameState(AbstractGame *parent,
int _secondsElapsed,
int _hostId,
bool _isLocalGame,
const QList<AbstractClient *> _clients,
bool _gameStateKnown,
bool _resuming,
int _currentPhase,
bool _gameClosed)
: QObject(parent), gameTimer(nullptr), secondsElapsed(_secondsElapsed), hostId(_hostId), isLocalGame(_isLocalGame),
clients(_clients), gameStateKnown(_gameStateKnown), resuming(_resuming), currentPhase(_currentPhase),
activePlayer(-1), gameClosed(_gameClosed)
{
gameTimer = new QTimer(this);
gameTimer->setInterval(1000);
connect(gameTimer, &QTimer::timeout, this, &GameState::incrementGameTime);
gameTimer->start();
}
void GameState::incrementGameTime()
{
setGameTime(++secondsElapsed);
}
void GameState::setGameTime(int _secondsElapsed)
{
secondsElapsed = _secondsElapsed;
emit updateTimeElapsedLabel(formatElapsedTime(_secondsElapsed));
}
QString GameState::formatElapsedTime(int _secondsElapsed)
{
int seconds = _secondsElapsed;
int minutes = seconds / 60;
seconds -= minutes * 60;
int hours = minutes / 60;
minutes -= hours * 60;
return QString::number(hours).rightJustified(2, '0') + ":" + QString::number(minutes).rightJustified(2, '0') + ":" +
QString::number(seconds).rightJustified(2, '0');
}

View file

@ -1,147 +0,0 @@
/**
* @file game_state.h
* @ingroup GameLogic
*/
//! \todo Document this file.
#ifndef COCKATRICE_GAME_STATE_H
#define COCKATRICE_GAME_STATE_H
#include <QList>
#include <QObject>
#include <QString>
#include <qtmetamacros.h>
class AbstractGame;
class AbstractClient;
class QTimer;
class GameState : public QObject
{
Q_OBJECT
public:
explicit GameState(AbstractGame *parent,
int secondsElapsed,
int hostId,
bool isLocalGame,
QList<AbstractClient *> clients,
bool gameStateKnown,
bool resuming,
int currentPhase,
bool gameClosed);
void setHostId(int _hostId)
{
hostId = _hostId;
}
QList<AbstractClient *> getClients() const
{
return clients;
}
bool getIsLocalGame() const
{
return isLocalGame;
}
bool isResuming() const
{
return resuming;
}
void setResuming(bool _resuming)
{
resuming = _resuming;
}
bool isGameStateKnown() const
{
return gameStateKnown;
}
int getCurrentPhase() const
{
return currentPhase;
}
void setCurrentPhase(int phase)
{
currentPhase = phase;
emit activePhaseChanged(phase);
}
void setActivePlayer(int activePlayerId)
{
activePlayer = activePlayerId;
emit activePlayerChanged(activePlayer);
}
int getActivePlayer() const
{
return activePlayer;
}
void setGameClosed(bool closed)
{
gameClosed = closed;
}
bool isGameClosed() const
{
return gameClosed;
}
void onStartedChanged(bool _started)
{
if (_started) {
emit gameStarted(_started);
} else {
emit gameStopped();
}
}
void setGameStateKnown(bool known)
{
gameStateKnown = known;
}
int getHostId() const
{
return hostId;
}
int getSecondsElapsed() const
{
return secondsElapsed;
}
signals:
void updateTimeElapsedLabel(QString newTime);
void gameStarted(bool resuming);
void gameStopped();
void activePhaseChanged(int activePhase);
void activePlayerChanged(int playerId);
public slots:
void incrementGameTime();
void setGameTime(int _secondsElapsed);
public:
static QString formatElapsedTime(int _secondsElapsed);
private:
QTimer *gameTimer;
int secondsElapsed;
int hostId;
const bool isLocalGame;
QList<AbstractClient *> clients;
bool gameStateKnown;
bool resuming;
int currentPhase;
int activePlayer;
bool gameClosed;
};
#endif // COCKATRICE_GAME_STATE_H

View file

@ -1,65 +0,0 @@
#include "phase.h"
#include <QByteArray>
#include <QList>
#include <QSharedPointer>
#include <QtGlobal>
Phase::Phase(const QString &_name, const QString &_color, const QString &_soundFileName)
: name(_name), color(_color), soundFileName(_soundFileName)
{
}
/**
* @return The translated name for the phase
*/
QString Phase::getName() const
{
return tr(name.toUtf8().data());
}
Phase Phases::getPhase(int phase)
{
if (0 <= phase && phase < Phases::phaseTypesCount) {
return phases[phase];
} else {
return unknownPhase;
}
}
int Phases::getLastSubphase(int phase)
{
if (0 <= phase && phase < Phases::phaseTypesCount) {
return subPhasesEnd[phase];
} else {
return phase;
}
}
QVector<int> getSubPhasesEnd()
{
QVector<int> array(Phases::phaseTypesCount);
for (int phaseEnd = Phases::phaseTypesCount - 1; phaseEnd >= 0;) {
int subPhase = phaseEnd;
for (; subPhase >= 0 && Phases::phases[phaseEnd].color == Phases::phases[subPhase].color; --subPhase) {
array[subPhase] = phaseEnd;
}
phaseEnd = subPhase;
}
return array;
}
const Phase Phases::unknownPhase(QT_TRANSLATE_NOOP("Phase", "Unknown Phase"), "black", "unknown_phase");
const Phase Phases::phases[Phases::phaseTypesCount] = {
{QT_TRANSLATE_NOOP("Phase", "Untap"), "green", "untap_step"},
{QT_TRANSLATE_NOOP("Phase", "Upkeep"), "green", "upkeep_step"},
{QT_TRANSLATE_NOOP("Phase", "Draw"), "green", "draw_step"},
{QT_TRANSLATE_NOOP("Phase", "First Main"), "blue", "main_1"},
{QT_TRANSLATE_NOOP("Phase", "Beginning of Combat"), "red", "start_combat"},
{QT_TRANSLATE_NOOP("Phase", "Declare Attackers"), "red", "attack_step"},
{QT_TRANSLATE_NOOP("Phase", "Declare Blockers"), "red", "block_step"},
{QT_TRANSLATE_NOOP("Phase", "Combat Damage"), "red", "damage_step"},
{QT_TRANSLATE_NOOP("Phase", "End of Combat"), "red", "end_combat"},
{QT_TRANSLATE_NOOP("Phase", "Second Main"), "blue", "main_2"},
{QT_TRANSLATE_NOOP("Phase", "End/Cleanup"), "green", "end_step"}};
const QVector<int> Phases::subPhasesEnd = getSubPhasesEnd();

View file

@ -1,38 +0,0 @@
/**
* @file phase.h
* @ingroup GameLogic
*/
//! \todo Document this file.
#ifndef PHASE_H
#define PHASE_H
#include <QCoreApplication>
#include <QString>
#include <QVector>
class Phase
{
Q_DECLARE_TR_FUNCTIONS(Phase)
QString name;
public:
QString color, soundFileName;
Phase(const QString &_name, const QString &_color, const QString &_soundFileName);
QString getName() const;
};
struct Phases
{
const static int phaseTypesCount = 11;
const static Phase unknownPhase;
const static Phase phases[phaseTypesCount];
const static QVector<int> subPhasesEnd;
static Phase getPhase(int);
static int getLastSubphase(int phase);
};
#endif // PHASE_H

View file

@ -1,28 +0,0 @@
/**
* @file card_menu_action_type.h
* @ingroup GameLogicPlayers
*/
//! \todo Document this file.
#ifndef COCKATRICE_CARD_MENU_ACTION_TYPE_H
#define COCKATRICE_CARD_MENU_ACTION_TYPE_H
enum CardMenuActionType
{
// Per-card attribute actions (must be <= cmClone for cardMenuAction() dispatch)
cmTap,
cmUntap,
cmDoesntUntap,
cmFlip,
cmPeek,
cmClone,
// Move actions (must be > cmClone for cardMenuAction() dispatch)
cmMoveToTopLibrary,
cmMoveToBottomLibrary,
cmMoveToHand,
cmMoveToGraveyard,
cmMoveToExile,
cmMoveToTable
};
#endif // COCKATRICE_CARD_MENU_ACTION_TYPE_H

View file

@ -1,22 +0,0 @@
/**
* @file card_move_request.h
* @ingroup GameLogicActions
*/
//! \todo Document this file.
#ifndef COCKATRICE_CARD_MOVE_REQUEST_H
#define COCKATRICE_CARD_MOVE_REQUEST_H
#include <QString>
/**
* @brief Describes one card a view wants moved, in logic terms rather than wire terms.
*/
struct CardMoveRequest
{
int cardId = -1;
bool faceDown = false;
QString pt = {};
};
#endif // COCKATRICE_CARD_MOVE_REQUEST_H

View file

@ -1,26 +0,0 @@
/**
* @file event_processing_options.h
* @ingroup GameLogicPlayers
*/
//! \todo Document this file.
#ifndef COCKATRICE_EVENT_PROCESSING_OPTIONS_H
#define COCKATRICE_EVENT_PROCESSING_OPTIONS_H
#include <QFlags>
// Define the base enum
enum EventProcessingOption
{
SKIP_REVEAL_WINDOW = 0x0001,
SKIP_TAP_ANIMATION = 0x0002,
SKIP_DAMAGE_ANIMATION = 0x0004
};
// Wrap it in a QFlags typedef
Q_DECLARE_FLAGS(EventProcessingOptions, EventProcessingOption)
// Add operator overloads (|, &, etc.)
Q_DECLARE_OPERATORS_FOR_FLAGS(EventProcessingOptions)
#endif // COCKATRICE_EVENT_PROCESSING_OPTIONS_H

View file

@ -1,19 +0,0 @@
/**
* @file move_top_cards_until_options.h
* @ingroup GameLogicPlayers
*/
//! \todo Document this file.
#ifndef COCKATRICE_MOVE_TOP_CARDS_UNTIL_OPTIONS_H
#define COCKATRICE_MOVE_TOP_CARDS_UNTIL_OPTIONS_H
#include <QStringList>
struct MoveTopCardsUntilOptions
{
QStringList exprs = {};
int numberOfHits = 1;
bool autoPlay = false;
};
#endif // COCKATRICE_MOVE_TOP_CARDS_UNTIL_OPTIONS_H

File diff suppressed because it is too large Load diff

View file

@ -1,308 +0,0 @@
/**
* @file player_actions.h
* @ingroup GameLogicActions
* @ingroup GameLogicPlayers
*/
//! \todo Document this file.
#ifndef COCKATRICE_PLAYER_ACTIONS_H
#define COCKATRICE_PLAYER_ACTIONS_H
#include "card_menu_action_type.h"
#include "card_move_request.h"
#include "libcockatrice/card/card_info.h"
#include "move_top_cards_until_options.h"
#include "token_info.h"
#include <QObject>
#include <QString>
#include <QStringList>
#include <libcockatrice/card/relation/card_relation_type.h>
#include <libcockatrice/filters/filter_string.h>
#include <qtmetamacros.h>
class CardItem;
class CardRelation;
class QAction;
class QTimer;
template <typename T> class QList;
namespace google
{
namespace protobuf
{
class Message;
}
} // namespace google
class CardState;
class CardZoneLogic;
class Command_MoveCard;
class PendingCommand;
class PlayerLogic;
struct ArrowData;
class PlayerActions : public QObject
{
Q_OBJECT
public:
enum CardsToReveal
{
RANDOM_CARD_FROM_ZONE = -2
};
explicit PlayerActions(PlayerLogic *player);
void sendGameCommand(PendingCommand *pend);
void sendGameCommand(const google::protobuf::Message &command);
PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd);
PendingCommand *prepareGameCommand(const QList<const ::google::protobuf::Message *> &cmdList);
/**
* @brief Moves the requested cards between zones on behalf of a view.
* @param startZone Zone the cards are dragged out of.
* @param targetZone Zone the cards are dropped onto.
* @param x Horizontal target index within the zone.
* @param y Vertical target index within the zone.
* @param cards The cards to move, as chosen by the view.
*/
void
moveCards(CardZoneLogic *startZone, CardZoneLogic *targetZone, int x, int y, const QList<CardMoveRequest> &cards);
/**
* @brief Prepares a dump of the given zone, for the caller to connect to and send.
* @param zone Zone to dump.
* @param numberCards How many cards to dump, -1 for all.
*/
PendingCommand *prepareZoneDump(CardZoneLogic *zone, int numberCards);
/**
* @brief Sets the tapped state of the cards that do not already have it.
* @param zoneName Zone the cards belong to.
* @param cards Cards the view wants toggled.
* @param tapped Desired tapped state.
*/
void actToggleTapped(const QString &zoneName, const QList<CardState *> &cards, bool tapped);
/**
* @brief Attaches one card to another on the table on behalf of a view.
* @param startCard Card being attached.
* @param targetCard Card being attached to.
*/
void attachCards(CardState *startCard, const CardState *targetCard);
/**
* @brief Draws an arrow from a card to another card or a player on behalf of a view.
*
* The view resolves the endpoints before playing the card out of hand, so the
* arrow still records the zone the card started in.
*
* @param startCard Card the arrow starts from.
* @param arrow Arrow endpoints and color, as resolved by the view.
* @param deleteInPhase Phase the arrow expires in, 0 to keep it until removed.
*/
void createArrow(CardState *startCard, const ArrowData &arrow, int deleteInPhase);
void moveOneCardUntil(CardState *card);
void stopMoveTopCardsUntil();
[[nodiscard]] bool isMovingCardsUntil() const
{
return movingCardsUntil;
}
signals:
void requestViewTopCardsDialog(int defaultNumberTopCards, int deckSize);
void requestViewBottomCardsDialog(int defaultNumberBottomCards, int deckSize);
void requestShuffleTopDialog(int defaultNumberTopCards, int maxCards);
void requestShuffleBottomDialog(int defaultNumberBottomCards, int maxCards);
void requestMulliganDialog(int startSize, int handSize, int deckSize);
void requestDrawCardsDialog(int defaultNumberTopCards, int deckSize);
void requestMoveTopCardsToDialog(int defaultNumberTopCards,
int maxCards,
const QString &targetZone,
const QString &zoneDisplayName,
bool faceDown);
void requestMoveTopCardsUntilDialog(MoveTopCardsUntilOptions options);
void requestMoveBottomCardsToDialog(int defaultNumberBottomCards,
int maxCards,
const QString &targetZone,
const QString &zoneDisplayName,
bool faceDown);
void requestDrawBottomCardsDialog(int defaultNumberBottomCards, int maxCards);
void requestRollDieDialog();
void requestCreateTokenDialog(const QStringList &predefinedTokens);
void requestCreateRelatedFromRelationDialog(const CardState *sourceCard, const CardRelation *cardRelation);
void requestMoveCardXCardsFromTopDialog(int defaultNumberTopCardsToPlaceBelow, int deckSize);
void requestSetPTDialog(const QString &oldPT);
void requestSetAnnotationDialog(const QString &oldAnnotation);
void requestSetCardCounterDialog(int counterId, const QString &oldValueForDlg);
void requestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed = false);
void requestSortHand(const QList<CardList::SortOption> &options);
void requestEnableAndSetCreateAnotherTokenAction(const QString &lastTokenName);
void requestSetLastToken(CardInfoPtr lastToken);
public slots:
void setLastToken(CardInfoPtr cardInfo);
void setLastTokenInfo(CardInfoPtr cardInfo);
void playCard(CardState *c, bool faceDown);
void playCardToTable(const CardState *c, bool faceDown);
void actUntapAll();
void actRequestRollDieDialog();
void actRollDie(int sides, int count);
void actFlipCoin();
void actRequestCreateTokenDialog(const QStringList &predefinedTokens);
void actCreateToken(TokenInfo tokenToCreate);
void actCreateAnotherToken();
void actRequestCreateRelatedFromRelationDialog(const CardState *sourceCard, const CardRelation *cardRelation);
bool createRelatedFromRelation(const CardState *sourceCard, const CardRelation *cardRelation, int variableCount);
void onRelatedCardCreated(const CardState *sourceCard, const CardRelation *cardRelation);
void setLastRelatedCreationSucceeded(bool succeeded)
{
lastRelatedCreationSucceeded = succeeded;
}
void actShuffle();
void actRequestShuffleTopDialog();
void actShuffleTop(int number);
void actRequestShuffleBottomDialog();
void actShuffleBottom(int number);
void actDrawCard();
void actRequestDrawCardsDialog();
void actDrawCards(int number);
void actUndoDraw();
void actRequestMulliganDialog();
void actMulligan(int number);
void actMulliganSameSize();
void actMulliganMinusOne();
void doMulligan(int number);
void actPlay(QList<CardState *> selectedCards);
void actPlayFacedown(QList<CardState *> selectedCards);
void actHide(QList<CardState *> selectedCards);
void actMoveTopCardToPlay();
void actMoveTopCardToPlayFaceDown();
void actMoveTopCardToGrave();
void actMoveTopCardToExile();
void actMoveTopCardsToGrave();
void actMoveTopCardsToGraveFaceDown();
void actMoveTopCardsToExile();
void actMoveTopCardsToExileFaceDown();
void actRequestMoveTopCardsUntilDialog();
void moveTopCardsUntil(const QString &expr, MoveTopCardsUntilOptions options);
void actMoveTopCardToBottom();
void actRequestMoveTopCardsToDialog(const QString &targetZone, const QString &zoneDisplayName, bool faceDown);
void moveTopCardsTo(int number, const QString &targetZone, bool faceDown);
void actDrawBottomCard();
void actRequestDrawBottomCardsDialog();
void actDrawBottomCards(int number);
void actMoveBottomCardToPlay();
void actMoveBottomCardToPlayFaceDown();
void actMoveBottomCardToGrave();
void actMoveBottomCardToExile();
void actMoveBottomCardsToGrave();
void actMoveBottomCardsToGraveFaceDown();
void actMoveBottomCardsToExile();
void actMoveBottomCardsToExileFaceDown();
void actMoveBottomCardToTop();
void actRequestMoveBottomCardsToDialog(const QString &targetZone, const QString &zoneDisplayName, bool faceDown);
void moveBottomCardsTo(int number, const QString &targetZone, bool faceDown);
void actViewLibrary();
void actViewHand();
void actRequestViewTopCardsDialog();
void actViewTopCards(int number);
void actRequestViewBottomCardsDialog();
void actViewBottomCards(int number);
void actAlwaysRevealTopCard(bool alwaysRevealTopCard);
void actAlwaysLookAtTopCard(bool alwaysRevealTopCard);
void actViewGraveyard();
void actLendLibrary(int lendToPlayerId);
void actRevealTopCards(int revealToPlayerId, int amount);
void actRevealRandomGraveyardCard(int revealToPlayerId);
void actViewRfg();
void actViewSideboard();
void actSayMessage();
void actOpenDeckInDeckEditor();
void actCreatePredefinedToken();
void actCreateRelatedCard(const CardState *sourceCard, int index);
void actCreateAllRelatedCards(const CardState *sourceCard);
void actRequestMoveCardXCardsFromTopDialog();
void actMoveCardXCardsFromTop(QList<CardState *> selectedCards, int number);
void actRemoveCardCounter(QList<CardState *> selectedCards, int counterId);
void actAddCardCounter(QList<CardState *> selectedCards, int counterId);
void actRequestSetCardCounterDialog(QList<CardState *> selectedCards, int counterId);
void actSetCardCounter(QList<CardState *> selectedCards, int counterId, const QString &counterValue);
void actIncrementAllCardCounters(QList<CardState *> cardsToUpdate);
void actUnattach(QList<CardState *> selectedCards);
void actIncPT(QList<CardState *> selectedCards, int deltaP, int deltaT);
void actResetPT(QList<CardState *> selectedCards);
void actRequestSetPTDialog(QList<CardState *> selectedCards);
void actSetPT(QList<CardState *> selectedCards, const QString &pt);
void actIncP(QList<CardState *> selectedCards);
void actDecP(QList<CardState *> selectedCards);
void actIncT(QList<CardState *> selectedCards);
void actDecT(QList<CardState *> selectedCards);
void actIncPT(QList<CardState *> selectedCards);
void actDecPT(QList<CardState *> selectedCards);
void actFlowP(QList<CardState *> selectedCards);
void actFlowT(QList<CardState *> selectedCards);
/** @brief Adjusts a player counter by a signed delta. */
void actIncrementCounter(int counterId, int delta);
/** @brief Sets a player counter to an absolute value. */
void actSetCounter(int counterId, int value);
void actReduceLifeByPower(QList<CardState *> selectedCards);
void actRequestSetAnnotationDialog(QList<CardState *> selectedCards);
void actSetAnnotation(QList<CardState *> selectedCards, const QString &annotation);
void actReveal(QList<CardState *> selectedCards, QAction *action);
void actRevealHand(int revealToPlayerId);
void actRevealRandomHandCard(int revealToPlayerId);
void actRevealLibrary(int revealToPlayerId);
void actSortHand();
void cardMenuAction(QList<CardState *> selectedCards, CardMenuActionType type);
private:
PlayerLogic *player;
int defaultNumberTopCards = 1;
int defaultNumberTopCardsToPlaceBelow = 1;
int defaultNumberBottomCards = 1;
int defaultNumberDieRoll = 20;
TokenInfo lastTokenInfo;
int lastTokenTableRow;
bool movingCardsUntil;
QTimer *moveTopCardTimer;
FilterString movingCardsUntilFilter;
int movingCardsUntilCounter = 0;
MoveTopCardsUntilOptions movingCardsUntilOptions;
bool lastRelatedCreationSucceeded = false;
void createCard(const CardState *sourceCard,
const QString &dbCardName,
CardRelationType attach = CardRelationType::DoesNotAttach,
bool persistent = false,
bool faceDown = false);
void playSelectedCards(QList<CardState *> selectedCards, bool faceDown = false);
void cmdSetTopCard(Command_MoveCard &cmd);
void cmdSetBottomCard(Command_MoveCard &cmd);
void offsetCardCounter(QList<CardState *> selectedCards, int counterId, int offset);
};
#endif // COCKATRICE_PLAYER_ACTIONS_H

View file

@ -1,704 +0,0 @@
#include "player_event_handler.h"
#include "../abstract_game.h"
#include "../board/arrow_data.h"
#include "../board/card_list.h"
#include "../board/card_state.h"
#include "../board/counter_state.h"
#include "../zones/card_zone_logic.h"
#include "../zones/hand_zone_logic.h"
#include "../zones/pile_zone_logic.h"
#include "../zones/view_zone_logic.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/printing/exact_card.h"
#include "libcockatrice/protocol/pb/card_attributes.pb.h"
#include "libcockatrice/protocol/pb/game_event.pb.h"
#include "libcockatrice/protocol/pb/game_event_context.pb.h"
#include "libcockatrice/protocol/pb/serverinfo_card.pb.h"
#include "libcockatrice/utility/card_ref.h"
#include "player_actions.h"
#include "player_info.h"
#include "player_logic.h"
#include "player_manager.h"
#include <QDebug>
#include <QList>
#include <QMap>
#include <QSharedPointer>
#include <QtGlobal>
#include <algorithm>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
#include <libcockatrice/protocol/pb/context_move_card.pb.h>
#include <libcockatrice/protocol/pb/context_undo_draw.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_create_arrow.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_del_counter.pb.h>
#include <libcockatrice/protocol/pb/event_delete_arrow.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_dump_zone.pb.h>
#include <libcockatrice/protocol/pb/event_flip_card.pb.h>
#include <libcockatrice/protocol/pb/event_game_log_notice.pb.h>
#include <libcockatrice/protocol/pb/event_game_say.pb.h>
#include <libcockatrice/protocol/pb/event_move_card.pb.h>
#include <libcockatrice/protocol/pb/event_reveal_cards.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_counter.pb.h>
#include <libcockatrice/protocol/pb/event_set_counter.pb.h>
#include <libcockatrice/protocol/pb/event_shuffle.pb.h>
#include <libcockatrice/utility/color.h>
#include <libcockatrice/utility/zone_names.h>
#include <qlogging.h>
#include <string>
PlayerEventHandler::PlayerEventHandler(PlayerLogic *_player) : QObject(_player), player(_player)
{
connect(this, &PlayerEventHandler::requestCardMenuUpdate, player, &PlayerLogic::requestCardMenuUpdate);
}
void PlayerEventHandler::eventGameSay(const Event_GameSay &event)
{
emit logSay(player, QString::fromStdString(event.message()));
}
void PlayerEventHandler::eventShuffle(const Event_Shuffle &event)
{
CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name()));
if (!zone) {
return;
}
auto &cardList = zone->getCards();
int absStart = event.start();
if (absStart < 0) { // negative indexes start from the end
absStart += cardList.length();
}
// close all views that contain shuffled cards
for (auto *viewLogic : zone->getViews()) {
if (viewLogic != nullptr) {
int length = viewLogic->getCards().length();
// we want to close empty views as well
if (length == 0 || length > absStart) { // note this assumes views always start at the top of the library
viewLogic->requestClose();
}
} else {
qWarning() << zone->getName() << "of" << player->getPlayerInfo()->getName() << "holds empty zoneview!";
}
}
// remove revealed card name on top of decks
if (absStart == 0 && !cardList.isEmpty()) {
cardList.first()->setCardRef({});
emit zone->updateGraphics();
}
emit logShuffle(player, zone, event.start(), event.end());
}
void PlayerEventHandler::eventRollDie(const Event_RollDie &event)
{
if (!event.values().empty()) {
QList<uint> rolls(event.values().begin(), event.values().end());
std::sort(rolls.begin(), rolls.end());
emit logRollDie(player, static_cast<int>(event.sides()), rolls);
} else if (event.value()) {
// Backwards compatibility for old clients
emit logRollDie(player, static_cast<int>(event.sides()), {event.value()});
}
}
void PlayerEventHandler::eventCreateArrow(const Event_CreateArrow &event)
{
auto data = QSharedPointer<ArrowData>::create(ArrowData::fromProto(
event.arrow_info(), player->getPlayerInfo()->getId(), player->getPlayerInfo()->getLocal()));
const auto &playerList = player->getGame()->getPlayerManager()->getPlayers();
PlayerLogic *startPlayer = playerList.value(data->startPlayerId);
PlayerLogic *targetPlayer = playerList.value(data->targetPlayerId);
QString startCardName, targetCardName;
if (startPlayer) {
if (auto *zone = startPlayer->getZones().value(data->startZone)) {
if (auto *card = zone->getCard(data->startCardId)) {
startCardName = card->getName();
}
}
}
if (!data->isPlayerTargeted() && targetPlayer) {
if (auto *zone = targetPlayer->getZones().value(data->targetZone)) {
if (auto *card = zone->getCard(data->targetCardId)) {
targetCardName = card->getName();
}
}
}
emit player->arrowCreateRequested(data);
if (startPlayer && targetPlayer && !startCardName.isEmpty() &&
(data->isPlayerTargeted() || !targetCardName.isEmpty())) {
emit logCreateArrow(player, startPlayer, startCardName, targetPlayer, targetCardName, data->isPlayerTargeted());
}
}
void PlayerEventHandler::eventDeleteArrow(const Event_DeleteArrow &event)
{
emit player->arrowDeleted(player->getPlayerInfo()->getId(), event.arrow_id());
}
void PlayerEventHandler::eventCreateToken(const Event_CreateToken &event)
{
CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name()));
if (!zone) {
return;
}
CardRef cardRef = {QString::fromStdString(event.card_name()), QString::fromStdString(event.card_provider_id())};
// use db PT if not provided in event and not face-down
QString pt = QString::fromStdString(event.pt());
if (pt.isEmpty() && !event.face_down()) {
ExactCard dbCard = CardDatabaseManager::query()->getCard(cardRef);
if (dbCard) {
pt = dbCard.getInfo().getPowTough();
}
}
emit logCreateToken(player, cardRef.name, pt, event.face_down());
ServerInfo_Card cardInfo;
cardInfo.set_id(event.card_id());
cardInfo.set_name(event.card_name());
cardInfo.set_provider_id(event.card_provider_id());
cardInfo.set_pt(pt.toStdString());
cardInfo.set_color(event.color());
cardInfo.set_annotation(event.annotation());
cardInfo.set_destroy_on_zone_change(event.destroy_on_zone_change());
cardInfo.set_face_down(event.face_down());
cardInfo.set_x(event.x());
cardInfo.set_y(event.y());
emit zone->createCardRequested(cardInfo, true);
}
void PlayerEventHandler::eventSetCardAttr(const Event_SetCardAttr &event,
const GameEventContext &context,
EventProcessingOptions options)
{
CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name()));
if (!zone) {
return;
}
if (!event.has_card_id()) {
const CardList &cards = zone->getCards();
for (int i = 0; i < cards.size(); ++i) {
setCardAttrHelper(context, cards.at(i), event.attribute(), QString::fromStdString(event.attr_value()), true,
options);
}
if (event.attribute() == AttrTapped) {
emit logSetTapped(player, nullptr, event.attr_value() == "1");
}
} else {
CardState *card = zone->getCard(event.card_id());
if (!card) {
qWarning() << "PlayerEventHandler::eventSetCardAttr: card id=" << event.card_id() << "not found";
return;
}
setCardAttrHelper(context, card, event.attribute(), QString::fromStdString(event.attr_value()), false, options);
}
}
void PlayerEventHandler::setCardAttrHelper(const GameEventContext &context,
CardState *card,
CardAttribute attribute,
const QString &avalue,
bool allCards,
EventProcessingOptions options)
{
if (card == nullptr) {
return;
}
bool moveCardContext = context.HasExtension(Context_MoveCard::ext);
switch (attribute) {
case AttrTapped: {
bool tapped = avalue == "1";
if (!(!tapped && card->getDoesntUntap() && allCards)) {
if (!allCards) {
emit logSetTapped(player, card, tapped);
}
bool canAnimate = !options.testFlag(SKIP_TAP_ANIMATION) && !moveCardContext;
card->setTapped(tapped, canAnimate);
}
break;
}
case AttrAttacking: {
card->setAttacking(avalue == "1");
break;
}
case AttrFaceDown: {
card->setFaceDown(avalue == "1");
break;
}
case AttrColor: {
card->setColor(avalue);
break;
}
case AttrAnnotation: {
emit logSetAnnotation(player, card, avalue);
card->setAnnotation(avalue);
break;
}
case AttrDoesntUntap: {
bool value = (avalue == "1");
emit logSetDoesntUntap(player, card, value);
card->setDoesntUntap(value);
break;
}
case AttrPT: {
emit logSetPT(player, card, avalue);
card->setPT(avalue);
break;
}
}
}
void PlayerEventHandler::eventSetCardCounter(const Event_SetCardCounter &event)
{
CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name()));
if (!zone) {
return;
}
CardState *card = zone->getCard(event.card_id());
if (!card) {
return;
}
int oldValue = card->getCounters().value(event.counter_id(), 0);
card->setCounter(event.counter_id(), event.counter_value());
emit requestCardMenuUpdate(card);
emit logSetCardCounter(player, card->getName(), event.counter_id(), event.counter_value(), oldValue);
}
void PlayerEventHandler::eventCreateCounter(const Event_CreateCounter &event)
{
player->addCounter(event.counter_info());
}
void PlayerEventHandler::eventSetCounter(const Event_SetCounter &event, EventProcessingOptions options)
{
CounterState *ctr = player->getCounters().value(event.counter_id(), nullptr);
if (!ctr) {
return;
}
int oldValue = ctr->getValue();
const bool skipDamageAnimation = options.testFlag(SKIP_DAMAGE_ANIMATION);
ctr->setValue(event.value(), skipDamageAnimation);
emit logSetCounter(player, ctr->getName(), event.value(), oldValue);
}
void PlayerEventHandler::eventDelCounter(const Event_DelCounter &event)
{
player->delCounter(event.counter_id());
}
void PlayerEventHandler::eventDumpZone(const Event_DumpZone &event)
{
PlayerLogic *zoneOwner = player->getGame()->getPlayerManager()->getPlayers().value(event.zone_owner_id(), 0);
if (!zoneOwner) {
return;
}
CardZoneLogic *zone = zoneOwner->getZones().value(QString::fromStdString(event.zone_name()), 0);
if (!zone) {
return;
}
emit logDumpZone(player, zone, event.number_cards(), event.is_reversed());
}
void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEventContext &context)
{
PlayerLogic *startPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.start_player_id());
if (!startPlayer) {
return;
}
QString startZoneString = QString::fromStdString(event.start_zone());
CardZoneLogic *startZone = startPlayer->getZones().value(startZoneString, 0);
PlayerLogic *targetPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.target_player_id());
if (!targetPlayer) {
return;
}
CardZoneLogic *targetZone;
if (event.has_target_zone()) {
targetZone = targetPlayer->getZones().value(QString::fromStdString(event.target_zone()), 0);
} else {
targetZone = startZone;
}
if (!startZone || !targetZone) {
return;
}
int position = event.position();
int x = event.x();
int y = event.y();
int logPosition = position;
int logX = x;
if (x == -1) {
x = 0;
}
CardState *card = startZone->takeCard(position, event.card_id(), startZone != targetZone);
if (card == nullptr) {
return;
}
const bool zoneChanged = startZone != targetZone;
const bool ownerChanged = zoneChanged && startZone->getPlayer() != targetZone->getPlayer();
emit cardViewRefreshRequested(card, zoneChanged, ownerChanged ? targetZone->getPlayer() : nullptr);
if (event.has_card_name()) {
QString name = QString::fromStdString(event.card_name());
QString providerId =
event.has_new_card_provider_id() ? QString::fromStdString(event.new_card_provider_id()) : "";
card->setCardRef({name, providerId});
}
if (card->getAttachedTo() && (startZone != targetZone)) {
CardState *parentCard = card->getAttachedTo();
// Route through CardItem so the old parent's attachedCards list is cleaned up: CardState::setAttachedTo
// only repoints the pointer, leaving a dangling entry that reorganizeCards/prepareDelete would dereference.
if (CardItem *item = toCardItem(card)) {
item->setAttachedTo(nullptr);
} else {
card->setAttachedTo(nullptr);
}
parentCard->getZone()->reorganizeCards();
}
card->setId(event.new_card_id());
card->setFaceDown(event.face_down());
if (startZone != targetZone) {
const QList<CardState *> &attachedCards = card->getAttachedCards();
for (auto attachedCard : attachedCards) {
emit targetZone->cardAdded(attachedCard);
}
}
// The log event has to be sent before the card is added to the target zone
// because the addCard function can modify the card object.
if (context.HasExtension(Context_UndoDraw::ext)) {
emit logUndoDraw(player, card->getName());
} else {
emit logMoveCard(player, card, startZone, logPosition, targetZone, logX);
}
targetZone->addCard(card, true, x, y);
emit cardZoneChanged(card, startZone == targetZone);
emit requestCardMenuUpdate(card);
if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == ZoneNames::DECK &&
targetZone->getName() == ZoneNames::STACK) {
player->getPlayerActions()->moveOneCardUntil(card);
}
}
void PlayerEventHandler::eventFlipCard(const Event_FlipCard &event)
{
CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name()));
if (!zone) {
return;
}
CardState *card = zone->getCard(event.card_id());
if (!card) {
return;
}
if (!event.face_down()) {
QString cardName = QString::fromStdString(event.card_name());
QString providerId = QString::fromStdString(event.card_provider_id());
card->setCardRef({cardName, providerId});
}
emit logFlipCard(player, card->getName(), event.face_down());
card->setFaceDown(event.face_down());
emit requestCardMenuUpdate(card);
}
void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event)
{
CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name()));
if (!zone) {
return;
}
CardState *card = zone->getCard(event.card_id());
if (!card) {
return;
}
QList<CardState *> attachedCards = card->getAttachedCards();
// This list is always empty except for buggy server implementations.
for (auto &attachedCard : attachedCards) {
card->removeAttachedCard(attachedCard);
attachedCard->setGridPoint(QPoint(-1, attachedCard->getGridPoint().y()));
attachedCard->setAttachedTo(nullptr);
if (attachedCard->getZone() == nullptr) {
attachedCard->deleteView();
} else {
emit attachedCard->getZone()->cardAdded(attachedCard);
attachedCard->getZone()->reorganizeCards();
}
}
emit logDestroyCard(player, card->getName());
zone->takeCard(-1, event.card_id(), true);
card->deleteView();
}
void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
{
const QMap<int, PlayerLogic *> &playerList = player->getGame()->getPlayerManager()->getPlayers();
PlayerLogic *targetPlayer = nullptr;
CardZoneLogic *targetZone = nullptr;
CardState *targetCard = nullptr;
if (event.has_target_player_id()) {
targetPlayer = playerList.value(event.target_player_id(), 0);
if (targetPlayer) {
targetZone = targetPlayer->getZones().value(QString::fromStdString(event.target_zone()), 0);
if (targetZone) {
targetCard = targetZone->getCard(event.target_card_id());
}
}
}
CardZoneLogic *startZone = player->getZone(QString::fromStdString(event.start_zone()));
if (!startZone) {
return;
}
CardState *startCard = startZone->getCard(event.card_id());
if (!startCard) {
return;
}
CardState *oldParent = startCard->getAttachedTo();
// Route through CardItem::setAttachedTo to keep the attachedCards graph, grid-x reset and zone emits intact:
// CardState::setAttachedTo alone only repoints the pointer, so the target would never list the card and the old
// parent would keep a dangling pointer.
if (CardItem *startItem = toCardItem(startCard)) {
startItem->setAttachedTo(toCardItem(targetCard));
} else {
startCard->setAttachedTo(targetCard);
}
startZone->reorganizeCards();
if ((startZone != targetZone) && targetZone) {
targetZone->reorganizeCards();
}
if (oldParent) {
oldParent->getZone()->reorganizeCards();
}
if (targetCard) {
emit logAttachCard(player, startCard->getName(), targetPlayer, targetCard->getName());
} else {
emit logUnattachCard(player, startCard->getName());
}
emit requestCardMenuUpdate(startCard);
}
void PlayerEventHandler::eventDrawCards(const Event_DrawCards &event)
{
CardZoneLogic *_deck = player->getDeckZone();
CardZoneLogic *_hand = player->getHandZone();
const int listSize = event.cards_size();
if (listSize) {
for (int i = 0; i < listSize; ++i) {
const ServerInfo_Card &cardInfo = event.cards(i);
CardState *card = _deck->takeCard(0, cardInfo.id());
QString cardName = QString::fromStdString(cardInfo.name());
QString providerId = QString::fromStdString(cardInfo.provider_id());
card->setCardRef({cardName, providerId});
_hand->addCard(card, false, -1);
}
} else {
const int number = event.number();
for (int i = 0; i < number; ++i) {
_hand->addCard(_deck->takeCard(0, -1), false, -1);
}
}
_hand->reorganizeCards();
_deck->reorganizeCards();
emit logDrawCards(player, event.number(), _deck->getCards().size() == 0);
}
void PlayerEventHandler::eventRevealCards(const Event_RevealCards &event, EventProcessingOptions options)
{
Q_UNUSED(options);
CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name()));
if (!zone) {
return;
}
PlayerLogic *otherPlayer = nullptr;
if (event.has_other_player_id()) {
otherPlayer = player->getGame()->getPlayerManager()->getPlayers().value(event.other_player_id());
if (!otherPlayer) {
return;
}
}
bool peeking = false;
QList<const ServerInfo_Card *> cardList;
const int cardListSize = event.cards_size();
for (int i = 0; i < cardListSize; ++i) {
const ServerInfo_Card *temp = &event.cards(i);
if (temp->face_down()) {
peeking = true;
}
cardList.append(temp);
}
if (peeking) {
for (const auto &card : cardList) {
QString cardName = QString::fromStdString(card->name());
QString providerId = QString::fromStdString(card->provider_id());
CardState *cardItem = zone->getCard(card->id());
if (!cardItem) {
continue;
}
cardItem->setCardRef({cardName, providerId});
emit logRevealCards(player, zone, card->id(), cardName, player, true, 1);
}
} else {
bool showZoneView = true;
QString cardName;
auto cardId = event.card_id_size() == 0 ? -1 : event.card_id(0);
if (cardList.size() == 1) {
cardName = QString::fromStdString(cardList.first()->name());
// Handle case of revealing top card of library in-place
if (cardId == 0 && dynamic_cast<PileZoneLogic *>(zone)) {
auto card = zone->getCards().first();
QString providerId = QString::fromStdString(cardList.first()->provider_id());
card->setCardRef({cardName, providerId});
emit zone->updateGraphics();
showZoneView = false;
}
}
if (!options.testFlag(SKIP_REVEAL_WINDOW) && showZoneView && !cardList.isEmpty()) {
emit player->requestRevealedZoneView(player, zone, cardList, event.grant_write_access());
}
emit logRevealCards(player, zone, cardId, cardName, otherPlayer, false,
event.has_number_of_cards() ? event.number_of_cards() : cardList.size(),
event.grant_write_access());
}
}
void PlayerEventHandler::eventChangeZoneProperties(const Event_ChangeZoneProperties &event)
{
CardZoneLogic *zone = player->getZone(QString::fromStdString(event.zone_name()));
if (!zone) {
return;
}
if (event.has_always_reveal_top_card()) {
zone->setAlwaysRevealTopCard(event.always_reveal_top_card());
emit logAlwaysRevealTopCard(player, zone, event.always_reveal_top_card());
}
if (event.has_always_look_at_top_card()) {
zone->setAlwaysRevealTopCard(event.always_look_at_top_card());
emit logAlwaysLookAtTopCard(player, zone, event.always_look_at_top_card());
}
}
void PlayerEventHandler::eventGameLogNotice(const Event_GameLogNotice &event)
{
Event_GameLogNotice::NoticeType type = event.notice_type();
switch (type) {
case Event_GameLogNotice::UNDO_DRAW_FAILED:
emit logUndoDrawFailed(player);
break;
default:
qWarning() << "Received Event_GameLogNotice with unknown noticeType: " << type;
}
}
void PlayerEventHandler::processGameEvent(GameEvent::GameEventType type,
const GameEvent &event,
const GameEventContext &context,
EventProcessingOptions options)
{
switch (type) {
case GameEvent::GAME_SAY:
eventGameSay(event.GetExtension(Event_GameSay::ext));
break;
case GameEvent::SHUFFLE:
eventShuffle(event.GetExtension(Event_Shuffle::ext));
break;
case GameEvent::ROLL_DIE:
eventRollDie(event.GetExtension(Event_RollDie::ext));
break;
case GameEvent::CREATE_ARROW:
eventCreateArrow(event.GetExtension(Event_CreateArrow::ext));
break;
case GameEvent::DELETE_ARROW:
eventDeleteArrow(event.GetExtension(Event_DeleteArrow::ext));
break;
case GameEvent::CREATE_TOKEN:
eventCreateToken(event.GetExtension(Event_CreateToken::ext));
break;
case GameEvent::SET_CARD_ATTR:
eventSetCardAttr(event.GetExtension(Event_SetCardAttr::ext), context, options);
break;
case GameEvent::SET_CARD_COUNTER:
eventSetCardCounter(event.GetExtension(Event_SetCardCounter::ext));
break;
case GameEvent::CREATE_COUNTER:
eventCreateCounter(event.GetExtension(Event_CreateCounter::ext));
break;
case GameEvent::SET_COUNTER:
eventSetCounter(event.GetExtension(Event_SetCounter::ext), options);
break;
case GameEvent::DEL_COUNTER:
eventDelCounter(event.GetExtension(Event_DelCounter::ext));
break;
case GameEvent::DUMP_ZONE:
eventDumpZone(event.GetExtension(Event_DumpZone::ext));
break;
case GameEvent::MOVE_CARD:
eventMoveCard(event.GetExtension(Event_MoveCard::ext), context);
break;
case GameEvent::FLIP_CARD:
eventFlipCard(event.GetExtension(Event_FlipCard::ext));
break;
case GameEvent::DESTROY_CARD:
eventDestroyCard(event.GetExtension(Event_DestroyCard::ext));
break;
case GameEvent::ATTACH_CARD:
eventAttachCard(event.GetExtension(Event_AttachCard::ext));
break;
case GameEvent::DRAW_CARDS:
eventDrawCards(event.GetExtension(Event_DrawCards::ext));
break;
case GameEvent::REVEAL_CARDS:
eventRevealCards(event.GetExtension(Event_RevealCards::ext), options);
break;
case GameEvent::CHANGE_ZONE_PROPERTIES:
eventChangeZoneProperties(event.GetExtension(Event_ChangeZoneProperties::ext));
break;
case GameEvent::GAME_LOG_NOTICE:
eventGameLogNotice(event.GetExtension(Event_GameLogNotice::ext));
break;
default: {
qWarning() << "unhandled game event" << type;
}
}
}

View file

@ -1,292 +0,0 @@
/**
* @file player_event_handler.h
* @ingroup GameLogicPlayers
* @brief Player-scoped game event handler.
*
* PlayerEventHandler applies game events that affect a single Player’s
* board state, zones, cards, counters, arrows, and related UI/log output.
*
* It is invoked by GameEventHandler after basic routing and validation.
* Each instance is bound 1:1 to a Player and must never mutate state
* belonging to other players except where explicitly required by events
* (e.g. moving cards between players, attaching cards, arrows).
*
* This class is intentionally stateful and tightly coupled to Player,
* PlayerActions, and the board/zones implementation. It performs both
* model mutation and UI-side bookkeeping (zone views, arrows, menus).
*/
#ifndef COCKATRICE_PLAYER_EVENT_HANDLER_H
#define COCKATRICE_PLAYER_EVENT_HANDLER_H
#include "event_processing_options.h"
#include <QObject>
#include <QString>
#include <libcockatrice/protocol/pb/game_event.pb.h>
#include <qtmetamacros.h>
#include <sys/types.h>
class CardState;
class CardZoneLogic;
class PlayerLogic;
class Event_AttachCard;
class Event_ChangeZoneProperties;
class Event_CreateArrow;
class Event_CreateCounter;
class Event_CreateToken;
class Event_DelCounter;
class Event_DeleteArrow;
class Event_DestroyCard;
class Event_DrawCards;
class Event_DumpZone;
class Event_FlipCard;
class Event_GameSay;
class Event_MoveCard;
class Event_RevealCards;
class Event_RollDie;
class Event_SetCardAttr;
class Event_SetCardCounter;
class Event_SetCounter;
class Event_Shuffle;
class Event_GameLogNotice;
class GameEventContext;
enum CardAttribute : int;
template <typename T> class QList;
/**
* @class PlayerEventHandler
* @brief Applies player-specific game events and emits corresponding log signals.
*
* Design notes:
* - All event handlers assume events are authoritative and already validated
* by the server.
* - Most handlers mutate both logical state (CardState, CardZoneLogic, counters)
* and visual/UI state (views, arrows, menus).
* - Logging signals are emitted *after* or *during* state mutation, depending
* on whether later mutations would invalidate log data.
*/
class PlayerEventHandler : public QObject
{
Q_OBJECT
public:
/**
* @brief Construct a PlayerEventHandler bound to a Player.
* @param player Owning player instance.
*/
explicit PlayerEventHandler(PlayerLogic *player);
/** @name Event dispatch
* @{
*/
/**
* @brief Dispatch a generic GameEvent to the appropriate handler.
*
* This is the single entry point used by GameEventHandler. It extracts
* the correct protobuf extension and forwards the event to a typed
* handler method.
*
* @param type Game event type enum.
* @param event Generic protobuf container.
* @param context Additional context (undo, judge, etc.).
* @param options Processing options (UI suppression, reveal behavior).
*/
void processGameEvent(GameEvent::GameEventType type,
const GameEvent &event,
const GameEventContext &context,
EventProcessingOptions options);
/** @} */
/** @name Chat and randomization events
* @{
*/
/// Handle in-game chat messages from this player.
void eventGameSay(const Event_GameSay &event);
/// Handle zone shuffle events (typically libraries).
void eventShuffle(const Event_Shuffle &event);
/// Handle die roll events.
void eventRollDie(const Event_RollDie &event);
/** @} */
/** @name Arrow and targeting events
* @{
*/
/// Create a visual arrow between cards or players.
void eventCreateArrow(const Event_CreateArrow &event);
/// Delete an existing arrow.
void eventDeleteArrow(const Event_DeleteArrow &event);
/** @} */
/** @name Token and card creation
* @{
*/
/// Create a token card in a target zone.
void eventCreateToken(const Event_CreateToken &event);
/** @} */
/** @name Card attribute and counter updates
* @{
*/
/**
* @brief Set a card attribute (tapped, PT, annotation, etc.).
*
* May apply to a single card or all cards in a zone if no card ID
* is provided by the event.
*/
void
eventSetCardAttr(const Event_SetCardAttr &event, const GameEventContext &context, EventProcessingOptions options);
/// Update a counter attached to a card.
void eventSetCardCounter(const Event_SetCardCounter &event);
/// Create a player-level counter.
void eventCreateCounter(const Event_CreateCounter &event);
/// Set a player-level counter value.
void eventSetCounter(const Event_SetCounter &event, EventProcessingOptions options);
/// Delete a player-level counter.
void eventDelCounter(const Event_DelCounter &event);
/** @} */
/** @name Zone-level operations
* @{
*/
/// Log a zone dump (e.g. reveal graveyard/library contents).
void eventDumpZone(const Event_DumpZone &event);
/**
* @brief Move a card between zones and/or players.
*
* This is one of the most complex handlers:
* - Removes the card from the start zone
* - Updates card identity and ownership if needed
* - Handles attachments and arrows
* - Emits appropriate move or undo-draw logs
* - Inserts the card into the target zone
*/
void eventMoveCard(const Event_MoveCard &event, const GameEventContext &context);
/// Flip a card face up or face down.
void eventFlipCard(const Event_FlipCard &event);
/// Destroy a card and clean up attachments.
void eventDestroyCard(const Event_DestroyCard &event);
/// Attach or detach a card to/from another card.
void eventAttachCard(const Event_AttachCard &event);
/** @} */
/** @name Draw and reveal operations
* @{
*/
/// Draw one or more cards from the deck.
void eventDrawCards(const Event_DrawCards &event);
/**
* @brief Reveal cards from a zone.
*
* Handles peeking, in-place top-card reveals, full reveal windows,
* and write-access granting.
*/
void eventRevealCards(const Event_RevealCards &event, EventProcessingOptions options);
/** @} */
/** @name Zone configuration
* @{
*/
/// Update zone visibility and reveal behavior.
void eventChangeZoneProperties(const Event_ChangeZoneProperties &event);
/** @} */
void eventGameLogNotice(const Event_GameLogNotice &event);
signals:
/** @name Logging signals
* @{
*/
void logSay(PlayerLogic *player, QString message);
void logShuffle(PlayerLogic *player, CardZoneLogic *zone, int start, int end);
void logRollDie(PlayerLogic *player, int sides, const QList<uint> &rolls);
void logCreateArrow(PlayerLogic *player,
PlayerLogic *startPlayer,
QString startCard,
PlayerLogic *targetPlayer,
QString targetCard,
bool _playerTarget);
void logCreateToken(PlayerLogic *player, QString cardName, QString pt, bool faceDown);
void logDrawCards(PlayerLogic *player, int number, bool deckIsEmpty);
void logUndoDraw(PlayerLogic *player, QString cardName);
void logUndoDrawFailed(PlayerLogic *player);
void logMoveCard(PlayerLogic *player,
CardState *card,
CardZoneLogic *startZone,
int oldX,
CardZoneLogic *targetZone,
int newX);
void logFlipCard(PlayerLogic *player, QString cardName, bool faceDown);
void logDestroyCard(PlayerLogic *player, QString cardName);
void logAttachCard(PlayerLogic *player, QString cardName, PlayerLogic *targetPlayer, QString targetCardName);
void logUnattachCard(PlayerLogic *player, QString cardName);
void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue);
void logSetTapped(PlayerLogic *player, CardState *card, bool tapped);
void logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue);
void logSetDoesntUntap(PlayerLogic *player, CardState *card, bool doesntUntap);
void logSetPT(PlayerLogic *player, CardState *card, QString newPT);
void logSetAnnotation(PlayerLogic *player, CardState *card, QString newAnnotation);
void logDumpZone(PlayerLogic *player, CardZoneLogic *zone, int numberCards, bool isReversed = false);
void logRevealCards(PlayerLogic *player,
CardZoneLogic *zone,
int cardId,
QString cardName,
PlayerLogic *otherPlayer,
bool faceDown,
int amount,
bool isLentToAnotherPlayer = false);
void logAlwaysRevealTopCard(PlayerLogic *player, CardZoneLogic *zone, bool reveal);
void logAlwaysLookAtTopCard(PlayerLogic *player, CardZoneLogic *zone, bool reveal);
/** @} */
void cardZoneChanged(CardState *card, bool sameZone);
void requestCardMenuUpdate(CardState *card);
/**
* @brief A card was just moved by the server and its view needs refreshing.
*
* @param card The card that moved.
* @param zoneChanged Whether the card crossed a zone boundary.
* @param newOwner The player the card now belongs to, or nullptr to leave the owner alone.
*/
void cardViewRefreshRequested(CardState *card, bool zoneChanged, PlayerLogic *newOwner);
private:
/** Owning player instance. */
PlayerLogic *player;
void setCardAttrHelper(const GameEventContext &context,
CardState *card,
CardAttribute attribute,
const QString &avalue,
bool allCards,
EventProcessingOptions options);
};
#endif // COCKATRICE_PLAYER_EVENT_HANDLER_H

View file

@ -1,10 +0,0 @@
#include "player_info.h"
#include "libcockatrice/protocol/pb/serverinfo_user.pb.h"
PlayerInfo::PlayerInfo(const ServerInfo_User &info, int _id, bool _local, bool _judge)
: id(_id), local(_local), judge(_judge)
{
userInfo = new ServerInfo_User;
userInfo->CopyFrom(info);
}

View file

@ -1,59 +0,0 @@
/**
* @file player_info.h
* @ingroup GameLogicPlayers
*/
//! \todo Document this file.
#ifndef COCKATRICE_PLAYER_INFO_H
#define COCKATRICE_PLAYER_INFO_H
#include <QObject>
#include <QString>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <qtmetamacros.h>
class PlayerInfo : public QObject
{
Q_OBJECT
public:
PlayerInfo(const ServerInfo_User &info, int id, bool local, bool judge);
ServerInfo_User *userInfo;
int id;
bool local;
bool judge;
int getId() const
{
return id;
}
ServerInfo_User *getUserInfo() const
{
return userInfo;
}
void setLocal(bool _local)
{
local = _local;
}
bool getLocal() const
{
return local;
}
bool getLocalOrJudge() const
{
return local || judge;
}
bool getJudge() const
{
return judge;
}
QString getName() const
{
return QString::fromStdString(userInfo->name());
}
};
#endif // COCKATRICE_PLAYER_INFO_H

View file

@ -1,374 +0,0 @@
#include "player_logic.h"
#include "../abstract_game.h"
#include "../board/arrow_data.h"
#include "../board/card_list.h"
#include "../board/card_state.h"
#include "../board/counter_state.h"
#include "../game_meta_info.h"
#include "../zones/hand_zone_logic.h"
#include "../zones/pile_zone_logic.h"
#include "../zones/stack_zone_logic.h"
#include "../zones/table_zone_logic.h"
#include "libcockatrice/protocol/pb/serverinfo_card.pb.h"
#include "libcockatrice/protocol/pb/serverinfo_counter.pb.h"
#include "libcockatrice/protocol/pb/serverinfo_playerproperties.pb.h"
#include "libcockatrice/utility/zone_names.h"
#include "player_actions.h"
#include "player_event_handler.h"
#include "player_info.h"
#include "player_manager.h"
#include <QDebug>
#include <QMapIterator>
#include <QMessageLogger>
#include <QMutableMapIterator>
#include <QSet>
#include <QSharedPointer>
#include <QtGlobal>
#include <libcockatrice/protocol/pb/serverinfo_player.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_zone.pb.h>
#include <libcockatrice/utility/card_ref.h>
#include <libcockatrice/utility/color.h>
#include <qalgorithms.h>
#include <string>
static CardItem *toCardItem(CardState *card)
{
return card == nullptr ? nullptr : qobject_cast<CardItem *>(card->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)),
playerEventHandler(new PlayerEventHandler(this)), playerActions(new PlayerActions(this)), active(false),
conceded(false), zoneId(0), dialogSemaphore(false)
{
initializeZones();
}
void PlayerLogic::initializeZones()
{
addZone(new PileZoneLogic(this, ZoneNames::DECK, false, true, false, this));
addZone(new PileZoneLogic(this, ZoneNames::GRAVE, false, false, true, this));
addZone(new PileZoneLogic(this, ZoneNames::EXILE, false, false, true, this));
addZone(new PileZoneLogic(this, ZoneNames::SIDEBOARD, false, false, false, this));
addZone(new TableZoneLogic(this, ZoneNames::TABLE, true, false, true, this));
addZone(new StackZoneLogic(this, ZoneNames::STACK, true, false, true, this));
bool visibleHand = playerInfo->getLocalOrJudge() ||
(game->getPlayerManager()->isSpectator() && game->getGameMetaInfo()->spectatorsOmniscient());
addZone(new HandZoneLogic(this, ZoneNames::HAND, false, false, visibleHand, this));
}
PlayerLogic::~PlayerLogic()
{
qCInfo(PlayerLog) << "Player destructor:" << getPlayerInfo()->getName();
QMapIterator<QString, CardZoneLogic *> i(zones);
while (i.hasNext()) {
delete i.next().value();
}
zones.clear();
delete getPlayerInfo()->userInfo;
}
void PlayerLogic::clear()
{
emit arrowsClearedLocally();
QMapIterator<QString, CardZoneLogic *> i(zones);
while (i.hasNext()) {
i.next().value()->clearContents();
}
clearCounters();
}
void PlayerLogic::setConceded(bool _conceded)
{
if (conceded != _conceded) {
conceded = _conceded;
if (conceded) {
clear();
}
emit concededChanged(getPlayerInfo()->getId(), conceded);
}
}
void PlayerLogic::setZoneId(int _zoneId)
{
if (zoneId != _zoneId) {
zoneId = _zoneId;
emit zoneIdChanged(zoneId);
}
}
void PlayerLogic::processPlayerInfo(const ServerInfo_Player &info)
{
static QSet<QString> builtinZones{/* PileZones */
ZoneNames::DECK, ZoneNames::GRAVE, ZoneNames::EXILE, ZoneNames::SIDEBOARD,
/* TableZone */
ZoneNames::TABLE,
/* StackZone */
ZoneNames::STACK,
/* HandZone */
ZoneNames::HAND};
clearCounters();
emit arrowsClearedLocally();
QMutableMapIterator<QString, CardZoneLogic *> zoneIt(zones);
while (zoneIt.hasNext()) {
zoneIt.next().value()->clearContents();
if (!builtinZones.contains(zoneIt.key())) {
zoneIt.remove();
}
}
emit clearCustomZonesMenu();
const int zoneListSize = info.zone_list_size();
for (int i = 0; i < zoneListSize; ++i) {
const ServerInfo_Zone &zoneInfo = info.zone_list(i);
QString zoneName = QString::fromStdString(zoneInfo.name());
CardZoneLogic *zone = zones.value(zoneName, 0);
if (!zone) {
// Create a new CardZone if it doesn't exist
if (zoneInfo.with_coords()) {
// Visibility not currently supported for TableZone
zone = addZone(new TableZoneLogic(this, zoneName, true, false, true, this));
} else {
// Zones without coordinats are always treated as non-shufflable
// PileZones, although supporting alternate hand or stack zones
// might make sense in some scenarios.
bool contentsKnown;
switch (zoneInfo.type()) {
case ServerInfo_Zone::PrivateZone:
contentsKnown =
playerInfo->getLocalOrJudge() || (game->getPlayerManager()->isSpectator() &&
game->getGameMetaInfo()->spectatorsOmniscient());
break;
case ServerInfo_Zone::PublicZone:
contentsKnown = true;
break;
case ServerInfo_Zone::HiddenZone:
contentsKnown = false;
break;
}
zone = addZone(new PileZoneLogic(this, zoneName, false, /* isShufflable */ false, contentsKnown, this));
}
// Non-builtin zones are hidden by default and can't be interacted
// with, except through menus.
emit zone->setGraphicsVisibility(false);
emit addViewCustomZoneActionToCustomZoneMenu(zoneName);
continue;
}
const int cardListSize = zoneInfo.card_list_size();
if (!cardListSize) {
for (int j = 0; j < zoneInfo.card_count(); ++j) {
emit zone->createCardRequested(ServerInfo_Card(), false);
}
} else {
for (int j = 0; j < cardListSize; ++j) {
ServerInfo_Card cardInfo = zoneInfo.card_list(j);
// Zones without coordinates (hand, piles, stack) preserve the order
// they arrive in on the server in the positions of their cards list.
// The x coordinate of such cards is always 0, so inserting at it
// would reverse the list on reconnect. Append instead.
if (!zoneInfo.with_coords()) {
cardInfo.set_x(-1);
cardInfo.set_y(-1);
}
emit zone->createCardRequested(cardInfo, false);
}
}
if (zoneInfo.has_always_reveal_top_card()) {
zone->setAlwaysRevealTopCard(zoneInfo.always_reveal_top_card());
}
zone->reorganizeCards();
}
const int counterListSize = info.counter_list_size();
for (int i = 0; i < counterListSize; ++i) {
addCounter(info.counter_list(i));
}
setConceded(info.properties().conceded());
}
void PlayerLogic::processCardAttachment(const ServerInfo_Player &info)
{
const int zoneListSize = info.zone_list_size();
for (int i = 0; i < zoneListSize; ++i) {
const ServerInfo_Zone &zoneInfo = info.zone_list(i);
CardZoneLogic *zone = zones.value(QString::fromStdString(zoneInfo.name()), 0);
if (!zone) {
continue;
}
const int cardListSize = zoneInfo.card_list_size();
for (int j = 0; j < cardListSize; ++j) {
const ServerInfo_Card &cardInfo = zoneInfo.card_list(j);
if (cardInfo.has_attach_player_id()) {
CardState *startCard = zone->getCard(cardInfo.id());
CardState *targetCard =
game->getCard(cardInfo.attach_player_id(), QString::fromStdString(cardInfo.attach_zone()),
cardInfo.attach_card_id());
if (!targetCard) {
continue;
}
// Route through CardItem::setAttachedTo to maintain the attachedCards graph and reparenting;
// CardState::setAttachedTo alone only repoints the pointer.
if (CardItem *startItem = toCardItem(startCard)) {
startItem->setAttachedTo(toCardItem(targetCard));
} else {
startCard->setAttachedTo(targetCard);
}
}
}
}
const int arrowListSize = info.arrow_list_size();
for (int i = 0; i < arrowListSize; ++i) {
emit arrowCreateRequested(QSharedPointer<ArrowData>::create(
ArrowData::fromProto(info.arrow_list(i), getPlayerInfo()->getId(), getPlayerInfo()->getLocal())));
}
}
void PlayerLogic::addCard(AbstractCardItem *card)
{
emit newCardAdded(card);
}
void PlayerLogic::deleteCard(CardState *card)
{
if (card == nullptr) {
return;
} else if (dialogSemaphore) {
cardsToDelete.append(card);
} else {
card->deleteView();
}
}
void PlayerLogic::setDeck(const DeckList &_deck)
{
deck = _deck;
emit deckChanged();
}
void PlayerLogic::setPlaymatFromProperties(const ServerInfo_PlayerProperties &props)
{
if (props.has_playmat_params() && !props.playmat_params().card_name().empty()) {
const auto &pp = props.playmat_params();
remotePlaymatCard = {QString::fromStdString(pp.card_name()), QString::fromStdString(pp.card_provider_id())};
remotePlaymatParams = {qBound(0.0, pp.margin_pct_l(), 0.95), qBound(0.0, pp.margin_pct_r(), 0.95),
qBound(0.0, pp.vertical_offset(), 1.0), qBound(0.1, pp.zoom(), 4.0)};
hasRemotePlaymat = true;
} else {
remotePlaymatCard = CardRef{};
remotePlaymatParams = PlaymatParams{};
hasRemotePlaymat = false;
}
emit playmatChanged();
}
CounterState *PlayerLogic::addCounter(const ServerInfo_Counter &counter)
{
return addCounter(counter.id(), QString::fromStdString(counter.name()),
convertColorToQColor(counter.counter_color()), counter.radius(), counter.count());
}
CounterState *PlayerLogic::addCounter(int id, const QString &name, const QColor &color, int radius, int value)
{
if (counters.contains(id)) {
return nullptr;
}
auto *state = new CounterState(id, name, color, radius, value, this);
counters.insert(id, state);
emit counterAdded(state);
return state;
}
void PlayerLogic::delCounter(int id)
{
auto *state = counters.take(id);
if (!state) {
return;
}
emit counterRemoved(id);
state->deleteLater();
}
void PlayerLogic::clearCounters()
{
for (int id : counters.keys()) {
emit counterRemoved(id);
}
qDeleteAll(counters);
counters.clear();
}
CounterState *PlayerLogic::getLifeCounter() const
{
for (auto *s : counters.values()) {
if (s->getName() == "life") {
return s;
}
}
return nullptr;
}
bool PlayerLogic::clearCardsToDelete()
{
if (cardsToDelete.isEmpty()) {
return false;
}
for (auto &i : cardsToDelete) {
if (i != nullptr) {
i->deleteView();
}
}
cardsToDelete.clear();
return true;
}
void PlayerLogic::setActive(bool _active)
{
active = _active;
emit activeChanged(active);
}
void PlayerLogic::onRequestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed)
{
emit requestZoneViewToggle(this, zoneName, numberCards, isReversed);
}
void PlayerLogic::updateZones()
{
getTableZone()->reorganizeCards();
}
void PlayerLogic::setGameStarted()
{
if (playerInfo->local) {
emit resetTopCardMenuActions();
}
setConceded(false);
}

View file

@ -1,267 +0,0 @@
/**
* @file player.h
* @ingroup GameLogicPlayers
*/
//! \todo Document this file.
#ifndef PLAYER_H
#define PLAYER_H
#include "../board/arrow_data.h"
#include "../board/counter_state.h"
#include "../zones/card_zone_logic.h"
#include "../zones/hand_zone_logic.h"
#include "../zones/pile_zone_logic.h"
#include "../zones/stack_zone_logic.h"
#include "../zones/table_zone_logic.h"
#include "libcockatrice/deck_list/deck_list.h"
#include "libcockatrice/utility/card_ref.h"
#include "libcockatrice/utility/playmat_params.h"
#include <QColor>
#include <QList>
#include <QLoggingCategory>
#include <QMap>
#include <QObject>
#include <QString>
#include <QTimer>
#include <libcockatrice/deck_list/loaded_deck.h>
#include <libcockatrice/filters/filter_string.h>
#include <libcockatrice/protocol/pb/card_attributes.pb.h>
#include <libcockatrice/protocol/pb/game_event.pb.h>
#include <libcockatrice/utility/zone_names.h>
#include <qtmetamacros.h>
// IWYU pragma: keep
// ServerInfo_Card appears inside QList<...> in signal and slot
// signatures, which the moc-generated code needs to be complete.
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
class CardItem;
class CounterState;
class QWidget;
class ServerInfo_PlayerProperties;
struct ArrowData;
struct LoadedDeck;
template <class T> class QSharedPointer;
inline Q_LOGGING_CATEGORY(PlayerLog, "player");
class AbstractCardItem;
class AbstractGame;
class PlayerInfo;
class PlayerEventHandler;
class PlayerActions;
class ServerInfo_Card;
class ServerInfo_Counter;
class ServerInfo_Player;
class ServerInfo_User;
const int MAX_TOKENS_PER_DIALOG = 99;
class PlayerLogic : public QObject
{
Q_OBJECT
signals:
void openDeckEditor(const LoadedDeck &deck);
void requestZoneViewToggle(PlayerLogic *player, const QString &zoneName, int numberCards, bool isReversed);
void requestRevealedZoneView(PlayerLogic *player,
CardZoneLogic *zone,
const QList<const ServerInfo_Card *> &cardList,
bool withWritePermission);
void deckChanged();
/** @brief Emitted when the remote playmat (card/params) is updated from player properties. */
void playmatChanged();
void newCardAdded(AbstractCardItem *card);
void requestCardMenuUpdate(CardState *card);
void counterAdded(CounterState *state);
void counterRemoved(int counterId);
void rearrangeCounters();
void activeChanged(bool active);
void zoneIdChanged(int zoneId);
void concededChanged(int playerId, bool conceded);
void clearCustomZonesMenu();
void addViewCustomZoneActionToCustomZoneMenu(QString zoneName);
void resetTopCardMenuActions();
void arrowCreateRequested(QSharedPointer<ArrowData> data);
void arrowDeleteRequested(int creatorId, int arrowId);
void arrowDeleted(int creatorId, int arrowId);
void arrowsClearedLocally(); // fires on clear() and processPlayerInfo
public slots:
void setActive(bool _active);
void onRequestZoneViewToggle(const QString &zoneName, int numberCards, bool isReversed);
public:
PlayerLogic(const ServerInfo_User &info, int _id, bool _local, bool _judge, AbstractGame *_parent);
~PlayerLogic() override;
void initializeZones();
void updateZones();
void clear();
void processPlayerInfo(const ServerInfo_Player &info);
void processCardAttachment(const ServerInfo_Player &info);
void addCard(AbstractCardItem *c);
void deleteCard(CardState *c);
bool clearCardsToDelete();
bool getActive() const
{
return active;
}
AbstractGame *getGame() const
{
return game;
}
[[nodiscard]] PlayerActions *getPlayerActions() const
{
return playerActions;
}
[[nodiscard]] PlayerEventHandler *getPlayerEventHandler() const
{
return playerEventHandler;
}
[[nodiscard]] PlayerInfo *getPlayerInfo() const
{
return playerInfo;
}
void setDeck(const DeckList &_deck);
[[nodiscard]] const DeckList &getDeck() const
{
return deck;
}
template <typename T> T *addZone(T *zone)
{
zones.insert(zone->getName(), zone);
return zone;
}
CardZoneLogic *getZone(const QString zoneName)
{
return zones.value(zoneName);
}
const QMap<QString, CardZoneLogic *> &getZones() const
{
return zones;
}
PileZoneLogic *getDeckZone()
{
return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::DECK));
}
PileZoneLogic *getGraveZone()
{
return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::GRAVE));
}
PileZoneLogic *getRfgZone()
{
return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::EXILE));
}
PileZoneLogic *getSideboardZone()
{
return qobject_cast<PileZoneLogic *>(zones.value(ZoneNames::SIDEBOARD));
}
TableZoneLogic *getTableZone()
{
return qobject_cast<TableZoneLogic *>(zones.value(ZoneNames::TABLE));
}
StackZoneLogic *getStackZone()
{
return qobject_cast<StackZoneLogic *>(zones.value(ZoneNames::STACK));
}
HandZoneLogic *getHandZone()
{
return qobject_cast<HandZoneLogic *>(zones.value(ZoneNames::HAND));
}
CounterState *addCounter(const ServerInfo_Counter &counter);
CounterState *addCounter(int id, const QString &name, const QColor &color, int radius, int value);
void delCounter(int counterId);
void clearCounters();
QMap<int, CounterState *> getCounters() const
{
return counters;
}
/**
* Gets the counter that represents the life total.
*/
CounterState *getLifeCounter() const;
void setConceded(bool _conceded);
bool getConceded() const
{
return conceded;
}
void setGameStarted();
void setDialogSemaphore(const bool _active)
{
dialogSemaphore = _active;
}
int getZoneId() const
{
return zoneId;
}
void setZoneId(int _zoneId);
void setPlaymatFromProperties(const ServerInfo_PlayerProperties &props);
const CardRef &getRemotePlaymatCard() const
{
return remotePlaymatCard;
}
const PlaymatParams &getRemotePlaymatParams() const
{
return remotePlaymatParams;
}
bool getHasRemotePlaymat() const
{
return hasRemotePlaymat;
}
private:
AbstractGame *game;
PlayerInfo *playerInfo;
PlayerEventHandler *playerEventHandler;
PlayerActions *playerActions;
bool active;
bool conceded;
DeckList deck;
int zoneId;
QMap<QString, CardZoneLogic *> zones;
QMap<int, CounterState *> counters;
bool dialogSemaphore;
QList<CardState *> cardsToDelete;
// Playmat from player properties (for opponent display)
CardRef remotePlaymatCard;
PlaymatParams remotePlaymatParams;
bool hasRemotePlaymat = false;
};
#endif

View file

@ -1,100 +0,0 @@
#include "player_manager.h"
#include "../abstract_game.h"
#include "../game_state.h"
#include "player_info.h"
#include "player_logic.h"
#include <QList>
#include <QMapIterator>
#include <QSharedPointer>
PlayerManager::PlayerManager(AbstractGame *_game,
int _localPlayerId,
bool _localPlayerIsJudge,
bool localPlayerIsSpectator)
: QObject(_game), game(_game), players(QMap<int, PlayerLogic *>()), localPlayerId(_localPlayerId),
localPlayerIsJudge(_localPlayerIsJudge), localPlayerIsSpectator(localPlayerIsSpectator)
{
}
bool PlayerManager::isMainPlayerConceded() const
{
PlayerLogic *player = players.value(localPlayerId, nullptr);
return player && player->getConceded();
}
PlayerLogic *PlayerManager::getActiveLocalPlayer(int activePlayer) const
{
PlayerLogic *active = players.value(activePlayer, 0);
if (active) {
if (active->getPlayerInfo()->getLocal()) {
return active;
}
}
QMapIterator<int, PlayerLogic *> playerIterator(players);
while (playerIterator.hasNext()) {
PlayerLogic *temp = playerIterator.next().value();
if (temp->getPlayerInfo()->getLocal()) {
return temp;
}
}
return nullptr;
}
bool PlayerManager::isLocalPlayer(int playerId)
{
return game->getGameState()->getIsLocalGame() || playerId == localPlayerId;
}
PlayerLogic *PlayerManager::addPlayer(int playerId, const ServerInfo_User &info)
{
auto *newPlayer = new PlayerLogic(info, playerId, isLocalPlayer(playerId) || game->getGameState()->getIsLocalGame(),
isJudge(), getGame());
connect(newPlayer, &PlayerLogic::concededChanged, this, &PlayerManager::onPlayerConceded);
players.insert(playerId, newPlayer);
emit playerAdded(newPlayer);
emit playerCountChanged();
return newPlayer;
}
void PlayerManager::removePlayer(int playerId)
{
PlayerLogic *player = getPlayer(playerId);
if (!player) {
return;
}
emit playerRemoved(player);
emit playerCountChanged();
players.remove(playerId);
player->deleteLater();
}
PlayerLogic *PlayerManager::getPlayer(int playerId) const
{
PlayerLogic *player = players.value(playerId, 0);
if (!player) {
return nullptr;
}
return player;
}
void PlayerManager::clearSpectators()
{
const QList<int> spectatorIds = spectators.keys();
for (int spectatorId : spectatorIds) {
removeSpectator(spectatorId);
}
}
void PlayerManager::onPlayerConceded(int playerId, bool conceded)
{
// Everything else cares about this
if (conceded) {
emit playerConceded(playerId);
} else {
emit playerUnconceded(playerId);
}
}

View file

@ -1,128 +0,0 @@
/**
* @file player_manager.h
* @ingroup GameLogicPlayers
*/
//! \todo Document this file.
#ifndef COCKATRICE_PLAYER_MANAGER_H
#define COCKATRICE_PLAYER_MANAGER_H
#include "libcockatrice/protocol/pb/serverinfo_user.pb.h"
#include <QMap>
#include <QObject>
#include <QString>
#include <libcockatrice/protocol/pb/serverinfo_playerproperties.pb.h>
#include <qtmetamacros.h>
class AbstractGame;
class PlayerLogic;
class PlayerManager : public QObject
{
Q_OBJECT
public:
PlayerManager(AbstractGame *_game, int _localPlayerId, bool _localPlayerIsJudge, bool localPlayerIsSpectator);
AbstractGame *game;
QMap<int, PlayerLogic *> players;
int localPlayerId;
bool localPlayerIsJudge;
bool localPlayerIsSpectator;
QMap<int, ServerInfo_User> spectators;
[[nodiscard]] bool isSpectator() const
{
return localPlayerIsSpectator;
}
[[nodiscard]] bool isJudge() const
{
return localPlayerIsJudge;
}
[[nodiscard]] int getLocalPlayerId() const
{
return localPlayerId;
}
[[nodiscard]] const QMap<int, PlayerLogic *> &getPlayers() const
{
return players;
}
[[nodiscard]] int getPlayerCount() const
{
return players.size();
}
[[nodiscard]] PlayerLogic *getActiveLocalPlayer(int activePlayer) const;
bool isLocalPlayer(int playerId);
PlayerLogic *addPlayer(int playerId, const ServerInfo_User &info);
void removePlayer(int playerId);
[[nodiscard]] PlayerLogic *getPlayer(int playerId) const;
void onPlayerConceded(int playerId, bool conceded);
[[nodiscard]] bool isMainPlayerConceded() const;
[[nodiscard]] bool isLocalPlayer(int playerId) const
{
return playerId == getLocalPlayerId();
}
[[nodiscard]] const QMap<int, ServerInfo_User> &getSpectators() const
{
return spectators;
}
[[nodiscard]] ServerInfo_User getSpectator(int playerId) const
{
return spectators.value(playerId);
}
[[nodiscard]] QString getSpectatorName(int spectatorId) const
{
return QString::fromStdString(spectators.value(spectatorId).name());
}
void addSpectator(int spectatorId, const ServerInfo_PlayerProperties &prop)
{
if (!spectators.contains(spectatorId)) {
spectators.insert(spectatorId, prop.user_info());
emit spectatorAdded(prop);
}
}
void removeSpectator(int spectatorId)
{
ServerInfo_User spectatorInfo = spectators.value(spectatorId);
spectators.remove(spectatorId);
emit spectatorRemoved(spectatorId, spectatorInfo);
}
/** @brief Remove all spectators, emitting the removal signal for each. */
void clearSpectators();
[[nodiscard]] AbstractGame *getGame() const
{
return game;
}
signals:
void playerAdded(PlayerLogic *player);
void playerRemoved(PlayerLogic *player);
void activeLocalPlayerConceded();
void activeLocalPlayerUnconceded();
void playerConceded(int playerId);
void playerUnconceded(int playerId);
void playerCountChanged();
void spectatorAdded(ServerInfo_PlayerProperties spectator);
void spectatorRemoved(int spectatorId, ServerInfo_User spectator);
};
#endif // COCKATRICE_PLAYER_MANAGER_H

View file

@ -1,23 +0,0 @@
/**
* @file token_info.h
* @ingroup GameLogicPlayers
*/
//! \todo Document this file.
#ifndef COCKATRICE_TOKEN_INFO_H
#define COCKATRICE_TOKEN_INFO_H
#include <QString>
struct TokenInfo
{
QString name;
QString color;
QString pt;
QString annotation;
bool destroy = true;
bool faceDown = false;
QString providerId;
};
#endif // COCKATRICE_TOKEN_INFO_H

View file

@ -1,18 +0,0 @@
#include "replay.h"
#include "game_meta_info.h"
#include "game_state.h"
#include "player/player_manager.h"
#include <QList>
class GameReplay;
class QObject;
Replay::Replay(QObject *_parent, const GameReplay *_replay, bool isLocalGame) : AbstractGame(_parent)
{
gameState = new GameState(this, 0, -1, isLocalGame, {}, false, false, -1, false);
connect(gameMetaInfo, &GameMetaInfo::startedChanged, gameState, &GameState::onStartedChanged);
playerManager = new PlayerManager(this, -1, false, true);
loadReplay(_replay);
}

View file

@ -1,26 +0,0 @@
/**
* @file replay.h
* @ingroup GameLogic
* @ingroup Replay
*/
//! \todo Document this file.
#ifndef COCKATRICE_REPLAY_H
#define COCKATRICE_REPLAY_H
#include "abstract_game.h"
#include <qtmetamacros.h>
class GameReplay;
class QObject;
class Replay : public AbstractGame
{
Q_OBJECT
public:
explicit Replay(QObject *_parent, const GameReplay *_replay, bool isLocalGame);
};
#endif // COCKATRICE_REPLAY_H

View file

@ -1,43 +0,0 @@
#ifndef COCKATRICE_CARD_ZONE_ALGORITHMS_H
#define COCKATRICE_CARD_ZONE_ALGORITHMS_H
namespace CardZoneAlgorithms
{
/**
* Shared insertion logic for zones where cards follow the standard pattern:
* clamp index, insert, clear identity if contents unknown, reset state.
* Visibility and parenting are handled by the graphics zone's onCardAdded().
*
* Zones with different post-add behavior (signal connections, positional resets,
* hidden cards, or coordinate-based placement) should NOT use this — implement
* addCardImpl directly instead.
*
* Template parameters allow testing with lightweight mocks that avoid Qt graphics
* dependencies.
*
* @tparam CardList Must provide: size() -> int, insert(int, CardType*),
* getContentsKnown() -> bool
* @tparam CardType Must provide: setId(int), setCardRef(CardRefType), resetState(bool)
* @param keepAnnotations Forwarded to card->resetState(). Stack-like zones preserve
* annotations across zone transitions; hand-like zones clear them.
*/
template <typename CardList, typename CardType>
void addCardToList(CardList &cards, CardType *card, int x, bool keepAnnotations)
{
if (x < 0 || x >= cards.size()) {
x = static_cast<int>(cards.size());
}
cards.insert(x, card);
if (!cards.getContentsKnown()) {
card->setId(-1);
card->setCardRef({});
}
card->resetState(keepAnnotations);
}
} // namespace CardZoneAlgorithms
#endif // COCKATRICE_CARD_ZONE_ALGORITHMS_H

View file

@ -1,234 +0,0 @@
#include "card_zone_logic.h"
#include "../../client/translation.h"
#include "../../game_graphics/zones/card_zone.h"
#include "../board/card_list.h"
#include "../board/card_state.h"
#include "../player/player_actions.h"
#include "../player/player_info.h"
#include "../player/player_logic.h"
#include "libcockatrice/card/database/card_database.h"
#include "view_zone_logic.h"
#include <QAction>
#include <QDebug>
#include <QMessageLogger>
#include <QSharedPointer>
#include <QVariant>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/protocol/pb/command_move_card.pb.h>
#include <libcockatrice/utility/zone_names.h>
#include <string>
/**
* @param _player the player that the zone belongs to
* @param _name internal name of the zone
* @param _hasCardAttr whether cards in the zone can have attributes set on them
* @param _isShufflable whether it makes sense to shuffle this zone by default after viewing it
* @param _contentsKnown whether the cards in the zone are known to the client
* @param parent the parent QObject.
*/
CardZoneLogic::CardZoneLogic(PlayerLogic *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QObject *parent)
: QObject(parent), player(_player), name(_name), cards(_contentsKnown), views{}, hasCardAttr(_hasCardAttr),
isShufflable(_isShufflable)
{
// If we join a game before the card db finishes loading, the cards might have the wrong printings.
// Force refresh all cards in the zone when db finishes loading to fix that.
connect(CardDatabaseManager::getInstance(), &CardDatabase::cardDatabaseLoadingFinished, this,
&CardZoneLogic::refreshCardInfos);
}
void CardZoneLogic::addCard(CardState *card, const bool reorganize, const int x, const int y)
{
if (!card) {
qCWarning(CardZoneLogicLog) << "CardZoneLogic::addCard() card is null; this shouldn't normally happen";
return;
}
for (auto *viewLogic : views) {
if (viewLogic->prepareAddCard(x)) {
ServerInfo_Card cardInfo;
const CardRef &cardRef = card->getCardRef();
cardInfo.set_name(cardRef.name.toStdString());
cardInfo.set_provider_id(cardRef.providerId.toStdString());
cardInfo.set_id(card->getId());
cardInfo.set_face_down(card->getFaceDown());
cardInfo.set_x(x);
cardInfo.set_y(y);
emit viewLogic->createCardRequested(cardInfo, reorganize);
}
}
card->setZone(this);
emit cardAdded(card);
addCardImpl(card, x, y);
if (reorganize) {
emit reorganizeCards();
}
emit cardCountChanged();
}
CardState *CardZoneLogic::takeCard(int position, int cardId, bool toNewZone)
{
if (position == -1) {
// position == -1 means either that the zone is indexed by card id
// or that it doesn't matter which card you take.
for (int i = 0; i < cards.size(); ++i) {
if (cards[i]->getId() == cardId) {
position = i;
break;
}
}
if (position == -1) {
position = 0;
}
}
if (position >= cards.size()) {
return nullptr;
}
for (auto *viewLogic : views) {
viewLogic->removeCard(position, toNewZone);
}
CardState *c = cards.takeAt(position);
c->setId(cardId);
emit reorganizeCards();
emit cardCountChanged();
return c;
}
CardState *CardZoneLogic::getCard(int cardId)
{
CardState *c = cards.findCard(cardId);
if (!c) {
qCWarning(CardZoneLogicLog) << "CardZoneLogic::getCard: card id=" << cardId << "not found";
return nullptr;
}
// If the card's id is -1, this zone is invisible,
// so we need to give the card an id as it comes out.
// It can be assumed that in an invisible zone, all cards are equal.
if (c->getId() == -1) {
c->setId(cardId);
}
return c;
}
void CardZoneLogic::removeCard(CardState *card)
{
if (!card) {
qCWarning(CardZoneLogicLog) << "CardZoneLogic::removeCard: card is null, this shouldn't normally happen";
return;
}
cards.removeOne(card);
emit reorganizeCards();
emit cardCountChanged();
player->deleteCard(card);
}
void CardZoneLogic::refreshCardInfos()
{
for (const auto &cardState : cards) {
cardState->refreshCardInfo();
}
}
void CardZoneLogic::moveAllToZone()
{
QList<QVariant> data = static_cast<QAction *>(sender())->data().toList();
if (data.length() < 2) {
return;
}
QString targetZone = data[0].toString();
int targetX = data[1].toInt();
Command_MoveCard cmd;
cmd.set_start_zone(getName().toStdString());
cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone(targetZone.toStdString());
cmd.set_x(targetX);
for (int i = 0; i < cards.size(); ++i) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(cards[i]->getId());
}
player->getPlayerActions()->sendGameCommand(cmd);
}
void CardZoneLogic::clearContents()
{
// First gather the cards into a safe temporary list.
const CardList toClear = cards;
// Detach and notify attached cards and zones *before* deleting anything.
for (CardState *card : toClear) {
// If an incorrectly implemented server doesn't return attached cards to whom they belong before dropping a
// player, we have to return them to avoid a crash.
const QList<CardState *> &attachedCards = card->getAttachedCards();
for (CardState *attachedCard : attachedCards) {
emit attachedCard->getZone()->cardAdded(attachedCard);
}
}
// Now request deletions after all manipulations are done.
for (CardState *card : toClear) {
player->deleteCard(card);
}
cards.clear();
emit cardCountChanged();
}
QString CardZoneLogic::getTranslatedName(bool theirOwn, GrammaticalCase gc) const
{
QString ownerName = player->getPlayerInfo()->getName();
if (name == ZoneNames::HAND) {
return (theirOwn ? tr("their hand", "nominative") : tr("%1's hand", "nominative").arg(ownerName));
} else if (name == ZoneNames::DECK) {
switch (gc) {
case CaseLookAtZone:
return (theirOwn ? tr("their library", "look at zone")
: tr("%1's library", "look at zone").arg(ownerName));
case CaseTopCardsOfZone:
return (theirOwn ? tr("of their library", "top cards of zone,")
: tr("of %1's library", "top cards of zone").arg(ownerName));
case CaseRevealZone:
return (theirOwn ? tr("their library", "reveal zone")
: tr("%1's library", "reveal zone").arg(ownerName));
case CaseShuffleZone:
return (theirOwn ? tr("their library", "shuffle") : tr("%1's library", "shuffle").arg(ownerName));
default:
return (theirOwn ? tr("their library", "nominative") : tr("%1's library", "nominative").arg(ownerName));
}
} else if (name == ZoneNames::GRAVE) {
return (theirOwn ? tr("their graveyard", "nominative") : tr("%1's graveyard", "nominative").arg(ownerName));
} else if (name == ZoneNames::EXILE) {
return (theirOwn ? tr("their exile", "nominative") : tr("%1's exile", "nominative").arg(ownerName));
} else if (name == ZoneNames::SIDEBOARD) {
switch (gc) {
case CaseLookAtZone:
return (theirOwn ? tr("their sideboard", "look at zone")
: tr("%1's sideboard", "look at zone").arg(ownerName));
case CaseNominative:
return (theirOwn ? tr("their sideboard", "nominative")
: tr("%1's sideboard", "nominative").arg(ownerName));
default:
break;
}
} else {
return (theirOwn ? tr("their custom zone '%1'", "nominative").arg(name)
: tr("%1's custom zone '%2'", "nominative").arg(ownerName).arg(name));
}
return QString();
}

View file

@ -1,127 +0,0 @@
/**
* @file card_zone_logic.h
* @ingroup GameLogicZones
*/
//! \todo Document this file.
#ifndef COCKATRICE_CARD_ZONE_LOGIC_H
#define COCKATRICE_CARD_ZONE_LOGIC_H
#include "../../client/translation.h"
#include "../board/card_list.h"
#include <QList>
#include <QLoggingCategory>
#include <QObject>
#include <QString>
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
#include <qtmetamacros.h>
inline Q_LOGGING_CATEGORY(CardZoneLogicLog, "card_zone_logic");
class PlayerLogic;
class ZoneViewZoneLogic;
class QMenu;
class QAction;
class QPainter;
class CardDragItem;
class CardZoneLogic : public QObject
{
Q_OBJECT
signals:
void cardAdded(CardState *addedCard);
/** @brief Asks the graphics side to build the card item for @p cardInfo and add it to this zone. */
void createCardRequested(const ServerInfo_Card &cardInfo, bool reorganize);
void cardCountChanged();
void reorganizeCards();
void updateGraphics();
void setGraphicsVisibility(bool visible);
void retranslateUi();
public:
explicit CardZoneLogic(PlayerLogic *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QObject *parent = nullptr);
void addCard(CardState *card, bool reorganize, int x, int y = -1);
// getCard() finds a card by id.
CardState *getCard(int cardId);
void removeCard(CardState *card);
// takeCard() finds a card by position and removes it from the zone and from all of its views.
virtual CardState *takeCard(int position, int cardId, bool canResize = true);
void rawInsertCard(CardState *card, int index)
{
cards.insert(index, card);
emit cardCountChanged();
}
[[nodiscard]] const CardList &getCards() const
{
return cards;
}
void sortCards(const QList<CardList::SortOption> &options)
{
cards.sortBy(options);
emit cardCountChanged();
}
[[nodiscard]] QString getName() const
{
return name;
}
[[nodiscard]] QString getTranslatedName(bool theirOwn, GrammaticalCase gc) const;
[[nodiscard]] PlayerLogic *getPlayer() const
{
return player;
}
[[nodiscard]] bool contentsKnown() const
{
return cards.getContentsKnown();
}
QList<ZoneViewZoneLogic *> &getViews()
{
return views;
}
void setAlwaysRevealTopCard(bool _alwaysRevealTopCard)
{
alwaysRevealTopCard = _alwaysRevealTopCard;
}
[[nodiscard]] bool getAlwaysRevealTopCard() const
{
return alwaysRevealTopCard;
}
[[nodiscard]] bool getHasCardAttr() const
{
return hasCardAttr;
}
[[nodiscard]] bool getIsShufflable() const
{
return isShufflable;
}
void clearContents();
public slots:
void moveAllToZone();
private slots:
void refreshCardInfos();
protected:
PlayerLogic *player;
QString name;
CardList cards;
QList<ZoneViewZoneLogic *> views;
bool hasCardAttr;
bool isShufflable;
bool alwaysRevealTopCard;
virtual void addCardImpl(CardState *card, int x, int y) = 0;
};
#endif // COCKATRICE_CARD_ZONE_LOGIC_H

View file

@ -1,22 +0,0 @@
#include "hand_zone_logic.h"
#include "../board/card_state.h"
#include "card_zone_algorithms.h"
class PlayerLogic;
class QObject;
HandZoneLogic::HandZoneLogic(PlayerLogic *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QObject *parent)
: CardZoneLogic(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent)
{
}
void HandZoneLogic::addCardImpl(CardState *card, int x, int /*y*/)
{
CardZoneAlgorithms::addCardToList(cards, card, x, false);
}

View file

@ -1,32 +0,0 @@
/**
* @file hand_zone_logic.h
* @ingroup GameLogicZones
*/
//! \todo Document this file.
#ifndef COCKATRICE_HAND_ZONE_LOGIC_H
#define COCKATRICE_HAND_ZONE_LOGIC_H
#include "card_zone_logic.h"
#include <QString>
#include <qtmetamacros.h>
class PlayerLogic;
class QObject;
class HandZoneLogic : public CardZoneLogic
{
Q_OBJECT
public:
HandZoneLogic(PlayerLogic *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QObject *parent = nullptr);
protected:
void addCardImpl(CardState *card, int x, int y) override;
};
#endif // COCKATRICE_HAND_ZONE_LOGIC_H

View file

@ -1,32 +0,0 @@
#include "pile_zone_logic.h"
#include "../board/card_state.h"
PileZoneLogic::PileZoneLogic(PlayerLogic *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QObject *parent)
: CardZoneLogic(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent)
{
}
void PileZoneLogic::addCardImpl(CardState *card, int x, int /*y*/)
{
connect(card, &CardState::cardPixmapUpdated, this, &PileZoneLogic::callUpdate);
// if x is negative set it to add at end
if (x < 0 || x >= cards.size()) {
x = cards.size();
}
cards.insert(x, card);
if (!contentsKnown()) {
card->setCardRef({});
card->setId(-1);
// If we obscure a previously revealed card, its name has to be forgotten
if (cards.size() > x + 1) {
cards.at(x + 1)->setCardRef({});
}
}
card->resetState(false);
}

View file

@ -1,37 +0,0 @@
/**
* @file pile_zone_logic.h
* @ingroup GameLogicZones
*/
//! \todo Document this file.
#ifndef COCKATRICE_PILE_ZONE_LOGIC_H
#define COCKATRICE_PILE_ZONE_LOGIC_H
#include "card_zone_logic.h"
#include <QString>
#include <qtmetamacros.h>
class PlayerLogic;
class QObject;
class PileZoneLogic : public CardZoneLogic
{
Q_OBJECT
signals:
void callUpdate();
public:
PileZoneLogic(PlayerLogic *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QObject *parent = nullptr);
protected:
void addCardImpl(CardState *card, int x, int y) override;
};
#endif // COCKATRICE_PILE_ZONE_LOGIC_H

View file

@ -1,22 +0,0 @@
#include "stack_zone_logic.h"
#include "../board/card_state.h"
#include "card_zone_algorithms.h"
class PlayerLogic;
class QObject;
StackZoneLogic::StackZoneLogic(PlayerLogic *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QObject *parent)
: CardZoneLogic(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent)
{
}
void StackZoneLogic::addCardImpl(CardState *card, int x, int /*y*/)
{
CardZoneAlgorithms::addCardToList(cards, card, x, true);
}

View file

@ -1,32 +0,0 @@
/**
* @file stack_zone_logic.h
* @ingroup GameLogicZones
*/
//! \todo Document this file.
#ifndef COCKATRICE_STACK_ZONE_LOGIC_H
#define COCKATRICE_STACK_ZONE_LOGIC_H
#include "card_zone_logic.h"
#include <QString>
#include <qtmetamacros.h>
class PlayerLogic;
class QObject;
class StackZoneLogic : public CardZoneLogic
{
Q_OBJECT
public:
StackZoneLogic(PlayerLogic *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QObject *parent = nullptr);
protected:
void addCardImpl(CardState *card, int x, int y) override;
};
#endif // COCKATRICE_STACK_ZONE_LOGIC_H

View file

@ -1,56 +0,0 @@
#include "table_zone_logic.h"
#include "../board/card_state.h"
#include <QPoint>
TableZoneLogic::TableZoneLogic(PlayerLogic *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QObject *parent)
: CardZoneLogic(_player, _name, _hasCardAttr, _isShufflable, _contentsKnown, parent)
{
}
void TableZoneLogic::addCardImpl(CardState *card, int _x, int _y)
{
cards.append(card);
if (!card->getFaceDown() && card->getPT().isEmpty()) {
card->setPT(card->getCardInfo().getPowTough());
}
if (card->getCardInfo().getUiAttributes().cipt && card->getCardInfo().getUiAttributes().landscapeOrientation) {
card->setDoesntUntap(true);
}
card->setGridPoint(QPoint(_x, _y));
}
CardState *TableZoneLogic::takeCard(int position, int cardId, bool toNewZone)
{
CardState *result = CardZoneLogic::takeCard(position, cardId);
if (toNewZone) {
emit contentSizeChanged();
}
return result;
}
int TableZoneLogic::clampValidTableRow(const int row)
{
if (row < 0) {
return 0;
}
if (row >= ROW_COUNT) {
return ROW_COUNT - 1;
}
return row;
}
int TableZoneLogic::tableRowToGridY(int tableRow)
{
if (tableRow > 2) {
tableRow = 1;
}
return clampValidTableRow(2 - tableRow);
}

View file

@ -1,61 +0,0 @@
/**
* @file table_zone_logic.h
* @ingroup GameLogicZones
*/
//! \todo Document this file.
#ifndef COCKATRICE_TABLE_ZONE_LOGIC_H
#define COCKATRICE_TABLE_ZONE_LOGIC_H
#include "card_zone_logic.h"
#include <QString>
#include <qtmetamacros.h>
class PlayerLogic;
class QObject;
class TableZoneLogic : public CardZoneLogic
{
Q_OBJECT
signals:
void contentSizeChanged();
void toggleTapped();
public:
TableZoneLogic(PlayerLogic *_player,
const QString &_name,
bool _hasCardAttr,
bool _isShufflable,
bool _contentsKnown,
QObject *parent = nullptr);
/** @brief Number of rows in the table zone grid (0=creatures, 1=noncreatures, 2=lands). */
static const int ROW_COUNT = 3;
/**
* Clamps a grid row index into [0, ROW_COUNT - 1].
*/
static int clampValidTableRow(const int row);
/**
* Converts a card's logical table row (0=creatures, 1=noncreatures, 2=lands)
* to the corresponding grid Y coordinate. Cards with tableRow > 2 (e.g.,
* instants/sorceries) default to the noncreatures row.
*/
static int tableRowToGridY(int tableRow);
protected:
void addCardImpl(CardState *card, int x, int y) override;
/**
* @brief Removes a card from view.
*
* @param position card position
* @param cardId id of card to take
* @param toNewZone Whether the destination of the card is not the same as the starting zone. Defaults to true
* @return CardState that has been removed
*/
CardState *takeCard(int position, int cardId, bool toNewZone = true) override;
};
#endif // COCKATRICE_TABLE_ZONE_LOGIC_H

View file

@ -1,195 +0,0 @@
#include "view_zone_logic.h"
#include "../../client/settings/cache_settings.h"
#include "../board/card_state.h"
#include <QtGlobal>
#include <libcockatrice/settings/interface_settings.h>
class PlayerLogic;
class QObject;
/**
* @param _player the player that the cards are revealed to.
* @param _origZone the zone the cards were revealed from.
* @param _revealZone if false, the cards will be face down.
* @param _writeableRevealZone whether the player can interact with the revealed cards.
*/
ZoneViewZoneLogic::ZoneViewZoneLogic(PlayerLogic *_player,
CardZoneLogic *_origZone,
int _numberCards,
bool _revealZone,
bool _writeableRevealZone,
bool _isReversed,
QObject *parent)
: CardZoneLogic(_player, _origZone->getName(), false, false, true, parent), origZone(_origZone),
numberCards(_numberCards), revealZone(_revealZone), writeableRevealZone(_writeableRevealZone),
isReversed(_isReversed)
{
}
/**
* Checks if inserting a card at the given position requires an actual new card to be created and added to the view.
* Also does any cardId updates that would be required if a card is inserted in that position.
*
* Note that this method can end up modifying the cardIds despite returning false.
* (for example, if the card is inserted into a hidden portion of the deck while the view is reversed)
*
* Make sure to call this method once before calling addCard(), so that you skip creating the card and calling
* addCard() if it's not required.
*
* @param x The position to insert the card at.
* @return Whether to proceed with calling addCard.
*/
bool ZoneViewZoneLogic::prepareAddCard(int x)
{
bool doInsert = false;
if (!isReversed) {
if (x <= cards.size() || cards.size() == -1) {
doInsert = true;
}
} else {
// map x (which is in origZone indexes) to this viewZone's cardList index
int firstId = cards.isEmpty() ? origZone->getCards().size() : cards.front()->getId();
int insertionIndex = x - firstId;
if (insertionIndex >= 0) {
// card was put into a portion of the deck that's in the view
doInsert = true;
} else {
// card was put into a portion of the deck that's not in the view; update ids but don't insert card
updateCardIds(ADD_CARD);
}
}
// autoclose check is done both here and in removeCard
if (cards.isEmpty() && !doInsert && SettingsCache::instance().userInterface().getCloseEmptyCardView()) {
emit closeView();
}
return doInsert;
}
/**
* Make sure prepareAddCard() was called before calling addCard().
* This method assumes we already checked that the card is being inserted into the visible portion
*/
void ZoneViewZoneLogic::addCardImpl(CardState *card, int x, int /*y*/)
{
if (!isReversed) {
// if x is negative set it to add at end
// if x is out-of-bounds then also set it to add at the end
if (x < 0 || x >= cards.size()) {
x = cards.size();
}
cards.insert(x, card);
} else {
// map x (which is in origZone indexes) to this viewZone's cardList index
int firstId = cards.isEmpty() ? origZone->getCards().size() : cards.front()->getId();
int insertionIndex = x - firstId;
// qMin to prevent out-of-bounds error when bottoming a card that is already in the view
cards.insert(qMin(insertionIndex, cards.size()), card);
}
updateCardIds(ADD_CARD);
reorganizeCards();
}
void ZoneViewZoneLogic::updateCardIds(CardAction action)
{
if (origZone->contentsKnown()) {
return;
}
if (cards.isEmpty()) {
return;
}
int cardCount = cards.size();
auto startId = 0;
if (isReversed) {
// the card has not been added to origZone's cardList at this point
startId = origZone->getCards().size() - cardCount;
switch (action) {
case INITIALIZE:
break;
case ADD_CARD:
startId += 1;
break;
case REMOVE_CARD:
startId -= 1;
break;
}
}
for (int i = 0; i < cardCount; ++i) {
cards[i]->setId(i + startId);
}
}
void ZoneViewZoneLogic::removeCard(int position, bool toNewZone)
{
if (isReversed) {
position -= cards.first()->getId();
if (position < 0 || position >= cards.size()) {
updateCardIds(REMOVE_CARD);
return;
}
}
if (position >= cards.size()) {
return;
}
CardState *card = cards.takeAt(position);
card->deleteView();
// The toNewZone check is to prevent the view from auto-closing if the view contains only a single card and that
// card gets dragged within the view.
// Another autoclose check is done in prepareAddCard so that the view autocloses if the last card was moved to an
// unrevealed portion of the same zone.
if (cards.isEmpty() && SettingsCache::instance().userInterface().getCloseEmptyCardView() && toNewZone) {
emit closeView();
return;
}
updateCardIds(REMOVE_CARD);
reorganizeCards();
}
void ZoneViewZoneLogic::requestClose()
{
emit closeView();
}
void ZoneViewZoneLogic::clearCards()
{
for (auto card : cards) {
card->deleteView();
}
cards.clear();
emit cardCountChanged();
}
void ZoneViewZoneLogic::setWriteableRevealZone(bool _writeableRevealZone)
{
if (writeableRevealZone && !_writeableRevealZone) {
emit addToViews();
} else if (!writeableRevealZone && _writeableRevealZone) {
emit removeFromViews();
}
writeableRevealZone = _writeableRevealZone;
}
PendingCommand *ZoneViewZoneLogic::requestZoneDump()
{
PlayerActions *actions = getPlayer()->getPlayerActions();
PendingCommand *pend = actions->prepareZoneDump(this, numberCards);
if (pend != nullptr) {
actions->sendGameCommand(pend);
}
return pend;
}

View file

@ -1,98 +0,0 @@
/**
* @file view_zone_logic.h
* @ingroup GameLogicZones
*/
//! \todo Document this file.
#ifndef COCKATRICE_VIEW_ZONE_LOGIC_H
#define COCKATRICE_VIEW_ZONE_LOGIC_H
#include "card_zone_logic.h"
#include <qtmetamacros.h>
class PlayerLogic;
class QObject;
class PendingCommand;
class ZoneViewZoneLogic : public CardZoneLogic
{
Q_OBJECT
signals:
void addToViews();
void removeFromViews();
void closeView();
private:
CardZoneLogic *origZone;
int numberCards;
bool revealZone, writeableRevealZone;
bool isReversed;
public:
enum CardAction
{
INITIALIZE,
ADD_CARD,
REMOVE_CARD
};
ZoneViewZoneLogic(PlayerLogic *_player,
CardZoneLogic *_origZone,
int _numberCards,
bool _revealZone,
bool _writeableRevealZone,
bool _isReversed,
QObject *parent = nullptr);
bool prepareAddCard(int x);
void removeCard(int position, bool toNewZone);
void updateCardIds(CardAction action);
/**
* @brief Asks the graphics view showing this zone to close itself.
*
* Logic code has no handle on the view, so it goes through the logic object the view was built from.
*/
void requestClose();
/** @brief Removes all cards from the view, without touching anything else. Used in replay rewind */
void clearCards();
int getNumberCards() const
{
return numberCards;
}
bool getRevealZone() const
{
return revealZone;
}
bool getWriteableRevealZone() const
{
return writeableRevealZone;
}
void setWriteableRevealZone(bool _writeableRevealZone);
bool getIsReversed() const
{
return isReversed;
}
CardZoneLogic *getOriginalZone() const
{
return origZone;
}
/**
* @brief Prepares and sends the zone-dump command for this view.
* @return The pending command, or nullptr when there is no zone to dump.
*
* Keeps command construction in game/ so graphics views don't reach
* through the player into PlayerActions to set it up. The command is sent
* asynchronously, so callers connect to finished() after calling this.
*/
PendingCommand *requestZoneDump();
protected:
void addCardImpl(CardState *card, int x, int y) override;
};
#endif // COCKATRICE_VIEW_ZONE_LOGIC_H

View file

@ -7,11 +7,18 @@
#ifndef ABSTRACTCARDITEM_H
#define ABSTRACTCARDITEM_H
#include "../../game/board/card_state.h"
#include "../animated_item.h"
#include "arrow_target.h"
#include "graphics_item_type.h"
#include <QColor>
#include <QGraphicsItem>
#include <QSizeF>
#include <QString>
#include <QtGlobal>
#include <libcockatrice/game/board/card_state.h>
#include <qtmetamacros.h>
class PlayerLogic;
class CardInfo;
class QPainter;

View file

@ -2,10 +2,6 @@
#include "../../client/settings/cache_settings.h"
#include "../../client/settings/shortcuts_settings.h"
#include "../../game/board/counter_state.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game_graphics/board/translate_counter_name.h"
#include "../../interface/widgets/menus/tearoff_menu.h"
@ -23,6 +19,10 @@
#include <QString>
#include <QVariant>
#include <QtGlobal>
#include <libcockatrice/game/board/counter_state.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/utility/expression.h>
#include <qnamespace.h>

View file

@ -2,11 +2,6 @@
#include "arrow_item.h"
#include "../../client/settings/cache_settings.h"
#include "../../game/board/arrow_data.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../game_scene.h"
#include "../player/player_target.h"
#include "../z_values.h"
@ -27,6 +22,11 @@
#include <QTransform>
#include <QtGlobal>
#include <QtMath>
#include <libcockatrice/game/board/arrow_data.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/settings/cards_display_settings.h>
#include <libcockatrice/utility/zone_names.h>
#include <math.h>

View file

@ -1,7 +1,6 @@
#ifndef ARROWITEM_H
#define ARROWITEM_H
#include "../../game/board/arrow_data.h"
#include "../animated_item.h"
#include "arrow_target.h"
#include "graphics_item_type.h"
@ -18,6 +17,7 @@
#include <QRectF>
#include <QSharedPointer>
#include <QtGlobal>
#include <libcockatrice/game/board/arrow_data.h>
#include <qtmetamacros.h>
class CardItem;

View file

@ -1,17 +1,6 @@
#include "card_item.h"
#include "../../client/settings/cache_settings.h"
#include "../../game/abstract_game.h"
#include "../../game/board/card_state.h"
#include "../../game/game_state.h"
#include "../../game/phase.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/player/player_manager.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/table_zone_logic.h"
#include "../../game/zones/view_zone_logic.h"
#include "../card_dimensions.h"
#include "../game_scene.h"
#include "arrow_item.h"
@ -41,6 +30,17 @@
#include <QVariant>
#include <QtGlobal>
#include <libcockatrice/card/card_info.h>
#include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/game/board/card_state.h>
#include <libcockatrice/game/game_state.h>
#include <libcockatrice/game/phase.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/player/player_manager.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/table_zone_logic.h>
#include <libcockatrice/game/zones/view_zone_logic.h>
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
#include <libcockatrice/settings/interface_settings.h>
#include <libcockatrice/utility/card_ref.h>

View file

@ -7,7 +7,6 @@
#ifndef CARDITEM_H
#define CARDITEM_H
#include "../../game/board/card_state.h"
#include "abstract_card_item.h"
#include "graphics_item_type.h"
@ -16,6 +15,7 @@
#include <QPointF>
#include <QString>
#include <QVariantList>
#include <libcockatrice/game/board/card_state.h>
#include <qtmetamacros.h>
class CardDragItem;

View file

@ -7,12 +7,11 @@
#ifndef DLG_CREATETOKEN_H
#define DLG_CREATETOKEN_H
#include "../../game/player/token_info.h"
#include <QDialog>
#include <QList>
#include <QString>
#include <QStringList>
#include <libcockatrice/game/player/token_info.h>
#include <qtmetamacros.h>
class QLabel;

View file

@ -7,12 +7,11 @@
#ifndef DLG_MOVE_TOP_CARDS_UNTIL_H
#define DLG_MOVE_TOP_CARDS_UNTIL_H
#include "../../game/player/move_top_cards_until_options.h"
#include <QDialog>
#include <QList>
#include <QString>
#include <QStringList>
#include <libcockatrice/game/player/move_top_cards_until_options.h>
#include <qtmetamacros.h>
class FilterString;

View file

@ -1,15 +1,6 @@
#include "game_scene.h"
#include "../client/settings/cache_settings.h"
#include "../game/abstract_game.h"
#include "../game/arrow_registry.h"
#include "../game/board/arrow_data.h"
#include "../game/player/player_actions.h"
#include "../game/player/player_event_handler.h"
#include "../game/player/player_info.h"
#include "../game/player/player_logic.h"
#include "../game/zones/card_zone_logic.h"
#include "../game/zones/view_zone_logic.h"
#include "animated_item.h"
#include "board/abstract_card_item.h"
#include "board/arrow_item.h"
@ -41,6 +32,14 @@
#include <QtMath>
#include <algorithm>
#include <functional>
#include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/game/board/arrow_data.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_event_handler.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/view_zone_logic.h>
#include <libcockatrice/settings/interface_settings.h>
#include <libcockatrice/utility/zone_names.h>
#include <numeric>

View file

@ -4,15 +4,6 @@
#include "../../client/settings/card_counter_settings.h"
#include "../../client/sound_engine.h"
#include "../../client/translation.h"
#include "../../game/abstract_game.h"
#include "../../game/board/card_list.h"
#include "../../game/game_state.h"
#include "../../game/phase.h"
#include "../../game/player/player_event_handler.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/player/player_manager.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../interface/card_localization.h"
#include "../../interface/widgets/server/chat_view/chat_view.h"
#include "../board/card_item.h"
@ -24,6 +15,15 @@
#include <QList>
#include <QSharedPointer>
#include <QStringList>
#include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/game_state.h>
#include <libcockatrice/game/phase.h>
#include <libcockatrice/game/player/player_event_handler.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/player/player_manager.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/protocol/pb/context_move_card.pb.h>
#include <libcockatrice/protocol/pb/context_mulligan.pb.h>
#include <libcockatrice/settings/chat_settings.h>

View file

@ -3,15 +3,7 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/card_counter_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../../../game/abstract_game.h"
#include "../../../game/player/card_menu_action_type.h"
#include "../../../game/player/player_info.h"
#include "../../../game/player/player_manager.h"
#include "../../../game/zones/card_zone_logic.h"
#include "../../board/card_item.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/view_zone_logic.h"
#include "../../game_scene.h"
#include "../player_graphics_item.h"
#include "libcockatrice/card/card_info.h"
@ -35,6 +27,14 @@
#include <QtGlobal>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/relation/card_relation.h>
#include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/game/player/card_menu_action_type.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/player/player_manager.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/view_zone_logic.h>
#include <libcockatrice/utility/zone_names.h>
#include <qnamespace.h>
#include <utility>

View file

@ -1,13 +1,13 @@
#include "custom_zone_menu.h"
#include "../../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game_scene.h"
#include "../player_graphics_item.h"
#include <QAction>
#include <QList>
#include <QVariant>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
CustomZoneMenu::CustomZoneMenu(PlayerGraphicsItem *_player) : player(_player)
{

View file

@ -2,13 +2,7 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../../../game/player/player_info.h"
#include "../../../game/player/player_manager.h"
#include "../../../game/zones/pile_zone_logic.h"
#include "../../../interface/widgets/menus/tearoff_menu.h"
#include "../../game/abstract_game.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../player_graphics_item.h"
#include <QAction>
@ -19,6 +13,12 @@
#include <QObject>
#include <QString>
#include <QVariant>
#include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/player/player_manager.h>
#include <libcockatrice/game/zones/pile_zone_logic.h>
#include <libcockatrice/utility/zone_names.h>
class QWidget;

View file

@ -2,14 +2,7 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../../../game/board/card_list.h"
#include "../../../game/player/player_info.h"
#include "../../../game/player/player_manager.h"
#include "../../../game/zones/hand_zone_logic.h"
#include "../../../interface/widgets/menus/tearoff_menu.h"
#include "../../game/abstract_game.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../player_graphics_item.h"
#include <QAction>
@ -20,6 +13,13 @@
#include <QObject>
#include <QString>
#include <QVariant>
#include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/player/player_manager.h>
#include <libcockatrice/game/zones/hand_zone_logic.h>
#include <libcockatrice/utility/zone_names.h>
class QWidget;

View file

@ -2,14 +2,7 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../../../game/board/card_list.h"
#include "../../../game/player/player_info.h"
#include "../../../game/player/player_manager.h"
#include "../../../game/zones/pile_zone_logic.h"
#include "../../../interface/widgets/menus/tearoff_menu.h"
#include "../../game/abstract_game.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../player_graphics_item.h"
#include <QAction>
@ -23,6 +16,13 @@
#include <QObject>
#include <QString>
#include <QVariant>
#include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/player/player_manager.h>
#include <libcockatrice/game/zones/pile_zone_logic.h>
class QWidget;

View file

@ -2,13 +2,13 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../../../game/player/card_menu_action_type.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../../game_scene.h"
#include "../player_graphics_item.h"
#include <QAction>
#include <libcockatrice/game/player/card_menu_action_type.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_logic.h>
MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to"))
{

View file

@ -2,12 +2,12 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../../game_scene.h"
#include "../player_graphics_item.h"
#include <QAction>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_logic.h>
PtMenu::PtMenu(PlayerGraphicsItem *player) : QMenu(tr("Power / toughness"))
{

View file

@ -1,10 +1,6 @@
#include "rfg_menu.h"
#include "../../../game/player/player_info.h"
#include "../../../game/zones/pile_zone_logic.h"
#include "../../../interface/widgets/menus/tearoff_menu.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../player_graphics_item.h"
#include <QAction>
@ -12,6 +8,10 @@
#include <QMenu>
#include <QString>
#include <QVariant>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/pile_zone_logic.h>
#include <libcockatrice/utility/zone_names.h>
class QWidget;

View file

@ -1,14 +1,14 @@
#include "say_menu.h"
#include "../../../client/settings/cache_settings.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../player_graphics_item.h"
#include <QAction>
#include <QKeySequence>
#include <QList>
#include <QString>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/settings/message_settings.h>
SayMenu::SayMenu(PlayerGraphicsItem *_player) : player(_player)

View file

@ -2,13 +2,13 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../../../game/player/player_info.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../player_graphics_item.h"
#include <QAction>
#include <QKeySequence>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
SideboardMenu::SideboardMenu(PlayerGraphicsItem *player, QMenu *playerMenu) : QMenu(playerMenu)
{

View file

@ -2,9 +2,6 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../../../game/player/player_info.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../../game_scene.h"
#include "../player_graphics_item.h"
#include "libcockatrice/card/card_info.h"
@ -14,6 +11,9 @@
#include <QSet>
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
UtilityMenu::UtilityMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) : QMenu(playerMenu), player(_player)
{

View file

@ -1,15 +1,6 @@
#include "player_graphics_item.h"
#include "../../client/settings/cache_settings.h"
#include "../../game/board/counter_state.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/hand_zone_logic.h"
#include "../../game/zones/pile_zone_logic.h"
#include "../../game/zones/stack_zone_logic.h"
#include "../../game/zones/table_zone_logic.h"
#include "../../interface/card_picture_loader/card_picture_loader.h"
#include "../../interface/widgets/cards/art_crop_attribution.h"
#include "../../interface/widgets/cards/card_art_utils.h"
@ -45,6 +36,15 @@
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list.h>
#include <libcockatrice/deck_list/playmat_resolver.h>
#include <libcockatrice/game/board/counter_state.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/hand_zone_logic.h>
#include <libcockatrice/game/zones/pile_zone_logic.h>
#include <libcockatrice/game/zones/stack_zone_logic.h>
#include <libcockatrice/game/zones/table_zone_logic.h>
#include <libcockatrice/settings/interface_settings.h>
#include <qnamespace.h>
#include <qnumeric.h>

View file

@ -1,8 +1,6 @@
#include "player_target.h"
#include "../../client/settings/cache_settings.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../interface/pixel_map_generator.h"
#include "../board/abstract_counter.h"
#include "../board/abstract_graphics_item.h"
@ -30,6 +28,8 @@
#include <QTransform>
#include <QtGlobal>
#include <QtMath>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
#include <qnamespace.h>
#include <qnumeric.h>

View file

@ -1,11 +1,6 @@
#include "hand_zone.h"
#include "../../client/settings/cache_settings.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/hand_zone_logic.h"
#include "../../interface/theme_manager.h"
#include "../board/card_drag_item.h"
#include "../board/card_item.h"
@ -18,6 +13,11 @@
#include <QPointF>
#include <QRectF>
#include <QString>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/hand_zone_logic.h>
#include <libcockatrice/settings/interface_settings.h>
#include <string>

View file

@ -7,10 +7,10 @@
#ifndef HANDZONE_H
#define HANDZONE_H
#include "../../game/board/card_list.h"
#include "select_zone.h"
#include <QtGlobal>
#include <libcockatrice/game/board/card_list.h>
#include <qtmetamacros.h>
class HandZoneLogic;

View file

@ -1,12 +1,6 @@
#include "pile_zone.h"
#include "../../client/settings/cache_settings.h"
#include "../../game/board/card_list.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/pile_zone_logic.h"
#include "../board/card_drag_item.h"
#include "../board/card_item.h"
#include "../card_dimensions.h"
@ -24,6 +18,12 @@
#include <QString>
#include <QTransform>
#include <QtGlobal>
#include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/pile_zone_logic.h>
#include <libcockatrice/settings/cards_display_settings.h>
#include <qnamespace.h>
#include <string>

View file

@ -1,11 +1,5 @@
#include "stack_zone.h"
#include "../../game/board/card_list.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/stack_zone_logic.h"
#include "../../interface/theme_manager.h"
#include "../board/card_drag_item.h"
#include "../board/card_item.h"
@ -19,6 +13,12 @@
#include <QPoint>
#include <QRectF>
#include <QString>
#include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/stack_zone_logic.h>
#include <qnumeric.h>
#include <string>

View file

@ -1,12 +1,6 @@
#include "table_zone.h"
#include "../../client/settings/cache_settings.h"
#include "../../game/board/card_list.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/table_zone_logic.h"
#include "../../interface/theme_manager.h"
#include "../board/abstract_card_item.h"
#include "../board/card_drag_item.h"
@ -32,6 +26,12 @@
#include <algorithm>
#include <iterator>
#include <libcockatrice/card/card_info.h>
#include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/table_zone_logic.h>
#include <libcockatrice/settings/interface_settings.h>
#include <libcockatrice/utility/zone_names.h>
#include <string>

View file

@ -1,11 +1,6 @@
#include "view_zone.h"
#include "../../client/settings/cache_settings.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_info.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/view_zone_logic.h"
#include "../board/card_drag_item.h"
#include "../board/card_item.h"
#include "../card_dimensions.h"
@ -26,6 +21,11 @@
#include <QtMath>
#include <functional>
#include <libcockatrice/card/card_localization.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/view_zone_logic.h>
#include <libcockatrice/protocol/pb/response_dump_zone.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
#include <libcockatrice/protocol/pending_command.h>

View file

@ -7,7 +7,6 @@
#ifndef ZONEVIEWERZONE_H
#define ZONEVIEWERZONE_H
#include "../../game/board/card_list.h"
#include "select_zone.h"
#include <QGraphicsLayoutItem>
@ -17,6 +16,7 @@
#include <QSizeF>
#include <QString>
#include <libcockatrice/filters/filter_string.h>
#include <libcockatrice/game/board/card_list.h>
#include <qnamespace.h>
#include <qtmetamacros.h>

View file

@ -3,11 +3,6 @@
#include "../../client/settings/cache_settings.h"
#include "../../client/translation.h"
#include "../../filters/syntax_help.h"
#include "../../game/board/card_list.h"
#include "../../game/player/player_actions.h"
#include "../../game/player/player_logic.h"
#include "../../game/zones/card_zone_logic.h"
#include "../../game/zones/view_zone_logic.h"
#include "../../interface/pixel_map_generator.h"
#include "../card_dimensions.h"
#include "../z_values.h"
@ -44,6 +39,11 @@
#include <QVariant>
#include <QWidget>
#include <QtGlobal>
#include <libcockatrice/game/board/card_list.h>
#include <libcockatrice/game/player/player_actions.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/zones/card_zone_logic.h>
#include <libcockatrice/game/zones/view_zone_logic.h>
#include <libcockatrice/settings/cards_display_settings.h>
#include <libcockatrice/settings/interface_settings.h>
#include <qnamespace.h>

View file

@ -1,11 +1,10 @@
#ifndef COCKATRICE_REPLAY_MANAGER_H
#define COCKATRICE_REPLAY_MANAGER_H
#include "../../../game/player/event_processing_options.h"
#include <QList>
#include <QObject>
#include <QtGlobal>
#include <libcockatrice/game/player/event_processing_options.h>
#include <qtmetamacros.h>
class GameReplay;

View file

@ -2,16 +2,7 @@
#include "../../../client/settings/cache_settings.h"
#include "../../../client/settings/shortcuts_settings.h"
#include "../../../game/abstract_game.h"
#include "../../../game/game_event_handler.h"
#include "../../../game/game_meta_info.h"
#include "../../../game/game_state.h"
#include "../../../game/player/player_info.h"
#include "../../../game/player/player_manager.h"
#include "../../../game_graphics/board/abstract_card_item.h"
#include "../game/game.h"
#include "../game/player/player_logic.h"
#include "../game/replay.h"
#include "../game_graphics/deckview/deck_view_container.h"
#include "../game_graphics/deckview/tabbed_deck_view_container.h"
#include "../game_graphics/game_scene.h"
@ -71,6 +62,15 @@
#include <QWidget>
#include <QtGlobal>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/game/abstract_game.h>
#include <libcockatrice/game/game.h>
#include <libcockatrice/game/game_event_handler.h>
#include <libcockatrice/game/game_meta_info.h>
#include <libcockatrice/game/game_state.h>
#include <libcockatrice/game/player/player_info.h>
#include <libcockatrice/game/player/player_logic.h>
#include <libcockatrice/game/player/player_manager.h>
#include <libcockatrice/game/replay.h>
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/serverinfo_player.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>