you can display tokens in editor (show tokens and choose 0 manacost) but you can't add them, useful for tracking missing images...
712 lines
17 KiB
C++
712 lines
17 KiB
C++
#include "PrecompiledHeader.h"
|
|
|
|
#include "OptionItem.h"
|
|
#include "PlayerData.h"
|
|
#include "Translate.h"
|
|
#include "PriceList.h"
|
|
#include "Subtypes.h"
|
|
|
|
//WSyncable
|
|
bool WSyncable::Hook(WSyncable* s)
|
|
{
|
|
if (hooked) return false;
|
|
hooked = s;
|
|
return true;
|
|
}
|
|
|
|
int WSyncable::getPos()
|
|
{
|
|
if (hooked) return hooked->getPos() + currentPos;
|
|
return currentPos;
|
|
}
|
|
|
|
bool WSyncable::next()
|
|
{
|
|
if (hooked) return hooked->next();
|
|
++currentPos;
|
|
return true;
|
|
}
|
|
|
|
bool WSyncable::prev()
|
|
{
|
|
if (hooked) return hooked->prev();
|
|
--currentPos;
|
|
return true;
|
|
}
|
|
|
|
//WSrcImage
|
|
JQuadPtr WSrcImage::getImage(int)
|
|
{
|
|
return WResourceManager::Instance()->RetrieveTempQuad(filename);
|
|
}
|
|
|
|
WSrcImage::WSrcImage(string s)
|
|
{
|
|
filename = s;
|
|
}
|
|
|
|
//WSrcCards
|
|
WSrcCards::WSrcCards(float delay)
|
|
{
|
|
mDelay = delay;
|
|
mLastInput = 0;
|
|
currentPos = 0;
|
|
filtersRoot = NULL;
|
|
}
|
|
|
|
JQuadPtr WSrcCards::getImage(int offset)
|
|
{
|
|
if (!WResourceManager::Instance()->IsThreaded())
|
|
{
|
|
if (mDelay && mLastInput < mDelay)
|
|
{
|
|
return WResourceManager::Instance()->RetrieveCard(getCard(offset), RETRIEVE_EXISTING);
|
|
}
|
|
}
|
|
|
|
return WResourceManager::Instance()->RetrieveCard(getCard(offset));
|
|
}
|
|
|
|
JQuadPtr WSrcCards::getThumb(int offset)
|
|
{
|
|
return WResourceManager::Instance()->RetrieveCard(getCard(offset), RETRIEVE_THUMB);
|
|
}
|
|
|
|
WSrcCards::~WSrcCards()
|
|
{
|
|
clearFilters();
|
|
cards.clear();
|
|
}
|
|
|
|
void WSrcCards::bakeFilters()
|
|
{
|
|
vector<MTGCard*> temp;
|
|
|
|
setOffset(0);
|
|
for (int t = 0; t < Size(); t++)
|
|
{
|
|
temp.push_back(getCard(t));
|
|
}
|
|
setOffset(0);
|
|
cards.clear();
|
|
cards.swap(temp);
|
|
clearFilters();
|
|
return;
|
|
}
|
|
|
|
bool WSrcCards::matchesFilters(MTGCard * c)
|
|
{
|
|
if (!c) return false;
|
|
if (!filtersRoot) return true;
|
|
return filtersRoot->isMatch(c);
|
|
}
|
|
|
|
int WSrcCards::Size(bool all)
|
|
{
|
|
if (!all && filtersRoot) return (int) validated.size();
|
|
return (int) cards.size();
|
|
}
|
|
|
|
MTGCard * WSrcCards::getCard(int offset, bool ignore)
|
|
{
|
|
int oldpos;
|
|
int size = (int) cards.size();
|
|
MTGCard * c = NULL;
|
|
if (!ignore && filtersRoot) size = (int) validated.size();
|
|
|
|
if (!size) return NULL;
|
|
|
|
oldpos = currentPos;
|
|
if (offset != 0) currentPos += offset;
|
|
while (currentPos < 0)
|
|
currentPos = size + currentPos;
|
|
currentPos = currentPos % size;
|
|
|
|
if (!ignore && filtersRoot)
|
|
c = cards[validated[currentPos]];
|
|
else
|
|
c = cards[currentPos];
|
|
currentPos = oldpos;
|
|
return c;
|
|
}
|
|
|
|
int WSrcCards::loadMatches(MTGAllCards* ac)
|
|
{
|
|
map<int, MTGCard *>::iterator it;
|
|
int count = 0;
|
|
if (!ac) return count;
|
|
|
|
for (it = ac->collection.begin(); it != ac->collection.end(); it++)
|
|
{
|
|
if (it->second && (matchesFilters(it->second)))
|
|
{
|
|
cards.push_back(it->second);
|
|
count++;
|
|
}
|
|
}
|
|
validate();
|
|
return count;
|
|
}
|
|
|
|
int WSrcCards::loadMatches(MTGDeck * deck)
|
|
{
|
|
map<int, int>::iterator it;
|
|
int count = 0;
|
|
if (!deck) return count;
|
|
for (it = deck->cards.begin(); it != deck->cards.end(); it++)
|
|
{
|
|
MTGCard * c = deck->getCardById(it->first);
|
|
if (c && (matchesFilters(c)))
|
|
{
|
|
cards.push_back(c);
|
|
count++;
|
|
}
|
|
}
|
|
validate();
|
|
return count;
|
|
}
|
|
|
|
int WSrcCards::loadMatches(WSrcCards* src, bool all)
|
|
{
|
|
int count = 0;
|
|
if (!src) return count;
|
|
|
|
MTGCard * c = NULL;
|
|
|
|
int oldp = src->getOffset();
|
|
src->setOffset(0);
|
|
for (int t = 0; t < src->Size(all); t++)
|
|
{
|
|
c = src->getCard(t, all);
|
|
if (matchesFilters(c))
|
|
{
|
|
cards.push_back(c);
|
|
count++;
|
|
}
|
|
}
|
|
src->setOffset(oldp);
|
|
validate();
|
|
return count;
|
|
}
|
|
|
|
int WSrcCards::addRandomCards(MTGDeck * i, int howmany)
|
|
{
|
|
if (!cards.size() || (filtersRoot && !validated.size())) return howmany;
|
|
for (int x = 0; x < howmany; x++)
|
|
{
|
|
if (validated.size())
|
|
{
|
|
size_t pos = rand() % validated.size();
|
|
MTGCard * c = cards[validated[pos]];
|
|
i->add(c);
|
|
}
|
|
else
|
|
{
|
|
size_t pos = rand() % cards.size();
|
|
i->add(cards[pos]);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int WSrcCards::addToDeck(MTGDeck * i, int num)
|
|
{
|
|
int oldpos = getOffset();
|
|
int added = 0;
|
|
int cycles = 0;
|
|
|
|
if (!i)
|
|
{
|
|
if (num < 0) return 0;
|
|
return num;
|
|
}
|
|
|
|
setOffset(0);
|
|
if (num < 0)
|
|
{ //Add it all;
|
|
MTGCard * c;
|
|
for (;;)
|
|
{
|
|
c = getCard();
|
|
if (!c || !next()) break;
|
|
i->add(c);
|
|
}
|
|
}
|
|
else
|
|
while (added < num)
|
|
{
|
|
MTGCard * c = getCard();
|
|
if (!next() || !c)
|
|
{
|
|
if (++cycles == WSrcCards::MAX_CYCLES)
|
|
{ //Abort the search, too many cycles.
|
|
setOffset(oldpos);
|
|
return num - added;
|
|
}
|
|
setOffset(0);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
i->add(c);
|
|
added++;
|
|
}
|
|
}
|
|
setOffset(oldpos);
|
|
return 0;
|
|
}
|
|
|
|
bool WSrcCards::next()
|
|
{
|
|
int size = (int) cards.size();
|
|
if (filtersRoot) size = (int) validated.size();
|
|
if (currentPos + 1 >= size) return false;
|
|
currentPos++;
|
|
return true;
|
|
}
|
|
|
|
bool WSrcCards::prev()
|
|
{
|
|
if (currentPos == 0) return false;
|
|
|
|
currentPos--;
|
|
return true;
|
|
}
|
|
|
|
bool WSrcCards::setOffset(int pos)
|
|
{
|
|
if (pos < 0 || pos >= (int) cards.size()) return false;
|
|
|
|
currentPos = pos;
|
|
if (!matchesFilters(cards[currentPos])) return next();
|
|
|
|
return true;
|
|
}
|
|
|
|
void WSrcCards::Shuffle()
|
|
{
|
|
std::random_shuffle(cards.begin(), cards.end());
|
|
validate();
|
|
}
|
|
|
|
void WSrcCards::validate()
|
|
{
|
|
validated.clear();
|
|
updateCounts();
|
|
if (!filtersRoot) return;
|
|
for (size_t t = 0; t < cards.size(); t++)
|
|
{
|
|
if(!options[Options::SHOWTOKENS].number)
|
|
{//don't add tokens or negative id
|
|
if (matchesFilters(cards[t]) && (cards[t]->getId() > 0) && (cards[t]->getRarity() != Constants::RARITY_T)) validated.push_back(t);
|
|
}
|
|
else
|
|
{//show but you cant add
|
|
if (matchesFilters(cards[t])) validated.push_back(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool WSrcCards::thisCard(int mtgid)
|
|
{
|
|
for (size_t t = 0; t < cards.size(); t++)
|
|
{
|
|
if (cards[t] && cards[t]->getId() == mtgid)
|
|
{
|
|
currentPos = (int) t;
|
|
return matchesFilters(cards[currentPos]);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool WSrcCards::isEmptySet(WCardFilter * f)
|
|
{
|
|
size_t max = cards.size();
|
|
if (validated.size()) max = validated.size();
|
|
if (!f) return (max > 0);
|
|
for (size_t t = 0; t < max; t++)
|
|
{
|
|
if (validated.size())
|
|
{
|
|
if (f->isMatch(cards[validated[t]])) return false;
|
|
}
|
|
else if (f->isMatch(cards[t])) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void WSrcCards::addFilter(WCardFilter * f)
|
|
{
|
|
if (filtersRoot == NULL)
|
|
filtersRoot = f;
|
|
else
|
|
filtersRoot = NEW WCFilterAND(f, filtersRoot);
|
|
validate();
|
|
currentPos = 0;
|
|
}
|
|
float WSrcCards::filterFee()
|
|
{
|
|
if (filtersRoot) return filtersRoot->filterFee();
|
|
return 0;
|
|
}
|
|
|
|
void WSrcCards::clearFilters()
|
|
{
|
|
SAFE_DELETE(filtersRoot);
|
|
validated.clear();
|
|
}
|
|
WCardFilter* WSrcCards::unhookFilters()
|
|
{
|
|
WCardFilter* temp = filtersRoot;
|
|
filtersRoot = NULL;
|
|
clearFilters();
|
|
return temp;
|
|
}
|
|
|
|
void WSrcCards::Sort(int method)
|
|
{
|
|
switch (method)
|
|
{
|
|
case WSrcCards::SORT_COLLECTOR:
|
|
std::sort(cards.begin(), cards.end(), WCSortCollector());
|
|
break;
|
|
case WSrcCards::SORT_RARITY:
|
|
std::sort(cards.begin(), cards.end(), WCSortRarity());
|
|
break;
|
|
case WSrcCards::SORT_ALPHA:
|
|
default:
|
|
std::sort(cards.begin(), cards.end(), WCSortAlpha());
|
|
break;
|
|
}
|
|
validate();
|
|
}
|
|
|
|
//WSrcUnlockedCards
|
|
WSrcUnlockedCards::WSrcUnlockedCards(float delay) :
|
|
WSrcCards(delay)
|
|
{
|
|
MTGAllCards * ac = MTGCollection();
|
|
map<int, MTGCard*>::iterator it;
|
|
|
|
char * unlocked = NULL;
|
|
unlocked = (char *) calloc(setlist.size(), sizeof(char));
|
|
//Figure out which sets are available.
|
|
for (int i = 0; i < setlist.size(); i++)
|
|
{
|
|
unlocked[i] = options[Options::optionSet(i)].number;
|
|
}
|
|
|
|
for (it = ac->collection.begin(); it != ac->collection.end(); it++)
|
|
{
|
|
if(!options[Options::SHOWTOKENS].number)
|
|
{//dont show tokens & negative id's
|
|
if (it->second && unlocked[it->second->setId] && (it->second->getId() > 0) && (it->second->getRarity() != Constants::RARITY_T)) cards.push_back(it->second);
|
|
}
|
|
else
|
|
{//show but you cant add
|
|
if (it->second && unlocked[it->second->setId]) cards.push_back(it->second);
|
|
}
|
|
}
|
|
if (unlocked)
|
|
{
|
|
free(unlocked);
|
|
unlocked = NULL;
|
|
}
|
|
|
|
if (cards.size())
|
|
{
|
|
Shuffle();
|
|
currentPos = 0;
|
|
}
|
|
}
|
|
|
|
//WSrcDeck
|
|
int WSrcDeck::loadMatches(MTGDeck * deck)
|
|
{
|
|
map<int, int>::iterator it;
|
|
int count = 0;
|
|
if (!deck) return count;
|
|
for (it = deck->cards.begin(); it != deck->cards.end(); it++)
|
|
{
|
|
MTGCard * c = deck->getCardById(it->first);
|
|
if(!options[Options::SHOWTOKENS].number)
|
|
{//dont show tokens & negative id's
|
|
if (c && matchesFilters(c) && (c->getId() > 0) && (c->getRarity() != Constants::RARITY_T))
|
|
{
|
|
Add(c, it->second);
|
|
count++;
|
|
}
|
|
}
|
|
else
|
|
{//show but you cant add
|
|
if (c && matchesFilters(c))
|
|
{
|
|
Add(c, it->second);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
validate();
|
|
return count;
|
|
}
|
|
|
|
void WSrcDeck::updateCounts()
|
|
{
|
|
vector<MTGCard*>::iterator it;
|
|
map<int, int>::iterator ccount;
|
|
clearCounts();
|
|
for (it = cards.begin(); it != cards.end(); it++)
|
|
{
|
|
ccount = copies.find((*it)->getMTGId());
|
|
if (ccount == copies.end()) continue;
|
|
addCount((*it), ccount->second);
|
|
}
|
|
}
|
|
|
|
void WSrcDeck::clearCounts()
|
|
{
|
|
counts[UNFILTERED_MIN_COPIES] = -1;
|
|
counts[UNFILTERED_MAX_COPIES] = 0;
|
|
for (int i = 0; i < MAX_COUNTS; i++)
|
|
counts[i] = 0;
|
|
}
|
|
|
|
void WSrcDeck::addCount(MTGCard * c, int qty)
|
|
{
|
|
if (!c || !c->data) return;
|
|
map<int, int>::iterator cp = copies.find(c->getMTGId());
|
|
|
|
if (matchesFilters(c))
|
|
{
|
|
counts[FILTERED_COPIES] += qty;
|
|
if (qty > 0 && cp != copies.end() && (*cp).second == qty)
|
|
counts[FILTERED_UNIQUE]++;
|
|
else if (qty < 0 && (cp == copies.end() || (*cp).second == 0)) counts[FILTERED_UNIQUE]--;
|
|
}
|
|
counts[UNFILTERED_COPIES] += qty;
|
|
if (qty > 0 && cp != copies.end() && (*cp).second == qty)
|
|
counts[UNFILTERED_UNIQUE]++;
|
|
else if (qty < 0 && (cp == copies.end() || (*cp).second == 0)) counts[UNFILTERED_UNIQUE]--;
|
|
for (int i = Constants::MTG_COLOR_ARTIFACT; i <= Constants::MTG_COLOR_LAND; i++)
|
|
if (c->data->hasColor(i)) counts[i] += qty;
|
|
if (counts[UNFILTERED_MIN_COPIES] < 0 || qty < counts[UNFILTERED_MIN_COPIES]) counts[UNFILTERED_MIN_COPIES] = qty;
|
|
if (qty > counts[UNFILTERED_MAX_COPIES]) counts[UNFILTERED_MAX_COPIES] = qty;
|
|
}
|
|
|
|
int WSrcDeck::Add(MTGCard * c, int quantity)
|
|
{
|
|
if (!c) return 0;
|
|
if (copies.find(c->getMTGId()) == copies.end())
|
|
{
|
|
cards.push_back(c);
|
|
}
|
|
copies[c->getMTGId()] += quantity;
|
|
addCount(c, quantity);
|
|
return 1;
|
|
}
|
|
|
|
int WSrcDeck::Remove(MTGCard * c, int quantity, bool erase)
|
|
{
|
|
if (!c) return 0;
|
|
map<int, int>::iterator it = copies.find(c->getMTGId());
|
|
if (it == copies.end()) return 0;
|
|
int amt = it->second;
|
|
if (amt < quantity) return 0;
|
|
amt -= quantity;
|
|
it->second = amt;
|
|
if (erase && amt == 0)
|
|
{
|
|
copies.erase(it);
|
|
vector<MTGCard*>::iterator i = find(cards.begin(), cards.end(), c);
|
|
if (i != cards.end()) cards.erase(i);
|
|
}
|
|
addCount(c, -quantity);
|
|
return 1;
|
|
}
|
|
|
|
void WSrcDeck::Rebuild(MTGDeck * d)
|
|
{
|
|
d->removeAll();
|
|
map<int, int>::iterator it;
|
|
for (it = copies.begin(); it != copies.end(); it++)
|
|
{
|
|
for (int i = 0; i < it->second; i++)
|
|
{
|
|
d->add(it->first);
|
|
}
|
|
}
|
|
}
|
|
|
|
int WSrcDeck::count(MTGCard * c)
|
|
{
|
|
if (!c) return counts[UNFILTERED_COPIES];
|
|
if (copies.find(c->getMTGId()) == copies.end()) return 0;
|
|
return copies[c->getMTGId()];
|
|
}
|
|
|
|
int WSrcDeck::countByName(MTGCard * card, bool editions)
|
|
{
|
|
string name = card->data->getLCName();
|
|
int total = 0;
|
|
vector<MTGCard*>::iterator it;
|
|
for (it = cards.begin(); it != cards.end(); it++)
|
|
{
|
|
if (*it && (*it)->data->getLCName() == name)
|
|
{
|
|
if (editions)
|
|
total++;
|
|
else
|
|
{
|
|
map<int, int>::iterator mi = copies.find((*it)->getMTGId());
|
|
if (mi != copies.end()) total += mi->second;
|
|
}
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
|
|
int WSrcDeck::getCount(int count)
|
|
{
|
|
if (count < 0 || count >= MAX_COUNTS) return counts[UNFILTERED_COPIES];
|
|
return counts[count];
|
|
}
|
|
|
|
int WSrcDeck::totalPrice()
|
|
{
|
|
int total = 0;
|
|
PriceList * pricelist = NEW PriceList("settings/prices.dat", MTGCollection());
|
|
map<int, int>::iterator it;
|
|
for (it = copies.begin(); it != copies.end(); it++)
|
|
{
|
|
int nb = it->second;
|
|
if (nb) total += pricelist->getPrice(it->first);
|
|
}
|
|
SAFE_DELETE(pricelist);
|
|
return total;
|
|
}
|
|
|
|
//WSrcDeckViewer
|
|
WSrcDeckViewer::WSrcDeckViewer(WSrcCards * _active, WSrcCards * _inactive) :
|
|
WSrcCards(0.2f)
|
|
{
|
|
active = _active;
|
|
inactive = _inactive;
|
|
}
|
|
|
|
void WSrcDeckViewer::swapSrc()
|
|
{
|
|
WSrcCards * temp = active;
|
|
active = inactive;
|
|
inactive = temp;
|
|
}
|
|
|
|
WSrcDeckViewer::~WSrcDeckViewer()
|
|
{
|
|
//Do nothing.
|
|
}
|
|
|
|
//Sorting methods:
|
|
int WCSortRarity::rareToInt(char r)
|
|
{
|
|
switch (r)
|
|
{
|
|
default:
|
|
case Constants::RARITY_T:
|
|
return 0;
|
|
case Constants::RARITY_L:
|
|
return 1;
|
|
case Constants::RARITY_C:
|
|
return 2;
|
|
case Constants::RARITY_U:
|
|
return 3;
|
|
case Constants::RARITY_R:
|
|
return 4;
|
|
case Constants::RARITY_M:
|
|
return 5;
|
|
case Constants::RARITY_S:
|
|
return 6;
|
|
}
|
|
}
|
|
|
|
bool WCSortRarity::operator()(const MTGCard*l, const MTGCard*r)
|
|
{
|
|
if (!l || !r || !l->data || !r->data) return false;
|
|
return (rareToInt(l->getRarity()) < rareToInt(r->getRarity()));
|
|
}
|
|
|
|
bool WCSortAlpha::operator()(const MTGCard*l, const MTGCard*r)
|
|
{
|
|
if (!l || !r || !l->data || !r->data) return false;
|
|
string ln = l->data->getLCName();
|
|
string rn = r->data->getLCName();
|
|
if (ln == rn) return l->getMTGId() < r->getMTGId();
|
|
return (ln < rn);
|
|
}
|
|
|
|
bool WCSortCollector::operator()(const MTGCard*l, const MTGCard*r)
|
|
{
|
|
if (!l || !r || !l->data || !r->data) return false;
|
|
|
|
if (l->setId != r->setId) return (l->setId < r->setId);
|
|
|
|
int lc, rc;
|
|
lc = l->data->countColors();
|
|
rc = r->data->countColors();
|
|
if (lc == 0) lc = 999;
|
|
if (rc == 0) rc = 999;
|
|
|
|
int isW = (int) l->data->hasColor(Constants::MTG_COLOR_WHITE) - (int) r->data->hasColor(Constants::MTG_COLOR_WHITE);
|
|
int isU = (int) l->data->hasColor(Constants::MTG_COLOR_BLUE) - (int) r->data->hasColor(Constants::MTG_COLOR_BLUE);
|
|
int isB = (int) l->data->hasColor(Constants::MTG_COLOR_BLACK) - (int) r->data->hasColor(Constants::MTG_COLOR_BLACK);
|
|
int isR = (int) l->data->hasColor(Constants::MTG_COLOR_RED) - (int) r->data->hasColor(Constants::MTG_COLOR_RED);
|
|
int isG = (int) l->data->hasColor(Constants::MTG_COLOR_GREEN) - (int) r->data->hasColor(Constants::MTG_COLOR_GREEN);
|
|
int isArt = (int) l->data->hasType(Subtypes::TYPE_ARTIFACT) - (int) r->data->hasType(Subtypes::TYPE_ARTIFACT);
|
|
int isLand = (int) l->data->hasType(Subtypes::TYPE_LAND) - (int) r->data->hasType(Subtypes::TYPE_LAND);
|
|
|
|
//Nested if hell. TODO: Farm these out to their own objects as a user-defined filter/sort system.
|
|
if (!isLand)
|
|
{
|
|
int isBasic = (int) l->data->hasType("Basic") - (int) r->data->hasType("Basic");
|
|
if (!isBasic)
|
|
{
|
|
if (!isArt)
|
|
{
|
|
if (lc == rc)
|
|
{
|
|
if (!isG)
|
|
{
|
|
if (!isR)
|
|
{
|
|
if (!isB)
|
|
{
|
|
if (!isU)
|
|
{
|
|
if (!isW)
|
|
{
|
|
string ln = l->data->getLCName();
|
|
string rn = r->data->getLCName();
|
|
if (ln.substr(0, 4) == "the ") ln = ln.substr(4);
|
|
if (rn.substr(0, 4) == "the ") rn = rn.substr(4);
|
|
return (ln < rn);
|
|
}
|
|
return (isW < 0);
|
|
}
|
|
return (isU < 0);
|
|
}
|
|
return (isB < 0);
|
|
}
|
|
return (isR < 0);
|
|
}
|
|
return (isG < 0);
|
|
}
|
|
return (lc < rc);
|
|
}
|
|
return (isArt < 0);
|
|
}
|
|
else
|
|
return (isBasic < 0);
|
|
}
|
|
return (isLand < 0);
|
|
}
|