diff --git a/Mage.Sets/src/mage/cards/p/ProfessorOnyx.java b/Mage.Sets/src/mage/cards/p/ProfessorOnyx.java new file mode 100644 index 00000000000..090d468a5ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProfessorOnyx.java @@ -0,0 +1,146 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.MagecraftAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.*; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetDiscard; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ProfessorOnyx extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent( + "a creature with the greatest power among creatures that player controls" + ); + + static { + filter.add(ProfessorOnyxPredicate.instance); + } + + public ProfessorOnyx(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.LILIANA); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + + // Magecraft — Whenever you cast or copy an instant or sorcery spell, each opponent loses 2 life and you gain 2 life. + Ability ability = new MagecraftAbility(new LoseLifeOpponentsEffect(2), false); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); + this.addAbility(ability); + + // +1: You lose 1 life. Look at the top three cards of your library. Put one of them into your hand and the rest into your graveyard. + ability = new LoyaltyAbility(new LoseLifeSourceControllerEffect(1), 1); + ability.addEffect(new LookLibraryAndPickControllerEffect( + StaticValue.get(3), false, StaticValue.get(1), StaticFilters.FILTER_CARD, + Zone.GRAVEYARD, false, false, false, Zone.HAND, false + ).setText("Look at the top three cards of your library. Put one of them into your hand and the rest into your graveyard")); + this.addAbility(ability); + + // −3: Each opponent sacrifices a creature with the greatest power among creatures that player controls. + this.addAbility(new LoyaltyAbility(new SacrificeOpponentsEffect(filter), -3)); + + // −8: Each opponent may discard a card. If they don't, they lose 3 life. Repeat this process six more times. + this.addAbility(new LoyaltyAbility(new ProfessorOnyxEffect(), -8)); + } + + private ProfessorOnyx(final ProfessorOnyx card) { + super(card); + } + + @Override + public ProfessorOnyx copy() { + return new ProfessorOnyx(this); + } +} + +enum ProfessorOnyxPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + return input.getPower().getValue() + >= game + .getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + input.getControllerId(), game + ) + .stream() + .filter(Objects::nonNull) + .map(MageObject::getPower) + .mapToInt(MageInt::getValue) + .max() + .orElse(Integer.MIN_VALUE); + } +} + +class ProfessorOnyxEffect extends OneShotEffect { + + ProfessorOnyxEffect() { + super(Outcome.Benefit); + staticText = "each opponent may discard a card. If they don't, " + + "they lose 3 life. Repeat this process six more times"; + } + + private ProfessorOnyxEffect(final ProfessorOnyxEffect effect) { + super(effect); + } + + @Override + public ProfessorOnyxEffect copy() { + return new ProfessorOnyxEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (int i = 0; i < 6; i++) { + Map playerMap = new HashMap<>(); + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + TargetDiscard target = new TargetDiscard( + 0, 1, StaticFilters.FILTER_CARD, playerId + ); + player.choose(Outcome.Discard, target, source.getSourceId(), game); + playerMap.put(playerId, game.getCard(target.getFirstTarget())); + } + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + if (playerMap.get(playerId) == null + || !player.discard(playerMap.get(playerId), false, source, game)) { + player.loseLife(3, game, source, false); + } + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java index bec822f4f4c..551fec3604d 100644 --- a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java +++ b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java @@ -30,6 +30,7 @@ public final class StrixhavenSchoolOfMages extends ExpansionSet { cards.add(new SetCardInfo("Kasmina, Enigma Sage", 196, Rarity.MYTHIC, mage.cards.k.KasminaEnigmaSage.class)); cards.add(new SetCardInfo("Lorehold Command", 199, Rarity.RARE, mage.cards.l.LoreholdCommand.class)); cards.add(new SetCardInfo("Prismari Command", 214, Rarity.RARE, mage.cards.p.PrismariCommand.class)); + cards.add(new SetCardInfo("Professor Onyx", 83, Rarity.MYTHIC, mage.cards.p.ProfessorOnyx.class)); cards.add(new SetCardInfo("Quandrix Command", 217, Rarity.RARE, mage.cards.q.QuandrixCommand.class)); cards.add(new SetCardInfo("Silverquill Command", 232, Rarity.RARE, mage.cards.s.SilverquillCommand.class)); cards.add(new SetCardInfo("Witherbloom Command", 248, Rarity.RARE, mage.cards.w.WitherbloomCommand.class)); diff --git a/Mage/src/main/java/mage/abilities/common/MagecraftAbility.java b/Mage/src/main/java/mage/abilities/common/MagecraftAbility.java new file mode 100644 index 00000000000..bd777118976 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/MagecraftAbility.java @@ -0,0 +1,44 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; + +/** + * @author TheElk801 + */ +public class MagecraftAbility extends TriggeredAbilityImpl { + + public MagecraftAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + } + + private MagecraftAbility(final MagecraftAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COPIED_STACKOBJECT + || event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Spell spell = game.getSpell(event.getTargetId()); + return spell != null && spell.isControlledBy(getControllerId()) && spell.isInstantOrSorcery(); + } + + @Override + public String getRule() { + return "Magecraft — Whenever you cast or copy an instant or sorcery spell, " + super.getRule(); + } + + @Override + public MagecraftAbility copy() { + return new MagecraftAbility(this); + } +} diff --git a/Mage/src/main/java/mage/constants/AbilityWord.java b/Mage/src/main/java/mage/constants/AbilityWord.java index e37da7f9d0e..18a15835f04 100644 --- a/Mage/src/main/java/mage/constants/AbilityWord.java +++ b/Mage/src/main/java/mage/constants/AbilityWord.java @@ -32,6 +32,7 @@ public enum AbilityWord { LANDFALL("Landfall"), LIEUTENANT("Lieutenant"), METALCRAFT("Metalcraft"), + MAGECRAFT("Magecraft"), MORBID("Morbid"), PARLEY("Parley"), RADIANCE("Radiance"),