diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index 21c9eeed..bd254308 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -492,13 +492,16 @@ CardInfo::CardInfo(CardDatabase *_db, const QString &_powtough, const QString &_text, const QStringList &_colors, + const QStringList &_relatedCards, + bool _upsideDownArt, int _loyalty, bool _cipt, int _tableRow, const SetList &_sets, const QStringMap &_customPicURLs, const QStringMap &_customPicURLsHq, - MuidMap _muIds) + MuidMap _muIds + ) : db(_db), name(_name), isToken(_isToken), @@ -509,6 +512,8 @@ CardInfo::CardInfo(CardDatabase *_db, powtough(_powtough), text(_text), colors(_colors), + relatedCards(_relatedCards), + upsideDownArt(_upsideDownArt), loyalty(_loyalty), customPicURLs(_customPicURLs), customPicURLsHq(_customPicURLsHq), @@ -590,7 +595,13 @@ void CardInfo::loadPixmap(QPixmap &pixmap) void CardInfo::imageLoaded(const QImage &image) { if (!image.isNull()) { - QPixmapCache::insert(pixmapCacheKey, QPixmap::fromImage(image)); + if(upsideDownArt) + { + QImage mirrorImage = image.mirrored(true, true); + QPixmapCache::insert(pixmapCacheKey, QPixmap::fromImage(mirrorImage)); + } else { + QPixmapCache::insert(pixmapCacheKey, QPixmap::fromImage(image)); + } emit pixmapUpdated(); } } @@ -695,6 +706,10 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) for (int i = 0; i < colors.size(); i++) xml.writeTextElement("color", colors[i]); + const QStringList &related = info->getRelatedCards(); + for (int i = 0; i < related.size(); i++) + xml.writeTextElement("related", related[i]); + xml.writeTextElement("manacost", info->getManaCost()); xml.writeTextElement("cmc", info->getCmc()); xml.writeTextElement("type", info->getCardType()); @@ -708,6 +723,8 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) xml.writeTextElement("cipt", "1"); if (info->getIsToken()) xml.writeTextElement("token", "1"); + if (info->getUpsideDownArt()) + xml.writeTextElement("upsidedown", "1"); xml.writeEndElement(); // card return xml; @@ -862,7 +879,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) break; if (xml.name() == "card") { QString name, manacost, cmc, type, pt, text; - QStringList colors; + QStringList colors, relatedCards; QStringMap customPicURLs, customPicURLsHq; MuidMap muids; SetList sets; @@ -870,6 +887,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) int loyalty = 0; bool cipt = false; bool isToken = false; + bool upsideDown = false; while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::EndElement) break; @@ -900,10 +918,14 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) } } else if (xml.name() == "color") colors << xml.readElementText(); + else if (xml.name() == "related") + relatedCards << xml.readElementText(); else if (xml.name() == "tablerow") tableRow = xml.readElementText().toInt(); else if (xml.name() == "cipt") cipt = (xml.readElementText() == "1"); + else if (xml.name() == "upsidedown") + upsideDown = (xml.readElementText() == "1"); else if (xml.name() == "loyalty") loyalty = xml.readElementText().toInt(); else if (xml.name() == "token") @@ -911,7 +933,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) } if (isToken == tokens) { - addCard(new CardInfo(this, name, isToken, manacost, cmc, type, pt, text, colors, loyalty, cipt, tableRow, sets, customPicURLs, customPicURLsHq, muids)); + addCard(new CardInfo(this, name, isToken, manacost, cmc, type, pt, text, colors, relatedCards, upsideDown, loyalty, cipt, tableRow, sets, customPicURLs, customPicURLsHq, muids)); } } } diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 30fdc1e0..af817778 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -132,6 +132,8 @@ private: QString powtough; QString text; QStringList colors; + QStringList relatedCards; + bool upsideDownArt; int loyalty; QStringMap customPicURLs, customPicURLsHq; MuidMap muIds; @@ -148,13 +150,16 @@ public: const QString &_powtough = QString(), const QString &_text = QString(), const QStringList &_colors = QStringList(), + const QStringList &_relatedCards = QStringList(), + bool _upsideDownArt = false, int _loyalty = 0, bool _cipt = false, int _tableRow = 0, const SetList &_sets = SetList(), const QStringMap &_customPicURLs = QStringMap(), const QStringMap &_customPicURLsHq = QStringMap(), - MuidMap muids = MuidMap()); + MuidMap muids = MuidMap() + ); ~CardInfo(); const QString &getName() const { return name; } const QString &getSimpleName() const { return simpleName; } @@ -174,6 +179,8 @@ public: void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); } void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); } const QStringList &getColors() const { return colors; } + const QStringList &getRelatedCards() const { return relatedCards; } + bool getUpsideDownArt() const { return upsideDownArt; } QString getCustomPicURL(const QString &set) const { return customPicURLs.value(set); } QString getCustomPicURLHq(const QString &set) const { return customPicURLsHq.value(set); } int getMuId(const QString &set) const { return muIds.value(set); } diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 30c0e6a5..d2291e4e 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -676,7 +676,7 @@ void Player::retranslateUi() for (int i = 0; i < allPlayersActions.size(); ++i) allPlayersActions[i]->setText(tr("&All players")); } - + aPlay->setText(tr("&Play")); aHide->setText(tr("&Hide")); aPlayFacedown->setText(tr("Play &Face Down")); @@ -1076,6 +1076,30 @@ void Player::actCreatePredefinedToken() actCreateAnotherToken(); } +void Player::actCreateRelatedCard() +{ + // get the clicked card + CardItem * sourceCard = game->getActiveCard(); + if(!sourceCard) + return; + + // get the target card name + QAction *action = static_cast(sender()); + CardInfo *cardInfo = db->getCard(action->text()); + + // create the token for the related card + Command_CreateToken cmd; + cmd.set_zone("table"); + cmd.set_card_name(cardInfo->getName().toStdString()); + cmd.set_color(cardInfo->getColors().isEmpty() ? QString().toStdString() : cardInfo->getColors().first().toLower().toStdString()); + cmd.set_pt(cardInfo->getPowTough().toStdString()); + cmd.set_destroy_on_zone_change(true); + cmd.set_target_zone(sourceCard->getZone()->getName().toStdString()); + cmd.set_target_card_id(sourceCard->getId()); + + sendGameCommand(cmd); +} + void Player::actSayMessage() { QAction *a = qobject_cast(sender()); @@ -2247,6 +2271,17 @@ void Player::updateCardMenu(CardItem *card) cardMenu->addAction(aFlip); if (card->getFaceDown()) cardMenu->addAction(aPeek); + + QStringList relatedCards = card->getInfo()->getRelatedCards(); + if(relatedCards.size()) + { + QMenu * createRelatedCardMenu = cardMenu->addMenu(tr("Cr&eate related card")); + + for (int i = 0; i < relatedCards.size(); ++i) { + QAction *a = createRelatedCardMenu->addAction(relatedCards.at(i)); + connect(a, SIGNAL(triggered()), this, SLOT(actCreateRelatedCard())); + } + } cardMenu->addSeparator(); cardMenu->addAction(aAttach); if (card->getAttachedTo()) diff --git a/cockatrice/src/player.h b/cockatrice/src/player.h index 1fed2b10..3aba14db 100644 --- a/cockatrice/src/player.h +++ b/cockatrice/src/player.h @@ -141,6 +141,7 @@ private slots: void actOpenDeckInDeckEditor(); void actCreatePredefinedToken(); + void actCreateRelatedCard(); void cardMenuAction(); void actCardCounterTrigger(); void actAttach(); diff --git a/common/pb/command_create_token.proto b/common/pb/command_create_token.proto index 574fcb21..9c34a478 100644 --- a/common/pb/command_create_token.proto +++ b/common/pb/command_create_token.proto @@ -11,6 +11,8 @@ message Command_CreateToken { optional bool destroy_on_zone_change = 6; optional sint32 x = 7; optional sint32 y = 8; + optional string target_zone = 9; + optional sint32 target_card_id = 10 [default = -1]; } diff --git a/common/server_player.cpp b/common/server_player.cpp index 9f02acb0..d1413147 100644 --- a/common/server_player.cpp +++ b/common/server_player.cpp @@ -1065,7 +1065,7 @@ Response::ResponseCode Server_Player::cmdAttachCard(const Command_AttachCard &cm return Response::RespOk; } -Response::ResponseCode Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) +Response::ResponseCode Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer & rc, GameEventStorage &ges) { if (spectator) return Response::RespFunctionNotAllowed; @@ -1109,8 +1109,20 @@ Response::ResponseCode Server_Player::cmdCreateToken(const Command_CreateToken & event.set_x(x); event.set_y(y); ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + + // chck if the token is a replacement for an existing card + if(cmd.target_card_id() < 0) + return Response::RespOk; + + 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()); + + return cmdAttachCard(cmd2, rc, ges); } Response::ResponseCode Server_Player::cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 84e742a8..3a5d2f4e 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -65,7 +65,10 @@ CardInfo *OracleImporter::addCard(const QString &setName, const QString &cardPT, int cardLoyalty, const QString &cardText, - const QStringList & colors) + const QStringList & colors, + const QStringList & relatedCards, + bool upsideDown + ) { QStringList cardTextRows = cardText.split("\n"); bool splitCard = false; @@ -97,7 +100,7 @@ CardInfo *OracleImporter::addCard(const QString &setName, bool cipt = cardText.contains("Hideaway") || (cardText.contains(cardName + " enters the battlefield tapped") && !cardText.contains(cardName + " enters the battlefield tapped unless")); - card = new CardInfo(this, cardName, isToken, cardCost, cmc, cardType, cardPT, cardText, colors, cardLoyalty, cipt); + card = new CardInfo(this, cardName, isToken, cardCost, cmc, cardType, cardPT, cardText, colors, relatedCards, upsideDown, cardLoyalty, cipt); int tableRow = 1; QString mainCardType = card->getMainCardType(); if ((mainCardType == "Land") || mArtifact) @@ -147,9 +150,11 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) QString cardPT; QString cardText; QStringList colors; + QStringList relatedCards; int cardId; int cardLoyalty; bool cardIsToken = false; + bool upsideDown = false; QMap splitCards; while (it.hasNext()) { @@ -169,7 +174,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) cardLoyalty = 0; // determine which subcard is the first one in the split - QStringList names=map.contains("names") ? map.value("names").toStringList() : QStringList(""); + QStringList names=map.contains("names") ? map.value("names").toStringList() : QStringList(); if(names.count()>0 && map.contains("name") && 0 == QString::compare(map.value("name").toString(), names.at(0))) @@ -204,6 +209,8 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) extractColors(card2->value("colors").toStringList(), colors); colors.removeDuplicates(); + relatedCards = QStringList(); + upsideDown = false; } else { // first card of a pair; enqueue for later merging // Conditional on cardId because promo prints have no muid - see #640 @@ -222,6 +229,16 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0; cardIsToken = map.value("layout") == "token"; + relatedCards = map.contains("names") ? map.value("names").toStringList() : QStringList(); + relatedCards.removeAll(cardName); + + if(0 == QString::compare(map.value("layout").toString(), QString("flip"), Qt::CaseInsensitive)) + { + QStringList cardNames = map.contains("names") ? map.value("names").toStringList() : QStringList(); + upsideDown = (cardNames.indexOf(cardName) > 0); + } else { + upsideDown = false; + } colors.clear(); extractColors(map.value("colors").toStringList(), colors); @@ -233,7 +250,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) } if (!cardIsToken) { - CardInfo *card = addCard(set->getShortName(), cardName, cardIsToken, cardId, cardCost, cmc, cardType, cardPT, cardLoyalty, cardText, colors); + CardInfo *card = addCard(set->getShortName(), cardName, cardIsToken, cardId, cardCost, cmc, cardType, cardPT, cardLoyalty, cardText, colors, relatedCards, upsideDown); if (!set->contains(card)) { card->addToSet(set); diff --git a/oracle/src/oracleimporter.h b/oracle/src/oracleimporter.h index e3f06a44..888eabaf 100644 --- a/oracle/src/oracleimporter.h +++ b/oracle/src/oracleimporter.h @@ -29,7 +29,7 @@ private: QVariantMap setsMap; QString dataDir; - CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, QString &cardCost, QString &cmc, const QString &cardType, const QString &cardPT, int cardLoyalty, const QString &cardText, const QStringList & colors); + CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, QString &cardCost, QString &cmc, const QString &cardType, const QString &cardPT, int cardLoyalty, const QString &cardText, const QStringList & colors, const QStringList & relatedCards, bool upsideDown); signals: void setIndexChanged(int cardsImported, int setIndex, const QString &setName); void dataReadProgress(int bytesRead, int totalBytes);