diff --git a/Mage.Sets/src/mage/cards/a/AbyssalNightstalker.java b/Mage.Sets/src/mage/cards/a/AbyssalNightstalker.java index 88a74ed2111..8c9cdcf5cbc 100644 --- a/Mage.Sets/src/mage/cards/a/AbyssalNightstalker.java +++ b/Mage.Sets/src/mage/cards/a/AbyssalNightstalker.java @@ -1,7 +1,6 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; import mage.abilities.effects.Effect; @@ -9,16 +8,18 @@ import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LoneFox */ public final class AbyssalNightstalker extends CardImpl { public AbyssalNightstalker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.NIGHTSTALKER); this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -26,7 +27,7 @@ public final class AbyssalNightstalker extends CardImpl { // Whenever Abyssal Nightstalker attacks and isn't blocked, defending player discards a card. Effect effect = new DiscardTargetEffect(1); effect.setText("defending player discards a card"); - this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(effect, false, true)); + this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(effect, false, SetTargetPointer.PLAYER)); } private AbyssalNightstalker(final AbyssalNightstalker card) { diff --git a/Mage.Sets/src/mage/cards/b/BoneDancer.java b/Mage.Sets/src/mage/cards/b/BoneDancer.java index 2b4b62846f5..40a15b543f7 100644 --- a/Mage.Sets/src/mage/cards/b/BoneDancer.java +++ b/Mage.Sets/src/mage/cards/b/BoneDancer.java @@ -1,7 +1,6 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; @@ -14,20 +13,21 @@ import mage.constants.*; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author LoneFox */ public final class BoneDancer extends CardImpl { public BoneDancer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.subtype.add(SubType.ZOMBIE); this.power = new MageInt(2); this.toughness = new MageInt(2); // Whenever Bone Dancer attacks and isn't blocked, you may put the top creature card of defending player's graveyard onto the battlefield under your control. If you do, Bone Dancer assigns no combat damage this turn. - Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(new BoneDancerEffect(), true, true); + Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(new BoneDancerEffect(), true, SetTargetPointer.PLAYER); ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CoralFighters.java b/Mage.Sets/src/mage/cards/c/CoralFighters.java index f4e3713a23d..382787f6883 100644 --- a/Mage.Sets/src/mage/cards/c/CoralFighters.java +++ b/Mage.Sets/src/mage/cards/c/CoralFighters.java @@ -1,34 +1,31 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.*; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * * @author LoneFox */ public final class CoralFighters extends CardImpl { public CoralFighters(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.MERFOLK); this.subtype.add(SubType.SOLDIER); this.power = new MageInt(1); this.toughness = new MageInt(1); // Whenever Coral Fighters attacks and isn't blocked, look at the top card of defending player's library. You may put that card on the bottom of that player's library. - this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(new CoralFightersEffect(), false, true)); + this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(new CoralFightersEffect(), false, SetTargetPointer.PLAYER)); } private CoralFighters(final CoralFighters card) { @@ -61,15 +58,14 @@ class CoralFightersEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player defendingPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); - if(controller != null && defendingPlayer != null) { + if (controller != null && defendingPlayer != null) { Card card = defendingPlayer.getLibrary().getFromTop(game); - if(card != null) { + if (card != null) { Cards cards = new CardsImpl(card); controller.lookAtCards("Coral Fighters", cards, game); if (controller.chooseUse(outcome, "Put that card on the bottom of its owner's library?", source, game)) { controller.moveCardToLibraryWithInfo(card, source, game, Zone.LIBRARY, false, false); - } - else { + } else { game.informPlayers(controller.getLogName() + " puts the card back on top of the library."); } return true; diff --git a/Mage.Sets/src/mage/cards/c/CryptCobra.java b/Mage.Sets/src/mage/cards/c/CryptCobra.java index e216fa457d5..0f309f05437 100644 --- a/Mage.Sets/src/mage/cards/c/CryptCobra.java +++ b/Mage.Sets/src/mage/cards/c/CryptCobra.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; import mage.abilities.effects.Effect; @@ -9,16 +8,18 @@ import mage.abilities.effects.common.counter.AddPoisonCounterTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LoneFox */ public final class CryptCobra extends CardImpl { public CryptCobra(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.SNAKE); this.power = new MageInt(3); this.toughness = new MageInt(3); @@ -26,7 +27,7 @@ public final class CryptCobra extends CardImpl { // Whenever Crypt Cobra attacks and isn't blocked, defending player gets a poison counter. Effect effect = new AddPoisonCounterTargetEffect(1); effect.setText("defending player gets a poison counter"); - this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(effect, false, true)); + this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(effect, false, SetTargetPointer.PLAYER)); } private CryptCobra(final CryptCobra card) { diff --git a/Mage.Sets/src/mage/cards/d/DauthiMindripper.java b/Mage.Sets/src/mage/cards/d/DauthiMindripper.java index 945f2acf7e6..051354c218f 100644 --- a/Mage.Sets/src/mage/cards/d/DauthiMindripper.java +++ b/Mage.Sets/src/mage/cards/d/DauthiMindripper.java @@ -1,7 +1,6 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -12,16 +11,18 @@ import mage.abilities.keyword.ShadowAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LoneFox */ public final class DauthiMindripper extends CardImpl { public DauthiMindripper(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.DAUTHI); this.subtype.add(SubType.MINION); this.power = new MageInt(2); @@ -32,7 +33,10 @@ public final class DauthiMindripper extends CardImpl { // Whenever Dauthi Mindripper attacks and isn't blocked, you may sacrifice it. If you do, defending player discards three cards. Effect effect = new DiscardTargetEffect(3); effect.setText("defending player discards three cards"); - this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(new DoIfCostPaid(effect, new SacrificeSourceCost()), false, true)); + this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility( + new DoIfCostPaid(effect, new SacrificeSourceCost()), + false, SetTargetPointer.PLAYER + )); } private DauthiMindripper(final DauthiMindripper card) { diff --git a/Mage.Sets/src/mage/cards/g/Guiltfeeder.java b/Mage.Sets/src/mage/cards/g/Guiltfeeder.java index 17a821200a8..10e84008a87 100644 --- a/Mage.Sets/src/mage/cards/g/Guiltfeeder.java +++ b/Mage.Sets/src/mage/cards/g/Guiltfeeder.java @@ -1,7 +1,6 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; import mage.abilities.dynamicvalue.common.CardsInTargetPlayersGraveyardCount; @@ -11,16 +10,18 @@ import mage.abilities.keyword.FearAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LoneFox */ public final class Guiltfeeder extends CardImpl { public Guiltfeeder(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.HORROR); this.power = new MageInt(0); this.toughness = new MageInt(4); @@ -30,7 +31,7 @@ public final class Guiltfeeder extends CardImpl { // Whenever Guiltfeeder attacks and isn't blocked, defending player loses 1 life for each card in their graveyard. Effect effect = new LoseLifeTargetEffect(new CardsInTargetPlayersGraveyardCount()); effect.setText("defending player loses 1 life for each card in their graveyard"); - this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(effect, false, true)); + this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(effect, false, SetTargetPointer.PLAYER)); } private Guiltfeeder(final Guiltfeeder card) { diff --git a/Mage.Sets/src/mage/cards/k/KeeperOfTresserhorn.java b/Mage.Sets/src/mage/cards/k/KeeperOfTresserhorn.java index f8f465aa27b..223748cacf2 100644 --- a/Mage.Sets/src/mage/cards/k/KeeperOfTresserhorn.java +++ b/Mage.Sets/src/mage/cards/k/KeeperOfTresserhorn.java @@ -1,7 +1,6 @@ package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; @@ -11,17 +10,19 @@ import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author LoneFox */ public final class KeeperOfTresserhorn extends CardImpl { public KeeperOfTresserhorn(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}"); this.subtype.add(SubType.AVATAR); this.power = new MageInt(6); this.toughness = new MageInt(6); @@ -29,7 +30,7 @@ public final class KeeperOfTresserhorn extends CardImpl { // Whenever Keeper of Tresserhorn attacks and isn't blocked, it assigns no combat damage this turn and defending player loses 2 life. Effect effect = new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn); effect.setText("it assigns no combat damage this turn"); - Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(effect, false, true); + Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(effect, false, SetTargetPointer.PLAYER); effect = new LoseLifeTargetEffect(2); effect.setText("and defending player loses 2 life"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java b/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java index bdc6b9bdb38..23a39d23889 100644 --- a/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java +++ b/Mage.Sets/src/mage/cards/l/LimDulsPaladin.java @@ -1,7 +1,6 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; @@ -17,23 +16,20 @@ import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCardInHand; +import java.util.UUID; + /** - * * @author LoneFox */ public final class LimDulsPaladin extends CardImpl { public LimDulsPaladin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.KNIGHT); this.power = new MageInt(0); @@ -48,7 +44,7 @@ public final class LimDulsPaladin extends CardImpl { // Whenever Lim-Dul's Paladin attacks and isn't blocked, it assigns no combat damage to defending player this turn and that player loses 4 life. Effect effect = new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn); effect.setText("it assigns no combat damage this turn"); - Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(effect, false, true); + Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(effect, false, SetTargetPointer.PLAYER); effect = new LoseLifeTargetEffect(4); effect.setText("and defending player loses 4 life"); ability.addEffect(effect); @@ -84,11 +80,11 @@ class LimDulsPaladinEffect extends SacrificeSourceUnlessPaysEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); - if(permanent != null) { + if (permanent != null) { super.apply(game, source); // Not in play anymore -> was sacrificed, draw a card - if(game.getPermanent(source.getSourceId()) == null) { - return new DrawCardSourceControllerEffect(1).apply(game, source); + if (game.getPermanent(source.getSourceId()) == null) { + return new DrawCardSourceControllerEffect(1).apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/m/MindstabThrull.java b/Mage.Sets/src/mage/cards/m/MindstabThrull.java index aa4d9473a51..b3160149487 100644 --- a/Mage.Sets/src/mage/cards/m/MindstabThrull.java +++ b/Mage.Sets/src/mage/cards/m/MindstabThrull.java @@ -1,7 +1,6 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -11,16 +10,18 @@ import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LoneFox */ public final class MindstabThrull extends CardImpl { public MindstabThrull(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.subtype.add(SubType.THRULL); this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -28,7 +29,9 @@ public final class MindstabThrull extends CardImpl { // Whenever Mindstab Thrull attacks and isn't blocked, you may sacrifice it. If you do, defending player discards three cards. Effect effect = new DiscardTargetEffect(3); effect.setText("defending player discards three cards"); - this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(new DoIfCostPaid(effect, new SacrificeSourceCost()), false, true)); + this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility( + new DoIfCostPaid(effect, new SacrificeSourceCost()), false, SetTargetPointer.PLAYER + )); } private MindstabThrull(final MindstabThrull card) { diff --git a/Mage.Sets/src/mage/cards/p/PygmyHippo.java b/Mage.Sets/src/mage/cards/p/PygmyHippo.java index 88a9a2784d3..bfd4ef79d05 100644 --- a/Mage.Sets/src/mage/cards/p/PygmyHippo.java +++ b/Mage.Sets/src/mage/cards/p/PygmyHippo.java @@ -25,13 +25,13 @@ public final class PygmyHippo extends CardImpl { public PygmyHippo(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}"); - + this.subtype.add(SubType.HIPPO); this.power = new MageInt(2); this.toughness = new MageInt(2); // Whenever Pygmy Hippo attacks and isn't blocked, you may have defending player activate a mana ability of each land they control and lose all unspent mana. If you do, Pygmy Hippo assigns no combat damage this turn and at the beginning of your next main phase this turn, you add an amount of {C} equal to the amount of mana that player lost this way. - this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(new PygmyHippoEffect(), true, true)); + this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(new PygmyHippoEffect(), true, SetTargetPointer.PLAYER)); } private PygmyHippo(final PygmyHippo card) { @@ -77,11 +77,11 @@ class PygmyHippoEffect extends OneShotEffect { game.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true), source); if (amountToAdd > 0) { - DelayedTriggeredAbility delayed = new AtTheBeginOfMainPhaseDelayedTriggeredAbility( - new AddManaToManaPoolSourceControllerEffect(new Mana(ManaType.COLORLESS, amountToAdd)), - false, TargetController.YOU, - AtTheBeginOfMainPhaseDelayedTriggeredAbility.PhaseSelection.NEXT_MAIN_THIS_TURN); - game.addDelayedTriggeredAbility(delayed, source); + DelayedTriggeredAbility delayed = new AtTheBeginOfMainPhaseDelayedTriggeredAbility( + new AddManaToManaPoolSourceControllerEffect(new Mana(ManaType.COLORLESS, amountToAdd)), + false, TargetController.YOU, + AtTheBeginOfMainPhaseDelayedTriggeredAbility.PhaseSelection.NEXT_MAIN_THIS_TURN); + game.addDelayedTriggeredAbility(delayed, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/SenuKeenEyedProtector.java b/Mage.Sets/src/mage/cards/s/SenuKeenEyedProtector.java new file mode 100644 index 00000000000..9d2dde61317 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SenuKeenEyedProtector.java @@ -0,0 +1,117 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAndIsNotBlockedAllTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.common.SourceInExileCondition; +import mage.abilities.costs.common.ExileSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class SenuKeenEyedProtector extends CardImpl { + + private static final FilterPermanent filter = + new FilterControlledCreaturePermanent("a legendary creature you control"); + + static { + filter.add(SuperType.LEGENDARY.getPredicate()); + } + + public SenuKeenEyedProtector(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // {T}, Exile Senu, Keen-Eyed Protector: You gain 2 life and scry 2. + Ability ability = new SimpleActivatedAbility( + new GainLifeEffect(2), + new TapSourceCost() + ); + ability.addCost(new ExileSourceCost()); + ability.addEffect(new ScryEffect(2).concatBy("and")); + this.addAbility(ability); + + // When a legendary creature you control attacks and isn't blocked, if Senu is exiled, put it onto the battlefield attacking. + this.addAbility( + new ConditionalInterveningIfTriggeredAbility( + new AttacksAndIsNotBlockedAllTriggeredAbility( + Zone.EXILED, new SenuKeenEyedProtectorEffect(), filter + ), SourceInExileCondition.instance, + "When a legendary creature you control attacks and isn't blocked, " + + "if {this} is exiled, put it onto the battlefield attacking" + ) + ); + } + + private SenuKeenEyedProtector(final SenuKeenEyedProtector card) { + super(card); + } + + @Override + public SenuKeenEyedProtector copy() { + return new SenuKeenEyedProtector(this); + } +} + +class SenuKeenEyedProtectorEffect extends OneShotEffect { + + SenuKeenEyedProtectorEffect() { + super(Outcome.PutCreatureInPlay); + staticText = "put it onto the battlefield attacking"; + } + + private SenuKeenEyedProtectorEffect(final SenuKeenEyedProtectorEffect effect) { + super(effect); + } + + @Override + public SenuKeenEyedProtectorEffect copy() { + return new SenuKeenEyedProtectorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = source.getSourceCardIfItStillExists(game); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent creature = game.getPermanent(card.getId()); + if (creature != null) { + game.getCombat().addAttackingCreature(creature.getId(), game); + } + return true; + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java b/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java index 62e8b552c20..4271f27742a 100644 --- a/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java +++ b/Mage.Sets/src/mage/cards/s/StinkdrinkerBandit.java @@ -1,20 +1,14 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksAndIsNotBlockedAllTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.ProwlAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.permanent.Permanent; -import mage.target.targetpointer.FixedTarget; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import java.util.UUID; @@ -23,6 +17,8 @@ import java.util.UUID; */ public final class StinkdrinkerBandit extends CardImpl { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.ROGUE, "a Rogue you control"); + public StinkdrinkerBandit(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.GOBLIN); @@ -35,7 +31,11 @@ public final class StinkdrinkerBandit extends CardImpl { this.addAbility(new ProwlAbility("{1}{B}")); // Whenever a Rogue you control attacks and isn't blocked, it gets +2/+1 until end of turn. - this.addAbility(new StinkdrinkerBanditTriggeredAbility()); + this.addAbility(new AttacksAndIsNotBlockedAllTriggeredAbility( + Zone.BATTLEFIELD, + new BoostTargetEffect(2, 1, Duration.EndOfTurn), + filter, false, SetTargetPointer.PERMANENT + )); } private StinkdrinkerBandit(final StinkdrinkerBandit card) { @@ -46,42 +46,4 @@ public final class StinkdrinkerBandit extends CardImpl { public StinkdrinkerBandit copy() { return new StinkdrinkerBandit(this); } -} - -class StinkdrinkerBanditTriggeredAbility extends TriggeredAbilityImpl { - - StinkdrinkerBanditTriggeredAbility() { - super(Zone.BATTLEFIELD, new BoostTargetEffect(2, 1, Duration.EndOfTurn)); - } - - private StinkdrinkerBanditTriggeredAbility(final StinkdrinkerBanditTriggeredAbility ability) { - super(ability); - } - - @Override - public StinkdrinkerBanditTriggeredAbility copy() { - return new StinkdrinkerBanditTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == EventType.UNBLOCKED_ATTACKER; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null - && isControlledBy(permanent.getControllerId()) - && permanent.hasSubtype(SubType.ROGUE, game)) { - getEffects().setTargetPointer(new FixedTarget(permanent, game)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever a Rogue you control attacks and isn't blocked, it gets +2/+1 until end of turn."; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/StromgaldSpy.java b/Mage.Sets/src/mage/cards/s/StromgaldSpy.java index 803bb09f6be..a728c912b66 100644 --- a/Mage.Sets/src/mage/cards/s/StromgaldSpy.java +++ b/Mage.Sets/src/mage/cards/s/StromgaldSpy.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; @@ -9,21 +8,16 @@ import mage.abilities.condition.common.SourceRemainsInZoneCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continuous.AssignNoCombatDamageSourceEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author L_J */ public final class StromgaldSpy extends CardImpl { @@ -39,10 +33,10 @@ public final class StromgaldSpy extends CardImpl { // for as long as Stromgald Spy remains on the battlefield. If you do, Stromgald Spy assigns no combat damage this turn. Ability ability = new AttacksAndIsNotBlockedTriggeredAbility( new ConditionalContinuousEffect( - new StromgaldSpyEffect(), - new SourceRemainsInZoneCondition(Zone.BATTLEFIELD), - "you may have defending player play with their hand revealed for as long as {this} remains on the battlefield"), - true, true); + new StromgaldSpyEffect(), + new SourceRemainsInZoneCondition(Zone.BATTLEFIELD), + "you may have defending player play with their hand revealed for as long as {this} remains on the battlefield"), + true, SetTargetPointer.PLAYER); ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SuqAtaAssassin.java b/Mage.Sets/src/mage/cards/s/SuqAtaAssassin.java index b777e66ddc6..106b20fda68 100644 --- a/Mage.Sets/src/mage/cards/s/SuqAtaAssassin.java +++ b/Mage.Sets/src/mage/cards/s/SuqAtaAssassin.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; import mage.abilities.effects.Effect; @@ -10,16 +9,18 @@ import mage.abilities.keyword.FearAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LoneFox */ public final class SuqAtaAssassin extends CardImpl { public SuqAtaAssassin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ASSASSIN); this.power = new MageInt(1); @@ -30,7 +31,7 @@ public final class SuqAtaAssassin extends CardImpl { // Whenever Suq'Ata Assassin attacks and isn't blocked, defending player gets a poison counter. Effect effect = new AddPoisonCounterTargetEffect(1); effect.setText("defending player gets a poison counter"); - this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(effect, false, true)); + this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(effect, false, SetTargetPointer.PLAYER)); } private SuqAtaAssassin(final SuqAtaAssassin card) { diff --git a/Mage.Sets/src/mage/cards/s/SwampMosquito.java b/Mage.Sets/src/mage/cards/s/SwampMosquito.java index ba3fb19e029..60d661ace91 100644 --- a/Mage.Sets/src/mage/cards/s/SwampMosquito.java +++ b/Mage.Sets/src/mage/cards/s/SwampMosquito.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; import mage.abilities.effects.Effect; @@ -10,16 +9,18 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LoneFox */ public final class SwampMosquito extends CardImpl { public SwampMosquito(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.INSECT); this.power = new MageInt(0); this.toughness = new MageInt(1); @@ -29,7 +30,7 @@ public final class SwampMosquito extends CardImpl { // Whenever Swamp Mosquito attacks and isn't blocked, defending player gets a poison counter. Effect effect = new AddPoisonCounterTargetEffect(1); effect.setText("defending player gets a poison counter"); - this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(effect, false, true)); + this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(effect, false, SetTargetPointer.PLAYER)); } private SwampMosquito(final SwampMosquito card) { diff --git a/Mage.Sets/src/mage/cards/w/WildfireEternal.java b/Mage.Sets/src/mage/cards/w/WildfireEternal.java index ba47cf24e72..13c44a60dcc 100644 --- a/Mage.Sets/src/mage/cards/w/WildfireEternal.java +++ b/Mage.Sets/src/mage/cards/w/WildfireEternal.java @@ -7,6 +7,7 @@ import mage.abilities.keyword.AfflictAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; @@ -34,7 +35,7 @@ public final class WildfireEternal extends CardImpl { // Whenever Wildfire Eternal attacks and isn't blocked, you may cast an instant or sorcery card from your hand without paying its mana cost. this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility( - new CastFromHandForFreeEffect(filter), false, true + new CastFromHandForFreeEffect(filter), false, SetTargetPointer.PLAYER )); } diff --git a/Mage.Sets/src/mage/sets/AssassinsCreed.java b/Mage.Sets/src/mage/sets/AssassinsCreed.java index 375e2a591d7..3ee28b16bc8 100644 --- a/Mage.Sets/src/mage/sets/AssassinsCreed.java +++ b/Mage.Sets/src/mage/sets/AssassinsCreed.java @@ -92,6 +92,7 @@ public final class AssassinsCreed extends ExpansionSet { cards.add(new SetCardInfo("Restart Sequence", 30, Rarity.UNCOMMON, mage.cards.r.RestartSequence.class)); cards.add(new SetCardInfo("Rooftop Bypass", 298, Rarity.RARE, mage.cards.r.RooftopBypass.class)); cards.add(new SetCardInfo("Royal Assassin", 93, Rarity.RARE, mage.cards.r.RoyalAssassin.class)); + cards.add(new SetCardInfo("Senu, Keen-Eyed Protector", 8, Rarity.RARE, mage.cards.s.SenuKeenEyedProtector.class)); cards.add(new SetCardInfo("Settlement Blacksmith", 280, Rarity.UNCOMMON, mage.cards.s.SettlementBlacksmith.class)); cards.add(new SetCardInfo("Shao Jun", 63, Rarity.UNCOMMON, mage.cards.s.ShaoJun.class)); cards.add(new SetCardInfo("Sigurd, Jarl of Ravensthorpe", 66, Rarity.RARE, mage.cards.s.SigurdJarlOfRavensthorpe.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/acr/SenuKeenEyedProtectorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/acr/SenuKeenEyedProtectorTest.java new file mode 100644 index 00000000000..862a4d57e72 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/acr/SenuKeenEyedProtectorTest.java @@ -0,0 +1,103 @@ +package org.mage.test.cards.single.acr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class SenuKeenEyedProtectorTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.SenuKeenEyedProtector Senu, Keen-Eyed Protector} {1}{W} + * Legendary Creature — Bird Scout + * Flying, vigilance + * {T}, Exile Senu, Keen-Eyed Protector: You gain 2 life and scry 2. + * When a legendary creature you control attacks and isn’t blocked, if Senu is exiled, put it onto the battlefield attacking. + * 2/1 + */ + private static final String senu = "Senu, Keen-Eyed Protector"; + + @Test + public void test_Trigger() { + setStrictChooseMode(true); + + addCard(Zone.EXILED, playerA, senu); + addCard(Zone.BATTLEFIELD, playerA, "Isamaru, Hound of Konda"); + + attack(1, playerA, "Isamaru, Hound of Konda"); + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerB, 20 - 2 - 2); + assertPermanentCount(playerA, senu, 1); + } + + @Test + public void test_NoTrigger_FromGraveyard() { + setStrictChooseMode(true); + + addCard(Zone.GRAVEYARD, playerA, senu); + addCard(Zone.BATTLEFIELD, playerA, "Isamaru, Hound of Konda"); + + attack(1, playerA, "Isamaru, Hound of Konda"); + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerB, 20 - 2); + assertPermanentCount(playerA, senu, 0); + } + + @Test + public void test_NoTrigger_FromHand() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, senu); + addCard(Zone.BATTLEFIELD, playerA, "Isamaru, Hound of Konda"); + + attack(1, playerA, "Isamaru, Hound of Konda"); + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerB, 20 - 2); + assertPermanentCount(playerA, senu, 0); + } + + @Test + public void test_NoTrigger_NonLegendaryAttack() { + setStrictChooseMode(true); + + addCard(Zone.EXILED, playerA, senu); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); + + attack(1, playerA, "Elite Vanguard"); + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerB, 20 - 2); + assertPermanentCount(playerA, senu, 0); + } + + @Test + public void test_NoTrigger_OpponentAttack() { + setStrictChooseMode(true); + + addCard(Zone.EXILED, playerB, senu); + addCard(Zone.BATTLEFIELD, playerA, "Isamaru, Hound of Konda"); + + attack(1, playerA, "Isamaru, Hound of Konda"); + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerB, 20 - 2); + assertPermanentCount(playerA, senu, 0); + assertPermanentCount(playerB, senu, 0); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedAllTriggeredAbility.java new file mode 100644 index 00000000000..25df7f8fab0 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedAllTriggeredAbility.java @@ -0,0 +1,75 @@ +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.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * @author Susucr + */ +public class AttacksAndIsNotBlockedAllTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterPermanent filter; + private final SetTargetPointer setTargetPointer; + + public AttacksAndIsNotBlockedAllTriggeredAbility(Effect effect, FilterPermanent filter) { + this(Zone.BATTLEFIELD, effect, filter); + } + + public AttacksAndIsNotBlockedAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter) { + this(zone, effect, filter, false, SetTargetPointer.NONE); + } + + public AttacksAndIsNotBlockedAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer) { + super(zone, effect, optional); + this.filter = filter; + this.setTargetPointer = setTargetPointer; + setTriggerPhrase("Whenever " + filter.getMessage() + " attacks and isn't blocked, "); + } + + private AttacksAndIsNotBlockedAllTriggeredAbility(final AttacksAndIsNotBlockedAllTriggeredAbility ability) { + super(ability); + this.filter = ability.filter; + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public AttacksAndIsNotBlockedAllTriggeredAbility copy() { + return new AttacksAndIsNotBlockedAllTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.UNBLOCKED_ATTACKER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null || !filter.match(permanent, getControllerId(), this, game)) { + return false; + } + switch (setTargetPointer) { + case NONE: + break; + case PLAYER: + this.getEffects().setTargetPointer(new FixedTarget( + game.getCombat().getDefendingPlayerId(event.getTargetId(), game), game + )); + break; + case PERMANENT: + getEffects().setTargetPointer(new FixedTarget(permanent, game)); + break; + default: + throw new IllegalArgumentException("Wrong code usage: not supported setTargetPointer"); + } + return true; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java index d1f7381716d..3955e39cec6 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksAndIsNotBlockedTriggeredAbility.java @@ -2,6 +2,7 @@ package mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -10,17 +11,17 @@ import mage.target.targetpointer.FixedTarget; public class AttacksAndIsNotBlockedTriggeredAbility extends TriggeredAbilityImpl { - private final boolean setTargetPointer; + private final SetTargetPointer setTargetPointer; public AttacksAndIsNotBlockedTriggeredAbility(Effect effect) { - this(effect, false, false); + this(effect, false); } public AttacksAndIsNotBlockedTriggeredAbility(Effect effect, boolean optional) { - this(effect, optional, false); + this(effect, optional, SetTargetPointer.NONE); } - public AttacksAndIsNotBlockedTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) { + public AttacksAndIsNotBlockedTriggeredAbility(Effect effect, boolean optional, SetTargetPointer setTargetPointer) { super(Zone.BATTLEFIELD, effect, optional); this.setTargetPointer = setTargetPointer; setTriggerPhrase("Whenever {this} attacks and isn't blocked, "); @@ -47,10 +48,16 @@ public class AttacksAndIsNotBlockedTriggeredAbility extends TriggeredAbilityImpl if (!event.getTargetId().equals(getSourceId())) { return false; } - if (setTargetPointer) { - this.getEffects().setTargetPointer(new FixedTarget( - game.getCombat().getDefendingPlayerId(getSourceId(), game), game - )); + switch (setTargetPointer) { + case NONE: + break; + case PLAYER: + this.getEffects().setTargetPointer(new FixedTarget( + game.getCombat().getDefendingPlayerId(getSourceId(), game), game + )); + break; + default: + throw new IllegalArgumentException("Wrong code usage: not supported setTargetPointer"); } return true; } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceInExileCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceInExileCondition.java new file mode 100644 index 00000000000..0625f697a11 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceInExileCondition.java @@ -0,0 +1,21 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.constants.Zone; +import mage.game.Game; + +/** + * If {this} is exiled + * + * @author Susucr + */ + +public enum SourceInExileCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game.getState().getZone(source.getSourceId()) == Zone.EXILED; + } +}