mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-01-24 18:34:49 -08:00
Compare commits
13 Commits
2026-01-01
...
tooomm-tra
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4373ab5e89 | ||
|
|
b748009b52 | ||
|
|
0fef7c149b | ||
|
|
69a3cecfe7 | ||
|
|
089342ee75 | ||
|
|
4fbb9d9682 | ||
|
|
84aefda486 | ||
|
|
73cc0541f5 | ||
|
|
bcf3939fee | ||
|
|
2e6f1128bb | ||
|
|
bbd8671e6e | ||
|
|
84e6907fa9 | ||
|
|
93a4647b04 |
@@ -1,11 +1,13 @@
|
||||
@page deck_search_syntax_help Deck Search Syntax Help
|
||||
|
||||
## Deck Search Syntax Help
|
||||
-----
|
||||
|
||||
The search bar recognizes a set of special commands.<br>
|
||||
In this list of examples below, each entry has an explanation and can be clicked to test the query. Note that all
|
||||
searches are case insensitive.
|
||||
|
||||
<dl>
|
||||
|
||||
<dt>Display Name (The deck name, or the filename if the deck name isn't set):</dt>
|
||||
<dd>[red deck wins](#red deck wins) <small>(Any deck with a display name containing the words red, deck, and wins)</small></dd>
|
||||
<dd>["red deck wins"](#%22red deck wins%22) <small>(Any deck with a display name containing the exact phrase "red deck wins")</small></dd>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
@page search_syntax_help Search Syntax Help
|
||||
|
||||
## Search Syntax Help
|
||||
-----
|
||||
|
||||
The search bar recognizes a set of special commands similar to some other card databases.<br>
|
||||
In this list of examples below, each entry has an explanation and can be clicked to test the query. Note that all searches are case insensitive.
|
||||
|
||||
<dl>
|
||||
|
||||
<dt>Name:</dt>
|
||||
<dd>[birds of paradise](#birds of paradise) <small>(Any card name containing the words birds, of, and paradise)</small></dd>
|
||||
<dd>["birds of paradise"](#%22birds of paradise%22) <small>(Any card name containing the exact phrase "birds of paradise")</small></dd>
|
||||
|
||||
@@ -660,6 +660,12 @@ private:
|
||||
{"Player/aMulligan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Mulligan"),
|
||||
parseSequenceString("Ctrl+M"),
|
||||
ShortcutGroup::Drawing)},
|
||||
{"Player/aMulliganSame", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Mulligan (Same hand size)"),
|
||||
parseSequenceString("Ctrl+Shift+M"),
|
||||
ShortcutGroup::Drawing)},
|
||||
{"Player/aMulliganMinusOne", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Mulligan (Hand size - 1)"),
|
||||
parseSequenceString("Ctrl+Shift+Alt+M"),
|
||||
ShortcutGroup::Drawing)},
|
||||
{"Player/aDrawCard", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Draw a Card"),
|
||||
parseSequenceString("Ctrl+D"),
|
||||
ShortcutGroup::Drawing)},
|
||||
|
||||
@@ -396,7 +396,7 @@ void CardMenu::addRelatedCardActions()
|
||||
relatedCardName = relatedCard.getName(); // "name"
|
||||
}
|
||||
|
||||
QString text = tr("Token: ");
|
||||
QString text = tr("Token") + ": ";
|
||||
if (cardRelation->getDoesAttach()) {
|
||||
text +=
|
||||
tr(cardRelation->getDoesTransform() ? "Transform into " : "Attach to ") + "\"" + relatedCardName + "\"";
|
||||
@@ -502,4 +502,4 @@ void CardMenu::setShortcutsActive()
|
||||
aRemoveCounter[i]->setShortcuts(shortcuts.getShortcut("Player/aRC" + colorWords[i]));
|
||||
aSetCounter[i]->setShortcuts(shortcuts.getShortcut("Player/aSC" + colorWords[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,16 @@ HandMenu::HandMenu(Player *_player, PlayerActions *actions, QWidget *parent) : T
|
||||
connect(aMulligan, &QAction::triggered, actions, &PlayerActions::actMulligan);
|
||||
addAction(aMulligan);
|
||||
|
||||
// Mulligan same size
|
||||
aMulliganSame = new QAction(this);
|
||||
connect(aMulliganSame, &QAction::triggered, actions, &PlayerActions::actMulliganSameSize);
|
||||
addAction(aMulliganSame);
|
||||
|
||||
// Mulligan -1
|
||||
aMulliganMinusOne = new QAction(this);
|
||||
connect(aMulliganMinusOne, &QAction::triggered, actions, &PlayerActions::actMulliganMinusOne);
|
||||
addAction(aMulliganMinusOne);
|
||||
|
||||
addSeparator();
|
||||
|
||||
mMoveHandMenu = addTearOffMenu(QString());
|
||||
@@ -104,7 +114,9 @@ void HandMenu::retranslateUi()
|
||||
aSortHandByType->setText(tr("Type"));
|
||||
aSortHandByManaValue->setText(tr("Mana Value"));
|
||||
|
||||
aMulligan->setText(tr("Take &mulligan"));
|
||||
aMulligan->setText(tr("Take &mulligan (Choose hand size)"));
|
||||
aMulliganSame->setText(tr("Take mulligan (Same hand size)"));
|
||||
aMulliganMinusOne->setText(tr("Take mulligan (Hand size - 1)"));
|
||||
|
||||
mMoveHandMenu->setTitle(tr("&Move hand to..."));
|
||||
aMoveHandToTopLibrary->setText(tr("&Top of library"));
|
||||
@@ -128,6 +140,8 @@ void HandMenu::setShortcutsActive()
|
||||
aSortHandByType->setShortcuts(shortcuts.getShortcut("Player/aSortHandByType"));
|
||||
aSortHandByManaValue->setShortcuts(shortcuts.getShortcut("Player/aSortHandByManaValue"));
|
||||
aMulligan->setShortcuts(shortcuts.getShortcut("Player/aMulligan"));
|
||||
aMulliganSame->setShortcuts(shortcuts.getShortcut("Player/aMulliganSame"));
|
||||
aMulliganMinusOne->setShortcuts(shortcuts.getShortcut("Player/aMulliganMinusOne"));
|
||||
aRevealHandToAll->setShortcuts(shortcuts.getShortcut("Player/aRevealHandToAll"));
|
||||
aRevealRandomHandCardToAll->setShortcuts(shortcuts.getShortcut("Player/aRevealRandomHandCardToAll"));
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ private:
|
||||
|
||||
QAction *aViewHand = nullptr;
|
||||
QAction *aMulligan = nullptr;
|
||||
QAction *aMulliganSame = nullptr;
|
||||
QAction *aMulliganMinusOne = nullptr;
|
||||
|
||||
QMenu *mSortHand = nullptr;
|
||||
QAction *aSortHandByName = nullptr;
|
||||
|
||||
@@ -310,28 +310,48 @@ void PlayerActions::actMulligan()
|
||||
{
|
||||
int startSize = SettingsCache::instance().getStartingHandSize();
|
||||
int handSize = player->getHandZone()->getCards().size();
|
||||
int deckSize = player->getDeckZone()->getCards().size() + handSize; // hand is shuffled back into the deck
|
||||
int deckSize = player->getDeckZone()->getCards().size() + handSize;
|
||||
|
||||
bool ok;
|
||||
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Draw hand"),
|
||||
tr("Number of cards: (max. %1)").arg(deckSize) + '\n' +
|
||||
tr("0 and lower are in comparison to current hand size"),
|
||||
startSize, -handSize, deckSize, 1, &ok);
|
||||
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
Command_Mulligan cmd;
|
||||
|
||||
if (number < 1) {
|
||||
if (handSize == 0) {
|
||||
return;
|
||||
}
|
||||
cmd.set_number(handSize + number);
|
||||
} else {
|
||||
cmd.set_number(number);
|
||||
number = handSize + number;
|
||||
}
|
||||
|
||||
doMulligan(number);
|
||||
SettingsCache::instance().setStartingHandSize(number);
|
||||
}
|
||||
|
||||
void PlayerActions::actMulliganSameSize()
|
||||
{
|
||||
int handSize = player->getHandZone()->getCards().size();
|
||||
doMulligan(handSize);
|
||||
}
|
||||
|
||||
void PlayerActions::actMulliganMinusOne()
|
||||
{
|
||||
int handSize = player->getHandZone()->getCards().size();
|
||||
int targetSize = qMax(1, handSize - 1);
|
||||
doMulligan(targetSize);
|
||||
}
|
||||
|
||||
void PlayerActions::doMulligan(int number)
|
||||
{
|
||||
if (number < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Command_Mulligan cmd;
|
||||
cmd.set_number(number);
|
||||
sendGameCommand(cmd);
|
||||
if (startSize != number) {
|
||||
SettingsCache::instance().setStartingHandSize(number);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerActions::actDrawCards()
|
||||
|
||||
@@ -85,6 +85,9 @@ public slots:
|
||||
void actDrawCards();
|
||||
void actUndoDraw();
|
||||
void actMulligan();
|
||||
void actMulliganSameSize();
|
||||
void actMulliganMinusOne();
|
||||
void doMulligan(int number);
|
||||
|
||||
void actPlay();
|
||||
void actPlayFacedown();
|
||||
|
||||
@@ -168,7 +168,7 @@ void DrawProbabilityWidget::updateFilterOptions()
|
||||
QMap<QString, int> categoryCounts;
|
||||
int totalDeckCards = 0;
|
||||
|
||||
const auto nodes = analyzer->getModel()->getDeckList()->getCardNodes();
|
||||
const auto nodes = analyzer->getModel()->getCardNodes();
|
||||
for (auto *node : nodes) {
|
||||
CardInfoPtr info = CardDatabaseManager::query()->getCard({node->getName()}).getCardPtr();
|
||||
if (!info) {
|
||||
|
||||
@@ -578,18 +578,19 @@ void DeckEditorDeckDockWidget::expandAll()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of all the currently selected card nodes in the decklist table.
|
||||
* Gets the source index of all the currently selected card nodes in the decklist table.
|
||||
* The list is in reverse order of the visual selection, so that rows can be deleted while iterating over them.
|
||||
*
|
||||
* @return A model index list containing all selected card nodes
|
||||
* @return A list containing the source indices of all selected card nodes
|
||||
*/
|
||||
QModelIndexList DeckEditorDeckDockWidget::getSelectedCardNodes() const
|
||||
QModelIndexList DeckEditorDeckDockWidget::getSelectedCardNodeSourceIndices() const
|
||||
{
|
||||
auto selectedRows = deckView->selectionModel()->selectedRows();
|
||||
|
||||
const auto notLeafNode = [this](const QModelIndex &index) {
|
||||
return getModel()->hasChildren(proxy->mapToSource(index));
|
||||
};
|
||||
const auto mapToSource = [this](const QModelIndex &index) { return proxy->mapToSource(index); };
|
||||
std::transform(selectedRows.begin(), selectedRows.end(), selectedRows.begin(), mapToSource);
|
||||
|
||||
const auto notLeafNode = [this](const QModelIndex &sourceIndex) { return getModel()->hasChildren(sourceIndex); };
|
||||
selectedRows.erase(std::remove_if(selectedRows.begin(), selectedRows.end(), notLeafNode), selectedRows.end());
|
||||
|
||||
std::reverse(selectedRows.begin(), selectedRows.end());
|
||||
@@ -608,10 +609,10 @@ void DeckEditorDeckDockWidget::actAddCard(const ExactCard &card, const QString &
|
||||
|
||||
void DeckEditorDeckDockWidget::actIncrementSelection()
|
||||
{
|
||||
auto selectedRows = getSelectedCardNodes();
|
||||
auto selectedRows = getSelectedCardNodeSourceIndices();
|
||||
|
||||
for (const auto &index : selectedRows) {
|
||||
offsetCountAtIndex(index, true);
|
||||
for (const auto &sourceIndex : selectedRows) {
|
||||
offsetCountAtIndex(sourceIndex, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,7 +631,7 @@ void DeckEditorDeckDockWidget::actSwapCard(const ExactCard &card, const QString
|
||||
|
||||
void DeckEditorDeckDockWidget::actSwapSelection()
|
||||
{
|
||||
auto selectedRows = getSelectedCardNodes();
|
||||
auto selectedRows = getSelectedCardNodeSourceIndices();
|
||||
|
||||
// hack to maintain the old reselection behavior when currently selected row of a single-selection gets deleted
|
||||
// TODO: remove the hack and also handle reselection when all rows of a multi-selection gets deleted
|
||||
@@ -638,8 +639,8 @@ void DeckEditorDeckDockWidget::actSwapSelection()
|
||||
deckView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
}
|
||||
|
||||
for (const auto ¤tIndex : selectedRows) {
|
||||
deckStateManager->swapCardAtIndex(currentIndex);
|
||||
for (const auto &sourceIndex : selectedRows) {
|
||||
deckStateManager->swapCardAtIndex(sourceIndex);
|
||||
}
|
||||
|
||||
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
@@ -659,7 +660,7 @@ void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString z
|
||||
|
||||
void DeckEditorDeckDockWidget::actDecrementSelection()
|
||||
{
|
||||
auto selectedRows = getSelectedCardNodes();
|
||||
auto selectedRows = getSelectedCardNodeSourceIndices();
|
||||
|
||||
// hack to maintain the old reselection behavior when currently selected row of a single-selection gets deleted
|
||||
// TODO: remove the hack and also handle reselection when all rows of a multi-selection gets deleted
|
||||
@@ -667,8 +668,8 @@ void DeckEditorDeckDockWidget::actDecrementSelection()
|
||||
deckView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
}
|
||||
|
||||
for (const auto &index : selectedRows) {
|
||||
offsetCountAtIndex(index, false);
|
||||
for (const auto &sourceIndex : selectedRows) {
|
||||
offsetCountAtIndex(sourceIndex, false);
|
||||
}
|
||||
|
||||
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
@@ -676,7 +677,7 @@ void DeckEditorDeckDockWidget::actDecrementSelection()
|
||||
|
||||
void DeckEditorDeckDockWidget::actRemoveCard()
|
||||
{
|
||||
auto selectedRows = getSelectedCardNodes();
|
||||
auto selectedRows = getSelectedCardNodeSourceIndices();
|
||||
|
||||
// hack to maintain the old reselection behavior when currently selected row of a single-selection gets deleted
|
||||
// TODO: remove the hack and also handle reselection when all rows of a multi-selection gets deleted
|
||||
@@ -684,8 +685,8 @@ void DeckEditorDeckDockWidget::actRemoveCard()
|
||||
deckView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
}
|
||||
|
||||
for (const auto &row : selectedRows) {
|
||||
deckStateManager->removeCardAtIndex(row);
|
||||
for (const auto &sourceIndex : selectedRows) {
|
||||
deckStateManager->removeCardAtIndex(sourceIndex);
|
||||
}
|
||||
|
||||
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
@@ -693,7 +694,7 @@ void DeckEditorDeckDockWidget::actRemoveCard()
|
||||
|
||||
/**
|
||||
* @brief Increments or decrements the amount of the card node at the index by 1.
|
||||
* @param idx The proxy index
|
||||
* @param idx The source index
|
||||
* @param isIncrement If true, increments the count. If false, decrements the count
|
||||
*/
|
||||
void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, bool isIncrement)
|
||||
@@ -702,12 +703,10 @@ void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, bool i
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndex sourceIndex = proxy->mapToSource(idx);
|
||||
|
||||
if (isIncrement) {
|
||||
deckStateManager->incrementCountAtIndex(sourceIndex);
|
||||
deckStateManager->incrementCountAtIndex(idx);
|
||||
} else {
|
||||
deckStateManager->decrementCountAtIndex(sourceIndex);
|
||||
deckStateManager->decrementCountAtIndex(idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ private:
|
||||
QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard;
|
||||
|
||||
DeckListModel *getModel() const;
|
||||
[[nodiscard]] QModelIndexList getSelectedCardNodes() const;
|
||||
[[nodiscard]] QModelIndexList getSelectedCardNodeSourceIndices() const;
|
||||
void offsetCountAtIndex(const QModelIndex &idx, bool isIncrement);
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -49,7 +49,7 @@ DlgForgotPasswordReset::DlgForgotPasswordReset(QWidget *parent) : QDialog(parent
|
||||
playernameEdit->setMaxLength(MAX_NAME_LENGTH);
|
||||
playernameLabel->setBuddy(playernameEdit);
|
||||
|
||||
tokenLabel = new QLabel(tr("Token:"));
|
||||
tokenLabel = new QLabel(tr("Token") + ":");
|
||||
tokenEdit = new QLineEdit();
|
||||
tokenEdit->setMaxLength(MAX_NAME_LENGTH);
|
||||
tokenLabel->setBuddy(tokenLabel);
|
||||
|
||||
@@ -441,7 +441,7 @@ AppearanceSettingsPage::AppearanceSettingsPage()
|
||||
});
|
||||
|
||||
homeTabBackgroundShuffleFrequencySpinBox.setRange(0, 3600);
|
||||
homeTabBackgroundShuffleFrequencySpinBox.setSuffix(tr(" seconds"));
|
||||
homeTabBackgroundShuffleFrequencySpinBox.setSuffix(QString(" ") + tr("seconds"));
|
||||
homeTabBackgroundShuffleFrequencySpinBox.setValue(SettingsCache::instance().getHomeTabBackgroundShuffleFrequency());
|
||||
connect(&homeTabBackgroundShuffleFrequencySpinBox, qOverload<int>(&QSpinBox::valueChanged),
|
||||
&SettingsCache::instance(), &SettingsCache::setHomeTabBackgroundShuffleFrequency);
|
||||
|
||||
@@ -72,6 +72,12 @@ void AllZonesCardAmountWidget::adjustFontSize(int scalePercentage)
|
||||
repaint();
|
||||
}
|
||||
|
||||
void AllZonesCardAmountWidget::setAmounts(int mainboardAmount, int sideboardAmount)
|
||||
{
|
||||
buttonBoxMainboard->setAmount(mainboardAmount);
|
||||
buttonBoxSideboard->setAmount(sideboardAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the card count in the mainboard zone.
|
||||
*
|
||||
@@ -79,7 +85,7 @@ void AllZonesCardAmountWidget::adjustFontSize(int scalePercentage)
|
||||
*/
|
||||
int AllZonesCardAmountWidget::getMainboardAmount()
|
||||
{
|
||||
return buttonBoxMainboard->countCardsInZone(DECK_ZONE_MAIN);
|
||||
return buttonBoxMainboard->getAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +95,15 @@ int AllZonesCardAmountWidget::getMainboardAmount()
|
||||
*/
|
||||
int AllZonesCardAmountWidget::getSideboardAmount()
|
||||
{
|
||||
return buttonBoxSideboard->countCardsInZone(DECK_ZONE_SIDE);
|
||||
return buttonBoxSideboard->getAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the amount is at least one in either the mainboard or sideboard.
|
||||
*/
|
||||
bool AllZonesCardAmountWidget::isNonZero()
|
||||
{
|
||||
return getMainboardAmount() > 0 || getSideboardAmount() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,8 @@ public:
|
||||
const ExactCard &rootCard);
|
||||
int getMainboardAmount();
|
||||
int getSideboardAmount();
|
||||
bool isNonZero();
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
void enterEvent(QEnterEvent *event) override;
|
||||
#else
|
||||
@@ -31,6 +33,7 @@ public:
|
||||
|
||||
public slots:
|
||||
void adjustFontSize(int scalePercentage);
|
||||
void setAmounts(int mainboardAmount, int sideboardAmount);
|
||||
|
||||
private:
|
||||
QVBoxLayout *layout;
|
||||
|
||||
@@ -45,20 +45,28 @@ CardAmountWidget::CardAmountWidget(QWidget *parent,
|
||||
connect(decrementButton, &QPushButton::clicked, this, &CardAmountWidget::removePrintingSideboard);
|
||||
}
|
||||
|
||||
cardCountInZone = new QLabel(QString::number(countCardsInZone(zoneName)), this);
|
||||
cardCountInZone = new QLabel(QString::number(amount), this);
|
||||
cardCountInZone->setAlignment(Qt::AlignCenter);
|
||||
|
||||
layout->addWidget(decrementButton);
|
||||
layout->addWidget(cardCountInZone);
|
||||
layout->addWidget(incrementButton);
|
||||
|
||||
// React to model changes
|
||||
connect(deckStateManager, &DeckStateManager::cardModified, this, &CardAmountWidget::updateCardCount);
|
||||
|
||||
// Connect slider for dynamic font size adjustment
|
||||
connect(cardSizeSlider, &QSlider::valueChanged, this, &CardAmountWidget::adjustFontSize);
|
||||
}
|
||||
|
||||
int CardAmountWidget::getAmount()
|
||||
{
|
||||
return amount;
|
||||
}
|
||||
|
||||
void CardAmountWidget::setAmount(int _amount)
|
||||
{
|
||||
amount = _amount;
|
||||
updateCardCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles the painting of the widget, drawing a semi-transparent background.
|
||||
*
|
||||
@@ -124,7 +132,7 @@ void CardAmountWidget::adjustFontSize(int scalePercentage)
|
||||
*/
|
||||
void CardAmountWidget::updateCardCount()
|
||||
{
|
||||
cardCountInZone->setText("<font color='white'>" + QString::number(countCardsInZone(zoneName)) + "</font>");
|
||||
cardCountInZone->setText("<font color='white'>" + QString::number(amount) + "</font>");
|
||||
layout->invalidate();
|
||||
layout->activate();
|
||||
}
|
||||
@@ -169,8 +177,8 @@ void CardAmountWidget::addPrinting(const QString &zone)
|
||||
QString foundProviderId =
|
||||
existing.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString();
|
||||
if (foundProviderId.isEmpty()) {
|
||||
int amount = existing.data(Qt::DisplayRole).toInt();
|
||||
extraCopies = amount - 1; // One less because we *always* add one
|
||||
int existingAmount = existing.data(Qt::DisplayRole).toInt();
|
||||
extraCopies = existingAmount - 1; // One less because we *always* add one
|
||||
replacingProviderless = true;
|
||||
}
|
||||
}
|
||||
@@ -246,23 +254,3 @@ void CardAmountWidget::decrementCardHelper(const QString &zone)
|
||||
return model->offsetCountAtIndex(idx, -1);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Counts the number of cards in a specific zone (mainboard or sideboard).
|
||||
*
|
||||
* @param deckZone The name of the zone (e.g., DECK_ZONE_MAIN or DECK_ZONE_SIDE).
|
||||
* @return The number of cards in the zone.
|
||||
*/
|
||||
int CardAmountWidget::countCardsInZone(const QString &deckZone)
|
||||
{
|
||||
QString uuid = rootCard.getPrinting().getUuid();
|
||||
|
||||
if (uuid.isEmpty()) {
|
||||
return 0; // Cards without uuids/providerIds CANNOT match another card, they are undefined for us.
|
||||
}
|
||||
|
||||
QList<ExactCard> cards = deckStateManager->getModel()->getCardsForZone(deckZone);
|
||||
|
||||
return std::count_if(cards.cbegin(), cards.cend(),
|
||||
[&uuid](const ExactCard &card) { return card.getPrinting().getUuid() == uuid; });
|
||||
}
|
||||
@@ -31,9 +31,10 @@ public:
|
||||
QSlider *cardSizeSlider,
|
||||
const ExactCard &rootCard,
|
||||
const QString &zoneName);
|
||||
int countCardsInZone(const QString &deckZone);
|
||||
int getAmount();
|
||||
|
||||
public slots:
|
||||
void setAmount(int _amount);
|
||||
void updateCardCount();
|
||||
void addPrinting(const QString &zone);
|
||||
|
||||
@@ -52,6 +53,7 @@ private:
|
||||
QLabel *cardCountInZone;
|
||||
|
||||
bool hovered;
|
||||
int amount = 0;
|
||||
|
||||
void decrementCardHelper(const QString &zoneName);
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck
|
||||
|
||||
// Connect deck model data change signal to update display
|
||||
connect(deckStateManager, &DeckStateManager::uniqueCardsChanged, this, &PrintingSelector::printingsInDeckChanged);
|
||||
connect(deckStateManager, &DeckStateManager::cardModified, this, &PrintingSelector::updateCardAmounts);
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
@@ -93,6 +94,36 @@ void PrintingSelector::printingsInDeckChanged()
|
||||
QTimer::singleShot(100, this, &PrintingSelector::updateDisplay);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A map of uuid to amounts (main, side).
|
||||
*/
|
||||
static QMap<QString, QPair<int, int>> tallyUuidCounts(const DeckListModel *model, const QString &cardName)
|
||||
{
|
||||
QMap<QString, QPair<int, int>> map;
|
||||
|
||||
auto mainNodes = model->getCardNodesForZone(DECK_ZONE_MAIN);
|
||||
for (auto &node : mainNodes) {
|
||||
if (node->getName() == cardName) {
|
||||
map[node->getCardProviderId()].first += node->getNumber();
|
||||
}
|
||||
}
|
||||
|
||||
auto sideNodes = model->getCardNodesForZone(DECK_ZONE_SIDE);
|
||||
for (auto &node : sideNodes) {
|
||||
if (node->getName() == cardName) {
|
||||
map[node->getCardProviderId()].second += node->getNumber();
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void PrintingSelector::updateCardAmounts()
|
||||
{
|
||||
auto map = tallyUuidCounts(deckStateManager->getModel(), selectedCard->getName());
|
||||
emit cardAmountsChanged(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the display by clearing the layout and loading new sets for the current card.
|
||||
*/
|
||||
@@ -156,6 +187,8 @@ void PrintingSelector::getAllSetsForCurrentCard()
|
||||
}
|
||||
printingsToUse = sortToolBar->prependPinnedPrintings(printingsToUse, selectedCard->getName());
|
||||
|
||||
auto uuidToAmounts = tallyUuidCounts(deckStateManager->getModel(), selectedCard->getName());
|
||||
|
||||
// Defer widget creation
|
||||
currentIndex = 0;
|
||||
|
||||
@@ -166,8 +199,11 @@ void PrintingSelector::getAllSetsForCurrentCard()
|
||||
cardSizeWidget->getSlider(), card);
|
||||
flowWidget->addWidget(cardDisplayWidget);
|
||||
cardDisplayWidget->clampSetNameToPicture();
|
||||
cardDisplayWidget->updateCardAmounts(uuidToAmounts);
|
||||
connect(cardDisplayWidget, &PrintingSelectorCardDisplayWidget::cardPreferenceChanged, this,
|
||||
&PrintingSelector::updateDisplay);
|
||||
connect(this, &PrintingSelector::cardAmountsChanged, cardDisplayWidget,
|
||||
&PrintingSelectorCardDisplayWidget::updateCardAmounts);
|
||||
}
|
||||
|
||||
// Stop timer when done
|
||||
|
||||
@@ -44,6 +44,7 @@ public slots:
|
||||
|
||||
private slots:
|
||||
void printingsInDeckChanged();
|
||||
void updateCardAmounts();
|
||||
|
||||
signals:
|
||||
/**
|
||||
@@ -55,6 +56,12 @@ signals:
|
||||
*/
|
||||
void nextCardRequested();
|
||||
|
||||
/**
|
||||
* The amounts of the printings in the deck has changed
|
||||
* @param uuidToAmounts Map of uuids to the amounts (maindeck, sideboard) in the deck
|
||||
*/
|
||||
void cardAmountsChanged(const QMap<QString, QPair<int, int>> &uuidToAmounts);
|
||||
|
||||
private:
|
||||
QVBoxLayout *layout;
|
||||
SettingsButtonWidget *displayOptionsWidget;
|
||||
|
||||
@@ -27,7 +27,7 @@ PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *pa
|
||||
DeckStateManager *deckStateManager,
|
||||
QSlider *cardSizeSlider,
|
||||
const ExactCard &rootCard)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent), rootCard(rootCard)
|
||||
{
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
@@ -64,3 +64,9 @@ void PrintingSelectorCardDisplayWidget::clampSetNameToPicture()
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void PrintingSelectorCardDisplayWidget::updateCardAmounts(const QMap<QString, QPair<int, int>> &uuidToAmounts)
|
||||
{
|
||||
auto [main, side] = uuidToAmounts.value(rootCard.getPrinting().getUuid());
|
||||
overlayWidget->updateCardAmounts(main, side);
|
||||
}
|
||||
|
||||
@@ -27,11 +27,13 @@ public:
|
||||
|
||||
public slots:
|
||||
void clampSetNameToPicture();
|
||||
void updateCardAmounts(const QMap<QString, QPair<int, int>> &uuidToAmounts);
|
||||
|
||||
signals:
|
||||
void cardPreferenceChanged();
|
||||
|
||||
private:
|
||||
ExactCard rootCard;
|
||||
QVBoxLayout *layout;
|
||||
SetNameAndCollectorsNumberDisplayWidget *setNameAndCollectorsNumberDisplayWidget;
|
||||
PrintingSelectorCardOverlayWidget *overlayWidget;
|
||||
|
||||
@@ -59,12 +59,6 @@ PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *pa
|
||||
allZonesCardAmountWidget = new AllZonesCardAmountWidget(this, deckStateManager, cardSizeSlider, _rootCard);
|
||||
|
||||
allZonesCardAmountWidget->raise(); // Ensure it's on top of the picture
|
||||
// Set initial visibility based on amounts
|
||||
if (allZonesCardAmountWidget->getMainboardAmount() > 0 || allZonesCardAmountWidget->getSideboardAmount() > 0) {
|
||||
allZonesCardAmountWidget->setVisible(true);
|
||||
} else {
|
||||
allZonesCardAmountWidget->setVisible(false);
|
||||
}
|
||||
|
||||
// Attempt to cast the parent to PrintingSelectorCardDisplayWidget
|
||||
if (const auto *parentWidget = qobject_cast<PrintingSelectorCardDisplayWidget *>(parent)) {
|
||||
@@ -113,8 +107,7 @@ void PrintingSelectorCardOverlayWidget::resizeEvent(QResizeEvent *event)
|
||||
/**
|
||||
* @brief Handles the mouse enter event when the cursor enters the overlay widget area.
|
||||
*
|
||||
* When the cursor enters the widget, the card information is updated, and the card amount widget
|
||||
* is displayed if the amounts are zero for both the mainboard and sideboard.
|
||||
* When the cursor enters the widget, the card amount widget becomes visible regardless of whether the amounts are zero.
|
||||
*
|
||||
* @param event The event triggered when the mouse enters the widget.
|
||||
*/
|
||||
@@ -126,16 +119,27 @@ void PrintingSelectorCardOverlayWidget::enterEvent(QEvent *event)
|
||||
{
|
||||
QWidget::enterEvent(event);
|
||||
deckEditor->updateCard(rootCard);
|
||||
|
||||
// Check if either mainboard or sideboard amount is greater than 0
|
||||
if (allZonesCardAmountWidget->getMainboardAmount() > 0 || allZonesCardAmountWidget->getSideboardAmount() > 0) {
|
||||
// Don't change visibility if amounts are greater than 0
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the widget if amounts are 0
|
||||
allZonesCardAmountWidget->setVisible(true);
|
||||
updateVisibility();
|
||||
}
|
||||
|
||||
void PrintingSelectorCardOverlayWidget::updateCardAmounts(int mainboardAmount, int sideboardAmount)
|
||||
{
|
||||
allZonesCardAmountWidget->setAmounts(mainboardAmount, sideboardAmount);
|
||||
updateVisibility();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the visibility of the widgets depending on the amounts and whether the mouse is hovering over.
|
||||
*/
|
||||
void PrintingSelectorCardOverlayWidget::updateVisibility()
|
||||
{
|
||||
if (allZonesCardAmountWidget->isNonZero() || underMouse()) {
|
||||
allZonesCardAmountWidget->setVisible(true);
|
||||
} else {
|
||||
allZonesCardAmountWidget->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the pin badge visibility and position based on the card's pinned state.
|
||||
*
|
||||
@@ -182,15 +186,7 @@ void PrintingSelectorCardOverlayWidget::updatePinBadgeVisibility()
|
||||
void PrintingSelectorCardOverlayWidget::leaveEvent(QEvent *event)
|
||||
{
|
||||
QWidget::leaveEvent(event);
|
||||
|
||||
// Check if either mainboard or sideboard amount is greater than 0
|
||||
if (allZonesCardAmountWidget->getMainboardAmount() > 0 || allZonesCardAmountWidget->getSideboardAmount() > 0) {
|
||||
// Don't hide the widget if amounts are greater than 0
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the widget if amounts are 0
|
||||
allZonesCardAmountWidget->setVisible(false);
|
||||
updateVisibility();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,11 @@ protected:
|
||||
signals:
|
||||
void cardPreferenceChanged();
|
||||
|
||||
public slots:
|
||||
void updateCardAmounts(int mainboardAmount, int sideboardAmount);
|
||||
|
||||
private slots:
|
||||
void updateVisibility();
|
||||
void updatePinBadgeVisibility();
|
||||
|
||||
private:
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
[[nodiscard]] QString getTabText() const override
|
||||
{
|
||||
auto cardName = cardToQuery.isNull() ? QString() : cardToQuery->getName();
|
||||
return tr("EDHRec: ") + cardName;
|
||||
return tr("EDHRec") + ": " + cardName;
|
||||
}
|
||||
|
||||
CardSizeWidget *getCardSizeSlider() const
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,4 +7,4 @@ This is the **main landing page** of the Cockatrice documentation.
|
||||
- Go to the @subpage user_reference page
|
||||
- Review the @subpage developer_reference
|
||||
|
||||
Or check out the [Cockatrice Webpage](https://cockatrice.github.io/).
|
||||
Or check out the <a href="https://cockatrice.github.io/" target="_blank" rel="noopener noreferrer">Cockatrice Webpage</a>.
|
||||
|
||||
@@ -9,7 +9,9 @@ set(HEADERS
|
||||
libcockatrice/deck_list/tree/inner_deck_list_node.h
|
||||
libcockatrice/deck_list/deck_list.h
|
||||
libcockatrice/deck_list/deck_list_history_manager.h
|
||||
libcockatrice/deck_list/deck_list_node_tree.h
|
||||
libcockatrice/deck_list/deck_list_memento.h
|
||||
libcockatrice/deck_list/sideboard_plan.h
|
||||
)
|
||||
|
||||
if(Qt6_FOUND)
|
||||
@@ -28,7 +30,7 @@ add_library(
|
||||
libcockatrice/deck_list/deck_list.cpp
|
||||
libcockatrice/deck_list/deck_list_history_manager.cpp
|
||||
libcockatrice/deck_list/deck_list_node_tree.cpp
|
||||
libcockatrice/deck_list/deck_list_node_tree.h
|
||||
libcockatrice/deck_list/sideboard_plan.cpp
|
||||
)
|
||||
|
||||
add_dependencies(libcockatrice_deck_list libcockatrice_protocol)
|
||||
|
||||
@@ -21,61 +21,7 @@ uint qHash(const QRegularExpression &key, uint seed) noexcept
|
||||
}
|
||||
#endif
|
||||
|
||||
SideboardPlan::SideboardPlan(const QString &_name, const QList<MoveCard_ToZone> &_moveList)
|
||||
: name(_name), moveList(_moveList)
|
||||
{
|
||||
}
|
||||
|
||||
void SideboardPlan::setMoveList(const QList<MoveCard_ToZone> &_moveList)
|
||||
{
|
||||
moveList = _moveList;
|
||||
}
|
||||
|
||||
bool SideboardPlan::readElement(QXmlStreamReader *xml)
|
||||
{
|
||||
while (!xml->atEnd()) {
|
||||
xml->readNext();
|
||||
const QString childName = xml->name().toString();
|
||||
if (xml->isStartElement()) {
|
||||
if (childName == "name")
|
||||
name = xml->readElementText();
|
||||
else if (childName == "move_card_to_zone") {
|
||||
MoveCard_ToZone m;
|
||||
while (!xml->atEnd()) {
|
||||
xml->readNext();
|
||||
const QString childName2 = xml->name().toString();
|
||||
if (xml->isStartElement()) {
|
||||
if (childName2 == "card_name")
|
||||
m.set_card_name(xml->readElementText().toStdString());
|
||||
else if (childName2 == "start_zone")
|
||||
m.set_start_zone(xml->readElementText().toStdString());
|
||||
else if (childName2 == "target_zone")
|
||||
m.set_target_zone(xml->readElementText().toStdString());
|
||||
} else if (xml->isEndElement() && (childName2 == "move_card_to_zone")) {
|
||||
moveList.append(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (xml->isEndElement() && (childName == "sideboard_plan"))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SideboardPlan::write(QXmlStreamWriter *xml)
|
||||
{
|
||||
xml->writeStartElement("sideboard_plan");
|
||||
xml->writeTextElement("name", name);
|
||||
for (auto &i : moveList) {
|
||||
xml->writeStartElement("move_card_to_zone");
|
||||
xml->writeTextElement("card_name", QString::fromStdString(i.card_name()));
|
||||
xml->writeTextElement("start_zone", QString::fromStdString(i.start_zone()));
|
||||
xml->writeTextElement("target_zone", QString::fromStdString(i.target_zone()));
|
||||
xml->writeEndElement();
|
||||
}
|
||||
xml->writeEndElement();
|
||||
}
|
||||
static const QString CURRENT_SIDEBOARD_PLAN_KEY = "";
|
||||
|
||||
bool DeckList::Metadata::isEmpty() const
|
||||
{
|
||||
@@ -93,36 +39,23 @@ DeckList::DeckList(const QString &nativeString)
|
||||
|
||||
DeckList::DeckList(const Metadata &metadata,
|
||||
const DecklistNodeTree &tree,
|
||||
const QMap<QString, SideboardPlan *> &sideboardPlans)
|
||||
const QMap<QString, SideboardPlan> &sideboardPlans)
|
||||
: metadata(metadata), sideboardPlans(sideboardPlans), tree(tree)
|
||||
{
|
||||
}
|
||||
|
||||
DeckList::~DeckList()
|
||||
QList<MoveCard_ToZone> DeckList::getCurrentSideboardPlan() const
|
||||
{
|
||||
QMapIterator<QString, SideboardPlan *> i(sideboardPlans);
|
||||
while (i.hasNext())
|
||||
delete i.next().value();
|
||||
}
|
||||
if (!sideboardPlans.contains(CURRENT_SIDEBOARD_PLAN_KEY)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<MoveCard_ToZone> DeckList::getCurrentSideboardPlan()
|
||||
{
|
||||
SideboardPlan *current = sideboardPlans.value(QString(), 0);
|
||||
if (!current)
|
||||
return QList<MoveCard_ToZone>();
|
||||
else
|
||||
return current->getMoveList();
|
||||
return sideboardPlans.value(CURRENT_SIDEBOARD_PLAN_KEY).getMoveList();
|
||||
}
|
||||
|
||||
void DeckList::setCurrentSideboardPlan(const QList<MoveCard_ToZone> &plan)
|
||||
{
|
||||
SideboardPlan *current = sideboardPlans.value(QString(), 0);
|
||||
if (!current) {
|
||||
current = new SideboardPlan;
|
||||
sideboardPlans.insert(QString(), current);
|
||||
}
|
||||
|
||||
current->setMoveList(plan);
|
||||
sideboardPlans[CURRENT_SIDEBOARD_PLAN_KEY].setMoveList(plan);
|
||||
}
|
||||
|
||||
bool DeckList::readElement(QXmlStreamReader *xml)
|
||||
@@ -151,11 +84,9 @@ bool DeckList::readElement(QXmlStreamReader *xml)
|
||||
} else if (childName == "zone") {
|
||||
tree.readZoneElement(xml);
|
||||
} else if (childName == "sideboard_plan") {
|
||||
SideboardPlan *newSideboardPlan = new SideboardPlan;
|
||||
if (newSideboardPlan->readElement(xml)) {
|
||||
sideboardPlans.insert(newSideboardPlan->getName(), newSideboardPlan);
|
||||
} else {
|
||||
delete newSideboardPlan;
|
||||
SideboardPlan newSideboardPlan;
|
||||
if (newSideboardPlan.readElement(xml)) {
|
||||
sideboardPlans.insert(newSideboardPlan.getName(), newSideboardPlan);
|
||||
}
|
||||
}
|
||||
} else if (xml->isEndElement() && (childName == "cockatrice_deck")) {
|
||||
@@ -194,9 +125,8 @@ void DeckList::write(QXmlStreamWriter *xml) const
|
||||
tree.write(xml);
|
||||
|
||||
// Write sideboard plans
|
||||
QMapIterator<QString, SideboardPlan *> i(sideboardPlans);
|
||||
while (i.hasNext()) {
|
||||
i.next().value()->write(xml);
|
||||
for (auto &sideboardPlan : sideboardPlans.values()) {
|
||||
sideboardPlan.write(xml);
|
||||
}
|
||||
|
||||
xml->writeEndElement(); // Close "cockatrice_deck"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file deck_list.h
|
||||
* @brief Defines the DeckList class and supporting types for managing a full
|
||||
* @brief Defines the DeckList class, which manages a full
|
||||
* deck structure including cards, zones, sideboard plans, and
|
||||
* serialization to/from multiple formats. This is a logic class which
|
||||
* does not care about Qt or user facing views.
|
||||
@@ -12,12 +12,12 @@
|
||||
|
||||
#include "deck_list_memento.h"
|
||||
#include "deck_list_node_tree.h"
|
||||
#include "sideboard_plan.h"
|
||||
#include "tree/inner_deck_list_node.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QVector>
|
||||
#include <QtCore/QXmlStreamReader>
|
||||
#include <libcockatrice/protocol/pb/move_card_to_zone.pb.h>
|
||||
#include <libcockatrice/utility/card_ref.h>
|
||||
|
||||
class AbstractDecklistNode;
|
||||
@@ -27,67 +27,6 @@ class QIODevice;
|
||||
class QTextStream;
|
||||
class InnerDecklistNode;
|
||||
|
||||
/**
|
||||
* @class SideboardPlan
|
||||
* @ingroup Decks
|
||||
* @brief Represents a predefined sideboarding strategy for a deck.
|
||||
*
|
||||
* Sideboard plans store a named list of card movements that should be applied
|
||||
* between the mainboard and sideboard for a specific matchup. Each movement
|
||||
* is expressed using a `MoveCard_ToZone` protobuf message.
|
||||
*
|
||||
* ### Responsibilities:
|
||||
* - Store the plan name and list of moves.
|
||||
* - Support XML serialization/deserialization.
|
||||
*
|
||||
* ### Typical usage:
|
||||
* A deck can contain multiple sideboard plans (e.g., "vs Aggro", "vs Control"),
|
||||
* each describing how to transform the main deck into its intended configuration.
|
||||
*/
|
||||
class SideboardPlan
|
||||
{
|
||||
private:
|
||||
QString name; ///< Human-readable name of this plan.
|
||||
QList<MoveCard_ToZone> moveList; ///< List of move instructions for this plan.
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new SideboardPlan.
|
||||
* @param _name The plan name.
|
||||
* @param _moveList Initial list of card move instructions.
|
||||
*/
|
||||
explicit SideboardPlan(const QString &_name = QString(),
|
||||
const QList<MoveCard_ToZone> &_moveList = QList<MoveCard_ToZone>());
|
||||
|
||||
/**
|
||||
* @brief Read a SideboardPlan from an XML stream.
|
||||
* @param xml XML reader positioned at the plan element.
|
||||
* @return true if parsing succeeded.
|
||||
*/
|
||||
bool readElement(QXmlStreamReader *xml);
|
||||
|
||||
/**
|
||||
* @brief Write this SideboardPlan to XML.
|
||||
* @param xml Stream to append the serialized element to.
|
||||
*/
|
||||
void write(QXmlStreamWriter *xml);
|
||||
|
||||
/// @return The plan name.
|
||||
[[nodiscard]] QString getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/// @return Const reference to the move list.
|
||||
[[nodiscard]] const QList<MoveCard_ToZone> &getMoveList() const
|
||||
{
|
||||
return moveList;
|
||||
}
|
||||
|
||||
/// @brief Replace the move list with a new one.
|
||||
void setMoveList(const QList<MoveCard_ToZone> &_moveList);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class DeckList
|
||||
* @ingroup Decks
|
||||
@@ -139,9 +78,9 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
Metadata metadata; ///< Deck metadata that is stored in the deck file
|
||||
QMap<QString, SideboardPlan *> sideboardPlans; ///< Named sideboard plans.
|
||||
DecklistNodeTree tree; ///< The deck tree (zones + cards).
|
||||
Metadata metadata; ///< Deck metadata that is stored in the deck file
|
||||
QMap<QString, SideboardPlan> sideboardPlans; ///< Named sideboard plans.
|
||||
DecklistNodeTree tree; ///< The deck tree (zones + cards).
|
||||
|
||||
/**
|
||||
* @brief Cached deck hash, recalculated lazily.
|
||||
@@ -193,8 +132,7 @@ public:
|
||||
/// @brief Construct from components
|
||||
DeckList(const Metadata &metadata,
|
||||
const DecklistNodeTree &tree,
|
||||
const QMap<QString, SideboardPlan *> &sideboardPlans = {});
|
||||
virtual ~DeckList();
|
||||
const QMap<QString, SideboardPlan> &sideboardPlans = {});
|
||||
|
||||
/**
|
||||
* @brief Gets a pointer to the underlying node tree.
|
||||
@@ -247,9 +185,9 @@ public:
|
||||
|
||||
/// @name Sideboard plans
|
||||
///@{
|
||||
QList<MoveCard_ToZone> getCurrentSideboardPlan();
|
||||
QList<MoveCard_ToZone> getCurrentSideboardPlan() const;
|
||||
void setCurrentSideboardPlan(const QList<MoveCard_ToZone> &plan);
|
||||
const QMap<QString, SideboardPlan *> &getSideboardPlans() const
|
||||
const QMap<QString, SideboardPlan> &getSideboardPlans() const
|
||||
{
|
||||
return sideboardPlans;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "sideboard_plan.h"
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
SideboardPlan::SideboardPlan(const QString &_name, const QList<MoveCard_ToZone> &_moveList)
|
||||
: name(_name), moveList(_moveList)
|
||||
{
|
||||
}
|
||||
|
||||
void SideboardPlan::setMoveList(const QList<MoveCard_ToZone> &_moveList)
|
||||
{
|
||||
moveList = _moveList;
|
||||
}
|
||||
|
||||
bool SideboardPlan::readElement(QXmlStreamReader *xml)
|
||||
{
|
||||
while (!xml->atEnd()) {
|
||||
xml->readNext();
|
||||
const QString childName = xml->name().toString();
|
||||
if (xml->isStartElement()) {
|
||||
if (childName == "name")
|
||||
name = xml->readElementText();
|
||||
else if (childName == "move_card_to_zone") {
|
||||
MoveCard_ToZone m;
|
||||
while (!xml->atEnd()) {
|
||||
xml->readNext();
|
||||
const QString childName2 = xml->name().toString();
|
||||
if (xml->isStartElement()) {
|
||||
if (childName2 == "card_name")
|
||||
m.set_card_name(xml->readElementText().toStdString());
|
||||
else if (childName2 == "start_zone")
|
||||
m.set_start_zone(xml->readElementText().toStdString());
|
||||
else if (childName2 == "target_zone")
|
||||
m.set_target_zone(xml->readElementText().toStdString());
|
||||
} else if (xml->isEndElement() && (childName2 == "move_card_to_zone")) {
|
||||
moveList.append(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (xml->isEndElement() && (childName == "sideboard_plan"))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SideboardPlan::write(QXmlStreamWriter *xml) const
|
||||
{
|
||||
xml->writeStartElement("sideboard_plan");
|
||||
xml->writeTextElement("name", name);
|
||||
for (auto &i : moveList) {
|
||||
xml->writeStartElement("move_card_to_zone");
|
||||
xml->writeTextElement("card_name", QString::fromStdString(i.card_name()));
|
||||
xml->writeTextElement("start_zone", QString::fromStdString(i.start_zone()));
|
||||
xml->writeTextElement("target_zone", QString::fromStdString(i.target_zone()));
|
||||
xml->writeEndElement();
|
||||
}
|
||||
xml->writeEndElement();
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef COCKATRICE_SIDEBOARD_PLAN_H
|
||||
#define COCKATRICE_SIDEBOARD_PLAN_H
|
||||
|
||||
#include <QList>
|
||||
#include <libcockatrice/protocol/pb/move_card_to_zone.pb.h>
|
||||
|
||||
class QXmlStreamWriter;
|
||||
class QXmlStreamReader;
|
||||
|
||||
/**
|
||||
* @class SideboardPlan
|
||||
* @ingroup Decks
|
||||
* @brief Represents a predefined sideboarding strategy for a deck.
|
||||
*
|
||||
* Sideboard plans store a named list of card movements that should be applied
|
||||
* between the mainboard and sideboard for a specific matchup. Each movement
|
||||
* is expressed using a `MoveCard_ToZone` protobuf message.
|
||||
*
|
||||
* ### Responsibilities:
|
||||
* - Store the plan name and list of moves.
|
||||
* - Support XML serialization/deserialization.
|
||||
*
|
||||
* ### Typical usage:
|
||||
* A deck can contain multiple sideboard plans (e.g., "vs Aggro", "vs Control"),
|
||||
* each describing how to transform the main deck into its intended configuration.
|
||||
*/
|
||||
class SideboardPlan
|
||||
{
|
||||
private:
|
||||
QString name; ///< Human-readable name of this plan.
|
||||
QList<MoveCard_ToZone> moveList; ///< List of move instructions for this plan.
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new SideboardPlan.
|
||||
* @param _name The plan name.
|
||||
* @param _moveList Initial list of card move instructions.
|
||||
*/
|
||||
explicit SideboardPlan(const QString &_name = "", const QList<MoveCard_ToZone> &_moveList = {});
|
||||
|
||||
/**
|
||||
* @brief Read a SideboardPlan from an XML stream.
|
||||
* @param xml XML reader positioned at the plan element.
|
||||
* @return true if parsing succeeded.
|
||||
*/
|
||||
bool readElement(QXmlStreamReader *xml);
|
||||
|
||||
/**
|
||||
* @brief Write this SideboardPlan to XML.
|
||||
* @param xml Stream to append the serialized element to.
|
||||
*/
|
||||
void write(QXmlStreamWriter *xml) const;
|
||||
|
||||
/// @return The plan name.
|
||||
[[nodiscard]] QString getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/// @return Const reference to the move list.
|
||||
[[nodiscard]] const QList<MoveCard_ToZone> &getMoveList() const
|
||||
{
|
||||
return moveList;
|
||||
}
|
||||
|
||||
/// @brief Replace the move list with a new one.
|
||||
void setMoveList(const QList<MoveCard_ToZone> &_moveList);
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_SIDEBOARD_PLAN_H
|
||||
@@ -637,6 +637,16 @@ QList<ExactCard> DeckListModel::getCardsForZone(const QString &zoneName) const
|
||||
return cardNodesToExactCards(nodes);
|
||||
}
|
||||
|
||||
QList<const DecklistCardNode *> DeckListModel::getCardNodes() const
|
||||
{
|
||||
return deckList->getCardNodes();
|
||||
}
|
||||
|
||||
QList<const DecklistCardNode *> DeckListModel::getCardNodesForZone(const QString &zoneName) const
|
||||
{
|
||||
return deckList->getCardNodes({zoneName});
|
||||
}
|
||||
|
||||
QList<QString> DeckListModel::getCardNames() const
|
||||
{
|
||||
auto nodes = deckList->getCardNodes();
|
||||
|
||||
@@ -339,6 +339,12 @@ public:
|
||||
[[nodiscard]] QList<ExactCard> getCards() const;
|
||||
[[nodiscard]] QList<ExactCard> getCardsForZone(const QString &zoneName) const;
|
||||
|
||||
/**
|
||||
* @brief Gets a list of all card nodes in the deck.
|
||||
*/
|
||||
[[nodiscard]] QList<const DecklistCardNode *> getCardNodes() const;
|
||||
[[nodiscard]] QList<const DecklistCardNode *> getCardNodesForZone(const QString &zoneName) const;
|
||||
|
||||
/**
|
||||
* @brief Gets a deduplicated list of all card names that appear in the model
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user