mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-18 08:22:15 -07:00
restore original
This commit is contained in:
parent
2c8ffdd5dc
commit
1e8d935880
2 changed files with 1548 additions and 0 deletions
947
cockatrice/src/game/cards/card_database.cpp
Normal file
947
cockatrice/src/game/cards/card_database.cpp
Normal file
|
|
@ -0,0 +1,947 @@
|
||||||
|
#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"
|
||||||
|
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
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<QString> 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<CardRelation *> &_relatedCards,
|
||||||
|
const QList<CardRelation *> &_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<CardRelation *>(), QList<CardRelation *>(),
|
||||||
|
CardInfoPerSetMap(), false, false, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPtr CardInfo::newInstance(const QString &_name,
|
||||||
|
const QString &_text,
|
||||||
|
bool _isToken,
|
||||||
|
QVariantHash _properties,
|
||||||
|
const QList<CardRelation *> &_relatedCards,
|
||||||
|
const QList<CardRelation *> &_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<QString, QVariant> 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>("CardInfoPtr");
|
||||||
|
qRegisterMetaType<CardInfoPtr>("CardSetPtr");
|
||||||
|
|
||||||
|
// add new parsers here
|
||||||
|
availableParsers << new CockatriceXml4Parser;
|
||||||
|
availableParsers << new CockatriceXml3Parser;
|
||||||
|
|
||||||
|
for (auto &parser : availableParsers) {
|
||||||
|
connect(parser, &ICardDatabaseParser::addCard, this, &CardDatabase::addCard, Qt::DirectConnection);
|
||||||
|
connect(parser, &ICardDatabaseParser::addSet, this, &CardDatabase::addSet, Qt::DirectConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(&SettingsCache::instance(), &SettingsCache::cardDatabasePathChanged, this,
|
||||||
|
&CardDatabase::loadCardDatabases);
|
||||||
|
}
|
||||||
|
|
||||||
|
CardDatabase::~CardDatabase()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
qDeleteAll(availableParsers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardDatabase::clear()
|
||||||
|
{
|
||||||
|
clearDatabaseMutex->lock();
|
||||||
|
|
||||||
|
QHashIterator<QString, CardInfoPtr> i(cards);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
if (i.value()) {
|
||||||
|
removeCard(i.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cards.clear();
|
||||||
|
simpleNameCards.clear();
|
||||||
|
|
||||||
|
sets.clear();
|
||||||
|
ICardDatabaseParser::clearSetlist();
|
||||||
|
|
||||||
|
loadStatus = NotLoaded;
|
||||||
|
|
||||||
|
clearDatabaseMutex->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardDatabase::addCard(CardInfoPtr card)
|
||||||
|
{
|
||||||
|
if (card == nullptr) {
|
||||||
|
qCDebug(CardDatabaseLog) << "CardDatabase::addCard(nullptr)";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if card already exists just add the new set property
|
||||||
|
if (cards.contains(card->getName())) {
|
||||||
|
CardInfoPtr sameCard = cards[card->getName()];
|
||||||
|
for (const auto &cardInfoPerSetList : card->getSets()) {
|
||||||
|
for (const CardInfoPerSet &set : cardInfoPerSetList) {
|
||||||
|
sameCard->addToSet(set.getPtr(), set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addCardMutex->lock();
|
||||||
|
cards.insert(card->getName(), card);
|
||||||
|
simpleNameCards.insert(card->getSimpleName(), card);
|
||||||
|
addCardMutex->unlock();
|
||||||
|
emit cardAdded(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardDatabase::removeCard(CardInfoPtr card)
|
||||||
|
{
|
||||||
|
if (card.isNull()) {
|
||||||
|
qCDebug(CardDatabaseLog) << "CardDatabase::removeCard(nullptr)";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto *cardRelation : card->getRelatedCards())
|
||||||
|
cardRelation->deleteLater();
|
||||||
|
|
||||||
|
for (auto *cardRelation : card->getReverseRelatedCards())
|
||||||
|
cardRelation->deleteLater();
|
||||||
|
|
||||||
|
for (auto *cardRelation : card->getReverseRelatedCards2Me())
|
||||||
|
cardRelation->deleteLater();
|
||||||
|
|
||||||
|
removeCardMutex->lock();
|
||||||
|
cards.remove(card->getName());
|
||||||
|
simpleNameCards.remove(card->getSimpleName());
|
||||||
|
removeCardMutex->unlock();
|
||||||
|
emit cardRemoved(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPtr CardDatabase::getCard(const QString &cardName) const
|
||||||
|
{
|
||||||
|
return getCardFromMap(cards, cardName);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<CardInfoPtr> CardDatabase::getCards(const QStringList &cardNames) const
|
||||||
|
{
|
||||||
|
QList<CardInfoPtr> cardInfos;
|
||||||
|
for (const QString &cardName : cardNames) {
|
||||||
|
CardInfoPtr ptr = getCardFromMap(cards, cardName);
|
||||||
|
if (ptr)
|
||||||
|
cardInfos.append(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<CardInfoPtr> CardDatabase::getCardsByNameAndProviderId(const QMap<QString, QString> &cardNames) const
|
||||||
|
{
|
||||||
|
QList<CardInfoPtr> cardInfos;
|
||||||
|
for (const QString &cardName : cardNames) {
|
||||||
|
CardInfoPtr ptr = getCardByNameAndProviderId(cardName, cardNames[cardName]);
|
||||||
|
if (ptr)
|
||||||
|
cardInfos.append(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPtr CardDatabase::getCardByNameAndProviderId(const QString &cardName, const QString &providerId) const
|
||||||
|
{
|
||||||
|
auto info = getCard(cardName);
|
||||||
|
if (providerId.isNull() || providerId.isEmpty() || info.isNull()) {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &cardInfoPerSetList : info->getSets()) {
|
||||||
|
for (const auto &set : cardInfoPerSetList) {
|
||||||
|
if (set.getProperty("uuid") == providerId) {
|
||||||
|
CardInfoPtr cardFromSpecificSet = info->clone();
|
||||||
|
cardFromSpecificSet->setPixmapCacheKey(QLatin1String("card_") + QString(info->getName()) +
|
||||||
|
QString("_") + QString(set.getProperty("uuid")));
|
||||||
|
return cardFromSpecificSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPtr CardDatabase::getCardBySimpleName(const QString &cardName) const
|
||||||
|
{
|
||||||
|
return getCardFromMap(simpleNameCards, CardInfo::simplifyName(cardName));
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPtr CardDatabase::guessCard(const QString &cardName, const QString &providerId) const
|
||||||
|
{
|
||||||
|
CardInfoPtr temp = providerId.isEmpty() ? getCard(cardName) : getCardByNameAndProviderId(cardName, providerId);
|
||||||
|
|
||||||
|
if (temp == nullptr) { // get card by simple name instead
|
||||||
|
temp = getCardBySimpleName(cardName);
|
||||||
|
if (temp == nullptr) { // still could not find the card, so simplify the cardName too
|
||||||
|
const auto &simpleCardName = CardInfo::simplifyName(cardName);
|
||||||
|
temp = getCardBySimpleName(simpleCardName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return temp; // returns nullptr if not found
|
||||||
|
}
|
||||||
|
|
||||||
|
CardSetPtr CardDatabase::getSet(const QString &setName)
|
||||||
|
{
|
||||||
|
if (sets.contains(setName)) {
|
||||||
|
return sets.value(setName);
|
||||||
|
} else {
|
||||||
|
CardSetPtr newSet = CardSet::newInstance(setName);
|
||||||
|
sets.insert(setName, newSet);
|
||||||
|
return newSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardDatabase::addSet(CardSetPtr set)
|
||||||
|
{
|
||||||
|
sets.insert(set->getShortName(), set);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetList CardDatabase::getSetList() const
|
||||||
|
{
|
||||||
|
SetList result;
|
||||||
|
QHashIterator<QString, CardSetPtr> i(sets);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
result << i.value();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPtr CardDatabase::getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const
|
||||||
|
{
|
||||||
|
if (cardMap.contains(cardName))
|
||||||
|
return cardMap.value(cardName);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadStatus CardDatabase::loadFromFile(const QString &fileName)
|
||||||
|
{
|
||||||
|
QFile file(fileName);
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
if (!file.isOpen()) {
|
||||||
|
return FileError;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto parser : availableParsers) {
|
||||||
|
file.reset();
|
||||||
|
if (parser->getCanParseFile(fileName, file)) {
|
||||||
|
file.reset();
|
||||||
|
parser->parseFile(file);
|
||||||
|
return Ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadStatus CardDatabase::loadCardDatabase(const QString &path)
|
||||||
|
{
|
||||||
|
auto startTime = QTime::currentTime();
|
||||||
|
LoadStatus tempLoadStatus = NotLoaded;
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
loadFromFileMutex->lock();
|
||||||
|
tempLoadStatus = loadFromFile(path);
|
||||||
|
loadFromFileMutex->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
int msecs = startTime.msecsTo(QTime::currentTime());
|
||||||
|
qCDebug(CardDatabaseLoadingLog) << "Path =" << path << "Status =" << tempLoadStatus << "Cards =" << cards.size()
|
||||||
|
<< "Sets =" << sets.size() << QString("%1ms").arg(msecs);
|
||||||
|
|
||||||
|
return tempLoadStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadStatus CardDatabase::loadCardDatabases()
|
||||||
|
{
|
||||||
|
reloadDatabaseMutex->lock();
|
||||||
|
|
||||||
|
qCDebug(CardDatabaseLoadingLog) << "Started";
|
||||||
|
|
||||||
|
clear(); // remove old db
|
||||||
|
|
||||||
|
loadStatus = loadCardDatabase(SettingsCache::instance().getCardDatabasePath()); // load main card database
|
||||||
|
loadCardDatabase(SettingsCache::instance().getTokenDatabasePath()); // load tokens database
|
||||||
|
loadCardDatabase(SettingsCache::instance().getSpoilerCardDatabasePath()); // load spoilers database
|
||||||
|
|
||||||
|
// find all custom card databases, recursively & following symlinks
|
||||||
|
// then load them alphabetically
|
||||||
|
QDirIterator customDatabaseIterator(SettingsCache::instance().getCustomCardDatabasePath(), QStringList() << "*.xml",
|
||||||
|
QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
||||||
|
QStringList databasePaths;
|
||||||
|
while (customDatabaseIterator.hasNext()) {
|
||||||
|
customDatabaseIterator.next();
|
||||||
|
databasePaths.push_back(customDatabaseIterator.filePath());
|
||||||
|
}
|
||||||
|
databasePaths.sort();
|
||||||
|
|
||||||
|
for (auto i = 0; i < databasePaths.size(); ++i) {
|
||||||
|
const auto &databasePath = databasePaths.at(i);
|
||||||
|
qCDebug(CardDatabaseLoadingLog) << "Loading Custom Set" << i << "(" << databasePath << ")";
|
||||||
|
loadCardDatabase(databasePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AFTER all the cards have been loaded
|
||||||
|
|
||||||
|
// Refresh the pixmap cache keys for all cards by setting them to the UUID of the preferred printing
|
||||||
|
refreshPreferredPrintings();
|
||||||
|
// resolve the reverse-related tags
|
||||||
|
refreshCachedReverseRelatedCards();
|
||||||
|
|
||||||
|
if (loadStatus == Ok) {
|
||||||
|
checkUnknownSets(); // update deck editors, etc
|
||||||
|
qCDebug(CardDatabaseLoadingSuccessOrFailureLog) << "Success";
|
||||||
|
emit cardDatabaseLoadingFinished();
|
||||||
|
} else {
|
||||||
|
qCDebug(CardDatabaseLoadingSuccessOrFailureLog) << "Failed";
|
||||||
|
emit cardDatabaseLoadingFailed(); // bring up the settings dialog
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadDatabaseMutex->unlock();
|
||||||
|
return loadStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardDatabase::refreshPreferredPrintings()
|
||||||
|
{
|
||||||
|
for (const CardInfoPtr &card : cards) {
|
||||||
|
card->setPixmapCacheKey(QLatin1String("card_") + QString(card->getName()) + QString("_") +
|
||||||
|
QString(getPreferredPrintingProviderIdForCard(card->getName())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPerSet CardDatabase::getPreferredSetForCard(const QString &cardName) const
|
||||||
|
{
|
||||||
|
CardInfoPtr cardInfo = getCard(cardName);
|
||||||
|
if (!cardInfo) {
|
||||||
|
return CardInfoPerSet(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPerSetMap setMap = cardInfo->getSets();
|
||||||
|
if (setMap.empty()) {
|
||||||
|
return CardInfoPerSet(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
CardSetPtr preferredSet = nullptr;
|
||||||
|
CardInfoPerSet preferredCard;
|
||||||
|
SetPriorityComparator comparator;
|
||||||
|
|
||||||
|
for (const auto &cardInfoPerSetList : setMap) {
|
||||||
|
for (auto &cardInfoForSet : cardInfoPerSetList) {
|
||||||
|
CardSetPtr currentSet = cardInfoForSet.getPtr();
|
||||||
|
if (!preferredSet || comparator(currentSet, preferredSet)) {
|
||||||
|
preferredSet = currentSet;
|
||||||
|
preferredCard = cardInfoForSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preferredSet) {
|
||||||
|
return preferredCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CardInfoPerSet(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPerSet CardDatabase::getSpecificSetForCard(const QString &cardName, const QString &providerId) const
|
||||||
|
{
|
||||||
|
CardInfoPtr cardInfo = getCard(cardName);
|
||||||
|
if (!cardInfo) {
|
||||||
|
return CardInfoPerSet(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPerSetMap setMap = cardInfo->getSets();
|
||||||
|
if (setMap.empty()) {
|
||||||
|
return CardInfoPerSet(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &cardInfoPerSetList : setMap) {
|
||||||
|
for (auto &cardInfoForSet : cardInfoPerSetList) {
|
||||||
|
if (cardInfoForSet.getProperty("uuid") == providerId) {
|
||||||
|
return cardInfoForSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (providerId.isNull()) {
|
||||||
|
return getPreferredSetForCard(cardName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CardInfoPerSet(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPerSet CardDatabase::getSpecificSetForCard(const QString &cardName,
|
||||||
|
const QString &setShortName,
|
||||||
|
const QString &collectorNumber) const
|
||||||
|
{
|
||||||
|
CardInfoPtr cardInfo = getCard(cardName);
|
||||||
|
if (!cardInfo) {
|
||||||
|
return CardInfoPerSet(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPerSetMap setMap = cardInfo->getSets();
|
||||||
|
if (setMap.empty()) {
|
||||||
|
return CardInfoPerSet(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &cardInfoPerSetList : setMap) {
|
||||||
|
for (auto &cardInfoForSet : cardInfoPerSetList) {
|
||||||
|
if (cardInfoForSet.getPtr()->getShortName() == setShortName &&
|
||||||
|
cardInfoForSet.getProperty("num") == collectorNumber) {
|
||||||
|
return cardInfoForSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CardInfoPerSet(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CardDatabase::getPreferredPrintingProviderIdForCard(const QString &cardName)
|
||||||
|
{
|
||||||
|
CardInfoPerSet preferredSetCardInfo = getPreferredSetForCard(cardName);
|
||||||
|
QString preferredPrintingProviderId = preferredSetCardInfo.getProperty(QString("uuid"));
|
||||||
|
if (preferredPrintingProviderId.isEmpty()) {
|
||||||
|
CardInfoPtr defaultCardInfo = getCard(cardName);
|
||||||
|
if (defaultCardInfo.isNull()) {
|
||||||
|
return cardName;
|
||||||
|
}
|
||||||
|
return defaultCardInfo->getName();
|
||||||
|
}
|
||||||
|
return preferredPrintingProviderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CardDatabase::isProviderIdForPreferredPrinting(const QString &cardName, const QString &providerId)
|
||||||
|
{
|
||||||
|
if (providerId.startsWith("card_")) {
|
||||||
|
return providerId ==
|
||||||
|
QLatin1String("card_") + cardName + QString("_") + getPreferredPrintingProviderIdForCard(cardName);
|
||||||
|
}
|
||||||
|
return providerId == getPreferredPrintingProviderIdForCard(cardName);
|
||||||
|
}
|
||||||
|
|
||||||
|
CardInfoPerSet CardDatabase::getSetInfoForCard(const CardInfoPtr &_card)
|
||||||
|
{
|
||||||
|
const CardInfoPerSetMap &setMap = _card->getSets();
|
||||||
|
if (setMap.empty()) {
|
||||||
|
return CardInfoPerSet(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &cardInfoPerSetList : setMap) {
|
||||||
|
for (const auto &cardInfoForSet : cardInfoPerSetList) {
|
||||||
|
if (QLatin1String("card_") + _card->getName() + QString("_") + cardInfoForSet.getProperty("uuid") ==
|
||||||
|
_card->getPixmapCacheKey()) {
|
||||||
|
return cardInfoForSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CardInfoPerSet(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardDatabase::refreshCachedReverseRelatedCards()
|
||||||
|
{
|
||||||
|
for (const CardInfoPtr &card : cards)
|
||||||
|
card->resetReverseRelatedCards2Me();
|
||||||
|
|
||||||
|
for (const CardInfoPtr &card : cards) {
|
||||||
|
if (card->getReverseRelatedCards().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CardRelation *cardRelation : card->getReverseRelatedCards()) {
|
||||||
|
const QString &targetCard = cardRelation->getName();
|
||||||
|
if (!cards.contains(targetCard)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *newCardRelation = new CardRelation(
|
||||||
|
card->getName(), cardRelation->getAttachType(), cardRelation->getIsCreateAllExclusion(),
|
||||||
|
cardRelation->getIsVariable(), cardRelation->getDefaultCount(), cardRelation->getIsPersistent());
|
||||||
|
cards.value(targetCard)->addReverseRelatedCards2Me(newCardRelation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList CardDatabase::getAllMainCardTypes() const
|
||||||
|
{
|
||||||
|
QSet<QString> types;
|
||||||
|
QHashIterator<QString, CardInfoPtr> cardIterator(cards);
|
||||||
|
while (cardIterator.hasNext()) {
|
||||||
|
types.insert(cardIterator.next().value()->getMainCardType());
|
||||||
|
}
|
||||||
|
return types.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardDatabase::checkUnknownSets()
|
||||||
|
{
|
||||||
|
auto _sets = getSetList();
|
||||||
|
|
||||||
|
if (_sets.getEnabledSetsNum()) {
|
||||||
|
// if some sets are first found on this run, ask the user
|
||||||
|
int numUnknownSets = _sets.getUnknownSetsNum();
|
||||||
|
QStringList unknownSetNames = _sets.getUnknownSetsNames();
|
||||||
|
if (numUnknownSets > 0) {
|
||||||
|
emit cardDatabaseNewSetsFound(numUnknownSets, unknownSetNames);
|
||||||
|
} else {
|
||||||
|
_sets.markAllAsKnown();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No set enabled. Probably this is the first time running trice
|
||||||
|
_sets.guessSortKeys();
|
||||||
|
_sets.sortByKey();
|
||||||
|
_sets.enableAll();
|
||||||
|
notifyEnabledSetsChanged();
|
||||||
|
|
||||||
|
emit cardDatabaseAllNewSetsEnabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardDatabase::enableAllUnknownSets()
|
||||||
|
{
|
||||||
|
auto _sets = getSetList();
|
||||||
|
_sets.enableAllUnknown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardDatabase::markAllSetsAsKnown()
|
||||||
|
{
|
||||||
|
auto _sets = getSetList();
|
||||||
|
_sets.markAllAsKnown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CardDatabase::notifyEnabledSetsChanged()
|
||||||
|
{
|
||||||
|
// refresh the list of cached set names
|
||||||
|
for (const CardInfoPtr &card : cards)
|
||||||
|
card->refreshCachedSetNames();
|
||||||
|
|
||||||
|
// inform the carddatabasemodels that they need to re-check their list of cards
|
||||||
|
emit cardDatabaseEnabledSetsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CardDatabase::saveCustomTokensToFile()
|
||||||
|
{
|
||||||
|
QString fileName =
|
||||||
|
SettingsCache::instance().getCustomCardDatabasePath() + "/" + CardDatabase::TOKENS_SETNAME + ".xml";
|
||||||
|
|
||||||
|
SetNameMap tmpSets;
|
||||||
|
CardSetPtr customTokensSet = getSet(CardDatabase::TOKENS_SETNAME);
|
||||||
|
tmpSets.insert(CardDatabase::TOKENS_SETNAME, customTokensSet);
|
||||||
|
|
||||||
|
CardNameMap tmpCards;
|
||||||
|
for (const CardInfoPtr &card : cards) {
|
||||||
|
if (card->getSets().contains(CardDatabase::TOKENS_SETNAME)) {
|
||||||
|
tmpCards.insert(card->getName(), card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<CardRelation *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
601
cockatrice/src/game/cards/card_database.h
Normal file
601
cockatrice/src/game/cards/card_database.h
Normal file
|
|
@ -0,0 +1,601 @@
|
||||||
|
#ifndef CARDDATABASE_H
|
||||||
|
#define CARDDATABASE_H
|
||||||
|
|
||||||
|
#include <QBasicMutex>
|
||||||
|
#include <QDate>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QVector>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
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<QString, QString> QStringMap;
|
||||||
|
typedef QSharedPointer<CardInfo> CardInfoPtr;
|
||||||
|
typedef QSharedPointer<CardSet> CardSetPtr;
|
||||||
|
typedef QMap<QString, QList<CardInfoPerSet>> CardInfoPerSetMap;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(CardInfoPtr)
|
||||||
|
|
||||||
|
class CardSet : public QList<CardInfoPtr>
|
||||||
|
{
|
||||||
|
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<CardSetPtr>
|
||||||
|
{
|
||||||
|
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<CardSet>(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<CardRelation *> relatedCards;
|
||||||
|
// the card i'm reverse-related to
|
||||||
|
QList<CardRelation *> reverseRelatedCards;
|
||||||
|
// the cards thare are reverse-related to me
|
||||||
|
QList<CardRelation *> 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<CardRelation *> &_relatedCards,
|
||||||
|
const QList<CardRelation *> &_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<CardRelation *> &_relatedCards,
|
||||||
|
const QList<CardRelation *> &_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<CardRelation *> &getRelatedCards() const
|
||||||
|
{
|
||||||
|
return relatedCards;
|
||||||
|
}
|
||||||
|
const QList<CardRelation *> &getReverseRelatedCards() const
|
||||||
|
{
|
||||||
|
return reverseRelatedCards;
|
||||||
|
}
|
||||||
|
const QList<CardRelation *> &getReverseRelatedCards2Me() const
|
||||||
|
{
|
||||||
|
return reverseRelatedCardsToMe;
|
||||||
|
}
|
||||||
|
const QList<CardRelation *> getAllRelatedCards() const
|
||||||
|
{
|
||||||
|
QList<CardRelation *> 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,
|
||||||
|
VersionTooOld,
|
||||||
|
Invalid,
|
||||||
|
NotLoaded,
|
||||||
|
FileError,
|
||||||
|
NoCards
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QHash<QString, CardInfoPtr> CardNameMap;
|
||||||
|
typedef QHash<QString, CardSetPtr> SetNameMap;
|
||||||
|
|
||||||
|
class CardDatabase : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
protected:
|
||||||
|
/*
|
||||||
|
* The cards, indexed by name.
|
||||||
|
*/
|
||||||
|
CardNameMap cards;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cards, indexed by their simple name.
|
||||||
|
*/
|
||||||
|
CardNameMap simpleNameCards;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The sets, indexed by short name.
|
||||||
|
*/
|
||||||
|
SetNameMap sets;
|
||||||
|
|
||||||
|
LoadStatus loadStatus;
|
||||||
|
|
||||||
|
QVector<ICardDatabaseParser *> availableParsers;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CardInfoPtr getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const;
|
||||||
|
void checkUnknownSets();
|
||||||
|
void refreshCachedReverseRelatedCards();
|
||||||
|
|
||||||
|
QBasicMutex *reloadDatabaseMutex = new QBasicMutex(), *clearDatabaseMutex = new QBasicMutex(),
|
||||||
|
*loadFromFileMutex = new QBasicMutex(), *addCardMutex = new QBasicMutex(),
|
||||||
|
*removeCardMutex = new QBasicMutex();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const char *TOKENS_SETNAME;
|
||||||
|
|
||||||
|
explicit CardDatabase(QObject *parent = nullptr);
|
||||||
|
~CardDatabase() override;
|
||||||
|
void clear();
|
||||||
|
void removeCard(CardInfoPtr card);
|
||||||
|
[[nodiscard]] CardInfoPtr getCard(const QString &cardName) const;
|
||||||
|
[[nodiscard]] QList<CardInfoPtr> getCards(const QStringList &cardNames) const;
|
||||||
|
QList<CardInfoPtr> getCardsByNameAndProviderId(const QMap<QString, QString> &cardNames) const;
|
||||||
|
[[nodiscard]] CardInfoPtr getCardByNameAndProviderId(const QString &cardName, const QString &providerId) const;
|
||||||
|
[[nodiscard]] CardInfoPerSet getPreferredSetForCard(const QString &cardName) const;
|
||||||
|
[[nodiscard]] CardInfoPerSet getSpecificSetForCard(const QString &cardName, const QString &providerId) const;
|
||||||
|
CardInfoPerSet
|
||||||
|
getSpecificSetForCard(const QString &cardName, const QString &setShortName, const QString &collectorNumber) const;
|
||||||
|
QString getPreferredPrintingProviderIdForCard(const QString &cardName);
|
||||||
|
[[nodiscard]] CardInfoPtr guessCard(const QString &cardName, const QString &providerId = QString()) const;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a card by its simple name. The name will be simplified in this
|
||||||
|
* function, so you don't need to simplify it beforehand.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] CardInfoPtr getCardBySimpleName(const QString &cardName) const;
|
||||||
|
|
||||||
|
CardSetPtr getSet(const QString &setName);
|
||||||
|
bool isProviderIdForPreferredPrinting(const QString &cardName, const QString &providerId);
|
||||||
|
static CardInfoPerSet getSetInfoForCard(const CardInfoPtr &_card);
|
||||||
|
const CardNameMap &getCardList() const
|
||||||
|
{
|
||||||
|
return cards;
|
||||||
|
}
|
||||||
|
SetList getSetList() const;
|
||||||
|
LoadStatus loadFromFile(const QString &fileName);
|
||||||
|
bool saveCustomTokensToFile();
|
||||||
|
QStringList getAllMainCardTypes() const;
|
||||||
|
LoadStatus getLoadStatus() const
|
||||||
|
{
|
||||||
|
return loadStatus;
|
||||||
|
}
|
||||||
|
void enableAllUnknownSets();
|
||||||
|
void markAllSetsAsKnown();
|
||||||
|
void notifyEnabledSetsChanged();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
LoadStatus loadCardDatabases();
|
||||||
|
void refreshPreferredPrintings();
|
||||||
|
void addCard(CardInfoPtr card);
|
||||||
|
void addSet(CardSetPtr set);
|
||||||
|
protected slots:
|
||||||
|
LoadStatus loadCardDatabase(const QString &path);
|
||||||
|
signals:
|
||||||
|
void cardDatabaseLoadingFinished();
|
||||||
|
void cardDatabaseLoadingFailed();
|
||||||
|
void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames);
|
||||||
|
void cardDatabaseAllNewSetsEnabled();
|
||||||
|
void cardDatabaseEnabledSetsChanged();
|
||||||
|
void cardAdded(CardInfoPtr card);
|
||||||
|
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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue