diff --git a/Mage.Sets/src/mage/cards/l/LivingLore.java b/Mage.Sets/src/mage/cards/l/LivingLore.java index b45827d5ea..9b75b7c7bc 100644 --- a/Mage.Sets/src/mage/cards/l/LivingLore.java +++ b/Mage.Sets/src/mage/cards/l/LivingLore.java @@ -1,30 +1,33 @@ package mage.cards.l; -import java.util.UUID; -import mage.ApprovingObject; import mage.MageInt; import mage.MageObject; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.DealsCombatDamageTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.*; -import mage.filter.common.FilterInstantOrSorceryCard; +import mage.filter.StaticFilters; import mage.game.ExileZone; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; +import mage.target.TargetCard; import mage.target.common.TargetCardInYourGraveyard; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class LivingLore extends CardImpl { @@ -36,15 +39,21 @@ public final class LivingLore extends CardImpl { this.toughness = new MageInt(0); // As Living Lore enters the battlefield, exile an instant or sorcery card from your graveyard. - this.addAbility(new AsEntersBattlefieldAbility(new LivingLoreExileEffect(), - "exile an instant or sorcery card from your graveyard")); + this.addAbility(new AsEntersBattlefieldAbility( + new LivingLoreExileEffect(), "exile an instant or sorcery card from your graveyard" + )); // Living Lore's power and toughness are each equal to the exiled card's converted mana cost. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new LivingLoreSetPowerToughnessSourceEffect())); + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SetPowerToughnessSourceEffect(LivingLoreValue.instance, Duration.EndOfGame) + .setText("{this}'s power and toughness are each equal to the exiled card's mana value") + )); // Whenever Living Lore deals combat damage, you may sacrifice it. If you do, // you may cast the exiled card without paying its mana cost. - this.addAbility(new DealsCombatDamageTriggeredAbility(new LivingLoreSacrificeEffect(), true)); + this.addAbility(new DealsCombatDamageTriggeredAbility(new DoIfCostPaid( + new LivingLoreCastEffect(), new SacrificeSourceCost().setText("sacrifice it") + ), false)); } private LivingLore(final LivingLore card) { @@ -76,125 +85,82 @@ class LivingLoreExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanentEntering(source.getSourceId()); - if (sourcePermanent != null && controller != null) { - TargetCardInYourGraveyard target = new TargetCardInYourGraveyard( - new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard")); - if (controller.chooseTarget(outcome, target, source, game)) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), - game.getState().getZoneChangeCounter(source.getSourceId()) + 1); - Card card = controller.getGraveyard().get(target.getFirstTarget(), game); - if (card != null) { - controller.moveCardsToExile(card, source, game, true, exileId, - sourcePermanent.getIdName()); - } - } - return true; + if (controller == null) { + return false; } - return false; - } - -} - -class LivingLoreSetPowerToughnessSourceEffect extends ContinuousEffectImpl { - - public LivingLoreSetPowerToughnessSourceEffect() { - super(Duration.Custom, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.BoostCreature); - staticText = "{this}'s power and toughness are each equal to the exiled card's mana value"; - } - - public LivingLoreSetPowerToughnessSourceEffect(final LivingLoreSetPowerToughnessSourceEffect effect) { - super(effect); - } - - @Override - public LivingLoreSetPowerToughnessSourceEffect copy() { - return new LivingLoreSetPowerToughnessSourceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - int zcc = game.getState().getZoneChangeCounter(source.getSourceId()); - if (permanent == null) { - permanent = game.getPermanentEntering(source.getSourceId()); - zcc++; - } - if (permanent == null) { - return true; - } - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), zcc); - if (exileId != null) { - ExileZone exileZone = game.getExile().getExileZone(exileId); - if (exileZone == null) { - return false; - } - Card exiledCard = null; - for (Card card : exileZone.getCards(game)) { - exiledCard = card; - break; - } - if (exiledCard != null) { - int value = exiledCard.getManaValue(); - permanent.getPower().setValue(value); - permanent.getToughness().setValue(value); - } + TargetCard target = new TargetCardInYourGraveyard( + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD + ); + target.setNotTarget(true); + controller.chooseTarget(outcome, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return false; } + controller.moveCardsToExile( + card, source, game, true, + CardUtil.getExileZoneId(game, source, 1), + CardUtil.getSourceName(game, source) + ); return true; } } -class LivingLoreSacrificeEffect extends OneShotEffect { +enum LivingLoreValue implements DynamicValue { + instance; - public LivingLoreSacrificeEffect() { - super(Outcome.PlayForFree); - this.staticText = "you may sacrifice it. If you do, you may cast " - + "the exiled card without paying its mana cost"; + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + int offset = game.getPermanent(sourceAbility.getSourceId()) != null ? 1 : 0; + ExileZone exileZone = game + .getExile() + .getExileZone(CardUtil.getExileZoneId(game, sourceAbility, offset)); + if (exileZone == null) { + return 0; + } + return exileZone + .getCards(game) + .stream() + .mapToInt(MageObject::getManaValue) + .sum(); } - public LivingLoreSacrificeEffect(final LivingLoreSacrificeEffect effect) { + @Override + public LivingLoreValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } +} + +class LivingLoreCastEffect extends OneShotEffect { + + LivingLoreCastEffect() { + super(Outcome.PlayForFree); + this.staticText = "you may cast the exiled card without paying its mana cost"; + } + + public LivingLoreCastEffect(final LivingLoreCastEffect effect) { super(effect); } @Override - public LivingLoreSacrificeEffect copy() { - return new LivingLoreSacrificeEffect(this); + public LivingLoreCastEffect copy() { + return new LivingLoreCastEffect(this); } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - MageObject mageObject = source.getSourceObject(game); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null - && mageObject != null - && new MageObjectReference(permanent, game).refersTo(mageObject, game)) { - if (permanent.sacrifice(source, game)) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), - source.getSourceObjectZoneChangeCounter()); - if (exileId != null) { - ExileZone exileZone = game.getExile().getExileZone(exileId); - Card exiledCard = null; - if (exileZone != null) { - for (Card card : exileZone.getCards(game)) { - exiledCard = card; - break; - } - } - if (exiledCard != null) { - if (exiledCard.getSpellAbility().canChooseTarget(game, controller.getId())) { - game.getState().setValue("PlayFromNotOwnHandZone" + exiledCard.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(exiledCard, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + exiledCard.getId(), null); - } - } - } - } - } - return true; - } - return false; + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source, -2)); + return controller != null + && exileZone != null + && !exileZone.isEmpty() + && CardUtil.castSpellWithAttributesForFree( + controller, source, game, new CardsImpl(exileZone), StaticFilters.FILTER_CARD + ); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/LivingLoreTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/LivingLoreTest.java deleted file mode 100644 index caa65d6157..0000000000 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/entersBattlefield/LivingLoreTest.java +++ /dev/null @@ -1,33 +0,0 @@ - -package org.mage.test.cards.replacement.entersBattlefield; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * - * @author LevelX2 - */ -public class LivingLoreTest extends CardTestPlayerBase { - - /** - * That the +1/+1 counters are added to Living Lore before state based - * actions take place - */ - @Test - public void testCountersAdded() { - addCard(Zone.BATTLEFIELD, playerA, "Island", 4); - addCard(Zone.HAND, playerA, "Living Lore"); //{3}{U} - addCard(Zone.GRAVEYARD, playerA, "Natural Connection", 1); // {2}{G} - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Living Lore"); - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - - assertPermanentCount(playerA, "Living Lore", 1); - assertPowerToughness(playerA, "Living Lore", 3, 3); - } - -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dtk/LivingLoreTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dtk/LivingLoreTest.java new file mode 100644 index 0000000000..70358ce060 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dtk/LivingLoreTest.java @@ -0,0 +1,54 @@ +package org.mage.test.cards.single.dtk; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author LevelX2 + */ +public class LivingLoreTest extends CardTestPlayerBase { + + /** + * That the +1/+1 counters are added to Living Lore before state based + * actions take place + */ + @Test + public void testCountersAdded() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.HAND, playerA, "Living Lore"); //{3}{U} + addCard(Zone.GRAVEYARD, playerA, "Natural Connection", 1); // {2}{G} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Living Lore"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Living Lore", 1); + assertPowerToughness(playerA, "Living Lore", 3, 3); + } + + @Test + public void testCastSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.HAND, playerA, "Living Lore"); //{3}{U} + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); // {R} + + addTarget(playerA, "Lightning Bolt"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Living Lore"); + + attack(3, playerA, "Living Lore", playerB); + setChoice(playerA, true); // sacrifice + setChoice(playerA, true); // cast spell + addTarget(playerA, playerB); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Living Lore", 0); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertLife(playerB, 20 - 1 - 3); + } +}