diff --git a/Mage.Sets/src/mage/cards/f/Flameskull.java b/Mage.Sets/src/mage/cards/f/Flameskull.java new file mode 100644 index 00000000000..7d3ac303d89 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/Flameskull.java @@ -0,0 +1,156 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.CantBlockAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.*; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class Flameskull extends CardImpl { + + public Flameskull(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); + + this.subtype.add(SubType.SKELETON); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Flameskull can't block. + this.addAbility(new CantBlockAbility()); + + // Rejuvenation — When Flameskull dies, exile it. If you do, exile the top card of your library. Until the end of your next turn, you may play one of those cards. + this.addAbility(new DiesSourceTriggeredAbility(new FlameskullEffect()) + .withFlavorWord("Rejuventation"), new FlameskullWatcher()); + } + + private Flameskull(final Flameskull card) { + super(card); + } + + @Override + public Flameskull copy() { + return new Flameskull(this); + } +} + +class FlameskullEffect extends OneShotEffect { + + FlameskullEffect() { + super(Outcome.Benefit); + staticText = "exile it. If you do, exile the top card of your library. " + + "Until the end of your next turn, you may play one of those cards"; + } + + private FlameskullEffect(final FlameskullEffect effect) { + super(effect); + } + + @Override + public FlameskullEffect copy() { + return new FlameskullEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (player == null || !(sourceObject instanceof Card)) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getFromTop(game)); + cards.add((Card) sourceObject); + player.moveCards(cards, Zone.EXILED, source, game); + game.addEffect(new FlameskullPlayEffect(cards, game), source); + return true; + } +} + +class FlameskullPlayEffect extends AsThoughEffectImpl { + + private final Set morSet = new HashSet<>(); + + FlameskullPlayEffect(Cards cards, Game game) { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.UntilEndOfYourNextTurn, Outcome.Benefit); + cards.stream() + .map(uuid -> new MageObjectReference(uuid, game)) + .forEach(morSet::add); + } + + private FlameskullPlayEffect(final FlameskullPlayEffect effect) { + super(effect); + this.morSet.addAll(effect.morSet); + } + + @Override + public FlameskullPlayEffect copy() { + return new FlameskullPlayEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + UUID objectIdToCast = CardUtil.getMainCardId(game, sourceId); + return source.isControlledBy(affectedControllerId) + && morSet.stream().anyMatch(mor -> mor.refersTo(objectIdToCast, game)) + && FlameskullWatcher.checkRef(source, morSet, game); + } +} + +class FlameskullWatcher extends Watcher { + + private final Map> morMap = new HashMap<>(); + private static final Set emptySet = new HashSet<>(); + + FlameskullWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST + || event.getAdditionalReference() == null) { + return; + } + MageObjectReference mor = event.getAdditionalReference().getApprovingMageObjectReference(); + Spell spell = game.getSpell(event.getTargetId()); + if (mor == null || spell == null) { + return; + } + morMap.computeIfAbsent(mor, x -> new HashSet<>()) + .add(new MageObjectReference(spell.getMainCard(), game, -1)); + } + + static boolean checkRef(Ability source, Set morSet, Game game) { + FlameskullWatcher watcher = game.getState().getWatcher(FlameskullWatcher.class); + return watcher != null + && watcher + .morMap + .getOrDefault(new MageObjectReference(source.getSourceObject(game), game), emptySet) + .stream() + .noneMatch(morSet::contains); + } +} diff --git a/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java b/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java index 27697d31a0b..2b9db117797 100644 --- a/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java +++ b/Mage.Sets/src/mage/sets/AdventuresInTheForgottenRealms.java @@ -101,6 +101,7 @@ public final class AdventuresInTheForgottenRealms extends ExpansionSet { cards.add(new SetCardInfo("Fifty Feet of Rope", 244, Rarity.UNCOMMON, mage.cards.f.FiftyFeetOfRope.class)); cards.add(new SetCardInfo("Fighter Class", 222, Rarity.RARE, mage.cards.f.FighterClass.class)); cards.add(new SetCardInfo("Find the Path", 183, Rarity.COMMON, mage.cards.f.FindThePath.class)); + cards.add(new SetCardInfo("Flameskull", 143, Rarity.MYTHIC, mage.cards.f.Flameskull.class)); cards.add(new SetCardInfo("Flumph", 15, Rarity.RARE, mage.cards.f.Flumph.class)); cards.add(new SetCardInfo("Fly", 59, Rarity.UNCOMMON, mage.cards.f.Fly.class)); cards.add(new SetCardInfo("Forest", 278, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS));