diff --git a/Mage.Sets/src/mage/cards/b/BileBlight.java b/Mage.Sets/src/mage/cards/b/BileBlight.java index 0b237561908..77b3e4f0fbc 100644 --- a/Mage.Sets/src/mage/cards/b/BileBlight.java +++ b/Mage.Sets/src/mage/cards/b/BileBlight.java @@ -61,7 +61,7 @@ class BileBlightEffect extends BoostAllEffect { } else { String name = target.getName(); for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - if (CardUtil.haveSameNames(perm.getName(), name)) { + if (CardUtil.haveSameNames(perm, name, game)) { affectedObjectList.add(new MageObjectReference(perm, game)); } } diff --git a/Mage.Sets/src/mage/cards/b/BrainPry.java b/Mage.Sets/src/mage/cards/b/BrainPry.java index 555d1368c16..8704271ed89 100644 --- a/Mage.Sets/src/mage/cards/b/BrainPry.java +++ b/Mage.Sets/src/mage/cards/b/BrainPry.java @@ -60,7 +60,7 @@ class BrainPryEffect extends OneShotEffect { if (targetPlayer != null && controller != null && sourceObject != null && cardName != null) { boolean hasDiscarded = false; for (Card card : targetPlayer.getHand().getCards(game)) { - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { targetPlayer.discard(card, source, game); hasDiscarded = true; break; diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapist.java b/Mage.Sets/src/mage/cards/c/CabalTherapist.java index 53d044bd862..66ed076a807 100644 --- a/Mage.Sets/src/mage/cards/c/CabalTherapist.java +++ b/Mage.Sets/src/mage/cards/c/CabalTherapist.java @@ -90,18 +90,7 @@ class CabalTherapistDiscardEffect extends OneShotEffect { if (card == null) { return true; } - if (card.isSplitCard()) { - SplitCard splitCard = (SplitCard) card; - if (CardUtil.haveSameNames(splitCard.getLeftHalfCard().getName(), cardName)) { - return false; - } else if (CardUtil.haveSameNames(splitCard.getRightHalfCard().getName(), cardName)) { - return false; - } - } - if (CardUtil.haveSameNames(card.getName(), cardName)) { - return false; - } - return true; + return !CardUtil.haveSameNames(card, cardName, game); }); targetPlayer.discard(hand, source, game); return true; diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapy.java b/Mage.Sets/src/mage/cards/c/CabalTherapy.java index 6aad9ff861c..0a25b54b9c0 100644 --- a/Mage.Sets/src/mage/cards/c/CabalTherapy.java +++ b/Mage.Sets/src/mage/cards/c/CabalTherapy.java @@ -75,18 +75,7 @@ class CabalTherapyEffect extends OneShotEffect { if (card == null) { return true; } - if (card.isSplitCard()) { - SplitCard splitCard = (SplitCard) card; - if (CardUtil.haveSameNames(splitCard.getLeftHalfCard().getName(), cardName)) { - return false; - } else if (CardUtil.haveSameNames(splitCard.getRightHalfCard().getName(), cardName)) { - return false; - } - } - if (CardUtil.haveSameNames(card.getName(), cardName)) { - return false; - } - return true; + return !CardUtil.haveSameNames(card, cardName, game); }); targetPlayer.discard(hand, source, game); return true; diff --git a/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java b/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java index 6baed0ceb09..4bddff8c30a 100644 --- a/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java +++ b/Mage.Sets/src/mage/cards/c/ConundrumSphinx.java @@ -85,7 +85,7 @@ class ConundrumSphinxEffect extends OneShotEffect { if (card != null) { Cards cards = new CardsImpl(card); player.revealCards(source, player.getName(), cards, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { player.moveCards(cards, Zone.HAND, source, game); } else { player.putCardsOnBottomOfLibrary(cards, game, source, false); diff --git a/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java b/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java index d6845d3243b..543b50c756f 100644 --- a/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java +++ b/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java @@ -133,7 +133,7 @@ class CorrosiveOozeCombatWatcher extends Watcher { if (event.getType() == GameEvent.EventType.BLOCKER_DECLARED) { Permanent attacker = game.getPermanent(event.getTargetId()); Permanent blocker = game.getPermanent(event.getSourceId()); - if (attacker != null && CardUtil.haveSameNames(attacker.getName(), "Corrosive Ooze")) { // To check for name is not working if Ooze is copied but name changed + if (attacker != null && CardUtil.haveSameNames(attacker, "Corrosive Ooze", game)) { // To check for name is not working if Ooze is copied but name changed if (blocker != null && hasAttachedEquipment(game, blocker)) { MageObjectReference oozeMor = new MageObjectReference(attacker, game); Set relatedCreatures = oozeBlocksOrBlocked.getOrDefault(oozeMor, new HashSet<>()); @@ -141,7 +141,7 @@ class CorrosiveOozeCombatWatcher extends Watcher { oozeBlocksOrBlocked.put(oozeMor, relatedCreatures); } } - if (blocker != null && CardUtil.haveSameNames(blocker.getName(), "Corrosive Ooze")) { + if (blocker != null && CardUtil.haveSameNames(blocker, "Corrosive Ooze", game)) { if (attacker != null && hasAttachedEquipment(game, attacker)) { MageObjectReference oozeMor = new MageObjectReference(blocker, game); Set relatedCreatures = oozeBlocksOrBlocked.getOrDefault(oozeMor, new HashSet<>()); diff --git a/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java b/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java index a6e289d8f46..90fd017f89b 100644 --- a/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java +++ b/Mage.Sets/src/mage/cards/c/CouncilOfTheAbsolute.java @@ -93,7 +93,7 @@ class CouncilOfTheAbsoluteReplacementEffect extends ContinuousRuleModifyingEffec if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { MageObject object = game.getObject(event.getSourceId()); String needName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - return object != null && CardUtil.haveSameNames(object.getName(), needName); + return object != null && CardUtil.haveSameNames(object, needName, game); } return false; } @@ -123,7 +123,7 @@ class CouncilOfTheAbsoluteCostReductionEffect extends CostModificationEffectImpl Card card = game.getCard(abilityToModify.getSourceId()); if (card != null) { String needName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY); - return CardUtil.haveSameNames(card.getName(), needName); + return CardUtil.haveSameNames(card, needName, game); } } return false; diff --git a/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java b/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java index f5bfddfe290..9089c535cfc 100644 --- a/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java +++ b/Mage.Sets/src/mage/cards/c/CrownOfEmpires.java @@ -61,9 +61,9 @@ class CrownOfEmpiresEffect extends OneShotEffect { boolean scepter = false; boolean throne = false; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { - if (CardUtil.haveSameNames(permanent.getName(), "Scepter of Empires")) { + if (CardUtil.haveSameNames(permanent, "Scepter of Empires", game)) { scepter = true; - } else if (CardUtil.haveSameNames(permanent.getName(), "Throne of Empires")) { + } else if (CardUtil.haveSameNames(permanent, "Throne of Empires", game)) { throne = true; } if (scepter && throne) break; diff --git a/Mage.Sets/src/mage/cards/c/CursedScroll.java b/Mage.Sets/src/mage/cards/c/CursedScroll.java index 70d1e5f0485..6ed0c99568f 100644 --- a/Mage.Sets/src/mage/cards/c/CursedScroll.java +++ b/Mage.Sets/src/mage/cards/c/CursedScroll.java @@ -70,7 +70,7 @@ class CursedScrollEffect extends OneShotEffect { } revealed.add(card); controller.revealCards(sourceObject.getIdName(), revealed, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { Permanent creature = game.getPermanent(targetPointer.getFirst(game, source)); if (creature != null) { creature.damage(2, source.getSourceId(), game, false, true); diff --git a/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java b/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java index 18824247a4b..1655ec9108d 100644 --- a/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java +++ b/Mage.Sets/src/mage/cards/d/DeathbellowWarCry.java @@ -72,6 +72,6 @@ class DeathbellowWarCryTarget extends TargetCardInLibrary { .map(game::getCard) .filter(Objects::nonNull) .map(Card::getName) - .noneMatch(n -> CardUtil.haveSameNames(n, card.getName())); + .noneMatch(n -> CardUtil.haveSameNames(card, n, game)); } } diff --git a/Mage.Sets/src/mage/cards/d/DementiaSliver.java b/Mage.Sets/src/mage/cards/d/DementiaSliver.java index 686a33a1ce4..1610c825ae9 100644 --- a/Mage.Sets/src/mage/cards/d/DementiaSliver.java +++ b/Mage.Sets/src/mage/cards/d/DementiaSliver.java @@ -86,7 +86,7 @@ class DementiaSliverEffect extends OneShotEffect { if (card != null) { revealed.add(card); opponent.revealCards(sourceObject.getName(), revealed, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { opponent.discard(card, source, game); } } diff --git a/Mage.Sets/src/mage/cards/d/Denied.java b/Mage.Sets/src/mage/cards/d/Denied.java index b5825c8b1ea..596b96114f4 100644 --- a/Mage.Sets/src/mage/cards/d/Denied.java +++ b/Mage.Sets/src/mage/cards/d/Denied.java @@ -63,7 +63,7 @@ class DeniedEffect extends OneShotEffect { player.revealCards("Denied!", player.getHand(), game, true); String namedCard = (String) object; for (Card card : player.getHand().getCards(game)) { - if (card != null && CardUtil.haveSameNames(card.getName(), namedCard)) { + if (card != null && CardUtil.haveSameNames(card, namedCard, game)) { game.getStack().counter(targetSpell.getId(), source.getSourceId(), game); break; } diff --git a/Mage.Sets/src/mage/cards/d/DetentionSphere.java b/Mage.Sets/src/mage/cards/d/DetentionSphere.java index 64943101ae9..c7a99d41a55 100644 --- a/Mage.Sets/src/mage/cards/d/DetentionSphere.java +++ b/Mage.Sets/src/mage/cards/d/DetentionSphere.java @@ -85,7 +85,7 @@ class DetentionSphereEntersEffect extends OneShotEffect { } else { String name = targetPermanent.getName(); for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - if (permanent != null && CardUtil.haveSameNames(permanent.getName(), name)) { + if (permanent != null && CardUtil.haveSameNames(permanent, name, game)) { controller.moveCardToExileWithInfo(permanent, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true); } } diff --git a/Mage.Sets/src/mage/cards/d/DiviningWitch.java b/Mage.Sets/src/mage/cards/d/DiviningWitch.java index 39e231aa203..a1a2f862bb3 100644 --- a/Mage.Sets/src/mage/cards/d/DiviningWitch.java +++ b/Mage.Sets/src/mage/cards/d/DiviningWitch.java @@ -92,7 +92,7 @@ public final class DiviningWitch extends CardImpl { if (card != null) { cardsToReaveal.add(card); // Put that card into your hand - if (CardUtil.haveSameNames(card.getName(), name)) { + if (CardUtil.haveSameNames(card, name, game)) { cardToHand = card; break; } diff --git a/Mage.Sets/src/mage/cards/f/Foreshadow.java b/Mage.Sets/src/mage/cards/f/Foreshadow.java index bfa869d0659..b3f778e9b3d 100644 --- a/Mage.Sets/src/mage/cards/f/Foreshadow.java +++ b/Mage.Sets/src/mage/cards/f/Foreshadow.java @@ -72,7 +72,7 @@ class ForeshadowEffect extends OneShotEffect { Card card = targetPlayer.getLibrary().getFromTop(game); if (card != null) { controller.moveCards(card, Zone.GRAVEYARD, source, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { controller.drawCards(1, source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/cards/l/LammastideWeave.java b/Mage.Sets/src/mage/cards/l/LammastideWeave.java index 9cd195c43b9..b8f979f472c 100644 --- a/Mage.Sets/src/mage/cards/l/LammastideWeave.java +++ b/Mage.Sets/src/mage/cards/l/LammastideWeave.java @@ -71,7 +71,7 @@ class LammastideWeaveEffect extends OneShotEffect { Card card = targetPlayer.getLibrary().getFromTop(game); if (card != null) { controller.moveCards(card, Zone.GRAVEYARD, source, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { controller.gainLife(card.getConvertedManaCost(), game, source); } } diff --git a/Mage.Sets/src/mage/cards/l/LiarsPendulum.java b/Mage.Sets/src/mage/cards/l/LiarsPendulum.java index 195878591b6..e30258077d1 100644 --- a/Mage.Sets/src/mage/cards/l/LiarsPendulum.java +++ b/Mage.Sets/src/mage/cards/l/LiarsPendulum.java @@ -93,7 +93,7 @@ class LiarsPendulumEffect extends OneShotEffect { rightGuess = opponentGuess; } } - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { rightGuess = opponentGuess; } } diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java b/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java index 71f059afe6d..3fd5ef28094 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheScroll.java @@ -76,7 +76,7 @@ class MagusOfTheScrollEffect extends OneShotEffect { } revealed.add(card); you.revealCards(sourceObject.getName(), revealed, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { Permanent creature = game.getPermanent(targetPointer.getFirst(game, source)); if (creature != null) { creature.damage(2, source.getSourceId(), game, false, true); diff --git a/Mage.Sets/src/mage/cards/p/PetraSphinx.java b/Mage.Sets/src/mage/cards/p/PetraSphinx.java index 253d4e77f01..c97a792299e 100644 --- a/Mage.Sets/src/mage/cards/p/PetraSphinx.java +++ b/Mage.Sets/src/mage/cards/p/PetraSphinx.java @@ -78,7 +78,7 @@ class PetraSphinxEffect extends OneShotEffect { if (card != null) { Cards cards = new CardsImpl(card); player.revealCards(source, cards, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { player.moveCards(cards, Zone.HAND, source, game); } else { player.moveCards(cards, Zone.GRAVEYARD, source, game); diff --git a/Mage.Sets/src/mage/cards/p/Predict.java b/Mage.Sets/src/mage/cards/p/Predict.java index 1f9ec4f9302..e4f98c5a954 100644 --- a/Mage.Sets/src/mage/cards/p/Predict.java +++ b/Mage.Sets/src/mage/cards/p/Predict.java @@ -67,7 +67,7 @@ class PredictEffect extends OneShotEffect { Card card = targetPlayer.getLibrary().getFromTop(game); if (card != null) { controller.moveCards(card, Zone.GRAVEYARD, source, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { amount = 2; } } diff --git a/Mage.Sets/src/mage/cards/r/ReflectorMage.java b/Mage.Sets/src/mage/cards/r/ReflectorMage.java index faabe8b4779..8de4580f253 100644 --- a/Mage.Sets/src/mage/cards/r/ReflectorMage.java +++ b/Mage.Sets/src/mage/cards/r/ReflectorMage.java @@ -119,7 +119,7 @@ class ExclusionRitualReplacementEffect extends ContinuousRuleModifyingEffectImpl if (spell != null && spell.isFaceDown(game)) { return false; // Face Down cast spell (Morph creature) has no name } - return CardUtil.haveSameNames(card.getName(), creatureName) && Objects.equals(ownerId, card.getOwnerId()); + return CardUtil.haveSameNames(card, creatureName, game) && Objects.equals(ownerId, card.getOwnerId()); } return false; } diff --git a/Mage.Sets/src/mage/cards/s/SearchTheCity.java b/Mage.Sets/src/mage/cards/s/SearchTheCity.java index 57ad86e633e..0522121de16 100644 --- a/Mage.Sets/src/mage/cards/s/SearchTheCity.java +++ b/Mage.Sets/src/mage/cards/s/SearchTheCity.java @@ -157,7 +157,7 @@ class SearchTheCityExiledCardToHandEffect extends OneShotEffect { ExileZone searchTheCityExileZone = game.getExile().getExileZone(source.getSourceId()); if (cardName != null && searchTheCityExileZone != null) { for (Card card : searchTheCityExileZone.getCards(game)) { - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { if (card.moveToZone(Zone.HAND, source.getSourceId(), game, true)) { game.informPlayers("Search the City: put " + card.getName() + " into owner's hand"); } diff --git a/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java b/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java index 153bc570f73..3466f128b5d 100644 --- a/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java +++ b/Mage.Sets/src/mage/cards/s/SpoilsOfTheVault.java @@ -68,7 +68,7 @@ class SpoilsOfTheVaultEffect extends OneShotEffect { for (Card card : controller.getLibrary().getCards(game)) { if (card != null) { cardsToReveal.add(card); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { controller.moveCards(card, Zone.HAND, source, game); break; } else { diff --git a/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java b/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java index a4b9bee2e9d..7c10810ef06 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtHemorrhage.java @@ -86,7 +86,7 @@ class ThoughtHemorrhageEffect extends OneShotEffect { + targetPlayer.getName(), targetPlayer.getHand(), game); int cardsFound = 0; for (Card card : targetPlayer.getHand().getCards(game)) { - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { cardsFound++; } } diff --git a/Mage.Sets/src/mage/cards/t/TunnelVision.java b/Mage.Sets/src/mage/cards/t/TunnelVision.java index 33b58c266c4..ff1a97a640b 100644 --- a/Mage.Sets/src/mage/cards/t/TunnelVision.java +++ b/Mage.Sets/src/mage/cards/t/TunnelVision.java @@ -73,7 +73,7 @@ class TunnelVisionEffect extends OneShotEffect { for (Card card : targetPlayer.getLibrary().getCards(game)) { cardsToReveal.add(card); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { namedCard = card; break; } diff --git a/Mage.Sets/src/mage/cards/v/VexingArcanix.java b/Mage.Sets/src/mage/cards/v/VexingArcanix.java index 7a77fc4238a..742e51c56ae 100644 --- a/Mage.Sets/src/mage/cards/v/VexingArcanix.java +++ b/Mage.Sets/src/mage/cards/v/VexingArcanix.java @@ -74,7 +74,7 @@ class VexingArcanixEffect extends OneShotEffect { if (card != null) { Cards cards = new CardsImpl(card); player.revealCards(sourceObject.getIdName(), cards, game); - if (CardUtil.haveSameNames(card.getName(), cardName)) { + if (CardUtil.haveSameNames(card, cardName, game)) { player.moveCards(cards, Zone.HAND, source, game); } else { player.moveCards(cards, Zone.GRAVEYARD, source, game); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java new file mode 100644 index 00000000000..ae6f1a97d64 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithCostModificationTest.java @@ -0,0 +1,201 @@ +package org.mage.test.cards.split; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class CastSplitCardsWithCostModificationTest extends CardTestPlayerBase { + + @Test + public void test_CostReduction_Simple() { + // {2}{W}{U} + // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name. + // Your opponents can’t cast spells with the chosen name. + // Spells with the chosen name you cast cost {2} less to cast. + addCard(Zone.HAND, playerA, "Council of the Absolute", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + addCard(Zone.HAND, playerA, "Blastfire Bolt", 1); // {5}{R}, 5 damage + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6 - 2); // -2 for cost reduction + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + + // cast Council + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute"); + setChoice(playerA, "Blastfire Bolt"); + + // cast bolt + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blastfire Bolt", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Balduvian Bears", 1); + } + + @Test + public void test_CostReduction_SplitLeft() { + // {2}{W}{U} + // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name. + // Your opponents can’t cast spells with the chosen name. + // Spells with the chosen name you cast cost {2} less to cast. + addCard(Zone.HAND, playerA, "Council of the Absolute", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 - 1); // -1 from cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 2); // check not working right cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + // cast Council + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute"); + setChoice(playerA, "Armed"); + + // cast Armed + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", false); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", false); + showAvaileableAbilities("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Armed", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } + + @Test + public void test_CostReduction_SplitRight() { + // {2}{W}{U} + // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name. + // Your opponents can’t cast spells with the chosen name. + // Spells with the chosen name you cast cost {2} less to cast. + addCard(Zone.HAND, playerA, "Council of the Absolute", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 2); // -2 from cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + // cast Council + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute"); + setChoice(playerA, "Dangerous"); + + // cast Dangerous + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", false); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", false); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dangerous", "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } + + @Test + @Ignore // TODO: cost modification don't work for fused spells, only for one of the part, see https://github.com/magefree/mage/issues/6603 + public void test_CostReduction_SplitFused_ReduceRight() { + // {2}{W}{U} + // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name. + // Your opponents can’t cast spells with the chosen name. + // Spells with the chosen name you cast cost {2} less to cast. + addCard(Zone.HAND, playerA, "Council of the Absolute", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); // no cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 2); // -2 from cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + // cast Council + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute"); + setChoice(playerA, "Dangerous"); + + // cast fused + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Armed // Dangerous"); + addTarget(playerA, "Balduvian Bears"); + addTarget(playerA, "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } + + @Test + @Ignore // TODO: cost modification don't work for fused spells, only for one of the part, see https://github.com/magefree/mage/issues/6603 + public void test_CostReduction_SplitFused_ReduceLeft() { + // {2}{W}{U} + // As Council of the Absolute enters the battlefield, choose a noncreature, nonland card name. + // Your opponents can’t cast spells with the chosen name. + // Spells with the chosen name you cast cost {2} less to cast. + addCard(Zone.HAND, playerA, "Council of the Absolute", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // + // Armed {1}{R} Target creature gets +1/+1 and gains double strike until end of turn. + // Dangerous {3}{G} All creatures able to block target creature this turn do so. + addCard(Zone.HAND, playerA, "Armed // Dangerous", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2 - 1); // -1 from cost reduction + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4 - 1); // -1 from cost reduction ON FUSED + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + + // cast Council + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}", 3); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Council of the Absolute"); + setChoice(playerA, "Armed"); + + // cast fused + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Armed", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Dangerous", true); + checkPlayableAbility("after reduction", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast fused Armed // Dangerous", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Armed // Dangerous"); + addTarget(playerA, "Balduvian Bears"); + addTarget(playerA, "Balduvian Bears"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Armed // Dangerous", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java index 25c5a66b225..1702f2f90ff 100644 --- a/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java +++ b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java @@ -1,5 +1,7 @@ package org.mage.test.testapi; +import mage.cards.Card; +import mage.cards.repository.CardRepository; import mage.constants.EmptyNames; import mage.constants.PhaseStep; import mage.constants.Zone; @@ -44,6 +46,18 @@ public class TestAliases extends CardTestPlayerBase { Assert.assertFalse(CardUtil.haveSameNames("Name", "123", true)); Assert.assertFalse(CardUtil.haveSameNames("Name", EmptyNames.FACE_DOWN_CREATURE.toString(), true)); Assert.assertFalse(CardUtil.haveSameNames("Name1", "Name2", true)); + + // name with split card + Card splitCard1 = CardRepository.instance.findCard("Armed // Dangerous").getCard(); + Card splitCard2 = CardRepository.instance.findCard("Alive // Well").getCard(); + Assert.assertTrue(CardUtil.haveSameNames(splitCard1, "Armed", currentGame)); + Assert.assertTrue(CardUtil.haveSameNames(splitCard1, "Dangerous", currentGame)); + Assert.assertTrue(CardUtil.haveSameNames(splitCard1, "Armed // Dangerous", currentGame)); + Assert.assertTrue(CardUtil.haveSameNames(splitCard1, splitCard1)); + Assert.assertFalse(CardUtil.haveSameNames(splitCard1, "Other", currentGame)); + Assert.assertFalse(CardUtil.haveSameNames(splitCard1, "Other // Dangerous", currentGame)); + Assert.assertFalse(CardUtil.haveSameNames(splitCard1, "Armed // Other", currentGame)); + Assert.assertFalse(CardUtil.haveSameNames(splitCard1, splitCard2)); } @Test diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java index b80f7766a70..bc266d2718f 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java @@ -31,11 +31,13 @@ public class NamePredicate implements Predicate { // A split card has the chosen name if one of its two names matches the chosen name. if (input instanceof SplitCard) { return CardUtil.haveSameNames(name, ((SplitCard) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || - CardUtil.haveSameNames(name, ((SplitCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames); + CardUtil.haveSameNames(name, ((SplitCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || + CardUtil.haveSameNames(name, input.getName(), this.ignoreMtgRuleForEmptyNames); } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { SplitCard card = (SplitCard) ((Spell) input).getCard(); return CardUtil.haveSameNames(name, card.getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || - CardUtil.haveSameNames(name, card.getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames); + CardUtil.haveSameNames(name, card.getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || + CardUtil.haveSameNames(name, card.getName(), this.ignoreMtgRuleForEmptyNames); } else { if (name.contains(" // ")) { String leftName = name.substring(0, name.indexOf(" // ")); diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 99022d44aef..b418ad2fd8a 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -11,6 +11,7 @@ import mage.constants.ColoredManaSymbol; import mage.constants.EmptyNames; import mage.constants.ManaType; import mage.filter.Filter; +import mage.filter.predicate.mageobject.NamePredicate; import mage.game.CardState; import mage.game.Game; import mage.game.permanent.Permanent; @@ -657,6 +658,14 @@ public final class CardUtil { return object1 != null && object2 != null && haveSameNames(object1.getName(), object2.getName()); } + public static boolean haveSameNames(MageObject object, String needName, Game game) { + return containsName(object, needName, game); + } + + public static boolean containsName(MageObject object, String name, Game game) { + return new NamePredicate(name).apply(object, game); + } + public static boolean haveEmptyName(String name) { return name == null || name.isEmpty() || name.equals(EmptyNames.FACE_DOWN_CREATURE.toString()) || name.equals(EmptyNames.FACE_DOWN_TOKEN.toString()); }