GUI: deck legality improves:

* Added partly valid status for deck legality panel (if all cards are fine but user must add more cards to complete, see #6854);
 * Improved legality errors sorting (important errors visible at the top now, e.g. commander's errors);
This commit is contained in:
Oleg Agafonov 2020-08-03 02:03:54 +04:00
parent 9dfc6eed69
commit e95b9f145c
20 changed files with 335 additions and 201 deletions

View file

@ -2,6 +2,7 @@ package mage.client.components;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidator;
import mage.cards.decks.DeckValidatorError;
import mage.cards.decks.importer.DeckImporter;
import org.unbescape.html.HtmlEscape;
import org.unbescape.html.HtmlEscapeLevel;
@ -10,7 +11,6 @@ import org.unbescape.html.HtmlEscapeType;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.Map;
/**
* @author Elandril
@ -19,6 +19,7 @@ public class LegalityLabel extends JLabel {
protected static final Color COLOR_UNKNOWN = new Color(174, 174, 174);
protected static final Color COLOR_LEGAL = new Color(117, 152, 110);
protected static final Color COLOR_PARTLY_LEGAL = new Color(191, 176, 80);
protected static final Color COLOR_NOT_LEGAL = new Color(191, 84, 74);
protected static final Color COLOR_TEXT = new Color(255, 255, 255);
protected static final Dimension DIM_MINIMUM = new Dimension(75, 25);
@ -105,11 +106,17 @@ public class LegalityLabel extends JLabel {
return HtmlEscape.escapeHtml(string, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA, HtmlEscapeLevel.LEVEL_0_ONLY_MARKUP_SIGNIFICANT_EXCEPT_APOS);
}
protected String formatInvalidTooltip(Map<String, String> invalid) {
return invalid.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
protected String formatInvalidTooltip(java.util.List<DeckValidatorError> sortedErrorsList) {
return sortedErrorsList.stream()
.reduce("<html><body><p>Deck is <span style='color:#BF544A;font-weight:bold;'>INVALID</span></p><u>The following problems have been found:</u><br><table>",
(str, entry) -> String.format("%s<tr><td><b>%s</b></td><td>%s</td></tr>", str, escapeHtml(entry.getKey()), escapeHtml(entry.getValue())), String::concat)
(str, error) -> String.format("%s<tr><td><b>%s</b></td><td>%s</td></tr>", str, escapeHtml(error.getGroup()), escapeHtml(error.getMessage())), String::concat)
+ "</table></body></html>";
}
protected String formatPartlyValidTooltip(java.util.List<DeckValidatorError> sortedErrorsList) {
return sortedErrorsList.stream()
.reduce("<html><body><p>Deck is <span style='color:#b8860b;font-weight:bold;'>PARTLY VALID</span></p><u>The following problems have been found:</u><br><table>",
(str, error) -> String.format("%s<tr><td><b>%s</b></td><td>%s</td></tr>", str, escapeHtml(error.getGroup()), escapeHtml(error.getMessage())), String::concat)
+ "</table></body></html>";
}
@ -139,6 +146,10 @@ public class LegalityLabel extends JLabel {
showState(COLOR_LEGAL, tooltip);
}
public void showStatePartlyLegal(String tooltip) {
showState(COLOR_PARTLY_LEGAL, tooltip);
}
public void showStateNotLegal(String tooltip) {
showState(COLOR_NOT_LEGAL, tooltip);
}
@ -157,8 +168,10 @@ public class LegalityLabel extends JLabel {
try {
if (validator.validate(deck)) {
showStateLegal("<html><body>Deck is <span style='color:green;font-weight:bold;'>VALID</span></body></html>");
} else if (validator.isPartlyValid()) {
showStatePartlyLegal(formatPartlyValidTooltip(validator.getErrorsListSorted()));
} else {
showStateNotLegal(formatInvalidTooltip(validator.getInvalid()));
showStateNotLegal(formatInvalidTooltip(validator.getErrorsListSorted()));
}
} catch (Exception e) {
showStateUnknown(String.format("<html><body><b>Deck could not be validated!</b><br>The following error occurred while validating this deck:<br>%s</body></html>", escapeHtml(e.getMessage())));

View file

@ -4,6 +4,7 @@ import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.decks.Constructed;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidatorErrorType;
import java.util.HashMap;
import java.util.Map;
@ -92,14 +93,14 @@ public class AusHighlander extends Constructed {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
if (deck.getCards().size() != getDeckMinSize()) {
invalid.put("Deck", "Must contain " + getDeckMinSize() + " singleton cards: has " + (deck.getCards().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + getDeckMinSize() + " singleton cards: has " + (deck.getCards().size()) + " cards");
valid = false;
}
if (deck.getSideboard().size() > 15) {
invalid.put("Sideboard", "Must contain at most 15 singleton cards: has " + (deck.getSideboard().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Sideboard", "Must contain at most 15 singleton cards: has " + (deck.getSideboard().size()) + " cards");
valid = false;
}
@ -139,12 +140,12 @@ public class AusHighlander extends Constructed {
String cn = entry.getKey();
if (pointMap.containsKey(cn)) {
totalPoints += pointMap.get(cn);
invalid.put(entry.getKey(), " " + pointMap.get(cn) + " point " + cn);
addError(DeckValidatorErrorType.OTHER, entry.getKey(), " " + pointMap.get(cn) + " point " + cn);
}
}
if (totalPoints > 7) {
invalid.put("Total points too high", "Your calculated point total was " + totalPoints);
invalid.put("Only you can see this!", "Your opponents will not be able to see this message or what cards are in your deck!");
addError(DeckValidatorErrorType.PRIMARY, "Total points too high", "Your calculated point total was " + totalPoints);
addError(DeckValidatorErrorType.PRIMARY, "Only you can see this!", "Your opponents will not be able to see this message or what cards are in your deck!");
valid = false;
}
return valid;

View file

@ -6,6 +6,7 @@ import mage.abilities.keyword.CompanionAbility;
import mage.cards.Card;
import mage.cards.decks.Constructed;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidatorErrorType;
import mage.filter.FilterMana;
import mage.util.ManaUtil;
@ -40,7 +41,7 @@ public class Brawl extends Constructed {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
Card brawler = null;
Card companion = null;
FilterMana colorIdentity = new FilterMana();
@ -60,32 +61,32 @@ public class Brawl extends Constructed {
companion = card2;
brawler = card1;
} else {
invalid.put("Brawl", "Sideboard must contain only the brawler and up to 1 companion");
addError(DeckValidatorErrorType.PRIMARY, "Brawl", "Sideboard must contain only the brawler and up to 1 companion");
valid = false;
}
} else {
invalid.put("Brawl", "Sideboard must contain only the brawler and up to 1 companion");
addError(DeckValidatorErrorType.PRIMARY, "Brawl", "Sideboard must contain only the brawler and up to 1 companion");
valid = false;
}
if (brawler != null) {
ManaUtil.collectColorIdentity(colorIdentity, brawler.getColorIdentity());
if (bannedCommander.contains(brawler.getName())) {
invalid.put("Brawl", "Brawler banned (" + brawler.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Brawl", "Brawler banned (" + brawler.getName() + ')');
valid = false;
}
if (!((brawler.isCreature() && brawler.isLegendary())
|| brawler.isPlaneswalker() || brawler.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) {
invalid.put("Brawl", "Invalid Brawler (" + brawler.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Brawl", "Invalid Brawler (" + brawler.getName() + ')');
valid = false;
}
}
if (companion != null && deck.getCards().size() + deck.getSideboard().size() != getDeckMinSize() + 1) {
invalid.put("Deck", "Must contain " + (getDeckMinSize() + 1) + " cards (companion doesn't count in deck size requirement): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + (getDeckMinSize() + 1) + " cards (companion doesn't count in deck size requirement): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
valid = false;
} else if (companion == null && deck.getCards().size() + deck.getSideboard().size() != getDeckMinSize()) {
invalid.put("Deck", "Must contain " + getDeckMinSize() + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + getDeckMinSize() + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
valid = false;
}
@ -96,7 +97,7 @@ public class Brawl extends Constructed {
for (String bannedCard : banned) {
if (counts.containsKey(bannedCard)) {
invalid.put(bannedCard, "Banned");
addError(DeckValidatorErrorType.BANNED, "Banned", bannedCard);
valid = false;
}
}
@ -114,7 +115,7 @@ public class Brawl extends Constructed {
&& !(colorIdentity.isColorless()
&& basicsInDeck.size() == 1
&& basicsInDeck.contains(card.getName()))) {
invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
valid = false;
}
}
@ -123,14 +124,14 @@ public class Brawl extends Constructed {
&& !(colorIdentity.isColorless()
&& basicsInDeck.size() == 1
&& basicsInDeck.contains(card.getName()))) {
invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
valid = false;
}
}
for (Card card : deck.getCards()) {
if (!isSetAllowed(card.getExpansionSetCode())) {
if (!legalSets(card)) {
invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
valid = false;
}
}
@ -138,7 +139,7 @@ public class Brawl extends Constructed {
for (Card card : deck.getSideboard()) {
if (!isSetAllowed(card.getExpansionSetCode())) {
if (!legalSets(card)) {
invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
valid = false;
}
}
@ -151,7 +152,7 @@ public class Brawl extends Constructed {
if (ability instanceof CompanionAbility) {
CompanionAbility companionAbility = (CompanionAbility) ability;
if (!companionAbility.isLegal(cards, getDeckMinSize())) {
invalid.put(companion.getName(), "Deck invalid for companion");
addError(DeckValidatorErrorType.PRIMARY, companion.getName(), "Deck invalid for companion");
valid = false;
}
break;

View file

@ -4,6 +4,7 @@ import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.decks.Constructed;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidatorErrorType;
import java.util.HashMap;
import java.util.Map;
@ -74,15 +75,15 @@ public class CanadianHighlander extends Constructed {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
if (deck.getCards().size() < 100) {
invalid.put("Deck", "Must contain 100 or more singleton cards: has " + (deck.getCards().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain 100 or more singleton cards: has " + (deck.getCards().size()) + " cards");
valid = false;
}
if (!deck.getSideboard().isEmpty()) {
invalid.put("Deck", "Sideboard can't contain any cards: has " + (deck.getSideboard().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Sideboard can't contain any cards: has " + (deck.getSideboard().size()) + " cards");
valid = false;
}
@ -97,11 +98,11 @@ public class CanadianHighlander extends Constructed {
String cn = entry.getKey();
if (pointMap.containsKey(cn)) {
totalPoints += pointMap.get(cn);
invalid.put(entry.getKey(), " " + pointMap.get(cn) + " point " + cn);
addError(DeckValidatorErrorType.OTHER, entry.getKey(), " " + pointMap.get(cn) + " point " + cn);
}
}
if (totalPoints > allowedPoints) {
invalid.put("Total points too high", "Your calculated point total was " + totalPoints);
addError(DeckValidatorErrorType.PRIMARY, "Total points too high", "Your calculated point total was " + totalPoints);
valid = false;
}
return valid;

View file

@ -12,6 +12,7 @@ import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.decks.Constructed;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidatorErrorType;
import mage.filter.FilterMana;
import mage.util.ManaUtil;
@ -97,7 +98,7 @@ public class Commander extends Constructed {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
FilterMana colorIdentity = new FilterMana();
Set<Card> commanders = new HashSet<>();
Card companion = null;
@ -136,19 +137,19 @@ public class Commander extends Constructed {
commanders.add(card1);
commanders.add(card2);
} else {
invalid.put("Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
valid = false;
}
} else {
invalid.put("Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
valid = false;
}
if (companion != null && deck.getCards().size() + deck.getSideboard().size() != 101) {
invalid.put("Deck", "Must contain " + 101 + " cards (companion doesn't count for deck size): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 101 + " cards (companion doesn't count for deck size): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
valid = false;
} else if (companion == null && deck.getCards().size() + deck.getSideboard().size() != 100) {
invalid.put("Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
valid = false;
}
@ -159,7 +160,7 @@ public class Commander extends Constructed {
for (String bannedCard : banned) {
if (counts.containsKey(bannedCard)) {
invalid.put(bannedCard, "Banned");
addError(DeckValidatorErrorType.BANNED, "Banned", bannedCard);
valid = false;
}
}
@ -170,18 +171,18 @@ public class Commander extends Constructed {
}
for (Card commander : commanders) {
if (bannedCommander.contains(commander.getName())) {
invalid.put("Commander", "Commander banned (" + commander.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander banned (" + commander.getName() + ')');
valid = false;
}
if ((!commander.isCreature() || !commander.isLegendary())
&& (!commander.isPlaneswalker() || !commander.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) {
invalid.put("Commander", "Commander invalid (" + commander.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander invalid (" + commander.getName() + ')');
valid = false;
}
if (commanders.size() == 2) {
if (commander.getAbilities().contains(PartnerAbility.getInstance())) {
if (bannedPartner.contains(commander.getName())) {
invalid.put("Commander", "Partner banned (" + commander.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Partner banned (" + commander.getName() + ')');
valid = false;
}
} else {
@ -192,7 +193,7 @@ public class Commander extends Constructed {
.map(PartnerWithAbility::getPartnerName)
.anyMatch(commanderNames::contains);
if (!partnersWith) {
invalid.put("Commander", "Commander without Partner (" + commander.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander without Partner (" + commander.getName() + ')');
valid = false;
}
}
@ -207,20 +208,20 @@ public class Commander extends Constructed {
for (Card card : deck.getCards()) {
if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) {
invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
valid = false;
}
}
for (Card card : deck.getSideboard()) {
if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) {
invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
valid = false;
}
}
for (Card card : deck.getCards()) {
if (!isSetAllowed(card.getExpansionSetCode())) {
if (!legalSets(card)) {
invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
valid = false;
}
}
@ -228,7 +229,7 @@ public class Commander extends Constructed {
for (Card card : deck.getSideboard()) {
if (!isSetAllowed(card.getExpansionSetCode())) {
if (!legalSets(card)) {
invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
valid = false;
}
}
@ -241,7 +242,7 @@ public class Commander extends Constructed {
if (ability instanceof CompanionAbility) {
CompanionAbility companionAbility = (CompanionAbility) ability;
if (!companionAbility.isLegal(cards, getDeckMinSize())) {
invalid.put(companion.getName(), "Deck invalid for companion");
addError(DeckValidatorErrorType.PRIMARY, companion.getName(), "Deck invalid for companion");
valid = false;
}
break;

View file

@ -2,6 +2,7 @@ package mage.deck;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidator;
import mage.cards.decks.DeckValidatorErrorType;
/**
* @author fireshoes
@ -33,10 +34,10 @@ public class Freeform extends DeckValidator {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
// http://magic.wizards.com/en/gameinfo/gameplay/formats/freeform
if (deck.getCards().size() < getDeckMinSize()) {
invalid.put("Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards");
valid = false;
}
return valid;

View file

@ -9,6 +9,7 @@ import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.decks.Constructed;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidatorErrorType;
import mage.filter.FilterMana;
import mage.util.ManaUtil;
@ -53,7 +54,7 @@ public class FreeformCommander extends Constructed {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
FilterMana colorIdentity = new FilterMana();
Set<Card> commanders = new HashSet<>();
Card companion = null;
@ -92,19 +93,19 @@ public class FreeformCommander extends Constructed {
commanders.add(card1);
commanders.add(card2);
} else {
invalid.put("Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
valid = false;
}
} else {
invalid.put("Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
valid = false;
}
if (companion != null && deck.getCards().size() + deck.getSideboard().size() != 101) {
invalid.put("Deck", "Must contain " + 101 + " cards (companion doesn't count for deck size): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 101 + " cards (companion doesn't count for deck size): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
valid = false;
} else if (companion == null && deck.getCards().size() + deck.getSideboard().size() != 100) {
invalid.put("Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
valid = false;
}
@ -119,7 +120,7 @@ public class FreeformCommander extends Constructed {
}
for (Card commander : commanders) {
if (!commander.isCreature() || !commander.isLegendary()) {
invalid.put("Commander", "For Freeform Commander, the commander must be a creature or be legendary. Yours was: " + commander.getName());
addError(DeckValidatorErrorType.PRIMARY, "Commander", "For Freeform Commander, the commander must be a creature or be legendary. Yours was: " + commander.getName());
valid = false;
}
if (commanders.size() == 2) {
@ -131,7 +132,7 @@ public class FreeformCommander extends Constructed {
.map(PartnerWithAbility::getPartnerName)
.anyMatch(commanderNames::contains);
if (!partnersWith) {
invalid.put("Commander", "Commander without Partner (" + commander.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander without Partner (" + commander.getName() + ')');
valid = false;
}
}
@ -146,13 +147,13 @@ public class FreeformCommander extends Constructed {
for (Card card : deck.getCards()) {
if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) {
invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
valid = false;
}
}
for (Card card : deck.getSideboard()) {
if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) {
invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
valid = false;
}
}
@ -164,7 +165,7 @@ public class FreeformCommander extends Constructed {
if (ability instanceof CompanionAbility) {
CompanionAbility companionAbility = (CompanionAbility) ability;
if (!companionAbility.isLegal(cards, getDeckMinSize())) {
invalid.put(companion.getName(), "Deck invalid for companion");
addError(DeckValidatorErrorType.PRIMARY, companion.getName(), "Deck invalid for companion");
valid = false;
}
break;

View file

@ -4,6 +4,7 @@ import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.decks.Constructed;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidatorError;
import java.util.*;
@ -104,10 +105,10 @@ public class HistoricalType2 extends Constructed {
@Override
public boolean validate(Deck deck) {
Map<String, String> leastInvalid = null;
List<DeckValidatorError> leastInvalid = null;
boolean valid = false;
invalid.clear();
errorsList.clear();
// first, check whether misty and batterskull are in the same deck.
Map<String, Integer> counts = new HashMap<>();
@ -125,7 +126,7 @@ public class HistoricalType2 extends Constructed {
for (String[] sets : standards) {
// clear the invalid list
invalid.clear();
errorsList.clear();
// add the sets to the setCodes.
setCodes = new ArrayList<>(Arrays.asList(sets));
@ -139,15 +140,15 @@ public class HistoricalType2 extends Constructed {
// if the map holding the invalid cards is empty, set it to a
// copy of the current invalid list.
if (leastInvalid == null) {
leastInvalid = new HashMap<>(this.getInvalid());
leastInvalid = new ArrayList<>(this.getErrorsList());
continue;
}
// see how many invalid cards there are. if there are less invalid
// cards than the stored invalid list, assign the current invalid
// to leastInvalid.
if (leastInvalid.size() > this.getInvalid().size()) {
leastInvalid = new HashMap<>(this.getInvalid());
if (leastInvalid.size() > this.getErrorsList().size()) {
leastInvalid = new ArrayList<>(this.getErrorsList());
}
}
@ -165,7 +166,7 @@ public class HistoricalType2 extends Constructed {
// clear the invalid list and set codes.
setCodes.clear();
invalid.clear();
errorsList.clear();
// increment the start and end dates.
start.set(Calendar.YEAR, start.get(Calendar.YEAR) + 1);
@ -183,7 +184,7 @@ public class HistoricalType2 extends Constructed {
// validate it. If it validates, clear the invalid cards and break.
if (super.validate(deck)) {
invalid.clear();
errorsList.clear();
valid = true;
break;
}
@ -192,17 +193,16 @@ public class HistoricalType2 extends Constructed {
// cards than the stored invalid list, assign the current invalid
// to leastInvalid.
if (leastInvalid == null) {
leastInvalid = new HashMap<>(this.getInvalid());
} else if (leastInvalid.size() > this.getInvalid().size()) {
leastInvalid = new HashMap<>(this.getInvalid());
leastInvalid = new ArrayList<>(this.getErrorsList());
} else if (leastInvalid.size() > this.getErrorsList().size()) {
leastInvalid = new ArrayList<>(this.getErrorsList());
}
}
// if no standard environment is valid, set the invalid to the
// invalid that had the least errors.
if (!valid) {
this.invalid = new HashMap<>(leastInvalid);
this.errorsList = new ArrayList<>(leastInvalid);
}
// return the validity.

View file

@ -3,6 +3,7 @@ package mage.deck;
import mage.cards.Card;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidator;
import mage.cards.decks.DeckValidatorErrorType;
import java.util.ArrayList;
import java.util.Arrays;
@ -30,17 +31,17 @@ public class Momir extends DeckValidator {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
if (deck.getCards().size() != getDeckMinSize()) {
invalid.put("Deck", "Must contain " + getDeckMinSize() + " cards: has " + deck.getCards().size() + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + getDeckMinSize() + " cards: has " + deck.getCards().size() + " cards");
valid = false;
}
List<String> basicLandNames = new ArrayList<>(Arrays.asList("Forest", "Island", "Mountain", "Swamp", "Plains", "Wastes"));
for (Card card : deck.getCards()) {
if (!basicLandNames.contains(card.getName())) {
invalid.put(card.getName(), "Only basic lands are allowed");
addError(DeckValidatorErrorType.OTHER, card.getName(), "Only basic lands are allowed");
valid = false;
}
}

View file

@ -5,6 +5,7 @@ import mage.abilities.keyword.PartnerAbility;
import mage.abilities.keyword.PartnerWithAbility;
import mage.cards.Card;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidatorErrorType;
import mage.filter.FilterMana;
import mage.util.ManaUtil;
@ -82,10 +83,10 @@ public class Oathbreaker extends Vintage {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
if (deck.getCards().size() + deck.getSideboard().size() != 60) {
invalid.put("Deck", "Must contain " + 60 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 60 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
valid = false;
}
@ -95,7 +96,7 @@ public class Oathbreaker extends Vintage {
for (String bannedCard : banned) {
if (counts.containsKey(bannedCard)) {
invalid.put(bannedCard, "Banned");
addError(DeckValidatorErrorType.BANNED, "Banned", bannedCard);
valid = false;
}
}
@ -106,7 +107,7 @@ public class Oathbreaker extends Vintage {
Set<String> signatureSpells = new HashSet<>();
FilterMana allCommandersColor = new FilterMana();
if (deck.getSideboard().size() < 2 || deck.getSideboard().size() > 4) {
invalid.put("Oathbreaker", "Sideboard must contain only 2 or 4 cards (oathbreaker + signature spell)");
addError(DeckValidatorErrorType.PRIMARY, "Oathbreaker", "Sideboard must contain only 2 or 4 cards (oathbreaker + signature spell)");
valid = false;
} else {
// collect data
@ -120,7 +121,7 @@ public class Oathbreaker extends Vintage {
// color identity from commanders only, not spell
ManaUtil.collectColorIdentity(allCommandersColor, commander.getColorIdentity());
} else {
invalid.put("Oathbreaker", "Only planeswalker can be Oathbreaker, not " + commander.getName());
addError(DeckValidatorErrorType.PRIMARY, "Oathbreaker", "Only planeswalker can be Oathbreaker, not " + commander.getName());
valid = false;
}
}
@ -128,15 +129,15 @@ public class Oathbreaker extends Vintage {
// check size (1+1 or 2+2 allows)
if (commanderNames.isEmpty() || commanderNames.size() > 2) {
invalid.put("Oathbreaker", "Sideboard must contains 1 or 2 oathbreakers, but found: " + commanderNames.size());
addError(DeckValidatorErrorType.PRIMARY, "Oathbreaker", "Sideboard must contains 1 or 2 oathbreakers, but found: " + commanderNames.size());
valid = false;
}
if (signatureSpells.isEmpty() || signatureSpells.size() > 2) {
invalid.put("Signature Spell", "Sideboard must contains 1 or 2 signature spells, but found: " + signatureSpells.size());
addError(DeckValidatorErrorType.PRIMARY, "Signature Spell", "Sideboard must contains 1 or 2 signature spells, but found: " + signatureSpells.size());
valid = false;
}
if (signatureSpells.size() != commanderNames.size()) {
invalid.put("Oathbreaker", "Sideboard must contains 1 + 1 or 2 + 2 cards, but found: " + commanderNames.size() + " + " + signatureSpells.size());
addError(DeckValidatorErrorType.PRIMARY, "Oathbreaker", "Sideboard must contains 1 + 1 or 2 + 2 cards, but found: " + commanderNames.size() + " + " + signatureSpells.size());
valid = false;
}
@ -153,7 +154,7 @@ public class Oathbreaker extends Vintage {
}
}
if (!partnersWith) {
invalid.put("Oathbreaker", "Oathbreaker without Partner (" + commander.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Oathbreaker", "Oathbreaker without Partner (" + commander.getName() + ')');
valid = false;
}
}
@ -175,7 +176,7 @@ public class Oathbreaker extends Vintage {
}
}
if (!haveSameColor) {
invalid.put("Signature Spell", "Can't find oathbreaker with compatible color identity (" + spell.getName() + " - " + spellColor + ")");
addError(DeckValidatorErrorType.PRIMARY, "Signature Spell", "Can't find oathbreaker with compatible color identity (" + spell.getName() + " - " + spellColor + ")");
valid = false;
}
}
@ -189,7 +190,7 @@ public class Oathbreaker extends Vintage {
for (Card card : deck.getCards()) {
if (!ManaUtil.isColorIdentityCompatible(allCommandersColor, card.getColorIdentity())) {
invalid.put(card.getName(), "Invalid color (" + card.getColorIdentity() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + card.getColorIdentity() + ')');
valid = false;
}
}
@ -197,7 +198,7 @@ public class Oathbreaker extends Vintage {
for (Card card : deck.getSideboard()) {
if (!isSetAllowed(card.getExpansionSetCode())) {
if (!legalSets(card)) {
invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
valid = false;
}
}

View file

@ -10,12 +10,12 @@ import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.decks.Constructed;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidatorErrorType;
import mage.cards.decks.PennyDreadfulLegalityUtil;
import mage.filter.FilterMana;
import mage.util.ManaUtil;
import java.util.*;
import java.util.Map.Entry;
/**
* @author spjspj
@ -47,7 +47,7 @@ public class PennyDreadfulCommander extends Constructed {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
FilterMana colorIdentity = new FilterMana();
Set<Card> commanders = new HashSet<>();
Card companion = null;
@ -86,19 +86,19 @@ public class PennyDreadfulCommander extends Constructed {
commanders.add(card1);
commanders.add(card2);
} else {
invalid.put("Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
valid = false;
}
} else {
invalid.put("Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only the commander(s) and up to 1 companion");
valid = false;
}
if (companion != null && deck.getCards().size() + deck.getSideboard().size() != 101) {
invalid.put("Deck", "Must contain " + 101 + " cards (companion doesn't count for deck size): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 101 + " cards (companion doesn't count for deck size): has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
valid = false;
} else if (companion == null && deck.getCards().size() + deck.getSideboard().size() != 100) {
invalid.put("Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + 100 + " cards: has " + (deck.getCards().size() + deck.getSideboard().size()) + " cards");
valid = false;
}
@ -113,7 +113,7 @@ public class PennyDreadfulCommander extends Constructed {
for (String wantedCard : counts.keySet()) {
if (!(pdAllowed.containsKey(wantedCard))) {
invalid.put(wantedCard, "Banned");
addError(DeckValidatorErrorType.BANNED, "Banned", wantedCard);
valid = false;
}
}
@ -124,12 +124,12 @@ public class PennyDreadfulCommander extends Constructed {
}
for (Card commander : commanders) {
if (bannedCommander.contains(commander.getName())) {
invalid.put("Commander", "Commander banned (" + commander.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander banned (" + commander.getName() + ')');
valid = false;
}
if ((!commander.isCreature() || !commander.isLegendary())
&& (!commander.isPlaneswalker() || !commander.getAbilities().contains(CanBeYourCommanderAbility.getInstance()))) {
invalid.put("Commander", "Commander invalid (" + commander.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander invalid (" + commander.getName() + ')');
valid = false;
}
if (commanders.size() == 2) {
@ -141,7 +141,7 @@ public class PennyDreadfulCommander extends Constructed {
.map(PartnerWithAbility::getPartnerName)
.anyMatch(commanderNames::contains);
if (!partnersWith) {
invalid.put("Commander", "Commander without Partner (" + commander.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander without Partner (" + commander.getName() + ')');
valid = false;
}
}
@ -156,20 +156,20 @@ public class PennyDreadfulCommander extends Constructed {
for (Card card : deck.getCards()) {
if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) {
invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
valid = false;
}
}
for (Card card : deck.getSideboard()) {
if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) {
invalid.put(card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')');
valid = false;
}
}
for (Card card : deck.getCards()) {
if (!isSetAllowed(card.getExpansionSetCode())) {
if (!legalSets(card)) {
invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
valid = false;
}
}
@ -177,7 +177,7 @@ public class PennyDreadfulCommander extends Constructed {
for (Card card : deck.getSideboard()) {
if (!isSetAllowed(card.getExpansionSetCode())) {
if (!legalSets(card)) {
invalid.put(card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set: " + card.getExpansionSetCode());
valid = false;
}
}
@ -190,7 +190,7 @@ public class PennyDreadfulCommander extends Constructed {
if (ability instanceof CompanionAbility) {
CompanionAbility companionAbility = (CompanionAbility) ability;
if (!companionAbility.isLegal(cards, getDeckMinSize())) {
invalid.put(companion.getName(), "Deck invalid for companion");
addError(DeckValidatorErrorType.PRIMARY, companion.getName(), "Deck invalid for companion");
valid = false;
}
break;

View file

@ -4,6 +4,7 @@ import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.decks.Constructed;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidatorError;
import java.util.*;
@ -77,10 +78,10 @@ public class SuperType2 extends Constructed {
@Override
public boolean validate(Deck deck) {
Map<String, String> leastInvalid = null;
List<DeckValidatorError> leastInvalid = null;
boolean valid = false;
invalid.clear();
errorsList.clear();
// first, check whether misty and batterskull are in the same deck.
Map<String, Integer> counts = new HashMap<>();
@ -98,7 +99,7 @@ public class SuperType2 extends Constructed {
for (String[] sets : standards) {
// clear the invalid list
invalid.clear();
errorsList.clear();
// add the sets to the setCodes.
setCodes = new ArrayList<>(Arrays.asList(sets));
@ -120,15 +121,15 @@ public class SuperType2 extends Constructed {
// if the map holding the invalid cards is empty, set it to a
// copy of the current invalid list.
if (leastInvalid == null) {
leastInvalid = new HashMap<>(this.getInvalid());
leastInvalid = new ArrayList<>(this.getErrorsList());
continue;
}
// see how many invalid cards there are. if there are less invalid
// cards than the stored invalid list, assign the current invalid
// to leastInvalid.
if (leastInvalid.size() > this.getInvalid().size()) {
leastInvalid = new HashMap<>(this.getInvalid());
if (leastInvalid.size() > this.getErrorsList().size()) {
leastInvalid = new ArrayList<>(this.getErrorsList());
}
}
@ -146,7 +147,7 @@ public class SuperType2 extends Constructed {
// clear the invalid list and set codes.
setCodes.clear();
invalid.clear();
errorsList.clear();
// increment the start and end dates.
start.set(Calendar.YEAR, start.get(Calendar.YEAR) + 1);
@ -172,7 +173,7 @@ public class SuperType2 extends Constructed {
// validate it. If it validates, clear the invalid cards and break.
if (super.validate(deck)) {
invalid.clear();
errorsList.clear();
valid = true;
break;
}
@ -181,17 +182,17 @@ public class SuperType2 extends Constructed {
// cards than the stored invalid list, assign the current invalid
// to leastInvalid.
if (leastInvalid == null) {
leastInvalid = new HashMap<>(this.getInvalid());
leastInvalid = new ArrayList<>(this.getErrorsList());
} else if (leastInvalid.size() > this.getInvalid().size()) {
leastInvalid = new HashMap<>(this.getInvalid());
} else if (leastInvalid.size() > this.getErrorsList().size()) {
leastInvalid = new ArrayList<>(this.getErrorsList());
}
}
// if no standard environment is valid, set the invalid to the
// invalid that had the least errors.
if (!valid) {
this.invalid = new HashMap<>(leastInvalid);
this.errorsList = new ArrayList<>(leastInvalid);
}
// return the validity.

View file

@ -6,6 +6,7 @@ import mage.cards.Sets;
import mage.cards.SplitCard;
import mage.cards.decks.Constructed;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidatorErrorType;
import mage.filter.FilterMana;
import mage.game.GameTinyLeadersImpl;
@ -103,10 +104,10 @@ public class TinyLeaders extends Constructed {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
if (deck.getCards().size() != getDeckMinSize()) {
invalid.put("Deck", "Must contain " + getDeckMinSize() + " cards: has " + deck.getCards().size() + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain " + getDeckMinSize() + " cards: has " + deck.getCards().size() + " cards");
valid = false;
}
@ -118,7 +119,7 @@ public class TinyLeaders extends Constructed {
for (String bannedCard : banned) {
if (counts.containsKey(bannedCard)) {
invalid.put(bannedCard, "Banned");
addError(DeckValidatorErrorType.BANNED, "Banned", bannedCard);
valid = false;
}
}
@ -135,16 +136,16 @@ public class TinyLeaders extends Constructed {
if (commander == null || commander.getManaCost().convertedManaCost() > 3) {
if (commander == null) {
if (deck.getName() == null) {
invalid.put("Leader", "You have to save your deck with the leader card name entered to the DECK NAME field of the DECK EDITOR (top left) so that XMage knows your leader."
addError(DeckValidatorErrorType.PRIMARY, "Leader", "You have to save your deck with the leader card name entered to the DECK NAME field of the DECK EDITOR (top left) so that XMage knows your leader."
+ "(You can use the \"Sultai\" for a UBG (3/3) default Commander or \"Glass\" for a colorless 3/3 default Commander.)");
} else {
invalid.put("Leader", "Leader [" + deck.getName() + "] not found. You have to enter the name of the leader card into the DECK NAME field of the DECK EDITOR (top left). Check your spelling "
addError(DeckValidatorErrorType.PRIMARY, "Leader", "Leader [" + deck.getName() + "] not found. You have to enter the name of the leader card into the DECK NAME field of the DECK EDITOR (top left). Check your spelling "
+ "(use the \"Sultai\" for a UBG (3/3) default Commander or \"Glass\" for a colorless (3/3) default Commander)");
}
}
if (commander != null && commander.getManaCost().convertedManaCost() > 3) {
invalid.put("Leader", "Commanders converted mana cost is greater than 3");
addError(DeckValidatorErrorType.PRIMARY, "Leader", "Commanders converted mana cost is greater than 3");
}
return false;
}
@ -162,21 +163,21 @@ public class TinyLeaders extends Constructed {
}
}
} else {
invalid.put("Commander", "Commander banned (" + commander.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander banned (" + commander.getName() + ')');
valid = false;
}
} else {
invalid.put("Commander", "Commander invalide (" + commander.getName() + ')');
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Commander invalide (" + commander.getName() + ')');
valid = false;
}
} else {
invalid.put("Commander", "Sideboard must contain only a maximum of 10 sideboard cards (the Tiny Leader name must be written to the deck name)");
addError(DeckValidatorErrorType.PRIMARY, "Commander", "Sideboard must contain only a maximum of 10 sideboard cards (the Tiny Leader name must be written to the deck name)");
valid = false;
}
for (Card card : deck.getCards()) {
if (!isSetAllowed(card.getExpansionSetCode())) {
if (!legalSets(card)) {
invalid.put(card.getName(), "Not allowed Set " + card.getExpansionSetCode());
addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set " + card.getExpansionSetCode());
valid = false;
}
}
@ -184,7 +185,7 @@ public class TinyLeaders extends Constructed {
for (Card card : deck.getSideboard()) {
if (!isSetAllowed(card.getExpansionSetCode())) {
if (!legalSets(card)) {
invalid.put(card.getName(), "Not allowed Set " + card.getExpansionSetCode());
addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Not allowed Set " + card.getExpansionSetCode());
valid = false;
}
}
@ -194,22 +195,22 @@ public class TinyLeaders extends Constructed {
private boolean isCardFormatValid(Card card, Card commander, FilterMana color) {
if (!cardHasValideColor(color, card)) {
invalid.put(card.getName(), "Invalid color (" + commander.getName() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + commander.getName() + ')');
return false;
}
//905.5b - Converted mana cost must be 3 or less
if (card instanceof SplitCard) {
if (((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() > 3) {
invalid.put(card.getName(), "Invalid cost (" + ((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid cost (" + ((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() + ')');
return false;
}
if (((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() > 3) {
invalid.put(card.getName(), "Invalid cost (" + ((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid cost (" + ((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() + ')');
return false;
}
} else if (card.getManaCost().convertedManaCost() > 3) {
invalid.put(card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ')');
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ')');
return false;
}
return true;

View file

@ -2,6 +2,7 @@ package mage.deck;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidator;
import mage.cards.decks.DeckValidatorErrorType;
import java.util.HashMap;
import java.util.Map;
@ -28,17 +29,17 @@ public class Limited extends DeckValidator {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
//20091005 - 100.2b
if (deck.getCards().size() < getDeckMinSize()) {
invalid.put("Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards");
valid = false;
}
Map<String, Integer> counts = new HashMap<>();
countCards(counts, deck.getCards());
for (Map.Entry<String, Integer> entry : counts.entrySet()) {
if (entry.getValue() > 7 && entry.getKey().equals("Seven Dwarves")) {
invalid.put(entry.getKey(), "Too many: " + entry.getValue());
addError(DeckValidatorErrorType.OTHER, entry.getKey(), "Too many: " + entry.getValue());
valid = false;
}
}

View file

@ -3,6 +3,7 @@ package mage.server;
import mage.MageException;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLists;
import mage.cards.decks.DeckValidatorError;
import mage.cards.decks.DeckValidatorFactory;
import mage.constants.RangeOfInfluence;
import mage.constants.TableState;
@ -32,11 +33,8 @@ import mage.server.util.ThreadExecutor;
import mage.view.ChatMessage;
import org.apache.log4j.Logger;
import java.util.Locale;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@ -150,9 +148,10 @@ public class TableController {
}
if (!Main.isTestMode() && !table.getValidator().validate(deck)) {
StringBuilder sb = new StringBuilder("You (").append(name).append(") have an invalid deck for the selected ").append(table.getValidator().getName()).append(" Format. \n\n");
for (Map.Entry<String, String> entry : table.getValidator().getInvalid().entrySet()) {
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n');
}
List<DeckValidatorError> errorsList = table.getValidator().getErrorsListSorted();
errorsList.stream().forEach(error -> {
sb.append(error.getGroup()).append(": ").append(error.getMessage()).append("\n");
});
sb.append("\n\nSelect a deck that is appropriate for the selected format and try again!");
user.showUserMessage("Join Table", sb.toString());
if (isOwner(userId)) {
@ -267,9 +266,10 @@ public class TableController {
if (!Main.isTestMode() && !table.getValidator().validate(deck)) {
StringBuilder sb = new StringBuilder("You (").append(name).append(") have an invalid deck for the selected ").append(table.getValidator().getName()).append(" Format. \n\n");
for (Map.Entry<String, String> entry : table.getValidator().getInvalid().entrySet()) {
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n');
}
List<DeckValidatorError> errorsList = table.getValidator().getErrorsListSorted();
errorsList.stream().forEach(error -> {
sb.append(error.getGroup()).append(": ").append(error.getMessage()).append("\n");
});
sb.append("\n\nSelect a deck that is appropriate for the selected format and try again!");
user.showUserMessage("Join Table", sb.toString());
if (isOwner(userId)) {
@ -425,9 +425,10 @@ public class TableController {
return false;
}
StringBuilder sb = new StringBuilder("Invalid deck for the selected ").append(table.getValidator().getName()).append(" format. \n\n");
for (Map.Entry<String, String> entry : table.getValidator().getInvalid().entrySet()) {
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n');
}
List<DeckValidatorError> errorsList = table.getValidator().getErrorsListSorted();
errorsList.stream().forEach(error -> {
sb.append(error.getGroup()).append(": ").append(error.getMessage()).append("\n");
});
sb.append("\n\nAdd enough cards and try again!");
_user.get().showUserMessage("Submit deck", sb.toString());
return false;

View file

@ -1,8 +1,5 @@
package org.mage.test.serverside.deck;
import java.util.ArrayList;
import java.util.List;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckValidator;
import mage.cards.repository.CardInfo;
@ -14,8 +11,10 @@ import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.MageTestBase;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author LevelX2
*/
public class DeckValidatorTest extends MageTestBase {
@ -65,7 +64,7 @@ public class DeckValidatorTest extends MageTestBase {
DeckValidator validator = new Standard();
boolean validationSuccessful = testDeckValid(validator, deck);
Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful);
Assert.assertTrue(validator.getErrorsListInfo(), validationSuccessful);
}
@Test
@ -79,7 +78,7 @@ public class DeckValidatorTest extends MageTestBase {
DeckValidator validator = new Standard();
testDeckValid(validator, deck, sideboard);
Assert.assertEquals("invalid message not correct",
"{Sideboard=Must contain no more than 15 cards : has 16 cards, Deck=Must contain at least 60 cards: has only 59 cards}", validator.getInvalid().toString());
"Deck=Must contain at least 60 cards: has only 59 cards, Sideboard=Must contain no more than 15 cards : has 16 cards", validator.getErrorsListInfo());
}
@Test
@ -229,119 +228,119 @@ public class DeckValidatorTest extends MageTestBase {
deckList.add(new CardNameAmount("Ancestral Vision", 4));
deckList.add(new CardNameAmount("Mountain", 56));
boolean validationSuccessful = testDeckValid(validator, deckList);
Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertTrue(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Ancient Den", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.add(new CardNameAmount("Birthing Pod", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Blazing Shoal", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Bloodbraid Elf", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertTrue(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Chrome Mox", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Cloudpost", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Dark Depths", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Deathrite Shaman", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Dig Through Time", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Dread Return", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Glimpse of Nature", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Great Furnace", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Green Sun's Zenith", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Hypergenesis", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Jace, the Mind Sculptor", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertTrue(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertTrue(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
deckList.clear();
deckList.add(new CardNameAmount("Mental Misstep", 4));
deckList.add(new CardNameAmount("Mountain", 56));
validationSuccessful = testDeckValid(validator, deckList);
Assert.assertFalse(validator.getInvalid().toString(), validationSuccessful);
validator.getInvalid().clear();
Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful);
validator.getErrorsList().clear();
}
private boolean testDeckValid(DeckValidator validator, List<CardNameAmount> cards) {

View file

@ -58,15 +58,15 @@ public class Constructed extends DeckValidator {
@Override
public boolean validate(Deck deck) {
boolean valid = true;
invalid.clear();
errorsList.clear();
//20091005 - 100.2a
if (deck.getCards().size() < getDeckMinSize()) {
invalid.put("Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Deck", "Must contain at least " + getDeckMinSize() + " cards: has only " + deck.getCards().size() + " cards");
valid = false;
}
//20130713 - 100.4a
if (deck.getSideboard().size() > 15) {
invalid.put("Sideboard", "Must contain no more than 15 cards : has " + deck.getSideboard().size() + " cards");
addError(DeckValidatorErrorType.DECK_SIZE, "Sideboard", "Must contain no more than 15 cards : has " + deck.getSideboard().size() + " cards");
valid = false;
}
@ -77,7 +77,7 @@ public class Constructed extends DeckValidator {
for (String bannedCard : banned) {
if (counts.containsKey(bannedCard)) {
invalid.put(bannedCard, "Banned");
addError(DeckValidatorErrorType.BANNED, "Banned", bannedCard);
valid = false;
}
}
@ -86,7 +86,7 @@ public class Constructed extends DeckValidator {
if (counts.containsKey(restrictedCard)) {
int count = counts.get(restrictedCard);
if (count > 1) {
invalid.put(restrictedCard, "Restricted: " + count);
addError(DeckValidatorErrorType.OTHER, restrictedCard, "Restricted amount: " + count);
valid = false;
}
}
@ -142,8 +142,8 @@ public class Constructed extends DeckValidator {
break;
}
}
if (!legal && !invalid.containsKey(card.getName())) {
invalid.put(card.getName(), "Invalid rarity: " + card.getRarity());
if (!legal && !errorsListContainsGroup(card.getName())) {
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid rarity: " + card.getRarity());
}
return legal;
}
@ -180,8 +180,8 @@ public class Constructed extends DeckValidator {
legal = true;
}
if (!legal && !invalid.containsKey(card.getName())) {
invalid.put(card.getName(), "Invalid set: " + card.getExpansionSetCode());
if (!legal && !errorsListContainsGroup(card.getName())) {
addError(DeckValidatorErrorType.WRONG_SET, card.getName(), "Invalid set: " + card.getExpansionSetCode());
}
return legal;
}
@ -192,11 +192,11 @@ public class Constructed extends DeckValidator {
if (entry.getValue() > maxCopies
&& !basicLandNames.contains(entry.getKey())
&& !anyNumberCardsAllowed.contains(entry.getKey())) {
invalid.put(entry.getKey(), "Too many: " + entry.getValue());
addError(DeckValidatorErrorType.OTHER, entry.getKey(), "Too many: " + entry.getValue());
valid = false;
}
if (entry.getValue() > 7 && entry.getKey().equals("Seven Dwarves")) {
invalid.put(entry.getKey(), "Too many: " + entry.getValue());
addError(DeckValidatorErrorType.OTHER, entry.getKey(), "Too many: " + entry.getValue());
valid = false;
}
}

View file

@ -3,9 +3,8 @@ package mage.cards.decks;
import mage.cards.Card;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author BetaSteward_at_googlemail.com
@ -14,8 +13,7 @@ public abstract class DeckValidator implements Serializable {
protected String name;
protected String shortName;
protected Map<String, String> invalid = new HashMap<>();
protected List<DeckValidatorError> errorsList = new ArrayList<>();
public DeckValidator(String name) {
setName(name);
@ -49,8 +47,62 @@ public abstract class DeckValidator implements Serializable {
this.shortName = shortName;
}
public Map<String, String> getInvalid() {
return invalid;
public List<DeckValidatorError> getErrorsList() {
return this.errorsList;
}
/**
* Get errors list sorted by error type and texts
*
* @return
*/
public List<DeckValidatorError> getErrorsListSorted() {
List<DeckValidatorError> list = new ArrayList<>(this.getErrorsList());
Collections.sort(list, new Comparator<DeckValidatorError>() {
@Override
public int compare(DeckValidatorError e1, DeckValidatorError e2) {
int res = 0;
// sort by error type
Integer order1 = e1.getErrorType().getSortOrder();
Integer order2 = e2.getErrorType().getSortOrder();
res = order2.compareTo(order1);
// sort by group
if (res != 0) {
res = e2.getGroup().compareTo(e1.getGroup());
}
// sort by message
if (res != 0) {
res = e2.getMessage().compareTo(e1.getMessage());
}
return res;
}
});
return list;
}
public String getErrorsListInfo() {
// for tests
return this.errorsList.stream()
.map(e -> e.getGroup() + "=" + e.getMessage())
.collect(Collectors.joining(", "));
}
public void addError(DeckValidatorErrorType errorType, String group, String message) {
this.errorsList.add(new DeckValidatorError(errorType, group, message));
}
public boolean errorsListContainsGroup(String group) {
return this.errorsList.stream().anyMatch(e -> e.getGroup().equals(group));
}
public boolean isPartlyValid() {
return errorsList.size() == 0 || !errorsList.stream().anyMatch(e -> !e.getErrorType().isPartlyLegal());
}
protected void countCards(Map<String, Integer> counts, Collection<Card> cards) {

View file

@ -0,0 +1,29 @@
package mage.cards.decks;
/**
* @author JayDi85
*/
public class DeckValidatorError {
private final DeckValidatorErrorType errorType;
private final String group;
private final String message;
public DeckValidatorError(DeckValidatorErrorType errorType, String group, String message) {
this.errorType = errorType;
this.group = group;
this.message = message;
}
public DeckValidatorErrorType getErrorType() {
return this.errorType;
}
public String getGroup() {
return this.group;
}
public String getMessage() {
return this.message;
}
}

View file

@ -0,0 +1,29 @@
package mage.cards.decks;
/**
* @author JayDi85
*/
public enum DeckValidatorErrorType {
PRIMARY(false, 10), // first errors to show (e.g. missing commander)
DECK_SIZE(true, 20), // wrong deck size (deck must be legal while building)
BANNED(false, 30),
WRONG_SET(false, 40),
OTHER(false, 50);
private final boolean partlyLegal; // for deck legality panel: is it partly legal (e.g. show deck legal even without full deck size)
private final int sortOrder; // errors list sort order from small to big
DeckValidatorErrorType(boolean partlyLegal, int sortOrder) {
this.partlyLegal = partlyLegal;
this.sortOrder = sortOrder;
}
public boolean isPartlyLegal() {
return this.partlyLegal;
}
public int getSortOrder() {
return this.sortOrder;
}
}