mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
Merge branch 'master' into tooomm-sentry_bump
This commit is contained in:
commit
d9b0ee92ca
224 changed files with 6721 additions and 2501 deletions
|
|
@ -244,17 +244,27 @@ void ConnectionController::onLoginError(int r,
|
|||
{
|
||||
switch (static_cast<Response::ResponseCode>(r)) {
|
||||
case Response::RespClientUpdateRequired: {
|
||||
QString formatted = "Missing Features: ";
|
||||
for (int i = 0; i < missingFeatures.size(); ++i) {
|
||||
formatted.append(QString("\n %1").arg(QChar(0x2022)) + " " + missingFeatures.value(i));
|
||||
}
|
||||
|
||||
QMessageBox msgBox(dialogParent);
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setWindowTitle(tr("Failed Login"));
|
||||
msgBox.setText(tr("Your client seems to be missing features this server requires for connection.") +
|
||||
"\n\n" + tr("To update your client, go to 'Help -> Check for Client Updates'."));
|
||||
msgBox.setDetailedText(formatted);
|
||||
|
||||
if (missingFeatures.isEmpty()) {
|
||||
// Without a feature list this is either a login the client stopped itself or a
|
||||
// strict server asking for a password; the reason string already says which and
|
||||
// what to do. No client update can fix any of those causes, so show it instead of
|
||||
// the update advice.
|
||||
msgBox.setText(reasonStr.isEmpty() ? tr("The login did not complete.") : reasonStr);
|
||||
} else {
|
||||
QString formatted = tr("Missing Features:");
|
||||
for (const QString &feature : missingFeatures) {
|
||||
formatted.append(QString("\n %1 ").arg(QChar(0x2022)) + feature);
|
||||
}
|
||||
|
||||
msgBox.setText(tr("Your client seems to be missing features this server requires for connection.") +
|
||||
"\n\n" + tr("To update your client, go to 'Help -> Check for Client Updates'."));
|
||||
msgBox.setDetailedText(formatted);
|
||||
}
|
||||
|
||||
msgBox.exec();
|
||||
break;
|
||||
}
|
||||
|
|
@ -517,14 +527,38 @@ void ConnectionController::onForgotPasswordSuccess()
|
|||
SettingsCache::instance().servers().setFPPlayerName("");
|
||||
}
|
||||
|
||||
void ConnectionController::onForgotPasswordError()
|
||||
void ConnectionController::onForgotPasswordError(int responseCode)
|
||||
{
|
||||
QMessageBox::warning(
|
||||
dialogParent, tr("Reset Password"),
|
||||
tr("Failed to reset user account password, please contact the server operator to reset your password."));
|
||||
SettingsCache::instance().servers().setFPHostName("");
|
||||
SettingsCache::instance().servers().setFPPort("");
|
||||
SettingsCache::instance().servers().setFPPlayerName("");
|
||||
// Rate limiting is retryable, so it must not clear the prefill.
|
||||
bool keepPrefill = false;
|
||||
QString message;
|
||||
switch (static_cast<Response::ResponseCode>(responseCode)) {
|
||||
case Response::RespTooManyRequests:
|
||||
// Retryable: keep the host/port/user prefill so the user can try again. The window
|
||||
// is server-configured (an hour by default), so do not promise a shorter wait.
|
||||
keepPrefill = true;
|
||||
message = tr("Too many password reset attempts from your address. Please wait before "
|
||||
"requesting a new reset.");
|
||||
break;
|
||||
case Response::RespFunctionNotAllowed:
|
||||
// Also returned for an unknown user, a banned user, and an expired token or
|
||||
// challenge, so the message must not claim a single cause.
|
||||
message = tr("The server did not accept the reset request. Check the user name, or contact "
|
||||
"the server operator to reset your password.");
|
||||
break;
|
||||
default:
|
||||
message = tr("Failed to reset user account password, please contact the server operator to reset your "
|
||||
"password.");
|
||||
break;
|
||||
}
|
||||
|
||||
QMessageBox::warning(dialogParent, tr("Reset Password"), message);
|
||||
|
||||
if (!keepPrefill) {
|
||||
SettingsCache::instance().servers().setFPHostName("");
|
||||
SettingsCache::instance().servers().setFPPort("");
|
||||
SettingsCache::instance().servers().setFPPlayerName("");
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionController::onPromptForgotPasswordReset()
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ private slots:
|
|||
|
||||
// Forgot-password flow
|
||||
void onForgotPasswordSuccess();
|
||||
void onForgotPasswordError();
|
||||
void onForgotPasswordError(int responseCode);
|
||||
void onPromptForgotPasswordReset();
|
||||
void onPromptForgotPasswordChallenge();
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ inline Q_LOGGING_CATEGORY(SettingsCacheLog, "settings_cache");
|
|||
|
||||
class ReleaseChannel;
|
||||
|
||||
#define DEFAULT_LANG_NAME "English"
|
||||
#define CLIENT_INFO_NOT_SET "notset"
|
||||
|
||||
#define DEFAULT_FONT_SIZE 12
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef TRANSLATION_H
|
||||
#define TRANSLATION_H
|
||||
|
||||
enum GrammaticalCase
|
||||
{
|
||||
CaseNominative,
|
||||
CaseLookAtZone,
|
||||
CaseTopCardsOfZone,
|
||||
CaseRevealZone,
|
||||
CaseShuffleZone
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -2,13 +2,18 @@
|
|||
#define COCKATRICE_SETTINGS_CARD_PREFERENCE_PROVIDER_H
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <libcockatrice/interfaces/interface_card_preference_provider.h>
|
||||
#include <libcockatrice/settings/card_override_settings.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
|
||||
class SettingsCardPreferenceProvider : public ICardPreferenceProvider
|
||||
class SettingsCardPreferenceProvider : public QObject, public ICardPreferenceProvider
|
||||
{
|
||||
public:
|
||||
explicit SettingsCardPreferenceProvider(QObject *parent = nullptr) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] QString getCardPreferenceOverride(const QString &cardName) const override
|
||||
{
|
||||
return SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardName);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include "deck_filter_string.h"
|
||||
|
||||
#include "../interface/deck_loader/loaded_deck.h"
|
||||
#include "libcockatrice/card/card_localization.h"
|
||||
#include "libcockatrice/card/database/card_database_querier.h"
|
||||
#include "libcockatrice/deck_list/deck_list.h"
|
||||
|
|
@ -15,6 +14,7 @@
|
|||
#include <algorithm>
|
||||
#include <any>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <libcockatrice/filters/filter_string.h>
|
||||
#include <libcockatrice/utility/peglib.h>
|
||||
#include <mutex>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <QString>
|
||||
#include <functional>
|
||||
#include <libcockatrice/card/card_localization.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
|
||||
struct CardSearchLanguage;
|
||||
struct LoadedDeck;
|
||||
|
|
|
|||
|
|
@ -1,69 +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);
|
||||
|
||||
activeCard = nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void AbstractGame::setActiveCard(CardItem *card)
|
||||
{
|
||||
activeCard = card;
|
||||
}
|
||||
|
||||
CardItem *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);
|
||||
}
|
||||
|
|
@ -1,70 +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 CardItem;
|
||||
class AbstractClient;
|
||||
class GameEventHandler;
|
||||
class GameMetaInfo;
|
||||
class GameReplay;
|
||||
class GameState;
|
||||
class PlayerManager;
|
||||
|
||||
class AbstractGame : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AbstractGame(QObject *parent);
|
||||
|
||||
GameMetaInfo *gameMetaInfo;
|
||||
GameState *gameState;
|
||||
GameEventHandler *gameEventHandler;
|
||||
PlayerManager *playerManager;
|
||||
CardItem *activeCard;
|
||||
|
||||
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);
|
||||
|
||||
CardItem *getCard(int playerId, const QString &zoneName, int cardId) const;
|
||||
|
||||
void setActiveCard(CardItem *card);
|
||||
CardItem *getActiveCard() const
|
||||
{
|
||||
return activeCard;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_ABSTRACT_GAME_H
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
#include "card_list.h"
|
||||
|
||||
#include "../../game_graphics/board/card_item.h"
|
||||
#include "libcockatrice/card/printing/exact_card.h"
|
||||
|
||||
#include <QChar>
|
||||
#include <QDebug>
|
||||
#include <QMessageLogger>
|
||||
#include <QSharedPointer>
|
||||
#include <algorithm>
|
||||
#include <compare>
|
||||
#include <libcockatrice/card/card_info.h>
|
||||
|
||||
CardList::CardList(bool _contentsKnown) : QList<CardItem *>(), contentsKnown(_contentsKnown)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds the CardItem 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 CardItem, or a nullptr if not found.
|
||||
*/
|
||||
CardItem *CardList::findCard(const int cardId) const
|
||||
{
|
||||
if (!contentsKnown && !empty()) {
|
||||
return at(0);
|
||||
} else {
|
||||
for (auto *cardItem : *this) {
|
||||
if (cardItem->getId() == cardId) {
|
||||
return cardItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sorts the list by using string comparison on properties extracted from the CardItem
|
||||
* 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](CardItem *a, CardItem *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 CardItem.
|
||||
*/
|
||||
std::function<QString(CardItem *)> CardList::getExtractorFor(SortOption option)
|
||||
{
|
||||
switch (option) {
|
||||
case NoSort:
|
||||
return [](CardItem *) { return ""; };
|
||||
case SortByMainType:
|
||||
return [](CardItem *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 [](CardItem *c) { return c->getCard() ? c->getCardInfo().getCmc().rightJustified(4, '0') : ""; };
|
||||
case SortByColorGrouping:
|
||||
return [](CardItem *c) { return c->getCard() ? getColorSortString(c->getCardInfo(), false) : ""; };
|
||||
case SortByName:
|
||||
return [](CardItem *c) { return c->getName(); };
|
||||
case SortByType:
|
||||
return [](CardItem *c) { return c->getCardInfo().getCardType(); };
|
||||
case SortByManaCost:
|
||||
return [](CardItem *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 [](CardItem *c) { return c->getCard() ? getColorSortString(c->getCardInfo(), true) : ""; };
|
||||
case SortByPt:
|
||||
// do the same padding trick as above
|
||||
return
|
||||
[](CardItem *c) { return c->getCard() ? c->getCardInfo().getPowTough().rightJustified(10, '0') : ""; };
|
||||
case SortBySet:
|
||||
return [](CardItem *c) { return c->getCardInfo().getSetsNames(); };
|
||||
case SortByPrinting:
|
||||
return [](CardItem *c) { return c->getProviderId(); };
|
||||
}
|
||||
|
||||
// this line should never be reached
|
||||
qCWarning(CardListLog) << "cardlist.cpp: Could not find extractor for SortOption" << option;
|
||||
return [](CardItem *) { return ""; };
|
||||
}
|
||||
|
|
@ -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 CardItem;
|
||||
|
||||
class CardList : public QList<CardItem *>
|
||||
{
|
||||
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);
|
||||
CardItem *findCard(const int cardId) const;
|
||||
bool getContentsKnown() const
|
||||
{
|
||||
return contentsKnown;
|
||||
}
|
||||
|
||||
void sortBy(const QList<SortOption> &options);
|
||||
|
||||
static std::function<QString(CardItem *)> getExtractorFor(SortOption option);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
#include "card_state.h"
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
void CardState::resetState(bool keepAnnotations)
|
||||
{
|
||||
attacking = false;
|
||||
counters.clear();
|
||||
pt.clear();
|
||||
if (!keepAnnotations) {
|
||||
annotation.clear();
|
||||
}
|
||||
attachedTo = nullptr;
|
||||
}
|
||||
|
||||
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(CardItem *_attachedTo)
|
||||
{
|
||||
if (attachedTo == _attachedTo) {
|
||||
return;
|
||||
}
|
||||
attachedTo = _attachedTo;
|
||||
emit attachedToChanged(_attachedTo);
|
||||
emit stateChanged();
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
#ifndef COCKATRICE_CARD_STATE_H
|
||||
#define COCKATRICE_CARD_STATE_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class CardZoneLogic;
|
||||
class CardItem;
|
||||
|
||||
class CardState : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
bool attacking = false;
|
||||
QMap<int, int> counters;
|
||||
QString annotation;
|
||||
QString pt;
|
||||
bool doesntUntap = false;
|
||||
bool destroyOnZoneChange = false;
|
||||
|
||||
CardItem *attachedTo = nullptr;
|
||||
CardZoneLogic *zone = nullptr;
|
||||
|
||||
signals:
|
||||
void stateChanged();
|
||||
|
||||
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(CardItem *newAttachedTo);
|
||||
void zoneChanged(CardState *changedCard, CardZoneLogic *newZone);
|
||||
|
||||
public:
|
||||
explicit CardState(QObject *parent, CardZoneLogic *_zone) : QObject(parent), zone(_zone)
|
||||
{
|
||||
}
|
||||
|
||||
void resetState(bool keepAnnotations);
|
||||
|
||||
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);
|
||||
|
||||
bool getDoesntUntap() const
|
||||
{
|
||||
return doesntUntap;
|
||||
}
|
||||
|
||||
void setDoesntUntap(bool _doesntUntap);
|
||||
|
||||
bool getDestroyOnZoneChange() const
|
||||
{
|
||||
return destroyOnZoneChange;
|
||||
}
|
||||
|
||||
void setDestroyOnZoneChange(bool _destroyOnZoneChange);
|
||||
|
||||
CardItem *getAttachedTo() const
|
||||
{
|
||||
return attachedTo;
|
||||
}
|
||||
|
||||
void setAttachedTo(CardItem *_attachedTo);
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_CARD_STATE_H
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1,577 +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_delete_arrow.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_reverse_turn.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_active_phase.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 <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::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();
|
||||
}
|
||||
|
|
@ -1,384 +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/serverinfo_player.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user.pb.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 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);
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @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
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
#include "game_meta_info.h"
|
||||
|
||||
#include "abstract_game.h"
|
||||
|
||||
GameMetaInfo::GameMetaInfo(AbstractGame *parent) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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');
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,262 +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 "../../game_graphics/board/card_item.h" // IWYU pragma: keep - moc instantiates the metatype for QList<CardItem *>, which Qt 6.4 needs complete
|
||||
#include "../../game_graphics/dialogs/dlg_create_token.h"
|
||||
#include "../../game_graphics/dialogs/dlg_move_top_cards_until.h"
|
||||
#include "../../game_graphics/player/card_menu_action_type.h"
|
||||
#include "../board/card_list.h"
|
||||
#include "libcockatrice/card/card_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 Command_MoveCard;
|
||||
class PendingCommand;
|
||||
class PlayerLogic;
|
||||
|
||||
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);
|
||||
|
||||
void moveOneCardUntil(CardItem *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 CardItem *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(CardItem *c, bool faceDown);
|
||||
void playCardToTable(const CardItem *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 CardItem *sourceCard, const CardRelation *cardRelation);
|
||||
bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation, int variableCount);
|
||||
void onRelatedCardCreated(const CardItem *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<CardItem *> selectedCards);
|
||||
void actPlayFacedown(QList<CardItem *> selectedCards);
|
||||
void actHide(QList<CardItem *> 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 actSelectAll();
|
||||
void actSelectRow();
|
||||
void actSelectColumn();
|
||||
|
||||
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();
|
||||
void actCreateAllRelatedCards();
|
||||
|
||||
void actRequestMoveCardXCardsFromTopDialog();
|
||||
void actMoveCardXCardsFromTop(QList<CardItem *> selectedCards, int number);
|
||||
void actRemoveCardCounter(QList<CardItem *> selectedCards, int counterId);
|
||||
void actAddCardCounter(QList<CardItem *> selectedCards, int counterId);
|
||||
void actRequestSetCardCounterDialog(QList<CardItem *> selectedCards, int counterId);
|
||||
void actSetCardCounter(QList<CardItem *> selectedCards, int counterId, const QString &counterValue);
|
||||
void actIncrementAllCardCounters(QList<CardItem *> cardsToUpdate);
|
||||
void actAttach();
|
||||
void actUnattach(QList<CardItem *> selectedCards);
|
||||
void actDrawArrow();
|
||||
void actIncPT(QList<CardItem *> selectedCards, int deltaP, int deltaT);
|
||||
void actResetPT(QList<CardItem *> selectedCards);
|
||||
void actRequestSetPTDialog(QList<CardItem *> selectedCards);
|
||||
void actSetPT(QList<CardItem *> selectedCards, const QString &pt);
|
||||
void actIncP(QList<CardItem *> selectedCards);
|
||||
void actDecP(QList<CardItem *> selectedCards);
|
||||
void actIncT(QList<CardItem *> selectedCards);
|
||||
void actDecT(QList<CardItem *> selectedCards);
|
||||
void actIncPT(QList<CardItem *> selectedCards);
|
||||
void actDecPT(QList<CardItem *> selectedCards);
|
||||
void actFlowP(QList<CardItem *> selectedCards);
|
||||
void actFlowT(QList<CardItem *> selectedCards);
|
||||
|
||||
void actReduceLifeByPower(QList<CardItem *> selectedCards);
|
||||
|
||||
void actRequestSetAnnotationDialog(QList<CardItem *> selectedCards);
|
||||
void actSetAnnotation(QList<CardItem *> selectedCards, const QString &annotation);
|
||||
void actReveal(QList<CardItem *> selectedCards, QAction *action);
|
||||
void actRevealHand(int revealToPlayerId);
|
||||
void actRevealRandomHandCard(int revealToPlayerId);
|
||||
void actRevealLibrary(int revealToPlayerId);
|
||||
|
||||
void actSortHand();
|
||||
|
||||
void cardMenuAction(QList<CardItem *> 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 CardItem *sourceCard,
|
||||
const QString &dbCardName,
|
||||
CardRelationType attach = CardRelationType::DoesNotAttach,
|
||||
bool persistent = false,
|
||||
bool faceDown = false);
|
||||
|
||||
void playSelectedCards(QList<CardItem *> selectedCards, bool faceDown = false);
|
||||
|
||||
void cmdSetTopCard(Command_MoveCard &cmd);
|
||||
void cmdSetBottomCard(Command_MoveCard &cmd);
|
||||
|
||||
void offsetCardCounter(QList<CardItem *> selectedCards, int counterId, int offset);
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_PLAYER_ACTIONS_H
|
||||
|
|
@ -1,683 +0,0 @@
|
|||
#include "player_event_handler.h"
|
||||
|
||||
#include "../../game_graphics/board/card_item.h"
|
||||
#include "../../game_graphics/zones/view_zone.h"
|
||||
#include "../abstract_game.h"
|
||||
#include "../board/arrow_data.h"
|
||||
#include "../board/card_list.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 "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/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/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 (ZoneViewZone *view : zone->getViews()) {
|
||||
if (view != nullptr) {
|
||||
int length = view->getLogic()->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
|
||||
view->close();
|
||||
}
|
||||
} 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())};
|
||||
CardItem *card = new CardItem(player, nullptr, cardRef, event.card_id());
|
||||
// use db PT if not provided in event and not face-down
|
||||
if (!QString::fromStdString(event.pt()).isEmpty()) {
|
||||
card->setPT(QString::fromStdString(event.pt()));
|
||||
} else if (!event.face_down()) {
|
||||
ExactCard dbCard = card->getCard();
|
||||
if (dbCard) {
|
||||
card->setPT(dbCard.getInfo().getPowTough());
|
||||
}
|
||||
}
|
||||
card->setColor(QString::fromStdString(event.color()));
|
||||
card->setAnnotation(QString::fromStdString(event.annotation()));
|
||||
card->setDestroyOnZoneChange(event.destroy_on_zone_change());
|
||||
card->setFaceDown(event.face_down());
|
||||
|
||||
emit logCreateToken(player, card->getName(), card->getPT(), card->getFaceDown());
|
||||
zone->addCard(card, true, event.x(), event.y());
|
||||
}
|
||||
|
||||
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 {
|
||||
CardItem *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,
|
||||
CardItem *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;
|
||||
}
|
||||
|
||||
CardItem *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;
|
||||
}
|
||||
CardItem *card = startZone->takeCard(position, event.card_id(), startZone != targetZone);
|
||||
if (card == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (startZone != targetZone) {
|
||||
card->deleteCardInfoPopup();
|
||||
}
|
||||
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)) {
|
||||
CardItem *parentCard = card->getAttachedTo();
|
||||
card->setAttachedTo(nullptr);
|
||||
parentCard->getZone()->reorganizeCards();
|
||||
}
|
||||
|
||||
card->deleteDragItem();
|
||||
|
||||
card->setId(event.new_card_id());
|
||||
card->setFaceDown(event.face_down());
|
||||
if (startZone != targetZone) {
|
||||
card->setBeingPointedAt(false);
|
||||
card->setHovered(false);
|
||||
|
||||
const QList<CardItem *> &attachedCards = card->getAttachedCards();
|
||||
for (auto attachedCard : attachedCards) {
|
||||
emit targetZone->cardAdded(attachedCard);
|
||||
}
|
||||
|
||||
if (startZone->getPlayer() != targetZone->getPlayer()) {
|
||||
card->setOwner(targetZone->getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
CardItem *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;
|
||||
}
|
||||
|
||||
CardItem *card = zone->getCard(event.card_id());
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
QList<CardItem *> attachedCards = card->getAttachedCards();
|
||||
// This list is always empty except for buggy server implementations.
|
||||
for (auto &attachedCard : attachedCards) {
|
||||
attachedCard->setAttachedTo(nullptr);
|
||||
}
|
||||
|
||||
emit logDestroyCard(player, card->getName());
|
||||
zone->takeCard(-1, event.card_id(), true);
|
||||
card->deleteLater();
|
||||
}
|
||||
|
||||
void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
|
||||
{
|
||||
const QMap<int, PlayerLogic *> &playerList = player->getGame()->getPlayerManager()->getPlayers();
|
||||
PlayerLogic *targetPlayer = nullptr;
|
||||
CardZoneLogic *targetZone = nullptr;
|
||||
CardItem *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;
|
||||
}
|
||||
|
||||
CardItem *startCard = startZone->getCard(event.card_id());
|
||||
if (!startCard) {
|
||||
return;
|
||||
}
|
||||
|
||||
CardItem *oldParent = startCard->getAttachedTo();
|
||||
|
||||
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);
|
||||
CardItem *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());
|
||||
CardItem *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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,284 +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 CardItem;
|
||||
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 (CardItem, 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,
|
||||
CardItem *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, CardItem *card, bool tapped);
|
||||
void logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue);
|
||||
void logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap);
|
||||
void logSetPT(PlayerLogic *player, CardItem *card, QString newPT);
|
||||
void logSetAnnotation(PlayerLogic *player, CardItem *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(CardItem *card, bool sameZone);
|
||||
void requestCardMenuUpdate(const CardItem *card);
|
||||
|
||||
private:
|
||||
/** Owning player instance. */
|
||||
PlayerLogic *player;
|
||||
|
||||
void setCardAttrHelper(const GameEventContext &context,
|
||||
CardItem *card,
|
||||
CardAttribute attribute,
|
||||
const QString &avalue,
|
||||
bool allCards,
|
||||
EventProcessingOptions options);
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_PLAYER_EVENT_HANDLER_H
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1,364 +0,0 @@
|
|||
#include "player_logic.h"
|
||||
|
||||
#include "../../game_graphics/board/card_item.h"
|
||||
#include "../abstract_game.h"
|
||||
#include "../board/arrow_data.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>
|
||||
|
||||
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) {
|
||||
zone->addCard(new CardItem(this), false, -1);
|
||||
}
|
||||
} else {
|
||||
for (int j = 0; j < cardListSize; ++j) {
|
||||
const ServerInfo_Card &cardInfo = zoneInfo.card_list(j);
|
||||
auto *card = new CardItem(this);
|
||||
card->processCardInfo(cardInfo);
|
||||
// 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()) {
|
||||
zone->addCard(card, false, cardInfo.x(), cardInfo.y());
|
||||
} else {
|
||||
zone->addCard(card, false, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
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()) {
|
||||
CardItem *startCard = zone->getCard(cardInfo.id());
|
||||
CardItem *targetCard =
|
||||
game->getCard(cardInfo.attach_player_id(), QString::fromStdString(cardInfo.attach_zone()),
|
||||
cardInfo.attach_card_id());
|
||||
if (!targetCard) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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(CardItem *card)
|
||||
{
|
||||
emit newCardAdded(card);
|
||||
}
|
||||
|
||||
void PlayerLogic::deleteCard(CardItem *card)
|
||||
{
|
||||
if (card == nullptr) {
|
||||
return;
|
||||
} else if (dialogSemaphore) {
|
||||
cardsToDelete.append(card);
|
||||
} else {
|
||||
card->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
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->deleteLater();
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
|
@ -1,272 +0,0 @@
|
|||
/**
|
||||
* @file player.h
|
||||
* @ingroup GameLogicPlayers
|
||||
*/
|
||||
//! \todo Document this file.
|
||||
|
||||
#ifndef PLAYER_H
|
||||
#define PLAYER_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 <QInputDialog>
|
||||
#include <QList>
|
||||
#include <QLoggingCategory>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#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(const CardItem *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(CardItem *c);
|
||||
void deleteCard(CardItem *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<CardItem *> cardsToDelete;
|
||||
|
||||
// Playmat from player properties (for opponent display)
|
||||
CardRef remotePlaymatCard;
|
||||
PlaymatParams remotePlaymatParams;
|
||||
bool hasRemotePlaymat = false;
|
||||
};
|
||||
|
||||
class AnnotationDialog : public QInputDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
public:
|
||||
explicit AnnotationDialog(QWidget *parent = nullptr) : QInputDialog(parent)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#ifndef COCKATRICE_CARD_ZONE_ALGORITHMS_H
|
||||
#define COCKATRICE_CARD_ZONE_ALGORITHMS_H
|
||||
|
||||
namespace CardZoneAlgorithms
|
||||
{
|
||||
|
||||
/**
|
||||
* Shared insertion logic for zones where cards become visible on add and follow
|
||||
* the standard pattern: clamp index, insert, clear identity if contents unknown,
|
||||
* reset state, show card.
|
||||
*
|
||||
* 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), setVisible(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);
|
||||
card->setVisible(true);
|
||||
}
|
||||
|
||||
} // namespace CardZoneAlgorithms
|
||||
|
||||
#endif // COCKATRICE_CARD_ZONE_ALGORITHMS_H
|
||||
|
|
@ -1,230 +0,0 @@
|
|||
#include "card_zone_logic.h"
|
||||
|
||||
#include "../../client/translation.h"
|
||||
#include "../../game_graphics/board/card_item.h"
|
||||
#include "../../game_graphics/zones/card_zone.h"
|
||||
#include "../../game_graphics/zones/view_zone.h"
|
||||
#include "../board/card_list.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(CardItem *card, const bool reorganize, const int x, const int y)
|
||||
{
|
||||
if (!card) {
|
||||
qCWarning(CardZoneLog) << "CardZoneLogic::addCard() card is null; this shouldn't normally happen";
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto *view : views) {
|
||||
if (qobject_cast<ZoneViewZoneLogic *>(view->getLogic())->prepareAddCard(x)) {
|
||||
auto copy = new CardItem(player, nullptr, card->getCardRef(), card->getId());
|
||||
copy->setFaceDown(card->getFaceDown());
|
||||
|
||||
view->getLogic()->addCard(copy, reorganize, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
card->setZone(this);
|
||||
emit cardAdded(card);
|
||||
addCardImpl(card, x, y);
|
||||
|
||||
if (reorganize) {
|
||||
emit reorganizeCards();
|
||||
}
|
||||
|
||||
emit cardCountChanged();
|
||||
}
|
||||
|
||||
CardItem *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 *view : views) {
|
||||
qobject_cast<ZoneViewZoneLogic *>(view->getLogic())->removeCard(position, toNewZone);
|
||||
}
|
||||
|
||||
CardItem *c = cards.takeAt(position);
|
||||
|
||||
c->setId(cardId);
|
||||
|
||||
emit reorganizeCards();
|
||||
emit cardCountChanged();
|
||||
return c;
|
||||
}
|
||||
|
||||
CardItem *CardZoneLogic::getCard(int cardId)
|
||||
{
|
||||
CardItem *c = cards.findCard(cardId);
|
||||
if (!c) {
|
||||
qCWarning(CardZoneLog) << "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(CardItem *card)
|
||||
{
|
||||
if (!card) {
|
||||
qCWarning(CardZoneLog) << "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 &cardItem : cards) {
|
||||
cardItem->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 (CardItem *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<CardItem *> &attachedCards = card->getAttachedCards();
|
||||
for (CardItem *attachedCard : attachedCards) {
|
||||
emit attachedCard->getZone()->cardAdded(attachedCard);
|
||||
}
|
||||
}
|
||||
|
||||
// Now request deletions after all manipulations are done.
|
||||
for (CardItem *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();
|
||||
}
|
||||
|
|
@ -1,120 +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 <qtmetamacros.h>
|
||||
|
||||
class CardItem;
|
||||
|
||||
inline Q_LOGGING_CATEGORY(CardZoneLogicLog, "card_zone_logic");
|
||||
|
||||
class PlayerLogic;
|
||||
class ZoneViewZone;
|
||||
|
||||
class CardZoneLogic : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void cardAdded(CardItem *addedCard);
|
||||
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(CardItem *card, bool reorganize, int x, int y = -1);
|
||||
// getCard() finds a card by id.
|
||||
CardItem *getCard(int cardId);
|
||||
void removeCard(CardItem *card);
|
||||
// takeCard() finds a card by position and removes it from the zone and from all of its views.
|
||||
virtual CardItem *takeCard(int position, int cardId, bool canResize = true);
|
||||
|
||||
void rawInsertCard(CardItem *card, int index)
|
||||
{
|
||||
cards.insert(index, card);
|
||||
}
|
||||
|
||||
[[nodiscard]] const CardList &getCards() const
|
||||
{
|
||||
return cards;
|
||||
}
|
||||
|
||||
void sortCards(const QList<CardList::SortOption> &options)
|
||||
{
|
||||
cards.sortBy(options);
|
||||
}
|
||||
[[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<ZoneViewZone *> &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<ZoneViewZone *> views;
|
||||
bool hasCardAttr;
|
||||
bool isShufflable;
|
||||
bool alwaysRevealTopCard;
|
||||
|
||||
virtual void addCardImpl(CardItem *card, int x, int y) = 0;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_CARD_ZONE_LOGIC_H
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#include "hand_zone_logic.h"
|
||||
|
||||
#include "../../game_graphics/board/card_item.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(CardItem *card, int x, int /*y*/)
|
||||
{
|
||||
CardZoneAlgorithms::addCardToList(cards, card, x, false);
|
||||
}
|
||||
|
|
@ -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(CardItem *card, int x, int y) override;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_HAND_ZONE_LOGIC_H
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
#include "pile_zone_logic.h"
|
||||
|
||||
#include "../../game_graphics/board/card_item.h"
|
||||
#include "../board/card_list.h"
|
||||
|
||||
class PlayerLogic;
|
||||
class QObject;
|
||||
|
||||
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(CardItem *card, int x, int /*y*/)
|
||||
{
|
||||
connect(card, &CardItem::sigPixmapUpdated, 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);
|
||||
card->setPos(0, 0);
|
||||
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->setVisible(false);
|
||||
card->resetState();
|
||||
}
|
||||
|
|
@ -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(CardItem *card, int x, int y) override;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_PILE_ZONE_LOGIC_H
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#include "stack_zone_logic.h"
|
||||
|
||||
#include "../../game_graphics/board/card_item.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(CardItem *card, int x, int /*y*/)
|
||||
{
|
||||
CardZoneAlgorithms::addCardToList(cards, card, x, true);
|
||||
}
|
||||
|
|
@ -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(CardItem *card, int x, int y) override;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_STACK_ZONE_LOGIC_H
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#include "table_zone_logic.h"
|
||||
|
||||
#include "../../game_graphics/board/card_item.h"
|
||||
#include "../board/card_list.h"
|
||||
#include "libcockatrice/card/card_info.h"
|
||||
|
||||
#include <QPoint>
|
||||
|
||||
class PlayerLogic;
|
||||
class QObject;
|
||||
|
||||
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(CardItem *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));
|
||||
card->setVisible(true);
|
||||
}
|
||||
|
||||
CardItem *TableZoneLogic::takeCard(int position, int cardId, bool toNewZone)
|
||||
{
|
||||
CardItem *result = CardZoneLogic::takeCard(position, cardId);
|
||||
|
||||
if (toNewZone) {
|
||||
emit contentSizeChanged();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1,46 +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);
|
||||
|
||||
protected:
|
||||
void addCardImpl(CardItem *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 CardItem that has been removed
|
||||
*/
|
||||
CardItem *takeCard(int position, int cardId, bool toNewZone = true) override;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_TABLE_ZONE_LOGIC_H
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
#include "view_zone_logic.h"
|
||||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../game_graphics/board/card_item.h"
|
||||
#include "../board/card_list.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 a new CardItem 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(CardItem *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;
|
||||
}
|
||||
|
||||
CardItem *card = cards.takeAt(position);
|
||||
card->deleteLater();
|
||||
|
||||
// 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::clearCards()
|
||||
{
|
||||
for (auto card : cards) {
|
||||
card->deleteLater();
|
||||
}
|
||||
cards.clear();
|
||||
}
|
||||
|
||||
void ZoneViewZoneLogic::setWriteableRevealZone(bool _writeableRevealZone)
|
||||
{
|
||||
|
||||
if (writeableRevealZone && !_writeableRevealZone) {
|
||||
emit addToViews();
|
||||
} else if (!writeableRevealZone && _writeableRevealZone) {
|
||||
emit removeFromViews();
|
||||
}
|
||||
writeableRevealZone = _writeableRevealZone;
|
||||
}
|
||||
|
|
@ -1,80 +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 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 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;
|
||||
}
|
||||
|
||||
protected:
|
||||
void addCardImpl(CardItem *card, int x, int y) override;
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_VIEW_ZONE_LOGIC_H
|
||||
|
|
@ -34,7 +34,6 @@
|
|||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <algorithm>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/settings/appearance_settings.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <libcockatrice/settings/debug_settings.h>
|
||||
|
|
@ -43,13 +42,23 @@
|
|||
class QSizeF;
|
||||
|
||||
AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef, PlayerLogic *_owner, int _id)
|
||||
: ArrowTarget(_owner, parent), id(_id), cardRef(cardRef), tapped(false), facedown(false), tapAngle(0),
|
||||
bgColor(Qt::transparent), isHovered(false), realZValue(0), pendingSelectionCollapse(false)
|
||||
: ArrowTarget(_owner, parent), state(new CardState(this, nullptr, cardRef, _id)), tapAngle(0),
|
||||
bgColor(Qt::transparent), isHovered(false), realZValue(0)
|
||||
{
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
setFlag(ItemIsSelectable);
|
||||
setCacheMode(DeviceCoordinateCache);
|
||||
|
||||
connect(state, &CardState::cardPixmapUpdated, this, &AbstractCardItem::pixmapUpdated);
|
||||
connect(state, &CardState::cardInfoChanged, this, &AbstractCardItem::onCardInfoChanged);
|
||||
connect(state, &CardState::cardRefChanged, this, &AbstractCardItem::onCardRefChanged);
|
||||
connect(state, &CardState::tappedChanged, this, &AbstractCardItem::onTappedChanged);
|
||||
connect(state, &CardState::facedownChanged, this, [this] { update(); });
|
||||
connect(state, &CardState::colorChanged, this, [this] {
|
||||
cacheBgColor();
|
||||
update();
|
||||
});
|
||||
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::displayCardNamesChanged, this,
|
||||
[this] { update(); });
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
|
||||
|
|
@ -67,7 +76,7 @@ AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef
|
|||
|
||||
AbstractCardItem::~AbstractCardItem()
|
||||
{
|
||||
emit deleteCardInfoPopup(cardRef.name);
|
||||
emit deleteCardInfoPopup(state->getCardRef().name);
|
||||
}
|
||||
|
||||
QRectF AbstractCardItem::boundingRect() const
|
||||
|
|
@ -90,30 +99,29 @@ void AbstractCardItem::pixmapUpdated()
|
|||
emit sigPixmapUpdated();
|
||||
}
|
||||
|
||||
void AbstractCardItem::refreshCardInfo()
|
||||
void AbstractCardItem::onCardInfoChanged()
|
||||
{
|
||||
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, &AbstractCardItem::pixmapUpdated);
|
||||
}
|
||||
|
||||
cacheBgColor();
|
||||
update();
|
||||
}
|
||||
|
||||
void AbstractCardItem::onCardRefChanged(const CardRef &oldCardRef, const CardRef & /*newCardRef*/)
|
||||
{
|
||||
emit deleteCardInfoPopup(oldCardRef.name);
|
||||
}
|
||||
|
||||
void AbstractCardItem::refreshCardInfo()
|
||||
{
|
||||
state->refreshCardInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to get the CardInfo of the exactCard
|
||||
* @return A const reference to the CardInfo, or an empty CardInfo if card was null
|
||||
*/
|
||||
const CardInfo &AbstractCardItem::getCardInfo() const
|
||||
{
|
||||
return exactCard.getInfo();
|
||||
return state->getCardInfo();
|
||||
}
|
||||
|
||||
void AbstractCardItem::setRealZValue(qreal _zValue)
|
||||
|
|
@ -158,13 +166,13 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
|
|||
QPixmap translatedPixmap;
|
||||
bool paintImage = true;
|
||||
|
||||
if (facedown || cardRef.name.isEmpty()) {
|
||||
if (state->getFaceDown() || state->getCardRef().name.isEmpty()) {
|
||||
// never reveal card color, always paint the card back
|
||||
CardPictureLoader::getCardBackPixmap(translatedPixmap, translatedSize.toSize());
|
||||
} else {
|
||||
// don't even spend time trying to load the picture if our size is too small
|
||||
if (translatedSize.width() > 10) {
|
||||
CardPictureLoader::getPixmap(translatedPixmap, exactCard, translatedSize.toSize());
|
||||
CardPictureLoader::getPixmap(translatedPixmap, state->getCard(), translatedSize.toSize());
|
||||
if (translatedPixmap.isNull()) {
|
||||
paintImage = false;
|
||||
}
|
||||
|
|
@ -185,19 +193,20 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
|
|||
painter->drawPath(shape());
|
||||
}
|
||||
|
||||
if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() || facedown) {
|
||||
if (translatedPixmap.isNull() || SettingsCache::instance().cardsDisplay().getDisplayCardNames() ||
|
||||
state->getFaceDown()) {
|
||||
painter->save();
|
||||
transformPainter(painter, translatedSize, angle);
|
||||
painter->setPen(Qt::white);
|
||||
painter->setBackground(Qt::black);
|
||||
painter->setBackgroundMode(Qt::OpaqueMode);
|
||||
QString nameStr;
|
||||
if (facedown) {
|
||||
nameStr = "# " + QString::number(id);
|
||||
if (state->getFaceDown()) {
|
||||
nameStr = "# " + QString::number(state->getId());
|
||||
} else {
|
||||
QString prefix = "";
|
||||
if (SettingsCache::instance().debug().getShowCardId()) {
|
||||
prefix = "#" + QString::number(id) + " ";
|
||||
prefix = "#" + QString::number(state->getId()) + " ";
|
||||
}
|
||||
nameStr = prefix + CardLocalization::displayName(getCardInfo());
|
||||
}
|
||||
|
|
@ -235,21 +244,6 @@ void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
|
|||
painter->restore();
|
||||
}
|
||||
|
||||
void AbstractCardItem::setCardRef(const CardRef &_cardRef)
|
||||
{
|
||||
if (cardRef == _cardRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit deleteCardInfoPopup(cardRef.name);
|
||||
if (exactCard) {
|
||||
disconnect(exactCard.getCardPtr().data(), nullptr, this, nullptr);
|
||||
}
|
||||
cardRef = _cardRef;
|
||||
|
||||
refreshCardInfo();
|
||||
}
|
||||
|
||||
void AbstractCardItem::setHovered(bool _hovered)
|
||||
{
|
||||
if (isHovered == _hovered) {
|
||||
|
|
@ -275,18 +269,17 @@ void AbstractCardItem::setHovered(bool _hovered)
|
|||
|
||||
void AbstractCardItem::setColor(const QString &_color)
|
||||
{
|
||||
color = _color;
|
||||
cacheBgColor();
|
||||
update();
|
||||
state->setColor(_color);
|
||||
}
|
||||
|
||||
void AbstractCardItem::cacheBgColor()
|
||||
{
|
||||
QChar colorChar;
|
||||
if (color.isEmpty()) {
|
||||
colorChar = exactCard.getInfo().getColorChar();
|
||||
const QString cardColor = state->getColor();
|
||||
if (cardColor.isEmpty()) {
|
||||
colorChar = state->getCardInfo().getColorChar();
|
||||
} else {
|
||||
colorChar = color.at(0);
|
||||
colorChar = cardColor.at(0);
|
||||
}
|
||||
|
||||
switch (colorChar.toLower().toLatin1()) {
|
||||
|
|
@ -314,17 +307,12 @@ void AbstractCardItem::cacheBgColor()
|
|||
}
|
||||
}
|
||||
|
||||
void AbstractCardItem::setTapped(bool _tapped, bool canAnimate)
|
||||
void AbstractCardItem::onTappedChanged(bool newTapped, bool canAnimate)
|
||||
{
|
||||
if (tapped == _tapped) {
|
||||
return;
|
||||
}
|
||||
|
||||
tapped = _tapped;
|
||||
if (SettingsCache::instance().cardsDisplay().getTapAnimation() && canAnimate) {
|
||||
static_cast<GameScene *>(scene())->registerAnimationItem(this);
|
||||
} else {
|
||||
tapAngle = tapped ? 90 : 0;
|
||||
tapAngle = newTapped ? 90 : 0;
|
||||
setTransform(QTransform()
|
||||
.translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F)
|
||||
.rotate(tapAngle)
|
||||
|
|
@ -338,12 +326,6 @@ bool AbstractCardItem::animationEvent()
|
|||
return false;
|
||||
}
|
||||
|
||||
void AbstractCardItem::setFaceDown(bool _facedown)
|
||||
{
|
||||
facedown = _facedown;
|
||||
update();
|
||||
}
|
||||
|
||||
void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
cancelPendingSelectionCollapse();
|
||||
|
|
@ -357,7 +339,7 @@ void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|||
}
|
||||
}
|
||||
if ((event->modifiers() & Qt::AltModifier) && event->button() == Qt::LeftButton) {
|
||||
emit cardShiftClicked(cardRef.name);
|
||||
emit cardShiftClicked(state->getCardRef().name);
|
||||
} else if ((event->modifiers() & Qt::ControlModifier)) {
|
||||
setSelected(!isSelected());
|
||||
} else if (!isSelected()) {
|
||||
|
|
@ -371,7 +353,7 @@ void AbstractCardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|||
if (event->button() == Qt::LeftButton) {
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
} else if (event->button() == Qt::MiddleButton) {
|
||||
emit showCardInfoPopup(event->screenPos(), cardRef);
|
||||
emit showCardInfoPopup(event->screenPos(), state->getCardRef());
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
|
|
@ -405,7 +387,7 @@ void AbstractCardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|||
}
|
||||
|
||||
if (event->button() == Qt::MiddleButton) {
|
||||
emit deleteCardInfoPopup(cardRef.name);
|
||||
emit deleteCardInfoPopup(state->getCardRef().name);
|
||||
}
|
||||
|
||||
// This function ensures the parent function doesn't mess around with our selection.
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
#include <QSizeF>
|
||||
#include <QString>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/card/printing/exact_card.h>
|
||||
#include <libcockatrice/utility/card_ref.h>
|
||||
#include <libcockatrice/game/board/card_state.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class PlayerLogic;
|
||||
|
|
@ -29,13 +28,8 @@ class AbstractCardItem : public ArrowTarget, public IAnimatedItem
|
|||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
ExactCard exactCard;
|
||||
int id;
|
||||
CardRef cardRef;
|
||||
bool tapped;
|
||||
bool facedown;
|
||||
CardState *state;
|
||||
int tapAngle;
|
||||
QString color;
|
||||
QColor bgColor;
|
||||
|
||||
private:
|
||||
|
|
@ -45,6 +39,9 @@ private:
|
|||
int selectionCollapseGeneration = 0; ///< Guards the deferred collapse timer against stale firings
|
||||
private slots:
|
||||
void pixmapUpdated();
|
||||
void onCardInfoChanged();
|
||||
void onCardRefChanged(const CardRef &oldCardRef, const CardRef &newCardRef);
|
||||
void onTappedChanged(bool newTapped, bool canAnimate);
|
||||
|
||||
public slots:
|
||||
void refreshCardInfo();
|
||||
|
|
@ -80,31 +77,38 @@ public:
|
|||
QSizeF getTranslatedSize(QPainter *painter) const;
|
||||
void paintPicture(QPainter *painter, const QSizeF &translatedSize, int angle);
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
|
||||
[[nodiscard]] CardState *getState() const
|
||||
{
|
||||
return state;
|
||||
}
|
||||
ExactCard getCard() const
|
||||
{
|
||||
return exactCard;
|
||||
return state->getCard();
|
||||
}
|
||||
const CardInfo &getCardInfo() const;
|
||||
int getId() const
|
||||
{
|
||||
return id;
|
||||
return state->getId();
|
||||
}
|
||||
void setId(int _id)
|
||||
{
|
||||
id = _id;
|
||||
state->setId(_id);
|
||||
}
|
||||
QString getName() const
|
||||
{
|
||||
return cardRef.name;
|
||||
return state->getCardRef().name;
|
||||
}
|
||||
QString getProviderId() const
|
||||
{
|
||||
return cardRef.providerId;
|
||||
return state->getCardRef().providerId;
|
||||
}
|
||||
void setCardRef(const CardRef &_cardRef)
|
||||
{
|
||||
state->setCardRef(_cardRef);
|
||||
}
|
||||
void setCardRef(const CardRef &_cardRef);
|
||||
CardRef getCardRef() const
|
||||
{
|
||||
return cardRef;
|
||||
return state->getCardRef();
|
||||
}
|
||||
qreal getRealZValue() const
|
||||
{
|
||||
|
|
@ -116,27 +120,33 @@ public:
|
|||
{
|
||||
return isHovered;
|
||||
}
|
||||
QString getColor() const
|
||||
[[nodiscard]] QString getColor() const
|
||||
{
|
||||
return color;
|
||||
return state->getColor();
|
||||
}
|
||||
void setColor(const QString &_color);
|
||||
bool getTapped() const
|
||||
{
|
||||
return tapped;
|
||||
return state->getTapped();
|
||||
}
|
||||
void setTapped(bool _tapped, bool canAnimate = false)
|
||||
{
|
||||
state->setTapped(_tapped, canAnimate);
|
||||
}
|
||||
void setTapped(bool _tapped, bool canAnimate = false);
|
||||
bool getFaceDown() const
|
||||
{
|
||||
return facedown;
|
||||
return state->getFaceDown();
|
||||
}
|
||||
void setFaceDown(bool _facedown)
|
||||
{
|
||||
state->setFaceDown(_facedown);
|
||||
}
|
||||
void setFaceDown(bool _facedown);
|
||||
void processHoverEvent();
|
||||
/** @brief Drop a pending collapse and disarm any deferred collapse timer. */
|
||||
void cancelPendingSelectionCollapse();
|
||||
void deleteCardInfoPopup()
|
||||
{
|
||||
emit deleteCardInfoPopup(cardRef.name);
|
||||
emit deleteCardInfoPopup(state->getCardRef().name);
|
||||
}
|
||||
|
||||
/** @brief Default: no per-tick animation. Subclasses override to animate. */
|
||||
|
|
|
|||
|
|
@ -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,8 +19,10 @@
|
|||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/protocol/pb/command_inc_counter.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_set_counter.pb.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/utility/expression.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
|
|
@ -151,10 +149,7 @@ void AbstractCounter::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|||
menu->exec(event->screenPos());
|
||||
}
|
||||
} else {
|
||||
Command_IncCounter cmd;
|
||||
cmd.set_counter_id(id);
|
||||
cmd.set_delta(event->button() == Qt::LeftButton ? 1 : -1);
|
||||
player->getPlayerActions()->sendGameCommand(cmd);
|
||||
player->getPlayerActions()->actIncrementCounter(id, event->button() == Qt::LeftButton ? 1 : -1);
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
|
|
@ -172,10 +167,7 @@ void AbstractCounter::hoverLeaveEvent(QGraphicsSceneHoverEvent *)
|
|||
|
||||
void AbstractCounter::incrementCounter()
|
||||
{
|
||||
Command_IncCounter cmd;
|
||||
cmd.set_counter_id(id);
|
||||
cmd.set_delta(static_cast<QAction *>(sender())->data().toInt());
|
||||
player->getPlayerActions()->sendGameCommand(cmd);
|
||||
player->getPlayerActions()->actIncrementCounter(id, static_cast<QAction *>(sender())->data().toInt());
|
||||
}
|
||||
|
||||
void AbstractCounter::setCounter()
|
||||
|
|
@ -199,10 +191,7 @@ void AbstractCounter::setCounter()
|
|||
}
|
||||
|
||||
Expression exp(value);
|
||||
Command_SetCounter cmd;
|
||||
cmd.set_counter_id(id);
|
||||
cmd.set_value(static_cast<int>(exp.parse(dlg.textValue())));
|
||||
player->getPlayerActions()->sendGameCommand(cmd);
|
||||
player->getPlayerActions()->actSetCounter(id, static_cast<int>(exp.parse(dlg.textValue())));
|
||||
}
|
||||
|
||||
AbstractCounterDialog::AbstractCounterDialog(const QString &name, const QString &value, QWidget *parent)
|
||||
|
|
|
|||
|
|
@ -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,12 +22,12 @@
|
|||
#include <QTransform>
|
||||
#include <QtGlobal>
|
||||
#include <QtMath>
|
||||
#include <libcockatrice/card/card_info.h>
|
||||
#include <libcockatrice/protocol/pb/command_attach_card.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_create_arrow.pb.h>
|
||||
#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/settings/interface_settings.h>
|
||||
#include <libcockatrice/utility/color.h>
|
||||
#include <libcockatrice/utility/zone_names.h>
|
||||
#include <math.h>
|
||||
#include <qnamespace.h>
|
||||
|
|
@ -426,19 +421,19 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|||
|
||||
CardZoneLogic *startZone = startCard->getZone();
|
||||
|
||||
Command_CreateArrow cmd;
|
||||
cmd.mutable_arrow_color()->CopyFrom(convertQColorToColor(data->color));
|
||||
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_start_zone(startZone->getName().toStdString());
|
||||
cmd.set_start_card_id(startCard->getId());
|
||||
ArrowData arrow;
|
||||
arrow.color = data->color;
|
||||
arrow.startPlayerId = startZone->getPlayer()->getPlayerInfo()->getId();
|
||||
arrow.startZone = startZone->getName();
|
||||
arrow.startCardId = startCard->getId();
|
||||
|
||||
if (auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem)) {
|
||||
CardZoneLogic *targetZone = targetCard->getZone();
|
||||
cmd.set_target_player_id(targetZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(targetZone->getName().toStdString());
|
||||
cmd.set_target_card_id(targetCard->getId());
|
||||
arrow.targetPlayerId = targetZone->getPlayer()->getPlayerInfo()->getId();
|
||||
arrow.targetZone = targetZone->getName();
|
||||
arrow.targetCardId = targetCard->getId();
|
||||
} else if (auto *targetPlayer = qgraphicsitem_cast<PlayerTarget *>(targetItem)) {
|
||||
cmd.set_target_player_id(targetPlayer->getOwner()->getPlayerInfo()->getId());
|
||||
arrow.targetPlayerId = targetPlayer->getOwner()->getPlayerInfo()->getId();
|
||||
} else {
|
||||
delArrow();
|
||||
return;
|
||||
|
|
@ -447,22 +442,9 @@ void ArrowDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|||
// if the card is in hand then we will move the card to stack or table as part of drawing the arrow
|
||||
if (startZone->getName() == ZoneNames::HAND) {
|
||||
startCard->playCard(false);
|
||||
CardInfoPtr ci = startCard->getCard().getCardPtr();
|
||||
bool playToStack = SettingsCache::instance().userInterface().getPlayToStack();
|
||||
if (ci && ((!playToStack && ci->getUiAttributes().tableRow == 3) ||
|
||||
(playToStack && ci->getUiAttributes().tableRow != 0 &&
|
||||
startCard->getZone()->getName() != ZoneNames::STACK))) {
|
||||
cmd.set_start_zone(ZoneNames::STACK);
|
||||
} else {
|
||||
cmd.set_start_zone(playToStack ? ZoneNames::STACK : ZoneNames::TABLE);
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteInPhase != 0) {
|
||||
cmd.set_delete_in_phase(deleteInPhase);
|
||||
}
|
||||
|
||||
player->getPlayerActions()->sendGameCommand(cmd);
|
||||
player->getPlayerActions()->createArrow(startCard->getState(), arrow, deleteInPhase);
|
||||
}
|
||||
|
||||
delArrow();
|
||||
|
|
@ -554,7 +536,7 @@ void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|||
auto *startCard = qgraphicsitem_cast<CardItem *>(startItem);
|
||||
auto *targetCard = qgraphicsitem_cast<CardItem *>(targetItem);
|
||||
if (startCard && targetCard) {
|
||||
attachCards(startCard, targetCard);
|
||||
player->getPlayerActions()->attachCards(startCard->getState(), targetCard->getState());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -563,23 +545,3 @@ void ArrowAttachItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|||
child->mouseReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void ArrowAttachItem::attachCards(CardItem *startCard, const CardItem *targetCard)
|
||||
{
|
||||
if (targetCard->getAttachedTo() || targetCard->getZone()->getName() != ZoneNames::TABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// move card onto table first if attaching from some other zone
|
||||
if (startCard->getZone()->getName() != ZoneNames::TABLE) {
|
||||
player->getPlayerActions()->playCardToTable(startCard, false);
|
||||
}
|
||||
|
||||
Command_AttachCard cmd;
|
||||
cmd.set_start_zone(ZoneNames::TABLE);
|
||||
cmd.set_card_id(startCard->getId());
|
||||
cmd.set_target_player_id(targetCard->getZone()->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(targetCard->getZone()->getName().toStdString());
|
||||
cmd.set_target_card_id(targetCard->getId());
|
||||
player->getPlayerActions()->sendGameCommand(cmd);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -131,7 +131,6 @@ private:
|
|||
PlayerLogic *player;
|
||||
QList<ArrowAttachItem *> childArrows;
|
||||
QMetaObject::Connection positionConnection;
|
||||
void attachCards(CardItem *startCard, const CardItem *targetCard);
|
||||
|
||||
public:
|
||||
explicit ArrowAttachItem(ArrowTarget *_startItem);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#include "arrow_registry.h"
|
||||
|
||||
#include "../game_graphics/board/arrow_item.h"
|
||||
#include "board/arrow_data.h"
|
||||
#include "arrow_item.h"
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <libcockatrice/game/board/arrow_data.h>
|
||||
|
||||
void ArrowRegistry::insert(QSharedPointer<ArrowData> data, ArrowItem *arrow)
|
||||
{
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
#include <libcockatrice/game/board/arrow_data.h>
|
||||
|
||||
class ArrowItem;
|
||||
struct ArrowData;
|
||||
|
|
@ -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>
|
||||
|
|
@ -53,8 +53,9 @@ CardItem::CardItem(PlayerLogic *_owner,
|
|||
const CardRef &cardRef,
|
||||
int _cardid,
|
||||
CardZoneLogic *_zone)
|
||||
: AbstractCardItem(parent, cardRef, _owner, _cardid), state(new CardState(this, _zone)), dragItem(nullptr)
|
||||
: AbstractCardItem(parent, cardRef, _owner, _cardid), dragItem(nullptr)
|
||||
{
|
||||
state->setZone(_zone);
|
||||
owner->addCard(this);
|
||||
|
||||
connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, this, [this](int counterId) {
|
||||
|
|
@ -62,25 +63,44 @@ CardItem::CardItem(PlayerLogic *_owner,
|
|||
update();
|
||||
}
|
||||
});
|
||||
|
||||
connect(state, &CardState::stateReset, this, [this] {
|
||||
if (scene()) {
|
||||
static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
|
||||
}
|
||||
update();
|
||||
});
|
||||
|
||||
// The retyped logic layer drives CardState setters directly (bypassing the CardItem wrappers that called
|
||||
// update()), so repaint on every state change: attacking, annotation, doesntUntap, P/T and counters.
|
||||
connect(state, &CardState::stateChanged, this, [this] { update(); });
|
||||
|
||||
connect(state, &CardState::viewDeleteRequested, this, &CardItem::deleteLater);
|
||||
}
|
||||
|
||||
void CardItem::prepareDelete()
|
||||
{
|
||||
if (owner != nullptr) {
|
||||
if (owner->getGame()->getActiveCard() == this) {
|
||||
auto *gameScene = qobject_cast<GameScene *>(scene());
|
||||
if (gameScene && gameScene->getActiveCard() == this) {
|
||||
emit owner->requestCardMenuUpdate(nullptr);
|
||||
owner->getGame()->setActiveCard(nullptr);
|
||||
gameScene->setActiveCard(nullptr);
|
||||
}
|
||||
owner = nullptr;
|
||||
}
|
||||
|
||||
while (!attachedCards.isEmpty()) {
|
||||
attachedCards.first()->setZone(nullptr); // so that it won't try to call reorganizeCards()
|
||||
attachedCards.first()->setAttachedTo(nullptr);
|
||||
const QList<CardState *> &attachedStates = state->getAttachedCards();
|
||||
while (!attachedStates.isEmpty()) {
|
||||
auto *attachedItem = qobject_cast<CardItem *>(attachedStates.first()->parent());
|
||||
if (attachedItem == nullptr) {
|
||||
break;
|
||||
}
|
||||
attachedItem->setZone(nullptr); // so that it won't try to call reorganizeCards()
|
||||
attachedItem->setAttachedTo(nullptr);
|
||||
}
|
||||
|
||||
if (state->getAttachedTo() != nullptr) {
|
||||
state->getAttachedTo()->removeAttachedCard(this);
|
||||
state->getAttachedTo()->removeAttachedCard(state);
|
||||
state->setAttachedTo(nullptr);
|
||||
}
|
||||
}
|
||||
|
|
@ -127,7 +147,7 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
|||
painter->save();
|
||||
transformPainter(painter, translatedSize, tapAngle);
|
||||
|
||||
if (!getFaceDown() && state->getPT() == exactCard.getInfo().getPowTough()) {
|
||||
if (!getFaceDown() && state->getPT() == state->getCardInfo().getPowTough()) {
|
||||
painter->setPen(Qt::white);
|
||||
} else {
|
||||
painter->setPen(QColor(255, 150, 0)); // dark orange
|
||||
|
|
@ -209,36 +229,13 @@ void CardItem::setPT(const QString &_pt)
|
|||
|
||||
void CardItem::setAttachedTo(CardItem *_attachedTo)
|
||||
{
|
||||
if (state->getAttachedTo() != nullptr) {
|
||||
state->getAttachedTo()->removeAttachedCard(this);
|
||||
}
|
||||
CardState *newParent = _attachedTo == nullptr ? nullptr : _attachedTo->getState();
|
||||
state->setAttachedTo(newParent);
|
||||
|
||||
gridPoint.setX(-1);
|
||||
state->setAttachedTo(_attachedTo);
|
||||
if (state->getAttachedTo() != nullptr) {
|
||||
// If the zone is being torn down, it might already be null by the time a card tries to un-attach all its
|
||||
// attached cards
|
||||
if (state->getAttachedTo()->getZone() == nullptr) {
|
||||
deleteLater();
|
||||
} else {
|
||||
emit state->getAttachedTo()->getZone()->cardAdded(this);
|
||||
state->getAttachedTo()->addAttachedCard(this);
|
||||
if (state->getZone() != state->getAttachedTo()->getZone()) {
|
||||
state->getAttachedTo()->getZone()->reorganizeCards();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the zone is being torn down, it might already be null by the time a card tries to un-attach all its
|
||||
// attached cards
|
||||
if (state->getZone() == nullptr) {
|
||||
deleteLater();
|
||||
} else {
|
||||
emit state->getZone()->cardAdded(this);
|
||||
}
|
||||
}
|
||||
|
||||
if (state->getZone() != nullptr) {
|
||||
state->getZone()->reorganizeCards();
|
||||
// If the zone is being torn down, it might already be null by the time a card tries to un-attach all its attached
|
||||
// cards
|
||||
if (newParent != nullptr ? newParent->getZone() == nullptr : state->getZone() == nullptr) {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -248,13 +245,6 @@ void CardItem::setAttachedTo(CardItem *_attachedTo)
|
|||
void CardItem::resetState(bool keepAnnotations)
|
||||
{
|
||||
state->resetState(keepAnnotations);
|
||||
attachedCards.clear();
|
||||
setTapped(false, false);
|
||||
setDoesntUntap(false);
|
||||
if (scene()) {
|
||||
static_cast<GameScene *>(scene())->unregisterAnimationItem(this);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void CardItem::processCardInfo(const ServerInfo_Card &_info)
|
||||
|
|
@ -390,7 +380,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|||
|
||||
// Use the buttonDownPos to align the hot spot with the position when
|
||||
// the user originally clicked
|
||||
createDragItem(id, event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown);
|
||||
createDragItem(state->getId(), event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown);
|
||||
dragItem->grabMouse();
|
||||
|
||||
int childIndex = 0;
|
||||
|
|
@ -433,42 +423,11 @@ void CardItem::playCard(bool faceDown)
|
|||
emit playSelected(this);
|
||||
}
|
||||
} else {
|
||||
state->getZone()->getPlayer()->getPlayerActions()->playCard(this, faceDown);
|
||||
state->getZone()->getPlayer()->getPlayerActions()->playCard(state, faceDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList CardItem::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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns true if the zone is a unwritable reveal zone view (eg a card reveal window). Will return false if zone
|
||||
* is nullptr.
|
||||
|
|
@ -493,7 +452,7 @@ void CardItem::handleClickedToPlay(bool shiftHeld)
|
|||
if (SettingsCache::instance().userInterface().getClickPlaysAllSelected()) {
|
||||
emit hideSelected(this);
|
||||
} else {
|
||||
state->getZone()->removeCard(this);
|
||||
state->getZone()->removeCard(state);
|
||||
}
|
||||
} else {
|
||||
playCard(shiftHeld);
|
||||
|
|
@ -537,16 +496,16 @@ bool CardItem::animationEvent()
|
|||
{
|
||||
int rotation = ROTATION_DEGREES_PER_FRAME;
|
||||
bool animationIncomplete = true;
|
||||
if (!tapped) {
|
||||
if (!state->getTapped()) {
|
||||
rotation *= -1;
|
||||
}
|
||||
|
||||
tapAngle += rotation;
|
||||
if (tapped && (tapAngle > 90)) {
|
||||
if (state->getTapped() && (tapAngle > 90)) {
|
||||
tapAngle = 90;
|
||||
animationIncomplete = false;
|
||||
}
|
||||
if (!tapped && (tapAngle < 0)) {
|
||||
if (!state->getTapped() && (tapAngle < 0)) {
|
||||
tapAngle = 0;
|
||||
animationIncomplete = false;
|
||||
}
|
||||
|
|
@ -567,7 +526,9 @@ QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value)
|
|||
bool selected = value.toBool();
|
||||
|
||||
if (selected) {
|
||||
owner->getGame()->setActiveCard(this);
|
||||
if (auto *gameScene = qobject_cast<GameScene *>(scene())) {
|
||||
gameScene->setActiveCard(this);
|
||||
}
|
||||
}
|
||||
|
||||
emit selectionChanged(this, selected);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -33,11 +33,7 @@ class CardItem : public AbstractCardItem
|
|||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
CardState *state;
|
||||
|
||||
QPoint gridPoint;
|
||||
CardDragItem *dragItem;
|
||||
QList<CardItem *> attachedCards;
|
||||
|
||||
void prepareDelete();
|
||||
void handleClickedToPlay(bool shiftHeld);
|
||||
|
|
@ -60,10 +56,6 @@ public:
|
|||
CardZoneLogic *_zone = nullptr);
|
||||
|
||||
void retranslateUi();
|
||||
[[nodiscard]] CardState *getState() const
|
||||
{
|
||||
return state;
|
||||
}
|
||||
[[nodiscard]] CardZoneLogic *getZone() const
|
||||
{
|
||||
return state->getZone();
|
||||
|
|
@ -72,15 +64,15 @@ public:
|
|||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
|
||||
[[nodiscard]] QPoint getGridPoint() const
|
||||
{
|
||||
return gridPoint;
|
||||
return state->getGridPoint();
|
||||
}
|
||||
void setGridPoint(const QPoint &_gridPoint)
|
||||
{
|
||||
gridPoint = _gridPoint;
|
||||
state->setGridPoint(_gridPoint);
|
||||
}
|
||||
[[nodiscard]] QPoint getGridPos() const
|
||||
{
|
||||
return gridPoint;
|
||||
return state->getGridPoint();
|
||||
}
|
||||
[[nodiscard]] PlayerLogic *getOwner() const
|
||||
{
|
||||
|
|
@ -123,22 +115,22 @@ public:
|
|||
{
|
||||
state->setDestroyOnZoneChange(_destroy);
|
||||
}
|
||||
[[nodiscard]] CardItem *getAttachedTo() const
|
||||
[[nodiscard]] CardState *getAttachedTo() const
|
||||
{
|
||||
return state->getAttachedTo();
|
||||
}
|
||||
void setAttachedTo(CardItem *_attachedTo);
|
||||
void addAttachedCard(CardItem *card)
|
||||
void addAttachedCard(CardState *card)
|
||||
{
|
||||
attachedCards.append(card);
|
||||
state->addAttachedCard(card);
|
||||
}
|
||||
void removeAttachedCard(CardItem *card)
|
||||
void removeAttachedCard(CardState *card)
|
||||
{
|
||||
attachedCards.removeOne(card);
|
||||
state->removeAttachedCard(card);
|
||||
}
|
||||
[[nodiscard]] const QList<CardItem *> &getAttachedCards() const
|
||||
[[nodiscard]] const QList<CardState *> &getAttachedCards() const
|
||||
{
|
||||
return attachedCards;
|
||||
return state->getAttachedCards();
|
||||
}
|
||||
void resetState(bool keepAnnotations = false);
|
||||
void processCardInfo(const ServerInfo_Card &_info);
|
||||
|
|
@ -150,26 +142,6 @@ public:
|
|||
void drawAttachArrow();
|
||||
void playCard(bool faceDown);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
protected:
|
||||
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,8 @@
|
|||
|
||||
#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_state.h"
|
||||
#include "../../interface/card_picture_loader/card_picture_loader.h"
|
||||
#include "../../interface/deck_loader/deck_file_format.h"
|
||||
#include "../../interface/deck_loader/deck_loader.h"
|
||||
#include "../../interface/deck_loader/loaded_deck.h"
|
||||
#include "../../interface/widgets/dialogs/dlg_load_deck.h"
|
||||
#include "../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h"
|
||||
#include "../../interface/widgets/dialogs/dlg_load_deck_from_website.h"
|
||||
|
|
@ -33,12 +28,12 @@
|
|||
#include <QRect>
|
||||
#include <QVBoxLayout>
|
||||
#include <libcockatrice/card/database/card_database_manager.h>
|
||||
#include <libcockatrice/deck_list/deck_file_format.h>
|
||||
#include <libcockatrice/deck_list/loaded_deck.h>
|
||||
#include <libcockatrice/deck_list/playmat_resolver.h>
|
||||
#include <libcockatrice/protocol/pb/command_deck_select.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_ready_start.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/game/abstract_game.h>
|
||||
#include <libcockatrice/game/game_event_handler.h>
|
||||
#include <libcockatrice/game/game_state.h>
|
||||
#include <libcockatrice/protocol/pb/response_deck_download.pb.h>
|
||||
#include <libcockatrice/protocol/pending_command.h>
|
||||
#include <libcockatrice/settings/interface_settings.h>
|
||||
|
|
@ -314,9 +309,7 @@ void DeckViewContainer::loadDeckFromDeckList(const DeckList &deck)
|
|||
return;
|
||||
}
|
||||
|
||||
Command_DeckSelect cmd;
|
||||
cmd.set_deck(deckString.toStdString());
|
||||
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(cmd);
|
||||
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareDeckSelect(deckString);
|
||||
connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished);
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId);
|
||||
|
||||
|
|
@ -345,15 +338,7 @@ void DeckViewContainer::resolveAndSendPlaymat()
|
|||
|
||||
lastResolvedPlaymat = resolved;
|
||||
|
||||
Command_SetPlaymat playmatCmd;
|
||||
auto *pp = playmatCmd.mutable_playmat_params();
|
||||
pp->set_card_name(resolved.card.name.toStdString());
|
||||
pp->set_card_provider_id(resolved.card.providerId.toStdString());
|
||||
pp->set_margin_pct_l(resolved.params.marginPctL);
|
||||
pp->set_margin_pct_r(resolved.params.marginPctR);
|
||||
pp->set_vertical_offset(resolved.params.verticalOffset);
|
||||
pp->set_zoom(resolved.params.zoom);
|
||||
PendingCommand *playmatPend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(playmatCmd);
|
||||
PendingCommand *playmatPend = parentGame->getGame()->getGameEventHandler()->prepareSetPlaymat(resolved);
|
||||
const QString sentName = resolved.card.name;
|
||||
connect(playmatPend, &PendingCommand::finished, this, [this, sentName](const Response &r) {
|
||||
// Only RespFunctionNotAllowed signals a card art DENY rule. Older servers that predate
|
||||
|
|
@ -384,9 +369,7 @@ void DeckViewContainer::loadRemoteDeck()
|
|||
{
|
||||
DlgLoadRemoteDeck dlg(parentGame->getGame()->getClientForPlayer(playerId), this);
|
||||
if (dlg.exec()) {
|
||||
Command_DeckSelect cmd;
|
||||
cmd.set_deck_id(dlg.getDeckId());
|
||||
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareGameCommand(cmd);
|
||||
PendingCommand *pend = parentGame->getGame()->getGameEventHandler()->prepareDeckSelectId(dlg.getDeckId());
|
||||
connect(pend, &PendingCommand::finished, this, &DeckViewContainer::deckSelectFinished);
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(pend, playerId);
|
||||
}
|
||||
|
|
@ -446,28 +429,17 @@ void DeckViewContainer::forceStart()
|
|||
return;
|
||||
}
|
||||
|
||||
Command_ReadyStart cmd;
|
||||
cmd.set_force_start(true);
|
||||
cmd.set_ready(true);
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
|
||||
parentGame->getGame()->getGameEventHandler()->sendForceStart(playerId);
|
||||
}
|
||||
|
||||
void DeckViewContainer::sideboardLockButtonClicked()
|
||||
{
|
||||
Command_SetSideboardLock cmd;
|
||||
cmd.set_locked(sideboardLockButton->getState());
|
||||
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
|
||||
parentGame->getGame()->getGameEventHandler()->sendSetSideboardLock(sideboardLockButton->getState(), playerId);
|
||||
}
|
||||
|
||||
void DeckViewContainer::sideboardPlanChanged()
|
||||
{
|
||||
Command_SetSideboardPlan cmd;
|
||||
const QList<MoveCard_ToZone> &newPlan = deckView->getSideboardPlan();
|
||||
for (const auto &i : newPlan) {
|
||||
cmd.add_move_list()->CopyFrom(i);
|
||||
}
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
|
||||
parentGame->getGame()->getGameEventHandler()->sendSetSideboardPlan(deckView->getSideboardPlan(), playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -479,9 +451,7 @@ void DeckViewContainer::sendReadyStartCommand(bool ready)
|
|||
resolveAndSendPlaymat();
|
||||
}
|
||||
|
||||
Command_ReadyStart cmd;
|
||||
cmd.set_ready(ready);
|
||||
parentGame->getGame()->getGameEventHandler()->sendGameCommand(cmd, playerId);
|
||||
parentGame->getGame()->getGameEventHandler()->sendReadyStart(ready, playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
13
cockatrice/src/game_graphics/dialogs/dlg_annotation.cpp
Normal file
13
cockatrice/src/game_graphics/dialogs/dlg_annotation.cpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#include "dlg_annotation.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
|
||||
void AnnotationDialog::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->key() == Qt::Key_Return && event->modifiers() & Qt::ControlModifier) {
|
||||
event->accept();
|
||||
accept();
|
||||
return;
|
||||
}
|
||||
QInputDialog::keyPressEvent(event);
|
||||
}
|
||||
22
cockatrice/src/game_graphics/dialogs/dlg_annotation.h
Normal file
22
cockatrice/src/game_graphics/dialogs/dlg_annotation.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* @file dlg_annotation.h
|
||||
* @ingroup GameDialogs
|
||||
*/
|
||||
|
||||
#ifndef COCKATRICE_DLG_ANNOTATION_H
|
||||
#define COCKATRICE_DLG_ANNOTATION_H
|
||||
|
||||
#include <QInputDialog>
|
||||
|
||||
class AnnotationDialog : public QInputDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
public:
|
||||
explicit AnnotationDialog(QWidget *parent = nullptr) : QInputDialog(parent)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_DLG_ANNOTATION_H
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <libcockatrice/game/player/token_info.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class QLabel;
|
||||
|
|
@ -25,17 +26,6 @@ class CardInfoPictureWidget;
|
|||
class QModelIndex;
|
||||
class QWidget;
|
||||
|
||||
struct TokenInfo
|
||||
{
|
||||
QString name;
|
||||
QString color;
|
||||
QString pt;
|
||||
QString annotation;
|
||||
bool destroy = true;
|
||||
bool faceDown = false;
|
||||
QString providerId;
|
||||
};
|
||||
|
||||
class DlgCreateToken : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <libcockatrice/game/player/move_top_cards_until_options.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class FilterString;
|
||||
|
|
@ -21,13 +22,6 @@ class QLabel;
|
|||
class QSpinBox;
|
||||
class QWidget;
|
||||
|
||||
struct MoveTopCardsUntilOptions
|
||||
{
|
||||
QStringList exprs = {};
|
||||
int numberOfHits = 1;
|
||||
bool autoPlay = false;
|
||||
};
|
||||
|
||||
class DlgMoveTopCardsUntil : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
|||
|
|
@ -1,18 +1,10 @@
|
|||
#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"
|
||||
#include "board/arrow_registry.h"
|
||||
#include "board/card_item.h"
|
||||
#include "phases_toolbar.h"
|
||||
#include "player/menu/player_menu.h"
|
||||
|
|
@ -40,6 +32,15 @@
|
|||
#include <QSharedPointer>
|
||||
#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>
|
||||
|
|
@ -112,6 +113,15 @@ void GameScene::retranslateUi()
|
|||
}
|
||||
}
|
||||
|
||||
QList<CardState *> GameScene::selectedCardStates() const
|
||||
{
|
||||
QList<CardState *> selectedStates;
|
||||
for (auto *card : selectedCards()) {
|
||||
selectedStates.append(card->getState());
|
||||
}
|
||||
return selectedStates;
|
||||
}
|
||||
|
||||
QList<CardItem *> GameScene::selectedCards() const
|
||||
{
|
||||
QList<CardItem *> selectedCards;
|
||||
|
|
@ -124,6 +134,67 @@ QList<CardItem *> GameScene::selectedCards() const
|
|||
return selectedCards;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Selects every card of @p zone for which @p filter holds.
|
||||
* @param filter A predicate to filter which cards are selected.
|
||||
*/
|
||||
static void selectCardsInZone(
|
||||
const CardZoneLogic *zone,
|
||||
std::function<bool(const CardItem *)> filter = [](const CardItem *) { return true; })
|
||||
{
|
||||
if (!zone) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto *cardState : zone->getCards()) {
|
||||
auto *cardItem = cardState == nullptr ? nullptr : qobject_cast<CardItem *>(cardState->parent());
|
||||
if (cardItem && filter(cardItem)) {
|
||||
cardItem->setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameScene::selectAllInZone(const CardItem *card)
|
||||
{
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
selectCardsInZone(card->getZone());
|
||||
}
|
||||
|
||||
void GameScene::selectSameRow(const CardItem *card)
|
||||
{
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto isSameRow = [card](const CardItem *cardItem) {
|
||||
return qAbs(card->scenePos().y() - cardItem->scenePos().y()) < 50;
|
||||
};
|
||||
selectCardsInZone(card->getZone(), isSameRow);
|
||||
}
|
||||
|
||||
void GameScene::selectSameColumn(const CardItem *card)
|
||||
{
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto isSameColumn = [card](const CardItem *cardItem) { return cardItem->x() == card->x(); };
|
||||
selectCardsInZone(card->getZone(), isSameColumn);
|
||||
}
|
||||
|
||||
CardItem *GameScene::getActiveCard() const
|
||||
{
|
||||
return activeCard;
|
||||
}
|
||||
|
||||
void GameScene::setActiveCard(CardItem *card)
|
||||
{
|
||||
activeCard = card;
|
||||
}
|
||||
|
||||
void GameScene::onCardSelectionChanged(AbstractCardItem *abstractCard, bool selected)
|
||||
{
|
||||
CardItem *card = qobject_cast<CardItem *>(abstractCard);
|
||||
|
|
@ -134,12 +205,12 @@ void GameScene::onCardSelectionChanged(AbstractCardItem *abstractCard, bool sele
|
|||
auto *owner = card->getOwner();
|
||||
|
||||
if (selected) {
|
||||
owner->requestCardMenuUpdate(card);
|
||||
owner->requestCardMenuUpdate(card->getState());
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedItems().isEmpty()) {
|
||||
owner->getGame()->setActiveCard(nullptr);
|
||||
setActiveCard(nullptr);
|
||||
owner->requestCardMenuUpdate(nullptr);
|
||||
}
|
||||
}
|
||||
|
|
@ -158,9 +229,9 @@ void GameScene::onCardRightClicked(AbstractCardItem *abstractCard, QPoint screen
|
|||
return;
|
||||
}
|
||||
|
||||
card->getOwner()->getGame()->setActiveCard(card);
|
||||
setActiveCard(card);
|
||||
|
||||
if (auto *menu = view->getPlayerMenu()->updateCardMenu(card)) {
|
||||
if (auto *menu = view->getPlayerMenu()->updateCardMenu(card->getState())) {
|
||||
menu->popup(screenPos);
|
||||
}
|
||||
}
|
||||
|
|
@ -173,7 +244,7 @@ void GameScene::playSelected(AbstractCardItem *card)
|
|||
if (!card->getOwner()) {
|
||||
return;
|
||||
}
|
||||
card->getOwner()->getPlayerActions()->actPlay(selectedCards());
|
||||
card->getOwner()->getPlayerActions()->actPlay(selectedCardStates());
|
||||
}
|
||||
|
||||
void GameScene::playSelectedFaceDown(AbstractCardItem *card)
|
||||
|
|
@ -184,7 +255,7 @@ void GameScene::playSelectedFaceDown(AbstractCardItem *card)
|
|||
if (!card->getOwner()) {
|
||||
return;
|
||||
}
|
||||
card->getOwner()->getPlayerActions()->actPlayFacedown(selectedCards());
|
||||
card->getOwner()->getPlayerActions()->actPlayFacedown(selectedCardStates());
|
||||
}
|
||||
|
||||
void GameScene::hideSelected(AbstractCardItem *card)
|
||||
|
|
@ -195,7 +266,7 @@ void GameScene::hideSelected(AbstractCardItem *card)
|
|||
if (!card->getOwner()) {
|
||||
return;
|
||||
}
|
||||
card->getOwner()->getPlayerActions()->actHide(selectedCards());
|
||||
card->getOwner()->getPlayerActions()->actHide(selectedCardStates());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -230,6 +301,8 @@ void GameScene::addPlayer(PlayerLogic *player)
|
|||
[this, id = player->getPlayerInfo()->getId()]() { clearArrowsForPlayerLocally(id); });
|
||||
|
||||
connect(player->getPlayerEventHandler(), &PlayerEventHandler::cardZoneChanged, this, &GameScene::onCardZoneChanged);
|
||||
connect(player->getPlayerEventHandler(), &PlayerEventHandler::cardViewRefreshRequested, this,
|
||||
&GameScene::onCardViewRefreshRequested);
|
||||
|
||||
rearrange();
|
||||
}
|
||||
|
|
@ -533,7 +606,11 @@ void GameScene::addArrow(QSharedPointer<ArrowData> data)
|
|||
return;
|
||||
}
|
||||
|
||||
CardItem *startCard = startZone->getCard(data->startCardId);
|
||||
CardState *startCardState = startZone->getCard(data->startCardId);
|
||||
if (!startCardState) {
|
||||
return;
|
||||
}
|
||||
CardItem *startCard = qobject_cast<CardItem *>(startCardState->parent());
|
||||
if (!startCard) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -544,7 +621,8 @@ void GameScene::addArrow(QSharedPointer<ArrowData> data)
|
|||
} else {
|
||||
auto *zone = targetView->getLogic()->getZones().value(data->targetZone);
|
||||
if (zone) {
|
||||
targetItem = zone->getCard(data->targetCardId);
|
||||
CardState *targetCardState = zone->getCard(data->targetCardId);
|
||||
targetItem = targetCardState == nullptr ? nullptr : qobject_cast<CardItem *>(targetCardState->parent());
|
||||
}
|
||||
}
|
||||
if (!targetItem) {
|
||||
|
|
@ -572,11 +650,15 @@ void GameScene::requestArrowDeletion(int playerId, int arrowId)
|
|||
}
|
||||
}
|
||||
|
||||
void GameScene::onCardZoneChanged(CardItem *card, bool sameZone)
|
||||
void GameScene::onCardZoneChanged(CardState *card, bool sameZone)
|
||||
{
|
||||
auto *item = qobject_cast<CardItem *>(card->parent());
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
QList<ArrowItem *> toDelete;
|
||||
for (auto *arrow : arrowRegistry.all()) {
|
||||
if (arrow->getStartItem() == card || arrow->getTargetItem() == card) {
|
||||
if (arrow->getStartItem() == item || arrow->getTargetItem() == item) {
|
||||
if (sameZone) {
|
||||
arrow->updatePath();
|
||||
} else {
|
||||
|
|
@ -589,6 +671,25 @@ void GameScene::onCardZoneChanged(CardItem *card, bool sameZone)
|
|||
}
|
||||
}
|
||||
|
||||
void GameScene::onCardViewRefreshRequested(CardState *card, bool zoneChanged, PlayerLogic *newOwner)
|
||||
{
|
||||
auto *item = qobject_cast<CardItem *>(card->parent());
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (zoneChanged) {
|
||||
item->deleteCardInfoPopup();
|
||||
}
|
||||
item->deleteDragItem();
|
||||
if (zoneChanged) {
|
||||
item->setBeingPointedAt(false);
|
||||
item->setHovered(false);
|
||||
}
|
||||
if (newOwner) {
|
||||
item->setOwner(newOwner);
|
||||
}
|
||||
}
|
||||
|
||||
void GameScene::clearArrowsForPlayer(int playerId)
|
||||
{
|
||||
for (int arrowId : arrowRegistry.idsForPlayer(playerId)) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#ifndef GAMESCENE_H
|
||||
#define GAMESCENE_H
|
||||
|
||||
#include "../game/arrow_registry.h"
|
||||
#include "animated_item.h"
|
||||
#include "board/arrow_item.h"
|
||||
#include "board/arrow_registry.h"
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QHash>
|
||||
|
|
@ -16,6 +18,8 @@
|
|||
#include <QString>
|
||||
#include <QTransform>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/game/board/arrow_data.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
// IWYU pragma: keep
|
||||
|
|
@ -39,6 +43,7 @@ class ZoneViewWidget;
|
|||
class CardZone;
|
||||
class AbstractCardItem;
|
||||
class CardItem;
|
||||
class CardState;
|
||||
class ServerInfo_Card;
|
||||
class PhasesToolbar;
|
||||
class QBasicTimer;
|
||||
|
|
@ -69,6 +74,7 @@ private:
|
|||
QList<ZoneViewWidget *> zoneViews; ///< Active zone view widgets
|
||||
QSize viewSize; ///< Current view size
|
||||
QPointer<CardItem> hoveredCard; ///< Currently hovered card
|
||||
QPointer<CardItem> activeCard; ///< Card the currently open context menu targets
|
||||
QBasicTimer *animationTimer; ///< Timer for scene animations
|
||||
QHash<QObject *, IAnimatedItem *> animatedItems; ///< Items currently animating
|
||||
QHash<QObject *, QMetaObject::Connection>
|
||||
|
|
@ -107,6 +113,22 @@ public:
|
|||
|
||||
/** @brief Gets all selected CardItems. */
|
||||
QList<CardItem *> selectedCards() const;
|
||||
QList<CardState *> selectedCardStates() const;
|
||||
|
||||
/** @brief Selects every card sharing the zone of @p card. */
|
||||
void selectAllInZone(const CardItem *card);
|
||||
|
||||
/** @brief Selects the cards laid out in the same visual row as @p card. */
|
||||
void selectSameRow(const CardItem *card);
|
||||
|
||||
/** @brief Selects the cards laid out in the same visual column as @p card. */
|
||||
void selectSameColumn(const CardItem *card);
|
||||
|
||||
/** @brief Card the currently open context menu targets, or null. */
|
||||
CardItem *getActiveCard() const;
|
||||
|
||||
/** @brief Sets the card the currently open context menu targets. */
|
||||
void setActiveCard(CardItem *card);
|
||||
|
||||
/**
|
||||
* @brief Adds a player to the scene and stores their graphics item.
|
||||
|
|
@ -261,7 +283,15 @@ public slots:
|
|||
/// Queues up arrow deletion but doesn't directly modify the scene
|
||||
void requestArrowDeletion(int playerId, int arrowId);
|
||||
|
||||
void onCardZoneChanged(CardItem *card, bool sameZone);
|
||||
void onCardZoneChanged(CardState *card, bool sameZone);
|
||||
/**
|
||||
* @brief Refreshes the view of a card that the server just moved.
|
||||
*
|
||||
* @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 onCardViewRefreshRequested(CardState *card, bool zoneChanged, PlayerLogic *newOwner);
|
||||
|
||||
protected:
|
||||
/** @brief Handles hover updates. */
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
#include "hand_counter.h"
|
||||
|
||||
#include "../game/board/card_list.h"
|
||||
#include "../game/zones/card_zone_logic.h"
|
||||
#include "../interface/pixel_map_generator.h"
|
||||
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
|
|
@ -14,6 +12,8 @@
|
|||
#include <QString>
|
||||
#include <QStringLiteral>
|
||||
#include <QTransform>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
class QGraphicsItem;
|
||||
|
|
|
|||
|
|
@ -3,16 +3,6 @@
|
|||
#include "../../client/settings/cache_settings.h"
|
||||
#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 +14,16 @@
|
|||
#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/translation.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>
|
||||
|
|
@ -283,7 +283,7 @@ void MessageLogWidget::logDestroyCard(PlayerLogic *player, QString cardName)
|
|||
}
|
||||
|
||||
void MessageLogWidget::logMoveCard(PlayerLogic *player,
|
||||
CardItem *card,
|
||||
CardState *card,
|
||||
CardZoneLogic *startZone,
|
||||
int oldX,
|
||||
CardZoneLogic *targetZone,
|
||||
|
|
@ -662,7 +662,7 @@ void MessageLogWidget::logSetActivePlayer(PlayerLogic *player)
|
|||
QString(tr("%1's turn.")).arg(player->getPlayerInfo()->getName()) + "</b></font><br>");
|
||||
}
|
||||
|
||||
void MessageLogWidget::logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation)
|
||||
void MessageLogWidget::logSetAnnotation(PlayerLogic *player, CardState *card, QString newAnnotation)
|
||||
{
|
||||
appendHtmlServerMessage(
|
||||
QString(tr("%1 sets annotation of %2 to %3."))
|
||||
|
|
@ -706,7 +706,7 @@ void MessageLogWidget::logSetCounter(PlayerLogic *player, QString counterName, i
|
|||
.arg(value - oldValue));
|
||||
}
|
||||
|
||||
void MessageLogWidget::logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap)
|
||||
void MessageLogWidget::logSetDoesntUntap(PlayerLogic *player, CardState *card, bool doesntUntap)
|
||||
{
|
||||
QString str;
|
||||
if (doesntUntap) {
|
||||
|
|
@ -717,7 +717,7 @@ void MessageLogWidget::logSetDoesntUntap(PlayerLogic *player, CardItem *card, bo
|
|||
appendHtmlServerMessage(str.arg(sanitizeHtml(player->getPlayerInfo()->getName())).arg(cardLink(card->getName())));
|
||||
}
|
||||
|
||||
void MessageLogWidget::logSetPT(PlayerLogic *player, CardItem *card, QString newPT)
|
||||
void MessageLogWidget::logSetPT(PlayerLogic *player, CardState *card, QString newPT)
|
||||
{
|
||||
if (currentContext == MessageContext_MoveCard) {
|
||||
return;
|
||||
|
|
@ -755,7 +755,7 @@ void MessageLogWidget::logSetSideboardLock(PlayerLogic *player, bool locked)
|
|||
}
|
||||
}
|
||||
|
||||
void MessageLogWidget::logSetTapped(PlayerLogic *player, CardItem *card, bool tapped)
|
||||
void MessageLogWidget::logSetTapped(PlayerLogic *player, CardState *card, bool tapped)
|
||||
{
|
||||
if (currentContext == MessageContext_MoveCard) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <qtmetamacros.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
class CardState;
|
||||
class AbstractGame;
|
||||
class CardItem;
|
||||
class GameEventContext;
|
||||
|
|
@ -76,7 +77,7 @@ public slots:
|
|||
void logLeaveSpectator(QString name, QString reason);
|
||||
void logNotReadyStart(PlayerLogic *player);
|
||||
void logMoveCard(PlayerLogic *player,
|
||||
CardItem *card,
|
||||
CardState *card,
|
||||
CardZoneLogic *startZone,
|
||||
int oldX,
|
||||
CardZoneLogic *targetZone,
|
||||
|
|
@ -97,13 +98,13 @@ public slots:
|
|||
void logSay(PlayerLogic *player, QString message);
|
||||
void logSetActivePhase(int phase);
|
||||
void logSetActivePlayer(PlayerLogic *player);
|
||||
void logSetAnnotation(PlayerLogic *player, CardItem *card, QString newAnnotation);
|
||||
void logSetAnnotation(PlayerLogic *player, CardState *card, QString newAnnotation);
|
||||
void logSetCardCounter(PlayerLogic *player, QString cardName, int counterId, int value, int oldValue);
|
||||
void logSetCounter(PlayerLogic *player, QString counterName, int value, int oldValue);
|
||||
void logSetDoesntUntap(PlayerLogic *player, CardItem *card, bool doesntUntap);
|
||||
void logSetPT(PlayerLogic *player, CardItem *card, QString newPT);
|
||||
void logSetDoesntUntap(PlayerLogic *player, CardState *card, bool doesntUntap);
|
||||
void logSetPT(PlayerLogic *player, CardState *card, QString newPT);
|
||||
void logSetSideboardLock(PlayerLogic *player, bool locked);
|
||||
void logSetTapped(PlayerLogic *player, CardItem *card, bool tapped);
|
||||
void logSetTapped(PlayerLogic *player, CardState *card, bool tapped);
|
||||
void logShuffle(PlayerLogic *player, CardZoneLogic *zone, int start, int end);
|
||||
void logSpectatorSay(const ServerInfo_User &spectator, QString message);
|
||||
void logUnattachCard(PlayerLogic *player, QString cardName);
|
||||
|
|
|
|||
|
|
@ -14,11 +14,6 @@
|
|||
#include <QTransform>
|
||||
#include <QtGlobal>
|
||||
#include <google/protobuf/stubs/port.h>
|
||||
#include <libcockatrice/protocol/pb/command_draw_cards.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_next_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/utility/zone_names.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qnumeric.h>
|
||||
|
||||
|
|
@ -120,9 +115,9 @@ PhasesToolbar::PhasesToolbar(QGraphicsItem *parent)
|
|||
: QGraphicsItem(parent), width(100), height(100), ySpacing(1), symbolSize(8)
|
||||
{
|
||||
auto *aUntapAll = new QAction(this);
|
||||
connect(aUntapAll, &QAction::triggered, this, &PhasesToolbar::actUntapAll);
|
||||
connect(aUntapAll, &QAction::triggered, this, &PhasesToolbar::untapAllRequested);
|
||||
auto *aDrawCard = new QAction(this);
|
||||
connect(aDrawCard, &QAction::triggered, this, &PhasesToolbar::actDrawCard);
|
||||
connect(aDrawCard, &QAction::triggered, this, &PhasesToolbar::drawCardRequested);
|
||||
|
||||
PhaseButton *untapButton = new PhaseButton("untap", this, aUntapAll);
|
||||
PhaseButton *upkeepButton = new PhaseButton("upkeep", this);
|
||||
|
|
@ -144,7 +139,7 @@ PhasesToolbar::PhasesToolbar(QGraphicsItem *parent)
|
|||
}
|
||||
|
||||
nextTurnButton = new PhaseButton("nextturn", this, nullptr, false);
|
||||
connect(nextTurnButton, &PhaseButton::clicked, this, &PhasesToolbar::actNextTurn);
|
||||
connect(nextTurnButton, &PhaseButton::clicked, this, &PhasesToolbar::nextTurnRequested);
|
||||
|
||||
rearrangeButtons();
|
||||
|
||||
|
|
@ -265,31 +260,5 @@ void PhasesToolbar::phaseButtonClicked()
|
|||
button->triggerDoubleClickAction();
|
||||
}
|
||||
|
||||
Command_SetActivePhase cmd;
|
||||
cmd.set_phase(static_cast<google::protobuf::uint32>(buttonList.indexOf(button)));
|
||||
|
||||
emit sendGameCommand(cmd, -1);
|
||||
}
|
||||
|
||||
void PhasesToolbar::actNextTurn()
|
||||
{
|
||||
emit sendGameCommand(Command_NextTurn(), -1);
|
||||
}
|
||||
|
||||
void PhasesToolbar::actUntapAll()
|
||||
{
|
||||
Command_SetCardAttr cmd;
|
||||
cmd.set_zone(ZoneNames::TABLE);
|
||||
cmd.set_attribute(AttrTapped);
|
||||
cmd.set_attr_value("0");
|
||||
|
||||
emit sendGameCommand(cmd, -1);
|
||||
}
|
||||
|
||||
void PhasesToolbar::actDrawCard()
|
||||
{
|
||||
Command_DrawCards cmd;
|
||||
cmd.set_number(1);
|
||||
|
||||
emit sendGameCommand(cmd, -1);
|
||||
emit setActivePhaseRequested(buttonList.indexOf(button));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,14 +17,6 @@
|
|||
class QAction;
|
||||
class QTimer;
|
||||
|
||||
namespace google
|
||||
{
|
||||
namespace protobuf
|
||||
{
|
||||
class Message;
|
||||
}
|
||||
} // namespace google
|
||||
|
||||
class PhaseButton : public QObject, public QGraphicsItem
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -95,11 +87,16 @@ public slots:
|
|||
void triggerPhaseAction(int phase);
|
||||
private slots:
|
||||
void phaseButtonClicked();
|
||||
void actNextTurn();
|
||||
void actUntapAll();
|
||||
void actDrawCard();
|
||||
signals:
|
||||
void sendGameCommand(const ::google::protobuf::Message &command, int playerId);
|
||||
/** @name Intent signals
|
||||
* The view reports what the user asked for; GameEventHandler builds the commands.
|
||||
* @{
|
||||
*/
|
||||
void setActivePhaseRequested(int phase);
|
||||
void nextTurnRequested();
|
||||
void untapAllRequested();
|
||||
void drawCardRequested();
|
||||
/** @} */
|
||||
|
||||
protected:
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) override;
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* @file card_menu_action_type.h
|
||||
* @ingroup GameMenusPlayers
|
||||
*/
|
||||
//! \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
|
||||
|
|
@ -3,16 +3,8 @@
|
|||
#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/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 "../card_menu_action_type.h"
|
||||
#include "../player_graphics_item.h"
|
||||
#include "libcockatrice/card/card_info.h"
|
||||
#include "libcockatrice/card/database/card_database_querier.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>
|
||||
|
|
@ -67,7 +67,7 @@ static QAction *makeAction(QObject *parent, Slot &&slot, bool checkable = false,
|
|||
return a;
|
||||
}
|
||||
|
||||
CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _shortcutsActive)
|
||||
CardMenu::CardMenu(PlayerGraphicsItem *_player, CardItem *_card, bool _shortcutsActive)
|
||||
: player(_player), card(_card), shortcutsActive(_shortcutsActive)
|
||||
{
|
||||
const QList<PlayerLogic *> &players = player->getLogic()->getGame()->getPlayerManager()->getPlayers().values();
|
||||
|
|
@ -86,7 +86,7 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _sho
|
|||
auto *gameScene = player->getGameScene();
|
||||
|
||||
// Single selection resolver used by all lambdas — called at trigger time
|
||||
auto sel = [gameScene]() { return gameScene->selectedCards(); };
|
||||
auto sel = [gameScene]() { return gameScene->selectedCardStates(); };
|
||||
|
||||
// Unified dispatcher for card menu actions
|
||||
auto invoke = [actions, sel](CardMenuActionType type) {
|
||||
|
|
@ -108,18 +108,18 @@ CardMenu::CardMenu(PlayerGraphicsItem *_player, const CardItem *_card, bool _sho
|
|||
aHide = makeAction(this, [actions, sel]() { actions->actHide(sel()); });
|
||||
aReduceLifeByPower = makeAction(this, [actions, sel]() { actions->actReduceLifeByPower(sel()); });
|
||||
|
||||
// Actions that use activeCard, not selection — direct connection
|
||||
// Actions bound to the card this menu was opened for, not to the current selection
|
||||
aAttach = new QAction(this);
|
||||
aDrawArrow = new QAction(this);
|
||||
aSelectAll = new QAction(this);
|
||||
aSelectRow = new QAction(this);
|
||||
aSelectColumn = new QAction(this);
|
||||
|
||||
connect(aAttach, &QAction::triggered, actions, &PlayerActions::actAttach);
|
||||
connect(aDrawArrow, &QAction::triggered, actions, &PlayerActions::actDrawArrow);
|
||||
connect(aSelectAll, &QAction::triggered, actions, &PlayerActions::actSelectAll);
|
||||
connect(aSelectRow, &QAction::triggered, actions, &PlayerActions::actSelectRow);
|
||||
connect(aSelectColumn, &QAction::triggered, actions, &PlayerActions::actSelectColumn);
|
||||
connect(aAttach, &QAction::triggered, this, &CardMenu::attachCard);
|
||||
connect(aDrawArrow, &QAction::triggered, this, &CardMenu::drawArrow);
|
||||
connect(aSelectAll, &QAction::triggered, this, [this, gameScene]() { gameScene->selectAllInZone(card); });
|
||||
connect(aSelectRow, &QAction::triggered, this, [this, gameScene]() { gameScene->selectSameRow(card); });
|
||||
connect(aSelectColumn, &QAction::triggered, this, [this, gameScene]() { gameScene->selectSameColumn(card); });
|
||||
|
||||
aRevealToAll = new QAction(this);
|
||||
|
||||
|
|
@ -337,7 +337,7 @@ void CardMenu::createHandOrCustomZoneMenu(bool canModifyCard)
|
|||
initContextualPlayersMenu(revealMenu, aRevealToAll);
|
||||
|
||||
connect(revealMenu, &QMenu::triggered, this, [this](QAction *action) {
|
||||
player->getLogic()->getPlayerActions()->actReveal(player->getGameScene()->selectedCards(), action);
|
||||
player->getLogic()->getPlayerActions()->actReveal(player->getGameScene()->selectedCardStates(), action);
|
||||
});
|
||||
|
||||
addSeparator();
|
||||
|
|
@ -441,6 +441,7 @@ void CardMenu::addRelatedCardActions()
|
|||
if (relatedCards.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto *playerActions = player->getLogic()->getPlayerActions();
|
||||
|
||||
addSeparator();
|
||||
int index = 0;
|
||||
|
|
@ -484,9 +485,10 @@ void CardMenu::addRelatedCardActions()
|
|||
}
|
||||
|
||||
auto *createRelated = new QAction(text, this);
|
||||
createRelated->setData(QVariant(index++));
|
||||
connect(createRelated, &QAction::triggered, player->getLogic()->getPlayerActions(),
|
||||
&PlayerActions::actCreateRelatedCard);
|
||||
const int relatedIndex = index++;
|
||||
connect(createRelated, &QAction::triggered, this, [this, playerActions, relatedIndex] {
|
||||
playerActions->actCreateRelatedCard(card->getState(), relatedIndex);
|
||||
});
|
||||
addAction(createRelated);
|
||||
}
|
||||
|
||||
|
|
@ -495,12 +497,26 @@ void CardMenu::addRelatedCardActions()
|
|||
createRelatedCards->setShortcuts(
|
||||
SettingsCache::instance().shortcuts().getShortcut("Player/aCreateRelatedTokens"));
|
||||
}
|
||||
connect(createRelatedCards, &QAction::triggered, player->getLogic()->getPlayerActions(),
|
||||
&PlayerActions::actCreateAllRelatedCards);
|
||||
connect(createRelatedCards, &QAction::triggered, this,
|
||||
[this, playerActions] { playerActions->actCreateAllRelatedCards(card->getState()); });
|
||||
addAction(createRelatedCards);
|
||||
}
|
||||
}
|
||||
|
||||
void CardMenu::attachCard()
|
||||
{
|
||||
if (card) {
|
||||
card->drawAttachArrow();
|
||||
}
|
||||
}
|
||||
|
||||
void CardMenu::drawArrow()
|
||||
{
|
||||
if (card) {
|
||||
card->drawArrow(Qt::red);
|
||||
}
|
||||
}
|
||||
|
||||
void CardMenu::retranslateUi()
|
||||
{
|
||||
aSelectAll->setText(tr("&Select All"));
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ signals:
|
|||
void cardInfoRequested(const CardRef &cardRef);
|
||||
|
||||
public:
|
||||
explicit CardMenu(PlayerGraphicsItem *player, const CardItem *card, bool shortcutsActive);
|
||||
explicit CardMenu(PlayerGraphicsItem *player, CardItem *card, bool shortcutsActive);
|
||||
void removePlayer(PlayerLogic *playerToRemove);
|
||||
void createTableMenu(bool canModifyCard);
|
||||
void createStackMenu(bool canModifyCard);
|
||||
|
|
@ -53,11 +53,13 @@ public:
|
|||
|
||||
private:
|
||||
PlayerGraphicsItem *player;
|
||||
const CardItem *card;
|
||||
CardItem *card;
|
||||
QList<QPair<QString, int>> playersInfo;
|
||||
bool shortcutsActive;
|
||||
|
||||
void addRelatedCardActions();
|
||||
void attachCard();
|
||||
void drawArrow();
|
||||
void retranslateUi();
|
||||
void initContextualPlayersMenu(QMenu *menu, QAction *allPlayersAction);
|
||||
void setShortcutsActive();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
#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 "../card_menu_action_type.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"))
|
||||
{
|
||||
|
|
@ -30,7 +30,7 @@ MoveMenu::MoveMenu(PlayerGraphicsItem *player) : QMenu(tr("Move to"))
|
|||
|
||||
auto invoke = [player](CardMenuActionType type) {
|
||||
return [type, player]() {
|
||||
player->getLogic()->getPlayerActions()->cardMenuAction(player->getGameScene()->selectedCards(), type);
|
||||
player->getLogic()->getPlayerActions()->cardMenuAction(player->getGameScene()->selectedCardStates(), type);
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../../client/settings/shortcuts_settings.h"
|
||||
#include "../../../game/abstract_game.h"
|
||||
#include "../../../game/player/player_info.h"
|
||||
#include "../../../game/player/player_logic.h"
|
||||
#include "../../../game/player/player_manager.h"
|
||||
#include "../../../game_graphics/zones/hand_zone.h"
|
||||
#include "../../../game_graphics/zones/pile_zone.h"
|
||||
#include "../../../game_graphics/zones/table_zone.h"
|
||||
|
|
@ -26,6 +22,10 @@
|
|||
|
||||
#include <QMenu>
|
||||
#include <QString>
|
||||
#include <libcockatrice/game/abstract_game.h>
|
||||
#include <libcockatrice/game/player/player_info.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/game/player/player_manager.h>
|
||||
|
||||
PlayerMenu::PlayerMenu(PlayerGraphicsItem *_player) : QObject(_player), player(_player)
|
||||
{
|
||||
|
|
@ -87,22 +87,24 @@ void PlayerMenu::setMenusForGraphicItems()
|
|||
}
|
||||
}
|
||||
|
||||
QMenu *PlayerMenu::updateCardMenu(const CardItem *card)
|
||||
QMenu *PlayerMenu::updateCardMenu(CardState *card)
|
||||
{
|
||||
if (!card) {
|
||||
emit cardMenuUpdated(nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto *cardItem = qobject_cast<CardItem *>(card->parent());
|
||||
|
||||
// If is spectator (as spectators don't need card menus), return
|
||||
// only update the menu if the card is actually selected
|
||||
if ((player->getLogic()->getGame()->getPlayerManager()->isSpectator() &&
|
||||
!player->getLogic()->getGame()->getPlayerManager()->isJudge()) ||
|
||||
player->getLogic()->getGame()->getActiveCard() != card) {
|
||||
cardItem == nullptr || player->getGameScene()->getActiveCard() != cardItem) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CardMenu *menu = new CardMenu(player, card, shortcutsActive);
|
||||
CardMenu *menu = new CardMenu(player, cardItem, shortcutsActive);
|
||||
connect(menu, &CardMenu::cardInfoRequested, this, &PlayerMenu::cardInfoRequested);
|
||||
emit cardMenuUpdated(menu);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#include <qtmetamacros.h>
|
||||
#include <utility>
|
||||
|
||||
class CardItem;
|
||||
class CardState;
|
||||
class CardMenu;
|
||||
class PlayerGraphicsItem;
|
||||
class AbstractPlayerComponent;
|
||||
|
|
@ -43,7 +43,7 @@ signals:
|
|||
|
||||
public slots:
|
||||
void setMenusForGraphicItems();
|
||||
QMenu *updateCardMenu(const CardItem *card);
|
||||
QMenu *updateCardMenu(CardState *card);
|
||||
|
||||
private slots:
|
||||
void refreshShortcuts();
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
{
|
||||
|
|
@ -15,34 +15,35 @@ PtMenu::PtMenu(PlayerGraphicsItem *player) : QMenu(tr("Power / toughness"))
|
|||
|
||||
aIncP = new QAction(this);
|
||||
connect(aIncP, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actIncP(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actIncP(player->getGameScene()->selectedCardStates()); });
|
||||
aDecP = new QAction(this);
|
||||
connect(aDecP, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actDecP(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actDecP(player->getGameScene()->selectedCardStates()); });
|
||||
aIncT = new QAction(this);
|
||||
connect(aIncT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actIncT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actIncT(player->getGameScene()->selectedCardStates()); });
|
||||
aDecT = new QAction(this);
|
||||
connect(aDecT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actDecT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actDecT(player->getGameScene()->selectedCardStates()); });
|
||||
aIncPT = new QAction(this);
|
||||
connect(aIncPT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actIncPT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actIncPT(player->getGameScene()->selectedCardStates()); });
|
||||
aDecPT = new QAction(this);
|
||||
connect(aDecPT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actDecPT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actDecPT(player->getGameScene()->selectedCardStates()); });
|
||||
aFlowP = new QAction(this);
|
||||
connect(aFlowP, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actFlowP(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actFlowP(player->getGameScene()->selectedCardStates()); });
|
||||
aFlowT = new QAction(this);
|
||||
connect(aFlowT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actFlowT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actFlowT(player->getGameScene()->selectedCardStates()); });
|
||||
aSetPT = new QAction(this);
|
||||
connect(aSetPT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actRequestSetPTDialog(player->getGameScene()->selectedCards()); });
|
||||
connect(aSetPT, &QAction::triggered, playerActions, [player, playerActions] {
|
||||
playerActions->actRequestSetPTDialog(player->getGameScene()->selectedCardStates());
|
||||
});
|
||||
aResetPT = new QAction(this);
|
||||
connect(aResetPT, &QAction::triggered, playerActions,
|
||||
[player, playerActions] { playerActions->actResetPT(player->getGameScene()->selectedCards()); });
|
||||
[player, playerActions] { playerActions->actResetPT(player->getGameScene()->selectedCardStates()); });
|
||||
|
||||
addAction(aIncP);
|
||||
addAction(aDecP);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
@ -44,7 +44,7 @@ UtilityMenu::UtilityMenu(PlayerGraphicsItem *_player, QMenu *playerMenu) : QMenu
|
|||
aIncrementAllCardCounters = new QAction(this);
|
||||
connect(aIncrementAllCardCounters, &QAction::triggered, playerActions, [this]() {
|
||||
player->getLogic()->getPlayerActions()->actIncrementAllCardCounters(
|
||||
player->getGameScene()->selectedCards());
|
||||
player->getGameScene()->selectedCardStates());
|
||||
});
|
||||
|
||||
createPredefinedTokenMenu = new QMenu(QString());
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
|
||||
#include "../../client/settings/cache_settings.h"
|
||||
#include "../../client/settings/card_counter_settings.h"
|
||||
#include "../../game/player/player_actions.h"
|
||||
#include "../../game/player/player_logic.h"
|
||||
#include "../../interface/widgets/utility/get_text_with_max.h"
|
||||
#include "../board/abstract_counter.h"
|
||||
#include "../board/card_item.h"
|
||||
#include "../dialogs/dlg_annotation.h"
|
||||
#include "../dialogs/dlg_create_token.h"
|
||||
#include "../dialogs/dlg_move_top_cards_until.h"
|
||||
#include "../dialogs/dlg_roll_dice.h"
|
||||
|
|
@ -17,6 +16,8 @@
|
|||
#include <QInputDialog>
|
||||
#include <QLineEdit>
|
||||
#include <libcockatrice/card/relation/card_relation.h>
|
||||
#include <libcockatrice/game/player/player_actions.h>
|
||||
#include <libcockatrice/game/player/player_logic.h>
|
||||
#include <libcockatrice/utility/string_limits.h>
|
||||
|
||||
PlayerDialogs::PlayerDialogs(PlayerGraphicsItem *_player, PlayerActions *_playerActions)
|
||||
|
|
@ -34,7 +35,7 @@ PlayerDialogs::PlayerDialogs(PlayerGraphicsItem *_player, PlayerActions *_player
|
|||
&PlayerDialogs::onShuffleBottomDialogRequested);
|
||||
|
||||
connect(playerActions, &PlayerActions::requestMulliganDialog, this, &PlayerDialogs::onMulliganDialogRequested);
|
||||
|
||||
connect(playerActions, &PlayerActions::startingHandSizeChosen, this, &PlayerDialogs::onStartingHandSizeChosen);
|
||||
connect(playerActions, &PlayerActions::requestDrawCardsDialog, this, &PlayerDialogs::onDrawCardsDialogRequested);
|
||||
|
||||
connect(playerActions, &PlayerActions::requestMoveTopCardsToDialog, this,
|
||||
|
|
@ -126,6 +127,11 @@ void PlayerDialogs::onMulliganDialogRequested(int startSize, int handSize, int d
|
|||
}
|
||||
}
|
||||
|
||||
void PlayerDialogs::onStartingHandSizeChosen(int number)
|
||||
{
|
||||
SettingsCache::instance().userInterface().setStartingHandSize(number);
|
||||
}
|
||||
|
||||
void PlayerDialogs::onDrawCardsDialogRequested(int defaultNumberTopCards, int deckSize)
|
||||
{
|
||||
bool ok;
|
||||
|
|
@ -196,7 +202,7 @@ void PlayerDialogs::onRollDieDialogRequested()
|
|||
playerActions->actRollDie(dlg.getDieSideCount(), dlg.getDiceToRollCount());
|
||||
}
|
||||
|
||||
void PlayerDialogs::onCreateRelatedFromRelationDialogRequested(const CardItem *sourceCard,
|
||||
void PlayerDialogs::onCreateRelatedFromRelationDialogRequested(const CardState *sourceCard,
|
||||
const CardRelation *cardRelation)
|
||||
{
|
||||
if (sourceCard == nullptr || cardRelation == nullptr) {
|
||||
|
|
@ -251,14 +257,14 @@ void PlayerDialogs::onMoveCardXCardsFromTopDialogRequested(int defaultNumberTopC
|
|||
number -= 1; // indexes start at 0
|
||||
|
||||
if (ok) {
|
||||
playerActions->actMoveCardXCardsFromTop(player->getGameScene()->selectedCards(), number);
|
||||
playerActions->actMoveCardXCardsFromTop(player->getGameScene()->selectedCardStates(), number);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerDialogs::onSetPTDialogRequested(const QString &oldPT)
|
||||
{
|
||||
bool ok;
|
||||
auto cards = player->getGameScene()->selectedCards();
|
||||
auto cards = player->getGameScene()->selectedCardStates();
|
||||
emit requestDialogSemaphore(true);
|
||||
QString pt = getTextWithMax(dialogParent(), tr("Change power/toughness"), tr("Change stats to:"), QLineEdit::Normal,
|
||||
oldPT, &ok);
|
||||
|
|
@ -273,7 +279,7 @@ void PlayerDialogs::onSetPTDialogRequested(const QString &oldPT)
|
|||
|
||||
void PlayerDialogs::onSetAnnotationDialogRequested(const QString &oldAnnotation)
|
||||
{
|
||||
auto cards = player->getGameScene()->selectedCards();
|
||||
auto cards = player->getGameScene()->selectedCardStates();
|
||||
emit requestDialogSemaphore(true);
|
||||
AnnotationDialog *dialog = new AnnotationDialog(dialogParent());
|
||||
dialog->setOptions(QInputDialog::UsePlainTextEditForTextInput);
|
||||
|
|
@ -291,7 +297,7 @@ void PlayerDialogs::onSetAnnotationDialogRequested(const QString &oldAnnotation)
|
|||
|
||||
void PlayerDialogs::onSetCardCounterDialogRequested(int counterId, const QString &oldValueForDlg)
|
||||
{
|
||||
auto cards = player->getGameScene()->selectedCards();
|
||||
auto cards = player->getGameScene()->selectedCardStates();
|
||||
emit requestDialogSemaphore(true);
|
||||
|
||||
auto &cardCounterSettings = SettingsCache::instance().cardCounters();
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
class CardItem;
|
||||
class CardRelation;
|
||||
class CardState;
|
||||
class PlayerActions;
|
||||
class QWidget;
|
||||
struct MoveTopCardsUntilOptions;
|
||||
|
|
@ -52,12 +53,13 @@ public slots:
|
|||
bool faceDown);
|
||||
void onDrawBottomCardsDialogRequested(int defaultNumberBottomCards, int maxCards);
|
||||
void onRollDieDialogRequested();
|
||||
void onCreateRelatedFromRelationDialogRequested(const CardItem *sourceCard, const CardRelation *cardRelation);
|
||||
void onCreateRelatedFromRelationDialogRequested(const CardState *sourceCard, const CardRelation *cardRelation);
|
||||
void onCreateTokenDialogRequested(const QStringList &predefinedTokens);
|
||||
void onMoveCardXCardsFromTopDialogRequested(int defaultNumberTopCardsToPlaceBelow, int deckSize);
|
||||
void onSetPTDialogRequested(const QString &oldPT);
|
||||
void onSetAnnotationDialogRequested(const QString &oldAnnotation);
|
||||
void onSetCardCounterDialogRequested(int counterId, const QString &oldValueForDlg);
|
||||
void onStartingHandSizeChosen(int number);
|
||||
|
||||
private:
|
||||
PlayerGraphicsItem *player;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -177,6 +177,22 @@ void PlayerGraphicsItem::initializeZones()
|
|||
|
||||
void PlayerGraphicsItem::onCustomZoneAdded(QString customZoneName)
|
||||
{
|
||||
// Custom zones have no board view, but their cards still need a graphics owner.
|
||||
// Without one, PileZone::onCardAdded never runs and cards moved from the table
|
||||
// or hand into a custom zone stay drawn at their old spot. Give them a hidden
|
||||
// pile zone so added cards are reparented to (0, 0) and hidden.
|
||||
CardZoneLogic *logic = player->getZone(customZoneName);
|
||||
if (auto *pileLogic = qobject_cast<PileZoneLogic *>(logic)) {
|
||||
auto *hiddenZone = new PileZone(pileLogic, this);
|
||||
hiddenZone->setVisible(false);
|
||||
hiddenZone->setPos(0, 0);
|
||||
zoneGraphicsItems.insert(customZoneName, hiddenZone);
|
||||
// Hide any cards that arrived before the graphics item existed.
|
||||
for (CardState *state : pileLogic->getCards()) {
|
||||
hiddenZone->onCardAdded(state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
zoneGraphicsItems.insert(customZoneName, nullptr); // Custom zone view goes here, if we ever implement it.
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ static int sumPowers(const QList<CardItem *> &cards)
|
|||
// calculate total power;
|
||||
int total = 0;
|
||||
for (auto card : cards) {
|
||||
QVariantList parsed = CardItem::parsePT(card->getPT());
|
||||
QVariantList parsed = CardState::parsePT(card->getPT());
|
||||
if (!parsed.isEmpty()) {
|
||||
int power = parsed.first().toInt(); // toInt will default to 0 if it's not an int
|
||||
total += qMax(power, 0);
|
||||
|
|
@ -44,7 +44,7 @@ static int sumToughness(const QList<CardItem *> &cards)
|
|||
{
|
||||
int total = 0;
|
||||
for (auto card : cards) {
|
||||
QVariantList parsed = CardItem::parsePT(card->getPT());
|
||||
QVariantList parsed = CardState::parsePT(card->getPT());
|
||||
if (parsed.size() == 2) {
|
||||
int toughness = parsed.at(1).toInt(); // toInt will default to 0 if it's not an int
|
||||
total += qMax(toughness, 0);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#include "card_zone.h"
|
||||
|
||||
#include "../../game/board/card_list.h"
|
||||
#include "../../game/zones/card_zone_logic.h"
|
||||
#include "../board/abstract_graphics_item.h"
|
||||
#include "../board/card_item.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QMenu>
|
||||
#include <libcockatrice/game/board/card_list.h>
|
||||
#include <libcockatrice/game/zones/card_zone_logic.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
class QGraphicsItem;
|
||||
|
|
@ -18,23 +18,60 @@ CardZone::CardZone(CardZoneLogic *_logic, QGraphicsItem *parent)
|
|||
: AbstractGraphicsItem(parent), menu(nullptr), doubleClickAction(0), logic(_logic)
|
||||
{
|
||||
connect(logic, &CardZoneLogic::retranslateUi, this, &CardZone::retranslateUi);
|
||||
// Invalidation is connected before any consumer so a handler that reads cardItems()
|
||||
// during the same signal emission always sees a stale flag and rebuilds.
|
||||
connect(logic, &CardZoneLogic::cardAdded, this, &CardZone::invalidateItems);
|
||||
connect(logic, &CardZoneLogic::cardCountChanged, this, &CardZone::invalidateItems);
|
||||
connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::invalidateItems);
|
||||
connect(logic, &CardZoneLogic::cardAdded, this, &CardZone::onCardAdded);
|
||||
connect(logic, &CardZoneLogic::createCardRequested, this, &CardZone::onCreateCardRequested);
|
||||
connect(logic, &CardZoneLogic::setGraphicsVisibility, this, [this](bool v) { this->setVisible(v); });
|
||||
connect(logic, &CardZoneLogic::updateGraphics, this, [this]() { update(); });
|
||||
connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::reorganizeCards);
|
||||
}
|
||||
|
||||
void CardZone::onCardAdded(CardItem *addedCard)
|
||||
void CardZone::invalidateItems()
|
||||
{
|
||||
addedCard->setParentItem(this);
|
||||
addedCard->setVisible(true);
|
||||
addedCard->update();
|
||||
itemsDirty = true;
|
||||
}
|
||||
|
||||
const QList<CardItem *> &CardZone::cardItems() const
|
||||
{
|
||||
if (itemsDirty) {
|
||||
items.clear();
|
||||
for (auto *cardState : getLogic()->getCards()) {
|
||||
if (auto *cardItem = qobject_cast<CardItem *>(cardState->parent())) {
|
||||
items.append(cardItem);
|
||||
}
|
||||
}
|
||||
itemsDirty = false;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
void CardZone::onCardAdded(CardState *addedCard)
|
||||
{
|
||||
auto *addedItem = addedCard == nullptr ? nullptr : qobject_cast<CardItem *>(addedCard->parent());
|
||||
if (addedItem == nullptr) {
|
||||
return;
|
||||
}
|
||||
addedItem->setParentItem(this);
|
||||
addedItem->setVisible(true);
|
||||
addedItem->update();
|
||||
}
|
||||
|
||||
void CardZone::onCreateCardRequested(const ServerInfo_Card &cardInfo, bool reorganize)
|
||||
{
|
||||
CardRef cardRef = {QString::fromStdString(cardInfo.name()), QString::fromStdString(cardInfo.provider_id())};
|
||||
auto *card = new CardItem(getLogic()->getPlayer(), nullptr, cardRef, cardInfo.id());
|
||||
card->processCardInfo(cardInfo);
|
||||
getLogic()->addCard(card->getState(), reorganize, cardInfo.x(), cardInfo.y());
|
||||
}
|
||||
|
||||
void CardZone::retranslateUi()
|
||||
{
|
||||
for (int i = 0; i < getLogic()->getCards().size(); ++i) {
|
||||
getLogic()->getCards()[i]->retranslateUi();
|
||||
for (auto *card : cardItems()) {
|
||||
card->retranslateUi();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,17 @@
|
|||
|
||||
class CardDragItem;
|
||||
class CardItem;
|
||||
class CardState;
|
||||
class CardZoneLogic;
|
||||
class QAction;
|
||||
class QGraphicsItem;
|
||||
class QMenu;
|
||||
class QPoint;
|
||||
class ServerInfo_Card;
|
||||
template <typename T> class QList;
|
||||
|
||||
class CardItem;
|
||||
|
||||
inline Q_LOGGING_CATEGORY(CardZoneLog, "card_zone");
|
||||
|
||||
/**
|
||||
|
|
@ -55,7 +59,9 @@ public slots:
|
|||
* Virtual so subclasses (e.g. SelectZone) can override parenting behavior — the Qt signal
|
||||
* connection in CardZone's constructor dispatches through the vtable.
|
||||
*/
|
||||
virtual void onCardAdded(CardItem *addedCard);
|
||||
virtual void onCardAdded(CardState *addedCard);
|
||||
/** @brief Builds the card item for @p cardInfo and hands it to this zone's logic. */
|
||||
void onCreateCardRequested(const ServerInfo_Card &cardInfo, bool reorganize);
|
||||
|
||||
public:
|
||||
enum
|
||||
|
|
@ -76,6 +82,15 @@ public:
|
|||
return logic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The CardItems backing getLogic()->getCards(), in zone order.
|
||||
*
|
||||
* Game logic talks about cards as states; this is the graphics side's view of the same list.
|
||||
* It is rebuilt on demand after any content change, so views never reach into the logic's
|
||||
* container themselves and never observe a stale one.
|
||||
*/
|
||||
[[nodiscard]] const QList<CardItem *> &cardItems() const;
|
||||
|
||||
void setMenu(QMenu *_menu, QAction *_doubleClickAction = 0)
|
||||
{
|
||||
menu = _menu;
|
||||
|
|
@ -83,7 +98,12 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
/** @brief Marks the cached card items stale. Connected to every content-changing signal. */
|
||||
void invalidateItems();
|
||||
|
||||
CardZoneLogic *logic;
|
||||
mutable QList<CardItem *> items;
|
||||
mutable bool itemsDirty = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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,7 +13,11 @@
|
|||
#include <QPointF>
|
||||
#include <QRectF>
|
||||
#include <QString>
|
||||
#include <libcockatrice/protocol/pb/command_move_card.pb.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/settings/interface_settings.h>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -48,8 +47,8 @@ void HandZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
|
|||
QPoint point = dropPoint + scenePos().toPoint();
|
||||
int x = -1;
|
||||
if (SettingsCache::instance().userInterface().getHorizontalHand()) {
|
||||
for (x = 0; x < getLogic()->getCards().size(); x++) {
|
||||
if (point.x() < static_cast<CardItem *>(getLogic()->getCards().at(x))->scenePos().x()) {
|
||||
for (x = 0; x < cardItems().size(); x++) {
|
||||
if (point.x() < cardItems().at(x)->scenePos().x()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -58,19 +57,13 @@ void HandZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
|
|||
x = calcDropIndexFromY(dropPoint.y(), !sameZone);
|
||||
}
|
||||
|
||||
Command_MoveCard cmd;
|
||||
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_start_zone(startZone->getName().toStdString());
|
||||
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(getLogic()->getName().toStdString());
|
||||
cmd.set_x(x);
|
||||
cmd.set_y(-1);
|
||||
|
||||
for (int i = 0; i < dragItems.size(); ++i) {
|
||||
cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId());
|
||||
QList<CardMoveRequest> cards;
|
||||
cards.reserve(dragItems.size());
|
||||
for (const CardDragItem *item : dragItems) {
|
||||
cards.append(CardMoveRequest{item->getId()});
|
||||
}
|
||||
|
||||
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
|
||||
getLogic()->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), x, -1, cards);
|
||||
}
|
||||
|
||||
QRectF HandZone::boundingRect() const
|
||||
|
|
@ -90,23 +83,23 @@ void HandZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio
|
|||
|
||||
void HandZone::reorganizeCards()
|
||||
{
|
||||
if (!getLogic()->getCards().isEmpty()) {
|
||||
const int cardCount = getLogic()->getCards().size();
|
||||
if (!cardItems().isEmpty()) {
|
||||
const int cardCount = cardItems().size();
|
||||
if (SettingsCache::instance().userInterface().getHorizontalHand()) {
|
||||
bool leftJustified = SettingsCache::instance().userInterface().getLeftJustified();
|
||||
qreal cardWidth = getLogic()->getCards().at(0)->boundingRect().width();
|
||||
qreal cardWidth = cardItems().at(0)->boundingRect().width();
|
||||
const int xPadding = leftJustified ? cardWidth * 1.4 : 5;
|
||||
qreal totalWidth =
|
||||
leftJustified ? boundingRect().width() - (1 * xPadding) - 5 : boundingRect().width() - 2 * xPadding;
|
||||
|
||||
if (cardCount == 1) {
|
||||
CardItem *c = getLogic()->getCards().at(0);
|
||||
CardItem *c = cardItems().at(0);
|
||||
qreal xPosition = leftJustified ? xPadding : xPadding + (totalWidth - cardWidth) / 2;
|
||||
c->setPos(xPosition, 5);
|
||||
c->setRealZValue(0);
|
||||
} else {
|
||||
for (int i = 0; i < cardCount; i++) {
|
||||
CardItem *c = getLogic()->getCards().at(i);
|
||||
CardItem *c = cardItems().at(i);
|
||||
// If the total width of the cards is smaller than the available width,
|
||||
// the cards do not need to overlap and are displayed in the center of the area.
|
||||
if (cardWidth * cardCount > totalWidth) {
|
||||
|
|
@ -131,7 +124,7 @@ void HandZone::reorganizeCards()
|
|||
|
||||
void HandZone::sortHand(const QList<CardList::SortOption> &options)
|
||||
{
|
||||
if (getLogic()->getCards().isEmpty()) {
|
||||
if (cardItems().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,7 +18,12 @@
|
|||
#include <QString>
|
||||
#include <QTransform>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/protocol/pb/command_move_card.pb.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/pile_zone_logic.h>
|
||||
#include <libcockatrice/settings/cards_display_settings.h>
|
||||
#include <qnamespace.h>
|
||||
#include <string>
|
||||
|
|
@ -67,36 +66,35 @@ void PileZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*optio
|
|||
{
|
||||
painter->drawPath(shape());
|
||||
|
||||
if (!getLogic()->getCards().isEmpty()) {
|
||||
getLogic()->getCards().at(0)->paintPicture(painter, getLogic()->getCards().at(0)->getTranslatedSize(painter),
|
||||
90);
|
||||
if (!cardItems().isEmpty()) {
|
||||
cardItems().at(0)->paintPicture(painter, cardItems().at(0)->getTranslatedSize(painter), 90);
|
||||
}
|
||||
|
||||
painter->translate(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F);
|
||||
painter->rotate(-90);
|
||||
painter->translate(-CardDimensions::WIDTH_HALF_F, -CardDimensions::HEIGHT_HALF_F);
|
||||
paintNumberEllipse(getLogic()->getCards().size(), 28, Qt::white, -1, -1, painter);
|
||||
paintNumberEllipse(cardItems().size(), 28, Qt::white, -1, -1, painter);
|
||||
}
|
||||
|
||||
void PileZone::handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneLogic *startZone, const QPoint &)
|
||||
{
|
||||
Command_MoveCard cmd;
|
||||
cmd.set_start_player_id(startZone->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_start_zone(startZone->getName().toStdString());
|
||||
cmd.set_target_player_id(getLogic()->getPlayer()->getPlayerInfo()->getId());
|
||||
cmd.set_target_zone(getLogic()->getName().toStdString());
|
||||
cmd.set_x(0);
|
||||
cmd.set_y(0);
|
||||
|
||||
for (int i = 0; i < dragItems.size(); ++i) {
|
||||
auto cardToMove = cmd.mutable_cards_to_move()->add_card();
|
||||
cardToMove->set_card_id(dragItems[i]->getId());
|
||||
if (dragItems[i]->isForceFaceDown()) {
|
||||
cardToMove->set_face_down(true);
|
||||
}
|
||||
QList<CardMoveRequest> cards;
|
||||
cards.reserve(dragItems.size());
|
||||
for (const CardDragItem *item : dragItems) {
|
||||
cards.append(CardMoveRequest{item->getId(), item->isForceFaceDown()});
|
||||
}
|
||||
|
||||
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
|
||||
getLogic()->getPlayer()->getPlayerActions()->moveCards(startZone, getLogic(), 0, 0, cards);
|
||||
}
|
||||
|
||||
void PileZone::onCardAdded(CardState *addedCard)
|
||||
{
|
||||
CardZone::onCardAdded(addedCard);
|
||||
auto *addedItem = addedCard == nullptr ? nullptr : qobject_cast<CardItem *>(addedCard->parent());
|
||||
if (addedItem) {
|
||||
addedItem->setPos(0, 0);
|
||||
addedItem->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void PileZone::reorganizeCards()
|
||||
|
|
@ -126,15 +124,14 @@ void PileZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|||
return;
|
||||
}
|
||||
|
||||
if (getLogic()->getCards().isEmpty()) {
|
||||
if (cardItems().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier);
|
||||
bool bottomCard = event->modifiers().testFlag(Qt::ControlModifier);
|
||||
CardItem *card = bottomCard ? getLogic()->getCards().last() : getLogic()->getCards().first();
|
||||
const int cardid =
|
||||
getLogic()->contentsKnown() ? card->getId() : (bottomCard ? getLogic()->getCards().size() - 1 : 0);
|
||||
CardItem *card = bottomCard ? cardItems().last() : cardItems().first();
|
||||
const int cardid = getLogic()->contentsKnown() ? card->getId() : (bottomCard ? cardItems().size() - 1 : 0);
|
||||
CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), forceFaceDown);
|
||||
drag->grabMouse();
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
|
|
@ -147,8 +144,8 @@ void PileZone::mouseReleaseEvent(QGraphicsSceneMouseEvent * /*event*/)
|
|||
|
||||
void PileZone::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
|
||||
{
|
||||
if (!getLogic()->getCards().isEmpty()) {
|
||||
getLogic()->getCards()[0]->processHoverEvent();
|
||||
if (!cardItems().isEmpty()) {
|
||||
cardItems()[0]->processHoverEvent();
|
||||
}
|
||||
QGraphicsItem::hoverEnterEvent(event);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ public:
|
|||
[[nodiscard]] QPainterPath shape() const override;
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
|
||||
void reorganizeCards() override;
|
||||
void onCardAdded(CardState *addedCard) override;
|
||||
void
|
||||
handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneLogic *startZone, const QPoint &dropPoint) override;
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue