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 25a5da5b83..7ddeecaeae 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -204,15 +204,32 @@ public class TestCardRenderDialog extends MageDialog { game.addPlayer(playerOpponent, deck); List cardViews = new ArrayList<>(); - ///* + /* // test morphed cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "263", 0, 0, 0, false)); // mountain cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "185", 0, 0, 0, true)); // Judith, the Scourge Diva - //*/ cardViews.add(createHandCard(game, playerYou.getId(), "DIS", "153")); // Odds // Ends (split card) cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "38")); // Animating Faerie (adventure card) cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, false, false)); // face down cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", true, false, true)); // morphed cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, true, false)); // manifested + //*/ + + /* //test emblems + cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false)); // Noxious Groodion + cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false)); // Knight of Sorrows + cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false)); // Huntmaster of the Fells, transforms + cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "221", 0, 0, 0, false)); // Bedeck // Bedazzle + cardViews.add(createPermanentCard(game, playerYou.getId(), "XLN", "234", 0, 0, 0, false)); // Conqueror's Galleon + cardViews.add(createEmblem(new AjaniAdversaryOfTyrantsEmblem())); // Emblem Ajani + cardViews.add(createPlane(new AkoumPlane())); // Plane - Akoum + //*/ + + //* //test split, transform and mdf in hands + cardViews.add(createHandCard(game, playerYou.getId(), "SOI", "97")); // Accursed Witch + cardViews.add(createHandCard(game, playerYou.getId(), "UMA", "225")); // Fire // Ice + cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "14")); // Giant Killer + cardViews.add(createHandCard(game, playerYou.getId(), "ZNR", "134")); // Akoum Warrior + //*/ // duplicate cards if (checkBoxGenerateManyCards.isSelected()) { @@ -226,17 +243,6 @@ public class TestCardRenderDialog extends MageDialog { } } - /* - cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "78", 125, 89, 0, false)); // Noxious Groodion - - cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "14", 3, 5, 2, false)); // Knight of Sorrows - cardViews.add(createPermanentCard(game, playerYou.getId(), "DKA", "140", 5, 2, 2, false)); // Huntmaster of the Fells, transforms - cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "221", 0, 0, 0, false)); // Bedeck // Bedazzle - cardViews.add(createPermanentCard(game, playerYou.getId(), "XLN", "234", 0, 0, 0, false)); // Conqueror's Galleon - cardViews.add(createEmblem(new AjaniAdversaryOfTyrantsEmblem())); // Emblem Ajani - cardViews.add(createPlane(new AkoumPlane())); // Plane - Akoum - //*/ - BigCard big = new BigCard(); CardsView view = new CardsView(cardViews); cardsPanel.loadCards(view, big, game.getId()); diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java index 3b17b9f84a..b54fa54594 100644 --- a/Mage.Common/src/main/java/mage/view/CardView.java +++ b/Mage.Common/src/main/java/mage/view/CardView.java @@ -417,7 +417,6 @@ public class CardView extends SimpleCardView { this.subTypes = card.getSubtype(game); this.superTypes = card.getSuperType(); this.color = card.getColor(game); - this.transformable = card.isTransformable(); this.flipCard = card.isFlipCard(); this.faceDown = !showFaceUp; @@ -436,7 +435,7 @@ public class CardView extends SimpleCardView { this.tokenDescriptor = card.getTokenDescriptor(); } // - // set code und card number for token copies to get the image + // set code and card number for token copies to get the image this.rules = card.getRules(game); this.type = ((PermanentToken) card).getToken().getTokenType(); } else { @@ -445,6 +444,8 @@ public class CardView extends SimpleCardView { } // transformable, double faces cards + this.transformable = card.isTransformable(); + Card secondSideCard = card.getSecondCardFace(); if (secondSideCard != null) { this.secondCardFace = new CardView(secondSideCard); @@ -458,14 +459,13 @@ public class CardView extends SimpleCardView { this.originalName = card.getName(); } - /* if (card instanceof ModalDoubleFacesCard) { + this.transformable = true; // enable GUI day/night button ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card; this.secondCardFace = new CardView(mdfCard.getRightHalfCard()); this.alternateName = mdfCard.getRightHalfCard().getName(); this.originalName = card.getName(); } - */ if (card instanceof Spell) { this.mageObjectType = MageObjectType.SPELL; diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index ecd7c163eb..8bee7eeb66 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -1,13 +1,10 @@ package mage.cards; -import com.google.common.collect.ImmutableList; import mage.MageObject; import mage.MageObjectImpl; import mage.Mana; import mage.ObjectColor; import mage.abilities.*; -import mage.abilities.hint.Hint; -import mage.abilities.hint.HintUtils; import mage.abilities.keyword.FlashbackAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.repository.PluginClassloaderRegistery; @@ -22,6 +19,7 @@ import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.game.stack.StackObject; +import mage.util.CardUtil; import mage.util.GameLog; import mage.util.SubTypeList; import mage.watchers.Watcher; @@ -218,52 +216,16 @@ public abstract class CardImpl extends MageObjectImpl implements Card { game.getState().getCardState(objectId).addInfo(key, value); } - protected static final List rulesError = ImmutableList.of("Exception occurred in rules generation"); - @Override public List getRules() { - try { - return getAbilities().getRules(this.getName()); - } catch (Exception e) { - logger.info("Exception in rules generation for card: " + this.getName(), e); - } - return rulesError; + Abilities sourceAbilities = this.getAbilities(); + return CardUtil.getCardRulesWithAdditionalInfo(this.getId(), this.getName(), sourceAbilities, sourceAbilities); } @Override public List getRules(Game game) { - try { - List rules = getAbilities(game).getRules(getName()); - if (game != null) { - // debug state - for (String data : game.getState().getCardState(objectId).getInfo().values()) { - rules.add(data); - } - // ability hints - List abilityHints = new ArrayList<>(); - if (HintUtils.ABILITY_HINTS_ENABLE) { - for (Ability ability : abilities) { - for (Hint hint : ability.getHints()) { - String s = hint.getText(game, ability); - if (s != null && !s.isEmpty()) { - abilityHints.add(s); - } - } - } - } - - // restrict hints only for permanents, not cards - // total hints - if (!abilityHints.isEmpty()) { - rules.add(HintUtils.HINT_START_MARK); - HintUtils.appendHints(rules, abilityHints); - } - } - return rules; - } catch (Exception e) { - logger.error("Exception in rules generation for card: " + this.getName(), e); - } - return rulesError; + Abilities sourceAbilities = this.getAbilities(game); + return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(), sourceAbilities, sourceAbilities); } /** diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java b/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java index 9db3fb9dce..5a981ae91a 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java @@ -12,6 +12,7 @@ import mage.abilities.costs.mana.ManaCosts; import mage.constants.*; import mage.game.Game; import mage.game.events.ZoneChangeEvent; +import mage.util.CardUtil; import mage.util.SubTypeList; import java.util.ArrayList; @@ -157,20 +158,7 @@ public abstract class ModalDoubleFacesCard extends CardImpl { @Override public Abilities getAbilities() { - Abilities allAbilites = new AbilitiesImpl<>(); - - // ignore default spell ability from main card (only halfes are actual) - for (Ability ability : super.getAbilities()) { - if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.MODAL) { - continue; - } - allAbilites.add(ability); - } - - allAbilites.addAll(super.getAbilities()); - allAbilites.addAll(leftHalfCard.getAbilities()); - allAbilites.addAll(rightHalfCard.getAbilities()); - return allAbilites; + return getInnerAbilities(false); } public Abilities getSharedAbilities(Game game) { @@ -180,6 +168,10 @@ public abstract class ModalDoubleFacesCard extends CardImpl { @Override public Abilities getAbilities(Game game) { + return getInnerAbilities(game, false); + } + + private Abilities getInnerAbilities(Game game, boolean showOnlyMainSide) { Abilities allAbilites = new AbilitiesImpl<>(); // ignore default spell ability from main card (only halfes are actual) @@ -191,10 +183,50 @@ public abstract class ModalDoubleFacesCard extends CardImpl { } allAbilites.addAll(leftHalfCard.getAbilities(game)); - allAbilites.addAll(rightHalfCard.getAbilities(game)); + if (!showOnlyMainSide) { + allAbilites.addAll(rightHalfCard.getAbilities(game)); + } + return allAbilites; } + private Abilities getInnerAbilities(boolean showOnlyMainSide) { + Abilities allAbilites = new AbilitiesImpl<>(); + + // ignore default spell ability from main card (only halfes are actual) + for (Ability ability : super.getAbilities()) { + if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType() == SpellAbilityType.MODAL) { + continue; + } + allAbilites.add(ability); + } + + allAbilites.addAll(leftHalfCard.getAbilities()); + if (!showOnlyMainSide) { + allAbilites.addAll(rightHalfCard.getAbilities()); + } + + return allAbilites; + } + + @Override + public List getRules() { + // rules must show only main side (another side visible by toggle/transform button in GUI) + // card hints from both sides + return CardUtil.getCardRulesWithAdditionalInfo(this.getId(), this.getName(), + this.getInnerAbilities(true), this.getInnerAbilities(false) + ); + } + + @Override + public List getRules(Game game) { + // rules must show only main side (another side visible by toggle/transform button in GUI) + // card hints from both sides + return CardUtil.getCardRulesWithAdditionalInfo(game, this.getId(), this.getName(), + this.getInnerAbilities(game, true), this.getInnerAbilities(game, false) + ); + } + @Override public boolean hasAbility(Ability ability, Game game) { return super.hasAbility(ability, game); diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java index f22c98fe3c..475841d928 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalfImpl.java @@ -100,4 +100,10 @@ public class ModalDoubleFacesCardHalfImpl extends CardImpl implements ModalDoubl this.power = power; this.toughness = toughness; } + + @Override + public String getIdName() { + // id must send to main card (popup card hint in game logs) + return getName() + " [" + parentCard.getId().toString().substring(0, 3) + ']'; + } } diff --git a/Mage/src/main/java/mage/cards/mock/MockCard.java b/Mage/src/main/java/mage/cards/mock/MockCard.java index 644770c7fa..3221ffda4f 100644 --- a/Mage/src/main/java/mage/cards/mock/MockCard.java +++ b/Mage/src/main/java/mage/cards/mock/MockCard.java @@ -78,7 +78,6 @@ public class MockCard extends CardImpl { if (this.isPlaneswalker()) { String startingLoyaltyString = card.getStartingLoyalty(); if (startingLoyaltyString.isEmpty()) { - //Logger.getLogger(MockCard.class).warn("Planeswalker `" + this.name + "` has empty starting loyalty."); } else { try { this.startingLoyalty = Integer.parseInt(startingLoyaltyString); diff --git a/Mage/src/main/java/mage/cards/repository/CardInfo.java b/Mage/src/main/java/mage/cards/repository/CardInfo.java index bdbd2e6d90..0d674bfbc8 100644 --- a/Mage/src/main/java/mage/cards/repository/CardInfo.java +++ b/Mage/src/main/java/mage/cards/repository/CardInfo.java @@ -198,14 +198,7 @@ public class CardInfo { rulesList.add(rule); } } else if (card instanceof ModalDoubleFacesCard) { - for (String rule : ((ModalDoubleFacesCard) card).getLeftHalfCard().getRules()) { - length += rule.length(); - rulesList.add(rule); - } - for (String rule : ((ModalDoubleFacesCard) card).getRightHalfCard().getRules()) { - length += rule.length(); - rulesList.add(rule); - } + // mdf card return main side's rules only (GUI can toggle it to another side) for (String rule : card.getRules()) { length += rule.length(); rulesList.add(rule); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 829e75df52..f60c34f77e 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -264,7 +264,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { // ability hints List abilityHints = new ArrayList<>(); if (HintUtils.ABILITY_HINTS_ENABLE) { - for (Ability ability : abilities) { + for (Ability ability : getAbilities(game)) { for (Hint hint : ability.getHints()) { String s = hint.getText(game, ability); if (s != null && !s.isEmpty()) { @@ -341,7 +341,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return rules; } catch (Exception e) { - return rulesError; + return CardUtil.RULES_ERROR_INFO; } } diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 1aa534dba1..a30e8cec7e 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -178,6 +178,12 @@ public class Spell extends StackObjImpl implements Card { + " as Adventure spell of " + GameLog.getColoredObjectIdName(adventureCard); } + if (card instanceof ModalDoubleFacesCardHalf) { + ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card.getMainCard(); + return GameLog.replaceNameByColoredName(card, getSpellAbility().toString(), mdfCard) + + " as mdf side of " + GameLog.getColoredObjectIdName(mdfCard); + } + return GameLog.replaceNameByColoredName(card, getSpellAbility().toString()); } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 3855446e34..efed0eb952 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1309,7 +1309,14 @@ public abstract class PlayerImpl implements Player, Serializable { card.getId(), card.getId(), playerId, activationStatus.getApprovingObject()); landEventAfter.setZone(cardZoneBefore); game.fireEvent(landEventAfter); - game.fireInformEvent(getLogName() + " plays " + card.getLogName()); + + String playText = getLogName() + " plays " + card.getLogName(); + if (card instanceof ModalDoubleFacesCardHalf) { + ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) card.getMainCard(); + playText = getLogName() + " plays " + GameLog.replaceNameByColoredName(card, card.getName(), mdfCard) + + " as MDF side of " + GameLog.getColoredObjectIdName(mdfCard); + } + game.fireInformEvent(playText); // game.removeBookmark(bookmark); resetStoredBookmark(game); // prevent undo after playing a land return true; diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 04e55a4455..bae18c8d37 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1,5 +1,6 @@ package mage.util; +import com.google.common.collect.ImmutableList; import mage.MageObject; import mage.Mana; import mage.abilities.Abilities; @@ -9,6 +10,8 @@ import mage.abilities.SpellAbility; import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.*; import mage.abilities.effects.ContinuousEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.HintUtils; import mage.cards.Card; import mage.cards.MeldCard; import mage.cards.ModalDoubleFacesCard; @@ -26,6 +29,7 @@ import mage.game.stack.Spell; import mage.players.Player; import mage.target.Target; import mage.util.functions.CopyTokenFunction; +import org.apache.log4j.Logger; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; @@ -39,6 +43,10 @@ import java.util.stream.Collectors; */ public final class CardUtil { + private static final Logger logger = Logger.getLogger(CardUtil.class); + + public static final List RULES_ERROR_INFO = ImmutableList.of("Exception occurred in rules generation"); + private static final String SOURCE_EXILE_ZONE_TEXT = "SourceExileZone"; static final String[] numberStrings = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", @@ -897,4 +905,54 @@ public final class CardUtil { return card.getName(); } } + + public static List getCardRulesWithAdditionalInfo(UUID cardId, String cardName, + Abilities rulesSource, Abilities hintAbilities) { + return getCardRulesWithAdditionalInfo(null, cardId, cardName, rulesSource, hintAbilities); + } + + /** + * Prepare rules list from abilities + * + * @param rulesSource abilities list to show as rules + * @param hintsSource abilities list to show as card hints only (you can add additional hints here; exameple: from second or transformed side) + */ + public static List getCardRulesWithAdditionalInfo(Game game, UUID cardId, String cardName, + Abilities rulesSource, Abilities hintsSource) { + try { + List rules = rulesSource.getRules(cardName); + + if (game != null) { + + // debug state + for (String data : game.getState().getCardState(cardId).getInfo().values()) { + rules.add(data); + } + + // ability hints + List abilityHints = new ArrayList<>(); + if (HintUtils.ABILITY_HINTS_ENABLE) { + for (Ability ability : hintsSource) { + for (Hint hint : ability.getHints()) { + String s = hint.getText(game, ability); + if (s != null && !s.isEmpty()) { + abilityHints.add(s); + } + } + } + } + + // restrict hints only for permanents, not cards + // total hints + if (!abilityHints.isEmpty()) { + rules.add(HintUtils.HINT_START_MARK); + HintUtils.appendHints(rules, abilityHints); + } + } + return rules; + } catch (Exception e) { + logger.error("Exception in rules generation for card: " + cardName, e); + } + return RULES_ERROR_INFO; + } }