From 255c2921040ee73faaf669444a0a6237dbb10b53 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 23 Dec 2020 02:31:41 +0400 Subject: [PATCH] * Deals damage divided as you choose - fixed that some cards can't choose planeswalkers (example: Arc Lightning, see #7276); Refactor: simplified FilterCreaturePlayerOrPlaneswalker to use single permanent filter; --- .../java/mage/player/ai/ComputerPlayer.java | 22 +++--- Mage.Sets/src/mage/cards/a/ArcTrail.java | 5 +- Mage.Sets/src/mage/cards/b/BondOfPassion.java | 10 +-- Mage.Sets/src/mage/cards/c/CauterySliver.java | 22 ++---- Mage.Sets/src/mage/cards/c/ConeOfFlame.java | 23 +++--- .../mage/cards/d/DrakusethMawOfFlames.java | 3 +- Mage.Sets/src/mage/cards/e/Endure.java | 14 ++-- .../src/mage/cards/i/ImperialGunner.java | 14 ++-- Mage.Sets/src/mage/cards/n/NeedleDrop.java | 14 ++-- .../mage/cards/n/NicolBolasGodPharaoh.java | 23 +++--- Mage.Sets/src/mage/cards/t/TwinBolt.java | 8 +- .../cards/single/m11/InfernoTitanTest.java | 39 ++++++++++ .../cards/single/soi/FlamebladeAngelTest.java | 44 +++++------ .../java/org/mage/test/player/TestPlayer.java | 3 - .../FilterCreaturePlayerOrPlaneswalker.java | 76 +++++++------------ .../mage/target/common/TargetAnyTarget.java | 47 ++---------- .../target/common/TargetAnyTargetAmount.java | 8 +- .../TargetCreatureOrPlaneswalkerAmount.java | 9 --- 18 files changed, 162 insertions(+), 222 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/m11/InfernoTitanTest.java diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 0a26da7dc17..43f362494c4 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -1,16 +1,10 @@ package mage.player.ai; -import java.io.IOException; -import java.io.Serializable; -import java.util.*; -import java.util.Map.Entry; import mage.ApprovingObject; import mage.ConditionalMana; import mage.MageObject; import mage.Mana; import mage.abilities.*; -import mage.abilities.costs.AlternativeSourceCosts; -import mage.abilities.costs.OptionalAdditionalSourceCosts; import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.*; import mage.abilities.effects.Effect; @@ -42,7 +36,6 @@ import mage.game.combat.CombatGroup; import mage.game.draft.Draft; import mage.game.draft.RateCard; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.match.Match; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -63,6 +56,11 @@ import mage.util.TournamentUtil; import mage.util.TreeNode; import org.apache.log4j.Logger; +import java.io.IOException; +import java.io.Serializable; +import java.util.*; +import java.util.Map.Entry; + /** * suitable for two player games and some multiplayer games * @@ -270,9 +268,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { List targets; TargetAnyTarget origTarget = (TargetAnyTarget) target.getOriginalTarget(); if (outcome.isGood()) { - targets = threats(abilityControllerId, sourceId, ((FilterCreaturePlayerOrPlaneswalker) origTarget.getFilter()).getCreatureFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, sourceId, ((FilterCreaturePlayerOrPlaneswalker) origTarget.getFilter()).getPermanentFilter(), game, target.getTargets()); } else { - targets = threats(randomOpponentId, sourceId, ((FilterCreaturePlayerOrPlaneswalker) origTarget.getFilter()).getCreatureFilter(), game, target.getTargets()); + targets = threats(randomOpponentId, sourceId, ((FilterCreaturePlayerOrPlaneswalker) origTarget.getFilter()).getPermanentFilter(), game, target.getTargets()); } for (Permanent permanent : targets) { List alreadyTargetted = target.getTargets(); @@ -651,9 +649,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { List targets; TargetAnyTarget origTarget = ((TargetAnyTarget) target.getOriginalTarget()); if (outcome.isGood()) { - targets = threats(abilityControllerId, sourceId, ((FilterCreaturePlayerOrPlaneswalker) origTarget.getFilter()).getCreatureFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, sourceId, ((FilterCreaturePlayerOrPlaneswalker) origTarget.getFilter()).getPermanentFilter(), game, target.getTargets()); } else { - targets = threats(randomOpponentId, sourceId, ((FilterCreaturePlayerOrPlaneswalker) origTarget.getFilter()).getCreatureFilter(), game, target.getTargets()); + targets = threats(randomOpponentId, sourceId, ((FilterCreaturePlayerOrPlaneswalker) origTarget.getFilter()).getPermanentFilter(), game, target.getTargets()); } if (targets.isEmpty()) { @@ -667,7 +665,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } if (targets.isEmpty() && required) { - targets = game.getBattlefield().getActivePermanents(((FilterCreaturePlayerOrPlaneswalker) origTarget.getFilter()).getCreatureFilter(), playerId, game); + targets = game.getBattlefield().getActivePermanents(((FilterCreaturePlayerOrPlaneswalker) origTarget.getFilter()).getPermanentFilter(), playerId, game); } for (Permanent permanent : targets) { List alreadyTargeted = target.getTargets(); diff --git a/Mage.Sets/src/mage/cards/a/ArcTrail.java b/Mage.Sets/src/mage/cards/a/ArcTrail.java index 2fc795542a3..b3d85968c09 100644 --- a/Mage.Sets/src/mage/cards/a/ArcTrail.java +++ b/Mage.Sets/src/mage/cards/a/ArcTrail.java @@ -33,9 +33,8 @@ public final class ArcTrail extends CardImpl { this.getSpellAbility().addTarget(target1); FilterCreaturePlayerOrPlaneswalker filter2 = new FilterCreaturePlayerOrPlaneswalker("another creature, player or planeswalker to deal 1 damage"); - AnotherTargetPredicate predicate = new AnotherTargetPredicate(2); - filter2.getCreatureFilter().add(predicate); - filter2.getPlayerFilter().add(predicate); + filter2.getPermanentFilter().add(new AnotherTargetPredicate(2)); + filter2.getPlayerFilter().add(new AnotherTargetPredicate(2)); TargetAnyTarget target2 = new TargetAnyTarget(1, 1, filter2); target2.setTargetTag(2); this.getSpellAbility().addTarget(target2); diff --git a/Mage.Sets/src/mage/cards/b/BondOfPassion.java b/Mage.Sets/src/mage/cards/b/BondOfPassion.java index 4e99511df04..174af8fe9dd 100644 --- a/Mage.Sets/src/mage/cards/b/BondOfPassion.java +++ b/Mage.Sets/src/mage/cards/b/BondOfPassion.java @@ -32,14 +32,12 @@ public final class BondOfPassion extends CardImpl { private static final FilterPermanent filter = new FilterCreaturePermanent(); - private static final FilterCreaturePlayerOrPlaneswalker filter2 + private static final FilterCreaturePlayerOrPlaneswalker otherFilter = new FilterCreaturePlayerOrPlaneswalker("any other target"); static { - filter.add(new AnotherTargetPredicate(1)); - filter2.getCreatureFilter().add(new AnotherTargetPredicate(2)); - filter2.getPlaneswalkerFilter().add(new AnotherTargetPredicate(2)); - filter2.getPlayerFilter().add(new AnotherTargetPredicate(2)); + otherFilter.getPlayerFilter().add(new AnotherTargetPredicate(2)); + otherFilter.getPlayerFilter().add(new AnotherTargetPredicate(2)); } public BondOfPassion(UUID ownerId, CardSetInfo setInfo) { @@ -50,7 +48,7 @@ public final class BondOfPassion extends CardImpl { Target target = new TargetPermanent(filter); target.setTargetTag(1); this.getSpellAbility().addTarget(target); - target = new TargetAnyTarget(filter2); + target = new TargetAnyTarget(otherFilter); target.setTargetTag(2); this.getSpellAbility().addTarget(target); } diff --git a/Mage.Sets/src/mage/cards/c/CauterySliver.java b/Mage.Sets/src/mage/cards/c/CauterySliver.java index 40c5d2f11af..bf6c2741f8f 100644 --- a/Mage.Sets/src/mage/cards/c/CauterySliver.java +++ b/Mage.Sets/src/mage/cards/c/CauterySliver.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -18,12 +16,12 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePlayerOrPlaneswalker; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author anonymous */ public final class CauterySliver extends CardImpl { @@ -43,10 +41,12 @@ public final class CauterySliver extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability1, Duration.WhileOnBattlefield, filter, "All Slivers have \"{1}, Sacrifice this permanent: This permanent deals 1 damage to any target.\""))); + // All Slivers have "{1}, Sacrifice this permanent: Prevent the next 1 damage that would be dealt to target Sliver creature or player this turn." + // All Slivers have "{1}, Sacrifice this permanent: Prevent the next 1 damage that would be dealt to target player, planeswalker, or Sliver creature this turn." Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventDamageToTargetEffect(Duration.EndOfTurn, 1), new ManaCostsImpl("1")); ability2.addCost(new SacrificeSourceCost()); - ability2.addTarget(new TargetSliverCreatureOrPlayer()); + ability2.addTarget(new TargetAnyTarget(new FilterCreatureOrPlayerByType(SubType.SLIVER, "Sliver creature or player"))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(ability2, Duration.WhileOnBattlefield, filter, "All Slivers have \"{1}, Sacrifice this permanent: Prevent the next 1 damage that would be dealt to target Sliver creature or player this turn.\""))); @@ -62,18 +62,10 @@ public final class CauterySliver extends CardImpl { } } -class TargetSliverCreatureOrPlayer extends TargetAnyTarget { - - public TargetSliverCreatureOrPlayer() { - super(); - filter = new FilterCreatureOrPlayerByType("Sliver", "Sliver creature or player"); - } -} - class FilterCreatureOrPlayerByType extends FilterCreaturePlayerOrPlaneswalker { - public FilterCreatureOrPlayerByType(String type, String name) { + public FilterCreatureOrPlayerByType(SubType subType, String name) { super(name); - creatureFilter = new FilterCreaturePermanent(type); + this.getPermanentFilter().add(subType.getPredicate()); } } diff --git a/Mage.Sets/src/mage/cards/c/ConeOfFlame.java b/Mage.Sets/src/mage/cards/c/ConeOfFlame.java index 3678ef1bc71..12cef95c651 100644 --- a/Mage.Sets/src/mage/cards/c/ConeOfFlame.java +++ b/Mage.Sets/src/mage/cards/c/ConeOfFlame.java @@ -24,24 +24,23 @@ public final class ConeOfFlame extends CardImpl { public ConeOfFlame(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}"); - // Cone of Flame deals 1 damage to any target, 2 damage to another any target, and 3 damage to a third any target. - FilterCreaturePlayerOrPlaneswalker filter1 = new FilterCreaturePlayerOrPlaneswalker("creature, player or planeswalker to deal 1 damage"); + // Cone of Flame deals 1 damage to any target, 2 damage to another target, and 3 damage to a third target. + // 1 + FilterCreaturePlayerOrPlaneswalker filter1 = new FilterCreaturePlayerOrPlaneswalker("any target to deal 1 damage"); TargetAnyTarget target1 = new TargetAnyTarget(1, 1, filter1); target1.setTargetTag(1); this.getSpellAbility().addTarget(target1); - - FilterCreaturePlayerOrPlaneswalker filter2 = new FilterCreaturePlayerOrPlaneswalker("another creature, player or planeswalker to deal 2 damage"); - AnotherTargetPredicate predicate2 = new AnotherTargetPredicate(2); - filter2.getCreatureFilter().add(predicate2); - filter2.getPlayerFilter().add(predicate2); + // 2 + FilterCreaturePlayerOrPlaneswalker filter2 = new FilterCreaturePlayerOrPlaneswalker("another target to deal 2 damage"); + filter2.getPermanentFilter().add(new AnotherTargetPredicate(2)); + filter2.getPlayerFilter().add(new AnotherTargetPredicate(2)); TargetAnyTarget target2 = new TargetAnyTarget(1, 1, filter2); target2.setTargetTag(2); this.getSpellAbility().addTarget(target2); - - FilterCreaturePlayerOrPlaneswalker filter3 = new FilterCreaturePlayerOrPlaneswalker("another creature, player or planeswalker to deal 3 damage"); - AnotherTargetPredicate predicate3 = new AnotherTargetPredicate(3); - filter3.getCreatureFilter().add(predicate3); - filter3.getPlayerFilter().add(predicate3); + // 3 + FilterCreaturePlayerOrPlaneswalker filter3 = new FilterCreaturePlayerOrPlaneswalker("third target to deal 3 damage"); + filter3.getPermanentFilter().add(new AnotherTargetPredicate(3)); + filter3.getPlayerFilter().add(new AnotherTargetPredicate(3)); TargetAnyTarget target3 = new TargetAnyTarget(1, 1, filter3); target3.setTargetTag(3); this.getSpellAbility().addTarget(target3); diff --git a/Mage.Sets/src/mage/cards/d/DrakusethMawOfFlames.java b/Mage.Sets/src/mage/cards/d/DrakusethMawOfFlames.java index 6b33b83dc75..48635637692 100644 --- a/Mage.Sets/src/mage/cards/d/DrakusethMawOfFlames.java +++ b/Mage.Sets/src/mage/cards/d/DrakusethMawOfFlames.java @@ -43,9 +43,8 @@ public final class DrakusethMawOfFlames extends CardImpl { target.setTargetTag(1); ability.addTarget(target); FilterCreaturePlayerOrPlaneswalker filter = new FilterCreaturePlayerOrPlaneswalker("any target"); - filter.getCreatureFilter().add(new AnotherTargetPredicate(2, true)); filter.getPlayerFilter().add(new AnotherTargetPredicate(2, true)); - filter.getPlaneswalkerFilter().add(new AnotherTargetPredicate(2, true)); + filter.getPermanentFilter().add(new AnotherTargetPredicate(2, true)); target = new TargetAnyTarget(0, 2, filter).withChooseHint("to deal 3 damage"); target.setTargetTag(2); ability.addTarget(target); diff --git a/Mage.Sets/src/mage/cards/e/Endure.java b/Mage.Sets/src/mage/cards/e/Endure.java index e6d952e1af9..e1c6b508918 100644 --- a/Mage.Sets/src/mage/cards/e/Endure.java +++ b/Mage.Sets/src/mage/cards/e/Endure.java @@ -1,7 +1,5 @@ - package mage.cards.e; -import java.util.UUID; import mage.abilities.effects.common.PreventAllDamageToAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -10,26 +8,26 @@ import mage.constants.Duration; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePlayerOrPlaneswalker; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class Endure extends CardImpl { - + private static final FilterCreaturePlayerOrPlaneswalker filter = new FilterCreaturePlayerOrPlaneswalker("you and permanents you control"); static { - filter.getCreatureFilter().add(TargetController.YOU.getControllerPredicate()); + filter.getPermanentFilter().add(TargetController.YOU.getControllerPredicate()); filter.getPlayerFilter().add(TargetController.YOU.getPlayerPredicate()); } public Endure(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{W}{W}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}{W}"); // Prevent all damage that would be dealt to you and permanents you control this turn. this.getSpellAbility().addEffect(new PreventAllDamageToAllEffect(Duration.EndOfTurn, filter)); - + } public Endure(final Endure card) { diff --git a/Mage.Sets/src/mage/cards/i/ImperialGunner.java b/Mage.Sets/src/mage/cards/i/ImperialGunner.java index 4fdc7f8c071..10e0b6d1c03 100644 --- a/Mage.Sets/src/mage/cards/i/ImperialGunner.java +++ b/Mage.Sets/src/mage/cards/i/ImperialGunner.java @@ -1,7 +1,5 @@ - package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -16,17 +14,15 @@ import mage.constants.Zone; import mage.filter.common.FilterCreaturePlayerOrPlaneswalker; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author Styxo */ public final class ImperialGunner extends CardImpl { - private static final FilterCreaturePlayerOrPlaneswalker filter = new FilterCreaturePlayerOrPlaneswalker("target player, planeswalker or Starship creature"); - - static { - filter.getCreatureFilter().add(SubType.STARSHIP.getPredicate()); - } + private static final FilterCreaturePlayerOrPlaneswalker filter = new FilterCreaturePlayerOrPlaneswalker( + "target player, planeswalker or Starship creature", SubType.STARSHIP); public ImperialGunner(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); @@ -35,7 +31,7 @@ public final class ImperialGunner extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - // {1},{T}:Imperial Gunner deals 1 damage to target player or Starship creature. + // {1},{T}: Imperial Gunner deals 1 damage to target player or Starship creature. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new ManaCostsImpl("{1}")); ability.addTarget(new TargetAnyTarget(filter)); ability.addCost(new TapSourceCost()); diff --git a/Mage.Sets/src/mage/cards/n/NeedleDrop.java b/Mage.Sets/src/mage/cards/n/NeedleDrop.java index 63a5b8535d6..e06248a3cf5 100644 --- a/Mage.Sets/src/mage/cards/n/NeedleDrop.java +++ b/Mage.Sets/src/mage/cards/n/NeedleDrop.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageItem; import mage.MageObject; import mage.abilities.effects.Effect; @@ -17,18 +15,18 @@ import mage.players.Player; import mage.target.common.TargetAnyTarget; import mage.watchers.common.DamageDoneWatcher; +import java.util.UUID; + /** - * * @author Quercitron */ public final class NeedleDrop extends CardImpl { - private static final FilterCreaturePlayerOrPlaneswalker FILTER = new FilterCreaturePlayerOrPlaneswalker(); + private static final FilterCreaturePlayerOrPlaneswalker filer = new FilterCreaturePlayerOrPlaneswalker(); static { - FILTER.getCreatureFilter().add(new DamagedThisTurnPredicate()); - FILTER.getPlaneswalkerFilter().add(new DamagedThisTurnPredicate()); - FILTER.getPlayerFilter().add(new DamagedThisTurnPredicate()); + filer.getPlayerFilter().add(new DamagedThisTurnPredicate()); + filer.getPermanentFilter().add(new DamagedThisTurnPredicate()); } public NeedleDrop(UUID ownerId, CardSetInfo setInfo) { @@ -38,7 +36,7 @@ public final class NeedleDrop extends CardImpl { Effect effect = new DamageTargetEffect(1); effect.setText("{this} deals 1 damage to any target that was dealt damage this turn"); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetAnyTarget(1, 1, FILTER)); + this.getSpellAbility().addTarget(new TargetAnyTarget(1, 1, filer)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java b/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java index f0849bde2d5..e49404b6c13 100644 --- a/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java +++ b/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java @@ -7,6 +7,7 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.ExileAllEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.cards.*; import mage.constants.*; import mage.filter.FilterCard; @@ -20,27 +21,25 @@ import mage.target.Target; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetCardInHand; import mage.target.common.TargetOpponent; +import mage.target.targetpointer.FixedTarget; import java.util.HashMap; import java.util.Map; import java.util.UUID; -import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; -import mage.target.targetpointer.FixedTarget; /** * @author Will */ public final class NicolBolasGodPharaoh extends CardImpl { - private static final FilterPermanent filter = new FilterNonlandPermanent(); - private static final FilterCreaturePlayerOrPlaneswalker filter2 + private static final FilterCreaturePlayerOrPlaneswalker damageFilter = new FilterCreaturePlayerOrPlaneswalker("opponent, creature an opponent controls, or planeswalker an opponent controls."); + private static final FilterPermanent exileFilter = new FilterNonlandPermanent(); static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - filter2.getPlayerFilter().add(TargetController.OPPONENT.getPlayerPredicate()); - filter2.getCreatureFilter().add(TargetController.OPPONENT.getControllerPredicate()); - filter2.getPlaneswalkerFilter().add(TargetController.OPPONENT.getControllerPredicate()); + damageFilter.getPlayerFilter().add(TargetController.OPPONENT.getPlayerPredicate()); + damageFilter.getPermanentFilter().add(TargetController.OPPONENT.getControllerPredicate()); + exileFilter.add(TargetController.OPPONENT.getControllerPredicate()); } public NicolBolasGodPharaoh(UUID ownerId, CardSetInfo setInfo) { @@ -58,13 +57,13 @@ public final class NicolBolasGodPharaoh extends CardImpl { // +1: Each opponent exiles two cards from their hand. this.addAbility(new LoyaltyAbility(new NicolBolasGodPharaohPlusOneEffect(), 1)); - // -4: Nicol Bolas, God-Pharaoh deals 7 damage to any target. + // -4: Nicol Bolas, God-Pharaoh deals 7 damage to target opponent, creature an opponent controls, or planeswalker an opponent controls. ability = new LoyaltyAbility(new DamageTargetEffect(7), -4); - ability.addTarget(new TargetAnyTarget(filter2)); + ability.addTarget(new TargetAnyTarget(damageFilter)); this.addAbility(ability); // -12: Exile each nonland permanent your opponents control. - this.addAbility(new LoyaltyAbility(new ExileAllEffect(filter) + this.addAbility(new LoyaltyAbility(new ExileAllEffect(exileFilter) .setText("exile each nonland permanent your opponents control"), -12)); } @@ -168,7 +167,7 @@ class NicolBolasGodPharaohPlusTwoEffect extends OneShotEffect { if (card.isLand()) { continue; } - ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.EndOfTurn, true); + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.EndOfTurn, true); effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); break; diff --git a/Mage.Sets/src/mage/cards/t/TwinBolt.java b/Mage.Sets/src/mage/cards/t/TwinBolt.java index 83ff55ae3d6..88ae84774c4 100644 --- a/Mage.Sets/src/mage/cards/t/TwinBolt.java +++ b/Mage.Sets/src/mage/cards/t/TwinBolt.java @@ -1,7 +1,5 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; @@ -9,21 +7,21 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetAnyTargetAmount; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TwinBolt extends CardImpl { public TwinBolt(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); // Twin Bolt deals 2 damage divided as you choose among one or two target creatures and/or players. Effect effect = new DamageMultiEffect(2); effect.setText("{this} deals 2 damage divided as you choose among one or two target creatures and/or players"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); - } public TwinBolt(final TwinBolt card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/InfernoTitanTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/InfernoTitanTest.java new file mode 100644 index 00000000000..04f56469e42 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/InfernoTitanTest.java @@ -0,0 +1,39 @@ +package org.mage.test.cards.single.m11; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class InfernoTitanTest extends CardTestPlayerBase { + + @Test + public void test_MustAbleToTargetPlaneswalkers() { + // bug: https://github.com/magefree/mage/issues/7276 + + // Whenever Inferno Titan enters the battlefield or attacks, it deals 3 damage divided as you choose among one, two, or three targets. + addCard(Zone.HAND, playerA, "Inferno Titan"); // {4}{R}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + // + addCard(Zone.BATTLEFIELD, playerA, "Chandra, Acolyte of Flame", 1); // 4 + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); // 2/2 + + // cast and devide damage (2x to creature and 1x to planeswalker) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Inferno Titan"); + addTargetAmount(playerA, "Grizzly Bears", 2); + addTargetAmount(playerA, "Chandra, Acolyte of Flame", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Inferno Titan", 1); + assertGraveyardCount(playerA, "Grizzly Bears", 1); + assertCounterCount(playerA, "Chandra, Acolyte of Flame", CounterType.LOYALTY, 4 - 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/FlamebladeAngelTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/FlamebladeAngelTest.java index fdf74c8533d..58cd7369e54 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/FlamebladeAngelTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/soi/FlamebladeAngelTest.java @@ -8,61 +8,61 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * 4RR - * Creature - Angel - * Flying - * - * Whenever a source an opponent controls deals damage to you or a permanent you control, - * you may have Flameblade Angel deal 1 damage to that source's controller. - * + * 4RR + * Creature - Angel + * Flying + *

