From 339c419d4bc7783d4e6c16adf3e4d91f83961999 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 7 Jan 2020 11:49:55 +0400 Subject: [PATCH] * UI: multiple improves for adventure/split cards: * Split cards shows left and right mana cost (in deck editor, hand, etc); * Adventure cards shows adventure and normal cost (in deck editor, hand, etc); * Adventure cards shows adventure spell name in deck editor's list; * Fixed missing loading cursor in deck editor searching; --- .../mage/client/deckeditor/CardSelector.java | 4 +- .../client/deckeditor/table/TableModel.java | 2 +- .../client/dialog/TestCardRenderDialog.java | 25 +++++- .../card/arcane/CardPanelComponentImpl.java | 2 +- .../org/mage/card/arcane/ManaSymbols.java | 26 ++++-- .../card/arcane/ManaSymbolsCellRenderer.java | 15 +++- .../mage/card/arcane/ModernCardRenderer.java | 9 +- .../mage/card/arcane/TextboxRuleParser.java | 27 +++--- .../src/main/java/mage/view/AbilityView.java | 7 +- .../src/main/java/mage/view/CardView.java | 83 +++++++++++++++---- .../main/java/mage/view/StackAbilityView.java | 7 +- .../main/java/mage/server/ChatManager.java | 2 +- .../main/java/mage/cards/mock/MockCard.java | 45 +++++++++- .../java/mage/cards/mock/MockSplitCard.java | 4 +- .../java/mage/cards/repository/CardInfo.java | 71 ++++++++++++++-- .../mage/cards/repository/CardRepository.java | 6 +- .../predicate/other/CardTextPredicate.java | 34 +++++--- Mage/src/main/java/mage/util/CardUtil.java | 44 ++++++---- 18 files changed, 311 insertions(+), 102 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java index ab7c601c4b..32ebb2273d 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java @@ -417,9 +417,9 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene private void filterCards() { FilterCard filter = buildFilter(); + MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR)); try { java.util.List filteredCards = new ArrayList<>(); - setCursor(new Cursor(Cursor.WAIT_CURSOR)); boolean chkPD = chkPennyDreadful.isSelected(); if (chkPD) { @@ -452,7 +452,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene this.currentView.loadCards(new CardsView(filteredCards), sortSetting, bigCard, null, false); this.cardCount.setText(String.valueOf(filteredCards.size())); } finally { - setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java b/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java index ee8f160a7e..dacaf0df74 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java @@ -212,7 +212,7 @@ public class TableModel extends AbstractTableModel implements ICardGrid { } return ""; case 1: - return c.getName(); + return c.getDisplayFullName(); // show full name in deck editor table, e.g. adventure with spell name case 2: // new svg images version return ManaSymbols.getStringManaCost(c.getManaCost()); diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java index 0ec31e57d6..937ef27081 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -18,8 +18,6 @@ import mage.game.Game; import mage.game.GameImpl; import mage.game.command.Emblem; import mage.game.command.Plane; -import mage.game.command.emblems.AjaniAdversaryOfTyrantsEmblem; -import mage.game.command.planes.AkoumPlane; import mage.game.match.MatchType; import mage.game.mulligan.Mulligan; import mage.game.mulligan.MulliganType; @@ -97,6 +95,21 @@ public class TestCardRenderDialog extends MageDialog { return cardView; } + private CardView createHandCard(Game game, UUID controllerId, String code, String cardNumber, int power, int toughness, int damage) { + CardInfo cardInfo = CardRepository.instance.findCard(code, cardNumber); + ExpansionInfo setInfo = ExpansionRepository.instance.getSetByCode(code); + CardSetInfo testSet = new CardSetInfo(cardInfo.getName(), setInfo.getCode(), cardNumber, cardInfo.getRarity(), + new CardGraphicInfo(cardInfo.getFrameStyle(), cardInfo.usesVariousArt())); + Card card = CardImpl.createCard(cardInfo.getClassName(), testSet); + + Set cardsList = new HashSet<>(); + cardsList.add(card); + game.loadCards(cardsList, controllerId); + + CardView cardView = new CardView(card); + return cardView; + } + private AbilityView createEmblem(Emblem emblem) { AbilityView emblemView = new AbilityView(emblem.getAbilities().get(0), emblem.getName(), new CardView(new EmblemView(emblem))); emblemView.setName(emblem.getName()); @@ -121,10 +134,17 @@ public class TestCardRenderDialog extends MageDialog { BigCard big = new BigCard(); CardsView view = new CardsView(); CardView card; + /* card = createCard(game, player.getId(), "RNA", "263", 0, 0, 0); // mountain view.put(card.getId(), card); card = createCard(game, player.getId(), "RNA", "185", 0, 0, 0); // Judith, the Scourge Diva view.put(card.getId(), card); + //*/ + card = createHandCard(game, player.getId(), "DIS", "153", 0, 0, 0); // Odds // Ends (split card) + view.put(card.getId(), card); + card = createHandCard(game, player.getId(), "ELD", "38", 2, 2, 0); // Animating Faerie (adventure card) + view.put(card.getId(), card); + /* card = createCard(game, player.getId(), "RNA", "78", 125, 89, 0); // Noxious Groodion view.put(card.getId(), card); card = createCard(game, player.getId(), "RNA", "14", 3, 5, 2); // Knight of Sorrows @@ -139,6 +159,7 @@ public class TestCardRenderDialog extends MageDialog { view.put(card.getId(), card); card = createPlane(new AkoumPlane()); // Plane - Akoum view.put(card.getId(), card); + //*/ cardsPanel.setCustomCardSize(new Dimension(getCardWidth(), getCardHeight())); cardsPanel.changeGUISize(); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java index 523d97a111..87d4033162 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java @@ -525,7 +525,7 @@ public class CardPanelComponentImpl extends CardPanel { int manaX = getCardXOffset() + getCardWidth() - manaMarginRight - manaWidth; int manaY = getCardYOffset() + manaMarginTop; - ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth(), Color.black, symbolMarginX); + ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth(), ModernCardRenderer.MANA_ICONS_TEXT_COLOR, symbolMarginX); } } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java index 75d361cbf7..306f098a87 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java @@ -1,6 +1,7 @@ package org.mage.card.arcane; import mage.abilities.hint.HintUtils; +import mage.cards.repository.CardInfo; import mage.cards.repository.ExpansionRepository; import mage.client.MageFrame; import mage.client.constants.Constants; @@ -587,7 +588,7 @@ public final class ManaSymbols { } public static void draw(Graphics g, String manaCost, int x, int y, int symbolWidth) { - draw(g, manaCost, x, y, symbolWidth, Color.white, 0); + draw(g, manaCost, x, y, symbolWidth, ModernCardRenderer.MANA_ICONS_TEXT_COLOR, 0); } public static void draw(Graphics g, String manaCost, int x, int y, int symbolWidth, Color symbolsTextColor, int symbolMarginX) { @@ -657,20 +658,24 @@ public final class ManaSymbols { if (image == null) { // TEXT draw - - labelRender.setText("{" + symbol + "}"); - labelRender.setForeground(symbolsTextColor); + String sampleAutoFontText = "{W}"; // need same font size for all -- use max symbol ever, not current text + if (symbol.equals(CardInfo.SPLIT_MANA_SEPARATOR_SHORT)) { + labelRender.setText(CardInfo.SPLIT_MANA_SEPARATOR_RENDER); + sampleAutoFontText = CardInfo.SPLIT_MANA_SEPARATOR_RENDER; // separator must be big + } else { + labelRender.setText("{" + symbol + "}"); + } labelRender.setSize(symbolWidth, symbolWidth); labelRender.setVerticalAlignment(SwingConstants.CENTER); + labelRender.setForeground(symbolsTextColor); labelRender.setHorizontalAlignment(SwingConstants.CENTER); - //labelRender.setBorder(new LineBorder(new Color(125, 250, 250), 1)); + //labelRender.setBorder(new LineBorder(new Color(125, 250, 250), 1)); // debug draw // fix font size for mana text // work for labels WITHOUT borders // https://stackoverflow.com/questions/2715118/how-to-change-the-size-of-the-font-of-a-jlabel-to-take-the-maximum-size Font labelFont = labelRender.getFont(); - String labelText = "{W}"; //labelRender.getText(); // need same font size for all -- use max symbol ever, not current text - int stringWidth = labelRender.getFontMetrics(labelFont).stringWidth(labelText); + int stringWidth = labelRender.getFontMetrics(labelFont).stringWidth(sampleAutoFontText); int componentWidth = labelRender.getWidth(); // Find out how much the font can grow in width. double widthRatio = (double) componentWidth / (double) stringWidth; @@ -702,7 +707,11 @@ public final class ManaSymbols { for (String s : manaCost) { sb.append(s); } - return sb.toString().replace("/", "").replace("{", "").replace("}", " ").trim(); + return sb.toString() + .replace("/", "") + .replace("{", "") + .replace("}", " ") + .trim(); } public enum Type { @@ -767,6 +776,7 @@ public final class ManaSymbols { htmlImagesPath = htmlImagesPath .replace("$", "@S@"); // paths with $ will rise error, need escape that + replaced = replaced.replace(CardInfo.SPLIT_MANA_SEPARATOR_FULL, CardInfo.SPLIT_MANA_SEPARATOR_RENDER); replaced = REPLACE_SYMBOLS_PATTERN.matcher(replaced).replaceAll( "$1$2(); } public CardView getSourceCard() { diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index cc7a62c719..45693aa8bb 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -1,8 +1,6 @@ package mage.view; import com.google.gson.annotations.Expose; -import java.util.*; -import java.util.stream.Collectors; import mage.MageObject; import mage.ObjectColor; import mage.abilities.Abilities; @@ -12,6 +10,8 @@ import mage.abilities.SpellAbility; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.keyword.AftermathAbility; import mage.cards.*; +import mage.cards.mock.MockCard; +import mage.cards.repository.CardInfo; import mage.constants.*; import mage.counters.Counter; import mage.counters.CounterType; @@ -26,8 +26,12 @@ import mage.game.stack.Spell; import mage.game.stack.StackAbility; import mage.target.Target; import mage.target.Targets; +import mage.util.CardUtil; import mage.util.SubTypeList; +import java.util.*; +import java.util.stream.Collectors; + /** * @author BetaSteward_at_googlemail.com */ @@ -41,6 +45,8 @@ public class CardView extends SimpleCardView { @Expose protected String displayName; @Expose + protected String displayFullName; + @Expose protected List rules; @Expose protected String power; @@ -55,7 +61,9 @@ public class CardView extends SimpleCardView { protected ObjectColor color; protected ObjectColor frameColor; protected FrameStyle frameStyle; - protected List manaCost; + // can combine multiple costs for MockCard from deck editor or db (left/right, card/adventure) + protected List manaCostLeft; + protected List manaCostRight; protected int convertedManaCost; protected Rarity rarity; @@ -137,6 +145,7 @@ public class CardView extends SimpleCardView { this.parentId = cardView.parentId; this.name = cardView.name; this.displayName = cardView.displayName; + this.displayFullName = cardView.displayFullName; this.rules = cardView.rules; this.power = cardView.power; this.toughness = cardView.toughness; @@ -148,7 +157,8 @@ public class CardView extends SimpleCardView { this.color = cardView.color; this.frameColor = cardView.frameColor; this.frameStyle = cardView.frameStyle; - this.manaCost = cardView.manaCost; + this.manaCostLeft = cardView.manaCostLeft; + this.manaCostRight = cardView.manaCostRight; this.convertedManaCost = cardView.convertedManaCost; this.rarity = cardView.rarity; @@ -210,8 +220,8 @@ public class CardView extends SimpleCardView { * @param card * @param game * @param controlled is the card view created for the card controller - used - * for morph / face down cards to know which player may see information for - * the card + * for morph / face down cards to know which player may see information for + * the card */ public CardView(Card card, Game game, boolean controlled) { this(card, game, controlled, false, false); @@ -237,12 +247,12 @@ public class CardView extends SimpleCardView { /** * @param card * @param game - * @param controlled is the card view created for the card controller - used - * for morph / face down cards to know which player may see information for - * the card + * @param controlled is the card view created for the card controller - used + * for morph / face down cards to know which player may see information for + * the card * @param showFaceDownCard if true and the card is not on the battlefield, - * also a face down card is shown in the view, face down cards will be shown - * @param storeZone if true the card zone will be set in the zone attribute. + * also a face down card is shown in the view, face down cards will be shown + * @param storeZone if true the card zone will be set in the zone attribute. */ public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) { super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null, card.getTokenDescriptor()); @@ -275,6 +285,7 @@ public class CardView extends SimpleCardView { if (controlled) { this.name = card.getName(); this.displayName = card.getName(); + this.displayFullName = card.getName(); this.alternateName = card.getName(); } this.power = "2"; @@ -313,6 +324,15 @@ public class CardView extends SimpleCardView { break; } } + + AdventureCard adventureCard = null; + AdventureCardSpell adventureCardSpell = null; + if (card instanceof AdventureCard) { + adventureCard = (AdventureCard) card; + adventureCardSpell = (AdventureCardSpell) adventureCard.getSpellCard(); + } + + String fullCardName; if (splitCard != null) { this.isSplitCard = true; leftSplitName = splitCard.getLeftHalfCard().getName(); @@ -323,16 +343,34 @@ public class CardView extends SimpleCardView { rightSplitCosts = splitCard.getRightHalfCard().getManaCost(); rightSplitRules = splitCard.getRightHalfCard().getRules(game); rightSplitTypeLine = getCardTypeLine(game, splitCard.getRightHalfCard()); + + fullCardName = card.getName(); // split card contains full name as normal + this.manaCostLeft = splitCard.getLeftHalfCard().getManaCost().getSymbols(); + this.manaCostRight = splitCard.getRightHalfCard().getManaCost().getSymbols(); + } else if (adventureCard != null) { + fullCardName = adventureCard.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + adventureCardSpell.getName(); + this.manaCostLeft = adventureCardSpell.getManaCost().getSymbols(); + this.manaCostRight = adventureCard.getManaCost().getSymbols(); + } else if (card instanceof MockCard) { + // deck editor cards + fullCardName = ((MockCard) card).getFullName(true); + this.manaCostLeft = ((MockCard) card).getManaCost(CardInfo.ManaCostSide.LEFT).getSymbols(); + this.manaCostRight = ((MockCard) card).getManaCost(CardInfo.ManaCostSide.RIGHT).getSymbols(); + } else { + fullCardName = card.getName(); + this.manaCostLeft = card.getManaCost().getSymbols(); + this.manaCostRight = new ArrayList<>(); } + //this.manaCost = card.getManaCost().getSymbols(); this.name = card.getImageName(); this.displayName = card.getName(); + this.displayFullName = fullCardName; if (game == null) { this.rules = card.getRules(); } else { this.rules = card.getRules(game); } - this.manaCost = card.getManaCost().getSymbols(); this.convertedManaCost = card.getManaCost().convertedManaCost(); if (card instanceof Permanent) { @@ -475,6 +513,7 @@ public class CardView extends SimpleCardView { this.name = object.getName(); this.displayName = object.getName(); + this.displayFullName = object.getName(); if (object instanceof Permanent) { this.mageObjectType = MageObjectType.PERMANENT; this.power = Integer.toString(object.getPower().getValue()); @@ -489,7 +528,8 @@ public class CardView extends SimpleCardView { this.subTypes = object.getSubtype(null); this.superTypes = object.getSuperType(); this.color = object.getColor(null); - this.manaCost = object.getManaCost().getSymbols(); + this.manaCostLeft = object.getManaCost().getSymbols(); + this.manaCostRight = new ArrayList<>(); this.convertedManaCost = object.getManaCost().convertedManaCost(); if (object instanceof PermanentToken) { this.mageObjectType = MageObjectType.TOKEN; @@ -547,6 +587,7 @@ public class CardView extends SimpleCardView { this.mageObjectType = MageObjectType.EMBLEM; this.name = emblem.getName(); this.displayName = name; + this.displayFullName = name; this.rules = emblem.getRules(); // emblem images are always with common (black) symbol this.frameStyle = FrameStyle.M15_NORMAL; @@ -561,6 +602,7 @@ public class CardView extends SimpleCardView { this.mageObjectType = MageObjectType.PLANE; this.name = plane.getName(); this.displayName = name; + this.displayFullName = name; this.rules = plane.getRules(); // Display the plane in landscape (similar to Fused cards) this.rotate = true; @@ -576,6 +618,7 @@ public class CardView extends SimpleCardView { this.mageObjectType = MageObjectType.NULL; this.name = designation.getName(); this.displayName = name; + this.displayFullName = name; this.rules = new ArrayList<>(); this.rules.add(stackAbility.getRule(designation.getName())); this.frameStyle = FrameStyle.M15_NORMAL; @@ -594,6 +637,7 @@ public class CardView extends SimpleCardView { private void fillEmpty(Card card, boolean controlled) { this.name = "Face Down"; this.displayName = name; + this.displayFullName = name; this.rules = new ArrayList<>(); this.power = ""; this.toughness = ""; @@ -605,7 +649,8 @@ public class CardView extends SimpleCardView { this.color = new ObjectColor(); this.frameColor = new ObjectColor(); this.frameStyle = FrameStyle.M15_NORMAL; - this.manaCost = new ArrayList<>(); + this.manaCostLeft = new ArrayList<>(); + this.manaCostRight = new ArrayList<>(); this.convertedManaCost = 0; // the controller can see more information (e.g. enlarged image) than other players for face down cards (e.g. Morph played face down) @@ -641,6 +686,7 @@ public class CardView extends SimpleCardView { this.id = token.getId(); this.name = token.getName(); this.displayName = token.getName(); + this.displayFullName = token.getName(); this.rules = token.getAbilities().getRules(this.name); this.power = token.getPower().toString(); this.toughness = token.getToughness().toString(); @@ -652,7 +698,8 @@ public class CardView extends SimpleCardView { this.color = token.getColor(null); this.frameColor = token.getFrameColor(null); this.frameStyle = token.getFrameStyle(); - this.manaCost = token.getManaCost().getSymbols(); + this.manaCostLeft = token.getManaCost().getSymbols(); + this.manaCostRight = new ArrayList<>(); this.rarity = Rarity.SPECIAL; this.type = token.getTokenType(); this.tokenDescriptor = token.getTokenDescriptor(); @@ -680,6 +727,10 @@ public class CardView extends SimpleCardView { return displayName; } + public String getDisplayFullName() { + return displayFullName; + } + public List getRules() { return rules; } @@ -745,7 +796,7 @@ public class CardView extends SimpleCardView { } public List getManaCost() { - return manaCost; + return CardUtil.concatManaSymbols(CardInfo.SPLIT_MANA_SEPARATOR_FULL, this.manaCostLeft, this.manaCostRight); } public int getConvertedManaCost() { diff --git a/Mage.Common/src/main/java/mage/view/StackAbilityView.java b/Mage.Common/src/main/java/mage/view/StackAbilityView.java index b4e1e2539f..6035f687be 100644 --- a/Mage.Common/src/main/java/mage/view/StackAbilityView.java +++ b/Mage.Common/src/main/java/mage/view/StackAbilityView.java @@ -40,12 +40,12 @@ public class StackAbilityView extends CardView { this.subTypes = ability.getSubtype(game); this.superTypes = ability.getSuperType(); this.color = ability.getColor(game); - this.manaCost = ability.getManaCost().getSymbols(); + this.manaCostLeft = ability.getManaCost().getSymbols(); + this.manaCostRight = new ArrayList<>(); this.cardTypes = ability.getCardType(); this.subTypes = ability.getSubtype(game); this.superTypes = ability.getSuperType(); this.color = ability.getColor(game); - this.manaCost = ability.getManaCost().getSymbols(); this.power = ability.getPower().toString(); this.toughness = ability.getToughness().toString(); String nameToShow; @@ -56,7 +56,8 @@ public class StackAbilityView extends CardView { tmpSourceCard.subTypes.clear(); tmpSourceCard.cardTypes.clear(); tmpSourceCard.cardTypes.add(CardType.CREATURE); - tmpSourceCard.manaCost.clear(); + tmpSourceCard.manaCostLeft.clear(); + tmpSourceCard.manaCostRight.clear(); tmpSourceCard.power = "2"; tmpSourceCard.toughness = "2"; nameToShow = "creature without name"; diff --git a/Mage.Server/src/main/java/mage/server/ChatManager.java b/Mage.Server/src/main/java/mage/server/ChatManager.java index be02de0ed5..d401d79ba4 100644 --- a/Mage.Server/src/main/java/mage/server/ChatManager.java +++ b/Mage.Server/src/main/java/mage/server/ChatManager.java @@ -248,7 +248,7 @@ public enum ChatManager { CardInfo cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(cardName, true); if (cardInfo != null) { cardInfo.getRules(); - message = "" + cardInfo.getName() + ": Cost:" + cardInfo.getManaCosts().toString() + ", Types:" + cardInfo.getTypes().toString() + ", "; + message = "" + cardInfo.getName() + ": Cost:" + cardInfo.getManaCosts(CardInfo.ManaCostSide.ALL).toString() + ", Types:" + cardInfo.getTypes().toString() + ", "; for (String rule : cardInfo.getRules()) { message = message + rule; } diff --git a/Mage/src/main/java/mage/cards/mock/MockCard.java b/Mage/src/main/java/mage/cards/mock/MockCard.java index 49dc039463..8598688a8d 100644 --- a/Mage/src/main/java/mage/cards/mock/MockCard.java +++ b/Mage/src/main/java/mage/cards/mock/MockCard.java @@ -2,6 +2,8 @@ package mage.cards.mock; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.cards.CardImpl; import mage.cards.repository.CardInfo; @@ -11,15 +13,24 @@ import org.apache.log4j.Logger; import java.util.List; /** + * Mock card for GUI (deck editor and panels) + * * @author North */ public class MockCard extends CardImpl { + static public String ADVENTURE_NAME_SEPARATOR = " // "; + // Needs to be here, as it is normally calculated from the // PlaneswalkerEntersWithLoyaltyAbility of the card... but the MockCard // only has MockAbilities. private int startingLoyalty; + // mana cost extra info for multiple mana drawing + protected ManaCosts manaCostLeft; + protected ManaCosts manaCostRight; + protected String adventureSpellName; + public MockCard(CardInfo card) { super(null, card.getName()); this.cardNumber = card.getCardNumber(); @@ -33,7 +44,9 @@ public class MockCard extends CardImpl { this.usesVariousArt = card.usesVariousArt(); - this.manaCost = new ManaCostsImpl(join(card.getManaCosts())); + this.manaCostLeft = new ManaCostsImpl(join(card.getManaCosts(CardInfo.ManaCostSide.LEFT))); + this.manaCostRight = new ManaCostsImpl(join(card.getManaCosts(CardInfo.ManaCostSide.RIGHT))); + this.manaCost = new ManaCostsImpl(join(card.getManaCosts(CardInfo.ManaCostSide.ALL))); this.color = card.getColor(); @@ -49,6 +62,10 @@ public class MockCard extends CardImpl { this.secondSideCard = new MockCard(CardRepository.instance.findCardWPreferredSet(card.getSecondSideName(), card.getSetCode(), false)); } + if (card.isAdventureCard()) { + this.adventureSpellName = card.getAdventureSpellName(); + } + if (this.isPlaneswalker()) { String startingLoyaltyString = card.getStartingLoyalty(); if (startingLoyaltyString.isEmpty()) { @@ -82,6 +99,32 @@ public class MockCard extends CardImpl { return new MockCard(this); } + @Override + public ManaCosts getManaCost() { + return manaCost; + } + + public ManaCosts getManaCost(CardInfo.ManaCostSide manaCostSide) { + switch (manaCostSide) { + case LEFT: + return manaCostLeft; + case RIGHT: + return manaCostRight; + default: + case ALL: + return manaCost; + } + } + + public String getFullName(boolean showSecondName) { + if (adventureSpellName != null) { + return getName() + ADVENTURE_NAME_SEPARATOR + adventureSpellName; + } else { + return getName(); + } + } + + private MageInt mageIntFromString(String value) { try { int intValue = Integer.parseInt(value); diff --git a/Mage/src/main/java/mage/cards/mock/MockSplitCard.java b/Mage/src/main/java/mage/cards/mock/MockSplitCard.java index d5694d0061..7858ba1e65 100644 --- a/Mage/src/main/java/mage/cards/mock/MockSplitCard.java +++ b/Mage/src/main/java/mage/cards/mock/MockSplitCard.java @@ -20,8 +20,8 @@ public class MockSplitCard extends SplitCard { public MockSplitCard(CardInfo card) { super(null, new CardSetInfo(card.getName(), card.getSetCode(), card.getCardNumber(), card.getRarity()), card.getTypes().toArray(new CardType[0]), - join(card.getManaCosts()), - "", + join(card.getManaCosts(CardInfo.ManaCostSide.LEFT)), + join(card.getManaCosts(CardInfo.ManaCostSide.RIGHT)), getSpellAbilityType(card)); this.expansionSetCode = card.getSetCode(); this.power = mageIntFromString(card.getPower()); diff --git a/Mage/src/main/java/mage/cards/repository/CardInfo.java b/Mage/src/main/java/mage/cards/repository/CardInfo.java index ab77294a9c..10e2e4cd3c 100644 --- a/Mage/src/main/java/mage/cards/repository/CardInfo.java +++ b/Mage/src/main/java/mage/cards/repository/CardInfo.java @@ -1,11 +1,8 @@ - package mage.cards.repository; import com.j256.ormlite.field.DataType; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; -import java.util.*; -import java.util.stream.Collectors; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.SpellAbility; @@ -18,6 +15,9 @@ import mage.util.CardUtil; import mage.util.SubTypeList; import org.apache.log4j.Logger; +import java.util.*; +import java.util.stream.Collectors; + /** * @author North */ @@ -27,6 +27,11 @@ public class CardInfo { private static final int MAX_RULE_LENGTH = 750; private static final String SEPARATOR = "@@@"; + + public static final String SPLIT_MANA_SEPARATOR_SHORT = "*"; + public static final String SPLIT_MANA_SEPARATOR_FULL = "{" + SPLIT_MANA_SEPARATOR_SHORT + "}"; + public static final String SPLIT_MANA_SEPARATOR_RENDER = " / "; + @DatabaseField(indexName = "name_index") protected String name; @DatabaseField(indexName = "setCode_cardNumber_index") @@ -89,6 +94,14 @@ public class CardInfo { protected String flipCardName; @DatabaseField protected String secondSideName; + @DatabaseField + protected boolean adventureCard; + @DatabaseField + protected String adventureSpellName; + + public enum ManaCostSide { + LEFT, RIGHT, ALL + } public CardInfo() { } @@ -116,6 +129,11 @@ public class CardInfo { this.secondSideName = secondSide.getName(); } + if (card instanceof AdventureCard) { + this.adventureCard = true; + this.adventureSpellName = ((AdventureCard) card).getSpellCard().getName(); + } + this.frameStyle = card.getFrameStyle().toString(); this.frameColor = card.getFrameColor(null).toString(); this.variousArt = card.getUsesVariousArt(); @@ -128,7 +146,19 @@ public class CardInfo { this.setTypes(card.getCardType()); this.setSubtypes(card.getSubtype(null).stream().map(SubType::toString).collect(Collectors.toList())); this.setSuperTypes(card.getSuperType()); - this.setManaCosts(card.getManaCost().getSymbols()); + + // mana cost can contains multiple cards (split left/right, card/adventure) + if (card instanceof SplitCard) { + List manaCostLeft = ((SplitCard) card).getLeftHalfCard().getManaCost().getSymbols(); + List manaCostRight = ((SplitCard) card).getRightHalfCard().getManaCost().getSymbols(); + this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight)); + } else if (card instanceof AdventureCard) { + List manaCostLeft = ((AdventureCard) card).getSpellCard().getManaCost().getSymbols(); // Spell from left like MTGA + List manaCostRight = card.getManaCost().getSymbols(); + this.setManaCosts(CardUtil.concatManaSymbols(SPLIT_MANA_SEPARATOR_FULL, manaCostLeft, manaCostRight)); + } else { + this.setManaCosts(card.getManaCost().getSymbols()); + } int length = 0; List rulesList = new ArrayList<>(); @@ -236,12 +266,27 @@ public class CardInfo { return sb.toString(); } - private List parseList(String list) { + private List parseList(String list, ManaCostSide manaCostSide) { if (list.isEmpty()) { return Collections.emptyList(); } - return Arrays.asList(list.split(SEPARATOR)); + List res = new ArrayList<>(); + boolean leftSide = true; + for (String s : list.split(SEPARATOR)) { + if (s.equals(SPLIT_MANA_SEPARATOR_FULL)) { + leftSide = false; + continue; + } + + if (manaCostSide.equals(ManaCostSide.ALL) + || (manaCostSide.equals(ManaCostSide.LEFT) && leftSide) + || (manaCostSide.equals(ManaCostSide.RIGHT) && !leftSide)) { + res.add(s); + } + } + + return res; } public final Set getTypes() { @@ -267,8 +312,8 @@ public class CardInfo { return convertedManaCost; } - public final List getManaCosts() { - return parseList(manaCosts); + public final List getManaCosts(ManaCostSide manaCostSide) { + return parseList(manaCosts, manaCostSide); } public final void setManaCosts(List manaCosts) { @@ -288,7 +333,7 @@ public class CardInfo { } public final List getRules() { - return parseList(rules); + return parseList(rules, ManaCostSide.ALL); } public final void setRules(List rules) { @@ -388,4 +433,12 @@ public class CardInfo { public String getSecondSideName() { return secondSideName; } + + public boolean isAdventureCard() { + return adventureCard; + } + + public String getAdventureSpellName() { + return adventureSpellName; + } } diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java index 1b5d45470d..3b6923ade1 100644 --- a/Mage/src/main/java/mage/cards/repository/CardRepository.java +++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java @@ -33,9 +33,9 @@ public enum CardRepository { private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE"; private static final String VERSION_ENTITY_NAME = "card"; // raise this if db structure was changed - private static final long CARD_DB_VERSION = 51; + private static final long CARD_DB_VERSION = 52; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 227; + private static final long CARD_CONTENT_VERSION = 228; private Dao cardDao; private Set classNames; private RepositoryEventSource eventSource = new RepositoryEventSource(); @@ -458,7 +458,7 @@ public enum CardRepository { public List findCardsCaseInsensitive(String name) { try { - String sqlName = name.toLowerCase(Locale.ENGLISH).replaceAll("\'", "\'\'"); + String sqlName = name.toLowerCase(Locale.ENGLISH).replaceAll("'", "''"); GenericRawResults rawResults = cardDao.queryRaw( "select * from " + CardRepository.VERSION_ENTITY_NAME + " where lower(name) = '" + sqlName + '\'', cardDao.getRawRowMapper()); diff --git a/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java b/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java index 1c2af8afdb..dc284f6200 100644 --- a/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/other/CardTextPredicate.java @@ -1,18 +1,19 @@ - package mage.filter.predicate.other; -import java.util.HashMap; -import java.util.Locale; - import mage.cards.AdventureCard; import mage.cards.Card; import mage.cards.SplitCard; +import mage.cards.mock.MockCard; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.predicate.Predicate; import mage.game.Game; +import java.util.HashMap; +import java.util.Locale; + /** + * Special predicate to search cards in deck editor * * @author North */ @@ -41,20 +42,29 @@ public class CardTextPredicate implements Predicate { } if (text.isEmpty() && isUnique) { - boolean found = !seenCards.keySet().contains(input.getName()); + boolean found = !seenCards.containsKey(input.getName()); seenCards.put(input.getName(), true); return found; } // first check in card name - if (inNames && input.getName().toLowerCase(Locale.ENGLISH).contains(text.toLowerCase(Locale.ENGLISH))) { - if (isUnique && seenCards.keySet().contains(input.getName())) { - return false; + if (inNames) { + String fullName = input.getName(); + if (input instanceof MockCard) { + fullName = ((MockCard) input).getFullName(true); + } else if (input instanceof AdventureCard) { + fullName = input.getName() + MockCard.ADVENTURE_NAME_SEPARATOR + ((AdventureCard) input).getSpellCard().getName(); } - if (isUnique) { - seenCards.put(input.getName(), true); + + if (fullName.toLowerCase(Locale.ENGLISH).contains(text.toLowerCase(Locale.ENGLISH))) { + if (isUnique && seenCards.containsKey(input.getName())) { + return false; + } + if (isUnique) { + seenCards.put(input.getName(), true); + } + return true; } - return true; } //separate by spaces @@ -109,7 +119,7 @@ public class CardTextPredicate implements Predicate { } } - if (found && isUnique && seenCards.keySet().contains(input.getName())) { + if (found && isUnique && seenCards.containsKey(input.getName())) { found = false; } if (!found) { diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index c653496503..97df0a692d 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1,11 +1,5 @@ package mage.util; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.text.SimpleDateFormat; -import java.util.Objects; -import java.util.UUID; import mage.MageObject; import mage.Mana; import mage.abilities.Ability; @@ -21,6 +15,15 @@ import mage.game.permanent.Permanent; import mage.game.permanent.token.Token; import mage.util.functions.CopyTokenFunction; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + /** * @author nantuko */ @@ -29,10 +32,10 @@ public final class CardUtil { private static final String SOURCE_EXILE_ZONE_TEXT = "SourceExileZone"; static final String[] numberStrings = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", - "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"}; + "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"}; static final String[] ordinalStrings = {"first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eightth", "ninth", - "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth"}; + "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth", "twentieth"}; public static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); @@ -147,8 +150,8 @@ public final class CardUtil { * * @param spellAbility * @param manaCostsToReduce costs to reduce - * @param convertToGeneric colored mana does reduce generic mana if no - * appropriate colored mana is in the costs included + * @param convertToGeneric colored mana does reduce generic mana if no + * appropriate colored mana is in the costs included */ public static void adjustCost(SpellAbility spellAbility, ManaCosts manaCostsToReduce, boolean convertToGeneric) { ManaCosts previousCost = spellAbility.getManaCostsToPay(); @@ -333,7 +336,7 @@ public final class CardUtil { * * @param number number to convert to text * @param forOne if the number is 1, this string will be returnedinstead of - * "one". + * "one". * @return */ public static String numberToText(int number, String forOne) { @@ -418,7 +421,7 @@ public final class CardUtil { /** * Creates and saves a (card + zoneChangeCounter) specific exileId. * - * @param game the current game + * @param game the current game * @param source source ability * @return the specific UUID */ @@ -453,9 +456,9 @@ public final class CardUtil { * be specific to a permanent instance. So they won't match, if a permanent * was e.g. exiled and came back immediately. * - * @param text short value to describe the value + * @param text short value to describe the value * @param cardId id of the card - * @param game the game + * @param game the game * @return */ public static String getCardZoneString(String text, UUID cardId, Game game) { @@ -609,9 +612,9 @@ public final class CardUtil { /** * Checks if a card had a given ability depending their historic cardState * - * @param ability the ability that is checked + * @param ability the ability that is checked * @param cardState the historic cardState (from LKI) - * @param cardId the id of the card + * @param cardId the id of the card * @param game * @return */ @@ -630,4 +633,13 @@ public final class CardUtil { } return false; } + + public static List concatManaSymbols(String delimeter, List mana1, List mana2) { + List res = new ArrayList<>(mana1); + if (res.size() > 0 && mana2.size() > 0 && delimeter != null && !delimeter.isEmpty()) { + res.add(delimeter); + } + res.addAll(mana2); + return res; + } }