[Refactor] Move card item creation out of game logic

This commit is contained in:
Lukas Brübach 2026-10-07 10:08:54 +02:00 • committed by GitHub
parent e0d2f158e9
commit 16caae5e5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 121 additions and 82 deletions

View file

@ -11,7 +11,6 @@
#include <QString> #include <QString>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class CardItem;
class AbstractClient; class AbstractClient;
class CardState; class CardState;
class GameEventHandler; class GameEventHandler;

View file

@ -12,8 +12,6 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
class CardZoneLogic; class CardZoneLogic;
class CardItem;
class CardState : public QObject class CardState : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -35,7 +33,7 @@ private:
QString color; QString color;
CardState *attachedTo = nullptr; CardState *attachedTo = nullptr;
QList<CardItem *> attachedCards; QList<CardState *> attachedCards;
CardZoneLogic *zone = nullptr; CardZoneLogic *zone = nullptr;
signals: signals:
@ -226,17 +224,17 @@ public:
void setAttachedTo(CardState *_attachedTo); void setAttachedTo(CardState *_attachedTo);
void addAttachedCard(CardItem *card) void addAttachedCard(CardState *card)
{ {
attachedCards.append(card); attachedCards.append(card);
} }
void removeAttachedCard(CardItem *card) void removeAttachedCard(CardState *card)
{ {
attachedCards.removeOne(card); attachedCards.removeOne(card);
} }
const QList<CardItem *> &getAttachedCards() const const QList<CardState *> &getAttachedCards() const
{ {
return attachedCards; return attachedCards;
} }

View file

@ -1,9 +1,9 @@
#include "player_event_handler.h" #include "player_event_handler.h"
#include "../../game_graphics/board/card_item.h"
#include "../abstract_game.h" #include "../abstract_game.h"
#include "../board/arrow_data.h" #include "../board/arrow_data.h"
#include "../board/card_list.h" #include "../board/card_list.h"
#include "../board/card_state.h"
#include "../board/counter_state.h" #include "../board/counter_state.h"
#include "../zones/card_zone_logic.h" #include "../zones/card_zone_logic.h"
#include "../zones/hand_zone_logic.h" #include "../zones/hand_zone_logic.h"
@ -27,6 +27,8 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QtGlobal> #include <QtGlobal>
#include <algorithm> #include <algorithm>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/protocol/pb/command_set_card_attr.pb.h>
#include <libcockatrice/protocol/pb/context_move_card.pb.h> #include <libcockatrice/protocol/pb/context_move_card.pb.h>
#include <libcockatrice/protocol/pb/context_undo_draw.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_attach_card.pb.h>
@ -49,15 +51,11 @@
#include <libcockatrice/protocol/pb/event_set_card_counter.pb.h> #include <libcockatrice/protocol/pb/event_set_card_counter.pb.h>
#include <libcockatrice/protocol/pb/event_set_counter.pb.h> #include <libcockatrice/protocol/pb/event_set_counter.pb.h>
#include <libcockatrice/protocol/pb/event_shuffle.pb.h> #include <libcockatrice/protocol/pb/event_shuffle.pb.h>
#include <libcockatrice/utility/color.h>
#include <libcockatrice/utility/zone_names.h> #include <libcockatrice/utility/zone_names.h>
#include <qlogging.h> #include <qlogging.h>
#include <string> #include <string>
static CardItem *toCardItem(CardState *card)
{
return card == nullptr ? nullptr : qobject_cast<CardItem *>(card->parent());
}
PlayerEventHandler::PlayerEventHandler(PlayerLogic *_player) : QObject(_player), player(_player) PlayerEventHandler::PlayerEventHandler(PlayerLogic *_player) : QObject(_player), player(_player)
{ {
connect(this, &PlayerEventHandler::requestCardMenuUpdate, player, &PlayerLogic::requestCardMenuUpdate); connect(this, &PlayerEventHandler::requestCardMenuUpdate, player, &PlayerLogic::requestCardMenuUpdate);
@ -160,23 +158,29 @@ void PlayerEventHandler::eventCreateToken(const Event_CreateToken &event)
} }
CardRef cardRef = {QString::fromStdString(event.card_name()), QString::fromStdString(event.card_provider_id())}; 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 // use db PT if not provided in event and not face-down
if (!QString::fromStdString(event.pt()).isEmpty()) { QString pt = QString::fromStdString(event.pt());
card->setPT(QString::fromStdString(event.pt())); if (pt.isEmpty() && !event.face_down()) {
} else if (!event.face_down()) { ExactCard dbCard = CardDatabaseManager::query()->getCard(cardRef);
ExactCard dbCard = card->getCard();
if (dbCard) { if (dbCard) {
card->setPT(dbCard.getInfo().getPowTough()); pt = 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()); emit logCreateToken(player, cardRef.name, pt, event.face_down());
zone->addCard(card->getState(), true, event.x(), event.y());
ServerInfo_Card cardInfo;
cardInfo.set_id(event.card_id());
cardInfo.set_name(event.card_name());
cardInfo.set_provider_id(event.card_provider_id());
cardInfo.set_pt(pt.toStdString());
cardInfo.set_color(event.color());
cardInfo.set_annotation(event.annotation());
cardInfo.set_destroy_on_zone_change(event.destroy_on_zone_change());
cardInfo.set_face_down(event.face_down());
cardInfo.set_x(event.x());
cardInfo.set_y(event.y());
emit zone->requestCreateCard(cardInfo, true);
} }
void PlayerEventHandler::eventSetCardAttr(const Event_SetCardAttr &event, void PlayerEventHandler::eventSetCardAttr(const Event_SetCardAttr &event,
@ -276,7 +280,7 @@ void PlayerEventHandler::eventSetCardCounter(const Event_SetCardCounter &event)
int oldValue = card->getCounters().value(event.counter_id(), 0); int oldValue = card->getCounters().value(event.counter_id(), 0);
card->setCounter(event.counter_id(), event.counter_value()); card->setCounter(event.counter_id(), event.counter_value());
emit requestCardMenuUpdate(toCardItem(card)); emit requestCardMenuUpdate(card);
emit logSetCardCounter(player, card->getName(), event.counter_id(), event.counter_value(), oldValue); emit logSetCardCounter(player, card->getName(), event.counter_id(), event.counter_value(), oldValue);
} }
@ -375,9 +379,9 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv
card->setId(event.new_card_id()); card->setId(event.new_card_id());
card->setFaceDown(event.face_down()); card->setFaceDown(event.face_down());
if (startZone != targetZone) { if (startZone != targetZone) {
const QList<CardItem *> &attachedCards = card->getAttachedCards(); const QList<CardState *> &attachedCards = card->getAttachedCards();
for (auto attachedCard : attachedCards) { for (auto attachedCard : attachedCards) {
emit targetZone->cardAdded(attachedCard->getState()); emit targetZone->cardAdded(attachedCard);
} }
} }
@ -391,8 +395,8 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv
targetZone->addCard(card, true, x, y); targetZone->addCard(card, true, x, y);
emit cardZoneChanged(toCardItem(card), startZone == targetZone); emit cardZoneChanged(card, startZone == targetZone);
emit requestCardMenuUpdate(toCardItem(card)); emit requestCardMenuUpdate(card);
if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == ZoneNames::DECK && if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == ZoneNames::DECK &&
targetZone->getName() == ZoneNames::STACK) { targetZone->getName() == ZoneNames::STACK) {
@ -419,7 +423,7 @@ void PlayerEventHandler::eventFlipCard(const Event_FlipCard &event)
emit logFlipCard(player, card->getName(), event.face_down()); emit logFlipCard(player, card->getName(), event.face_down());
card->setFaceDown(event.face_down()); card->setFaceDown(event.face_down());
emit requestCardMenuUpdate(toCardItem(card)); emit requestCardMenuUpdate(card);
} }
void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event) void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event)
@ -434,10 +438,18 @@ void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event)
return; return;
} }
QList<CardItem *> attachedCards = card->getAttachedCards(); QList<CardState *> attachedCards = card->getAttachedCards();
// This list is always empty except for buggy server implementations. // This list is always empty except for buggy server implementations.
for (auto &attachedCard : attachedCards) { for (auto &attachedCard : attachedCards) {
card->removeAttachedCard(attachedCard);
attachedCard->setGridPoint(QPoint(-1, attachedCard->getGridPoint().y()));
attachedCard->setAttachedTo(nullptr); attachedCard->setAttachedTo(nullptr);
if (attachedCard->getZone() == nullptr) {
attachedCard->deleteView();
} else {
emit attachedCard->getZone()->cardAdded(attachedCard);
attachedCard->getZone()->reorganizeCards();
}
} }
emit logDestroyCard(player, card->getName()); emit logDestroyCard(player, card->getName());
@ -495,7 +507,7 @@ void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
} else { } else {
emit logUnattachCard(player, startCard->getName()); emit logUnattachCard(player, startCard->getName());
} }
emit requestCardMenuUpdate(toCardItem(startCard)); emit requestCardMenuUpdate(startCard);
} }
void PlayerEventHandler::eventDrawCards(const Event_DrawCards &event) void PlayerEventHandler::eventDrawCards(const Event_DrawCards &event)

