1 #include "carddatabase.h"
2 
3 #include "carddbparser/cockatricexml3.h"
4 #include "carddbparser/cockatricexml4.h"
5 #include "game_specific_terms.h"
6 #include "pictureloader.h"
7 #include "settingscache.h"
8 #include "spoilerbackgroundupdater.h"
9 
10 #include <QCryptographicHash>
11 #include <QDebug>
12 #include <QDir>
13 #include <QDirIterator>
14 #include <QFile>
15 #include <QMessageBox>
16 #include <QRegularExpression>
17 #include <algorithm>
18 #include <utility>
19 
20 const char *CardDatabase::TOKENS_SETNAME = "TK";
21 
CardSet(const QString & _shortName,const QString & _longName,const QString & _setType,const QDate & _releaseDate)22 CardSet::CardSet(const QString &_shortName,
23                  const QString &_longName,
24                  const QString &_setType,
25                  const QDate &_releaseDate)
26     : shortName(_shortName), longName(_longName), releaseDate(_releaseDate), setType(_setType)
27 {
28     loadSetOptions();
29 }
30 
newInstance(const QString & _shortName,const QString & _longName,const QString & _setType,const QDate & _releaseDate)31 CardSetPtr CardSet::newInstance(const QString &_shortName,
32                                 const QString &_longName,
33                                 const QString &_setType,
34                                 const QDate &_releaseDate)
35 {
36     CardSetPtr ptr(new CardSet(_shortName, _longName, _setType, _releaseDate));
37     // ptr->setSmartPointer(ptr);
38     return ptr;
39 }
40 
getCorrectedShortName() const41 QString CardSet::getCorrectedShortName() const
42 {
43     // For Windows machines.
44     QSet<QString> invalidFileNames;
45     invalidFileNames << "CON"
46                      << "PRN"
47                      << "AUX"
48                      << "NUL"
49                      << "COM1"
50                      << "COM2"
51                      << "COM3"
52                      << "COM4"
53                      << "COM5"
54                      << "COM6"
55                      << "COM7"
56                      << "COM8"
57                      << "COM9"
58                      << "LPT1"
59                      << "LPT2"
60                      << "LPT3"
61                      << "LPT4"
62                      << "LPT5"
63                      << "LPT6"
64                      << "LPT7"
65                      << "LPT8"
66                      << "LPT9";
67 
68     return invalidFileNames.contains(shortName) ? shortName + "_" : shortName;
69 }
70 
loadSetOptions()71 void CardSet::loadSetOptions()
72 {
73     sortKey = SettingsCache::instance().cardDatabase().getSortKey(shortName);
74     enabled = SettingsCache::instance().cardDatabase().isEnabled(shortName);
75     isknown = SettingsCache::instance().cardDatabase().isKnown(shortName);
76 }
77 
setSortKey(unsigned int _sortKey)78 void CardSet::setSortKey(unsigned int _sortKey)
79 {
80     sortKey = _sortKey;
81     SettingsCache::instance().cardDatabase().setSortKey(shortName, _sortKey);
82 }
83 
setEnabled(bool _enabled)84 void CardSet::setEnabled(bool _enabled)
85 {
86     enabled = _enabled;
87     SettingsCache::instance().cardDatabase().setEnabled(shortName, _enabled);
88 }
89 
setIsKnown(bool _isknown)90 void CardSet::setIsKnown(bool _isknown)
91 {
92     isknown = _isknown;
93     SettingsCache::instance().cardDatabase().setIsKnown(shortName, _isknown);
94 }
95 
96 class SetList::KeyCompareFunctor
97 {
98 public:
operator ()(const CardSetPtr & a,const CardSetPtr & b) const99     inline bool operator()(const CardSetPtr &a, const CardSetPtr &b) const
100     {
101         if (a.isNull() || b.isNull()) {
102             qDebug() << "SetList::KeyCompareFunctor a or b is null";
103             return false;
104         }
105 
106         return a->getSortKey() < b->getSortKey();
107     }
108 };
109 
sortByKey()110 void SetList::sortByKey()
111 {
112     std::sort(begin(), end(), KeyCompareFunctor());
113 }
114 
getEnabledSetsNum()115 int SetList::getEnabledSetsNum()
116 {
117     int num = 0;
118     for (int i = 0; i < size(); ++i) {
119         CardSetPtr set = at(i);
120         if (set && set->getEnabled()) {
121             ++num;
122         }
123     }
124     return num;
125 }
126 
getUnknownSetsNum()127 int SetList::getUnknownSetsNum()
128 {
129     int num = 0;
130     for (int i = 0; i < size(); ++i) {
131         CardSetPtr set = at(i);
132         if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) {
133             ++num;
134         }
135     }
136     return num;
137 }
138 
getUnknownSetsNames()139 QStringList SetList::getUnknownSetsNames()
140 {
141     QStringList sets = QStringList();
142     for (int i = 0; i < size(); ++i) {
143         CardSetPtr set = at(i);
144         if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) {
145             sets << set->getShortName();
146         }
147     }
148     return sets;
149 }
150 
enableAllUnknown()151 void SetList::enableAllUnknown()
152 {
153     for (int i = 0; i < size(); ++i) {
154         CardSetPtr set = at(i);
155         if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) {
156             set->setIsKnown(true);
157             set->setEnabled(true);
158         } else if (set && set->getIsKnownIgnored() && !set->getEnabled()) {
159             set->setEnabled(true);
160         }
161     }
162 }
163 
enableAll()164 void SetList::enableAll()
165 {
166     for (int i = 0; i < size(); ++i) {
167         CardSetPtr set = at(i);
168 
169         if (set == nullptr) {
170             qDebug() << "enabledAll has null";
171             continue;
172         }
173 
174         if (!set->getIsKnownIgnored()) {
175             set->setIsKnown(true);
176         }
177 
178         set->setEnabled(true);
179     }
180 }
181 
markAllAsKnown()182 void SetList::markAllAsKnown()
183 {
184     for (int i = 0; i < size(); ++i) {
185         CardSetPtr set = at(i);
186         if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) {
187             set->setIsKnown(true);
188             set->setEnabled(false);
189         } else if (set && set->getIsKnownIgnored() && !set->getEnabled()) {
190             set->setEnabled(true);
191         }
192     }
193 }
194 
guessSortKeys()195 void SetList::guessSortKeys()
196 {
197     // sort by release date DESC; invalid dates to the bottom.
198     QDate distantFuture(2050, 1, 1);
199     int aHundredYears = 36500;
200     for (int i = 0; i < size(); ++i) {
201         CardSetPtr set = at(i);
202         if (set.isNull()) {
203             qDebug() << "guessSortKeys set is null";
204             continue;
205         }
206 
207         QDate date = set->getReleaseDate();
208         if (date.isNull()) {
209             set->setSortKey(static_cast<unsigned int>(aHundredYears));
210         } else {
211             set->setSortKey(static_cast<unsigned int>(date.daysTo(distantFuture)));
212         }
213     }
214 }
215 
CardInfoPerSet(const CardSetPtr & _set)216 CardInfoPerSet::CardInfoPerSet(const CardSetPtr &_set) : set(_set)
217 {
218 }
219 
CardInfo(const QString & _name,const QString & _text,bool _isToken,QVariantHash _properties,const QList<CardRelation * > & _relatedCards,const QList<CardRelation * > & _reverseRelatedCards,CardInfoPerSetMap _sets,bool _cipt,int _tableRow,bool _upsideDownArt)220 CardInfo::CardInfo(const QString &_name,
221                    const QString &_text,
222                    bool _isToken,
223                    QVariantHash _properties,
224                    const QList<CardRelation *> &_relatedCards,
225                    const QList<CardRelation *> &_reverseRelatedCards,
226                    CardInfoPerSetMap _sets,
227                    bool _cipt,
228                    int _tableRow,
229                    bool _upsideDownArt)
230     : name(_name), text(_text), isToken(_isToken), properties(std::move(_properties)), relatedCards(_relatedCards),
231       reverseRelatedCards(_reverseRelatedCards), sets(std::move(_sets)), cipt(_cipt), tableRow(_tableRow),
232       upsideDownArt(_upsideDownArt)
233 {
234     pixmapCacheKey = QLatin1String("card_") + name;
235     simpleName = CardInfo::simplifyName(name);
236 
237     refreshCachedSetNames();
238 }
239 
~CardInfo()240 CardInfo::~CardInfo()
241 {
242     PictureLoader::clearPixmapCache(smartThis);
243 }
244 
newInstance(const QString & _name,const QString & _text,bool _isToken,QVariantHash _properties,const QList<CardRelation * > & _relatedCards,const QList<CardRelation * > & _reverseRelatedCards,CardInfoPerSetMap _sets,bool _cipt,int _tableRow,bool _upsideDownArt)245 CardInfoPtr CardInfo::newInstance(const QString &_name,
246                                   const QString &_text,
247                                   bool _isToken,
248                                   QVariantHash _properties,
249                                   const QList<CardRelation *> &_relatedCards,
250                                   const QList<CardRelation *> &_reverseRelatedCards,
251                                   CardInfoPerSetMap _sets,
252                                   bool _cipt,
253                                   int _tableRow,
254                                   bool _upsideDownArt)
255 {
256     CardInfoPtr ptr(new CardInfo(_name, _text, _isToken, std::move(_properties), _relatedCards, _reverseRelatedCards,
257                                  _sets, _cipt, _tableRow, _upsideDownArt));
258     ptr->setSmartPointer(ptr);
259 
260     for (const CardInfoPerSet &set : _sets) {
261         set.getPtr()->append(ptr);
262     }
263 
264     return ptr;
265 }
266 
getCorrectedName() const267 QString CardInfo::getCorrectedName() const
268 {
269     QString result = name;
270     // Fire // Ice, Circle of Protection: Red, "Ach! Hans, Run!", Who/What/When/Where/Why, Question Elemental?
271     return result.remove(" // ").remove(':').remove('"').remove('?').replace('/', ' ');
272 }
273 
addToSet(const CardSetPtr & _set,const CardInfoPerSet _info)274 void CardInfo::addToSet(const CardSetPtr &_set, const CardInfoPerSet _info)
275 {
276     _set->append(smartThis);
277     sets.insert(_set->getShortName(), _info);
278 
279     refreshCachedSetNames();
280 }
281 
refreshCachedSetNames()282 void CardInfo::refreshCachedSetNames()
283 {
284     QStringList setList;
285     // update the cached list of set names
286     for (const auto &set : sets) {
287         if (set.getPtr()->getEnabled()) {
288             setList << set.getPtr()->getShortName();
289         }
290     }
291     setsNames = setList.join(", ");
292 }
293 
simplifyName(const QString & name)294 QString CardInfo::simplifyName(const QString &name)
295 {
296     static const QRegularExpression spaceOrSplit("(\\s+|\\/\\/.*)");
297     static const QRegularExpression nonAlnum("[^a-z0-9]");
298 
299     QString simpleName = name.toLower();
300 
301     // remove spaces and right halves of split cards
302     simpleName.remove(spaceOrSplit);
303 
304     // So Aetherling would work, but not Ætherling since 'Æ' would get replaced
305     // with nothing.
306     simpleName.replace("æ", "ae");
307 
308     // Replace Jötun Grunt with Jotun Grunt.
309     simpleName = simpleName.normalized(QString::NormalizationForm_KD);
310 
311     // remove all non alphanumeric characters from the name
312     simpleName.remove(nonAlnum);
313     return simpleName;
314 }
315 
getColorChar() const316 const QChar CardInfo::getColorChar() const
317 {
318     QString colors = getColors();
319     switch (colors.size()) {
320         case 0:
321             return QChar();
322         case 1:
323             return colors.at(0);
324         default:
325             return QChar('m');
326     }
327 }
328 
CardDatabase(QObject * parent)329 CardDatabase::CardDatabase(QObject *parent) : QObject(parent), loadStatus(NotLoaded)
330 {
331     qRegisterMetaType<CardInfoPtr>("CardInfoPtr");
332     qRegisterMetaType<CardInfoPtr>("CardSetPtr");
333 
334     // add new parsers here
335     availableParsers << new CockatriceXml4Parser;
336     availableParsers << new CockatriceXml3Parser;
337 
338     for (auto &parser : availableParsers) {
339         connect(parser, SIGNAL(addCard(CardInfoPtr)), this, SLOT(addCard(CardInfoPtr)), Qt::DirectConnection);
340         connect(parser, SIGNAL(addSet(CardSetPtr)), this, SLOT(addSet(CardSetPtr)), Qt::DirectConnection);
341     }
342 
343     connect(&SettingsCache::instance(), SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabases()));
344 }
345 
~CardDatabase()346 CardDatabase::~CardDatabase()
347 {
348     clear();
349     qDeleteAll(availableParsers);
350 }
351 
clear()352 void CardDatabase::clear()
353 {
354     clearDatabaseMutex->lock();
355 
356     QHashIterator<QString, CardInfoPtr> i(cards);
357     while (i.hasNext()) {
358         i.next();
359         if (i.value()) {
360             removeCard(i.value());
361         }
362     }
363 
364     cards.clear();
365     simpleNameCards.clear();
366 
367     sets.clear();
368     ICardDatabaseParser::clearSetlist();
369 
370     loadStatus = NotLoaded;
371 
372     clearDatabaseMutex->unlock();
373 }
374 
addCard(CardInfoPtr card)375 void CardDatabase::addCard(CardInfoPtr card)
376 {
377     if (card == nullptr) {
378         qDebug() << "addCard(nullptr)";
379         return;
380     }
381 
382     // if card already exists just add the new set property
383     if (cards.contains(card->getName())) {
384         CardInfoPtr sameCard = cards[card->getName()];
385         for (const CardInfoPerSet &set : card->getSets()) {
386             sameCard->addToSet(set.getPtr(), set);
387         }
388         return;
389     }
390 
391     addCardMutex->lock();
392     cards.insert(card->getName(), card);
393     simpleNameCards.insert(card->getSimpleName(), card);
394     addCardMutex->unlock();
395     emit cardAdded(card);
396 }
397 
removeCard(CardInfoPtr card)398 void CardDatabase::removeCard(CardInfoPtr card)
399 {
400     if (card.isNull()) {
401         qDebug() << "removeCard(nullptr)";
402         return;
403     }
404 
405     for (auto *cardRelation : card->getRelatedCards())
406         cardRelation->deleteLater();
407 
408     for (auto *cardRelation : card->getReverseRelatedCards())
409         cardRelation->deleteLater();
410 
411     for (auto *cardRelation : card->getReverseRelatedCards2Me())
412         cardRelation->deleteLater();
413 
414     removeCardMutex->lock();
415     cards.remove(card->getName());
416     simpleNameCards.remove(card->getSimpleName());
417     removeCardMutex->unlock();
418     emit cardRemoved(card);
419 }
420 
getCard(const QString & cardName) const421 CardInfoPtr CardDatabase::getCard(const QString &cardName) const
422 {
423     return getCardFromMap(cards, cardName);
424 }
425 
getCards(const QStringList & cardNames) const426 QList<CardInfoPtr> CardDatabase::getCards(const QStringList &cardNames) const
427 {
428     QList<CardInfoPtr> cardInfos;
429     foreach (QString cardName, cardNames) {
430         CardInfoPtr ptr = getCardFromMap(cards, cardName);
431         if (ptr)
432             cardInfos.append(ptr);
433     }
434 
435     return cardInfos;
436 }
437 
getCardBySimpleName(const QString & cardName) const438 CardInfoPtr CardDatabase::getCardBySimpleName(const QString &cardName) const
439 {
440     return getCardFromMap(simpleNameCards, CardInfo::simplifyName(cardName));
441 }
442 
guessCard(const QString & cardName) const443 CardInfoPtr CardDatabase::guessCard(const QString &cardName) const
444 {
445     CardInfoPtr temp = getCard(cardName);
446     if (temp == nullptr) { // get card by simple name instead
447         temp = getCardBySimpleName(cardName);
448         if (temp == nullptr) { // still could not find the card, so simplify the cardName too
449             QString simpleCardName = CardInfo::simplifyName(cardName);
450             temp = getCardBySimpleName(simpleCardName);
451         }
452     }
453     return temp; // returns nullptr if not found
454 }
455 
getSet(const QString & setName)456 CardSetPtr CardDatabase::getSet(const QString &setName)
457 {
458     if (sets.contains(setName)) {
459         return sets.value(setName);
460     } else {
461         CardSetPtr newSet = CardSet::newInstance(setName);
462         sets.insert(setName, newSet);
463         return newSet;
464     }
465 }
466 
addSet(CardSetPtr set)467 void CardDatabase::addSet(CardSetPtr set)
468 {
469     sets.insert(set->getShortName(), set);
470 }
471 
getSetList() const472 SetList CardDatabase::getSetList() const
473 {
474     SetList result;
475     QHashIterator<QString, CardSetPtr> i(sets);
476     while (i.hasNext()) {
477         i.next();
478         result << i.value();
479     }
480     return result;
481 }
482 
getCardFromMap(const CardNameMap & cardMap,const QString & cardName) const483 CardInfoPtr CardDatabase::getCardFromMap(const CardNameMap &cardMap, const QString &cardName) const
484 {
485     if (cardMap.contains(cardName))
486         return cardMap.value(cardName);
487 
488     return {};
489 }
490 
loadFromFile(const QString & fileName)491 LoadStatus CardDatabase::loadFromFile(const QString &fileName)
492 {
493     QFile file(fileName);
494     file.open(QIODevice::ReadOnly);
495     if (!file.isOpen()) {
496         return FileError;
497     }
498 
499     for (auto parser : availableParsers) {
500         file.reset();
501         if (parser->getCanParseFile(fileName, file)) {
502             file.reset();
503             parser->parseFile(file);
504             return Ok;
505         }
506     }
507 
508     return Invalid;
509 }
510 
loadCardDatabase(const QString & path)511 LoadStatus CardDatabase::loadCardDatabase(const QString &path)
512 {
513     LoadStatus tempLoadStatus = NotLoaded;
514     if (!path.isEmpty()) {
515         loadFromFileMutex->lock();
516         tempLoadStatus = loadFromFile(path);
517         loadFromFileMutex->unlock();
518     }
519 
520     qDebug() << "[CardDatabase] loadCardDatabase(): Path =" << path << "Status =" << tempLoadStatus
521              << "Cards =" << cards.size() << "Sets=" << sets.size();
522 
523     return tempLoadStatus;
524 }
525 
loadCardDatabases()526 LoadStatus CardDatabase::loadCardDatabases()
527 {
528     reloadDatabaseMutex->lock();
529 
530     qDebug() << "CardDatabase::loadCardDatabases start";
531 
532     clear(); // remove old db
533 
534     loadStatus = loadCardDatabase(SettingsCache::instance().getCardDatabasePath()); // load main card database
535     loadCardDatabase(SettingsCache::instance().getTokenDatabasePath());             // load tokens database
536     loadCardDatabase(SettingsCache::instance().getSpoilerCardDatabasePath());       // load spoilers database
537 
538     // find all custom card databases, recursively & following symlinks
539     // then load them alphabetically
540     QDirIterator customDatabaseIterator(SettingsCache::instance().getCustomCardDatabasePath(), QStringList() << "*.xml",
541                                         QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
542     QStringList databasePaths;
543     while (customDatabaseIterator.hasNext()) {
544         customDatabaseIterator.next();
545         databasePaths.push_back(customDatabaseIterator.filePath());
546     }
547     databasePaths.sort();
548 
549     for (auto i = 0; i < databasePaths.size(); ++i) {
550         const auto &databasePath = databasePaths.at(i);
551         qDebug() << "Loading Custom Set" << i << "(" << databasePath << ")";
552         loadCardDatabase(databasePath);
553     }
554 
555     // AFTER all the cards have been loaded
556 
557     // resolve the reverse-related tags
558     refreshCachedReverseRelatedCards();
559 
560     if (loadStatus == Ok) {
561         checkUnknownSets(); // update deck editors, etc
562         qDebug() << "CardDatabase::loadCardDatabases success";
563     } else {
564         qDebug() << "CardDatabase::loadCardDatabases failed";
565         emit cardDatabaseLoadingFailed(); // bring up the settings dialog
566     }
567 
568     reloadDatabaseMutex->unlock();
569     return loadStatus;
570 }
571 
refreshCachedReverseRelatedCards()572 void CardDatabase::refreshCachedReverseRelatedCards()
573 {
574     for (const CardInfoPtr &card : cards)
575         card->resetReverseRelatedCards2Me();
576 
577     for (const CardInfoPtr &card : cards) {
578         if (card->getReverseRelatedCards().isEmpty()) {
579             continue;
580         }
581 
582         foreach (CardRelation *cardRelation, card->getReverseRelatedCards()) {
583             const QString &targetCard = cardRelation->getName();
584             if (!cards.contains(targetCard)) {
585                 continue;
586             }
587 
588             auto *newCardRelation = new CardRelation(card->getName(), cardRelation->getDoesAttach(),
589                                                      cardRelation->getIsCreateAllExclusion(),
590                                                      cardRelation->getIsVariable(), cardRelation->getDefaultCount());
591             cards.value(targetCard)->addReverseRelatedCards2Me(newCardRelation);
592         }
593     }
594 }
595 
getAllMainCardTypes() const596 QStringList CardDatabase::getAllMainCardTypes() const
597 {
598     QSet<QString> types;
599     QHashIterator<QString, CardInfoPtr> cardIterator(cards);
600     while (cardIterator.hasNext()) {
601         types.insert(cardIterator.next().value()->getMainCardType());
602     }
603     return types.values();
604 }
605 
checkUnknownSets()606 void CardDatabase::checkUnknownSets()
607 {
608     SetList sets = getSetList();
609 
610     if (sets.getEnabledSetsNum()) {
611         // if some sets are first found on this run, ask the user
612         int numUnknownSets = sets.getUnknownSetsNum();
613         QStringList unknownSetNames = sets.getUnknownSetsNames();
614         if (numUnknownSets > 0) {
615             emit cardDatabaseNewSetsFound(numUnknownSets, unknownSetNames);
616         } else {
617             sets.markAllAsKnown();
618         }
619     } else {
620         // No set enabled. Probably this is the first time running trice
621         sets.guessSortKeys();
622         sets.sortByKey();
623         sets.enableAll();
624         notifyEnabledSetsChanged();
625 
626         emit cardDatabaseAllNewSetsEnabled();
627     }
628 }
629 
enableAllUnknownSets()630 void CardDatabase::enableAllUnknownSets()
631 {
632     SetList sets = getSetList();
633     sets.enableAllUnknown();
634 }
635 
markAllSetsAsKnown()636 void CardDatabase::markAllSetsAsKnown()
637 {
638     SetList sets = getSetList();
639     sets.markAllAsKnown();
640 }
641 
notifyEnabledSetsChanged()642 void CardDatabase::notifyEnabledSetsChanged()
643 {
644     // refresh the list of cached set names
645     for (const CardInfoPtr &card : cards)
646         card->refreshCachedSetNames();
647 
648     // inform the carddatabasemodels that they need to re-check their list of cards
649     emit cardDatabaseEnabledSetsChanged();
650 }
651 
saveCustomTokensToFile()652 bool CardDatabase::saveCustomTokensToFile()
653 {
654     QString fileName =
655         SettingsCache::instance().getCustomCardDatabasePath() + "/" + CardDatabase::TOKENS_SETNAME + ".xml";
656 
657     SetNameMap tmpSets;
658     CardSetPtr customTokensSet = getSet(CardDatabase::TOKENS_SETNAME);
659     tmpSets.insert(CardDatabase::TOKENS_SETNAME, customTokensSet);
660 
661     CardNameMap tmpCards;
662     for (const CardInfoPtr &card : cards) {
663         if (card->getSets().contains(CardDatabase::TOKENS_SETNAME)) {
664             tmpCards.insert(card->getName(), card);
665         }
666     }
667 
668     availableParsers.first()->saveToFile(tmpSets, tmpCards, fileName);
669     return true;
670 }
671 
CardRelation(const QString & _name,bool _doesAttach,bool _isCreateAllExclusion,bool _isVariableCount,int _defaultCount)672 CardRelation::CardRelation(const QString &_name,
673                            bool _doesAttach,
674                            bool _isCreateAllExclusion,
675                            bool _isVariableCount,
676                            int _defaultCount)
677     : name(_name), doesAttach(_doesAttach), isCreateAllExclusion(_isCreateAllExclusion),
678       isVariableCount(_isVariableCount), defaultCount(_defaultCount)
679 {
680 }
681 
resetReverseRelatedCards2Me()682 void CardInfo::resetReverseRelatedCards2Me()
683 {
684     foreach (CardRelation *cardRelation, this->getReverseRelatedCards2Me()) {
685         cardRelation->deleteLater();
686     }
687     reverseRelatedCardsToMe = QList<CardRelation *>();
688 }
689 
690 // Back-compatibility methods. Remove ASAP
getCardType() const691 const QString CardInfo::getCardType() const
692 {
693     return getProperty(Mtg::CardType);
694 }
setCardType(const QString & value)695 void CardInfo::setCardType(const QString &value)
696 {
697     setProperty(Mtg::CardType, value);
698 }
getCmc() const699 const QString CardInfo::getCmc() const
700 {
701     return getProperty(Mtg::ConvertedManaCost);
702 }
getColors() const703 const QString CardInfo::getColors() const
704 {
705     return getProperty(Mtg::Colors);
706 }
setColors(const QString & value)707 void CardInfo::setColors(const QString &value)
708 {
709     setProperty(Mtg::Colors, value);
710 }
getLoyalty() const711 const QString CardInfo::getLoyalty() const
712 {
713     return getProperty(Mtg::Loyalty);
714 }
getMainCardType() const715 const QString CardInfo::getMainCardType() const
716 {
717     return getProperty(Mtg::MainCardType);
718 }
getManaCost() const719 const QString CardInfo::getManaCost() const
720 {
721     return getProperty(Mtg::ManaCost);
722 }
getPowTough() const723 const QString CardInfo::getPowTough() const
724 {
725     return getProperty(Mtg::PowTough);
726 }
setPowTough(const QString & value)727 void CardInfo::setPowTough(const QString &value)
728 {
729     setProperty(Mtg::PowTough, value);
730 }
731