From 7cbe4101728123e6deb2cf1795fa2babb004ee53 Mon Sep 17 00:00:00 2001 From: arxanas Date: Mon, 30 Jun 2014 01:18:05 -0400 Subject: [PATCH] Fix #45: don't send tokens to deckstats. --- cockatrice/src/carddatabase.cpp | 35 ++--------------------- cockatrice/src/carddatabase.h | 2 +- cockatrice/src/deckstats_interface.cpp | 39 ++++++++++++++++++++++++-- cockatrice/src/deckstats_interface.h | 14 ++++++++- cockatrice/src/tab_deck_editor.cpp | 5 +++- common/decklist.cpp | 30 ++++++++++++++------ common/decklist.h | 23 +++++++++++++++ oracle/src/oracleimporter.cpp | 4 ++- 8 files changed, 105 insertions(+), 47 deletions(-) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index eff101f9..c57ac792 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -652,41 +652,13 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml) } } -LoadStatus CardDatabase::loadFromFile(const QString &fileName, bool tokens) +LoadStatus CardDatabase::loadFromFile(const QString &fileName) { QFile file(fileName); file.open(QIODevice::ReadOnly); if (!file.isOpen()) return FileError; - if (tokens) { - QMutableHashIterator i(cardHash); - while (i.hasNext()) { - i.next(); - if (i.value()->getIsToken()) { - delete i.value(); - i.remove(); - } - } - } else { - QHashIterator setIt(setHash); - while (setIt.hasNext()) { - setIt.next(); - delete setIt.value(); - } - setHash.clear(); - - QMutableHashIterator i(cardHash); - while (i.hasNext()) { - i.next(); - if (!i.value()->getIsToken()) { - delete i.value(); - i.remove(); - } - } - cardHash.clear(); - } - QXmlStreamReader xml(&file); while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::StartElement) { @@ -738,8 +710,7 @@ bool CardDatabase::saveToFile(const QString &fileName, bool tokens) QHashIterator cardIterator(cardHash); while (cardIterator.hasNext()) { CardInfo *card = cardIterator.next().value(); - if (card->getIsToken() == tokens) - xml << card; + xml << card; } xml.writeEndElement(); // cards @@ -773,7 +744,7 @@ LoadStatus CardDatabase::loadCardDatabase(const QString &path, bool tokens) { LoadStatus tempLoadStatus = NotLoaded; if (!path.isEmpty()) - tempLoadStatus = loadFromFile(path, tokens); + tempLoadStatus = loadFromFile(path); if (tempLoadStatus == Ok) { SetList allSets; diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 09edeff4..be82099d 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -186,7 +186,7 @@ public: CardSet *getSet(const QString &setName); QList getCardList() const { return cardHash.values(); } SetList getSetList() const; - LoadStatus loadFromFile(const QString &fileName, bool tokens = false); + LoadStatus loadFromFile(const QString &fileName); bool saveToFile(const QString &fileName, bool tokens = false); QStringList getAllColors() const; QStringList getAllMainCardTypes() const; diff --git a/cockatrice/src/deckstats_interface.cpp b/cockatrice/src/deckstats_interface.cpp index eae4caae..d3c96d8b 100644 --- a/cockatrice/src/deckstats_interface.cpp +++ b/cockatrice/src/deckstats_interface.cpp @@ -7,8 +7,10 @@ #include #include -DeckStatsInterface::DeckStatsInterface(QObject *parent) - : QObject(parent) +DeckStatsInterface::DeckStatsInterface( + CardDatabase &_cardDatabase, + QObject *parent +) : QObject(parent), cardDatabase(_cardDatabase) { manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryFinished(QNetworkReply *))); @@ -42,7 +44,11 @@ void DeckStatsInterface::queryFinished(QNetworkReply *reply) void DeckStatsInterface::analyzeDeck(DeckList *deck) { QUrl params; - params.addQueryItem("deck", deck->writeToString_Plain()); + + DeckList deckWithoutTokens; + copyDeckWithoutTokens(*deck, deckWithoutTokens); + + params.addQueryItem("deck", deckWithoutTokens.writeToString_Plain()); QByteArray data; data.append(params.encodedQuery()); @@ -51,3 +57,30 @@ void DeckStatsInterface::analyzeDeck(DeckList *deck) manager->post(request, data); } + +struct CopyIfNotAToken { + CardDatabase &cardDatabase; + DeckList &destination; + + CopyIfNotAToken( + CardDatabase &_cardDatabase, + DeckList &_destination + ) : cardDatabase(_cardDatabase), destination(_destination) {}; + + void operator()( + const InnerDecklistNode *node, + const DecklistCardNode *card + ) const { + if (!cardDatabase.getCard(card->getName())->getIsToken()) { + destination.addCard(card->getName(), node->getName()); + } + } +}; + +void DeckStatsInterface::copyDeckWithoutTokens( + const DeckList &source, + DeckList &destination +) { + CopyIfNotAToken copyIfNotAToken(cardDatabase, destination); + source.forEachCard(copyIfNotAToken); +} diff --git a/cockatrice/src/deckstats_interface.h b/cockatrice/src/deckstats_interface.h index e8454dd5..928d5220 100644 --- a/cockatrice/src/deckstats_interface.h +++ b/cockatrice/src/deckstats_interface.h @@ -1,6 +1,8 @@ #ifndef DECKSTATS_INTERFACE_H #define DECKSTATS_INTERFACE_H +#include "carddatabase.h" +#include "decklist.h" #include class QNetworkAccessManager; @@ -11,10 +13,20 @@ class DeckStatsInterface : public QObject { Q_OBJECT private: QNetworkAccessManager *manager; + + CardDatabase &cardDatabase; + + /** + * Deckstats doesn't recognize token cards, and instead tries to find the + * closest non-token card instead. So we construct a new deck which has no + * tokens. + */ + void copyDeckWithoutTokens(const DeckList &source, DeckList& destination); + private slots: void queryFinished(QNetworkReply *reply); public: - DeckStatsInterface(QObject *parent = 0); + DeckStatsInterface(CardDatabase &_cardDatabase, QObject *parent = 0); void analyzeDeck(DeckList *deck); }; diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 0c1d44e1..96fb9a90 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -508,7 +508,10 @@ void TabDeckEditor::actPrintDeck() void TabDeckEditor::actAnalyzeDeck() { - DeckStatsInterface *interface = new DeckStatsInterface(this); // it deletes itself when done + DeckStatsInterface *interface = new DeckStatsInterface( + *databaseModel->getDatabase(), + this + ); // it deletes itself when done interface->analyzeDeck(deckModel->getDeckList()); } diff --git a/common/decklist.cpp b/common/decklist.cpp index 90b5cb7e..1dd9acf3 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -579,16 +579,30 @@ bool DeckList::loadFromFile_Plain(QIODevice *device) return loadFromStream_Plain(in); } +struct WriteToStream { + QTextStream &stream; + + WriteToStream(QTextStream &_stream) : stream(_stream) {} + + void operator()( + const InnerDecklistNode *node, + const DecklistCardNode *card + ) { + if (node->getName() == "side") { + stream << "SB: "; + } + stream << QString("%1 %2\n").arg( + card->getNumber() + ).arg( + card->getName() + ); + } +}; + bool DeckList::saveToStream_Plain(QTextStream &out) { - // Support for this is only possible if the internal structure doesn't get more complicated. - for (int i = 0; i < root->size(); i++) { - InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - out << QString("%1%2 %3\n").arg(node->getName() == "side" ? "SB: " : "").arg(card->getNumber()).arg(card->getName()); - } - } + WriteToStream writeToStream(out); + forEachCard(writeToStream); return true; } diff --git a/common/decklist.h b/common/decklist.h index 2eed52fc..4dfd2bbd 100644 --- a/common/decklist.h +++ b/common/decklist.h @@ -117,6 +117,7 @@ public: QString getName() const { return name; } void setName(const QString &_name) { name = _name; } float getPrice() const { return price; } + void setPrice(const float _price) { price = _price; } }; @@ -169,6 +170,28 @@ public: InnerDecklistNode *getRoot() const { return root; } DecklistCardNode *addCard(const QString &cardName, const QString &zoneName); bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = 0); + + /** + * Calls a given function object for each card in the deck. It must + * take a InnerDecklistNode* as its first argument and a + * DecklistCardNode* as its second. + */ + template + void forEachCard(Callback &callback) const { + // Support for this is only possible if the internal structure + // doesn't get more complicated. + for (int i = 0; i < root->size(); i++) { + const InnerDecklistNode *node = + dynamic_cast(root->at(i)); + for (int j = 0; j < node->size(); j++) { + const DecklistCardNode *card = + dynamic_cast( + node->at(j) + ); + callback(node, card); + } + } + } }; #endif diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 70972d92..0da35abe 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -137,6 +137,7 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) QString cardText; int cardId; int cardLoyalty; + bool cardIsToken = false; QMap splitCards; while (it.hasNext()) { @@ -197,9 +198,10 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) cardText = map.contains("text") ? map.value("text").toString() : QString(""); cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0; + cardIsToken = map.value("layout") == "token"; } - CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n")); + CardInfo *card = addCard(set->getShortName(), cardName, cardIsToken, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n")); if (!set->contains(card)) { card->addToSet(set);