View file

@ -27,7 +27,6 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <sys/types.h> #include <sys/types.h>
class CardItem;
class CardState; class CardState;
class CardZoneLogic; class CardZoneLogic;
class PlayerLogic; class PlayerLogic;
@ -62,7 +61,7 @@ template <typename T> class QList;
* Design notes: * Design notes:
* - All event handlers assume events are authoritative and already validated * - All event handlers assume events are authoritative and already validated
* by the server. * by the server.
* - Most handlers mutate both logical state (CardItem, CardZoneLogic, counters) * - Most handlers mutate both logical state (CardState, CardZoneLogic, counters)
* and visual/UI state (views, arrows, menus). * and visual/UI state (views, arrows, menus).
* - Logging signals are emitted *after* or *during* state mutation, depending * - Logging signals are emitted *after* or *during* state mutation, depending
* on whether later mutations would invalidate log data. * on whether later mutations would invalidate log data.
@ -267,8 +266,8 @@ signals:
void logAlwaysLookAtTopCard(PlayerLogic *player, CardZoneLogic *zone, bool reveal); void logAlwaysLookAtTopCard(PlayerLogic *player, CardZoneLogic *zone, bool reveal);
/** @} */ /** @} */
void cardZoneChanged(CardItem *card, bool sameZone); void cardZoneChanged(CardState *card, bool sameZone);
void requestCardMenuUpdate(CardItem *card); void requestCardMenuUpdate(CardState *card);
/** /**
* @brief A card was just moved by the server and its view needs refreshing. * @brief A card was just moved by the server and its view needs refreshing.
* *

View file

@ -1,8 +1,9 @@
#include "player_logic.h" #include "player_logic.h"
#include "../../game_graphics/board/card_item.h"
#include "../abstract_game.h" #include "../abstract_game.h"
#include "../board/arrow_data.h" #include "../board/arrow_data.h"
#include "../board/card_list.h"
#include "../board/card_state.h"
#include "../board/counter_state.h" #include "../board/counter_state.h"
#include "../game_meta_info.h" #include "../game_meta_info.h"
#include "../zones/hand_zone_logic.h" #include "../zones/hand_zone_logic.h"
@ -177,22 +178,20 @@ void PlayerLogic::processPlayerInfo(const ServerInfo_Player &info)
const int cardListSize = zoneInfo.card_list_size(); const int cardListSize = zoneInfo.card_list_size();
if (!cardListSize) { if (!cardListSize) {
for (int j = 0; j < zoneInfo.card_count(); ++j) { for (int j = 0; j < zoneInfo.card_count(); ++j) {
zone->addCard((new CardItem(this))->getState(), false, -1); emit zone->requestCreateCard(ServerInfo_Card(), false);
} }
} else { } else {
for (int j = 0; j < cardListSize; ++j) { for (int j = 0; j < cardListSize; ++j) {
const ServerInfo_Card &cardInfo = zoneInfo.card_list(j); ServerInfo_Card cardInfo = zoneInfo.card_list(j);
auto *card = new CardItem(this);
card->processCardInfo(cardInfo);
// Zones without coordinates (hand, piles, stack) preserve the order // Zones without coordinates (hand, piles, stack) preserve the order
// they arrive in on the server in the positions of their cards list. // 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 // The x coordinate of such cards is always 0, so inserting at it
// would reverse the list on reconnect. Append instead. // would reverse the list on reconnect. Append instead.
if (zoneInfo.with_coords()) { if (!zoneInfo.with_coords()) {
zone->addCard(card->getState(), false, cardInfo.x(), cardInfo.y()); cardInfo.set_x(-1);
} else { cardInfo.set_y(-1);
zone->addCard(card->getState(), false, -1);
} }
emit zone->requestCreateCard(cardInfo, false);
} }
} }
if (zoneInfo.has_always_reveal_top_card()) { if (zoneInfo.has_always_reveal_top_card()) {
@ -250,7 +249,7 @@ void PlayerLogic::processCardAttachment(const ServerInfo_Player &info)
} }
} }
void PlayerLogic::addCard(CardItem *card) void PlayerLogic::addCard(AbstractCardItem *card)
{ {
emit newCardAdded(card); emit newCardAdded(card);
} }

View file

@ -73,7 +73,7 @@ signals:
/** @brief Emitted when the remote playmat (card/params) is updated from player properties. */ /** @brief Emitted when the remote playmat (card/params) is updated from player properties. */
void playmatChanged(); void playmatChanged();
void newCardAdded(AbstractCardItem *card); void newCardAdded(AbstractCardItem *card);
void requestCardMenuUpdate(CardItem *card); void requestCardMenuUpdate(CardState *card);
void counterAdded(CounterState *state); void counterAdded(CounterState *state);
void counterRemoved(int counterId); void counterRemoved(int counterId);
void rearrangeCounters(); void rearrangeCounters();
@ -103,7 +103,7 @@ public:
void processPlayerInfo(const ServerInfo_Player &info); void processPlayerInfo(const ServerInfo_Player &info);
void processCardAttachment(const ServerInfo_Player &info); void processCardAttachment(const ServerInfo_Player &info);
void addCard(CardItem *c); void addCard(AbstractCardItem *c);
void deleteCard(CardState *c); void deleteCard(CardState *c);
bool clearCardsToDelete(); bool clearCardsToDelete();

