Merge branch 'Cockatrice:master' into master

This commit is contained in:
Bruno Alexandre Rosa 2026-03-04 17:37:58 -03:00 committed by GitHub
commit 01cf650ca4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 180 additions and 132 deletions

View file

@ -13,9 +13,10 @@
CardDragItem::CardDragItem(CardItem *_item,
int _id,
const QPointF &_hotSpot,
bool _faceDown,
bool _forceFaceDown,
AbstractCardDragItem *parentDrag)
: AbstractCardDragItem(_item, _hotSpot, parentDrag), id(_id), faceDown(_faceDown), occupied(false), currentZone(0)
: AbstractCardDragItem(_item, _hotSpot, parentDrag), id(_id), forceFaceDown(_forceFaceDown), occupied(false),
currentZone(0)
{
}

View file

@ -16,7 +16,7 @@ class CardDragItem : public AbstractCardDragItem
Q_OBJECT
private:
int id;
bool faceDown;
bool forceFaceDown;
bool occupied;
CardZone *currentZone;
@ -24,15 +24,15 @@ public:
CardDragItem(CardItem *_item,
int _id,
const QPointF &_hotSpot,
bool _faceDown,
bool _forceFaceDown,
AbstractCardDragItem *parentDrag = 0);
int getId() const
{
return id;
}
bool getFaceDown() const
bool isForceFaceDown() const
{
return faceDown;
return forceFaceDown;
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
void updatePosition(const QPointF &cursorScenePos) override;

View file

@ -212,10 +212,12 @@ void CardItem::setAttachedTo(CardItem *_attachedTo)
}
}
/**
* @brief Resets the fields that should be reset after a zone transition
*/
void CardItem::resetState(bool keepAnnotations)
{
attacking = false;
facedown = false;
counters.clear();
pt.clear();
if (!keepAnnotations) {
@ -251,10 +253,10 @@ void CardItem::processCardInfo(const ServerInfo_Card &_info)
setDoesntUntap(_info.doesnt_untap());
}
CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown)
CardDragItem *CardItem::createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool forceFaceDown)
{
deleteDragItem();
dragItem = new CardDragItem(this, _id, _pos, faceDown);
dragItem = new CardDragItem(this, _id, _pos, forceFaceDown);
dragItem->setVisible(false);
scene()->addItem(dragItem);
dragItem->updatePosition(_scenePos);
@ -352,7 +354,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
// Use the buttonDownPos to align the hot spot with the position when
// the user originally clicked
createDragItem(id, event->buttonDownPos(Qt::LeftButton), event->scenePos(), facedown || forceFaceDown);
createDragItem(id, event->buttonDownPos(Qt::LeftButton), event->scenePos(), forceFaceDown);
dragItem->grabMouse();
int childIndex = 0;
@ -366,8 +368,7 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
childPos = card->pos() - pos();
else
childPos = QPointF(childIndex * CARD_WIDTH / 2, 0);
CardDragItem *drag =
new CardDragItem(card, card->getId(), childPos, card->getFaceDown() || forceFaceDown, dragItem);
CardDragItem *drag = new CardDragItem(card, card->getId(), childPos, forceFaceDown, dragItem);
drag->setPos(dragItem->pos() + childPos);
scene()->addItem(drag);
}

View file

@ -142,7 +142,7 @@ public:
void processCardInfo(const ServerInfo_Card &_info);
bool animationEvent();
CardDragItem *createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool faceDown);
CardDragItem *createDragItem(int _id, const QPointF &_pos, const QPointF &_scenePos, bool forceFaceDown);
void deleteDragItem();
void drawArrow(const QColor &arrowColor);
void drawAttachArrow();

View file

@ -314,9 +314,17 @@ void MessageLogWidget::logMoveCard(Player *player,
finalStr = tr("%1 puts %2 into play%3.");
}
} else if (targetZoneName == GRAVE_ZONE_NAME) {
finalStr = tr("%1 puts %2%3 into their graveyard.");
if (card->getFaceDown()) {
finalStr = tr("%1 puts %2%3 into their graveyard face down.");
} else {
finalStr = tr("%1 puts %2%3 into their graveyard.");
}
} else if (targetZoneName == EXILE_ZONE_NAME) {
finalStr = tr("%1 exiles %2%3.");
if (card->getFaceDown()) {
finalStr = tr("%1 exiles %2%3 face down.");
} else {
finalStr = tr("%1 exiles %2%3.");
}
} else if (targetZoneName == HAND_ZONE_NAME) {
finalStr = tr("%1 moves %2%3 to their hand.");
} else if (targetZoneName == DECK_ZONE_NAME) {
@ -335,10 +343,18 @@ void MessageLogWidget::logMoveCard(Player *player,
finalStr = tr("%1 moves %2%3 to sideboard.");
} else if (targetZoneName == STACK_ZONE_NAME) {
soundEngine->playSound("play_card");
finalStr = tr("%1 plays %2%3.");
if (card->getFaceDown()) {
finalStr = tr("%1 plays %2%3 face down.");
} else {
finalStr = tr("%1 plays %2%3.");
}
} else {
fourthArg = targetZoneName;
finalStr = tr("%1 moves %2%3 to custom zone '%4'.");
if (card->getFaceDown()) {
finalStr = tr("%1 moves %2%3 to custom zone '%4' face down.");
} else {
finalStr = tr("%1 moves %2%3 to custom zone '%4'.");
}
}
QString message = finalStr.arg(sanitizeHtml(player->getPlayerInfo()->getName()), cardStr, nameFrom.second);

View file

@ -424,37 +424,15 @@ void PlayerActions::actMoveTopCardToExile()
void PlayerActions::actMoveTopCardsToGrave()
{
const int maxCards = player->getDeckZone()->getCards().size();
if (maxCards == 0) {
return;
}
bool ok;
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move top cards to grave"),
tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1,
maxCards, 1, &ok);
if (!ok) {
return;
} else if (number > maxCards) {
number = maxCards;
}
defaultNumberTopCards = number;
Command_MoveCard cmd;
cmd.set_start_zone("deck");
cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone("grave");
cmd.set_x(0);
cmd.set_y(0);
for (int i = number - 1; i >= 0; --i) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(i);
}
sendGameCommand(cmd);
moveTopCardsTo("grave", tr("grave"));
}
void PlayerActions::actMoveTopCardsToExile()
{
moveTopCardsTo("rfg", tr("exile"));
}
void PlayerActions::moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName)
{
const int maxCards = player->getDeckZone()->getCards().size();
if (maxCards == 0) {
@ -462,12 +440,14 @@ void PlayerActions::actMoveTopCardsToExile()
}
bool ok;
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move top cards to exile"),
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move top cards to %1").arg(zoneDisplayName),
tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberTopCards, 1,
maxCards, 1, &ok);
if (!ok) {
return;
} else if (number > maxCards) {
}
if (number > maxCards) {
number = maxCards;
}
defaultNumberTopCards = number;
@ -475,7 +455,7 @@ void PlayerActions::actMoveTopCardsToExile()
Command_MoveCard cmd;
cmd.set_start_zone("deck");
cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone("rfg");
cmd.set_target_zone(targetZone.toStdString());
cmd.set_x(0);
cmd.set_y(0);
@ -628,37 +608,15 @@ void PlayerActions::actMoveBottomCardToExile()
void PlayerActions::actMoveBottomCardsToGrave()
{
const int maxCards = player->getDeckZone()->getCards().size();
if (maxCards == 0) {
return;
}
bool ok;
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move bottom cards to grave"),
tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1,
maxCards, 1, &ok);
if (!ok) {
return;
} else if (number > maxCards) {
number = maxCards;
}
defaultNumberBottomCards = number;
Command_MoveCard cmd;
cmd.set_start_zone("deck");
cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone("grave");
cmd.set_x(0);
cmd.set_y(0);
for (int i = maxCards - number; i < maxCards; ++i) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(i);
}
sendGameCommand(cmd);
moveBottomCardsTo("grave", tr("grave"));
}
void PlayerActions::actMoveBottomCardsToExile()
{
moveBottomCardsTo("rfg", tr("exile"));
}
void PlayerActions::moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName)
{
const int maxCards = player->getDeckZone()->getCards().size();
if (maxCards == 0) {
@ -666,12 +624,14 @@ void PlayerActions::actMoveBottomCardsToExile()
}
bool ok;
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move bottom cards to exile"),
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Move bottom cards to %1").arg(zoneDisplayName),
tr("Number of cards: (max. %1)").arg(maxCards), defaultNumberBottomCards, 1,
maxCards, 1, &ok);
if (!ok) {
return;
} else if (number > maxCards) {
}
if (number > maxCards) {
number = maxCards;
}
defaultNumberBottomCards = number;
@ -679,7 +639,7 @@ void PlayerActions::actMoveBottomCardsToExile()
Command_MoveCard cmd;
cmd.set_start_zone("deck");
cmd.set_target_player_id(player->getPlayerInfo()->getId());
cmd.set_target_zone("rfg");
cmd.set_target_zone(targetZone.toStdString());
cmd.set_x(0);
cmd.set_y(0);

View file

@ -180,6 +180,9 @@ private:
FilterString movingCardsUntilFilter;
int movingCardsUntilCounter = 0;
void moveTopCardsTo(const QString &targetZone, const QString &zoneDisplayName);
void moveBottomCardsTo(const QString &targetZone, const QString &zoneDisplayName);
void createCard(const CardItem *sourceCard,
const QString &dbCardName,
CardRelationType attach = CardRelationType::DoesNotAttach,

View file

@ -43,8 +43,10 @@ void CardZoneLogic::addCard(CardItem *card, const bool reorganize, const int x,
for (auto *view : views) {
if (qobject_cast<ZoneViewZoneLogic *>(view->getLogic())->prepareAddCard(x)) {
view->getLogic()->addCard(new CardItem(player, nullptr, card->getCardRef(), card->getId()), reorganize, x,
y);
auto copy = new CardItem(player, nullptr, card->getCardRef(), card->getId());
copy->setFaceDown(card->getFaceDown());
view->getLogic()->addCard(copy, reorganize, x, y);
}
}

View file

@ -68,8 +68,13 @@ void PileZone::handleDropEvent(const QList<CardDragItem *> &dragItems, CardZoneL
cmd.set_x(0);
cmd.set_y(0);
for (int i = 0; i < dragItems.size(); ++i)
cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId());
for (int i = 0; i < dragItems.size(); ++i) {
auto cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(dragItems[i]->getId());
if (dragItems[i]->isForceFaceDown()) {
cardToMove->set_face_down(true);
}
}
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
}
@ -101,12 +106,12 @@ void PileZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
if (getLogic()->getCards().isEmpty())
return;
bool faceDown = event->modifiers().testFlag(Qt::ShiftModifier);
bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier);
bool bottomCard = event->modifiers().testFlag(Qt::ControlModifier);
CardItem *card = bottomCard ? getLogic()->getCards().last() : getLogic()->getCards().first();
const int cardid =
getLogic()->contentsKnown() ? card->getId() : (bottomCard ? getLogic()->getCards().size() - 1 : 0);
CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), faceDown);
CardDragItem *drag = card->createDragItem(cardid, event->pos(), event->scenePos(), forceFaceDown);
drag->grabMouse();
setCursor(Qt::OpenHandCursor);
}

View file

@ -78,7 +78,11 @@ void StackZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
for (CardDragItem *item : dragItems) {
if (item) {
cmd.mutable_cards_to_move()->add_card()->set_card_id(item->getId());
auto cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(item->getId());
if (item->isForceFaceDown()) {
cardToMove->set_face_down(true);
}
}
}

View file

