diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index 05ccb0f917..2a29199eeb 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -1415,14 +1415,15 @@ class ImportFilter extends FileFilter { || ext.equalsIgnoreCase("dek") || ext.equalsIgnoreCase("cod") || ext.equalsIgnoreCase("o8d") - || ext.equalsIgnoreCase("draft"); + || ext.equalsIgnoreCase("draft") + || ext.equalsIgnoreCase("mtga"); } return false; } @Override public String getDescription() { - return "All formats (*.dec; *.mwDeck; *.txt; *.dek; *.cod; *.o8d; *.draft)"; + return "All formats (*.dec; *.mwDeck; *.txt; *.dek; *.cod; *.o8d; *.draft; *.mtga)"; } } diff --git a/Mage/src/main/java/mage/cards/decks/CardNameUtil.java b/Mage/src/main/java/mage/cards/decks/CardNameUtil.java new file mode 100644 index 0000000000..53700b7e12 --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/CardNameUtil.java @@ -0,0 +1,24 @@ +package mage.cards.decks; + +import java.util.regex.Pattern; + +public class CardNameUtil { + + public static final Pattern CARD_NAME_PATTERN = Pattern.compile("[ !\"&',\\-./0-9:A-Za-z]+"); + + public static String normalizeCardName(String name) { + return name + .replace("&", "//") + .replace("Æ", "Ae") + .replace("ö", "o") + .replace("û", "u") + .replace("í", "i") + .replace("â", "a") + .replace("á", "a") + .replace("à", "a") + .replace("é", "e") + .replace("ú", "u") + .replace("\"", "'"); + } + +} diff --git a/Mage/src/main/java/mage/cards/decks/importer/CardLookup.java b/Mage/src/main/java/mage/cards/decks/importer/CardLookup.java index 2a7aa1ae3e..73d07878e9 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/CardLookup.java +++ b/Mage/src/main/java/mage/cards/decks/importer/CardLookup.java @@ -9,14 +9,47 @@ import mage.cards.repository.CardRepository; public class CardLookup { - public static final CardLookup instance = new CardLookup(); + public static final CardLookup instance = new CardLookup(); - public Optional lookupCardInfo(String name) { - return Optional.ofNullable(CardRepository.instance.findPreferedCoreExpansionCard(name, true)); - } + public Optional lookupCardInfo(String name) { + return Optional.ofNullable(CardRepository.instance.findPreferedCoreExpansionCard(name, true)); + } - public List lookupCardInfo(CardCriteria criteria) { - return CardRepository.instance.findCards(criteria); - } + public List lookupCardInfo(CardCriteria criteria) { + return CardRepository.instance.findCards(criteria); + } + + public Optional lookupCardInfo(String name, String set) { + Optional result = lookupCardInfo(new CardCriteria().name(name).setCodes(set)) + .stream() + .findAny(); + if (result.isPresent()) { + return result; + } + + return lookupCardInfo(name); + } + + public Optional lookupCardInfo(String name, String set, String cardNumber) { + Optional result; + try { + int intCardNumber = Integer.parseInt(cardNumber); + result = lookupCardInfo( + new CardCriteria() + .name(name) + .setCodes(set) + .minCardNumber(intCardNumber) + .maxCardNumber(intCardNumber)) + .stream() + .findAny(); + if (result.isPresent()) { + return result; + } + } catch (NumberFormatException ignored) { + /* ignored */ + } + + return lookupCardInfo(name, set); + } } diff --git a/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java index b7ba7f8059..dd153c5abe 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java @@ -36,6 +36,8 @@ public abstract class DeckImporter { return new O8dDeckImporter(); } else if (file.toLowerCase(Locale.ENGLISH).endsWith("draft")) { return new DraftLogImporter(); + } else if (file.toLowerCase(Locale.ENGLISH).endsWith("mtga")) { + return new MtgaImporter(); } else { return null; } diff --git a/Mage/src/main/java/mage/cards/decks/importer/MtgaImporter.java b/Mage/src/main/java/mage/cards/decks/importer/MtgaImporter.java new file mode 100644 index 0000000000..4949690873 --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/importer/MtgaImporter.java @@ -0,0 +1,60 @@ +package mage.cards.decks.importer; + +import com.google.common.collect.ImmutableMap; +import mage.cards.decks.CardNameUtil; +import mage.cards.decks.DeckCardInfo; +import mage.cards.decks.DeckCardLists; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static mage.cards.decks.CardNameUtil.CARD_NAME_PATTERN; + +public class MtgaImporter extends PlainTextDeckImporter { + + private static final Map SET_REMAPPING = ImmutableMap.of("DAR", "DOM"); + private static final Pattern MTGA_PATTERN = Pattern.compile( + "(\\p{Digit}+)" + + "\\p{javaWhitespace}+" + + "(" + CARD_NAME_PATTERN.pattern() + ")" + + "\\p{javaWhitespace}+" + + "\\((\\p{Alnum}+)\\)" + + "\\p{javaWhitespace}+" + + "(\\p{Digit}+)"); + + private final CardLookup lookup = getCardLookup(); + private boolean sideboard = false; + + @Override + protected void readLine(String line, DeckCardLists deckList) { + if (line.trim().equals("")) { + sideboard = true; + return; + } + + Matcher m = MTGA_PATTERN.matcher(CardNameUtil.normalizeCardName(line)); + if (m.matches()) { + int count = Integer.parseInt(m.group(1)); + String name = m.group(2); + String set = SET_REMAPPING.getOrDefault(m.group(3), m.group(3)); + String cardNumber = m.group(4); + final List zone = sideboard ? deckList.getSideboard() : deckList.getCards(); + Optional found = lookup.lookupCardInfo(name, set, cardNumber); + if (!found.isPresent()) { + sbMessage.append("Cound not find card for '").append(line).append("'\n"); + } else { + found.ifPresent(card -> zone.addAll(Collections.nCopies(count, + new DeckCardInfo(card.getName(), card.getCardNumber(), card.getSetCode())))); + } + } else { + sbMessage.append("Error reading '").append(line).append("'\n"); + } + } + +} diff --git a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java index 6e3e0e66e2..e38a19e6eb 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java @@ -1,5 +1,6 @@ package mage.cards.decks.importer; +import mage.cards.decks.CardNameUtil; import mage.cards.decks.DeckCardInfo; import mage.cards.decks.DeckCardLists; import mage.cards.repository.CardInfo; @@ -109,18 +110,7 @@ public class TxtDeckImporter extends PlainTextDeckImporter { cardAmount = 1; } - lineName = lineName - .replace("&", "//") - .replace("Æ", "Ae") - .replace("ö", "o") - .replace("û", "u") - .replace("í", "i") - .replace("â", "a") - .replace("á", "a") - .replace("à", "a") - .replace("é", "e") - .replace("ú", "u") - .replace("\"", "'"); + lineName = CardNameUtil.normalizeCardName(lineName); if (lineName.contains("//") && !lineName.contains(" // ")) { lineName = lineName.replace("//", " // "); } diff --git a/Mage/src/test/java/mage/cards/decks/importer/MtgaImporterTest.java b/Mage/src/test/java/mage/cards/decks/importer/MtgaImporterTest.java new file mode 100644 index 0000000000..2db8dea404 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/MtgaImporterTest.java @@ -0,0 +1,43 @@ +package mage.cards.decks.importer; + +import mage.cards.decks.DeckCardLists; +import org.junit.Test; + +import java.io.File; + +import static org.junit.Assert.*; + +public class MtgaImporterTest { + + private static final FakeCardLookup LOOKUP = new FakeCardLookup(); + + @Test + public void testImport() { + MtgaImporter importer = new MtgaImporter() { + @Override + public CardLookup getCardLookup() { + return LOOKUP; + } + }; + StringBuilder errors = new StringBuilder(); + DeckCardLists deck = importer.importDeck( + "src/test/java/mage/cards/decks/importer/samples/testdeck.mtga", errors); + + TestDeckChecker.checker() + .addMain("Niv-Mizzet Reborn", 1) + .addMain("Teferi, Time Raveler", 1) + .addMain("Dovin's Veto", 1) + .addMain("Knight of Autumn", 1) + .addMain("Expansion // Explosion", 1) + .addMain("Forest", 1) + .addMain("Teferi, Hero of Dominaria", 1) + + .addSide("Unmoored Ego", 1) + .addSide("Beacon Bolt", 1) + + .verify(deck, 7, 2); + + assertEquals("", errors.toString()); + } + +} \ No newline at end of file diff --git a/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.mtga b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.mtga new file mode 100644 index 0000000000..3c4fc72401 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.mtga @@ -0,0 +1,10 @@ +1 Niv-Mizzet Reborn (WAR) 208 +1 Teferi, Time Raveler (WAR) 221 +1 Dovin's Veto (WAR) 193 +1 Knight of Autumn (GRN) 183 +1 Expansion // Explosion (GRN) 224 +1 Forest (XLN) 277 +1 Teferi, Hero of Dominaria (DAR) 207 + +1 Unmoored Ego (GRN) 212 +1 Beacon Bolt (GRN) 154