View file

@ -1,7 +1,9 @@
#include "card_zone_logic.h" #include "card_zone_logic.h"
#include "../../client/translation.h" #include "../../client/translation.h"
#include "../../game_graphics/board/card_item.h" #include "../../game_graphics/zones/card_zone.h"
#include "../board/card_list.h"
#include "../board/card_state.h"
#include "../player/player_actions.h" #include "../player/player_actions.h"
#include "../player/player_info.h" #include "../player/player_info.h"
#include "../player/player_logic.h" #include "../player/player_logic.h"
@ -50,10 +52,15 @@ void CardZoneLogic::addCard(CardState *card, const bool reorganize, const int x,
for (auto *viewLogic : views) { for (auto *viewLogic : views) {
if (viewLogic->prepareAddCard(x)) { if (viewLogic->prepareAddCard(x)) {
auto copy = new CardItem(player, nullptr, card->getCardRef(), card->getId()); ServerInfo_Card cardInfo;
copy->setFaceDown(card->getFaceDown()); const CardRef &cardRef = card->getCardRef();
cardInfo.set_name(cardRef.name.toStdString());
viewLogic->addCard(copy->getState(), reorganize, x, y); cardInfo.set_provider_id(cardRef.providerId.toStdString());
cardInfo.set_id(card->getId());
cardInfo.set_face_down(card->getFaceDown());
cardInfo.set_x(x);
cardInfo.set_y(y);
emit viewLogic->requestCreateCard(cardInfo, reorganize);
} }
} }
@ -168,9 +175,9 @@ void CardZoneLogic::clearContents()
for (CardState *card : toClear) { for (CardState *card : toClear) {
// If an incorrectly implemented server doesn't return attached cards to whom they belong before dropping a // 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. // player, we have to return them to avoid a crash.
const QList<CardItem *> &attachedCards = card->getAttachedCards(); const QList<CardState *> &attachedCards = card->getAttachedCards();
for (CardItem *attachedCard : attachedCards) { for (CardState *attachedCard : attachedCards) {
emit attachedCard->getZone()->cardAdded(attachedCard->getState()); emit attachedCard->getZone()->cardAdded(attachedCard);
} }
} }

View file

@ -14,10 +14,9 @@
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <libcockatrice/protocol/pb/serverinfo_card.pb.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
class CardItem;
inline Q_LOGGING_CATEGORY(CardZoneLogicLog, "card_zone_logic"); inline Q_LOGGING_CATEGORY(CardZoneLogicLog, "card_zone_logic");
class PlayerLogic; class PlayerLogic;
@ -33,6 +32,8 @@ class CardZoneLogic : public QObject
signals: signals:
void cardAdded(CardState *addedCard); void cardAdded(CardState *addedCard);
/** @brief Asks the graphics side to build the card item for @p cardInfo and add it to this zone. */
void requestCreateCard(const ServerInfo_Card &cardInfo, bool reorganize);
void cardCountChanged(); void cardCountChanged();
void reorganizeCards(); void reorganizeCards();
void updateGraphics(); void updateGraphics();

View file

@ -35,7 +35,7 @@ ZoneViewZoneLogic::ZoneViewZoneLogic(PlayerLogic *_player,
* Note that this method can end up modifying the cardIds despite returning false. * 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) * (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 * Make sure to call this method once before calling addCard(), so that you skip creating the card and calling
* addCard() if it's not required. * addCard() if it's not required.
* *
* @param x The position to insert the card at. * @param x The position to insert the card at.

View file

@ -89,14 +89,18 @@ void CardItem::prepareDelete()
owner = nullptr; owner = nullptr;
} }
const QList<CardItem *> &attachedCards = state->getAttachedCards(); const QList<CardState *> &attachedStates = state->getAttachedCards();
while (!attachedCards.isEmpty()) { while (!attachedStates.isEmpty()) {
attachedCards.first()->setZone(nullptr); // so that it won't try to call reorganizeCards() auto *attachedItem = qobject_cast<CardItem *>(attachedStates.first()->parent());
attachedCards.first()->setAttachedTo(nullptr); if (attachedItem == nullptr) {
break;
}
attachedItem->setZone(nullptr); // so that it won't try to call reorganizeCards()
attachedItem->setAttachedTo(nullptr);
} }
if (state->getAttachedTo() != nullptr) { if (state->getAttachedTo() != nullptr) {
state->getAttachedTo()->removeAttachedCard(this); state->getAttachedTo()->removeAttachedCard(state);
state->setAttachedTo(nullptr); state->setAttachedTo(nullptr);
} }
} }
@ -226,7 +230,7 @@ void CardItem::setPT(const QString &_pt)
void CardItem::setAttachedTo(CardItem *_attachedTo) void CardItem::setAttachedTo(CardItem *_attachedTo)
{ {
if (state->getAttachedTo() != nullptr) { if (state->getAttachedTo() != nullptr) {
state->getAttachedTo()->removeAttachedCard(this); state->getAttachedTo()->removeAttachedCard(state);
} }
state->setGridPoint(QPoint(-1, state->getGridPoint().y())); state->setGridPoint(QPoint(-1, state->getGridPoint().y()));
@ -238,7 +242,7 @@ void CardItem::setAttachedTo(CardItem *_attachedTo)
deleteLater(); deleteLater();
} else { } else {
emit state->getAttachedTo()->getZone()->cardAdded(state); emit state->getAttachedTo()->getZone()->cardAdded(state);
state->getAttachedTo()->addAttachedCard(this); state->getAttachedTo()->addAttachedCard(state);
if (state->getZone() != state->getAttachedTo()->getZone()) { if (state->getZone() != state->getAttachedTo()->getZone()) {
state->getAttachedTo()->getZone()->reorganizeCards(); state->getAttachedTo()->getZone()->reorganizeCards();
} }

View file

@ -120,15 +120,15 @@ public:
return state->getAttachedTo(); return state->getAttachedTo();
} }
void setAttachedTo(CardItem *_attachedTo); void setAttachedTo(CardItem *_attachedTo);
void addAttachedCard(CardItem *card) void addAttachedCard(CardState *card)
{ {
state->addAttachedCard(card); state->addAttachedCard(card);
} }
void removeAttachedCard(CardItem *card) void removeAttachedCard(CardState *card)
{ {
state->removeAttachedCard(card); state->removeAttachedCard(card);
} }
[[nodiscard]] const QList<CardItem *> &getAttachedCards() const [[nodiscard]] const QList<CardState *> &getAttachedCards() const
{ {
return state->getAttachedCards(); return state->getAttachedCards();
} }

