Cockatrice/common/decklist.h
ebbit1q 1b62022f25 don't update the deckhash on load
add a new property to decklists that tracks if the deck needs to update
the deckhash

only update the deckhash if needed when it is asked for

this saves computing the deckhash for every loaded deck as performed by
the visual deck selector with impunity

the copy constructor also now no longer computes the deck hash again

the deck hash is now only computed once, on deck load, as expected,
resulting in massive amounts of cpu cycles not being wasted
2025-03-10 19:50:47 +01:00

400 lines
12 KiB
C++

#ifndef DECKLIST_H
#define DECKLIST_H
#include <QMap>
#include <QVector>
// Required on Mac. Forward declaration doesn't work. Don't ask why.
#include <QtCore/QXmlStreamReader>
#include <QtCore/QXmlStreamWriter>
#include <common/pb/move_card_to_zone.pb.h>
class CardDatabase;
class QIODevice;
class QTextStream;
class InnerDecklistNode;
#define DECK_ZONE_MAIN "main"
#define DECK_ZONE_SIDE "side"
#define DECK_ZONE_TOKENS "tokens"
class SideboardPlan
{
private:
QString name;
QList<MoveCard_ToZone> moveList;
public:
explicit SideboardPlan(const QString &_name = QString(),
const QList<MoveCard_ToZone> &_moveList = QList<MoveCard_ToZone>());
bool readElement(QXmlStreamReader *xml);
void write(QXmlStreamWriter *xml);
QString getName() const
{
return name;
}
const QList<MoveCard_ToZone> &getMoveList() const
{
return moveList;
}
void setMoveList(const QList<MoveCard_ToZone> &_moveList);
};
enum DeckSortMethod
{
ByNumber,
ByName,
Default
};
class AbstractDecklistNode
{
protected:
InnerDecklistNode *parent;
DeckSortMethod sortMethod;
public:
explicit AbstractDecklistNode(InnerDecklistNode *_parent = nullptr);
virtual ~AbstractDecklistNode() = default;
virtual void setSortMethod(DeckSortMethod method)
{
sortMethod = method;
}
virtual QString getName() const = 0;
virtual QString getCardProviderId() const = 0;
virtual QString getCardSetShortName() const = 0;
virtual QString getCardCollectorNumber() const = 0;
[[nodiscard]] virtual bool isDeckHeader() const = 0;
InnerDecklistNode *getParent() const
{
return parent;
}
int depth() const;
virtual int height() const = 0;
virtual bool compare(AbstractDecklistNode *other) const = 0;
virtual bool readElement(QXmlStreamReader *xml) = 0;
virtual void writeElement(QXmlStreamWriter *xml) = 0;
};
class InnerDecklistNode : public AbstractDecklistNode, public QList<AbstractDecklistNode *>
{
QString name;
QString cardSetShortName;
QString cardCollectorNumber;
QString cardProviderId;
class compareFunctor;
public:
explicit InnerDecklistNode(QString _name = QString(), InnerDecklistNode *_parent = nullptr)
: AbstractDecklistNode(_parent), name(std::move(_name))
{
}
explicit InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent = nullptr);
~InnerDecklistNode() override;
void setSortMethod(DeckSortMethod method) override;
[[nodiscard]] QString getName() const override
{
return name;
}
void setName(const QString &_name)
{
name = _name;
}
static QString visibleNameFromName(const QString &_name);
[[nodiscard]] virtual QString getVisibleName() const;
[[nodiscard]] QString getCardProviderId() const override
{
return cardProviderId;
}
void setCardProviderId(const QString &_cardProviderId)
{
cardProviderId = _cardProviderId;
}
[[nodiscard]] QString getCardSetShortName() const override
{
return cardSetShortName;
}
void setCardSetShortName(const QString &_cardSetShortName)
{
cardSetShortName = _cardSetShortName;
}
[[nodiscard]] QString getCardCollectorNumber() const override
{
return cardCollectorNumber;
}
void setCardCollectorNumber(const QString &_cardCollectorNumber)
{
cardCollectorNumber = _cardCollectorNumber;
}
[[nodiscard]] bool isDeckHeader() const override
{
return true;
}
void clearTree();
AbstractDecklistNode *findChild(const QString &_name);
AbstractDecklistNode *findCardChildByNameProviderIdAndNumber(const QString &_name,
const QString &_providerId,
const QString &_cardNumber = "");
int height() const override;
int recursiveCount(bool countTotalCards = false) const;
bool compare(AbstractDecklistNode *other) const override;
bool compareNumber(AbstractDecklistNode *other) const;
bool compareName(AbstractDecklistNode *other) const;
QVector<QPair<int, int>> sort(Qt::SortOrder order = Qt::AscendingOrder);
bool readElement(QXmlStreamReader *xml) override;
void writeElement(QXmlStreamWriter *xml) override;
};
class AbstractDecklistCardNode : public AbstractDecklistNode
{
public:
explicit AbstractDecklistCardNode(InnerDecklistNode *_parent = nullptr) : AbstractDecklistNode(_parent)
{
}
virtual int getNumber() const = 0;
virtual void setNumber(int _number) = 0;
QString getName() const override = 0;
virtual void setName(const QString &_name) = 0;
virtual QString getCardProviderId() const override = 0;
virtual void setCardProviderId(const QString &_cardProviderId) = 0;
virtual QString getCardSetShortName() const override = 0;
virtual void setCardSetShortName(const QString &_cardSetShortName) = 0;
virtual QString getCardCollectorNumber() const override = 0;
virtual void setCardCollectorNumber(const QString &_cardSetNumber) = 0;
int height() const override
{
return 0;
}
bool compare(AbstractDecklistNode *other) const override;
bool compareNumber(AbstractDecklistNode *other) const;
bool compareName(AbstractDecklistNode *other) const;
bool readElement(QXmlStreamReader *xml) override;
void writeElement(QXmlStreamWriter *xml) override;
};
class DecklistCardNode : public AbstractDecklistCardNode
{
QString name;
int number;
QString cardSetShortName;
QString cardSetNumber;
QString cardProviderId;
public:
explicit DecklistCardNode(QString _name = QString(),
int _number = 1,
InnerDecklistNode *_parent = nullptr,
QString _cardSetShortName = QString(),
QString _cardSetNumber = QString(),
QString _cardProviderId = QString())
: AbstractDecklistCardNode(_parent), name(std::move(_name)), number(_number),
cardSetShortName(std::move(_cardSetShortName)), cardSetNumber(std::move(_cardSetNumber)),
cardProviderId(std::move(_cardProviderId))
{
}
explicit DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent);
int getNumber() const override
{
return number;
}
void setNumber(int _number) override
{
number = _number;
}
QString getName() const override
{
return name;
}
void setName(const QString &_name) override
{
name = _name;
}
QString getCardProviderId() const override
{
return cardProviderId;
}
void setCardProviderId(const QString &_providerId) override
{
cardProviderId = _providerId;
}
QString getCardSetShortName() const override
{
return cardSetShortName;
}
void setCardSetShortName(const QString &_cardSetShortName) override
{
cardSetShortName = _cardSetShortName;
}
QString getCardCollectorNumber() const override
{
return cardSetNumber;
}
void setCardCollectorNumber(const QString &_cardSetNumber) override
{
cardSetNumber = _cardSetNumber;
}
[[nodiscard]] bool isDeckHeader() const override
{
return false;
}
};
class DeckList : public QObject
{
Q_OBJECT
private:
QString name, comments;
QPair<QString, QString> bannerCard;
QString deckHash;
bool deckHashDirty;
QString lastLoadedTimestamp;
QStringList tags;
QMap<QString, SideboardPlan *> sideboardPlans;
InnerDecklistNode *root;
void getCardListHelper(InnerDecklistNode *node, QSet<QString> &result) const;
void getCardListWithProviderIdHelper(InnerDecklistNode *item, QMap<QString, QString> &result) const;
InnerDecklistNode *getZoneObjFromName(const QString &zoneName);
protected:
virtual QString getCardZoneFromName(const QString /*cardName*/, QString currentZoneName)
{
return currentZoneName;
};
virtual QString getCompleteCardName(const QString &cardName) const
{
return cardName;
};
signals:
void deckHashChanged();
void deckTagsChanged();
public slots:
void setName(const QString &_name = QString())
{
name = _name;
}
void setComments(const QString &_comments = QString())
{
comments = _comments;
}
void setTags(const QStringList &_tags = QStringList())
{
tags = _tags;
emit deckTagsChanged();
}
void addTag(const QString &_tag)
{
tags.append(_tag);
emit deckTagsChanged();
}
void clearTags()
{
tags.clear();
emit deckTagsChanged();
}
void setBannerCard(const QPair<QString, QString> &_bannerCard = QPair<QString, QString>())
{
bannerCard = _bannerCard;
}
void setLastLoadedTimestamp(const QString &_lastLoadedTimestamp = QString())
{
lastLoadedTimestamp = _lastLoadedTimestamp;
}
public:
explicit DeckList();
DeckList(const DeckList &other);
explicit DeckList(const QString &nativeString);
~DeckList() override;
QString getName() const
{
return name;
}
QString getComments() const
{
return comments;
}
QStringList getTags() const
{
return tags;
}
QPair<QString, QString> getBannerCard() const
{
return bannerCard;
}
QString getLastLoadedTimestamp() const
{
return lastLoadedTimestamp;
}
QList<MoveCard_ToZone> getCurrentSideboardPlan();
void setCurrentSideboardPlan(const QList<MoveCard_ToZone> &plan);
const QMap<QString, SideboardPlan *> &getSideboardPlans() const
{
return sideboardPlans;
}
bool readElement(QXmlStreamReader *xml);
void write(QXmlStreamWriter *xml);
bool loadFromXml(QXmlStreamReader *xml);
bool loadFromString_Native(const QString &nativeString);
QString writeToString_Native();
bool loadFromFile_Native(QIODevice *device);
bool saveToFile_Native(QIODevice *device);
bool loadFromStream_Plain(QTextStream &stream);
bool loadFromFile_Plain(QIODevice *device);
bool saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards);
bool saveToFile_Plain(QIODevice *device, bool prefixSideboardCards = true, bool slashTappedOutSplitCards = false);
QString writeToString_Plain(bool prefixSideboardCards = true, bool slashTappedOutSplitCards = false);
void cleanList();
bool isEmpty() const
{
return root->isEmpty() && name.isEmpty() && comments.isEmpty() && sideboardPlans.isEmpty();
}
QStringList getCardList() const;
QMap<QString, QString> getCardListWithProviderId() const;
int getSideboardSize() const;
QString getDeckHash();
void updateDeckHash();
InnerDecklistNode *getRoot() const
{
return root;
}
DecklistCardNode *addCard(const QString &cardName,
const QString &zoneName,
const QString &cardSetName = QString(),
const QString &cardSetCollectorNumber = QString(),
const QString &cardProviderId = QString());
bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr);
/**
* 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 <typename Callback> void forEachCard(Callback &callback)
{
// 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<InnerDecklistNode *>(root->at(i));
for (int j = 0; j < node->size(); j++) {
DecklistCardNode *card = dynamic_cast<DecklistCardNode *>(node->at(j));
callback(node, card);
}
}
}
};
#endif