diff --git a/Mage.Sets/src/mage/cards/h/HistoriansBoon.java b/Mage.Sets/src/mage/cards/h/HistoriansBoon.java index 26030bff420..5302f5ce476 100644 --- a/Mage.Sets/src/mage/cards/h/HistoriansBoon.java +++ b/Mage.Sets/src/mage/cards/h/HistoriansBoon.java @@ -80,7 +80,7 @@ class HistoriansBoonTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { StackObject stackObject = game.getStack().getStackObject(event.getTargetId()); - Permanent permanent = game.getPermanent(event.getSourceId()); + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); if (stackObject == null || permanent == null || !permanent.isControlledBy(getControllerId()) diff --git a/Mage.Sets/src/mage/cards/n/NarciFableSinger.java b/Mage.Sets/src/mage/cards/n/NarciFableSinger.java new file mode 100644 index 00000000000..d526217430e --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NarciFableSinger.java @@ -0,0 +1,87 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.FinalChapterAbilityResolvesTriggeredAbility; +import mage.abilities.common.SacrificePermanentTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class NarciFableSinger extends CardImpl { + + public NarciFableSinger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.BARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Whenever you sacrifice an enchantment, draw a card. + this.addAbility(new SacrificePermanentTriggeredAbility( + new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_PERMANENT_ENCHANTMENT + )); + + // Whenever the final chapter ability of a Saga you control resolves, each opponent loses X life and you gain X life, where X is that Saga's mana value. + this.addAbility(new FinalChapterAbilityResolvesTriggeredAbility( + new NarciFableSingerEffect(), true + )); + } + + private NarciFableSinger(final NarciFableSinger card) { + super(card); + } + + @Override + public NarciFableSinger copy() { + return new NarciFableSinger(this); + } +} + +class NarciFableSingerEffect extends OneShotEffect { + + NarciFableSingerEffect() { + super(Outcome.Benefit); + staticText = "each opponent loses X life and you gain X life, where X is that Saga's mana value."; + } + + private NarciFableSingerEffect(final NarciFableSingerEffect ability) { + super(ability); + } + + @Override + public NarciFableSingerEffect copy() { + return new NarciFableSingerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int value = (int) getValue("SAGA_MANACOST"); + if (value > 0) { + new LoseLifeOpponentsEffect(value).apply(game, source); + new GainLifeEffect(value).apply(game, source); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TomBombadil.java b/Mage.Sets/src/mage/cards/t/TomBombadil.java index b6a33e5e97b..60a87206a1e 100644 --- a/Mage.Sets/src/mage/cards/t/TomBombadil.java +++ b/Mage.Sets/src/mage/cards/t/TomBombadil.java @@ -1,34 +1,25 @@ package mage.cards.t; -import java.util.Optional; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.SagaAbility; +import mage.abilities.common.FinalChapterAbilityResolvesTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CountersOnPermanentsCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.common.CountersOnPermanentsHint; import mage.abilities.keyword.HexproofAbility; import mage.abilities.keyword.IndestructibleAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.players.Player; -import mage.util.CardUtil; -import mage.abilities.hint.common.CountersOnPermanentsHint; + +import java.util.UUID; /** * @author alexander-novo @@ -49,21 +40,17 @@ public final class TomBombadil extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // As long as there are four or more lore counters among Sagas you control, Tom - // Bombadil has hexproof and indestructible. + // As long as there are four or more lore counters among Sagas you control, Tom Bombadil has hexproof and indestructible. Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( - new GainAbilitySourceEffect(HexproofAbility.getInstance()), - condition, - "As long as there are four or more lore counters among Sagas you control, {this} has hexproof")); + new GainAbilitySourceEffect(HexproofAbility.getInstance()), + condition, + "As long as there are four or more lore counters among Sagas you control, {this} has hexproof")); ability.addEffect(new ConditionalContinuousEffect( - new GainAbilitySourceEffect(IndestructibleAbility.getInstance()), condition, "and indestructible")); + new GainAbilitySourceEffect(IndestructibleAbility.getInstance()), condition, "and indestructible")); this.addAbility(ability.addHint(hint)); - // Whenever the final chapter ability of a Saga you control resolves, reveal - // cards from the top of your library until you reveal a Saga card. Put that - // card onto the battlefield and the rest on the bottom of your library in a - // random order. This ability triggers only once each turn. - this.addAbility(new TomBombadilTriggeredAbility().setTriggersOnceEachTurn(true)); + // Whenever the final chapter ability of a Saga you control resolves, reveal cards from the top of your library until you reveal a Saga card. Put that card onto the battlefield and the rest on the bottom of your library in a random order. This ability triggers only once each turn. + this.addAbility(new FinalChapterAbilityResolvesTriggeredAbility(new TomBombadilEffect()).setTriggersOnceEachTurn(true)); } private TomBombadil(final TomBombadil card) { @@ -76,71 +63,6 @@ public final class TomBombadil extends CardImpl { } } -class TomBombadilTriggeredAbility extends TriggeredAbilityImpl { - - public TomBombadilTriggeredAbility() { - super(Zone.BATTLEFIELD, new TomBombadilEffect(), false); - setTriggerPhrase("Whenever the final chapter ability of a Saga you control resolves, "); - } - - public TomBombadilTriggeredAbility(final TomBombadilTriggeredAbility ability) { - super(ability); - } - - @Override - public TomBombadilTriggeredAbility copy() { - return new TomBombadilTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.RESOLVING_ABILITY; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - // At this point, the stack ability no longer exists, so we can only reference - // the ability that it came from. For EventType.RESOLVING_ABILITY, targetID is - // the ID of the original ability (on the permanent) that the resolving ability - // came from. - Optional ability_opt = game.getAbility(event.getTargetId(), event.getSourceId()); - if (!ability_opt.isPresent()) - return false; - - // Make sure it was a triggered ability (needed for checking if it's a chapter - // ability) - Ability ability = ability_opt.get(); - if (!(ability instanceof TriggeredAbility)) - return false; - - // Make sure it was a chapter ability - TriggeredAbility triggeredAbility = (TriggeredAbility) ability; - if (!SagaAbility.isChapterAbility(triggeredAbility)) - return false; - - // There's a chance that the permanent that this abiltiy came from no longer - // exists, so try and find it on the battlefield or check last known - // information. - // This permanent is needed to check if the resolving ability was the final - // chapter ability on that permanent. - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (permanent == null - || !permanent.isControlledBy(getControllerId()) - || !permanent.hasSubtype(SubType.SAGA, game)) { - return false; - } - // Find the max chapter number from that permanent - int maxChapter = CardUtil - .castStream(permanent.getAbilities(game).stream(), SagaAbility.class) - .map(SagaAbility::getMaxChapter) - .mapToInt(SagaChapter::getNumber) - .sum(); - // Check if the ability was the last one - return SagaAbility.isFinalAbility(triggeredAbility, maxChapter); - } - -} - // From PrismaticBridgeEffect class TomBombadilEffect extends OneShotEffect { diff --git a/Mage.Sets/src/mage/sets/CommanderMasters.java b/Mage.Sets/src/mage/sets/CommanderMasters.java index f3ee7f6c8bc..3b91f3ed721 100644 --- a/Mage.Sets/src/mage/sets/CommanderMasters.java +++ b/Mage.Sets/src/mage/sets/CommanderMasters.java @@ -420,6 +420,7 @@ public final class CommanderMasters extends ExpansionSet { cards.add(new SetCardInfo("Nadier's Nightblade", 175, Rarity.COMMON, mage.cards.n.NadiersNightblade.class)); cards.add(new SetCardInfo("Nahiri, the Harbinger", 929, Rarity.MYTHIC, mage.cards.n.NahiriTheHarbinger.class)); cards.add(new SetCardInfo("Nahiri, the Lithomancer", 45, Rarity.RARE, mage.cards.n.NahiriTheLithomancer.class)); + cards.add(new SetCardInfo("Narci, Fable Singer", 710, Rarity.MYTHIC, mage.cards.n.NarciFableSinger.class)); cards.add(new SetCardInfo("Narset of the Ancient Way", 930, Rarity.MYTHIC, mage.cards.n.NarsetOfTheAncientWay.class)); cards.add(new SetCardInfo("Narset, Enlightened Master", 931, Rarity.MYTHIC, mage.cards.n.NarsetEnlightenedMaster.class)); cards.add(new SetCardInfo("Narset, Parter of Veils", 853, Rarity.UNCOMMON, mage.cards.n.NarsetParterOfVeils.class)); diff --git a/Mage/src/main/java/mage/abilities/common/FinalChapterAbilityResolvesTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/FinalChapterAbilityResolvesTriggeredAbility.java new file mode 100644 index 00000000000..ef711a0a354 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/FinalChapterAbilityResolvesTriggeredAbility.java @@ -0,0 +1,101 @@ +package mage.abilities.common; + +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.Optional; + +/** + * @author alexander-novo, Susucr + */ +public class FinalChapterAbilityResolvesTriggeredAbility extends TriggeredAbilityImpl { + + private final boolean rememberSaga; + + public FinalChapterAbilityResolvesTriggeredAbility(Effect effect) { + this(effect, false); + } + + public FinalChapterAbilityResolvesTriggeredAbility(Effect effect, boolean rememberSaga) { + super(Zone.BATTLEFIELD, effect, false); + this.rememberSaga = rememberSaga; + setTriggerPhrase("Whenever the final chapter ability of a Saga you control resolves, "); + } + + private FinalChapterAbilityResolvesTriggeredAbility(final FinalChapterAbilityResolvesTriggeredAbility ability) { + super(ability); + this.rememberSaga = ability.rememberSaga; + } + + @Override + public FinalChapterAbilityResolvesTriggeredAbility copy() { + return new FinalChapterAbilityResolvesTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.RESOLVING_ABILITY; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + // At this point, the stack ability no longer exists, so we can only reference + // the ability that it came from. For EventType.RESOLVING_ABILITY, targetID is + // the ID of the original ability (on the permanent) that the resolving ability + // came from. + Optional ability_opt = game.getAbility(event.getTargetId(), event.getSourceId()); + if (!ability_opt.isPresent()) + return false; + + // Make sure it was a triggered ability (needed for checking if it's a chapter + // ability) + Ability ability = ability_opt.get(); + if (!(ability instanceof TriggeredAbility)) + return false; + + // Make sure it was a chapter ability + TriggeredAbility triggeredAbility = (TriggeredAbility) ability; + if (!SagaAbility.isChapterAbility(triggeredAbility)) + return false; + + // There's a chance that the permanent that this abiltiy came from no longer + // exists, so try and find it on the battlefield or check last known + // information. + // This permanent is needed to check if the resolving ability was the final + // chapter ability on that permanent. + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); + if (permanent == null + || !permanent.isControlledBy(getControllerId()) + || !permanent.hasSubtype(SubType.SAGA, game)) { + return false; + } + + // Find the max chapter number from that permanent + int maxChapter = CardUtil + .castStream(permanent.getAbilities(game).stream(), SagaAbility.class) + .map(SagaAbility::getMaxChapter) + .mapToInt(SagaChapter::getNumber) + .sum(); + + // Check if the ability was the last one + if (!SagaAbility.isFinalAbility(triggeredAbility, maxChapter)) { + return false; + } + + if (rememberSaga) { + getEffects().setTargetPointer(new FixedTarget(permanent.getId(), game)); + getEffects().setValue("SAGA_MANACOST", permanent.getManaValue()); + } + return true; + } +}