View file

@ -205,7 +205,7 @@ void GameScene::onCardSelectionChanged(AbstractCardItem *abstractCard, bool sele
auto *owner = card->getOwner(); auto *owner = card->getOwner();
if (selected) { if (selected) {
owner->requestCardMenuUpdate(card); owner->requestCardMenuUpdate(card->getState());
return; return;
} }
@ -231,7 +231,7 @@ void GameScene::onCardRightClicked(AbstractCardItem *abstractCard, QPoint screen
setActiveCard(card); setActiveCard(card);
if (auto *menu = view->getPlayerMenu()->updateCardMenu(card)) { if (auto *menu = view->getPlayerMenu()->updateCardMenu(card->getState())) {
menu->popup(screenPos); menu->popup(screenPos);
} }
} }
@ -650,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; QList<ArrowItem *> toDelete;
for (auto *arrow : arrowRegistry.all()) { for (auto *arrow : arrowRegistry.all()) {
if (arrow->getStartItem() == card || arrow->getTargetItem() == card) { if (arrow->getStartItem() == item || arrow->getTargetItem() == item) {
if (sameZone) { if (sameZone) {
arrow->updatePath(); arrow->updatePath();
} else { } else {

View file

@ -283,7 +283,7 @@ public slots:
/// Queues up arrow deletion but doesn't directly modify the scene /// Queues up arrow deletion but doesn't directly modify the scene
void requestArrowDeletion(int playerId, int arrowId); 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. * @brief Refreshes the view of a card that the server just moved.
* *

View file

@ -87,22 +87,24 @@ void PlayerMenu::setMenusForGraphicItems()
} }
} }
QMenu *PlayerMenu::updateCardMenu(CardItem *card) QMenu *PlayerMenu::updateCardMenu(CardState *card)
{ {
if (!card) { if (!card) {
emit cardMenuUpdated(nullptr); emit cardMenuUpdated(nullptr);
return nullptr; return nullptr;
} }
auto *cardItem = qobject_cast<CardItem *>(card->parent());
// If is spectator (as spectators don't need card menus), return // If is spectator (as spectators don't need card menus), return
// only update the menu if the card is actually selected // only update the menu if the card is actually selected
if ((player->getLogic()->getGame()->getPlayerManager()->isSpectator() && if ((player->getLogic()->getGame()->getPlayerManager()->isSpectator() &&
!player->getLogic()->getGame()->getPlayerManager()->isJudge()) || !player->getLogic()->getGame()->getPlayerManager()->isJudge()) ||
player->getGameScene()->getActiveCard() != card) { cardItem == nullptr || player->getGameScene()->getActiveCard() != cardItem) {
return nullptr; return nullptr;
} }
CardMenu *menu = new CardMenu(player, card, shortcutsActive); CardMenu *menu = new CardMenu(player, cardItem, shortcutsActive);
connect(menu, &CardMenu::cardInfoRequested, this, &PlayerMenu::cardInfoRequested); connect(menu, &CardMenu::cardInfoRequested, this, &PlayerMenu::cardInfoRequested);
emit cardMenuUpdated(menu); emit cardMenuUpdated(menu);

View file

@ -14,7 +14,7 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <utility> #include <utility>
class CardItem; class CardState;
class CardMenu; class CardMenu;
class PlayerGraphicsItem; class PlayerGraphicsItem;
class AbstractPlayerComponent; class AbstractPlayerComponent;
@ -43,7 +43,7 @@ signals:
public slots: public slots:
void setMenusForGraphicItems(); void setMenusForGraphicItems();
QMenu *updateCardMenu(CardItem *card); QMenu *updateCardMenu(CardState *card);
private slots: private slots:
void refreshShortcuts(); void refreshShortcuts();

View file

@ -24,6 +24,7 @@ CardZone::CardZone(CardZoneLogic *_logic, QGraphicsItem *parent)
connect(logic, &CardZoneLogic::cardCountChanged, this, &CardZone::invalidateItems); connect(logic, &CardZoneLogic::cardCountChanged, this, &CardZone::invalidateItems);
connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::invalidateItems); connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::invalidateItems);
connect(logic, &CardZoneLogic::cardAdded, this, &CardZone::onCardAdded); connect(logic, &CardZoneLogic::cardAdded, this, &CardZone::onCardAdded);
connect(logic, &CardZoneLogic::requestCreateCard, this, &CardZone::onCreateCardRequested);
connect(logic, &CardZoneLogic::setGraphicsVisibility, this, [this](bool v) { this->setVisible(v); }); connect(logic, &CardZoneLogic::setGraphicsVisibility, this, [this](bool v) { this->setVisible(v); });
connect(logic, &CardZoneLogic::updateGraphics, this, [this]() { update(); }); connect(logic, &CardZoneLogic::updateGraphics, this, [this]() { update(); });
connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::reorganizeCards); connect(logic, &CardZoneLogic::reorganizeCards, this, &CardZone::reorganizeCards);
@ -59,6 +60,14 @@ void CardZone::onCardAdded(CardState *addedCard)
addedItem->update(); 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() void CardZone::retranslateUi()
{ {
for (auto *card : cardItems()) { for (auto *card : cardItems()) {

View file

@ -58,6 +58,8 @@ public slots:
* connection in CardZone's constructor dispatches through the vtable. * connection in CardZone's constructor dispatches through the vtable.
*/ */
virtual void onCardAdded(CardState *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: public:
enum enum

View file

@ -248,11 +248,14 @@ void TableZone::reorganizeCards()
cardItems()[i]->setPos(actualX, actualY); cardItems()[i]->setPos(actualX, actualY);
cardItems()[i]->setRealZValue(ZValues::tableCardZValue(actualX, actualY)); cardItems()[i]->setRealZValue(ZValues::tableCardZValue(actualX, actualY));
QListIterator<CardItem *> attachedCardIterator(cardItems()[i]->getAttachedCards()); QListIterator<CardState *> attachedCardIterator(cardItems()[i]->getAttachedCards());
int j = 0; int j = 0;
while (attachedCardIterator.hasNext()) { while (attachedCardIterator.hasNext()) {
++j; ++j;
CardItem *attachedCard = attachedCardIterator.next(); CardItem *attachedCard = qobject_cast<CardItem *>(attachedCardIterator.next()->parent());
if (attachedCard == nullptr) {
continue;
}
qreal childX = actualX - j * STACKED_CARD_OFFSET_X; qreal childX = actualX - j * STACKED_CARD_OFFSET_X;
qreal childY = y + 5; qreal childY = y + 5;
attachedCard->setPos(childX, childY); attachedCard->setPos(childX, childY);