+ * Whenever a source an opponent controls deals damage to you or a permanent you control, + * you may have Flameblade Angel deal 1 damage to that source's controller. + * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public class FlamebladeAngelTest extends CardTestPlayerBase { - + /** * Reported bug: Not triggering when damage is dealt to the creatures I control. */ @Test public void testDamageToCreature() { - + addCard(Zone.BATTLEFIELD, playerA, "Flameblade Angel"); addCard(Zone.BATTLEFIELD, playerA, "Wall of Roots"); // 0/5 addCard(Zone.HAND, playerB, "Shock"); // instant deals 2 dmg to creature/player addCard(Zone.BATTLEFIELD, playerB, "Mountain"); - + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock"); addTarget(playerB, "Wall of Roots"); - + setStopAt(2, PhaseStep.BEGIN_COMBAT); execute(); - + Permanent roots = getPermanent("Wall of Roots", playerA); Assert.assertEquals("Wall of Roots should have 2 damage dealt to it", 2, roots.getDamage()); assertGraveyardCount(playerB, "Shock", 1); assertLife(playerA, 20); assertLife(playerB, 19); // Angel should deal 1 damage to Shock's controller } - + /** * Reported bug: Not triggering when damage is dealt to the creatures I control. */ @Test public void testDamageToMultipleCreatures() { - + addCard(Zone.BATTLEFIELD, playerA, "Flameblade Angel"); addCard(Zone.BATTLEFIELD, playerA, "Wall of Roots"); // 0/5 addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); // 2/2 addCard(Zone.HAND, playerB, "Shock", 2); // instant deals 2 dmg to creature/player addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2); - + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock"); addTarget(playerB, "Wall of Roots"); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock"); addTarget(playerB, "Grizzly Bears"); - + setStopAt(2, PhaseStep.BEGIN_COMBAT); execute(); - + Permanent roots = getPermanent("Wall of Roots", playerA); Assert.assertEquals("Wall of Roots should have 2 damage dealt to it", 2, roots.getDamage()); assertGraveyardCount(playerB, "Shock", 2); @@ -70,27 +70,27 @@ public class FlamebladeAngelTest extends CardTestPlayerBase { assertLife(playerA, 20); assertLife(playerB, 18); // Angel should deal 1 damage twice to Shock's controller } - + /** * Reported bug: Not triggering when damage is dealt to the creatures I control. */ @Test public void testCombatDamageToCreatures() { - + addCard(Zone.BATTLEFIELD, playerA, "Flameblade Angel"); addCard(Zone.BATTLEFIELD, playerA, "Wall of Roots"); // 0/5 addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); // 2/2 addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard"); // 2/1 addCard(Zone.BATTLEFIELD, playerB, "Hill Giant"); // 3/3 - + attack(2, playerB, "Elite Vanguard"); attack(2, playerB, "Hill Giant"); block(2, playerA, "Wall of Roots", "Hill Giant"); block(2, playerA, "Grizzly Bears", "Elite Vanguard"); - + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); - + Permanent roots = getPermanent("Wall of Roots", playerA); Assert.assertEquals("Wall of Roots should have 3 damage dealt to it", 3, roots.getDamage()); assertGraveyardCount(playerA, "Grizzly Bears", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 8471bdbb594..e34fcf16490 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -2281,9 +2281,6 @@ public class TestPlayer implements Player { if (filter instanceof FilterCreatureOrPlayer) { filter = ((FilterCreatureOrPlayer) filter).getCreatureFilter(); } - if (filter instanceof FilterCreaturePlayerOrPlaneswalker) { - filter = ((FilterCreaturePlayerOrPlaneswalker) filter).getCreatureFilter(); - } if (filter instanceof FilterPermanentOrPlayer) { filter = ((FilterPermanentOrPlayer) filter).getPermanentFilter(); } diff --git a/Mage/src/main/java/mage/filter/common/FilterCreaturePlayerOrPlaneswalker.java b/Mage/src/main/java/mage/filter/common/FilterCreaturePlayerOrPlaneswalker.java index 999d4b98e65..92d5ddf9597 100644 --- a/Mage/src/main/java/mage/filter/common/FilterCreaturePlayerOrPlaneswalker.java +++ b/Mage/src/main/java/mage/filter/common/FilterCreaturePlayerOrPlaneswalker.java @@ -1,72 +1,48 @@ package mage.filter.common; -import mage.MageItem; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.MageObject; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; -import java.util.UUID; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; /** + * If you add predicate to permanentFilter then it will be applied to planeswalker too + * * @author JRHerlehy Created on 4/8/18. */ public class FilterCreaturePlayerOrPlaneswalker extends FilterPermanentOrPlayer { - protected FilterCreaturePermanent creatureFilter; - protected FilterPlaneswalkerPermanent planeswalkerFilter; - public FilterCreaturePlayerOrPlaneswalker() { this("any target"); } public FilterCreaturePlayerOrPlaneswalker(String name) { + this(name, (SubType) null); + } + + public FilterCreaturePlayerOrPlaneswalker(String name, SubType... andCreatureTypes) { super(name); - creatureFilter = new FilterCreaturePermanent(); - planeswalkerFilter = new FilterPlaneswalkerPermanent(); + List> allCreaturePredicates = Arrays.stream(andCreatureTypes) + .filter(Objects::nonNull) + .map(SubType::getPredicate) + .collect(Collectors.toList()); + allCreaturePredicates.add(0, CardType.CREATURE.getPredicate()); + Predicate planeswalkerPredicate = CardType.PLANESWALKER.getPredicate(); + + this.permanentFilter.add(Predicates.or( + Predicates.and(allCreaturePredicates), + planeswalkerPredicate + )); } public FilterCreaturePlayerOrPlaneswalker(final FilterCreaturePlayerOrPlaneswalker filter) { super(filter); - this.creatureFilter = filter.creatureFilter.copy(); - this.planeswalkerFilter = filter.planeswalkerFilter.copy(); - } - - @Override - public boolean match(MageItem o, Game game) { - if (super.match(o, game)) { - if (o instanceof Player) { - return playerFilter.match((Player) o, game); - } else if (o instanceof Permanent) { - return creatureFilter.match((Permanent) o, game) - || planeswalkerFilter.match((Permanent) o, game); - } - } - return false; - } - - @Override - public boolean match(MageItem o, UUID sourceId, UUID playerId, Game game) { - if (super.match(o, game)) { // process predicates - if (o instanceof Player) { - return playerFilter.match((Player) o, sourceId, playerId, game); - } else if (o instanceof Permanent) { - return creatureFilter.match((Permanent) o, sourceId, playerId, game) - || planeswalkerFilter.match((Permanent) o, sourceId, playerId, game); - } - } - return false; - } - - public FilterCreaturePermanent getCreatureFilter() { - return this.creatureFilter; - } - - public FilterPlaneswalkerPermanent getPlaneswalkerFilter() { - return this.planeswalkerFilter; - } - - public void setCreatureFilter(FilterCreaturePermanent creatureFilter) { - this.creatureFilter = creatureFilter; } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetAnyTarget.java b/Mage/src/main/java/mage/target/common/TargetAnyTarget.java index f2fa5459b3d..f5f9f9393d5 100644 --- a/Mage/src/main/java/mage/target/common/TargetAnyTarget.java +++ b/Mage/src/main/java/mage/target/common/TargetAnyTarget.java @@ -116,7 +116,7 @@ public class TargetAnyTarget extends TargetImpl { } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getCreatureFilter(), sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) { if (permanent.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(permanent, sourceId, sourceControllerId, game)) { count++; if (count >= this.minNumberOfTargets) { @@ -125,15 +125,6 @@ public class TargetAnyTarget extends TargetImpl { } } - for (Permanent planeswalker : game.getBattlefield().getActivePermanents(filter.getPlaneswalkerFilter(), sourceControllerId, game)) { - if (planeswalker.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(planeswalker, sourceId, sourceControllerId, game)) { - count++; - if (count >= this.minNumberOfTargets) { - return true; - } - } - } - return false; } @@ -160,7 +151,7 @@ public class TargetAnyTarget extends TargetImpl { } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getCreatureFilter(), sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) { if (filter.match(permanent, null, sourceControllerId, game)) { count++; if (count >= this.minNumberOfTargets) { @@ -169,15 +160,6 @@ public class TargetAnyTarget extends TargetImpl { } } - for (Permanent planeswalker : game.getBattlefield().getActivePermanents(filter.getPlaneswalkerFilter(), sourceControllerId, game)) { - if (filter.match(planeswalker, null, sourceControllerId, game)) { - count++; - if (count >= this.minNumberOfTargets) { - return true; - } - } - } - return false; } @@ -190,25 +172,18 @@ public class TargetAnyTarget extends TargetImpl { Player player = game.getPlayer(playerId); if (player != null && player.canBeTargetedBy(targetSource, sourceControllerId, game) - && filter.getPlayerFilter().match(player, sourceId, sourceControllerId, game)) { + && filter.match(player, sourceId, sourceControllerId, game)) { possibleTargets.add(playerId); } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getCreatureFilter(), sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) { if (permanent.canBeTargetedBy(targetSource, sourceControllerId, game) - && filter.getCreatureFilter().match(permanent, sourceId, sourceControllerId, game)) { + && filter.match(permanent, sourceId, sourceControllerId, game)) { possibleTargets.add(permanent.getId()); } } - for (Permanent planeswalker : game.getBattlefield().getActivePermanents(filter.getPlaneswalkerFilter(), sourceControllerId, game)) { - if (planeswalker.canBeTargetedBy(targetSource, sourceControllerId, game) - && filter.getPlaneswalkerFilter().match(planeswalker, sourceId, sourceControllerId, game)) { - possibleTargets.add(planeswalker.getId()); - } - } - return possibleTargets; } @@ -218,23 +193,17 @@ public class TargetAnyTarget extends TargetImpl { for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null && filter.getPlayerFilter().match(player, game)) { + if (player != null && filter.match(player, game)) { possibleTargets.add(playerId); } } - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getCreatureFilter(), sourceControllerId, game)) { - if (filter.getCreatureFilter().match(permanent, null, sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) { + if (filter.getPermanentFilter().match(permanent, null, sourceControllerId, game)) { possibleTargets.add(permanent.getId()); } } - for (Permanent planeswalker : game.getBattlefield().getActivePermanents(filter.getPlaneswalkerFilter(), sourceControllerId, game)) { - if (filter.getPlaneswalkerFilter().match(planeswalker, null, sourceControllerId, game)) { - possibleTargets.add(planeswalker.getId()); - } - } - return possibleTargets; } diff --git a/Mage/src/main/java/mage/target/common/TargetAnyTargetAmount.java b/Mage/src/main/java/mage/target/common/TargetAnyTargetAmount.java index 6c9acb06d08..62270edd244 100644 --- a/Mage/src/main/java/mage/target/common/TargetAnyTargetAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetAnyTargetAmount.java @@ -2,23 +2,17 @@ package mage.target.common; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.constants.CardType; import mage.constants.Zone; import mage.filter.common.FilterCreaturePlayerOrPlaneswalker; -import mage.filter.common.FilterPermanentOrPlayer; /** * @author BetaSteward_at_googlemail.com */ public class TargetAnyTargetAmount extends TargetPermanentOrPlayerAmount { - private static final FilterPermanentOrPlayer defaultFilter + private static final FilterCreaturePlayerOrPlaneswalker defaultFilter = new FilterCreaturePlayerOrPlaneswalker("targets"); - static { - defaultFilter.getPermanentFilter().add(CardType.CREATURE.getPredicate()); - } - public TargetAnyTargetAmount(int amount) { this(amount, 0); } diff --git a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java index 9525c28e7ab..a0308c2915d 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java @@ -1,6 +1,5 @@ package mage.target.common; -import mage.abilities.dynamicvalue.DynamicValue; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; @@ -16,18 +15,10 @@ public class TargetCreatureOrPlaneswalkerAmount extends TargetPermanentAmount { super(amount, defaultFilter); } - public TargetCreatureOrPlaneswalkerAmount(DynamicValue amount) { - super(amount, defaultFilter); - } - public TargetCreatureOrPlaneswalkerAmount(int amount, FilterPermanent filter) { super(amount, filter); } - public TargetCreatureOrPlaneswalkerAmount(DynamicValue amount, FilterPermanent filter) { - super(amount, filter); - } - private TargetCreatureOrPlaneswalkerAmount(final TargetCreatureOrPlaneswalkerAmount target) { super(target); }