diff --git a/Mage.Sets/src/mage/cards/d/DemonicCovenant.java b/Mage.Sets/src/mage/cards/d/DemonicCovenant.java index 02be019387c..0ab82065414 100644 --- a/Mage.Sets/src/mage/cards/d/DemonicCovenant.java +++ b/Mage.Sets/src/mage/cards/d/DemonicCovenant.java @@ -1,7 +1,7 @@ package mage.cards.d; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksPlayerWithCreaturesTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -12,12 +12,11 @@ import mage.cards.CardSetInfo; import mage.cards.Cards; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Controllable; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.Game; -import mage.game.events.DefenderAttackedEvent; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.DemonToken; import mage.players.Player; @@ -30,6 +29,7 @@ import java.util.UUID; * @author TheElk801 */ public final class DemonicCovenant extends CardImpl { + FilterPermanent filter = new FilterControlledPermanent(SubType.DEMON,"Demons you control"); public DemonicCovenant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.KINDRED, CardType.ENCHANTMENT}, "{4}{B}{B}"); @@ -37,12 +37,14 @@ public final class DemonicCovenant extends CardImpl { this.subtype.add(SubType.DEMON); // Whenever one or more Demons you control attack a player, you draw a card and lose 1 life. - this.addAbility(new DemonicCovenantTriggeredAbility()); + Ability abilityAttack = new AttacksPlayerWithCreaturesTriggeredAbility(new DrawCardSourceControllerEffect(1, true), filter, SetTargetPointer.NONE); + abilityAttack.addEffect(new LoseLifeSourceControllerEffect(1).setText("and lose 1 life")); + this.addAbility(abilityAttack); // At the beginning of your end step, create a 5/5 black Demon creature token with flying, then mill two cards. If two cards that share all their card types were milled this way, sacrifice Demonic Covenant. - Ability ability = new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new DemonToken())); - ability.addEffect(new DemonicCovenantEffect()); - this.addAbility(ability); + Ability abilityEndStep = new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new DemonToken())); + abilityEndStep.addEffect(new DemonicCovenantEffect()); + this.addAbility(abilityEndStep); } private DemonicCovenant(final DemonicCovenant card) { @@ -55,40 +57,6 @@ public final class DemonicCovenant extends CardImpl { } } -class DemonicCovenantTriggeredAbility extends TriggeredAbilityImpl { - - DemonicCovenantTriggeredAbility() { - super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1, true)); - this.addEffect(new LoseLifeSourceControllerEffect(1).setText("and lose 1 life")); - this.setTriggerPhrase("Whenever one or more Demons you control attack a player, "); - } - - private DemonicCovenantTriggeredAbility(final DemonicCovenantTriggeredAbility ability) { - super(ability); - } - - @Override - public DemonicCovenantTriggeredAbility copy() { - return new DemonicCovenantTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return game.getPlayer(event.getTargetId()) != null - && ((DefenderAttackedEvent) event) - .getAttackers(game) - .stream() - .filter(permanent -> permanent.hasSubtype(SubType.DEMON, game)) - .map(Controllable::getControllerId) - .anyMatch(this::isControlledBy); - } -} - class DemonicCovenantEffect extends OneShotEffect { DemonicCovenantEffect() { diff --git a/Mage.Sets/src/mage/cards/e/EchoingAssault.java b/Mage.Sets/src/mage/cards/e/EchoingAssault.java new file mode 100644 index 00000000000..985ecd88d62 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EchoingAssault.java @@ -0,0 +1,107 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksPlayerWithCreaturesTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author notgreat + */ +public final class EchoingAssault extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("nontoken creature that's attacking that player"); + + static { + filter.add(TokenPredicate.FALSE); + filter.add(EchoingAssaultPredicate.instance); + } + + public EchoingAssault(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}"); + + + // Creature tokens you control have menace. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + new MenaceAbility(false), Duration.WhileOnBattlefield, StaticFilters.FILTER_CREATURE_TOKENS + ))); + + // Whenever you attack a player, choose target nontoken creature that's attacking that player. Create a token that's a copy of that creature, except it's 1/1. The token enters tapped and attacking that player. Sacrifice it at the beginning of the next end step. + Ability ability = new AttacksPlayerWithCreaturesTriggeredAbility(new EchoingAssaultEffect(), SetTargetPointer.NONE); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private EchoingAssault(final EchoingAssault card) { + super(card); + } + + @Override + public EchoingAssault copy() { + return new EchoingAssault(this); + } +} + +enum EchoingAssaultPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return CardUtil.getEffectValueFromAbility(input.getSource(), "playerAttacked", UUID.class) + .filter(uuid -> uuid.equals(game.getCombat().getDefenderId(input.getObject().getId()))) + .isPresent(); + } +} + +class EchoingAssaultEffect extends OneShotEffect { + + EchoingAssaultEffect() { + super(Outcome.Benefit); + staticText = "choose target nontoken creature that's attacking that player. Create a token that's a copy of that creature, except it's 1/1. The token enters tapped and attacking that player. Sacrifice it at the beginning of the next end step."; + } + + private EchoingAssaultEffect(final EchoingAssaultEffect effect) { + super(effect); + } + + @Override + public EchoingAssaultEffect copy() { + return new EchoingAssaultEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Object attackedPlayer = getValue("playerAttacked"); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null || !(attackedPlayer instanceof UUID)) { + return false; + } + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, false, + 1, true, true, (UUID) attackedPlayer, 1, 1, false); + effect.setSavedPermanent(permanent); + effect.apply(game, source); + effect.sacrificeTokensCreatedAtNextEndStep(game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FirkraagCunningInstigator.java b/Mage.Sets/src/mage/cards/f/FirkraagCunningInstigator.java index 80cc5b4c077..df63aa6c02e 100644 --- a/Mage.Sets/src/mage/cards/f/FirkraagCunningInstigator.java +++ b/Mage.Sets/src/mage/cards/f/FirkraagCunningInstigator.java @@ -2,7 +2,7 @@ package mage.cards.f; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.AttacksPlayerWithCreaturesTriggeredAbility; import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.combat.GoadTargetEffect; @@ -14,7 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicate; import mage.game.Game; @@ -29,12 +29,11 @@ import java.util.UUID; */ public final class FirkraagCunningInstigator extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent(); - private static final FilterPermanent filterDragons = new FilterControlledCreaturePermanent("Dragons you control"); + private static final FilterPermanent filterHadToAttack = new FilterCreaturePermanent(); + private static final FilterPermanent filterDragons = new FilterControlledPermanent(SubType.DRAGON, "Dragons you control"); static { - filter.add(FirkraagCunningInstigatorPredicate.instance); - filterDragons.add(SubType.DRAGON.getPredicate()); + filterHadToAttack.add(FirkraagCunningInstigatorPredicate.instance); } public FirkraagCunningInstigator(UUID ownerId, CardSetInfo setInfo) { @@ -52,7 +51,7 @@ public final class FirkraagCunningInstigator extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Whenever one or more Dragons you control attack an opponent, goad target creature that player controls. - Ability abilityGoad = new AttacksWithCreaturesTriggeredAbility(Zone.BATTLEFIELD, new GoadTargetEffect(), 1, filterDragons, true); + Ability abilityGoad = new AttacksPlayerWithCreaturesTriggeredAbility(new GoadTargetEffect(), 1, filterDragons, SetTargetPointer.PLAYER, true); abilityGoad.addTarget(new TargetPermanent(new FilterCreaturePermanent("target creature that player controls"))); abilityGoad.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(abilityGoad); @@ -61,7 +60,7 @@ public final class FirkraagCunningInstigator extends CardImpl { Ability ability = new DealsDamageToAPlayerAllTriggeredAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance()) .setText("you put a +1/+1 counter on {this}"), - filter, false, SetTargetPointer.NONE, + filterHadToAttack, false, SetTargetPointer.NONE, true, false, TargetController.OPPONENT ).setTriggerPhrase("Whenever a creature deals combat damage to one of your opponents, " + "if that creature had to attack this combat, "); diff --git a/Mage.Sets/src/mage/cards/g/GornogTheRedReaper.java b/Mage.Sets/src/mage/cards/g/GornogTheRedReaper.java index 348a79f0797..93f24e52fe4 100644 --- a/Mage.Sets/src/mage/cards/g/GornogTheRedReaper.java +++ b/Mage.Sets/src/mage/cards/g/GornogTheRedReaper.java @@ -2,7 +2,7 @@ package mage.cards.g; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.AttacksPlayerWithCreaturesTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; @@ -17,9 +17,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.AttackingPredicate; -import mage.target.common.TargetCreaturePermanent; +import mage.target.TargetPermanent; import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -29,16 +30,17 @@ import java.util.UUID; */ public final class GornogTheRedReaper extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.WARRIOR, "Attacking Warriors"); - private static final FilterPermanent filter2 = new FilterPermanent(SubType.COWARD, "Cowards your opponents control"); + private static final FilterPermanent filterAttackWarrior = new FilterCreaturePermanent(SubType.WARRIOR, "Attacking Warriors"); + private static final FilterPermanent filterWarrior = new FilterControlledPermanent(SubType.WARRIOR, "Warriors you control"); + private static final FilterPermanent filterCoward = new FilterPermanent(SubType.COWARD, "Cowards your opponents control"); static { - filter.add(AttackingPredicate.instance); - filter2.add(TargetController.OPPONENT.getControllerPredicate()); + filterAttackWarrior.add(AttackingPredicate.instance); + filterCoward.add(TargetController.OPPONENT.getControllerPredicate()); } - private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter2, null); - private static final Hint hint = new ValueHint(filter2.getMessage(), xValue); + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filterCoward, null); + private static final Hint hint = new ValueHint(filterCoward.getMessage(), xValue); public GornogTheRedReaper(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); @@ -56,16 +58,16 @@ public final class GornogTheRedReaper extends CardImpl { this.addAbility(new SimpleStaticAbility(new CowardsCantBlockWarriorsEffect())); // Whenever one or more Warriors you control attack a player, target creature that player controls becomes a Coward. - Ability ability = new AttacksWithCreaturesTriggeredAbility(Zone.BATTLEFIELD, - new BecomesCreatureTypeTargetEffect(Duration.EndOfGame, SubType.COWARD), - 1, filter, true); - ability.addTarget(new TargetCreaturePermanent()); + Ability ability = new AttacksPlayerWithCreaturesTriggeredAbility( + new BecomesCreatureTypeTargetEffect(Duration.EndOfGame, SubType.COWARD).setText("target creature that player controls becomes a Coward"), + filterWarrior, SetTargetPointer.PLAYER); + ability.addTarget(new TargetPermanent()); ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); // Attacking Warriors you control get +X/+0, where X is the number of Cowards your opponents control. this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( - xValue, StaticValue.get(0), Duration.WhileOnBattlefield, filter, false + xValue, StaticValue.get(0), Duration.WhileOnBattlefield, filterAttackWarrior, false )).addHint(hint)); } diff --git a/Mage.Sets/src/mage/cards/h/HornOfTheMark.java b/Mage.Sets/src/mage/cards/h/HornOfTheMark.java index 5b171cc7533..b59d54fb0bf 100644 --- a/Mage.Sets/src/mage/cards/h/HornOfTheMark.java +++ b/Mage.Sets/src/mage/cards/h/HornOfTheMark.java @@ -1,18 +1,14 @@ package mage.cards.h; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksPlayerWithCreaturesTriggeredAbility; import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.PutCards; +import mage.constants.SetTargetPointer; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.game.Controllable; -import mage.game.Game; -import mage.game.events.DefenderAttackedEvent; -import mage.game.events.GameEvent; import java.util.UUID; @@ -27,7 +23,9 @@ public final class HornOfTheMark extends CardImpl { this.supertype.add(SuperType.LEGENDARY); // Whenever two or more creatures you control attack a player, look at the top five cards of your library. You may reveal a creature card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. - this.addAbility(new HornOfTheMarkTriggeredAbility()); + this.addAbility(new AttacksPlayerWithCreaturesTriggeredAbility( + new LookLibraryAndPickControllerEffect(5, 1, StaticFilters.FILTER_CARD_CREATURE_A, PutCards.HAND, PutCards.BOTTOM_RANDOM), + 2, StaticFilters.FILTER_CONTROLLED_CREATURES, SetTargetPointer.NONE, false)); } private HornOfTheMark(final HornOfTheMark card) { @@ -39,39 +37,3 @@ public final class HornOfTheMark extends CardImpl { return new HornOfTheMark(this); } } - -class HornOfTheMarkTriggeredAbility extends TriggeredAbilityImpl { - - HornOfTheMarkTriggeredAbility() { - super(Zone.BATTLEFIELD, new LookLibraryAndPickControllerEffect( - 5, 1, StaticFilters.FILTER_CARD_CREATURE_A, PutCards.HAND, PutCards.BOTTOM_RANDOM - )); - this.setTriggerPhrase("Whenever two or more creatures you control attack a player, "); - } - - private HornOfTheMarkTriggeredAbility(final HornOfTheMarkTriggeredAbility ability) { - super(ability); - } - - @Override - public HornOfTheMarkTriggeredAbility copy() { - return new HornOfTheMarkTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - DefenderAttackedEvent dEvent = ((DefenderAttackedEvent) event); - return game.getPlayer(dEvent.getTargetId()) != null - && dEvent - .getAttackers(game) - .stream() - .map(Controllable::getControllerId) - .filter(this::isControlledBy) - .count() >= 2; - } -} diff --git a/Mage.Sets/src/mage/cards/k/KarazikarTheEyeTyrant.java b/Mage.Sets/src/mage/cards/k/KarazikarTheEyeTyrant.java index 368a9b6c0df..87999eba723 100644 --- a/Mage.Sets/src/mage/cards/k/KarazikarTheEyeTyrant.java +++ b/Mage.Sets/src/mage/cards/k/KarazikarTheEyeTyrant.java @@ -1,22 +1,20 @@ package mage.cards.k; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksPlayerWithCreaturesTriggeredAbility; import mage.abilities.effects.common.*; import mage.abilities.effects.common.combat.GoadTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.players.Player; import mage.target.TargetPermanent; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import mage.target.targetpointer.FixedTarget; import java.util.Set; @@ -26,6 +24,7 @@ import java.util.UUID; * @author TheElk801 */ public final class KarazikarTheEyeTyrant extends CardImpl { + FilterPermanent filter = new FilterCreaturePermanent("creature that player controls"); public KarazikarTheEyeTyrant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{R}"); @@ -36,7 +35,11 @@ public final class KarazikarTheEyeTyrant extends CardImpl { this.toughness = new MageInt(5); // Whenever you attack a player, tap target creature that player controls and goad it. - this.addAbility(new KarazikarTheEyeTyrantFirstTriggeredAbility()); + Ability ability = new AttacksPlayerWithCreaturesTriggeredAbility(new TapTargetEffect(), SetTargetPointer.PLAYER); + ability.addEffect(new GoadTargetEffect().setText("goad it. " + GoadTargetEffect.goadReminderText).concatBy("and")); + ability.addTarget(new TargetPermanent(filter)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); + this.addAbility(ability); // Whenever an opponent attacks another one of your opponents, you and the attacking player each draw a card and lose 1 life. this.addAbility(new KarazikarTheEyeTyrantSecondTriggeredAbility()); @@ -52,49 +55,6 @@ public final class KarazikarTheEyeTyrant extends CardImpl { } } -class KarazikarTheEyeTyrantFirstTriggeredAbility extends TriggeredAbilityImpl { - - KarazikarTheEyeTyrantFirstTriggeredAbility() { - super(Zone.BATTLEFIELD, new TapTargetEffect(), false); - this.addEffect(new GoadTargetEffect()); - } - - private KarazikarTheEyeTyrantFirstTriggeredAbility(final KarazikarTheEyeTyrantFirstTriggeredAbility ability) { - super(ability); - } - - @Override - public KarazikarTheEyeTyrantFirstTriggeredAbility copy() { - return new KarazikarTheEyeTyrantFirstTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!isControlledBy(event.getPlayerId())) { - return false; - } - Player player = game.getPlayer(event.getTargetId()); - if (player == null) { - return false; - } - FilterPermanent filter = new FilterCreaturePermanent("creature controlled by " + player.getName()); - filter.add(new ControllerIdPredicate(player.getId())); - this.getTargets().clear(); - this.addTarget(new TargetPermanent(filter)); - return true; - } - - @Override - public String getRule() { - return "Whenever you attack a player, tap target creature that player controls and goad it."; - } -} - class KarazikarTheEyeTyrantSecondTriggeredAbility extends TriggeredAbilityImpl { KarazikarTheEyeTyrantSecondTriggeredAbility() { diff --git a/Mage.Sets/src/mage/cards/l/LandrovalHorizonWitness.java b/Mage.Sets/src/mage/cards/l/LandrovalHorizonWitness.java index 2766a625627..324ac56eca3 100644 --- a/Mage.Sets/src/mage/cards/l/LandrovalHorizonWitness.java +++ b/Mage.Sets/src/mage/cards/l/LandrovalHorizonWitness.java @@ -1,19 +1,18 @@ package mage.cards.l; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.AttacksPlayerWithCreaturesTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterAttackingCreature; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.game.Game; -import mage.game.events.DefenderAttackedEvent; -import mage.game.events.GameEvent; import mage.target.TargetPermanent; import java.util.UUID; @@ -23,6 +22,12 @@ import java.util.UUID; */ public final class LandrovalHorizonWitness extends CardImpl { + private static final FilterPermanent filter + = new FilterAttackingCreature("attacking creature without flying"); + + static { + filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); + } public LandrovalHorizonWitness(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); @@ -36,7 +41,11 @@ public final class LandrovalHorizonWitness extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever two or more creatures you control attack a player, target attacking creature without flying gains flying until end of turn. - this.addAbility(new LandrovalHorizonWitnessTriggeredAbility()); + Ability ability = new AttacksPlayerWithCreaturesTriggeredAbility( + new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), + 2, StaticFilters.FILTER_CONTROLLED_CREATURES, SetTargetPointer.NONE, false); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); } private LandrovalHorizonWitness(final LandrovalHorizonWitness card) { @@ -48,40 +57,3 @@ public final class LandrovalHorizonWitness extends CardImpl { return new LandrovalHorizonWitness(this); } } - -class LandrovalHorizonWitnessTriggeredAbility extends TriggeredAbilityImpl { - - private static final FilterPermanent filter - = new FilterAttackingCreature("attacking creature without flying"); - - static { - filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); - } - - LandrovalHorizonWitnessTriggeredAbility() { - super(Zone.BATTLEFIELD, new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn)); - this.addTarget(new TargetPermanent(filter)); - setTriggerPhrase("Whenever two or more creatures you control attack a player, "); - } - - private LandrovalHorizonWitnessTriggeredAbility(final LandrovalHorizonWitnessTriggeredAbility ability) { - super(ability); - } - - @Override - public LandrovalHorizonWitnessTriggeredAbility copy() { - return new LandrovalHorizonWitnessTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return isControlledBy(event.getPlayerId()) - && game.getPlayer(event.getTargetId()) != null - && ((DefenderAttackedEvent) event).getAttackers(game).size() >= 2; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MeriadocBrandybuck.java b/Mage.Sets/src/mage/cards/m/MeriadocBrandybuck.java index cde68efd66b..09c7d3c85cc 100644 --- a/Mage.Sets/src/mage/cards/m/MeriadocBrandybuck.java +++ b/Mage.Sets/src/mage/cards/m/MeriadocBrandybuck.java @@ -1,17 +1,16 @@ package mage.cards.m; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksPlayerWithCreaturesTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.DefenderAttackedEvent; -import mage.game.events.GameEvent; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.permanent.token.FoodToken; import java.util.UUID; @@ -20,6 +19,7 @@ import java.util.UUID; * @author TheElk801 */ public final class MeriadocBrandybuck extends CardImpl { + FilterPermanent filter = new FilterControlledPermanent(SubType.HALFLING,"Halflings you control"); public MeriadocBrandybuck(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); @@ -31,7 +31,7 @@ public final class MeriadocBrandybuck extends CardImpl { this.toughness = new MageInt(2); // Whenever one or more Halflings you control attack a player, create a Food token. - this.addAbility(new MeriadocBrandybuckTriggeredAbility()); + this.addAbility(new AttacksPlayerWithCreaturesTriggeredAbility(new CreateTokenEffect(new FoodToken()), filter, SetTargetPointer.NONE)); } private MeriadocBrandybuck(final MeriadocBrandybuck card) { @@ -43,35 +43,3 @@ public final class MeriadocBrandybuck extends CardImpl { return new MeriadocBrandybuck(this); } } - -class MeriadocBrandybuckTriggeredAbility extends TriggeredAbilityImpl { - - MeriadocBrandybuckTriggeredAbility() { - super(Zone.BATTLEFIELD, new CreateTokenEffect(new FoodToken())); - setTriggerPhrase("Whenever one or more Halflings you control attack a player, "); - } - - private MeriadocBrandybuckTriggeredAbility(final MeriadocBrandybuckTriggeredAbility ability) { - super(ability); - } - - @Override - public MeriadocBrandybuckTriggeredAbility copy() { - return new MeriadocBrandybuckTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return isControlledBy(event.getPlayerId()) - && game.getPlayer(event.getTargetId()) != null - && ((DefenderAttackedEvent) event) - .getAttackers(game) - .stream() - .anyMatch(permanent -> permanent.hasSubtype(SubType.HALFLING, game)); - } -} diff --git a/Mage.Sets/src/mage/cards/n/NeyaliSunsVanguard.java b/Mage.Sets/src/mage/cards/n/NeyaliSunsVanguard.java index 6955a000629..10e73a88ab8 100644 --- a/Mage.Sets/src/mage/cards/n/NeyaliSunsVanguard.java +++ b/Mage.Sets/src/mage/cards/n/NeyaliSunsVanguard.java @@ -2,7 +2,7 @@ package mage.cards.n; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksPlayerWithCreaturesTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -12,11 +12,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.AttackingPredicate; import mage.filter.predicate.permanent.TokenPredicate; -import mage.game.Controllable; import mage.game.Game; -import mage.game.events.DefenderAttackedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; @@ -33,11 +32,13 @@ import java.util.UUID; */ public final class NeyaliSunsVanguard extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("attacking tokens"); + private static final FilterPermanent filterAttacking = new FilterPermanent("attacking tokens"); + private static final FilterPermanent filterControlled = new FilterControlledPermanent("tokens you control"); static { - filter.add(AttackingPredicate.instance); - filter.add(TokenPredicate.TRUE); + filterAttacking.add(AttackingPredicate.instance); + filterAttacking.add(TokenPredicate.TRUE); + filterControlled.add(TokenPredicate.TRUE); } public NeyaliSunsVanguard(UUID ownerId, CardSetInfo setInfo) { @@ -51,11 +52,13 @@ public final class NeyaliSunsVanguard extends CardImpl { // Attacking tokens you control have double strike. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter + DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filterAttacking ))); // Whenever one or more tokens you control attack a player, exile the top card of your library. During any turn you attacked with a token, you may play that card. - this.addAbility(new NeyaliSunsVanguardTriggeredAbility()); + Ability ability = new AttacksPlayerWithCreaturesTriggeredAbility(new NeyaliSunsVanguardEffect(), filterControlled, SetTargetPointer.NONE); + ability.addWatcher(new NeyaliSunsVanguardWatcher()); + this.addAbility(ability); } private NeyaliSunsVanguard(final NeyaliSunsVanguard card) { @@ -68,49 +71,11 @@ public final class NeyaliSunsVanguard extends CardImpl { } } -class NeyaliSunsVanguardTriggeredAbility extends TriggeredAbilityImpl { - - NeyaliSunsVanguardTriggeredAbility() { - super(Zone.BATTLEFIELD, new NeyaliSunsVanguardEffect()); - this.addWatcher(new NeyaliSunsVanguardWatcher()); - } - - private NeyaliSunsVanguardTriggeredAbility(final NeyaliSunsVanguardTriggeredAbility ability) { - super(ability); - } - - @Override - public NeyaliSunsVanguardTriggeredAbility copy() { - return new NeyaliSunsVanguardTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return game.getPlayer(event.getTargetId()) != null - && ((DefenderAttackedEvent) event) - .getAttackers(game) - .stream() - .filter(PermanentToken.class::isInstance) - .map(Controllable::getControllerId) - .anyMatch(this::isControlledBy); - } - - @Override - public String getRule() { - return "Whenever one or more tokens you control attack a player, exile the top card of your library. " + - "During any turn you attacked with a token, you may play that card."; - } -} - class NeyaliSunsVanguardEffect extends OneShotEffect { NeyaliSunsVanguardEffect() { super(Outcome.Benefit); + this.setText("exile the top card of your library. During any turn you attacked with a token, you may play that card."); } private NeyaliSunsVanguardEffect(final NeyaliSunsVanguardEffect effect) { diff --git a/Mage.Sets/src/mage/cards/o/OrdruunMentor.java b/Mage.Sets/src/mage/cards/o/OrdruunMentor.java index a42c25b507a..6e6a8b680bd 100644 --- a/Mage.Sets/src/mage/cards/o/OrdruunMentor.java +++ b/Mage.Sets/src/mage/cards/o/OrdruunMentor.java @@ -1,21 +1,21 @@ package mage.cards.o; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.AttacksPlayerWithCreaturesTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.MentorAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.util.CardUtil; @@ -27,6 +27,12 @@ import java.util.UUID; */ public final class OrdruunMentor extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent("creature that's attacking that player"); + + static { + filter.add(OrdruunMentorPredicate.instance); + } + public OrdruunMentor(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R/W}"); @@ -39,7 +45,10 @@ public final class OrdruunMentor extends CardImpl { this.addAbility(new MentorAbility()); // Whenever you attack a player, target creature that's attacking that player gains first strike until end of turn. - this.addAbility(new OrdruunMentorTriggeredAbility()); + Ability ability = new AttacksPlayerWithCreaturesTriggeredAbility( + new GainAbilityTargetEffect(FirstStrikeAbility.getInstance()), SetTargetPointer.NONE); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); } private OrdruunMentor(final OrdruunMentor card) { @@ -51,54 +60,13 @@ public final class OrdruunMentor extends CardImpl { return new OrdruunMentor(this); } } - -class OrdruunMentorTriggeredAbility extends TriggeredAbilityImpl { - - private enum OrdruunMentorPredicate implements ObjectSourcePlayerPredicate { - instance; - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - return CardUtil.getEffectValueFromAbility( - input.getSource(), "playerAttacked", UUID.class - ) - .filter(uuid -> uuid.equals(game.getCombat().getDefenderId(input.getObject().getId()))) - .isPresent(); - } - } - - private static final FilterPermanent filter = new FilterCreaturePermanent("creature that's attacking that player"); - - static { - filter.add(OrdruunMentorPredicate.instance); - } - - OrdruunMentorTriggeredAbility() { - super(Zone.BATTLEFIELD, new GainAbilityTargetEffect(FirstStrikeAbility.getInstance()).setText("")); - this.setTriggerPhrase("Whenever you attack a player, "); - this.addTarget(new TargetPermanent(filter)); - } - - private OrdruunMentorTriggeredAbility(final OrdruunMentorTriggeredAbility ability) { - super(ability); - } +enum OrdruunMentorPredicate implements ObjectSourcePlayerPredicate { + instance; @Override - public OrdruunMentorTriggeredAbility copy() { - return new OrdruunMentorTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!isControlledBy(event.getPlayerId()) || game.getPlayer(event.getTargetId()) == null) { - return false; - } - this.getEffects().setValue("playerAttacked", event.getTargetId()); - return true; + public boolean apply(ObjectSourcePlayer input, Game game) { + return CardUtil.getEffectValueFromAbility(input.getSource(), "playerAttacked", UUID.class) + .filter(uuid -> uuid.equals(game.getCombat().getDefenderId(input.getObject().getId()))) + .isPresent(); } } diff --git a/Mage.Sets/src/mage/cards/s/SoaringLightbringer.java b/Mage.Sets/src/mage/cards/s/SoaringLightbringer.java index ffde788564e..388f6ca634b 100644 --- a/Mage.Sets/src/mage/cards/s/SoaringLightbringer.java +++ b/Mage.Sets/src/mage/cards/s/SoaringLightbringer.java @@ -2,7 +2,7 @@ package mage.cards.s; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksPlayerWithCreaturesTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -13,9 +13,7 @@ import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.token.GlimmerToken; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -47,7 +45,7 @@ public final class SoaringLightbringer extends CardImpl { ))); // Whenever you attack a player, create a 1/1 white Glimmer enchantment creature token that's tapped and attacking that player. - this.addAbility(new SoaringLightbringerTriggeredAbility()); + this.addAbility(new AttacksPlayerWithCreaturesTriggeredAbility(new SoaringLightbringerEffect(), SetTargetPointer.PLAYER)); } private SoaringLightbringer(final SoaringLightbringer card) { @@ -60,37 +58,6 @@ public final class SoaringLightbringer extends CardImpl { } } -class SoaringLightbringerTriggeredAbility extends TriggeredAbilityImpl { - - SoaringLightbringerTriggeredAbility() { - super(Zone.BATTLEFIELD, new SoaringLightbringerEffect()); - setTriggerPhrase("Whenever you attack a player, "); - } - - private SoaringLightbringerTriggeredAbility(final SoaringLightbringerTriggeredAbility ability) { - super(ability); - } - - @Override - public SoaringLightbringerTriggeredAbility copy() { - return new SoaringLightbringerTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!isControlledBy(event.getPlayerId()) || game.getPlayer(event.getTargetId()) == null) { - return false; - } - this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); - return true; - } -} - class SoaringLightbringerEffect extends OneShotEffect { SoaringLightbringerEffect() { diff --git a/Mage.Sets/src/mage/sets/BloomburrowCommander.java b/Mage.Sets/src/mage/sets/BloomburrowCommander.java index 854ec0853ea..cf26ee2d650 100644 --- a/Mage.Sets/src/mage/sets/BloomburrowCommander.java +++ b/Mage.Sets/src/mage/sets/BloomburrowCommander.java @@ -93,6 +93,7 @@ public final class BloomburrowCommander extends ExpansionSet { cards.add(new SetCardInfo("Devilish Valet", 195, Rarity.RARE, mage.cards.d.DevilishValet.class)); cards.add(new SetCardInfo("Domri, Anarch of Bolas", 98, Rarity.RARE, mage.cards.d.DomriAnarchOfBolas.class)); cards.add(new SetCardInfo("Dusk // Dawn", 138, Rarity.RARE, mage.cards.d.DuskDawn.class)); + cards.add(new SetCardInfo("Echoing Assault", 24, Rarity.RARE, mage.cards.e.EchoingAssault.class)); cards.add(new SetCardInfo("Elspeth, Sun's Champion", 97, Rarity.MYTHIC, mage.cards.e.ElspethSunsChampion.class)); cards.add(new SetCardInfo("End-Raze Forerunners", 214, Rarity.RARE, mage.cards.e.EndRazeForerunners.class)); cards.add(new SetCardInfo("Esika's Chariot", 215, Rarity.RARE, mage.cards.e.EsikasChariot.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dsc/SoaringLightbringerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dsc/SoaringLightbringerTest.java new file mode 100644 index 00000000000..e7639577d15 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dsc/SoaringLightbringerTest.java @@ -0,0 +1,86 @@ +package org.mage.test.cards.single.dsc; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +/** + * @author notgreat + */ +public class SoaringLightbringerTest extends CardTestCommander4Players { + + @Test + public void test_AttacksDoubled() { + addCard(Zone.BATTLEFIELD, playerA, "Soaring Lightbringer"); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + attack(1, playerA, "Soaring Lightbringer", playerB); + attack(1, playerA, "Memnite", playerB); + + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertTappedCount("Glimmer Token", true, 1); + assertLife(playerB, 20 - 4 - 1 - 1); + } + + @Test + public void test_AttacksTwo() { + addCard(Zone.BATTLEFIELD, playerA, "Soaring Lightbringer"); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + attack(1, playerA, "Soaring Lightbringer", playerB); + attack(1, playerA, "Memnite", playerD); + + setChoice(playerA, "Whenever"); // Order triggers + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertTappedCount("Glimmer Token", true, 2); + assertLife(playerB, 20 - 4 - 1); + assertLife(playerD, 20 - 1 - 1); + } + + @Test + public void test_AttacksPlaneswalker() { + addCard(Zone.BATTLEFIELD, playerD, "Soaring Lightbringer"); + addCard(Zone.BATTLEFIELD, playerD, "Memnite"); + addCard(Zone.HAND, playerA, "Nissa Revane"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nissa Revane"); + attack(2, playerD, "Memnite", "Nissa Revane"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_COMBAT); + execute(); + + assertPermanentCount(playerD, "Glimmer Token", 0); + assertCounterCount("Nissa Revane", CounterType.LOYALTY, 1); + } + + @Test + public void test_AttacksEnters() { + addCard(Zone.BATTLEFIELD, playerA, "Soaring Lightbringer"); + addCard(Zone.BATTLEFIELD, playerA, "Falconer Adept"); + + attack(1, playerA, "Falconer Adept", playerB); + setChoice(playerA, "Whenever"); // Order triggers + addTarget(playerA, playerC); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertTappedCount("Glimmer Token", true, 1); + assertTappedCount("Bird Token", true, 1); + assertLife(playerB, 20 - 2 - 1); + assertLife(playerC, 20 - 1); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/AttacksPlayerWithCreaturesTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksPlayerWithCreaturesTriggeredAbility.java new file mode 100644 index 00000000000..1264c40f4cd --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/AttacksPlayerWithCreaturesTriggeredAbility.java @@ -0,0 +1,107 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.DefenderAttackedEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * based heavily on AttacksWithCreaturesTriggeredAbility + * @author notgreat + */ +public class AttacksPlayerWithCreaturesTriggeredAbility extends TriggeredAbilityImpl { + private final FilterPermanent filter; + private final int minAttackers; + private final boolean onlyOpponents; + private final SetTargetPointer setTargetPointer; + + public AttacksPlayerWithCreaturesTriggeredAbility(Effect effect, SetTargetPointer setTargetPointer) { + this(effect, StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED, setTargetPointer); + } + + public AttacksPlayerWithCreaturesTriggeredAbility(Effect effect, FilterPermanent filter, SetTargetPointer setTargetPointer) { + this(effect, 1, filter, setTargetPointer, false); + } + + public AttacksPlayerWithCreaturesTriggeredAbility(Effect effect, int minAttackers, FilterPermanent filter, SetTargetPointer setTargetPointer, boolean onlyOpponents) { + this(Zone.BATTLEFIELD, effect, minAttackers, filter, setTargetPointer, onlyOpponents, false); + } + + public AttacksPlayerWithCreaturesTriggeredAbility(Zone zone, Effect effect, int minAttackers, FilterPermanent filter, SetTargetPointer setTargetPointer, boolean onlyOpponents, boolean optional) { + super(zone, effect, optional); + this.filter = filter; + this.minAttackers = minAttackers; + this.onlyOpponents = onlyOpponents; + this.setTargetPointer = setTargetPointer; + if (minAttackers == 1 && StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED.equals(filter) && !onlyOpponents) { + setTriggerPhrase("Whenever you attack a player, "); + } else { + setTriggerPhrase("Whenever " + CardUtil.numberToText(minAttackers) + " or more " + filter.getMessage() + + " attack " + (onlyOpponents ? "an opponent" : "a player") + ", "); + } + } + + protected AttacksPlayerWithCreaturesTriggeredAbility(final AttacksPlayerWithCreaturesTriggeredAbility ability) { + super(ability); + this.filter = ability.filter; + this.minAttackers = ability.minAttackers; + this.onlyOpponents = ability.onlyOpponents; + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public AttacksPlayerWithCreaturesTriggeredAbility copy() { + return new AttacksPlayerWithCreaturesTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Player player = game.getPlayer(getControllerId()); + UUID attackedId = event.getTargetId(); + if (player == null || game.getPlayer(attackedId) == null) { + return false; + } + DefenderAttackedEvent attackedEvent = (DefenderAttackedEvent) event; + List attackers = attackedEvent.getAttackers(game).stream() + .filter(permanent -> filter.match(permanent, controllerId, this, game)) + .collect(Collectors.toList()); + if (attackers.size() < minAttackers || (onlyOpponents && !game.isOpponent(player, attackedId))) { + return false; + } + switch (setTargetPointer){ + case NONE: + break; + case PLAYER: + getEffects().setTargetPointer(new FixedTarget(attackedId)); + break; + case PERMANENT: + getEffects().setTargetPointer(new FixedTargets(new ArrayList<>(attackers), game)); + break; + default: + throw new UnsupportedOperationException("Unexpected setTargetPointer in AttacksPlayerWithCreaturesTriggeredAbility: " + setTargetPointer); + + } + this.getEffects().setValue("playerAttacked",attackedId); + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java index 4fe12b5667b..9c2619323cc 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/combat/GoadTargetEffect.java @@ -25,6 +25,7 @@ public class GoadTargetEffect extends ContinuousEffectImpl { * turn of the controller of that spell or ability, that creature attacks * each combat if able and attacks a player other than that player if able. */ + public static String goadReminderText = "(Until your next turn, that creature attacks each combat if able and attacks a player other than you if able.)"; public GoadTargetEffect() { this(Duration.UntilYourNextTurn); } @@ -74,6 +75,6 @@ public class GoadTargetEffect extends ContinuousEffectImpl { return staticText; } return "goad " + getTargetPointer().describeTargets(mode.getTargets(), "that creature") - + ". (Until your next turn, that creature attacks each combat if able and attacks a player other than you if able.)"; + + ". "+goadReminderText; } }