[Refactor] Retype card containers and zone logic onto CardState (#7434)

* [Refactor] Retype card containers and zone logic onto CardState

* [Game] Restore attach bookkeeping via CardItem and repaint on state change

* [Refactor] Give custom zones a hidden graphics owner

Custom PileZoneLogic zones had no CardZone, so moving a card into one
left the CardItem drawn at its old spot. Create a hidden PileZone in
onCustomZoneAdded so cards are reparented and hidden.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-10-31 18:44:14 +01:00 • committed by GitHub
parent 5d0d466930
commit 65e9f1b370
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 232 additions and 159 deletions

View file

@ -53,6 +53,11 @@
#include <qlogging.h>
#include <string>
static CardItem *toCardItem(CardState *card)
{
return card == nullptr ? nullptr : qobject_cast<CardItem *>(card->parent());
}
PlayerEventHandler::PlayerEventHandler(PlayerLogic *_player) : QObject(_player), player(_player)
{
connect(this, &PlayerEventHandler::requestCardMenuUpdate, player, &PlayerLogic::requestCardMenuUpdate);
@ -171,7 +176,7 @@ void PlayerEventHandler::eventCreateToken(const Event_CreateToken &event)
card->setFaceDown(event.face_down());
emit logCreateToken(player, card->getName(), card->getPT(), card->getFaceDown());
zone->addCard(card, true, event.x(), event.y());
zone->addCard(card->getState(), true, event.x(), event.y());
}
void PlayerEventHandler::eventSetCardAttr(const Event_SetCardAttr &event,
@ -193,7 +198,7 @@ void PlayerEventHandler::eventSetCardAttr(const Event_SetCardAttr &event,
emit logSetTapped(player, nullptr, event.attr_value() == "1");
}
} else {
CardItem *card = zone->getCard(event.card_id());
CardState *card = zone->getCard(event.card_id());
if (!card) {
qWarning() << "PlayerEventHandler::eventSetCardAttr: card id=" << event.card_id() << "not found";
return;
@ -203,7 +208,7 @@ void PlayerEventHandler::eventSetCardAttr(const Event_SetCardAttr &event,
}
void PlayerEventHandler::setCardAttrHelper(const GameEventContext &context,
CardItem *card,
CardState *card,
CardAttribute attribute,
const QString &avalue,
bool allCards,
@ -264,14 +269,14 @@ void PlayerEventHandler::eventSetCardCounter(const Event_SetCardCounter &event)
return;
}
CardItem *card = zone->getCard(event.card_id());
CardState *card = zone->getCard(event.card_id());
if (!card) {
return;
}
int oldValue = card->getCounters().value(event.counter_id(), 0);
card->setCounter(event.counter_id(), event.counter_value());
emit requestCardMenuUpdate(card);
emit requestCardMenuUpdate(toCardItem(card));
emit logSetCardCounter(player, card->getName(), event.counter_id(), event.counter_value(), oldValue);
}
@ -341,13 +346,13 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv
if (x == -1) {
x = 0;
}
CardItem *card = startZone->takeCard(position, event.card_id(), startZone != targetZone);
CardState *card = startZone->takeCard(position, event.card_id(), startZone != targetZone);
if (card == nullptr) {
return;
}
const bool zoneChanged = startZone != targetZone;
const bool ownerChanged = zoneChanged && startZone->getPlayer() != targetZone->getPlayer();
emit cardViewRefreshRequested(card->getState(), zoneChanged, ownerChanged ? targetZone->getPlayer() : nullptr);
emit cardViewRefreshRequested(card, zoneChanged, ownerChanged ? targetZone->getPlayer() : nullptr);
if (event.has_card_name()) {
QString name = QString::fromStdString(event.card_name());
QString providerId =
@ -357,7 +362,13 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv
if (card->getAttachedTo() && (startZone != targetZone)) {
CardState *parentCard = card->getAttachedTo();
card->setAttachedTo(nullptr);
// Route through CardItem so the old parent's attachedCards list is cleaned up: CardState::setAttachedTo
// only repoints the pointer, leaving a dangling entry that reorganizeCards/prepareDelete would dereference.
if (CardItem *item = toCardItem(card)) {
item->setAttachedTo(nullptr);
} else {
card->setAttachedTo(nullptr);
}
parentCard->getZone()->reorganizeCards();
}
@ -366,7 +377,7 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv
if (startZone != targetZone) {
const QList<CardItem *> &attachedCards = card->getAttachedCards();
for (auto attachedCard : attachedCards) {
emit targetZone->cardAdded(attachedCard);
emit targetZone->cardAdded(attachedCard->getState());
}
}
@ -380,12 +391,12 @@ void PlayerEventHandler::eventMoveCard(const Event_MoveCard &event, const GameEv
targetZone->addCard(card, true, x, y);
emit cardZoneChanged(card, startZone == targetZone);
emit requestCardMenuUpdate(card);
emit cardZoneChanged(toCardItem(card), startZone == targetZone);
emit requestCardMenuUpdate(toCardItem(card));
if (player->getPlayerActions()->isMovingCardsUntil() && startZoneString == ZoneNames::DECK &&
targetZone->getName() == ZoneNames::STACK) {
player->getPlayerActions()->moveOneCardUntil(card);
player->getPlayerActions()->moveOneCardUntil(toCardItem(card));
}
}
@ -395,7 +406,7 @@ void PlayerEventHandler::eventFlipCard(const Event_FlipCard &event)
if (!zone) {
return;
}
CardItem *card = zone->getCard(event.card_id());
CardState *card = zone->getCard(event.card_id());
if (!card) {
return;
}
@ -408,7 +419,7 @@ void PlayerEventHandler::eventFlipCard(const Event_FlipCard &event)
emit logFlipCard(player, card->getName(), event.face_down());
card->setFaceDown(event.face_down());
emit requestCardMenuUpdate(card);
emit requestCardMenuUpdate(toCardItem(card));
}
void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event)
@ -418,7 +429,7 @@ void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event)
return;
}
CardItem *card = zone->getCard(event.card_id());
CardState *card = zone->getCard(event.card_id());
if (!card) {
return;
}
@ -431,7 +442,7 @@ void PlayerEventHandler::eventDestroyCard(const Event_DestroyCard &event)
emit logDestroyCard(player, card->getName());
zone->takeCard(-1, event.card_id(), true);
card->getState()->deleteView();
card->deleteView();
}
void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
@ -439,7 +450,7 @@ 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;
CardState *targetCard = nullptr;
if (event.has_target_player_id()) {
targetPlayer = playerList.value(event.target_player_id(), 0);
if (targetPlayer) {
@ -455,14 +466,21 @@ void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
return;
}
CardItem *startCard = startZone->getCard(event.card_id());
CardState *startCard = startZone->getCard(event.card_id());
if (!startCard) {
return;
}
CardState *oldParent = startCard->getAttachedTo();
startCard->setAttachedTo(targetCard);
// Route through CardItem::setAttachedTo to keep the attachedCards graph, grid-x reset and zone emits intact:
// CardState::setAttachedTo alone only repoints the pointer, so the target would never list the card and the old
// parent would keep a dangling pointer.
if (CardItem *startItem = toCardItem(startCard)) {
startItem->setAttachedTo(toCardItem(targetCard));
} else {
startCard->setAttachedTo(targetCard);
}
startZone->reorganizeCards();
if ((startZone != targetZone) && targetZone) {
@ -477,7 +495,7 @@ void PlayerEventHandler::eventAttachCard(const Event_AttachCard &event)
} else {
emit logUnattachCard(player, startCard->getName());
}
emit requestCardMenuUpdate(startCard);
emit requestCardMenuUpdate(toCardItem(startCard));
}
void PlayerEventHandler::eventDrawCards(const Event_DrawCards &event)
@ -489,7 +507,7 @@ void PlayerEventHandler::eventDrawCards(const Event_DrawCards &event)
if (listSize) {
for (int i = 0; i < listSize; ++i) {
const ServerInfo_Card &cardInfo = event.cards(i);
CardItem *card = _deck->takeCard(0, cardInfo.id());
CardState *card = _deck->takeCard(0, cardInfo.id());
QString cardName = QString::fromStdString(cardInfo.name());
QString providerId = QString::fromStdString(cardInfo.provider_id());
card->setCardRef({cardName, providerId});
@ -537,7 +555,7 @@ void PlayerEventHandler::eventRevealCards(const Event_RevealCards &event, EventP
for (const auto &card : cardList) {
QString cardName = QString::fromStdString(card->name());
QString providerId = QString::fromStdString(card->provider_id());
CardItem *cardItem = zone->getCard(card->id());
CardState *cardItem = zone->getCard(card->id());
if (!cardItem) {
continue;
}