From 42e7a8b42365a68bdfdbc83b74053566dce0f139 Mon Sep 17 00:00:00 2001 From: Basile Clement Date: Fri, 3 Mar 2023 15:54:51 +0000 Subject: [PATCH] Better support Double-Faced Cards (#4753) * Better support Double-Faced Cards This patch allows cards to be (virtually) transformed into other cards while preserving their state, essentially implemeting the MTG mechanic of the same name. On the server side, this is implemented by allowing cards to be "stashed away". A card that is stashed away is not in any zone, but is instead owned by another card. When a token is destroyed due to a zone change, if it had a card stashed away, that card is placed in the target zone instead of the token. On the database side, `attach="transform"` is used on `` and `` to indicate that the created token should be transformed this way. Old servers ignore the new field in `Command_CreateToken` and will perform a regular attachment, as currently. * Address review comments * Prevent tokens from being stashed * format.sh --- cockatrice/src/carddatabase.cpp | 6 +- cockatrice/src/carddatabase.h | 35 +++- .../src/carddbparser/cockatricexml3.cpp | 6 +- .../src/carddbparser/cockatricexml4.cpp | 11 +- cockatrice/src/player.cpp | 43 ++-- cockatrice/src/player.h | 7 +- common/pb/command_create_token.proto | 11 + common/server_card.cpp | 38 +++- common/server_card.h | 27 ++- common/server_player.cpp | 192 ++++++++++++++---- oracle/src/oracleimporter.cpp | 7 +- 11 files changed, 300 insertions(+), 83 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 57ffe297..20b893e9 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -588,7 +588,7 @@ void CardDatabase::refreshCachedReverseRelatedCards() } auto *newCardRelation = new CardRelation( - card->getName(), cardRelation->getDoesAttach(), cardRelation->getIsCreateAllExclusion(), + card->getName(), cardRelation->getAttachType(), cardRelation->getIsCreateAllExclusion(), cardRelation->getIsVariable(), cardRelation->getDefaultCount(), cardRelation->getIsPersistent()); cards.value(targetCard)->addReverseRelatedCards2Me(newCardRelation); } @@ -672,12 +672,12 @@ bool CardDatabase::saveCustomTokensToFile() } CardRelation::CardRelation(const QString &_name, - bool _doesAttach, + AttachType _attachType, bool _isCreateAllExclusion, bool _isVariableCount, int _defaultCount, bool _isPersistent) - : name(_name), doesAttach(_doesAttach), isCreateAllExclusion(_isCreateAllExclusion), + : name(_name), attachType(_attachType), isCreateAllExclusion(_isCreateAllExclusion), isVariableCount(_isVariableCount), defaultCount(_defaultCount), isPersistent(_isPersistent) { } diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 4350c5a5..d8ba8419 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -452,9 +452,17 @@ signals: class CardRelation : public QObject { Q_OBJECT +public: + enum AttachType + { + DoesNotAttach = 0, + AttachTo = 1, + TransformInto = 2, + }; + private: QString name; - bool doesAttach; + AttachType attachType; bool isCreateAllExclusion; bool isVariableCount; int defaultCount; @@ -462,7 +470,7 @@ private: public: explicit CardRelation(const QString &_name = QString(), - bool _doesAttach = false, + AttachType _attachType = DoesNotAttach, bool _isCreateAllExclusion = false, bool _isVariableCount = false, int _defaultCount = 1, @@ -472,13 +480,32 @@ public: { return name; } + AttachType getAttachType() const + { + return attachType; + } bool getDoesAttach() const { - return doesAttach; + return attachType != DoesNotAttach; + } + bool getDoesTransform() const + { + return attachType == TransformInto; + } + QString getAttachTypeAsString() const + { + switch (attachType) { + case AttachTo: + return "attach"; + case TransformInto: + return "transform"; + default: + return ""; + } } bool getCanCreateAnother() const { - return !doesAttach; + return !getDoesAttach(); } bool getIsCreateAllExclusion() const { diff --git a/cockatrice/src/carddbparser/cockatricexml3.cpp b/cockatrice/src/carddbparser/cockatricexml3.cpp index d13148e1..098a9def 100644 --- a/cockatrice/src/carddbparser/cockatricexml3.cpp +++ b/cockatrice/src/carddbparser/cockatricexml3.cpp @@ -224,7 +224,7 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml) sets.insert(setName, setInfo); // related cards } else if (xmlName == "related" || xmlName == "reverse-related") { - bool attach = false; + CardRelation::AttachType attach = CardRelation::DoesNotAttach; bool exclude = false; bool variable = false; int count = 1; @@ -246,7 +246,7 @@ void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml) } if (attrs.hasAttribute("attach")) { - attach = true; + attach = CardRelation::AttachTo; } if (attrs.hasAttribute("exclude")) { @@ -459,4 +459,4 @@ bool CockatriceXml3Parser::saveToFile(SetNameMap sets, xml.writeEndDocument(); return true; -} \ No newline at end of file +} diff --git a/cockatrice/src/carddbparser/cockatricexml4.cpp b/cockatrice/src/carddbparser/cockatricexml4.cpp index 7e22a7cc..a030ea4d 100644 --- a/cockatrice/src/carddbparser/cockatricexml4.cpp +++ b/cockatrice/src/carddbparser/cockatricexml4.cpp @@ -182,7 +182,7 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) } // related cards } else if (xmlName == "related" || xmlName == "reverse-related") { - bool attach = false; + CardRelation::AttachType attachType = CardRelation::DoesNotAttach; bool exclude = false; bool variable = false; bool persistent = false; @@ -205,7 +205,8 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) } if (attrs.hasAttribute("attach")) { - attach = true; + attachType = attrs.value("attach").toString() == "transform" ? CardRelation::TransformInto + : CardRelation::AttachTo; } if (attrs.hasAttribute("exclude")) { @@ -216,7 +217,7 @@ void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) persistent = true; } - auto *relation = new CardRelation(cardName, attach, exclude, variable, count, persistent); + auto *relation = new CardRelation(cardName, attachType, exclude, variable, count, persistent); if (xmlName == "reverse-related") { reverseRelatedCards << relation; } else { @@ -294,7 +295,7 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in for (auto i : related) { xml.writeStartElement("related"); if (i->getDoesAttach()) { - xml.writeAttribute("attach", "attach"); + xml.writeAttribute("attach", i->getAttachTypeAsString()); } if (i->getIsCreateAllExclusion()) { xml.writeAttribute("exclude", "exclude"); @@ -318,7 +319,7 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &in for (auto i : reverseRelated) { xml.writeStartElement("reverse-related"); if (i->getDoesAttach()) { - xml.writeAttribute("attach", "attach"); + xml.writeAttribute("attach", i->getAttachTypeAsString()); } if (i->getIsCreateAllExclusion()) { diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 560523f3..cd64dadf 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -1677,7 +1677,7 @@ void Player::actCreateAllRelatedCards() dbName = cardRelationAll->getName(); bool persistent = cardRelationAll->getIsPersistent(); for (int i = 0; i < cardRelationAll->getDefaultCount(); ++i) { - createCard(sourceCard, dbName, false, persistent); + createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); } ++tokensTypesCreated; if (tokensTypesCreated == 1) { @@ -1692,7 +1692,7 @@ void Player::actCreateAllRelatedCards() dbName = cardRelationNotExcluded->getName(); bool persistent = cardRelationNotExcluded->getIsPersistent(); for (int i = 0; i < cardRelationNotExcluded->getDefaultCount(); ++i) { - createCard(sourceCard, dbName, false, persistent); + createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); } ++tokensTypesCreated; if (tokensTypesCreated == 1) { @@ -1731,23 +1731,22 @@ bool Player::createRelatedFromRelation(const CardItem *sourceCard, const CardRel return false; } for (int i = 0; i < count; ++i) { - createCard(sourceCard, dbName, false, persistent); + createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); } } else if (cardRelation->getDefaultCount() > 1) { for (int i = 0; i < cardRelation->getDefaultCount(); ++i) { - createCard(sourceCard, dbName, false, persistent); + createCard(sourceCard, dbName, CardRelation::DoesNotAttach, persistent); } } else { - if (cardRelation->getDoesAttach()) { - createAttachedCard(sourceCard, dbName, persistent); - } else { - createCard(sourceCard, dbName, false, persistent); - } + createCard(sourceCard, dbName, cardRelation->getAttachType(), persistent); } return true; } -void Player::createCard(const CardItem *sourceCard, const QString &dbCardName, bool attach, bool persistent) +void Player::createCard(const CardItem *sourceCard, + const QString &dbCardName, + CardRelation::AttachType attachType, + bool persistent) { CardInfoPtr cardInfo = db->getCard(dbCardName); @@ -1786,18 +1785,24 @@ void Player::createCard(const CardItem *sourceCard, const QString &dbCardName, b cmd.set_x(gridPoint.x()); cmd.set_y(gridPoint.y()); - if (attach) { - cmd.set_target_card_id(sourceCard->getId()); + switch (attachType) { + case CardRelation::DoesNotAttach: + break; + + case CardRelation::AttachTo: + cmd.set_target_card_id(sourceCard->getId()); + cmd.set_target_mode(Command_CreateToken::ATTACH_TO); + break; + + case CardRelation::TransformInto: + cmd.set_target_card_id(sourceCard->getId()); + cmd.set_target_mode(Command_CreateToken::TRANSFORM_INTO); + break; } sendGameCommand(cmd); } -void Player::createAttachedCard(const CardItem *sourceCard, const QString &dbCardName, bool persistent) -{ - createCard(sourceCard, dbCardName, true, persistent); -} - void Player::actSayMessage() { auto *a = qobject_cast(sender()); @@ -3550,6 +3555,7 @@ void Player::addRelatedCardActions(const CardItem *card, QMenu *cardMenu) CardInfoPtr relatedCard = db->getCard(cardRelation->getName()); if (relatedCard == nullptr) continue; + QString relatedCardName; if (relatedCard->getPowTough().size() > 0) { relatedCardName = relatedCard->getPowTough() + " " + relatedCard->getName(); // "n/n name" @@ -3559,7 +3565,8 @@ void Player::addRelatedCardActions(const CardItem *card, QMenu *cardMenu) QString text = tr("Token: "); if (cardRelation->getDoesAttach()) { - text += tr("Attach to ") + "\"" + relatedCardName + "\""; + text += + tr(cardRelation->getDoesTransform() ? "Transform into " : "Attach to ") + "\"" + relatedCardName + "\""; } else if (cardRelation->getIsVariable()) { text += "X " + relatedCardName; } else if (cardRelation->getDefaultCount() != 1) { diff --git a/cockatrice/src/player.h b/cockatrice/src/player.h index 44429567..7f14b7d5 100644 --- a/cockatrice/src/player.h +++ b/cockatrice/src/player.h @@ -287,9 +287,10 @@ private: bool allCards); void addRelatedCardActions(const CardItem *card, QMenu *cardMenu); void addRelatedCardView(const CardItem *card, QMenu *cardMenu); - void - createCard(const CardItem *sourceCard, const QString &dbCardName, bool attach = false, bool persistent = false); - void createAttachedCard(const CardItem *sourceCard, const QString &dbCardName, bool persistent = false); + void createCard(const CardItem *sourceCard, + const QString &dbCardName, + CardRelation::AttachType attach = CardRelation::DoesNotAttach, + bool persistent = false); bool createRelatedFromRelation(const CardItem *sourceCard, const CardRelation *cardRelation); QRectF bRect; diff --git a/common/pb/command_create_token.proto b/common/pb/command_create_token.proto index d5425648..4ee60b28 100644 --- a/common/pb/command_create_token.proto +++ b/common/pb/command_create_token.proto @@ -1,6 +1,14 @@ syntax = "proto2"; import "game_commands.proto"; + message Command_CreateToken { + enum TargetMode { + // Attach the target to the token + ATTACH_TO = 0; + // Transform the target into the token + TRANSFORM_INTO = 1; + } + extend GameCommand { optional Command_CreateToken ext = 1010; } @@ -14,4 +22,7 @@ message Command_CreateToken { optional sint32 y = 8; optional string target_zone = 9; optional sint32 target_card_id = 10 [default = -1]; + + // What to do with the target card. Ignored if there is no target card. + optional TargetMode target_mode = 11; } diff --git a/common/server_card.cpp b/common/server_card.cpp index 0359fa16..5bf8ee78 100644 --- a/common/server_card.cpp +++ b/common/server_card.cpp @@ -19,13 +19,18 @@ ***************************************************************************/ #include "server_card.h" +#include "pb/event_set_card_attr.pb.h" +#include "pb/event_set_card_counter.pb.h" #include "pb/serverinfo_card.pb.h" #include "server_cardzone.h" #include "server_player.h" +#include + Server_Card::Server_Card(QString _name, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone) : zone(_zone), id(_id), coord_x(_coord_x), coord_y(_coord_y), name(_name), tapped(false), attacking(false), - facedown(false), color(), ptString(), annotation(), destroyOnZoneChange(false), doesntUntap(false), parentCard(0) + facedown(false), color(), ptString(), annotation(), destroyOnZoneChange(false), doesntUntap(false), parentCard(0), + stashedCard(nullptr) { } @@ -37,6 +42,11 @@ Server_Card::~Server_Card() if (parentCard) parentCard->removeAttachedCard(this); + + if (stashedCard) { + stashedCard->deleteLater(); + stashedCard = nullptr; + } } void Server_Card::resetState() @@ -51,11 +61,20 @@ void Server_Card::resetState() QString Server_Card::setAttribute(CardAttribute attribute, const QString &avalue, bool allCards) { + if (attribute == AttrTapped && avalue != "1" && allCards && doesntUntap) + return QVariant(tapped).toString(); + + return setAttribute(attribute, avalue); +} + +QString Server_Card::setAttribute(CardAttribute attribute, const QString &avalue, Event_SetCardAttr *event) +{ + if (event) + event->set_attribute(attribute); + switch (attribute) { case AttrTapped: { - bool value = avalue == "1"; - if (!(!value && allCards && doesntUntap)) - setTapped(value); + setTapped(avalue == "1"); break; } case AttrAttacking: @@ -69,6 +88,8 @@ QString Server_Card::setAttribute(CardAttribute attribute, const QString &avalue break; case AttrPT: setPT(avalue); + if (event) + event->set_attr_value(getPT().toStdString()); return getPT(); case AttrAnnotation: setAnnotation(avalue); @@ -77,15 +98,22 @@ QString Server_Card::setAttribute(CardAttribute attribute, const QString &avalue setDoesntUntap(avalue == "1"); break; } + if (event) + event->set_attr_value(avalue.toStdString()); return avalue; } -void Server_Card::setCounter(int id, int value) +void Server_Card::setCounter(int id, int value, Event_SetCardCounter *event) { if (value) counters.insert(id, value); else counters.remove(id); + + if (event) { + event->set_counter_id(id); + event->set_counter_value(value); + } } void Server_Card::setParentCard(Server_Card *_parentCard) diff --git a/common/server_card.h b/common/server_card.h index c921f86e..0e1052d9 100644 --- a/common/server_card.h +++ b/common/server_card.h @@ -28,6 +28,8 @@ #include class Server_CardZone; +class Event_SetCardCounter; +class Event_SetCardAttr; class Server_Card : public Server_ArrowTarget { @@ -49,6 +51,7 @@ private: Server_Card *parentCard; QList attachedCards; + Server_Card *stashedCard; public: Server_Card(QString _name, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone = 0); @@ -141,7 +144,7 @@ public: { name = _name; } - void setCounter(int id, int value); + void setCounter(int id, int value, Event_SetCardCounter *event = nullptr); void setTapped(bool _tapped) { tapped = _tapped; @@ -183,9 +186,31 @@ public: { attachedCards.removeOne(card); } + void setStashedCard(Server_Card *card) + { + // setStashedCard should only be called on creation of a new card, so + // there should never be an already existing stashed card. + Q_ASSERT(!stashedCard); + + // Stashed cards can't themselves have stashed cards, and tokens can't + // be stashed. + if (card->stashedCard || card->getDestroyOnZoneChange()) { + stashedCard = card->takeStashedCard(); + card->deleteLater(); + } else { + stashedCard = card; + } + } + Server_Card *takeStashedCard() + { + Server_Card *oldStashedCard = stashedCard; + stashedCard = nullptr; + return oldStashedCard; + } void resetState(); QString setAttribute(CardAttribute attribute, const QString &avalue, bool allCards); + QString setAttribute(CardAttribute attribute, const QString &avalue, Event_SetCardAttr *event = nullptr); void getInfo(ServerInfo_Card *info); }; diff --git a/common/server_player.cpp b/common/server_player.cpp index 5cd95ce9..d10cfc60 100644 --- a/common/server_player.cpp +++ b/common/server_player.cpp @@ -376,6 +376,36 @@ void Server_Player::revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorag } } +static Event_CreateToken makeCreateTokenEvent(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord) +{ + Event_CreateToken event; + event.set_zone_name(zone->getName().toStdString()); + event.set_card_id(card->getId()); + event.set_card_name(card->getName().toStdString()); + event.set_color(card->getColor().toStdString()); + event.set_pt(card->getPT().toStdString()); + event.set_annotation(card->getAnnotation().toStdString()); + event.set_destroy_on_zone_change(card->getDestroyOnZoneChange()); + event.set_x(xCoord); + event.set_y(yCoord); + return event; +} + +static Event_AttachCard makeAttachCardEvent(Server_Card *attachedCard, Server_Card *parentCard = nullptr) +{ + Event_AttachCard event; + event.set_start_zone(attachedCard->getZone()->getName().toStdString()); + event.set_card_id(attachedCard->getId()); + + if (parentCard) { + event.set_target_player_id(parentCard->getZone()->getPlayer()->getPlayerId()); + event.set_target_zone(parentCard->getZone()->getName().toStdString()); + event.set_target_card_id(parentCard->getId()); + } + + return event; +} + Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, Server_CardZone *startzone, const QList &_cards, @@ -485,8 +515,19 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, event.set_card_id(static_cast(card->getId())); ges.enqueueGameEvent(event, playerId); - card->deleteLater(); - } else { + if (Server_Card *stashedCard = card->takeStashedCard()) { + stashedCard->setId(newCardId()); + ges.enqueueGameEvent(makeCreateTokenEvent(startzone, stashedCard, card->getX(), card->getY()), + playerId); + card->deleteLater(); + card = stashedCard; + } else { + card->deleteLater(); + card = nullptr; + } + } + + if (card) { ++xIndex; int newX = xCoord + xIndex; @@ -622,10 +663,7 @@ void Server_Player::unattachCard(GameEventStorage &ges, Server_Card *card) Server_Card *parentCard = card->getParentCard(); card->setParentCard(nullptr); - Event_AttachCard event; - event.set_start_zone(zone->getName().toStdString()); - event.set_card_id(card->getId()); - ges.enqueueGameEvent(event, playerId); + ges.enqueueGameEvent(makeAttachCardEvent(card), playerId); auto *cardToMove = new CardToMove; cardToMove->set_card_id(card->getId()); @@ -1280,13 +1318,7 @@ Server_Player::cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer & delete cardToMove; } - Event_AttachCard event; - event.set_start_zone(startzone->getName().toStdString()); - event.set_card_id(card->getId()); - event.set_target_player_id(targetPlayer->getPlayerId()); - event.set_target_zone(targetzone->getName().toStdString()); - event.set_target_card_id(targetCard->getId()); - ges.enqueueGameEvent(event, playerId); + ges.enqueueGameEvent(makeAttachCardEvent(card, targetCard), playerId); startzone->fixFreeSpaces(ges); } else { @@ -1315,9 +1347,40 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer return Response::RespNameNotFound; } - QString cardName = nameFromStdString(cmd.card_name()); int xCoord = cmd.x(); int yCoord = cmd.y(); + + Server_Card *targetCard = nullptr; + if (cmd.has_target_card_id()) { + Server_CardZone *targetZone = zones.value(nameFromStdString(cmd.target_zone())); + if (targetZone) { + targetCard = targetZone->getCard(cmd.target_card_id()); + if (targetCard && cmd.target_mode() == Command_CreateToken::TRANSFORM_INTO) { + if (targetCard->getParentCard()) { + ges.enqueueGameEvent(makeAttachCardEvent(targetCard), playerId); + } + + for (Server_Card *attachedCard : targetCard->getAttachedCards()) { + ges.enqueueGameEvent(makeAttachCardEvent(attachedCard), + attachedCard->getZone()->getPlayer()->getPlayerId()); + } + + if (zone->hasCoords() && zone == targetZone) { + xCoord = targetCard->getX(); + yCoord = targetCard->getY(); + } + + targetZone->removeCard(targetCard); + + Event_DestroyCard event; + event.set_zone_name(targetZone->getName().toStdString()); + event.set_card_id(static_cast<::google::protobuf::uint32>(cmd.target_card_id())); + ges.enqueueGameEvent(event, playerId); + } + } + } + + QString cardName = nameFromStdString(cmd.card_name()); if (zone->hasCoords()) { xCoord = zone->getFreeGridColumn(xCoord, yCoord, cardName, false); } @@ -1336,33 +1399,89 @@ Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer card->setDestroyOnZoneChange(cmd.destroy_on_zone_change()); zone->insertCard(card, xCoord, yCoord); - - Event_CreateToken event; - event.set_zone_name(zone->getName().toStdString()); - event.set_card_id(card->getId()); - event.set_card_name(card->getName().toStdString()); - event.set_color(card->getColor().toStdString()); - event.set_pt(card->getPT().toStdString()); - event.set_annotation(card->getAnnotation().toStdString()); - event.set_destroy_on_zone_change(card->getDestroyOnZoneChange()); - event.set_x(xCoord); - event.set_y(yCoord); - ges.enqueueGameEvent(event, playerId); + ges.enqueueGameEvent(makeCreateTokenEvent(zone, card, xCoord, yCoord), playerId); // check if the token is a replacement for an existing card - if (cmd.target_card_id() < 0) { + if (!targetCard) { return Response::RespOk; } - Command_AttachCard cmd2; - cmd2.set_start_zone(cmd.target_zone()); - cmd2.set_card_id(cmd.target_card_id()); + switch (cmd.target_mode()) { + case Command_CreateToken::ATTACH_TO: { + Command_AttachCard cmd2; + cmd2.set_start_zone(cmd.target_zone()); + cmd2.set_card_id(cmd.target_card_id()); - cmd2.set_target_player_id(zone->getPlayer()->getPlayerId()); - cmd2.set_target_zone(cmd.zone()); - cmd2.set_target_card_id(card->getId()); + cmd2.set_target_player_id(zone->getPlayer()->getPlayerId()); + cmd2.set_target_zone(cmd.zone()); + cmd2.set_target_card_id(card->getId()); - return cmdAttachCard(cmd2, rc, ges); + return cmdAttachCard(cmd2, rc, ges); + } + + case Command_CreateToken::TRANSFORM_INTO: { + // Copy attributes that are not present in the CreateToken event + Event_SetCardAttr event; + event.set_zone_name(card->getZone()->getName().toStdString()); + event.set_card_id(card->getId()); + + if (card->getTapped() != targetCard->getTapped()) { + card->setAttribute(AttrTapped, QVariant(targetCard->getTapped()).toString(), &event); + ges.enqueueGameEvent(event, playerId); + } + + if (card->getAttacking() != targetCard->getAttacking()) { + card->setAttribute(AttrAttacking, QVariant(targetCard->getAttacking()).toString(), &event); + ges.enqueueGameEvent(event, playerId); + } + + if (card->getFaceDown() != targetCard->getFaceDown()) { + card->setAttribute(AttrFaceDown, QVariant(targetCard->getFaceDown()).toString(), &event); + ges.enqueueGameEvent(event, playerId); + } + + if (card->getDoesntUntap() != targetCard->getDoesntUntap()) { + card->setAttribute(AttrDoesntUntap, QVariant(targetCard->getDoesntUntap()).toString(), &event); + ges.enqueueGameEvent(event, playerId); + } + + // Copy counters + QMapIterator i(targetCard->getCounters()); + while (i.hasNext()) { + i.next(); + + Event_SetCardCounter event; + event.set_zone_name(card->getZone()->getName().toStdString()); + event.set_card_id(card->getId()); + + card->setCounter(i.key(), i.value(), &event); + ges.enqueueGameEvent(event, playerId); + } + + // Copy parent card + if (Server_Card *parentCard = targetCard->getParentCard()) { + targetCard->setParentCard(nullptr); + card->setParentCard(parentCard); + + ges.enqueueGameEvent(makeAttachCardEvent(card, parentCard), playerId); + } + + // Copy attachments + while (!targetCard->getAttachedCards().isEmpty()) { + Server_Card *attachedCard = targetCard->getAttachedCards().last(); + attachedCard->setParentCard(card); + + ges.enqueueGameEvent(makeAttachCardEvent(attachedCard, card), + attachedCard->getZone()->getPlayer()->getPlayerId()); + } + + targetCard->resetState(); + card->setStashedCard(targetCard); + break; + } + } + + return Response::RespOk; } Response::ResponseCode @@ -1517,13 +1636,10 @@ Server_Player::cmdSetCardCounter(const Command_SetCardCounter &cmd, ResponseCont return Response::RespNameNotFound; } - card->setCounter(cmd.counter_id(), cmd.counter_value()); - Event_SetCardCounter event; event.set_zone_name(zone->getName().toStdString()); event.set_card_id(card->getId()); - event.set_counter_id(cmd.counter_id()); - event.set_counter_value(cmd.counter_value()); + card->setCounter(cmd.counter_id(), cmd.counter_value(), &event); ges.enqueueGameEvent(event, playerId); return Response::RespOk; diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index bb3b22e3..ae7c35aa 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -370,12 +370,12 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, static const QRegularExpression meldNameRegex{"then meld them into ([^\\.]*)"}; QString additionalName = meldNameRegex.match(text).captured(1); if (!additionalName.isNull()) { - relatedCards.append(new CardRelation(additionalName, true)); + relatedCards.append(new CardRelation(additionalName, CardRelation::TransformInto)); } } else { for (const QString &additionalName : name.split(" // ")) { if (additionalName != faceName) { - relatedCards.append(new CardRelation(additionalName, true)); + relatedCards.append(new CardRelation(additionalName, CardRelation::TransformInto)); } } } @@ -389,7 +389,8 @@ int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, if (givenRelated.contains("spellbook")) { auto spbk = givenRelated.value("spellbook").toStringList(); for (const QString &spbkName : spbk) { - relatedCards.append(new CardRelation(spbkName, false, false, false, 1, true)); + relatedCards.append( + new CardRelation(spbkName, CardRelation::DoesNotAttach, false, false, 1, true)); } } }