diff --git a/Mage.Sets/src/mage/cards/l/LithoformEngine.java b/Mage.Sets/src/mage/cards/l/LithoformEngine.java index f3ff281357e..360678d4fb8 100644 --- a/Mage.Sets/src/mage/cards/l/LithoformEngine.java +++ b/Mage.Sets/src/mage/cards/l/LithoformEngine.java @@ -1,26 +1,20 @@ package mage.cards.l; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CopyTargetStackAbilityEffect; import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SuperType; import mage.constants.TargetController; import mage.filter.FilterSpell; import mage.filter.FilterStackObject; import mage.filter.common.FilterInstantOrSorcerySpell; import mage.filter.predicate.mageobject.PermanentPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.StackAbility; -import mage.players.Player; import mage.target.TargetSpell; import mage.target.common.TargetActivatedOrTriggeredAbility; @@ -51,7 +45,7 @@ public final class LithoformEngine extends CardImpl { this.supertype.add(SuperType.LEGENDARY); // {2}, {T}: Copy target activated or triggered ability you control. You may choose new targets for the copy. - Ability ability = new SimpleActivatedAbility(new LithoformEngineEffect(), new GenericManaCost(2)); + Ability ability = new SimpleActivatedAbility(new CopyTargetStackAbilityEffect(), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetActivatedOrTriggeredAbility(filter)); this.addAbility(ability); @@ -80,35 +74,3 @@ public final class LithoformEngine extends CardImpl { return new LithoformEngine(this); } } - -class LithoformEngineEffect extends OneShotEffect { - - public LithoformEngineEffect() { - super(Outcome.Copy); - this.staticText = "Copy target activated or triggered ability you control. You may choose new targets for the copy"; - } - - public LithoformEngineEffect(final LithoformEngineEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source)); - if (stackAbility != null) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); - return true; - } - } - return false; - - } - - @Override - public LithoformEngineEffect copy() { - return new LithoformEngineEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/s/StrionicResonator.java b/Mage.Sets/src/mage/cards/s/StrionicResonator.java index b549308e45e..e2470fe3e9a 100644 --- a/Mage.Sets/src/mage/cards/s/StrionicResonator.java +++ b/Mage.Sets/src/mage/cards/s/StrionicResonator.java @@ -1,17 +1,15 @@ package mage.cards.s; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CopyTargetStackAbilityEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.stack.StackAbility; +import mage.constants.TargetController; +import mage.filter.FilterStackObject; import mage.target.common.TargetTriggeredAbility; import java.util.UUID; @@ -21,13 +19,19 @@ import java.util.UUID; */ public final class StrionicResonator extends CardImpl { + private static final FilterStackObject filter = new FilterStackObject("triggered ability you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + public StrionicResonator(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // {2}, {T}: Copy target triggered ability you control. You may choose new targets for the copy. - Ability ability = new SimpleActivatedAbility(new StrionicResonatorEffect(), new ManaCostsImpl<>("{2}")); + Ability ability = new SimpleActivatedAbility(new CopyTargetStackAbilityEffect(), new ManaCostsImpl<>("{2}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetTriggeredAbility()); + ability.addTarget(new TargetTriggeredAbility(filter)); this.addAbility(ability); } @@ -40,31 +44,3 @@ public final class StrionicResonator extends CardImpl { return new StrionicResonator(this); } } - -class StrionicResonatorEffect extends OneShotEffect { - - public StrionicResonatorEffect() { - super(Outcome.Copy); - this.staticText = "Copy target triggered ability you control. You may choose new targets for the copy"; - } - - public StrionicResonatorEffect(final StrionicResonatorEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source)); - if (stackAbility == null) { - return false; - } - stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); - return true; - - } - - @Override - public StrionicResonatorEffect copy() { - return new StrionicResonatorEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java b/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java index 9c1f477579b..8d47fbfac66 100644 --- a/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java +++ b/Mage.Sets/src/mage/cards/t/TawnosUrzasApprentice.java @@ -6,22 +6,17 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CopyTargetStackAbilityEffect; import mage.constants.SubType; import mage.constants.SuperType; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.FilterStackObject; import mage.filter.predicate.other.ArtifactSourcePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.StackAbility; -import mage.players.Player; import mage.target.common.TargetActivatedOrTriggeredAbility; /** @@ -50,7 +45,7 @@ public final class TawnosUrzasApprentice extends CardImpl { this.addAbility(HasteAbility.getInstance()); // {U}{R}, {T}: Copy target activated or triggered ability you control from an artifact source. You may choose new targets for the copy. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TawnosUrzasApprenticeEffect(), new ManaCostsImpl<>("{U}{R}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CopyTargetStackAbilityEffect(), new ManaCostsImpl<>("{U}{R}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetActivatedOrTriggeredAbility(filter)); this.addAbility(ability); @@ -65,35 +60,3 @@ public final class TawnosUrzasApprentice extends CardImpl { return new TawnosUrzasApprentice(this); } } - -class TawnosUrzasApprenticeEffect extends OneShotEffect { - - public TawnosUrzasApprenticeEffect() { - super(Outcome.Copy); - this.staticText = "copy target activated or triggered ability you control from an artifact source. You may choose new targets for the copy"; - } - - public TawnosUrzasApprenticeEffect(final TawnosUrzasApprenticeEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source)); - if (stackAbility != null) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); - return true; - } - } - return false; - - } - - @Override - public TawnosUrzasApprenticeEffect copy() { - return new TawnosUrzasApprenticeEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/t/ThePeregrineDynamo.java b/Mage.Sets/src/mage/cards/t/ThePeregrineDynamo.java index 67bd37d8726..0d59feacd45 100644 --- a/Mage.Sets/src/mage/cards/t/ThePeregrineDynamo.java +++ b/Mage.Sets/src/mage/cards/t/ThePeregrineDynamo.java @@ -6,7 +6,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CopyTargetStackAbilityEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -48,7 +48,7 @@ public final class ThePeregrineDynamo extends CardImpl { this.addAbility(HasteAbility.getInstance()); // {1}, {T}: Copy target activated or triggered ability you control from another legendary source that's not a commander. You may choose new targets for the copy. - Ability ability = new SimpleActivatedAbility(new ThePeregrineDynamoEffect(), new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(new CopyTargetStackAbilityEffect(), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetActivatedOrTriggeredAbility(filter)); this.addAbility(ability); @@ -79,31 +79,3 @@ enum ThePeregrineDynamoPredicate implements ObjectSourcePlayerPredicate("{G}")); + Ability ability = new SimpleActivatedAbility(new CopyTargetStackAbilityEffect(), new ManaCostsImpl<>("{G}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetStackObject(filter2)); + ability.addTarget(new TargetActivatedOrTriggeredAbility(filter2)); this.addAbility(ability); } @@ -68,41 +63,3 @@ public final class WeaverOfHarmony extends CardImpl { return new WeaverOfHarmony(this); } } - -enum WeaverOfHarmonyPredicate implements Predicate { - instance; - - @Override - public boolean apply(StackObject input, Game game) { - return input instanceof StackAbility - && ((StackAbility) input).getSourceObject(game).isEnchantment(game); - } -} - -class WeaverOfHarmonyEffect extends OneShotEffect { - - WeaverOfHarmonyEffect() { - super(Outcome.Benefit); - staticText = "copy target activated or triggered ability you control " + - "from an enchantment source. You may choose new targets for the copy"; - } - - private WeaverOfHarmonyEffect(final WeaverOfHarmonyEffect effect) { - super(effect); - } - - @Override - public WeaverOfHarmonyEffect copy() { - return new WeaverOfHarmonyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source)); - if (stackAbility == null) { - return false; - } - stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); - return true; - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyTargetStackAbilityEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyTargetStackAbilityEffect.java new file mode 100644 index 00000000000..d8688d03f65 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyTargetStackAbilityEffect.java @@ -0,0 +1,52 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.stack.StackAbility; + +public class CopyTargetStackAbilityEffect extends OneShotEffect { + + /** + * Copy target (activated/triggered) ability on the stack, choosing new targets for the copy + */ + public CopyTargetStackAbilityEffect() { + super(Outcome.Copy); + } + + public CopyTargetStackAbilityEffect(final CopyTargetStackAbilityEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source)); + if (stackAbility == null) { + return false; + } + stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); + return true; + } + + @Override + public CopyTargetStackAbilityEffect copy() { + return new CopyTargetStackAbilityEffect(this); + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder(); + sb.append("copy "); + if (!mode.getTargets().isEmpty()) { + sb.append("target ").append(mode.getTargets().get(0).getTargetName()); + } + sb.append(". You may choose new targets for the copy"); + return sb.toString(); + } + +} diff --git a/Mage/src/main/java/mage/filter/predicate/other/EnchantmentSourcePredicate.java b/Mage/src/main/java/mage/filter/predicate/other/EnchantmentSourcePredicate.java new file mode 100644 index 00000000000..ba5264c2bd3 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/other/EnchantmentSourcePredicate.java @@ -0,0 +1,24 @@ +package mage.filter.predicate.other; + +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.StackAbility; +import mage.game.stack.StackObject; + +/** + * @author LevelX2 + */ +public enum EnchantmentSourcePredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + return input instanceof StackAbility + && ((StackAbility) input).getSourceObject(game).isEnchantment(game); + } + + @Override + public String toString() { + return "Source(Enchantment)"; + } +} diff --git a/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java b/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java index 37028f2f19a..fc137486e9f 100644 --- a/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java +++ b/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java @@ -1,32 +1,36 @@ package mage.target.common; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; +import mage.constants.AbilityType; import mage.constants.Zone; import mage.filter.Filter; -import mage.filter.FilterAbility; +import mage.filter.FilterStackObject; import mage.game.Game; import mage.game.stack.StackObject; import mage.target.TargetObject; -import java.util.HashSet; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; /** * @author Styxo */ public class TargetTriggeredAbility extends TargetObject { - public TargetTriggeredAbility() { + protected final FilterStackObject filter; + + public TargetTriggeredAbility(FilterStackObject filter) { this.minNumberOfTargets = 1; this.maxNumberOfTargets = 1; this.zone = Zone.STACK; - this.targetName = "target triggered ability you control"; + this.targetName = filter.getMessage(); + this.filter = filter; } public TargetTriggeredAbility(final TargetTriggeredAbility target) { super(target); + this.filter = target.filter.copy(); } @Override @@ -37,28 +41,29 @@ public class TargetTriggeredAbility extends TargetObject { } StackObject stackObject = game.getStack().getStackObject(id); - return stackObject != null - && stackObject.getStackAbility() instanceof TriggeredAbility + return isTriggeredAbility(stackObject) && source != null - && stackObject.getStackAbility().isControlledBy(source.getControllerId()); + && filter.match(stackObject, source.getControllerId(), source, game); } @Override public boolean canChoose(UUID sourceControllerId, Ability source, Game game) { - return canChoose(sourceControllerId, game); - } - - @Override - public boolean canChoose(UUID sourceControllerId, Game game) { for (StackObject stackObject : game.getStack()) { - if (stackObject.getStackAbility() instanceof TriggeredAbility - && stackObject.getStackAbility().isControlledBy(sourceControllerId)) { + if (isTriggeredAbility(stackObject) + && filter.match(stackObject, sourceControllerId, source, game)) { return true; } } return false; } + @Override + public boolean canChoose(UUID sourceControllerId, Game game) { + return game.getStack() + .stream() + .anyMatch(TargetTriggeredAbility::isTriggeredAbility); + } + @Override public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { return possibleTargets(sourceControllerId, game); @@ -66,14 +71,10 @@ public class TargetTriggeredAbility extends TargetObject { @Override public Set possibleTargets(UUID sourceControllerId, Game game) { - Set possibleTargets = new HashSet<>(); - for (StackObject stackObject : game.getStack()) { - if (stackObject.getStackAbility() instanceof TriggeredAbility - && stackObject.getStackAbility().isControlledBy(sourceControllerId)) { - possibleTargets.add(stackObject.getStackAbility().getId()); - } - } - return possibleTargets; + return game.getStack().stream() + .filter(TargetTriggeredAbility::isTriggeredAbility) + .map(stackObject -> stackObject.getStackAbility().getId()) + .collect(Collectors.toSet()); } @Override @@ -83,7 +84,18 @@ public class TargetTriggeredAbility extends TargetObject { @Override public Filter getFilter() { - return new FilterAbility(); + return filter; + } + + static boolean isTriggeredAbility(StackObject stackObject) { + if (stackObject == null) { + return false; + } + if (stackObject instanceof Ability) { + Ability ability = (Ability) stackObject; + return ability.getAbilityType() == AbilityType.TRIGGERED; + } + return false; } }