From 432de6f9fea19705bf55a9dbd82a42ea8a2c9f36 Mon Sep 17 00:00:00 2001 From: Jmlundeen <98545818+Jmlundeen@users.noreply.github.com> Date: Sun, 7 Sep 2025 16:50:34 -0500 Subject: [PATCH] [SPM] implement Terrific Team-Up and create a shared effect (#13954) * create common TargetsDamageTargetsEffect * [SPM] implement Superior Terrific Team-Up * fix band together second target predicate * remove unnecessary checks in TargetsDamageTargetsEffect --- Mage.Sets/src/mage/cards/b/BandTogether.java | 57 +--------- Mage.Sets/src/mage/cards/c/ComboAttack.java | 50 +-------- .../mage/cards/c/CoordinatedClobbering.java | 61 +--------- .../src/mage/cards/f/FriendlyRivalry.java | 60 +--------- .../src/mage/cards/g/GracefulTakedown.java | 99 +++-------------- .../src/mage/cards/t/TandemTakedown.java | 58 +--------- .../src/mage/cards/t/TerrificTeamUp.java | 66 +++++++++++ Mage.Sets/src/mage/sets/MarvelsSpiderMan.java | 1 + .../cards/single/spm/TerrificTeamUpTest.java | 61 ++++++++++ .../single/woe/GracefulTakedownTest.java | 7 +- .../common/TargetsDamageTargetsEffect.java | 105 ++++++++++++++++++ 11 files changed, 271 insertions(+), 354 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/t/TerrificTeamUp.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TerrificTeamUpTest.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/TargetsDamageTargetsEffect.java diff --git a/Mage.Sets/src/mage/cards/b/BandTogether.java b/Mage.Sets/src/mage/cards/b/BandTogether.java index cab380356ca..e7dc695688d 100644 --- a/Mage.Sets/src/mage/cards/b/BandTogether.java +++ b/Mage.Sets/src/mage/cards/b/BandTogether.java @@ -1,17 +1,13 @@ package mage.cards.b; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.other.AnotherTargetPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.Target; import mage.target.TargetPermanent; @@ -27,19 +23,19 @@ public final class BandTogether extends CardImpl { static { filter.add(new AnotherTargetPredicate(1)); - filter2.add(new AnotherTargetPredicate(2)); + filter2.add(new AnotherTargetPredicate(3)); } public BandTogether(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); // Up to two target creatures you control each deal damage equal to their power to another target creature. - this.getSpellAbility().addEffect(new BandTogetherEffect()); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(true)); Target target = new TargetPermanent(0, 2, filter, false); target.setTargetTag(1); this.getSpellAbility().addTarget(target); target = new TargetPermanent(1, 1, filter2, false); - target.setTargetTag(2); + target.setTargetTag(3); this.getSpellAbility().addTarget(target); } @@ -52,48 +48,3 @@ public final class BandTogether extends CardImpl { return new BandTogether(this); } } - -class BandTogetherEffect extends OneShotEffect { - - BandTogetherEffect() { - super(Outcome.Benefit); - this.staticText = "Up to two target creatures you control each deal damage equal to their power to another target creature."; - } - - private BandTogetherEffect(final BandTogetherEffect effect) { - super(effect); - } - - @Override - public BandTogetherEffect copy() { - return new BandTogetherEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - if (source.getTargets().size() < 2) { - return false; - } - - Target damageTarget = source.getTargets().get(0); - Target destTarget = source.getTargets().get(1); - if (damageTarget.getTargets().isEmpty() || destTarget.getTargets().isEmpty()) { - return false; - } - - Permanent permanentDamage1 = damageTarget.getTargets().isEmpty() ? null : game.getPermanent(damageTarget.getTargets().get(0)); - Permanent permanentDamage2 = damageTarget.getTargets().size() < 2 ? null : game.getPermanent(damageTarget.getTargets().get(1)); - Permanent permanentDest = game.getPermanent(destTarget.getTargets().get(0)); - if (permanentDest == null) { - return false; - } - - if (permanentDamage1 != null) { - permanentDest.damage(permanentDamage1.getPower().getValue(), permanentDamage1.getId(), source, game, false, true); - } - if (permanentDamage2 != null) { - permanentDest.damage(permanentDamage2.getPower().getValue(), permanentDamage2.getId(), source, game, false, true); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/c/ComboAttack.java b/Mage.Sets/src/mage/cards/c/ComboAttack.java index 8acc59d0cf4..ba36b21c8db 100644 --- a/Mage.Sets/src/mage/cards/c/ComboAttack.java +++ b/Mage.Sets/src/mage/cards/c/ComboAttack.java @@ -1,15 +1,11 @@ package mage.cards.c; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.common.FilterTeamCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -26,9 +22,9 @@ public final class ComboAttack extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); // Two target creatures your team controls each deal damage equal to their power to target creature. - this.getSpellAbility().addEffect(new ComboAttackEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(2, filter)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(true)); + this.getSpellAbility().addTarget(new TargetPermanent(2, filter).setTargetTag(1)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent().setTargetTag(3)); } private ComboAttack(final ComboAttack card) { @@ -40,41 +36,3 @@ public final class ComboAttack extends CardImpl { return new ComboAttack(this); } } - -class ComboAttackEffect extends OneShotEffect { - - ComboAttackEffect() { - super(Outcome.Benefit); - this.staticText = "Two target creatures your team controls each deal damage equal to their power to target creature"; - } - - private ComboAttackEffect(final ComboAttackEffect effect) { - super(effect); - } - - @Override - public ComboAttackEffect copy() { - return new ComboAttackEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - if (source.getTargets().size() < 2) { - return false; - } - Permanent permanent3 = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (permanent3 == null) { - return false; - } - // You can’t cast Combo Attack without targeting two creatures your team controls. - // If one of those creatures is an illegal target as Combo Attack resolves, - // the other will still deal damage equal to its power. (2018-06-08) - for (UUID id : source.getTargets().get(0).getTargets()) { - Permanent permanent = game.getPermanent(id); - if (permanent != null) { - permanent3.damage(permanent.getPower().getValue(), permanent.getId(), source, game); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CoordinatedClobbering.java b/Mage.Sets/src/mage/cards/c/CoordinatedClobbering.java index 01b66f18355..21e6436de84 100644 --- a/Mage.Sets/src/mage/cards/c/CoordinatedClobbering.java +++ b/Mage.Sets/src/mage/cards/c/CoordinatedClobbering.java @@ -1,21 +1,15 @@ package mage.cards.c; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetOpponentsCreaturePermanent; -import java.util.List; -import java.util.Objects; import java.util.UUID; -import java.util.stream.Collectors; /** * @author TheElk801 @@ -26,11 +20,12 @@ public final class CoordinatedClobbering extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}"); // Tap one or two target untapped creatures you control. They each deal damage equal to their power to target creature an opponent controls. - this.getSpellAbility().addEffect(new CoordinatedClobberingEffect()); + this.getSpellAbility().addEffect(new TapTargetEffect()); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(false)); this.getSpellAbility().addTarget(new TargetPermanent( 1, 2, StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURES - )); - this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent()); + ).setTargetTag(1)); + this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent().setTargetTag(3)); } private CoordinatedClobbering(final CoordinatedClobbering card) { @@ -42,48 +37,4 @@ public final class CoordinatedClobbering extends CardImpl { return new CoordinatedClobbering(this); } } - -class CoordinatedClobberingEffect extends OneShotEffect { - - CoordinatedClobberingEffect() { - super(Outcome.Benefit); - staticText = "tap one or two target untapped creatures you control. " + - "They each deal damage equal to their power to target creature an opponent controls"; - } - - private CoordinatedClobberingEffect(final CoordinatedClobberingEffect effect) { - super(effect); - } - - @Override - public CoordinatedClobberingEffect copy() { - return new CoordinatedClobberingEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - List permanents = this - .getTargetPointer() - .getTargets(game, source) - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - if (permanents.isEmpty()) { - return false; - } - for (Permanent permanent : permanents) { - permanent.tap(source, game); - } - Permanent targetPermanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (targetPermanent == null) { - return true; - } - game.processAction(); - for (Permanent permanent : permanents) { - targetPermanent.damage(permanent.getPower().getValue(), permanent.getId(), source, game); - } - return true; - } -} // flame on! diff --git a/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java b/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java index c9748da9aaf..d01788246fa 100644 --- a/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java +++ b/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java @@ -1,24 +1,17 @@ package mage.cards.f; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.other.AnotherTargetPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import java.util.UUID; /** @@ -37,7 +30,7 @@ public final class FriendlyRivalry extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{G}"); // Target creature you control and up to one other target legendary creature you control each deal damage equal to their power to target creature you don't control. - this.getSpellAbility().addEffect(new FriendlyRivalryEffect()); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(true)); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent().setTargetTag(1).withChooseHint("to deal damage")); this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter2).setTargetTag(2).withChooseHint("to deal damage")); this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL).setTargetTag(3).withChooseHint("to take damage")); @@ -52,52 +45,3 @@ public final class FriendlyRivalry extends CardImpl { return new FriendlyRivalry(this); } } - -class FriendlyRivalryEffect extends OneShotEffect { - - FriendlyRivalryEffect() { - super(Outcome.Benefit); - staticText = "Target creature you control and up to one other target legendary " + - "creature you control each deal damage equal to their power to target creature you don't control."; - } - - private FriendlyRivalryEffect(final FriendlyRivalryEffect effect) { - super(effect); - } - - @Override - public FriendlyRivalryEffect copy() { - return new FriendlyRivalryEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int size = source.getTargets().size(); - if (size < 3) { - throw new IllegalArgumentException("Wrong code usage. Lost targets list, must has 3, but found: " + source.getTargets()); - } - - List toDealDamage = new ArrayList<>(); - source.getTargets().getTargetsByTag(1).stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .forEach(toDealDamage::add); - source.getTargets().getTargetsByTag(2).stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .forEach(toDealDamage::add); - Permanent toTakeDamage = source.getTargets().getTargetsByTag(3).stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .findFirst().orElse(null); - if (toDealDamage.isEmpty() || toTakeDamage == null) { - return false; - } - - toDealDamage.forEach(permanent -> { - toTakeDamage.damage(permanent.getPower().getValue(), permanent.getId(), source, game, false, true); - }); - - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/g/GracefulTakedown.java b/Mage.Sets/src/mage/cards/g/GracefulTakedown.java index 42225c2acb6..b8298e3b973 100644 --- a/Mage.Sets/src/mage/cards/g/GracefulTakedown.java +++ b/Mage.Sets/src/mage/cards/g/GracefulTakedown.java @@ -1,36 +1,38 @@ package mage.cards.g; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.filter.predicate.permanent.EnchantedPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; -import java.util.List; -import java.util.Objects; import java.util.UUID; -import java.util.stream.Collectors; /** * @author TheElk801 */ public final class GracefulTakedown extends CardImpl { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("enchanted creatures you control"); + private static final FilterControlledCreaturePermanent otherCreatureFilter = new FilterControlledCreaturePermanent("other target creature you control"); + static { + filter.add(EnchantedPredicate.instance); + otherCreatureFilter.add(new AnotherTargetPredicate(2)); + } + public GracefulTakedown(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); // Any number of target enchanted creatures you control and up to one other target creature you control each deal damage equal to their power to target creature you don't control. - this.getSpellAbility().addEffect(new GracefulTakedownEffect()); - this.getSpellAbility().addTarget(new GracefulTakedownTarget()); - this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(true)); + this.getSpellAbility().addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter).setTargetTag(1).withChooseHint("enchanted")); + this.getSpellAbility().addTarget(new TargetPermanent(0, 1, otherCreatureFilter).setTargetTag(2)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL).setTargetTag(3)); } private GracefulTakedown(final GracefulTakedown card) { @@ -42,78 +44,3 @@ public final class GracefulTakedown extends CardImpl { return new GracefulTakedown(this); } } - -class GracefulTakedownTarget extends TargetPermanent { - - private static final FilterPermanent filter = new FilterControlledCreaturePermanent("enchanted creatures you control and up to one other creature you control"); - - GracefulTakedownTarget() { - super(0, Integer.MAX_VALUE, filter); - } - - private GracefulTakedownTarget(final GracefulTakedownTarget target) { - super(target); - } - - @Override - public GracefulTakedownTarget copy() { - return new GracefulTakedownTarget(this); - } - - @Override - public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { - if (!super.canTarget(playerId, id, source, game)) { - return false; - } - Permanent permanent = game.getPermanent(id); - return permanent != null - && (EnchantedPredicate.instance.apply(permanent, game) - || this - .getTargets() - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .allMatch(p -> p.getId().equals(id) || EnchantedPredicate.instance.apply(p, game))); - } -} - -class GracefulTakedownEffect extends OneShotEffect { - - GracefulTakedownEffect() { - super(Outcome.Benefit); - staticText = "any number of target enchanted creatures you control and up to one other target creature " + - "you control each deal damage equal to their power to target creature you don't control"; - } - - private GracefulTakedownEffect(final GracefulTakedownEffect effect) { - super(effect); - } - - @Override - public GracefulTakedownEffect copy() { - return new GracefulTakedownEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - List permanents = source - .getTargets() - .get(0) - .getTargets() - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - if (permanents.isEmpty()) { - return false; - } - Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (creature == null) { - return false; - } - for (Permanent permanent : permanents) { - creature.damage(permanent.getPower().getValue(), permanent.getId(), source, game); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/t/TandemTakedown.java b/Mage.Sets/src/mage/cards/t/TandemTakedown.java index 50f7b03cc53..7620676e8b1 100644 --- a/Mage.Sets/src/mage/cards/t/TandemTakedown.java +++ b/Mage.Sets/src/mage/cards/t/TandemTakedown.java @@ -1,18 +1,13 @@ package mage.cards.t; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.other.AnotherTargetPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -23,7 +18,7 @@ import java.util.UUID; */ public final class TandemTakedown extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("creature, planeswalker, or battle"); + private static final FilterPermanent filter = new FilterPermanent("another target creature, planeswalker, or battle"); static { filter.add(Predicates.or( @@ -40,9 +35,9 @@ public final class TandemTakedown extends CardImpl { // Up to two target creatures you control each get +1/+0 until end of turn. They each deal damage equal to their power to another target creature, planeswalker, or battle. this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0) .setText("up to two target creatures you control each get +1/+0 until end of turn")); - this.getSpellAbility().addEffect(new TandemTakedownEffect()); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(false)); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, 2).setTargetTag(1)); - this.getSpellAbility().addTarget(new TargetPermanent(filter).setTargetTag(2)); + this.getSpellAbility().addTarget(new TargetPermanent(filter).setTargetTag(3)); } private TandemTakedown(final TandemTakedown card) { @@ -54,48 +49,3 @@ public final class TandemTakedown extends CardImpl { return new TandemTakedown(this); } } - -class TandemTakedownEffect extends OneShotEffect { - - TandemTakedownEffect() { - super(Outcome.Benefit); - staticText = "They each deal damage equal to their power to another target creature, planeswalker, or battle"; - } - - private TandemTakedownEffect(final TandemTakedownEffect effect) { - super(effect); - } - - @Override - public TandemTakedownEffect copy() { - return new TandemTakedownEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - if (source.getTargets().size() < 2) { - return false; - } - - Target damageTarget = source.getTargets().get(0); - Target destTarget = source.getTargets().get(1); - if (damageTarget.getTargets().isEmpty() || destTarget.getTargets().isEmpty()) { - return false; - } - - Permanent permanentDamage1 = damageTarget.getTargets().isEmpty() ? null : game.getPermanent(damageTarget.getTargets().get(0)); - Permanent permanentDamage2 = damageTarget.getTargets().size() < 2 ? null : game.getPermanent(damageTarget.getTargets().get(1)); - Permanent permanentDest = game.getPermanent(destTarget.getTargets().get(0)); - if (permanentDest == null) { - return false; - } - - if (permanentDamage1 != null) { - permanentDest.damage(permanentDamage1.getPower().getValue(), permanentDamage1.getId(), source, game, false, true); - } - if (permanentDamage2 != null) { - permanentDest.damage(permanentDamage2.getPower().getValue(), permanentDamage2.getId(), source, game, false, true); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/t/TerrificTeamUp.java b/Mage.Sets/src/mage/cards/t/TerrificTeamUp.java new file mode 100644 index 00000000000..ce5d251e0e5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TerrificTeamUp.java @@ -0,0 +1,66 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class TerrificTeamUp extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("you control a permanent with mana value 4 or greater"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.OR_GREATER, 4)); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint(condition, "You control a permanent with mana value 4 or greater"); + + public TerrificTeamUp(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{G}"); + + + // This spell costs {2} less to cast if you control a permanent with mana value 4 or greater. + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, condition)); + ability.setRuleAtTheTop(true); + ability.addHint(hint); + this.addAbility(ability); + + // One or two target creatures you control each get +1/+0 until end of turn. They each deal damage equal to their power to target creature an opponent controls. + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0)); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(false)); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(1, 2).setTargetTag(1).withChooseHint("boost and deal damage")); + this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent().setTargetTag(3)); + } + + private TerrificTeamUp(final TerrificTeamUp card) { + super(card); + } + + @Override + public TerrificTeamUp copy() { + return new TerrificTeamUp(this); + } +} + + diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java b/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java index 6c4f6df4ef4..6e49cf6464a 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java @@ -270,6 +270,7 @@ public final class MarvelsSpiderMan extends ExpansionSet { cards.add(new SetCardInfo("Symbiote Spider-Man", 156, Rarity.RARE, mage.cards.s.SymbioteSpiderMan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Symbiote Spider-Man", 217, Rarity.RARE, mage.cards.s.SymbioteSpiderMan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Taxi Driver", 97, Rarity.COMMON, mage.cards.t.TaxiDriver.class)); + cards.add(new SetCardInfo("Terrific Team-Up", 120, Rarity.UNCOMMON, mage.cards.t.TerrificTeamUp.class)); cards.add(new SetCardInfo("The Clone Saga", 219, Rarity.RARE, mage.cards.t.TheCloneSaga.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Clone Saga", 28, Rarity.RARE, mage.cards.t.TheCloneSaga.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Death of Gwen Stacy", 223, Rarity.RARE, mage.cards.t.TheDeathOfGwenStacy.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TerrificTeamUpTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TerrificTeamUpTest.java new file mode 100644 index 00000000000..706d34f7b84 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TerrificTeamUpTest.java @@ -0,0 +1,61 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.filter.Filter; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class TerrificTeamUpTest extends CardTestPlayerBase { + + /* + Terrific Team-Up + {3}{G} + Instant + This spell costs {2} less to cast if you control a permanent with mana value 4 or greater. + One or two target creatures you control each get +1/+0 until end of turn. They each deal damage equal to their power to target creature an opponent controls. + */ + private static final String terrificTeamUp = "Terrific Team-Up"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + /* + Sea Monster + {4}{U}{U} + Creature - Serpent + Sea Monster can't attack unless defending player controls an Island. + 6/6 + */ + private static final String seaMonster = "Sea Monster"; + + @Test + public void testTerrificTeamUp() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, bearCub, 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerB, seaMonster); + addCard(Zone.HAND, playerA, terrificTeamUp); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, terrificTeamUp); + addTarget(playerA, bearCub, 2); + addTarget(playerA, seaMonster); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerB, seaMonster, 1); + assertPowerToughness(playerA, bearCub, 3, 2, Filter.ComparisonScope.All); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/GracefulTakedownTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/GracefulTakedownTest.java index ad185845a26..19ceebb0ee3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/GracefulTakedownTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/GracefulTakedownTest.java @@ -4,6 +4,7 @@ import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Assert; import org.junit.Test; +import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestPlayerBase; /** @@ -38,7 +39,8 @@ public class GracefulTakedownTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, takedown); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, takedown); - addTarget(playerA, cub); + addTarget(playerA, TestPlayer.TARGET_SKIP); // enchanted + addTarget(playerA, cub); // one other addTarget(playerA, ancient); setStopAt(1, PhaseStep.END_COMBAT); @@ -62,7 +64,8 @@ public class GracefulTakedownTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, strength, piker, true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, takedown); - addTarget(playerA, cub + "^" + piker); + addTarget(playerA, piker); // enchanted + addTarget(playerA, cub); // one other addTarget(playerA, ancient); setStopAt(1, PhaseStep.END_COMBAT); diff --git a/Mage/src/main/java/mage/abilities/effects/common/TargetsDamageTargetsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TargetsDamageTargetsEffect.java new file mode 100644 index 00000000000..cad1c21c75b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/TargetsDamageTargetsEffect.java @@ -0,0 +1,105 @@ +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.permanent.Permanent; +import mage.target.Target; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Used for [target creatures] deal damage equal to their power to [target creature] + *
set targets using tags .setTargetTag(1) + *
set the first target tag for creatures dealing damage + *
set the second target tag for additional creatures, not required (Friendly Rivalry) + *
set the third target tag for creatures receiving damage + * + * @author Jmlundeen + */ +public class TargetsDamageTargetsEffect extends OneShotEffect { + + private final boolean describeDamagingTargets; + + public TargetsDamageTargetsEffect(boolean describeDamagingTargets) { + super(Outcome.Benefit); + this.describeDamagingTargets = describeDamagingTargets; + } + + private TargetsDamageTargetsEffect(final TargetsDamageTargetsEffect effect) { + super(effect); + this.describeDamagingTargets = effect.describeDamagingTargets; + } + + @Override + public TargetsDamageTargetsEffect copy() { + return new TargetsDamageTargetsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (source.getTargets().size() < 2) { + return false; + } + + Target damageTarget = source.getTargets().getByTag(1); + Target additionalDamageTarget = source.getTargets().getByTag(2); + Target destTarget = source.getTargets().getByTag(3); + + List damagingPermanents = new ArrayList<>(); + List receivingPermanents = new ArrayList<>(); + for (UUID id : damageTarget.getTargets()) { + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + damagingPermanents.add(permanent); + } + } + if (additionalDamageTarget != null) { + for (UUID id : additionalDamageTarget.getTargets()) { + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + damagingPermanents.add(permanent); + } + } + } + for (UUID id : destTarget.getTargets()) { + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + receivingPermanents.add(permanent); + } + } + + if (receivingPermanents.isEmpty() || damagingPermanents.isEmpty()) { + return false; + } + for (Permanent receivingPermanent : receivingPermanents) { + for (Permanent damagingPermanent: damagingPermanents) { + receivingPermanent.damage(damagingPermanent.getPower().getValue(), damagingPermanent.getId(), source, game, false, true); + } + } + return true; + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder(); + if (describeDamagingTargets) { + sb.append(mode.getTargets().getByTag(1).getDescription()); + if (mode.getTargets().getByTag(2) != null) { + sb.append(" and ").append(mode.getTargets().getByTag(2).getDescription()); + } + } else { + sb.append("they"); + } + sb.append(" each deal damage equal to their power to "); + sb.append(mode.getTargets().getByTag(3).getDescription()); + return sb.toString(); + } +} \ No newline at end of file