mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
Import Moxfield signature spells into the deck [skip ci] (#7751)
Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
ada8f399f1
commit
dc4aa19d23
5 changed files with 148 additions and 2 deletions
|
|
@ -54,7 +54,11 @@ public:
|
||||||
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
|
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outStream.flush();
|
||||||
|
outStream.seek(0);
|
||||||
deckList.loadFromStream_Plain(outStream, false, CardNameNormalizer());
|
deckList.loadFromStream_Plain(outStream, false, CardNameNormalizer());
|
||||||
|
deckList.setName(deckName);
|
||||||
|
deckList.setComments(deckDescription);
|
||||||
deckList.forEachCard(CardNodeFunction::ResolveProviderId());
|
deckList.forEachCard(CardNodeFunction::ResolveProviderId());
|
||||||
deckList.setName(deckName);
|
deckList.setName(deckName);
|
||||||
deckList.setComments(deckDescription);
|
deckList.setComments(deckDescription);
|
||||||
|
|
@ -100,7 +104,11 @@ public:
|
||||||
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
|
outStream << quantity << ' ' << cardName << " (" << setName << ") " << collectorNumber << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outStream.flush();
|
||||||
|
outStream.seek(0);
|
||||||
deckList.loadFromStream_Plain(outStream, false, CardNameNormalizer());
|
deckList.loadFromStream_Plain(outStream, false, CardNameNormalizer());
|
||||||
|
deckList.setName(deckName);
|
||||||
|
deckList.setComments(deckDescription);
|
||||||
deckList.forEachCard(CardNodeFunction::ResolveProviderId());
|
deckList.forEachCard(CardNodeFunction::ResolveProviderId());
|
||||||
deckList.setName(deckName);
|
deckList.setName(deckName);
|
||||||
deckList.setComments(deckDescription);
|
deckList.setComments(deckDescription);
|
||||||
|
|
@ -119,6 +127,24 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Moxfield stores Oathbreaker signature spells in their own board,
|
||||||
|
// separate from both mainboard and commanders. They are playable deck
|
||||||
|
// cards, but must not replace the Oathbreaker as the banner card.
|
||||||
|
const QJsonObject signatureSpells = obj.value("signatureSpells").toObject();
|
||||||
|
for (auto it = signatureSpells.begin(); it != signatureSpells.end(); ++it) {
|
||||||
|
const QJsonObject entry = it.value().toObject();
|
||||||
|
const QJsonObject card = entry.value("card").toObject();
|
||||||
|
const QString name = card.value("name").toString();
|
||||||
|
const int quantity = entry.value("quantity").toInt();
|
||||||
|
if (name.isEmpty() || quantity <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const QString set = card.value("set").toString().toUpper();
|
||||||
|
const QString collectorNumber = card.value("cn").toString();
|
||||||
|
const QString providerId = card.value("scryfall_id").toString();
|
||||||
|
deckList.addCards(name, quantity, DECK_ZONE_MAIN, -1, set, collectorNumber, providerId);
|
||||||
|
}
|
||||||
|
|
||||||
return deckList;
|
return deckList;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -423,8 +423,23 @@ DecklistCardNode *DeckList::addCard(const QString &cardName,
|
||||||
const QString &cardProviderId,
|
const QString &cardProviderId,
|
||||||
bool formatLegal)
|
bool formatLegal)
|
||||||
{
|
{
|
||||||
auto node =
|
return addCards(cardName, 1, zoneName, position, cardSetName, cardSetCollectorNumber, cardProviderId, formatLegal);
|
||||||
tree.addCard(cardName, 1, zoneName, position, cardSetName, cardSetCollectorNumber, cardProviderId, formatLegal);
|
}
|
||||||
|
|
||||||
|
DecklistCardNode *DeckList::addCards(const QString &cardName,
|
||||||
|
int amount,
|
||||||
|
const QString &zoneName,
|
||||||
|
int position,
|
||||||
|
const QString &cardSetName,
|
||||||
|
const QString &cardSetCollectorNumber,
|
||||||
|
const QString &cardProviderId,
|
||||||
|
bool formatLegal)
|
||||||
|
{
|
||||||
|
if (amount <= 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto node = tree.addCard(cardName, amount, zoneName, position, cardSetName, cardSetCollectorNumber, cardProviderId,
|
||||||
|
formatLegal);
|
||||||
refreshDeckHash();
|
refreshDeckHash();
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -284,6 +284,14 @@ public:
|
||||||
const QString &cardSetCollectorNumber = QString(),
|
const QString &cardSetCollectorNumber = QString(),
|
||||||
const QString &cardProviderId = QString(),
|
const QString &cardProviderId = QString(),
|
||||||
const bool formatLegal = true);
|
const bool formatLegal = true);
|
||||||
|
DecklistCardNode *addCards(const QString &cardName,
|
||||||
|
int amount,
|
||||||
|
const QString &zoneName,
|
||||||
|
int position = -1,
|
||||||
|
const QString &cardSetName = QString(),
|
||||||
|
const QString &cardSetCollectorNumber = QString(),
|
||||||
|
const QString &cardProviderId = QString(),
|
||||||
|
const bool formatLegal = true);
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
/** @name Deck identity */
|
/** @name Deck identity */
|
||||||
|
|
|
||||||
|
|
@ -463,6 +463,19 @@ endif()
|
||||||
|
|
||||||
# Guard must match the condition for src/single_instance_manager.cpp in cockatrice/CMakeLists.txt.
|
# Guard must match the condition for src/single_instance_manager.cpp in cockatrice/CMakeLists.txt.
|
||||||
if(WITH_CLIENT)
|
if(WITH_CLIENT)
|
||||||
|
cockatrice_add_test(
|
||||||
|
NAME
|
||||||
|
moxfield_signature_spells_test
|
||||||
|
SOURCES
|
||||||
|
${VERSION_STRING_CPP}
|
||||||
|
${CMAKE_SOURCE_DIR}/cockatrice/src/interface/deck_loader/card_node_function.cpp
|
||||||
|
moxfield_signature_spells_test.cpp
|
||||||
|
LIBS
|
||||||
|
libcockatrice_card
|
||||||
|
libcockatrice_deck_list
|
||||||
|
INCLUDE_DIRS
|
||||||
|
${CMAKE_SOURCE_DIR}/cockatrice/src
|
||||||
|
)
|
||||||
cockatrice_add_test(
|
cockatrice_add_test(
|
||||||
NAME
|
NAME
|
||||||
single_instance_manager_test
|
single_instance_manager_test
|
||||||
|
|
|
||||||
84
tests/moxfield_signature_spells_test.cpp
Normal file
84
tests/moxfield_signature_spells_test.cpp
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
#include "client/network/parsers/interface_json_deck_parser.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <libcockatrice/deck_list/tree/deck_list_card_node.h>
|
||||||
|
#include <libcockatrice/deck_list/tree/inner_deck_list_node.h>
|
||||||
|
|
||||||
|
TEST(MoxfieldImport, SignatureSpellIsAddedToMainWithoutReplacingCommanderBanner)
|
||||||
|
{
|
||||||
|
// Moxfield v2 board objects map IDs to entries with quantity and card.
|
||||||
|
const auto input = QJsonDocument::fromJson(R"json({
|
||||||
|
"name": "Oathbreaker example", "description": "Notes retained",
|
||||||
|
"mainboard": {"land": {"quantity": 1, "card": {"name": "Island", "set": "abc", "cn": "1"}}},
|
||||||
|
"sideboard": {"side": {"quantity": 1, "card": {"name": "Mountain", "set": "abc", "cn": "2"}}},
|
||||||
|
"commanders": {"walker": {"quantity": 1, "card": {"name": "Jace", "set": "abc", "cn": "3", "scryfall_id": "walker-id"}}},
|
||||||
|
"signatureSpells": {"spell": {"quantity": 1, "card": {"name": "Lightning Bolt", "set": "xyz", "cn": "4", "scryfall_id": "spell-id"}}}
|
||||||
|
})json")
|
||||||
|
.object();
|
||||||
|
MoxfieldJsonParser parser;
|
||||||
|
DeckList deck = parser.parse(input);
|
||||||
|
int mainSpellCount = 0;
|
||||||
|
int sideSpellCount = 0;
|
||||||
|
int mainLands = 0;
|
||||||
|
int sideLands = 0;
|
||||||
|
deck.forEachCard([&](InnerDecklistNode *zone, DecklistCardNode *card) {
|
||||||
|
if (card->getName() == "Island" && zone->getName() == DECK_ZONE_MAIN) {
|
||||||
|
mainLands += card->getNumber();
|
||||||
|
}
|
||||||
|
if (card->getName() == "Mountain" && zone->getName() == DECK_ZONE_SIDE) {
|
||||||
|
sideLands += card->getNumber();
|
||||||
|
}
|
||||||
|
if (card->getName() != "Lightning Bolt") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (zone->getName() == DECK_ZONE_MAIN) {
|
||||||
|
mainSpellCount += card->getNumber();
|
||||||
|
EXPECT_EQ(card->getCardSetShortName(), "XYZ");
|
||||||
|
EXPECT_EQ(card->getCardCollectorNumber(), "4");
|
||||||
|
EXPECT_EQ(card->getCardProviderId(), "spell-id");
|
||||||
|
} else if (zone->getName() == DECK_ZONE_SIDE) {
|
||||||
|
sideSpellCount += card->getNumber();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
EXPECT_EQ(mainSpellCount, 1);
|
||||||
|
EXPECT_EQ(sideSpellCount, 0);
|
||||||
|
EXPECT_EQ(mainLands, 1);
|
||||||
|
EXPECT_EQ(sideLands, 1);
|
||||||
|
EXPECT_EQ(deck.getName(), "Oathbreaker example");
|
||||||
|
EXPECT_EQ(deck.getComments(), "Notes retained");
|
||||||
|
EXPECT_EQ(deck.getBannerCard().name, "Jace");
|
||||||
|
EXPECT_EQ(deck.getBannerCard().providerId, "walker-id");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MoxfieldImport, QuantityAwareDeckInsertionRetainsCountAndPrinting)
|
||||||
|
{
|
||||||
|
DeckList deck;
|
||||||
|
auto *card = deck.addCards("Island", 2, DECK_ZONE_MAIN, -1, "ABC", "4", "id");
|
||||||
|
ASSERT_NE(card, nullptr);
|
||||||
|
EXPECT_EQ(card->getNumber(), 2);
|
||||||
|
EXPECT_EQ(card->getCardProviderId(), "id");
|
||||||
|
EXPECT_EQ(deck.addCards("Island", 0, DECK_ZONE_MAIN), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OnlineImport, ArchidektPreservesNameDescriptionAndCards)
|
||||||
|
{
|
||||||
|
const auto input = QJsonDocument::fromJson(R"json({
|
||||||
|
"name": "Online deck", "description": "Deck notes",
|
||||||
|
"cards": [{"quantity": 2, "card": {
|
||||||
|
"oracleCard": {"name": "Island"}, "edition": {"editioncode": "abc"}, "collectorNumber": "1"
|
||||||
|
}}]
|
||||||
|
})json")
|
||||||
|
.object();
|
||||||
|
ArchidektJsonParser parser;
|
||||||
|
const DeckList deck = parser.parse(input);
|
||||||
|
int count = 0;
|
||||||
|
deck.forEachCard([&](InnerDecklistNode *zone, DecklistCardNode *card) {
|
||||||
|
if (zone->getName() == DECK_ZONE_MAIN && card->getName() == "Island") {
|
||||||
|
count += card->getNumber();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
EXPECT_EQ(count, 2);
|
||||||
|
EXPECT_EQ(deck.getName(), "Online deck");
|
||||||
|
EXPECT_EQ(deck.getComments(), "Deck notes");
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue