From 08c7d2ab8f06fe1b19d242e8e4921707f16584c6 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Fri, 7 Jun 2024 12:36:41 -0400 Subject: [PATCH] implement [M3C] Planar Nexus [MH3] Omo, Queen of Vesuva and [UNF] Nearby Planet ; introduce AllNonbasicLandTypes status (#12203) --- Mage.Sets/src/mage/cards/n/NearbyPlanet.java | 75 ++++++++ .../src/mage/cards/o/OmoQueenOfVesuva.java | 161 ++++++++++++++++++ Mage.Sets/src/mage/cards/p/PlanarNexus.java | 75 ++++++++ .../mage/sets/ModernHorizons3Commander.java | 2 + Mage.Sets/src/mage/sets/Unfinity.java | 1 + .../single/m3c/OmoQueenOfVesuvaTest.java | 89 ++++++++++ .../cards/single/m3c/PlanarNexusTest.java | 112 ++++++++++++ Mage/src/main/java/mage/MageObject.java | 19 +++ Mage/src/main/java/mage/MageObjectImpl.java | 22 ++- .../BecomesEnchantmentSourceEffect.java | 1 - .../main/java/mage/counters/CounterType.java | 1 + .../java/mage/designations/Designation.java | 13 ++ .../java/mage/game/command/Commander.java | 15 ++ .../main/java/mage/game/command/Dungeon.java | 13 ++ .../main/java/mage/game/command/Emblem.java | 13 ++ .../main/java/mage/game/command/Plane.java | 13 ++ Mage/src/main/java/mage/game/stack/Spell.java | 15 ++ .../java/mage/game/stack/StackAbility.java | 13 ++ Mage/src/main/java/mage/util/SubTypes.java | 11 ++ 19 files changed, 659 insertions(+), 5 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/n/NearbyPlanet.java create mode 100644 Mage.Sets/src/mage/cards/o/OmoQueenOfVesuva.java create mode 100644 Mage.Sets/src/mage/cards/p/PlanarNexus.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/m3c/OmoQueenOfVesuvaTest.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/m3c/PlanarNexusTest.java diff --git a/Mage.Sets/src/mage/cards/n/NearbyPlanet.java b/Mage.Sets/src/mage/cards/n/NearbyPlanet.java new file mode 100644 index 00000000000..b50a41cb84c --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NearbyPlanet.java @@ -0,0 +1,75 @@ +package mage.cards.n; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NearbyPlanet extends CardImpl { + + public NearbyPlanet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Rangeling + this.addAbility(new SimpleStaticAbility(Zone.ALL, new NearbyPlanetEffect())); + + // Nearby Planet enters the battlefield tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // When Nearby Planet enters the battlefield, sacrifice it unless you pay {1}. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new SacrificeSourceUnlessPaysEffect(new GenericManaCost(1)).setText("sacrifice it unless you pay {1}") + )); + } + + private NearbyPlanet(final NearbyPlanet card) { + super(card); + } + + @Override + public NearbyPlanet copy() { + return new NearbyPlanet(this); + } +} + +class NearbyPlanetEffect extends ContinuousEffectImpl { + + NearbyPlanetEffect() { + super(Duration.Custom, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); + staticText = "Rangeling (This card is every land type, including Plains, Island, Swamp, " + + "Mountain, Forest, Desert, Gate, Lair, Locus, and all those Urza's ones.)."; + } + + private NearbyPlanetEffect(final NearbyPlanetEffect effect) { + super(effect); + } + + @Override + public NearbyPlanetEffect copy() { + return new NearbyPlanetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject sourceObject = game.getObject(source); + if (sourceObject == null) { + return false; + } + sourceObject.addSubType(game, SubType.PLAINS, SubType.ISLAND, SubType.SWAMP, SubType.MOUNTAIN, SubType.FOREST); + sourceObject.setIsAllNonbasicLandTypes(game, true); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OmoQueenOfVesuva.java b/Mage.Sets/src/mage/cards/o/OmoQueenOfVesuva.java new file mode 100644 index 00000000000..5aa0161315d --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OmoQueenOfVesuva.java @@ -0,0 +1,161 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.mana.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetLandPermanent; +import mage.target.targetpointer.EachTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OmoQueenOfVesuva extends CardImpl { + + public OmoQueenOfVesuva(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G/U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SHAPESHIFTER); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(1); + this.toughness = new MageInt(5); + + // Whenever Omo, Queen of Vesuva enters the battlefield or attacks, put an everything counter on each of up to one target land and up to one target creature. + Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility( + new AddCountersTargetEffect(CounterType.EVERYTHING.createInstance()) + .setTargetPointer(new EachTargetPointer()) + .setText("put an everything counter on each of up to one target land and up to one target creature") + ); + ability.addTarget(new TargetLandPermanent(0, 1)); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + + // Each land with an everything counter on it is every land type in addition to its other types. + this.addAbility(new SimpleStaticAbility(new OmoQueenOfVesuvaLandEffect())); + + // Each nonland creature with an everything counter on it is every creature type. + this.addAbility(new SimpleStaticAbility(new OmoQueenOfVesuvaCreatureEffect())); + } + + private OmoQueenOfVesuva(final OmoQueenOfVesuva card) { + super(card); + } + + @Override + public OmoQueenOfVesuva copy() { + return new OmoQueenOfVesuva(this); + } +} + +class OmoQueenOfVesuvaLandEffect extends ContinuousEffectImpl { + + private static final Ability[] basicManaAbilities = { + new WhiteManaAbility(), + new BlueManaAbility(), + new BlackManaAbility(), + new RedManaAbility(), + new GreenManaAbility() + }; + private static final FilterPermanent filter = new FilterLandPermanent(); + + static { + filter.add(CounterType.EVERYTHING.getPredicate()); + } + + public OmoQueenOfVesuvaLandEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); + this.staticText = "each land with an everything counter on it is every land type in addition to its other types"; + dependencyTypes.add(DependencyType.BecomeMountain); + dependencyTypes.add(DependencyType.BecomeForest); + dependencyTypes.add(DependencyType.BecomeSwamp); + dependencyTypes.add(DependencyType.BecomeIsland); + dependencyTypes.add(DependencyType.BecomePlains); + } + + private OmoQueenOfVesuvaLandEffect(final OmoQueenOfVesuvaLandEffect effect) { + super(effect); + } + + @Override + public OmoQueenOfVesuvaLandEffect copy() { + return new OmoQueenOfVesuvaLandEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { + permanent.addSubType( + game, + SubType.PLAINS, + SubType.ISLAND, + SubType.SWAMP, + SubType.MOUNTAIN, + SubType.FOREST + ); + permanent.setIsAllNonbasicLandTypes(game, true); + // Optimization: Remove basic mana abilities since they are redundant with AnyColorManaAbility + // and keeping them will only produce too many combinations inside ManaOptions + for (Ability basicManaAbility : basicManaAbilities) { + if (permanent.getAbilities(game).containsRule(basicManaAbility)) { + permanent.removeAbility(basicManaAbility, source.getSourceId(), game); + } + } + // Add the {T}: Add one mana of any color ability + // This is functionally equivalent to having five "{T}: Add {COLOR}" for each COLOR in {W}{U}{B}{R}{G} + AnyColorManaAbility ability = new AnyColorManaAbility(); + if (!permanent.getAbilities(game).containsRule(ability)) { + permanent.addAbility(ability, source.getSourceId(), game); + } + } + return true; + } +} + +class OmoQueenOfVesuvaCreatureEffect extends ContinuousEffectImpl { + + private static final FilterPermanent filter = new FilterNonlandPermanent(); + + static { + filter.add(CardType.CREATURE.getPredicate()); + filter.add(CounterType.EVERYTHING.getPredicate()); + } + + public OmoQueenOfVesuvaCreatureEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); + this.staticText = "each nonland creature with an everything counter on it is every creature type"; + dependencyTypes.add(DependencyType.AddingCreatureType); + } + + private OmoQueenOfVesuvaCreatureEffect(final OmoQueenOfVesuvaCreatureEffect effect) { + super(effect); + } + + @Override + public OmoQueenOfVesuvaCreatureEffect copy() { + return new OmoQueenOfVesuvaCreatureEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { + permanent.setIsAllCreatureTypes(game, true); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PlanarNexus.java b/Mage.Sets/src/mage/cards/p/PlanarNexus.java new file mode 100644 index 00000000000..c4dcff83f6c --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PlanarNexus.java @@ -0,0 +1,75 @@ +package mage.cards.p; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.mana.AnyColorManaAbility; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PlanarNexus extends CardImpl { + + public PlanarNexus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Planar Nexus is every nonbasic land type. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new PlanarNexusEffect())); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {1}, {T}: Add one mana of any color. + Ability ability = new AnyColorManaAbility(new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private PlanarNexus(final PlanarNexus card) { + super(card); + } + + @Override + public PlanarNexus copy() { + return new PlanarNexus(this); + } +} + +class PlanarNexusEffect extends ContinuousEffectImpl { + + PlanarNexusEffect() { + super(Duration.Custom, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); + staticText = "{this} is every nonbasic land type. " + + "(Nonbasic land types include Cave, Desert, Gate, Lair, " + + "Locus, Mine, Power-Plant, Sphere, Tower, and Urza's.)."; + } + + private PlanarNexusEffect(final PlanarNexusEffect effect) { + super(effect); + } + + @Override + public PlanarNexusEffect copy() { + return new PlanarNexusEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject sourceObject = game.getObject(source); + if (sourceObject == null) { + return false; + } + sourceObject.setIsAllNonbasicLandTypes(game, true); + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/ModernHorizons3Commander.java b/Mage.Sets/src/mage/sets/ModernHorizons3Commander.java index 92ccb83455a..22b40700460 100644 --- a/Mage.Sets/src/mage/sets/ModernHorizons3Commander.java +++ b/Mage.Sets/src/mage/sets/ModernHorizons3Commander.java @@ -196,9 +196,11 @@ public final class ModernHorizons3Commander extends ExpansionSet { cards.add(new SetCardInfo("Nissa, Steward of Elements", 270, Rarity.MYTHIC, mage.cards.n.NissaStewardOfElements.class)); cards.add(new SetCardInfo("Oblivion Sower", 158, Rarity.MYTHIC, mage.cards.o.OblivionSower.class)); cards.add(new SetCardInfo("Oblivion Stone", 303, Rarity.RARE, mage.cards.o.OblivionStone.class)); + cards.add(new SetCardInfo("Omo, Queen of Vesuva", 2, Rarity.MYTHIC, mage.cards.o.OmoQueenOfVesuva.class)); cards.add(new SetCardInfo("Opal Palace", 361, Rarity.COMMON, mage.cards.o.OpalPalace.class)); cards.add(new SetCardInfo("Overflowing Basin", 362, Rarity.RARE, mage.cards.o.OverflowingBasin.class)); cards.add(new SetCardInfo("Path of Ancestry", 363, Rarity.COMMON, mage.cards.p.PathOfAncestry.class)); + cards.add(new SetCardInfo("Planar Nexus", 80, Rarity.RARE, mage.cards.p.PlanarNexus.class)); cards.add(new SetCardInfo("Poison Dart Frog", 238, Rarity.COMMON, mage.cards.p.PoisonDartFrog.class)); cards.add(new SetCardInfo("Polygoyf", 65, Rarity.RARE, mage.cards.p.Polygoyf.class)); cards.add(new SetCardInfo("Pongify", 190, Rarity.UNCOMMON, mage.cards.p.Pongify.class)); diff --git a/Mage.Sets/src/mage/sets/Unfinity.java b/Mage.Sets/src/mage/sets/Unfinity.java index 292d30d4bda..86bf7616201 100644 --- a/Mage.Sets/src/mage/sets/Unfinity.java +++ b/Mage.Sets/src/mage/sets/Unfinity.java @@ -42,6 +42,7 @@ public final class Unfinity extends ExpansionSet { cards.add(new SetCardInfo("Monoxa, Midway Manager", 173, Rarity.UNCOMMON, mage.cards.m.MonoxaMidwayManager.class)); cards.add(new SetCardInfo("Mountain", 238, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_UST_VARIOUS)); cards.add(new SetCardInfo("Mountain", 243, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_UST_VARIOUS)); + cards.add(new SetCardInfo("Nearby Planet", 198, Rarity.COMMON, mage.cards.n.NearbyPlanet.class)); cards.add(new SetCardInfo("Non-Human Cannonball", 115, Rarity.COMMON, mage.cards.n.NonHumanCannonball.class)); cards.add(new SetCardInfo("One-Clown Band", 117, Rarity.COMMON, mage.cards.o.OneClownBand.class)); cards.add(new SetCardInfo("Overgrown Tomb", 284, Rarity.RARE, mage.cards.o.OvergrownTomb.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m3c/OmoQueenOfVesuvaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m3c/OmoQueenOfVesuvaTest.java new file mode 100644 index 00000000000..f75c523bb81 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m3c/OmoQueenOfVesuvaTest.java @@ -0,0 +1,89 @@ +package org.mage.test.cards.single.m3c; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class OmoQueenOfVesuvaTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.o.OmoQueenOfVesuva Omo, Queen of Vesuva} {2}{G/U} + * Legendary Creature — Shapeshifter Noble + * Whenever Omo, Queen of Vesuva enters the battlefield or attacks, put an everything counter on each of up to one target land and up to one target creature. + * Each land with an everything counter on it is every land type in addition to its other types. + * Each nonland creature with an everything counter on it is every creature type. + * 1/5 + */ + private static final String omo = "Omo, Queen of Vesuva"; + + @Test + public void test_TronLand() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.BATTLEFIELD, playerA, "Urza's Tower"); + addCard(Zone.HAND, playerA, omo); + addCard(Zone.HAND, playerA, "Abzan Banner"); + + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 3); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, omo, true); + addTarget(playerA, "Forest"); + addTarget(playerA, TestPlayer.TARGET_SKIP); + + // Urza's Tower makes {3} mana + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Abzan Banner"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Abzan Banner", 1); + } + + @Test + public void test_IsForest() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerA, "Coiling Woodworm", 1); // Coiling Woodworm’s power is equal to the number of Forests on the battlefield. + addCard(Zone.HAND, playerA, omo); + addCard(Zone.HAND, playerA, "Llanowar Elves"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, omo); + addTarget(playerA, "Island"); + addTarget(playerA, TestPlayer.TARGET_SKIP); + + setChoice(playerA, "Green"); // mana color to cast Llanowar Elves + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Llanowar Elves"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, "Coiling Woodworm", 1, 1); + assertPermanentCount(playerA, "Llanowar Elves", 1); + } + + @Test + public void test_EveryCreatureSubType() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerA, "Coiling Woodworm", 1); // Coiling Woodworm’s power is equal to the number of Forests on the battlefield. + addCard(Zone.BATTLEFIELD, playerA, "Imperious Perfect", 1); // Other Elves you control get +1/+1. + addCard(Zone.HAND, playerA, omo); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, omo, true); + addTarget(playerA, "Island"); + addTarget(playerA, "Coiling Woodworm"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertCounterCount(playerA, "Coiling Woodworm", CounterType.EVERYTHING, 1); + assertPowerToughness(playerA, "Coiling Woodworm", 2, 2); // 1 Forest, and is an Elf + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m3c/PlanarNexusTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m3c/PlanarNexusTest.java new file mode 100644 index 00000000000..10987c86e34 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m3c/PlanarNexusTest.java @@ -0,0 +1,112 @@ +package org.mage.test.cards.single.m3c; + +import mage.abilities.keyword.TrampleAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class PlanarNexusTest extends CardTestPlayerBase { + + private static final String nexus = "Planar Nexus"; + private static final String cloudpost = "Cloudpost"; + private static final String glimmerpost = "Glimmerpost"; + private static final String golem = "Stone Golem"; + + @Test + public void testLocus() { + addCard(Zone.BATTLEFIELD, playerA, nexus); + addCard(Zone.BATTLEFIELD, playerA, cloudpost); + addCard(Zone.HAND, playerA, glimmerpost); + addCard(Zone.HAND, playerA, golem); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, glimmerpost); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 + 3); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, golem); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, golem, 1); + } + + private static final String tower = "Urza's Tower"; + private static final String sentinel = "Gilded Sentinel"; + + @Test + public void testTronLand() { + addCard(Zone.BATTLEFIELD, playerA, nexus); + addCard(Zone.BATTLEFIELD, playerA, tower); + addCard(Zone.HAND, playerA, sentinel); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sentinel); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, sentinel, 1); + } + + private static final String seas = "Spreading Seas"; + + @Test + public void testSpreadingSeas() { + addCard(Zone.BATTLEFIELD, playerA, "Island"); + addCard(Zone.BATTLEFIELD, playerA, nexus); + addCard(Zone.HAND, playerA, seas); + addCard(Zone.HAND, playerA, glimmerpost); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, seas, nexus); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, glimmerpost); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 + 1); + } + + private static final String vine = "Gatecreeper Vine"; + + @Test + public void testGateSearch() { + addCard(Zone.LIBRARY, playerA, nexus); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.HAND, playerA, vine); + + setChoice(playerA, true); + addTarget(playerA, nexus); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vine); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, nexus, 1); + } + + private static final String naga = "Sidewinder Naga"; + + @Test + public void testDesertGraveyard() { + addCard(Zone.GRAVEYARD, playerA, nexus); + addCard(Zone.BATTLEFIELD, playerA, naga); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, naga, 3 + 1, 2); + assertAbility(playerA, naga, TrampleAbility.getInstance(), true); + } +} diff --git a/Mage/src/main/java/mage/MageObject.java b/Mage/src/main/java/mage/MageObject.java index ce337272aaa..c5b498ae270 100644 --- a/Mage/src/main/java/mage/MageObject.java +++ b/Mage/src/main/java/mage/MageObject.java @@ -471,6 +471,9 @@ public interface MageObject extends MageItem, Serializable, Copyable if (subTypeSet == SubTypeSet.CreatureType || subTypeSet == null) { this.setIsAllCreatureTypes(game, mageObject.isAllCreatureTypes(game)); } + if (subTypeSet == SubTypeSet.NonBasicLandType || subTypeSet == null) { + this.setIsAllNonbasicLandTypes(game, mageObject.isAllNonbasicLandTypes(game)); + } for (SubType subType : mageObject.getSubtype(game)) { if (subType.getSubTypeSet() == subTypeSet || subTypeSet == null) { this.addSubType(game, subType); @@ -485,10 +488,12 @@ public interface MageObject extends MageItem, Serializable, Copyable default void removeAllSubTypes(Game game, SubTypeSet subTypeSet) { if (subTypeSet == null) { setIsAllCreatureTypes(game, false); + setIsAllNonbasicLandTypes(game, false); game.getState().getCreateMageObjectAttribute(this, game).getSubtype().clear(); } else if (subTypeSet == SubTypeSet.CreatureType) { removeAllCreatureTypes(game); } else if (subTypeSet == SubTypeSet.NonBasicLandType) { + setIsAllNonbasicLandTypes(game, false); game.getState().getCreateMageObjectAttribute(this, game).getSubtype().removeAll(SubType.getLandTypes()); } else { game.getState().getCreateMageObjectAttribute(this, game).getSubtype().removeAll(SubType.getBySubTypeSet(subTypeSet)); @@ -497,11 +502,13 @@ public interface MageObject extends MageItem, Serializable, Copyable default void retainAllArtifactSubTypes(Game game) { setIsAllCreatureTypes(game, false); + setIsAllNonbasicLandTypes(game, false); game.getState().getCreateMageObjectAttribute(this, game).getSubtype().retainAll(SubType.getArtifactTypes()); } default void retainAllEnchantmentSubTypes(Game game) { setIsAllCreatureTypes(game, false); + setIsAllNonbasicLandTypes(game, false); game.getState().getCreateMageObjectAttribute(this, game).getSubtype().retainAll(SubType.getEnchantmentTypes()); } @@ -603,5 +610,17 @@ public interface MageObject extends MageItem, Serializable, Copyable */ void setIsAllCreatureTypes(Game game, boolean value); + boolean isAllNonbasicLandTypes(Game game); + + void setIsAllNonbasicLandTypes(boolean value); + + /** + * Change all nonbasic land type mark temporary, for continuous effects only + * + * @param game + * @param value + */ + void setIsAllNonbasicLandTypes(Game game, boolean value); + void removePTCDA(); } diff --git a/Mage/src/main/java/mage/MageObjectImpl.java b/Mage/src/main/java/mage/MageObjectImpl.java index 0a2606d220b..02d88de6f35 100644 --- a/Mage/src/main/java/mage/MageObjectImpl.java +++ b/Mage/src/main/java/mage/MageObjectImpl.java @@ -323,10 +323,9 @@ public abstract class MageObjectImpl implements MageObject { if (value == null) { return false; } - if (value.getSubTypeSet() == SubTypeSet.CreatureType && isAllCreatureTypes(game)) { - return true; - } - return getSubtype(game).contains(value); + return value.getSubTypeSet() == SubTypeSet.CreatureType && isAllCreatureTypes(game) + || value.getSubTypeSet() == SubTypeSet.NonBasicLandType && isAllNonbasicLandTypes(game) + || getSubtype(game).contains(value); } @Override @@ -375,6 +374,21 @@ public abstract class MageObjectImpl implements MageObject { this.getSubtype(game).setIsAllCreatureTypes(value && (this.isTribal(game) || this.isCreature(game))); } + @Override + public boolean isAllNonbasicLandTypes(Game game) { + return this.getSubtype(game).isAllNonbasicLandTypes(); + } + + @Override + public void setIsAllNonbasicLandTypes(boolean value) { + this.getSubtype().setIsAllNonbasicLandTypes(value && this.isLand()); + } + + @Override + public void setIsAllNonbasicLandTypes(Game game, boolean value) { + this.getSubtype(game).setIsAllNonbasicLandTypes(value && this.isLand(game)); + } + /** * Remove power/toughness character defining abilities */ diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java index dd57998ef35..755024a0555 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesEnchantmentSourceEffect.java @@ -43,7 +43,6 @@ public class BecomesEnchantmentSourceEffect extends ContinuousEffectImpl { permanent.removeAllCardTypes(game); permanent.addCardType(game, CardType.ENCHANTMENT); permanent.retainAllEnchantmentSubTypes(game); - permanent.setIsAllCreatureTypes(game, false); return true; } } diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index dca4c78e6bd..429ffd8e82e 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -71,6 +71,7 @@ public enum CounterType { ENERGY("energy"), ENLIGHTENED("enlightened"), EON("eon"), + EVERYTHING("everything"), EXALTED("exalted"), EXPERIENCE("experience"), EYEBALL("eyeball"), diff --git a/Mage/src/main/java/mage/designations/Designation.java b/Mage/src/main/java/mage/designations/Designation.java index edab643575c..cb76e274bc0 100644 --- a/Mage/src/main/java/mage/designations/Designation.java +++ b/Mage/src/main/java/mage/designations/Designation.java @@ -181,6 +181,19 @@ public abstract class Designation extends MageObjectImpl { public void setIsAllCreatureTypes(boolean value) { } + @Override + public void setIsAllNonbasicLandTypes(Game game, boolean value) { + } + + @Override + public boolean isAllNonbasicLandTypes(Game game) { + return false; + } + + @Override + public void setIsAllNonbasicLandTypes(boolean value) { + } + @Override public void setIsAllCreatureTypes(Game game, boolean value) { } diff --git a/Mage/src/main/java/mage/game/command/Commander.java b/Mage/src/main/java/mage/game/command/Commander.java index a124394f927..7ff69e6101d 100644 --- a/Mage/src/main/java/mage/game/command/Commander.java +++ b/Mage/src/main/java/mage/game/command/Commander.java @@ -316,6 +316,21 @@ public class Commander extends CommandObjectImpl { sourceObject.setIsAllCreatureTypes(game, value); } + @Override + public boolean isAllNonbasicLandTypes(Game game) { + return sourceObject.isAllNonbasicLandTypes(game); + } + + @Override + public void setIsAllNonbasicLandTypes(boolean value) { + sourceObject.setIsAllNonbasicLandTypes(value); + } + + @Override + public void setIsAllNonbasicLandTypes(Game game, boolean value) { + sourceObject.setIsAllNonbasicLandTypes(game, value); + } + @Override public void removePTCDA() { } diff --git a/Mage/src/main/java/mage/game/command/Dungeon.java b/Mage/src/main/java/mage/game/command/Dungeon.java index 2b1e65b422c..adf15b5b8f4 100644 --- a/Mage/src/main/java/mage/game/command/Dungeon.java +++ b/Mage/src/main/java/mage/game/command/Dungeon.java @@ -331,6 +331,19 @@ public class Dungeon extends CommandObjectImpl { public void setIsAllCreatureTypes(Game game, boolean value) { } + @Override + public boolean isAllNonbasicLandTypes(Game game) { + return false; + } + + @Override + public void setIsAllNonbasicLandTypes(boolean value) { + } + + @Override + public void setIsAllNonbasicLandTypes(Game game, boolean value) { + } + public void discardEffects() { for (Ability ability : abilites) { for (Effect effect : ability.getEffects()) { diff --git a/Mage/src/main/java/mage/game/command/Emblem.java b/Mage/src/main/java/mage/game/command/Emblem.java index 7cb934200a9..570d030d1be 100644 --- a/Mage/src/main/java/mage/game/command/Emblem.java +++ b/Mage/src/main/java/mage/game/command/Emblem.java @@ -246,6 +246,19 @@ public abstract class Emblem extends CommandObjectImpl { public void setIsAllCreatureTypes(Game game, boolean value) { } + @Override + public boolean isAllNonbasicLandTypes(Game game) { + return false; + } + + @Override + public void setIsAllNonbasicLandTypes(boolean value) { + } + + @Override + public void setIsAllNonbasicLandTypes(Game game, boolean value) { + } + public void discardEffects() { for (Ability ability : abilites) { for (Effect effect : ability.getEffects()) { diff --git a/Mage/src/main/java/mage/game/command/Plane.java b/Mage/src/main/java/mage/game/command/Plane.java index 9ee4394ebd8..4e48daaf7cd 100644 --- a/Mage/src/main/java/mage/game/command/Plane.java +++ b/Mage/src/main/java/mage/game/command/Plane.java @@ -268,6 +268,19 @@ public abstract class Plane extends CommandObjectImpl { public void setIsAllCreatureTypes(Game game, boolean value) { } + @Override + public boolean isAllNonbasicLandTypes(Game game) { + return false; + } + + @Override + public void setIsAllNonbasicLandTypes(boolean value) { + } + + @Override + public void setIsAllNonbasicLandTypes(Game game, boolean value) { + } + public void discardEffects() { for (Ability ability : abilites) { for (Effect effect : ability.getEffects()) { diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index d3cb54c8c4a..56f7c5cc3fc 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -1181,6 +1181,21 @@ public class Spell extends StackObjectImpl implements Card { card.setIsAllCreatureTypes(game, value); } + @Override + public boolean isAllNonbasicLandTypes(Game game) { + return card.isAllNonbasicLandTypes(game); + } + + @Override + public void setIsAllNonbasicLandTypes(boolean value) { + card.setIsAllNonbasicLandTypes(value); + } + + @Override + public void setIsAllNonbasicLandTypes(Game game, boolean value) { + card.setIsAllNonbasicLandTypes(game, value); + } + @Override public List getAttachments() { throw new UnsupportedOperationException("Not supported."); diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index 67a357a529d..dedc8115a29 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -740,6 +740,19 @@ public class StackAbility extends StackObjectImpl implements Ability { public void setIsAllCreatureTypes(Game game, boolean value) { } + @Override + public boolean isAllNonbasicLandTypes(Game game) { + return false; + } + + @Override + public void setIsAllNonbasicLandTypes(boolean value) { + } + + @Override + public void setIsAllNonbasicLandTypes(Game game, boolean value) { + } + @Override public StackAbility setTargetAdjuster(TargetAdjuster targetAdjuster) { this.targetAdjuster = targetAdjuster; diff --git a/Mage/src/main/java/mage/util/SubTypes.java b/Mage/src/main/java/mage/util/SubTypes.java index 501595b7481..79caea31158 100644 --- a/Mage/src/main/java/mage/util/SubTypes.java +++ b/Mage/src/main/java/mage/util/SubTypes.java @@ -9,6 +9,7 @@ import java.util.Collections; public class SubTypes extends ArrayList { private boolean isAllCreatureTypes = false; + private boolean isAllNonbasicLandTypes = false; public SubTypes(SubType... subTypes) { super(); @@ -18,6 +19,7 @@ public class SubTypes extends ArrayList { protected SubTypes(final SubTypes list) { this.addAll(list); this.isAllCreatureTypes = list.isAllCreatureTypes; + this.isAllNonbasicLandTypes = list.isAllNonbasicLandTypes; } public SubTypes copy() { @@ -32,6 +34,7 @@ public class SubTypes extends ArrayList { this.clear(); this.addAll(subtypes); this.isAllCreatureTypes = subtypes.isAllCreatureTypes; + this.isAllNonbasicLandTypes = subtypes.isAllNonbasicLandTypes; } public boolean removeAll(SubType... subTypes) { @@ -45,4 +48,12 @@ public class SubTypes extends ArrayList { public boolean isAllCreatureTypes() { return isAllCreatureTypes; } + + public void setIsAllNonbasicLandTypes(boolean allNonbasicLandTypes) { + isAllNonbasicLandTypes = allNonbasicLandTypes; + } + + public boolean isAllNonbasicLandTypes() { + return isAllNonbasicLandTypes; + } }