diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index ca16d3d15..cfe0430e8 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -26,6 +26,7 @@ set(cockatrice_SOURCES src/client/ui/widgets/cards/card_info_text_widget.cpp src/client/ui/widgets/cards/card_info_display_widget.cpp src/client/ui/widgets/cards/card_size_widget.cpp + src/game/cards/card_info.cpp src/game/cards/card_item.cpp src/game/cards/card_list.cpp src/game/zones/card_zone.cpp diff --git a/cockatrice/resources/config/qtlogging.ini b/cockatrice/resources/config/qtlogging.ini index b141cbd00..378b962b7 100644 --- a/cockatrice/resources/config/qtlogging.ini +++ b/cockatrice/resources/config/qtlogging.ini @@ -44,6 +44,7 @@ # cockatrice_xml.* = false # cockatrice_xml.xml_3_parser = false # cockatrice_xml.xml_4_parser = false +# card_info = false # card_list = false # stack_zone = false diff --git a/cockatrice/src/game/cards/card_database.cpp b/cockatrice/src/game/cards/card_database.cpp index c7e2f06f2..b22bc2891 100644 --- a/cockatrice/src/game/cards/card_database.cpp +++ b/cockatrice/src/game/cards/card_database.cpp @@ -1,10 +1,8 @@ #include "card_database.h" -#include "../../client/network/spoiler_background_updater.h" #include "../../client/ui/picture_loader/picture_loader.h" #include "../../settings/cache_settings.h" #include "../../utility/card_set_comparator.h" -#include "../game_specific_terms.h" #include "./card_database_parser/cockatrice_xml_3.h" #include "./card_database_parser/cockatrice_xml_4.h" @@ -20,351 +18,6 @@ const char *CardDatabase::TOKENS_SETNAME = "TK"; -CardSet::CardSet(const QString &_shortName, - const QString &_longName, - const QString &_setType, - const QDate &_releaseDate, - const CardSet::Priority _priority) - : shortName(_shortName), longName(_longName), releaseDate(_releaseDate), setType(_setType), priority(_priority) -{ - loadSetOptions(); -} - -CardSetPtr CardSet::newInstance(const QString &_shortName, - const QString &_longName, - const QString &_setType, - const QDate &_releaseDate, - const Priority _priority) -{ - CardSetPtr ptr(new CardSet(_shortName, _longName, _setType, _releaseDate, _priority)); - // ptr->setSmartPointer(ptr); - return ptr; -} - -QString CardSet::getCorrectedShortName() const -{ - // For Windows machines. - QSet invalidFileNames; - invalidFileNames << "CON" - << "PRN" - << "AUX" - << "NUL" - << "COM1" - << "COM2" - << "COM3" - << "COM4" - << "COM5" - << "COM6" - << "COM7" - << "COM8" - << "COM9" - << "LPT1" - << "LPT2" - << "LPT3" - << "LPT4" - << "LPT5" - << "LPT6" - << "LPT7" - << "LPT8" - << "LPT9"; - - return invalidFileNames.contains(shortName) ? shortName + "_" : shortName; -} - -void CardSet::loadSetOptions() -{ - sortKey = SettingsCache::instance().cardDatabase().getSortKey(shortName); - enabled = SettingsCache::instance().cardDatabase().isEnabled(shortName); - isknown = SettingsCache::instance().cardDatabase().isKnown(shortName); -} - -void CardSet::setSortKey(unsigned int _sortKey) -{ - sortKey = _sortKey; - SettingsCache::instance().cardDatabase().setSortKey(shortName, _sortKey); -} - -void CardSet::setEnabled(bool _enabled) -{ - enabled = _enabled; - SettingsCache::instance().cardDatabase().setEnabled(shortName, _enabled); -} - -void CardSet::setIsKnown(bool _isknown) -{ - isknown = _isknown; - SettingsCache::instance().cardDatabase().setIsKnown(shortName, _isknown); -} - -class SetList::KeyCompareFunctor -{ -public: - inline bool operator()(const CardSetPtr &a, const CardSetPtr &b) const - { - if (a.isNull() || b.isNull()) { - qCDebug(CardDatabaseLog) << "SetList::KeyCompareFunctor a or b is null"; - return false; - } - - return a->getSortKey() < b->getSortKey(); - } -}; - -void SetList::sortByKey() -{ - std::sort(begin(), end(), KeyCompareFunctor()); -} - -int SetList::getEnabledSetsNum() -{ - int num = 0; - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set && set->getEnabled()) { - ++num; - } - } - return num; -} - -int SetList::getUnknownSetsNum() -{ - int num = 0; - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { - ++num; - } - } - return num; -} - -QStringList SetList::getUnknownSetsNames() -{ - QStringList sets = QStringList(); - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { - sets << set->getShortName(); - } - } - return sets; -} - -void SetList::enableAllUnknown() -{ - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { - set->setIsKnown(true); - set->setEnabled(true); - } else if (set && set->getIsKnownIgnored() && !set->getEnabled()) { - set->setEnabled(true); - } - } -} - -void SetList::enableAll() -{ - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - - if (set == nullptr) { - qCDebug(CardDatabaseLog) << "enabledAll has null"; - continue; - } - - if (!set->getIsKnownIgnored()) { - set->setIsKnown(true); - } - - set->setEnabled(true); - } -} - -void SetList::markAllAsKnown() -{ - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { - set->setIsKnown(true); - set->setEnabled(false); - } else if (set && set->getIsKnownIgnored() && !set->getEnabled()) { - set->setEnabled(true); - } - } -} - -void SetList::guessSortKeys() -{ - defaultSort(); - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set.isNull()) { - qCDebug(CardDatabaseLog) << "guessSortKeys set is null"; - continue; - } - set->setSortKey(i); - } -} - -void SetList::defaultSort() -{ - std::sort(begin(), end(), [](const CardSetPtr &a, const CardSetPtr &b) { - // Sort by priority, then by release date, then by short name - if (a->getPriority() != b->getPriority()) { - return a->getPriority() < b->getPriority(); // lowest first - } else if (a->getReleaseDate() != b->getReleaseDate()) { - return a->getReleaseDate() > b->getReleaseDate(); // most recent first - } else { - return a->getShortName() < b->getShortName(); // alphabetically - } - }); -} - -CardInfoPerSet::CardInfoPerSet(const CardSetPtr &_set) : set(_set) -{ -} - -CardInfo::CardInfo(const QString &_name, - const QString &_text, - bool _isToken, - QVariantHash _properties, - const QList &_relatedCards, - const QList &_reverseRelatedCards, - CardInfoPerSetMap _sets, - bool _cipt, - bool _landscapeOrientation, - int _tableRow, - bool _upsideDownArt) - : name(_name), text(_text), isToken(_isToken), properties(std::move(_properties)), relatedCards(_relatedCards), - reverseRelatedCards(_reverseRelatedCards), sets(std::move(_sets)), cipt(_cipt), - landscapeOrientation(_landscapeOrientation), tableRow(_tableRow), upsideDownArt(_upsideDownArt) -{ - pixmapCacheKey = QLatin1String("card_") + name; - simpleName = CardInfo::simplifyName(name); - - refreshCachedSetNames(); -} - -CardInfo::~CardInfo() -{ - PictureLoader::clearPixmapCache(smartThis); -} - -CardInfoPtr CardInfo::newInstance(const QString &_name) -{ - return newInstance(_name, QString(), false, QVariantHash(), QList(), QList(), - CardInfoPerSetMap(), false, false, 0, false); -} - -CardInfoPtr CardInfo::newInstance(const QString &_name, - const QString &_text, - bool _isToken, - QVariantHash _properties, - const QList &_relatedCards, - const QList &_reverseRelatedCards, - CardInfoPerSetMap _sets, - bool _cipt, - bool _landscapeOrientation, - int _tableRow, - bool _upsideDownArt) -{ - CardInfoPtr ptr(new CardInfo(_name, _text, _isToken, std::move(_properties), _relatedCards, _reverseRelatedCards, - _sets, _cipt, _landscapeOrientation, _tableRow, _upsideDownArt)); - ptr->setSmartPointer(ptr); - - for (const auto &cardInfoPerSetList : _sets) { - for (const CardInfoPerSet &set : cardInfoPerSetList) { - set.getPtr()->append(ptr); - break; - } - } - - return ptr; -} - -QString CardInfo::getCorrectedName() const -{ - // remove all the characters reserved in windows file paths, - // other oses only disallow a subset of these so it covers all - static const QRegularExpression rmrx(R"(( // |[*<>:"\\?\x00-\x08\x10-\x1f]))"); - static const QRegularExpression spacerx(R"([/\x09-\x0f])"); - static const QString space(' '); - QString result = name; - // Fire // Ice, Circle of Protection: Red, "Ach! Hans, Run!", Who/What/When/Where/Why, Question Elemental? - return result.remove(rmrx).replace(spacerx, space); -} - -void CardInfo::addToSet(const CardSetPtr &_set, const CardInfoPerSet _info) -{ - _set->append(smartThis); - sets[_set->getShortName()].append(_info); - - refreshCachedSetNames(); -} - -void CardInfo::combineLegalities(const QVariantHash &props) -{ - QHashIterator it(props); - while (it.hasNext()) { - it.next(); - if (it.key().startsWith("format-")) { - smartThis->setProperty(it.key(), it.value().toString()); - } - } -} - -void CardInfo::refreshCachedSetNames() -{ - QStringList setList; - // update the cached list of set names - for (const auto &cardInfoPerSetList : sets) { - for (const auto &set : cardInfoPerSetList) { - if (set.getPtr()->getEnabled()) { - setList << set.getPtr()->getShortName(); - } - break; - } - } - setsNames = setList.join(", "); -} - -QString CardInfo::simplifyName(const QString &name) -{ - static const QRegularExpression spaceOrSplit("(\\s+|\\/\\/.*)"); - static const QRegularExpression nonAlnum("[^a-z0-9]"); - - QString simpleName = name.toLower(); - - // remove spaces and right halves of split cards - simpleName.remove(spaceOrSplit); - - // So Aetherling would work, but not Ætherling since 'Æ' would get replaced - // with nothing. - simpleName.replace("æ", "ae"); - - // Replace Jötun Grunt with Jotun Grunt. - simpleName = simpleName.normalized(QString::NormalizationForm_KD); - - // remove all non alphanumeric characters from the name - simpleName.remove(nonAlnum); - return simpleName; -} - -const QChar CardInfo::getColorChar() const -{ - QString colors = getColors(); - switch (colors.size()) { - case 0: - return QChar(); - case 1: - return colors.at(0); - default: - return QChar('m'); - } -} - CardDatabase::CardDatabase(QObject *parent) : QObject(parent), loadStatus(NotLoaded) { qRegisterMetaType("CardInfoPtr"); @@ -883,65 +536,4 @@ bool CardDatabase::saveCustomTokensToFile() availableParsers.first()->saveToFile(tmpSets, tmpCards, fileName); return true; -} - -CardRelation::CardRelation(const QString &_name, - AttachType _attachType, - bool _isCreateAllExclusion, - bool _isVariableCount, - int _defaultCount, - bool _isPersistent) - : name(_name), attachType(_attachType), isCreateAllExclusion(_isCreateAllExclusion), - isVariableCount(_isVariableCount), defaultCount(_defaultCount), isPersistent(_isPersistent) -{ -} - -void CardInfo::resetReverseRelatedCards2Me() -{ - for (CardRelation *cardRelation : this->getReverseRelatedCards2Me()) { - cardRelation->deleteLater(); - } - reverseRelatedCardsToMe = QList(); -} - -// Back-compatibility methods. Remove ASAP -const QString CardInfo::getCardType() const -{ - return getProperty(Mtg::CardType); -} -void CardInfo::setCardType(const QString &value) -{ - setProperty(Mtg::CardType, value); -} -const QString CardInfo::getCmc() const -{ - return getProperty(Mtg::ConvertedManaCost); -} -const QString CardInfo::getColors() const -{ - return getProperty(Mtg::Colors); -} -void CardInfo::setColors(const QString &value) -{ - setProperty(Mtg::Colors, value); -} -const QString CardInfo::getLoyalty() const -{ - return getProperty(Mtg::Loyalty); -} -const QString CardInfo::getMainCardType() const -{ - return getProperty(Mtg::MainCardType); -} -const QString CardInfo::getManaCost() const -{ - return getProperty(Mtg::ManaCost); -} -const QString CardInfo::getPowTough() const -{ - return getProperty(Mtg::PowTough); -} -void CardInfo::setPowTough(const QString &value) -{ - setProperty(Mtg::PowTough, value); -} +} \ No newline at end of file diff --git a/cockatrice/src/game/cards/card_database.h b/cockatrice/src/game/cards/card_database.h index 12b4a06fe..236278b30 100644 --- a/cockatrice/src/game/cards/card_database.h +++ b/cockatrice/src/game/cards/card_database.h @@ -1,16 +1,14 @@ #ifndef CARDDATABASE_H #define CARDDATABASE_H +#include "card_info.h" + #include #include #include #include #include -#include -#include -#include #include -#include #include #include @@ -18,406 +16,8 @@ inline Q_LOGGING_CATEGORY(CardDatabaseLog, "card_database"); inline Q_LOGGING_CATEGORY(CardDatabaseLoadingLog, "card_database.loading"); inline Q_LOGGING_CATEGORY(CardDatabaseLoadingSuccessOrFailureLog, "card_database.loading.success_or_failure"); -class CardDatabase; -class CardInfo; -class CardInfoPerSet; -class CardSet; -class CardRelation; class ICardDatabaseParser; -typedef QMap QStringMap; -typedef QSharedPointer CardInfoPtr; -typedef QSharedPointer CardSetPtr; -typedef QMap> CardInfoPerSetMap; - -Q_DECLARE_METATYPE(CardInfoPtr) - -class CardSet : public QList -{ -public: - enum Priority - { - PriorityFallback = 0, - PriorityPrimary = 10, - PrioritySecondary = 20, - PriorityReprint = 30, - PriorityOther = 40, - PriorityLowest = 100, - }; - -private: - QString shortName, longName; - unsigned int sortKey; - QDate releaseDate; - QString setType; - Priority priority; - bool enabled, isknown; - -public: - explicit CardSet(const QString &_shortName = QString(), - const QString &_longName = QString(), - const QString &_setType = QString(), - const QDate &_releaseDate = QDate(), - const Priority _priority = PriorityFallback); - static CardSetPtr newInstance(const QString &_shortName = QString(), - const QString &_longName = QString(), - const QString &_setType = QString(), - const QDate &_releaseDate = QDate(), - const Priority _priority = PriorityFallback); - QString getCorrectedShortName() const; - QString getShortName() const - { - return shortName; - } - QString getLongName() const - { - return longName; - } - QString getSetType() const - { - return setType; - } - QDate getReleaseDate() const - { - return releaseDate; - } - Priority getPriority() const - { - return priority; - } - void setLongName(const QString &_longName) - { - longName = _longName; - } - void setSetType(const QString &_setType) - { - setType = _setType; - } - void setReleaseDate(const QDate &_releaseDate) - { - releaseDate = _releaseDate; - } - void setPriority(const Priority _priority) - { - priority = _priority; - } - - void loadSetOptions(); - int getSortKey() const - { - return sortKey; - } - void setSortKey(unsigned int _sortKey); - bool getEnabled() const - { - return enabled; - } - void setEnabled(bool _enabled); - bool getIsKnown() const - { - return isknown; - } - void setIsKnown(bool _isknown); - - // Determine incomplete sets. - bool getIsKnownIgnored() const - { - return longName.length() + setType.length() + releaseDate.toString().length() == 0; - } -}; - -class SetList : public QList -{ -private: - class KeyCompareFunctor; - -public: - void sortByKey(); - void guessSortKeys(); - void enableAllUnknown(); - void enableAll(); - void markAllAsKnown(); - int getEnabledSetsNum(); - int getUnknownSetsNum(); - QStringList getUnknownSetsNames(); - void defaultSort(); -}; - -class CardInfoPerSet -{ -public: - explicit CardInfoPerSet(const CardSetPtr &_set = QSharedPointer(nullptr)); - ~CardInfoPerSet() = default; - - bool operator==(const CardInfoPerSet &other) const - { - return this->set == other.set && this->properties == other.properties; - } - -private: - CardSetPtr set; - // per-set card properties; - QVariantHash properties; - -public: - const CardSetPtr getPtr() const - { - return set; - } - const QStringList getProperties() const - { - return properties.keys(); - } - const QString getProperty(const QString &propertyName) const - { - return properties.value(propertyName).toString(); - } - void setProperty(const QString &_name, const QString &_value) - { - properties.insert(_name, _value); - } -}; - -class CardInfo : public QObject -{ - Q_OBJECT -private: - CardInfoPtr smartThis; - // The card name - QString name; - // The name without punctuation or capitalization, for better card name recognition. - QString simpleName; - // The key used to identify this card in the cache - QString pixmapCacheKey; - // card text - QString text; - // whether this is not a "real" card but a token - bool isToken; - // basic card properties; common for all the sets - QVariantHash properties; - // the cards i'm related to - QList relatedCards; - // the card i'm reverse-related to - QList reverseRelatedCards; - // the cards thare are reverse-related to me - QList reverseRelatedCardsToMe; - // card sets - CardInfoPerSetMap sets; - // cached set names - QString setsNames; - // positioning properties; used by UI - bool cipt; - bool landscapeOrientation; - int tableRow; - bool upsideDownArt; - -public: - explicit CardInfo(const QString &_name, - const QString &_text, - bool _isToken, - QVariantHash _properties, - const QList &_relatedCards, - const QList &_reverseRelatedCards, - CardInfoPerSetMap _sets, - bool _cipt, - bool _landscapeOrientation, - int _tableRow, - bool _upsideDownArt); - CardInfo(const CardInfo &other) - : QObject(other.parent()), name(other.name), simpleName(other.simpleName), pixmapCacheKey(other.pixmapCacheKey), - text(other.text), isToken(other.isToken), properties(other.properties), relatedCards(other.relatedCards), - reverseRelatedCards(other.reverseRelatedCards), reverseRelatedCardsToMe(other.reverseRelatedCardsToMe), - sets(other.sets), setsNames(other.setsNames), cipt(other.cipt), - landscapeOrientation(other.landscapeOrientation), tableRow(other.tableRow), upsideDownArt(other.upsideDownArt) - { - } - ~CardInfo() override; - - static CardInfoPtr newInstance(const QString &_name); - - static CardInfoPtr newInstance(const QString &_name, - const QString &_text, - bool _isToken, - QVariantHash _properties, - const QList &_relatedCards, - const QList &_reverseRelatedCards, - CardInfoPerSetMap _sets, - bool _cipt, - bool _landscapeOrientation, - int _tableRow, - bool _upsideDownArt); - - CardInfoPtr clone() const - { - // Use the copy constructor to create a new instance - CardInfoPtr newCardInfo = CardInfoPtr(new CardInfo(*this)); - newCardInfo->setSmartPointer(newCardInfo); // Set the smart pointer for the new instance - return newCardInfo; - } - - void setSmartPointer(CardInfoPtr _ptr) - { - smartThis = std::move(_ptr); - } - - // basic properties - inline const QString &getName() const - { - return name; - } - const QString &getSimpleName() const - { - return simpleName; - } - void setPixmapCacheKey(QString _pixmapCacheKey) - { - pixmapCacheKey = _pixmapCacheKey; - } - const QString &getPixmapCacheKey() const - { - return pixmapCacheKey; - } - - const QString &getText() const - { - return text; - } - void setText(const QString &_text) - { - text = _text; - emit cardInfoChanged(smartThis); - } - - bool getIsToken() const - { - return isToken; - } - const QStringList getProperties() const - { - return properties.keys(); - } - const QString getProperty(const QString &propertyName) const - { - return properties.value(propertyName).toString(); - } - void setProperty(const QString &_name, const QString &_value) - { - properties.insert(_name, _value); - emit cardInfoChanged(smartThis); - } - bool hasProperty(const QString &propertyName) const - { - return properties.contains(propertyName); - } - const CardInfoPerSetMap &getSets() const - { - return sets; - } - const QString &getSetsNames() const - { - return setsNames; - } - const QString getSetProperty(const QString &setName, const QString &propertyName) const - { - if (!sets.contains(setName)) - return ""; - - for (const auto &set : sets[setName]) { - if (QLatin1String("card_") + this->getName() + QString("_") + QString(set.getProperty("uuid")) == - this->getPixmapCacheKey()) { - return set.getProperty(propertyName); - } - } - - return sets[setName][0].getProperty(propertyName); - } - - // related cards - const QList &getRelatedCards() const - { - return relatedCards; - } - const QList &getReverseRelatedCards() const - { - return reverseRelatedCards; - } - const QList &getReverseRelatedCards2Me() const - { - return reverseRelatedCardsToMe; - } - const QList getAllRelatedCards() const - { - QList result; - result.append(getRelatedCards()); - result.append(getReverseRelatedCards2Me()); - return result; - } - void resetReverseRelatedCards2Me(); - void addReverseRelatedCards2Me(CardRelation *cardRelation) - { - reverseRelatedCardsToMe.append(cardRelation); - } - - // positioning - bool getCipt() const - { - return cipt; - } - bool getLandscapeOrientation() const - { - return landscapeOrientation; - } - int getTableRow() const - { - return tableRow; - } - void setTableRow(int _tableRow) - { - tableRow = _tableRow; - } - bool getUpsideDownArt() const - { - return upsideDownArt; - } - const QChar getColorChar() const; - - // Back-compatibility methods. Remove ASAP - const QString getCardType() const; - void setCardType(const QString &value); - const QString getCmc() const; - const QString getColors() const; - void setColors(const QString &value); - const QString getLoyalty() const; - const QString getMainCardType() const; - const QString getManaCost() const; - const QString getPowTough() const; - void setPowTough(const QString &value); - - // methods using per-set properties - QString getCustomPicURL(const QString &set) const - { - return getSetProperty(set, "picurl"); - } - QString getCorrectedName() const; - void addToSet(const CardSetPtr &_set, CardInfoPerSet _info = CardInfoPerSet()); - void combineLegalities(const QVariantHash &props); - void emitPixmapUpdated() - { - emit pixmapUpdated(); - } - void refreshCachedSetNames(); - - /** - * Simplify a name to have no punctuation and lowercase all letters, for - * less strict name-matching. - */ - static QString simplifyName(const QString &name); - -signals: - void pixmapUpdated(); - void cardInfoChanged(CardInfoPtr card); -}; - enum LoadStatus { Ok, @@ -523,79 +123,4 @@ signals: void cardRemoved(CardInfoPtr card); }; -class CardRelation : public QObject -{ - Q_OBJECT -public: - enum AttachType - { - DoesNotAttach = 0, - AttachTo = 1, - TransformInto = 2, - }; - -private: - QString name; - AttachType attachType; - bool isCreateAllExclusion; - bool isVariableCount; - int defaultCount; - bool isPersistent; - -public: - explicit CardRelation(const QString &_name = QString(), - AttachType _attachType = DoesNotAttach, - bool _isCreateAllExclusion = false, - bool _isVariableCount = false, - int _defaultCount = 1, - bool _isPersistent = false); - - inline const QString &getName() const - { - return name; - } - AttachType getAttachType() const - { - return attachType; - } - bool getDoesAttach() const - { - 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 !getDoesAttach(); - } - bool getIsCreateAllExclusion() const - { - return isCreateAllExclusion; - } - bool getIsVariable() const - { - return isVariableCount; - } - int getDefaultCount() const - { - return defaultCount; - } - bool getIsPersistent() const - { - return isPersistent; - } -}; #endif diff --git a/cockatrice/src/game/cards/card_info.cpp b/cockatrice/src/game/cards/card_info.cpp new file mode 100644 index 000000000..c4e00638b --- /dev/null +++ b/cockatrice/src/game/cards/card_info.cpp @@ -0,0 +1,418 @@ +#include "card_info.h" + +#include "../../client/ui/picture_loader/picture_loader.h" +#include "../../settings/cache_settings.h" +#include "../game_specific_terms.h" + +#include +#include +#include +#include +#include +#include + +CardSet::CardSet(const QString &_shortName, + const QString &_longName, + const QString &_setType, + const QDate &_releaseDate, + const CardSet::Priority _priority) + : shortName(_shortName), longName(_longName), releaseDate(_releaseDate), setType(_setType), priority(_priority) +{ + loadSetOptions(); +} + +CardSetPtr CardSet::newInstance(const QString &_shortName, + const QString &_longName, + const QString &_setType, + const QDate &_releaseDate, + const Priority _priority) +{ + CardSetPtr ptr(new CardSet(_shortName, _longName, _setType, _releaseDate, _priority)); + // ptr->setSmartPointer(ptr); + return ptr; +} + +QString CardSet::getCorrectedShortName() const +{ + // For Windows machines. + QSet invalidFileNames; + invalidFileNames << "CON" + << "PRN" + << "AUX" + << "NUL" + << "COM1" + << "COM2" + << "COM3" + << "COM4" + << "COM5" + << "COM6" + << "COM7" + << "COM8" + << "COM9" + << "LPT1" + << "LPT2" + << "LPT3" + << "LPT4" + << "LPT5" + << "LPT6" + << "LPT7" + << "LPT8" + << "LPT9"; + + return invalidFileNames.contains(shortName) ? shortName + "_" : shortName; +} + +void CardSet::loadSetOptions() +{ + sortKey = SettingsCache::instance().cardDatabase().getSortKey(shortName); + enabled = SettingsCache::instance().cardDatabase().isEnabled(shortName); + isknown = SettingsCache::instance().cardDatabase().isKnown(shortName); +} + +void CardSet::setSortKey(unsigned int _sortKey) +{ + sortKey = _sortKey; + SettingsCache::instance().cardDatabase().setSortKey(shortName, _sortKey); +} + +void CardSet::setEnabled(bool _enabled) +{ + enabled = _enabled; + SettingsCache::instance().cardDatabase().setEnabled(shortName, _enabled); +} + +void CardSet::setIsKnown(bool _isknown) +{ + isknown = _isknown; + SettingsCache::instance().cardDatabase().setIsKnown(shortName, _isknown); +} + +class SetList::KeyCompareFunctor +{ +public: + inline bool operator()(const CardSetPtr &a, const CardSetPtr &b) const + { + if (a.isNull() || b.isNull()) { + qCDebug(CardInfoLog) << "SetList::KeyCompareFunctor a or b is null"; + return false; + } + + return a->getSortKey() < b->getSortKey(); + } +}; + +void SetList::sortByKey() +{ + std::sort(begin(), end(), KeyCompareFunctor()); +} + +int SetList::getEnabledSetsNum() +{ + int num = 0; + for (int i = 0; i < size(); ++i) { + CardSetPtr set = at(i); + if (set && set->getEnabled()) { + ++num; + } + } + return num; +} + +int SetList::getUnknownSetsNum() +{ + int num = 0; + for (int i = 0; i < size(); ++i) { + CardSetPtr set = at(i); + if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { + ++num; + } + } + return num; +} + +QStringList SetList::getUnknownSetsNames() +{ + QStringList sets = QStringList(); + for (int i = 0; i < size(); ++i) { + CardSetPtr set = at(i); + if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { + sets << set->getShortName(); + } + } + return sets; +} + +void SetList::enableAllUnknown() +{ + for (int i = 0; i < size(); ++i) { + CardSetPtr set = at(i); + if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { + set->setIsKnown(true); + set->setEnabled(true); + } else if (set && set->getIsKnownIgnored() && !set->getEnabled()) { + set->setEnabled(true); + } + } +} + +void SetList::enableAll() +{ + for (int i = 0; i < size(); ++i) { + CardSetPtr set = at(i); + + if (set == nullptr) { + qCDebug(CardInfoLog) << "enabledAll has null"; + continue; + } + + if (!set->getIsKnownIgnored()) { + set->setIsKnown(true); + } + + set->setEnabled(true); + } +} + +void SetList::markAllAsKnown() +{ + for (int i = 0; i < size(); ++i) { + CardSetPtr set = at(i); + if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { + set->setIsKnown(true); + set->setEnabled(false); + } else if (set && set->getIsKnownIgnored() && !set->getEnabled()) { + set->setEnabled(true); + } + } +} + +void SetList::guessSortKeys() +{ + defaultSort(); + for (int i = 0; i < size(); ++i) { + CardSetPtr set = at(i); + if (set.isNull()) { + qCDebug(CardInfoLog) << "guessSortKeys set is null"; + continue; + } + set->setSortKey(i); + } +} + +void SetList::defaultSort() +{ + std::sort(begin(), end(), [](const CardSetPtr &a, const CardSetPtr &b) { + // Sort by priority, then by release date, then by short name + if (a->getPriority() != b->getPriority()) { + return a->getPriority() < b->getPriority(); // lowest first + } else if (a->getReleaseDate() != b->getReleaseDate()) { + return a->getReleaseDate() > b->getReleaseDate(); // most recent first + } else { + return a->getShortName() < b->getShortName(); // alphabetically + } + }); +} + +CardInfoPerSet::CardInfoPerSet(const CardSetPtr &_set) : set(_set) +{ +} + +CardInfo::CardInfo(const QString &_name, + const QString &_text, + bool _isToken, + QVariantHash _properties, + const QList &_relatedCards, + const QList &_reverseRelatedCards, + CardInfoPerSetMap _sets, + bool _cipt, + bool _landscapeOrientation, + int _tableRow, + bool _upsideDownArt) + : name(_name), text(_text), isToken(_isToken), properties(std::move(_properties)), relatedCards(_relatedCards), + reverseRelatedCards(_reverseRelatedCards), sets(std::move(_sets)), cipt(_cipt), + landscapeOrientation(_landscapeOrientation), tableRow(_tableRow), upsideDownArt(_upsideDownArt) +{ + pixmapCacheKey = QLatin1String("card_") + name; + simpleName = CardInfo::simplifyName(name); + + refreshCachedSetNames(); +} + +CardInfo::~CardInfo() +{ + PictureLoader::clearPixmapCache(smartThis); +} + +CardInfoPtr CardInfo::newInstance(const QString &_name) +{ + return newInstance(_name, QString(), false, QVariantHash(), QList(), QList(), + CardInfoPerSetMap(), false, false, 0, false); +} + +CardInfoPtr CardInfo::newInstance(const QString &_name, + const QString &_text, + bool _isToken, + QVariantHash _properties, + const QList &_relatedCards, + const QList &_reverseRelatedCards, + CardInfoPerSetMap _sets, + bool _cipt, + bool _landscapeOrientation, + int _tableRow, + bool _upsideDownArt) +{ + CardInfoPtr ptr(new CardInfo(_name, _text, _isToken, std::move(_properties), _relatedCards, _reverseRelatedCards, + _sets, _cipt, _landscapeOrientation, _tableRow, _upsideDownArt)); + ptr->setSmartPointer(ptr); + + for (const auto &cardInfoPerSetList : _sets) { + for (const CardInfoPerSet &set : cardInfoPerSetList) { + set.getPtr()->append(ptr); + break; + } + } + + return ptr; +} + +QString CardInfo::getCorrectedName() const +{ + // remove all the characters reserved in windows file paths, + // other oses only disallow a subset of these so it covers all + static const QRegularExpression rmrx(R"(( // |[*<>:"\\?\x00-\x08\x10-\x1f]))"); + static const QRegularExpression spacerx(R"([/\x09-\x0f])"); + static const QString space(' '); + QString result = name; + // Fire // Ice, Circle of Protection: Red, "Ach! Hans, Run!", Who/What/When/Where/Why, Question Elemental? + return result.remove(rmrx).replace(spacerx, space); +} + +void CardInfo::addToSet(const CardSetPtr &_set, const CardInfoPerSet _info) +{ + _set->append(smartThis); + sets[_set->getShortName()].append(_info); + + refreshCachedSetNames(); +} + +void CardInfo::combineLegalities(const QVariantHash &props) +{ + QHashIterator it(props); + while (it.hasNext()) { + it.next(); + if (it.key().startsWith("format-")) { + smartThis->setProperty(it.key(), it.value().toString()); + } + } +} + +void CardInfo::refreshCachedSetNames() +{ + QStringList setList; + // update the cached list of set names + for (const auto &cardInfoPerSetList : sets) { + for (const auto &set : cardInfoPerSetList) { + if (set.getPtr()->getEnabled()) { + setList << set.getPtr()->getShortName(); + } + break; + } + } + setsNames = setList.join(", "); +} + +QString CardInfo::simplifyName(const QString &name) +{ + static const QRegularExpression spaceOrSplit("(\\s+|\\/\\/.*)"); + static const QRegularExpression nonAlnum("[^a-z0-9]"); + + QString simpleName = name.toLower(); + + // remove spaces and right halves of split cards + simpleName.remove(spaceOrSplit); + + // So Aetherling would work, but not Ætherling since 'Æ' would get replaced + // with nothing. + simpleName.replace("æ", "ae"); + + // Replace Jötun Grunt with Jotun Grunt. + simpleName = simpleName.normalized(QString::NormalizationForm_KD); + + // remove all non alphanumeric characters from the name + simpleName.remove(nonAlnum); + return simpleName; +} + +const QChar CardInfo::getColorChar() const +{ + QString colors = getColors(); + switch (colors.size()) { + case 0: + return QChar(); + case 1: + return colors.at(0); + default: + return QChar('m'); + } +} + +CardRelation::CardRelation(const QString &_name, + AttachType _attachType, + bool _isCreateAllExclusion, + bool _isVariableCount, + int _defaultCount, + bool _isPersistent) + : name(_name), attachType(_attachType), isCreateAllExclusion(_isCreateAllExclusion), + isVariableCount(_isVariableCount), defaultCount(_defaultCount), isPersistent(_isPersistent) +{ +} + +void CardInfo::resetReverseRelatedCards2Me() +{ + for (CardRelation *cardRelation : this->getReverseRelatedCards2Me()) { + cardRelation->deleteLater(); + } + reverseRelatedCardsToMe = QList(); +} + +// Back-compatibility methods. Remove ASAP +const QString CardInfo::getCardType() const +{ + return getProperty(Mtg::CardType); +} +void CardInfo::setCardType(const QString &value) +{ + setProperty(Mtg::CardType, value); +} +const QString CardInfo::getCmc() const +{ + return getProperty(Mtg::ConvertedManaCost); +} +const QString CardInfo::getColors() const +{ + return getProperty(Mtg::Colors); +} +void CardInfo::setColors(const QString &value) +{ + setProperty(Mtg::Colors, value); +} +const QString CardInfo::getLoyalty() const +{ + return getProperty(Mtg::Loyalty); +} +const QString CardInfo::getMainCardType() const +{ + return getProperty(Mtg::MainCardType); +} +const QString CardInfo::getManaCost() const +{ + return getProperty(Mtg::ManaCost); +} +const QString CardInfo::getPowTough() const +{ + return getProperty(Mtg::PowTough); +} +void CardInfo::setPowTough(const QString &value) +{ + setProperty(Mtg::PowTough, value); +} diff --git a/cockatrice/src/game/cards/card_info.h b/cockatrice/src/game/cards/card_info.h new file mode 100644 index 000000000..9d06bc5f6 --- /dev/null +++ b/cockatrice/src/game/cards/card_info.h @@ -0,0 +1,491 @@ +#ifndef CARD_INFO_H +#define CARD_INFO_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +inline Q_LOGGING_CATEGORY(CardInfoLog, "card_info"); + +class CardInfo; +class CardInfoPerSet; +class CardSet; +class CardRelation; +class ICardDatabaseParser; + +typedef QMap QStringMap; +typedef QSharedPointer CardInfoPtr; +typedef QSharedPointer CardSetPtr; +typedef QMap> CardInfoPerSetMap; + +Q_DECLARE_METATYPE(CardInfoPtr) + +class CardSet : public QList +{ +public: + enum Priority + { + PriorityFallback = 0, + PriorityPrimary = 10, + PrioritySecondary = 20, + PriorityReprint = 30, + PriorityOther = 40, + PriorityLowest = 100, + }; + +private: + QString shortName, longName; + unsigned int sortKey; + QDate releaseDate; + QString setType; + Priority priority; + bool enabled, isknown; + +public: + explicit CardSet(const QString &_shortName = QString(), + const QString &_longName = QString(), + const QString &_setType = QString(), + const QDate &_releaseDate = QDate(), + const Priority _priority = PriorityFallback); + static CardSetPtr newInstance(const QString &_shortName = QString(), + const QString &_longName = QString(), + const QString &_setType = QString(), + const QDate &_releaseDate = QDate(), + const Priority _priority = PriorityFallback); + QString getCorrectedShortName() const; + QString getShortName() const + { + return shortName; + } + QString getLongName() const + { + return longName; + } + QString getSetType() const + { + return setType; + } + QDate getReleaseDate() const + { + return releaseDate; + } + Priority getPriority() const + { + return priority; + } + void setLongName(const QString &_longName) + { + longName = _longName; + } + void setSetType(const QString &_setType) + { + setType = _setType; + } + void setReleaseDate(const QDate &_releaseDate) + { + releaseDate = _releaseDate; + } + void setPriority(const Priority _priority) + { + priority = _priority; + } + + void loadSetOptions(); + int getSortKey() const + { + return sortKey; + } + void setSortKey(unsigned int _sortKey); + bool getEnabled() const + { + return enabled; + } + void setEnabled(bool _enabled); + bool getIsKnown() const + { + return isknown; + } + void setIsKnown(bool _isknown); + + // Determine incomplete sets. + bool getIsKnownIgnored() const + { + return longName.length() + setType.length() + releaseDate.toString().length() == 0; + } +}; + +class SetList : public QList +{ +private: + class KeyCompareFunctor; + +public: + void sortByKey(); + void guessSortKeys(); + void enableAllUnknown(); + void enableAll(); + void markAllAsKnown(); + int getEnabledSetsNum(); + int getUnknownSetsNum(); + QStringList getUnknownSetsNames(); + void defaultSort(); +}; + +class CardInfoPerSet +{ +public: + explicit CardInfoPerSet(const CardSetPtr &_set = QSharedPointer(nullptr)); + ~CardInfoPerSet() = default; + + bool operator==(const CardInfoPerSet &other) const + { + return this->set == other.set && this->properties == other.properties; + } + +private: + CardSetPtr set; + // per-set card properties; + QVariantHash properties; + +public: + const CardSetPtr getPtr() const + { + return set; + } + const QStringList getProperties() const + { + return properties.keys(); + } + const QString getProperty(const QString &propertyName) const + { + return properties.value(propertyName).toString(); + } + void setProperty(const QString &_name, const QString &_value) + { + properties.insert(_name, _value); + } +}; + +class CardInfo : public QObject +{ + Q_OBJECT +private: + CardInfoPtr smartThis; + // The card name + QString name; + // The name without punctuation or capitalization, for better card name recognition. + QString simpleName; + // The key used to identify this card in the cache + QString pixmapCacheKey; + // card text + QString text; + // whether this is not a "real" card but a token + bool isToken; + // basic card properties; common for all the sets + QVariantHash properties; + // the cards i'm related to + QList relatedCards; + // the card i'm reverse-related to + QList reverseRelatedCards; + // the cards thare are reverse-related to me + QList reverseRelatedCardsToMe; + // card sets + CardInfoPerSetMap sets; + // cached set names + QString setsNames; + // positioning properties; used by UI + bool cipt; + bool landscapeOrientation; + int tableRow; + bool upsideDownArt; + +public: + explicit CardInfo(const QString &_name, + const QString &_text, + bool _isToken, + QVariantHash _properties, + const QList &_relatedCards, + const QList &_reverseRelatedCards, + CardInfoPerSetMap _sets, + bool _cipt, + bool _landscapeOrientation, + int _tableRow, + bool _upsideDownArt); + CardInfo(const CardInfo &other) + : QObject(other.parent()), name(other.name), simpleName(other.simpleName), pixmapCacheKey(other.pixmapCacheKey), + text(other.text), isToken(other.isToken), properties(other.properties), relatedCards(other.relatedCards), + reverseRelatedCards(other.reverseRelatedCards), reverseRelatedCardsToMe(other.reverseRelatedCardsToMe), + sets(other.sets), setsNames(other.setsNames), cipt(other.cipt), + landscapeOrientation(other.landscapeOrientation), tableRow(other.tableRow), upsideDownArt(other.upsideDownArt) + { + } + ~CardInfo() override; + + static CardInfoPtr newInstance(const QString &_name); + + static CardInfoPtr newInstance(const QString &_name, + const QString &_text, + bool _isToken, + QVariantHash _properties, + const QList &_relatedCards, + const QList &_reverseRelatedCards, + CardInfoPerSetMap _sets, + bool _cipt, + bool _landscapeOrientation, + int _tableRow, + bool _upsideDownArt); + + CardInfoPtr clone() const + { + // Use the copy constructor to create a new instance + CardInfoPtr newCardInfo = CardInfoPtr(new CardInfo(*this)); + newCardInfo->setSmartPointer(newCardInfo); // Set the smart pointer for the new instance + return newCardInfo; + } + + void setSmartPointer(CardInfoPtr _ptr) + { + smartThis = std::move(_ptr); + } + + // basic properties + inline const QString &getName() const + { + return name; + } + const QString &getSimpleName() const + { + return simpleName; + } + void setPixmapCacheKey(QString _pixmapCacheKey) + { + pixmapCacheKey = _pixmapCacheKey; + } + const QString &getPixmapCacheKey() const + { + return pixmapCacheKey; + } + + const QString &getText() const + { + return text; + } + void setText(const QString &_text) + { + text = _text; + emit cardInfoChanged(smartThis); + } + + bool getIsToken() const + { + return isToken; + } + const QStringList getProperties() const + { + return properties.keys(); + } + const QString getProperty(const QString &propertyName) const + { + return properties.value(propertyName).toString(); + } + void setProperty(const QString &_name, const QString &_value) + { + properties.insert(_name, _value); + emit cardInfoChanged(smartThis); + } + bool hasProperty(const QString &propertyName) const + { + return properties.contains(propertyName); + } + const CardInfoPerSetMap &getSets() const + { + return sets; + } + const QString &getSetsNames() const + { + return setsNames; + } + const QString getSetProperty(const QString &setName, const QString &propertyName) const + { + if (!sets.contains(setName)) + return ""; + + for (const auto &set : sets[setName]) { + if (QLatin1String("card_") + this->getName() + QString("_") + QString(set.getProperty("uuid")) == + this->getPixmapCacheKey()) { + return set.getProperty(propertyName); + } + } + + return sets[setName][0].getProperty(propertyName); + } + + // related cards + const QList &getRelatedCards() const + { + return relatedCards; + } + const QList &getReverseRelatedCards() const + { + return reverseRelatedCards; + } + const QList &getReverseRelatedCards2Me() const + { + return reverseRelatedCardsToMe; + } + const QList getAllRelatedCards() const + { + QList result; + result.append(getRelatedCards()); + result.append(getReverseRelatedCards2Me()); + return result; + } + void resetReverseRelatedCards2Me(); + void addReverseRelatedCards2Me(CardRelation *cardRelation) + { + reverseRelatedCardsToMe.append(cardRelation); + } + + // positioning + bool getCipt() const + { + return cipt; + } + bool getLandscapeOrientation() const + { + return landscapeOrientation; + } + int getTableRow() const + { + return tableRow; + } + void setTableRow(int _tableRow) + { + tableRow = _tableRow; + } + bool getUpsideDownArt() const + { + return upsideDownArt; + } + const QChar getColorChar() const; + + // Back-compatibility methods. Remove ASAP + const QString getCardType() const; + void setCardType(const QString &value); + const QString getCmc() const; + const QString getColors() const; + void setColors(const QString &value); + const QString getLoyalty() const; + const QString getMainCardType() const; + const QString getManaCost() const; + const QString getPowTough() const; + void setPowTough(const QString &value); + + // methods using per-set properties + QString getCustomPicURL(const QString &set) const + { + return getSetProperty(set, "picurl"); + } + QString getCorrectedName() const; + void addToSet(const CardSetPtr &_set, CardInfoPerSet _info = CardInfoPerSet()); + void combineLegalities(const QVariantHash &props); + void emitPixmapUpdated() + { + emit pixmapUpdated(); + } + void refreshCachedSetNames(); + + /** + * Simplify a name to have no punctuation and lowercase all letters, for + * less strict name-matching. + */ + static QString simplifyName(const QString &name); + +signals: + void pixmapUpdated(); + void cardInfoChanged(CardInfoPtr card); +}; + +class CardRelation : public QObject +{ + Q_OBJECT +public: + enum AttachType + { + DoesNotAttach = 0, + AttachTo = 1, + TransformInto = 2, + }; + +private: + QString name; + AttachType attachType; + bool isCreateAllExclusion; + bool isVariableCount; + int defaultCount; + bool isPersistent; + +public: + explicit CardRelation(const QString &_name = QString(), + AttachType _attachType = DoesNotAttach, + bool _isCreateAllExclusion = false, + bool _isVariableCount = false, + int _defaultCount = 1, + bool _isPersistent = false); + + inline const QString &getName() const + { + return name; + } + AttachType getAttachType() const + { + return attachType; + } + bool getDoesAttach() const + { + 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 !getDoesAttach(); + } + bool getIsCreateAllExclusion() const + { + return isCreateAllExclusion; + } + bool getIsVariable() const + { + return isVariableCount; + } + int getDefaultCount() const + { + return defaultCount; + } + bool getIsPersistent() const + { + return isPersistent; + } +}; +#endif diff --git a/dbconverter/CMakeLists.txt b/dbconverter/CMakeLists.txt index 78fe36350..ca661376e 100644 --- a/dbconverter/CMakeLists.txt +++ b/dbconverter/CMakeLists.txt @@ -9,6 +9,7 @@ set(dbconverter_SOURCES ../cockatrice/src/game/cards/card_database_parser/card_database_parser.cpp ../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_3.cpp ../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_4.cpp + ../cockatrice/src/game/cards/card_info.cpp ../cockatrice/src/settings/settings_manager.cpp ${VERSION_STRING_CPP} ) diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index addfecd78..6f8a7043a 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -18,6 +18,7 @@ set(oracle_SOURCES src/qt-json/json.cpp ../cockatrice/src/game/cards/card_database.cpp ../cockatrice/src/game/cards/card_database_manager.cpp + ../cockatrice/src/game/cards/card_info.cpp ../cockatrice/src/client/ui/picture_loader/picture_loader.cpp ../cockatrice/src/client/ui/picture_loader/picture_loader_worker.cpp ../cockatrice/src/client/ui/picture_loader/picture_to_load.cpp diff --git a/tests/carddatabase/CMakeLists.txt b/tests/carddatabase/CMakeLists.txt index 3ca812c22..403d14d1d 100644 --- a/tests/carddatabase/CMakeLists.txt +++ b/tests/carddatabase/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable( ../../cockatrice/src/game/cards/card_database_parser/card_database_parser.cpp ../../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_3.cpp ../../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_4.cpp + ../../cockatrice/src/game/cards/card_info.cpp ../../cockatrice/src/settings/settings_manager.cpp carddatabase_test.cpp mocks.cpp @@ -35,6 +36,7 @@ add_executable( ../../cockatrice/src/game/cards/card_database_parser/card_database_parser.cpp ../../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_3.cpp ../../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_4.cpp + ../../cockatrice/src/game/cards/card_info.cpp ../../cockatrice/src/game/filters/filter_card.cpp ../../cockatrice/src/game/filters/filter_string.cpp ../../cockatrice/src/game/filters/filter_tree.cpp