diff --git a/Mage.Sets/src/mage/cards/b/BattleMammoth.java b/Mage.Sets/src/mage/cards/b/BattleMammoth.java index 868a36c2bc..3255852857 100644 --- a/Mage.Sets/src/mage/cards/b/BattleMammoth.java +++ b/Mage.Sets/src/mage/cards/b/BattleMammoth.java @@ -1,7 +1,7 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetControlledPermanentTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.ForetellAbility; import mage.abilities.keyword.TrampleAbility; @@ -9,14 +9,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; -import mage.players.Player; -import java.util.*; +import java.util.UUID; /** * @author TheElk801 @@ -34,7 +28,9 @@ public final class BattleMammoth extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card. - this.addAbility(new BattleMammothTriggeredAbility()); + this.addAbility(new BecomesTargetControlledPermanentTriggeredAbility( + new DrawCardSourceControllerEffect(1), true + )); // Foretell {2}{G}{G} this.addAbility(new ForetellAbility(this, "{2}{G}{G}")); @@ -49,65 +45,3 @@ public final class BattleMammoth extends CardImpl { return new BattleMammoth(this); } } - -class BattleMammothTriggeredAbility extends TriggeredAbilityImpl { - - BattleMammothTriggeredAbility() { - super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), true); - } - - private BattleMammothTriggeredAbility(final BattleMammothTriggeredAbility ability) { - super(ability); - } - - @Override - public BattleMammothTriggeredAbility copy() { - return new BattleMammothTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.TARGETED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); - if (sourceObject == null) { - return false; - } - Player targetter = game.getPlayer(event.getPlayerId()); - if (targetter == null || !targetter.hasOpponent(this.controllerId, game)) { - return false; - } - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null || !permanent.isControlledBy(this.getControllerId())) { - return false; - } - // If a spell or ability an opponent controls targets a single permanent you control more than once, - // Battle Mammoth’s triggered ability will trigger only once. - // However, if a spell or ability an opponent controls targets multiple permanents you control, - // Battle Mammoth’s triggered ability will trigger once for each of those permanents. - - // targetMap - key - targetId, value - Set of stackObject Ids - Map> targetMap = (Map>) game.getState().getValue("targetMap" + this.id); - if (targetMap == null) { - targetMap = new HashMap<>(); - } - Set sourceObjects = targetMap.get(event.getTargetId()); - if (sourceObjects == null) { - sourceObjects = new HashSet<>(); - } - if (!sourceObjects.add(sourceObject.getId())) { - return false; - } - targetMap.put(event.getTargetId(), sourceObjects); - game.getState().setValue("targetMap" + this.id, targetMap); - return true; - } - - @Override - public String getRule() { - return "Whenever a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card."; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java new file mode 100644 index 0000000000..437655b059 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java @@ -0,0 +1,209 @@ +package mage.cards.m; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BecomesTargetControlledPermanentTriggeredAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.delayed.AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacesCard; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.command.emblems.LukkaWaywardBonderEmblem; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; + +import java.util.Objects; +import java.util.UUID; + +import static mage.constants.Outcome.Benefit; + +/** + * @author TheElk801 + */ +public final class MilaCraftyCompanion extends ModalDoubleFacesCard { + + public MilaCraftyCompanion(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.FOX}, "{1}{W}{W}", + "Lukka, Wayward Bonder", + new CardType[]{CardType.PLANESWALKER}, new SubType[]{SubType.LUKKA}, "{4}{R}{R}" + ); + + // 1. + // Mila, Crafty Companion + // Legendary Creature - Fox + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().setPT(2, 3); + + // Whenever an opponent attacks one or more planeswalkers you control, put a loyalty counter on each planeswalker you control. + this.getLeftHalfCard().addAbility(new MilaCraftyCompanionTriggeredAbility()); + + // Whenever a permanent you control becomes the target of a spell or ability and opponent controls, you may draw a card. + this.getLeftHalfCard().addAbility(new BecomesTargetControlledPermanentTriggeredAbility( + new DrawCardSourceControllerEffect(1), true + )); + + // 2. + // Lukka, Wayward Bonder + // Legendary Planeswalker - Lukka + this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); + this.getRightHalfCard().addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + + // +1: You may discard a card. If you do, draw a card. If a creature card was discarded this way, draw two cards instead. + this.getRightHalfCard().addAbility(new LoyaltyAbility(new LukkaWaywardBonderDiscardEffect(), 1)); + + // −2: Return target creature card from your graveyard to the battlefield. It gains haste. Exile it at the beginning of your next upkeep. + Ability ability = new LoyaltyAbility(new LukkaWaywardBonderReturnEffect(), -2); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.getRightHalfCard().addAbility(ability); + + // −7: You get an emblem with "Whenever a creature enters the battlefield under your control, it deals damage equal to its power to any target." + this.getRightHalfCard().addAbility(new LoyaltyAbility( + new GetEmblemEffect(new LukkaWaywardBonderEmblem()), -7 + )); + } + + private MilaCraftyCompanion(final MilaCraftyCompanion card) { + super(card); + } + + @Override + public MilaCraftyCompanion copy() { + return new MilaCraftyCompanion(this); + } +} + +class MilaCraftyCompanionTriggeredAbility extends TriggeredAbilityImpl { + + MilaCraftyCompanionTriggeredAbility() { + super(Zone.BATTLEFIELD, new AddCountersAllEffect( + CounterType.LOYALTY.createInstance(), StaticFilters.FILTER_CONTROLLED_PERMANENT_PLANESWALKER + ), false); + } + + private MilaCraftyCompanionTriggeredAbility(final MilaCraftyCompanionTriggeredAbility ability) { + super(ability); + } + + @Override + public MilaCraftyCompanionTriggeredAbility copy() { + return new MilaCraftyCompanionTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return game.getCombat() + .getAttackers() + .stream() + .filter(attackerId -> game.getOpponents(getControllerId()).contains(game.getControllerId(attackerId))) + .map(game.getCombat()::getDefenderId) + .map(game::getPermanent) + .filter(Objects::nonNull) + .filter(MageObject::isPlaneswalker) + .map(Controllable::getControllerId) + .anyMatch(getControllerId()::equals); + } + + @Override + public String getRule() { + return "Whenever an opponent attacks one or more planeswalkers you control, " + + "put a loyalty counter on each planeswalker you control."; + } +} + +class LukkaWaywardBonderDiscardEffect extends OneShotEffect { + + LukkaWaywardBonderDiscardEffect() { + super(Benefit); + staticText = "you may discard a card. If you do, draw a card. " + + "If a creature card was discarded this way, draw two cards instead"; + } + + private LukkaWaywardBonderDiscardEffect(final LukkaWaywardBonderDiscardEffect effect) { + super(effect); + } + + @Override + public LukkaWaywardBonderDiscardEffect copy() { + return new LukkaWaywardBonderDiscardEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.discard(0, 1, false, source, game).getRandom(game); + if (card == null) { + return false; + } + player.drawCards(card.isCreature() ? 2 : 1, source, game); + return true; + } +} + +class LukkaWaywardBonderReturnEffect extends OneShotEffect { + + LukkaWaywardBonderReturnEffect() { + super(Outcome.Benefit); + staticText = "return target creature card from your graveyard to the battlefield. " + + "It gains haste. Exile it at the beginning of your next upkeep"; + } + + private LukkaWaywardBonderReturnEffect(final LukkaWaywardBonderReturnEffect effect) { + super(effect); + } + + @Override + public LukkaWaywardBonderReturnEffect copy() { + return new LukkaWaywardBonderReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getFirstTarget()); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent == null) { + return false; + } + game.addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setTargetPointer(new FixedTarget(permanent, game)), source); + game.addDelayedTriggeredAbility(new AtTheBeginOfYourNextUpkeepDelayedTriggeredAbility( + new ExileTargetEffect() + .setText("Exile it at the beginning of your next upkeep.") + .setTargetPointer(new FixedTarget(permanent, game)), + Duration.Custom, true + )); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java index 18dd2f3eb2..f3c631f2cd 100644 --- a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java +++ b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java @@ -178,6 +178,7 @@ public final class StrixhavenSchoolOfMages extends ExpansionSet { cards.add(new SetCardInfo("Master Symmetrist", 138, Rarity.UNCOMMON, mage.cards.m.MasterSymmetrist.class)); cards.add(new SetCardInfo("Mentor's Guidance", 46, Rarity.UNCOMMON, mage.cards.m.MentorsGuidance.class)); cards.add(new SetCardInfo("Mercurial Transformation", 47, Rarity.UNCOMMON, mage.cards.m.MercurialTransformation.class)); + cards.add(new SetCardInfo("Mila, Crafty Companion", 153, Rarity.MYTHIC, mage.cards.m.MilaCraftyCompanion.class)); cards.add(new SetCardInfo("Moldering Karok", 206, Rarity.COMMON, mage.cards.m.MolderingKarok.class)); cards.add(new SetCardInfo("Mortality Spear", 207, Rarity.UNCOMMON, mage.cards.m.MortalitySpear.class)); cards.add(new SetCardInfo("Mountain", 372, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage/src/main/java/mage/abilities/common/BecomesTargetControlledPermanentTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BecomesTargetControlledPermanentTriggeredAbility.java new file mode 100644 index 0000000000..6132b9e505 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/BecomesTargetControlledPermanentTriggeredAbility.java @@ -0,0 +1,77 @@ +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.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.players.Player; + +import java.util.*; + +/** + * @author weirddan455 + */ +public class BecomesTargetControlledPermanentTriggeredAbility extends TriggeredAbilityImpl { + + public BecomesTargetControlledPermanentTriggeredAbility(Effect effect, boolean optional) { + super(Zone.BATTLEFIELD, effect, optional); + } + + private BecomesTargetControlledPermanentTriggeredAbility(final BecomesTargetControlledPermanentTriggeredAbility ability) { + super(ability); + } + + @Override + public BecomesTargetControlledPermanentTriggeredAbility copy() { + return new BecomesTargetControlledPermanentTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.TARGETED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + StackObject sourceObject = game.getStack().getStackObject(event.getSourceId()); + if (sourceObject == null) { + return false; + } + Player targetter = game.getPlayer(event.getPlayerId()); + if (targetter == null || !targetter.hasOpponent(this.controllerId, game)) { + return false; + } + Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); + if (permanent == null || !permanent.isControlledBy(this.getControllerId())) { + return false; + } + // If a spell or ability an opponent controls targets a single permanent you control more than once, + // Battle Mammoth’s triggered ability will trigger only once. + // However, if a spell or ability an opponent controls targets multiple permanents you control, + // Battle Mammoth’s triggered ability will trigger once for each of those permanents. + + // targetMap - key - targetId, value - Set of stackObject Ids + Map> targetMap = (Map>) game.getState().getValue("targetMap" + this.id); + if (targetMap == null) { + targetMap = new HashMap<>(); + } + Set sourceObjects = targetMap.get(event.getTargetId()); + if (sourceObjects == null) { + sourceObjects = new HashSet<>(); + } + if (!sourceObjects.add(sourceObject.getId())) { + return false; + } + targetMap.put(event.getTargetId(), sourceObjects); + game.getState().setValue("targetMap" + this.id, targetMap); + return true; + } + + @Override + public String getRule() { + return "Whenever a permanent you control becomes the target of a spell or ability an opponent controls, " + super.getRule(); + } +} diff --git a/Mage/src/main/java/mage/game/command/emblems/LukkaWaywardBonderEmblem.java b/Mage/src/main/java/mage/game/command/emblems/LukkaWaywardBonderEmblem.java new file mode 100644 index 0000000000..bb6180fea6 --- /dev/null +++ b/Mage/src/main/java/mage/game/command/emblems/LukkaWaywardBonderEmblem.java @@ -0,0 +1,63 @@ +package mage.game.command.emblems; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.command.Emblem; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetAnyTarget; + +/** + * @author TheElk801 + */ +public final class LukkaWaywardBonderEmblem extends Emblem { + + // −7: You get an emblem with "Whenever a creature enters the battlefield under your control, it deals damage equal to its power to any target." + public LukkaWaywardBonderEmblem() { + this.setName("Emblem Lukka"); + this.setExpansionSetCodeForImage("STX"); + Ability ability = new EntersBattlefieldControlledTriggeredAbility( + new LukkaWaywardBonderEmblemEffect(), StaticFilters.FILTER_PERMANENT_CREATURE_A + ); + ability.addTarget(new TargetAnyTarget()); + this.getAbilities().add(ability); + } +} + +class LukkaWaywardBonderEmblemEffect extends OneShotEffect { + + LukkaWaywardBonderEmblemEffect() { + super(Outcome.Benefit); + staticText = "it deals damage equal to its power to any target"; + } + + private LukkaWaywardBonderEmblemEffect(final LukkaWaywardBonderEmblemEffect effect) { + super(effect); + } + + @Override + public LukkaWaywardBonderEmblemEffect copy() { + return new LukkaWaywardBonderEmblemEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = (Permanent) getValue("permanentEnteringBattlefield"); + if (permanent == null || permanent.getPower().getValue() < 1) { + return false; + } + Permanent targetPermanent = game.getPermanent(source.getFirstTarget()); + if (targetPermanent != null) { + return targetPermanent.damage(permanent.getPower().getValue(), permanent.getId(), source, game) > 0; + } + Player targetPlayer = game.getPlayer(source.getFirstTarget()); + if (targetPermanent != null) { + return targetPermanent.damage(permanent.getPower().getValue(), permanent.getId(), source, game) > 0; + } + return false; + } +}