@ -134,8 +134,10 @@ void TableZone::handleDropEventByGrid(const QList<CardDragItem *> &dragItems,
for (const auto &item : dragItems) {
CardToMove *ctm = cmd.mutable_cards_to_move()->add_card();
ctm->set_card_id(item->getId());
ctm->set_face_down(item->getFaceDown());
if (startZone->getName() != getLogic()->getName() && !item->getFaceDown()) {
if (item->isForceFaceDown()) {
ctm->set_face_down(true);
}
if (startZone->getName() != getLogic()->getName() && !item->isForceFaceDown()) {
const auto &card = item->getItem()->getCard();
if (card) {
ctm->set_pt(card.getInfo().getPowTough().toStdString());

View file

@ -73,7 +73,10 @@ void ZoneViewZone::initializeCards(const QList<const ServerInfo_Card *> &cardLis
for (int i = 0; i < cardList.size(); ++i) {
auto card = cardList[i];
CardRef cardRef = {QString::fromStdString(card->name()), QString::fromStdString(card->provider_id())};
getLogic()->addCard(new CardItem(getLogic()->getPlayer(), this, cardRef, card->id()), false, i);
auto copy = new CardItem(getLogic()->getPlayer(), this, cardRef, card->id());
copy->setFaceDown(card->face_down());
getLogic()->addCard(copy, false, i);
}
reorganizeCards();
} else if (!qobject_cast<ZoneViewZoneLogic *>(getLogic())->getOriginalZone()->contentsKnown()) {
@ -91,8 +94,10 @@ void ZoneViewZone::initializeCards(const QList<const ServerInfo_Card *> &cardLis
int number = numberCards == -1 ? c.size() : (numberCards < c.size() ? numberCards : c.size());
for (int i = 0; i < number; i++) {
CardItem *card = c.at(i);
getLogic()->addCard(new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId()), false,
i);
auto copy = new CardItem(getLogic()->getPlayer(), this, card->getCardRef(), card->getId());
copy->setFaceDown(card->getFaceDown());
getLogic()->addCard(copy, false, i);
}
reorganizeCards();
}
@ -107,6 +112,7 @@ void ZoneViewZone::zoneDumpReceived(const Response &r)
auto cardName = QString::fromStdString(cardInfo.name());
auto cardProviderId = QString::fromStdString(cardInfo.provider_id());
auto card = new CardItem(getLogic()->getPlayer(), this, {cardName, cardProviderId}, cardInfo.id(), getLogic());
card->setFaceDown(cardInfo.face_down());
getLogic()->rawInsertCard(card, i);
}
@ -279,8 +285,13 @@ void ZoneViewZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
cmd.set_y(0);
cmd.set_is_reversed(qobject_cast<ZoneViewZoneLogic *>(getLogic())->getIsReversed());
for (int i = 0; i < dragItems.size(); ++i)
cmd.mutable_cards_to_move()->add_card()->set_card_id(dragItems[i]->getId());
for (int i = 0; i < dragItems.size(); ++i) {
auto cardToMove = cmd.mutable_cards_to_move()->add_card();
cardToMove->set_card_id(dragItems[i]->getId());
if (dragItems[i]->isForceFaceDown()) {
cardToMove->set_face_down(true);
}
}
getLogic()->getPlayer()->getPlayerActions()->sendGameCommand(cmd);
}

View file

@ -8,7 +8,7 @@ CardInfoPictureArtCropWidget::CardInfoPictureArtCropWidget(QWidget *parent)
hide();
}
QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &targetSize)
QPixmap CardInfoPictureArtCropWidget::getBackground()
{
// Load the full-resolution card image, not a pre-scaled one
QPixmap fullResPixmap;
@ -17,10 +17,8 @@ QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &target
} else {
CardPictureLoader::getCardBackPixmap(fullResPixmap, QSize(745, 1040));
}
// Fail-safe if loading failed
if (fullResPixmap.isNull()) {
return QPixmap(targetSize);
return QPixmap(); // return null qpixmap
}
const QSize sz = fullResPixmap.size();
@ -33,9 +31,7 @@ QPixmap CardInfoPictureArtCropWidget::getProcessedBackground(const QSize &target
foilRect = foilRect.intersected(fullResPixmap.rect()); // always clamp to source bounds
// Crop first, then scale for best quality
// return full resolution image crop
QPixmap cropped = fullResPixmap.copy(foilRect);
QPixmap scaled = cropped.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
return scaled;
return cropped;
}

View file

@ -16,8 +16,8 @@ class CardInfoPictureArtCropWidget : public CardInfoPictureWidget
public:
explicit CardInfoPictureArtCropWidget(QWidget *parent = nullptr);
// Returns a processed (cropped & scaled) version of the pixmap
QPixmap getProcessedBackground(const QSize &targetSize);
// Returns a cropped version of the pixmap
QPixmap getBackground();
};
#endif // CARD_INFO_PICTURE_ART_CROP_WIDGET_H

View file

