diff --git a/Mage.Sets/src/mage/cards/k/KathrilAspectWarper.java b/Mage.Sets/src/mage/cards/k/KathrilAspectWarper.java index ae76f96d6a0..342bfe0df74 100644 --- a/Mage.Sets/src/mage/cards/k/KathrilAspectWarper.java +++ b/Mage.Sets/src/mage/cards/k/KathrilAspectWarper.java @@ -137,7 +137,7 @@ class KathrilAspectWarperEffect extends OneShotEffect { if (ability instanceof DeathtouchAbility) { return CounterType.DEATHTOUCH; } - if (ability instanceof HexproofInterface) { + if (ability instanceof HexproofBaseAbility) { return CounterType.HEXPROOF; } if (ability instanceof IndestructibleAbility) { diff --git a/Mage.Sets/src/mage/cards/m/MajesticMyriarch.java b/Mage.Sets/src/mage/cards/m/MajesticMyriarch.java index 0a102ab4f4a..847eb1ed20b 100644 --- a/Mage.Sets/src/mage/cards/m/MajesticMyriarch.java +++ b/Mage.Sets/src/mage/cards/m/MajesticMyriarch.java @@ -75,7 +75,7 @@ class MajesticMyriarchEffect extends OneShotEffect { filterDeathtouch.add(new AbilityPredicate(DeathtouchAbility.class)); filterDoubleStrike.add(new AbilityPredicate(DoubleStrikeAbility.class)); filterHaste.add(new AbilityPredicate(HasteAbility.class)); - filterHexproof.add(new AbilityPredicate(HexproofInterface.class)); + filterHexproof.add(new AbilityPredicate(HexproofBaseAbility.class)); filterIndestructible.add(new AbilityPredicate(IndestructibleAbility.class)); filterLifelink.add(new AbilityPredicate(LifelinkAbility.class)); filterMenace.add(new AbilityPredicate(MenaceAbility.class)); diff --git a/Mage.Sets/src/mage/cards/o/OdricLunarchMarshal.java b/Mage.Sets/src/mage/cards/o/OdricLunarchMarshal.java index 60e9cd9be7b..bd0a2c6af49 100644 --- a/Mage.Sets/src/mage/cards/o/OdricLunarchMarshal.java +++ b/Mage.Sets/src/mage/cards/o/OdricLunarchMarshal.java @@ -66,7 +66,7 @@ class OdricLunarchMarshalEffect extends OneShotEffect { filterDeathtouch.add(new AbilityPredicate(DeathtouchAbility.class)); filterDoubleStrike.add(new AbilityPredicate(DoubleStrikeAbility.class)); filterHaste.add(new AbilityPredicate(HasteAbility.class)); - filterHexproof.add(new AbilityPredicate(HexproofInterface.class)); + filterHexproof.add(new AbilityPredicate(HexproofBaseAbility.class)); filterIndestructible.add(new AbilityPredicate(IndestructibleAbility.class)); filterLifelink.add(new AbilityPredicate(LifelinkAbility.class)); filterMenace.add(new AbilityPredicate(MenaceAbility.class)); diff --git a/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java b/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java index e87a2f033af..b030d577dbf 100644 --- a/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java +++ b/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java @@ -85,7 +85,7 @@ class RayamiFirstOfTheFallenEffect extends ContinuousEffectImpl { || ability instanceof DoubleStrikeAbility || ability instanceof DeathtouchAbility || ability instanceof HasteAbility - || ability instanceof HexproofInterface + || ability instanceof HexproofBaseAbility || ability instanceof IndestructibleAbility || ability instanceof LifelinkAbility || ability instanceof MenaceAbility diff --git a/Mage.Sets/src/mage/cards/s/SelectiveAdaptation.java b/Mage.Sets/src/mage/cards/s/SelectiveAdaptation.java index de47de808fc..f5818b5067d 100644 --- a/Mage.Sets/src/mage/cards/s/SelectiveAdaptation.java +++ b/Mage.Sets/src/mage/cards/s/SelectiveAdaptation.java @@ -49,7 +49,7 @@ class SelectiveAdaptationEffect extends OneShotEffect { DOUBLE_STRIKE(DoubleStrikeAbility.class, "double strike"), DEATHTOUCH(DeathtouchAbility.class, "deathtouch"), HASTE(HasteAbility.class, "haste"), - HEXPROOF(HexproofInterface.class, "hexproof"), + HEXPROOF(HexproofBaseAbility.class, "hexproof"), INDESTRUCTIBLE(IndestructibleAbility.class, "indestructible"), LIFELINK(LifelinkAbility.class, "lifelink"), MENACE(MenaceAbility.class, "menace"), diff --git a/Mage.Sets/src/mage/cards/s/Soulflayer.java b/Mage.Sets/src/mage/cards/s/Soulflayer.java index a86bda44c31..5e5b152ab1d 100644 --- a/Mage.Sets/src/mage/cards/s/Soulflayer.java +++ b/Mage.Sets/src/mage/cards/s/Soulflayer.java @@ -107,7 +107,7 @@ class SoulflayerEffect extends ContinuousEffectImpl implements SourceEffect { if (cardAbility instanceof HasteAbility) { abilitiesToAdd.add(HasteAbility.getInstance()); } - if (cardAbility instanceof HexproofInterface) { + if (cardAbility instanceof HexproofBaseAbility) { abilitiesToAdd.add(HexproofAbility.getInstance()); } if (cardAbility instanceof IndestructibleAbility) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java index 668479559ab..c5fc534f911 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java @@ -9,7 +9,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class HexproofTest extends CardTestPlayerBase { @@ -37,6 +36,7 @@ public class HexproofTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Elder of Laurels", 3, 4); assertAbility(playerA, "Elder of Laurels", HexproofAbility.getInstance(), true); } + /** * Tests one target gets hexproof */ @@ -64,4 +64,42 @@ public class HexproofTest extends CardTestPlayerBase { assertAbility(playerA, "Elder of Laurels", HexproofAbility.getInstance(), true); assertPermanentCount(playerA, "Arbor Elf", 0); } + + /** + * Tests hexproof from a color with opponent's spells + */ + @Test + public void testHexproofFromColorOpponentSpells() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + addCard(Zone.HAND, playerA, "Murder", 2); + + addCard(Zone.BATTLEFIELD, playerB, "Knight of Grace"); + addCard(Zone.BATTLEFIELD, playerB, "Knight of Malice"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder", "Knight of Grace"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder", "Knight of Malice"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Knight of Grace", 1); + assertPermanentCount(playerB, "Knight of Malice", 0); + } + + /** + * Tests hexproof from a color with controller's spells + */ + @Test + public void testHexproofFromColorOwnSpells() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, "Knight of Grace"); + addCard(Zone.HAND, playerA, "Murder"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder", "Knight of Grace"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Knight of Grace", 0); + } } diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofAbility.java index ae848c54378..eb42f64c80f 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofAbility.java @@ -1,8 +1,7 @@ package mage.abilities.keyword; -import mage.abilities.MageSingleton; -import mage.abilities.common.SimpleStaticAbility; -import mage.constants.Zone; +import mage.MageObject; +import mage.game.Game; import java.io.ObjectStreamException; @@ -12,7 +11,7 @@ import java.io.ObjectStreamException; * * @author loki */ -public class HexproofAbility extends SimpleStaticAbility implements MageSingleton, HexproofInterface { +public class HexproofAbility extends HexproofBaseAbility { private static final HexproofAbility instance; @@ -29,7 +28,12 @@ public class HexproofAbility extends SimpleStaticAbility implements MageSingleto } private HexproofAbility() { - super(Zone.BATTLEFIELD, null); + super(); + } + + @Override + public boolean checkObject(MageObject source, Game game) { + return true; } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofBaseAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofBaseAbility.java new file mode 100644 index 00000000000..539ca5d5135 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofBaseAbility.java @@ -0,0 +1,21 @@ +package mage.abilities.keyword; + +import mage.MageObject; +import mage.abilities.MageSingleton; +import mage.abilities.common.SimpleStaticAbility; +import mage.constants.Zone; +import mage.game.Game; + +/** + * an abstract base class for hexproof abilities + * + * @author TheElk801 + */ +public abstract class HexproofBaseAbility extends SimpleStaticAbility implements MageSingleton { + + HexproofBaseAbility() { + super(Zone.BATTLEFIELD, null); + } + + public abstract boolean checkObject(MageObject source, Game game); +} diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlackAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlackAbility.java index 40042fca854..82f2275563d 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlackAbility.java @@ -1,8 +1,7 @@ package mage.abilities.keyword; -import mage.abilities.MageSingleton; -import mage.abilities.common.SimpleStaticAbility; -import mage.constants.Zone; +import mage.MageObject; +import mage.game.Game; import java.io.ObjectStreamException; @@ -12,7 +11,7 @@ import java.io.ObjectStreamException; * * @author igoudt */ -public class HexproofFromBlackAbility extends SimpleStaticAbility implements MageSingleton, HexproofInterface { +public class HexproofFromBlackAbility extends HexproofBaseAbility { private static final HexproofFromBlackAbility instance; @@ -29,7 +28,12 @@ public class HexproofFromBlackAbility extends SimpleStaticAbility implements Mag } private HexproofFromBlackAbility() { - super(Zone.BATTLEFIELD, null); + super(); + } + + @Override + public boolean checkObject(MageObject source, Game game) { + return source.getColor(game).isBlack(); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlueAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlueAbility.java index 9e80dc0b318..ac7317f0253 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlueAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofFromBlueAbility.java @@ -1,8 +1,7 @@ package mage.abilities.keyword; -import mage.abilities.MageSingleton; -import mage.abilities.common.SimpleStaticAbility; -import mage.constants.Zone; +import mage.MageObject; +import mage.game.Game; import java.io.ObjectStreamException; @@ -12,7 +11,7 @@ import java.io.ObjectStreamException; * * @author igoudt */ -public class HexproofFromBlueAbility extends SimpleStaticAbility implements MageSingleton, HexproofInterface { +public class HexproofFromBlueAbility extends HexproofBaseAbility { private static final HexproofFromBlueAbility instance; @@ -29,7 +28,12 @@ public class HexproofFromBlueAbility extends SimpleStaticAbility implements Mage } private HexproofFromBlueAbility() { - super(Zone.BATTLEFIELD, null); + super(); + } + + @Override + public boolean checkObject(MageObject source, Game game) { + return source.getColor(game).isBlue(); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofFromMonocoloredAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofFromMonocoloredAbility.java index c41d72297b3..27a711692a6 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofFromMonocoloredAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofFromMonocoloredAbility.java @@ -1,8 +1,7 @@ package mage.abilities.keyword; -import mage.abilities.MageSingleton; -import mage.abilities.common.SimpleStaticAbility; -import mage.constants.Zone; +import mage.MageObject; +import mage.game.Game; import java.io.ObjectStreamException; @@ -12,7 +11,7 @@ import java.io.ObjectStreamException; * * @author TheElk801 */ -public class HexproofFromMonocoloredAbility extends SimpleStaticAbility implements MageSingleton, HexproofInterface { +public class HexproofFromMonocoloredAbility extends HexproofBaseAbility { private static final HexproofFromMonocoloredAbility instance; @@ -29,7 +28,12 @@ public class HexproofFromMonocoloredAbility extends SimpleStaticAbility implemen } private HexproofFromMonocoloredAbility() { - super(Zone.BATTLEFIELD, null); + super(); + } + + @Override + public boolean checkObject(MageObject source, Game game) { + return !source.getColor(game).isMulticolored() && !source.getColor(game).isColorless(); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofFromWhiteAbility.java b/Mage/src/main/java/mage/abilities/keyword/HexproofFromWhiteAbility.java index 771df7eefc2..ec1d60a0206 100644 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofFromWhiteAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/HexproofFromWhiteAbility.java @@ -1,8 +1,7 @@ package mage.abilities.keyword; -import mage.abilities.MageSingleton; -import mage.abilities.common.SimpleStaticAbility; -import mage.constants.Zone; +import mage.MageObject; +import mage.game.Game; import java.io.ObjectStreamException; @@ -12,7 +11,7 @@ import java.io.ObjectStreamException; * * @author igoudt */ -public class HexproofFromWhiteAbility extends SimpleStaticAbility implements MageSingleton, HexproofInterface { +public class HexproofFromWhiteAbility extends HexproofBaseAbility { private static final HexproofFromWhiteAbility instance; @@ -29,7 +28,12 @@ public class HexproofFromWhiteAbility extends SimpleStaticAbility implements Mag } private HexproofFromWhiteAbility() { - super(Zone.BATTLEFIELD, null); + super(); + } + + @Override + public boolean checkObject(MageObject source, Game game) { + return source.getColor(game).isWhite(); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/HexproofInterface.java b/Mage/src/main/java/mage/abilities/keyword/HexproofInterface.java deleted file mode 100644 index f86b09d558a..00000000000 --- a/Mage/src/main/java/mage/abilities/keyword/HexproofInterface.java +++ /dev/null @@ -1,9 +0,0 @@ -package mage.abilities.keyword; - -/** - * an interface for hexproof abilities - * - * @author TheElk801 - */ -public interface HexproofInterface { -} diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/AbilityPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/AbilityPredicate.java index cbd1c579d6b..23baecae34b 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/AbilityPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/AbilityPredicate.java @@ -1,4 +1,3 @@ - package mage.filter.predicate.mageobject; import mage.MageObject; @@ -13,9 +12,9 @@ import mage.game.Game; */ public class AbilityPredicate implements Predicate { - private final Class abilityClass; + private final Class abilityClass; - public AbilityPredicate(Class abilityClass) { + public AbilityPredicate(Class abilityClass) { this.abilityClass = abilityClass; } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 16073d3c834..cee725a145d 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -29,7 +29,6 @@ import mage.game.command.CommandObject; import mage.game.events.*; import mage.game.events.GameEvent.EventType; import mage.game.permanent.token.SquirrelToken; -import mage.game.permanent.token.Token; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; @@ -1062,43 +1061,13 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return false; } } - if (abilities.containsKey(HexproofAbility.getInstance().getId())) { - if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)) { - return false; - } - } - - if (abilities.containsKey(HexproofFromWhiteAbility.getInstance().getId())) { - if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isWhite()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromBlueAbility.getInstance().getId())) { - if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isBlue()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromBlackAbility.getInstance().getId())) { - if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isBlack()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromMonocoloredAbility.getInstance().getId())) { - if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && !source.getColor(game).isColorless() && !source.getColor(game).isMulticolored()) { - return false; - } + if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game) + && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null + && abilities.stream() + .filter(HexproofBaseAbility.class::isInstance) + .map(HexproofBaseAbility.class::cast) + .anyMatch(ability -> ability.checkObject(source, game))) { + return false; } if (hasProtectionFrom(source, game)) { diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index aa67d35a624..75fc352ec58 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -610,50 +610,14 @@ public abstract class PlayerImpl implements Player, Serializable { if (abilities.containsKey(ShroudAbility.getInstance().getId())) { return false; } - - if (abilities.containsKey(HexproofAbility.getInstance().getId())) { - if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), - AsThoughEffectType.HEXPROOF, null, sourceControllerId, game)) { - return false; - } - } - - if (abilities.containsKey(HexproofFromWhiteAbility.getInstance().getId())) { - if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), - AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isWhite()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromBlueAbility.getInstance().getId())) { - if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), - AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isBlue()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromBlackAbility.getInstance().getId())) { - if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), - AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && source.getColor(game).isBlack()) { - return false; - } - } - - if (abilities.containsKey(HexproofFromMonocoloredAbility.getInstance().getId())) { - if (sourceControllerId != null && this.hasOpponent(sourceControllerId, game) - && null == game.getContinuousEffects().asThough(this.getId(), - AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) - && !source.getColor(game).isColorless() - && !source.getColor(game).isMulticolored()) { - return false; - } + if (sourceControllerId != null + && this.hasOpponent(sourceControllerId, game) + && game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null + && abilities.stream() + .filter(HexproofBaseAbility.class::isInstance) + .map(HexproofBaseAbility.class::cast) + .anyMatch(ability -> ability.checkObject(source, game))) { + return false; } return !hasProtectionFrom(source, game);