@ -58,20 +58,24 @@ void HomeWidget::initializeBackgroundFromSource()
auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
cardChangeTimer->stop();
switch (backgroundSourceType) {
case BackgroundSources::Theme:
cardChangeTimer->stop();
background = QPixmap("theme:backgrounds/home");
backgroundSourceDeck = DeckList();
backgroundSourceCard->setCard(ExactCard());
updateButtonsToBackgroundColor();
update();
break;
case BackgroundSources::RandomCardArt:
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
backgroundSourceDeck = DeckList();
updateRandomCard();
onBackgroundShuffleFrequencyChanged();
break;
case BackgroundSources::DeckFileArt:
loadBackgroundSourceDeck();
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
updateRandomCard();
onBackgroundShuffleFrequencyChanged();
break;
}
}
@ -83,6 +87,20 @@ void HomeWidget::loadBackgroundSourceDeck()
backgroundSourceDeck = deckOpt.has_value() ? deckOpt.value().deckList : DeckList();
}
void HomeWidget::setRandomCard(ExactCard &newCard)
{
static constexpr int ATTEMPTS = 10;
for (int i = 0; i < ATTEMPTS; ++i) {
ExactCard tmpCard = CardDatabaseManager::query()->getRandomCard();
if (tmpCard != backgroundSourceCard->getCard() && tmpCard.getCardPtr()->getProperty("layout") == "normal" &&
tmpCard.getPrinting().getSet() != nullptr) {
newCard = tmpCard;
return;
}
}
qWarning() << "failed to set random card image after" << ATTEMPTS << "attempts";
}
void HomeWidget::updateRandomCard()
{
auto backgroundSourceType = BackgroundSources::fromId(SettingsCache::instance().getHomeTabBackgroundSource());
@ -93,10 +111,7 @@ void HomeWidget::updateRandomCard()
case BackgroundSources::Theme:
break;
case BackgroundSources::RandomCardArt:
do {
newCard = CardDatabaseManager::query()->getRandomCard();
} while (newCard == backgroundSourceCard->getCard() &&
newCard.getCardPtr()->getProperty("layout") != "normal");
setRandomCard(newCard);
break;
case BackgroundSources::DeckFileArt:
QList<CardRef> cardRefs = backgroundSourceDeck.getCardRefList();
@ -124,25 +139,20 @@ void HomeWidget::updateRandomCard()
connect(newCard.getCardPtr().data(), &CardInfo::pixmapUpdated, this, &HomeWidget::updateBackgroundProperties);
backgroundSourceCard->setCard(newCard);
background = backgroundSourceCard->getProcessedBackground(size());
if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() <= 0) {
cardChangeTimer->stop();
}
background = backgroundSourceCard->getBackground();
}
void HomeWidget::onBackgroundShuffleFrequencyChanged()
{
cardChangeTimer->stop();
if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() <= 0) {
return;
if (SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() > 0) {
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
}
cardChangeTimer->start(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency() * 1000);
}
void HomeWidget::updateBackgroundProperties()
{
background = backgroundSourceCard->getProcessedBackground(size());
background = backgroundSourceCard->getBackground();
updateButtonsToBackgroundColor();
update(); // Triggers repaint
}
@ -300,14 +310,17 @@ void HomeWidget::paintEvent(QPaintEvent *event)
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
background = background.scaled(size(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
if (!background.isNull()) {
QSize widgetSize = size() * devicePixelRatio();
QPixmap toDraw = background.scaled(widgetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
// Draw already-scaled background centered
QSize widgetSize = size();
QSize bgSize = background.size();
QPoint topLeft((widgetSize.width() - bgSize.width()) / 2, (widgetSize.height() - bgSize.height()) / 2);
// Draw scaled background centered
QSize bgSize = toDraw.size();
QPoint topLeft((widgetSize.width() - bgSize.width()) / (devicePixelRatio() * 2), // undo scaling for painter
(widgetSize.height() - bgSize.height()) / (devicePixelRatio() * 2));
painter.drawPixmap(topLeft, background);
painter.drawPixmap(topLeft, toDraw);
}
// Draw translucent black overlay with rounded corners
QRectF overlayRect(5, 5, width() - 10, height() - 10);
@ -321,8 +334,11 @@ void HomeWidget::paintEvent(QPaintEvent *event)
QString cardName;
ExactCard card = backgroundSourceCard->getCard();
if (card) {
cardName = card.getCardPtr()->getName() + " (" + card.getPrinting().getSet()->getCorrectedShortName() + ") " +
card.getPrinting().getProperty("num");
cardName = card.getCardPtr()->getName();
if (card.getPrinting().getSet() != nullptr) {
cardName += " (" + card.getPrinting().getSet()->getCorrectedShortName() + ") " +
card.getPrinting().getProperty("num");
}
}
if (!cardName.isEmpty() && SettingsCache::instance().getHomeTabDisplayCardName()) {

View file

@ -45,6 +45,7 @@ private:
QPair<QColor, QColor> gradientColors;
HomeStyledButton *connectButton;
void setRandomCard(ExactCard &newCard);
void loadBackgroundSourceDeck();
};