From d6c2f031d0dccc457028494fb29eb6e2be86cb86 Mon Sep 17 00:00:00 2001 From: jeffwadsworth Date: Thu, 6 Feb 2020 17:47:08 -0600 Subject: [PATCH] - Fixed #6236 --- Mage.Sets/src/mage/cards/b/BloodMoon.java | 113 ++++------ Mage.Sets/src/mage/cards/c/Cloudform.java | 10 +- Mage.Sets/src/mage/cards/c/Conspiracy.java | 212 ++++++++++-------- Mage.Sets/src/mage/cards/c/Conversion.java | 45 +--- .../src/mage/cards/e/EnchantedEvening.java | 5 +- Mage.Sets/src/mage/cards/f/Frogify.java | 3 +- Mage.Sets/src/mage/cards/g/Glaciers.java | 49 +--- Mage.Sets/src/mage/cards/h/Humility.java | 94 ++++---- .../src/mage/cards/i/IllusionaryTerrain.java | 55 +++-- Mage.Sets/src/mage/cards/k/KormusBell.java | 11 +- .../src/mage/cards/m/MycosynthLattice.java | 4 +- Mage.Sets/src/mage/cards/n/NecroticOoze.java | 16 +- Mage.Sets/src/mage/cards/o/Opalescence.java | 130 ++++++----- .../src/mage/cards/p/PhantasmalTerrain.java | 146 ++++++------ .../src/mage/cards/q/QuicksilverFountain.java | 13 +- Mage.Sets/src/mage/cards/s/SpreadingSeas.java | 6 +- .../src/mage/cards/s/StarfieldOfNyx.java | 148 ++++++------ .../src/mage/cards/s/StormtideLeviathan.java | 97 ++++---- .../mage/cards/u/UrborgTombOfYawgmoth.java | 88 +++++++- .../continuous/DependentEffectsTest.java | 12 +- .../LandTypeChangingEffectsTest.java | 7 +- .../test/cards/continuous/LayerTests.java | 71 ++++++ .../effects/ContinuousEffectImpl.java | 44 ++-- .../abilities/effects/ContinuousEffects.java | 40 +++- .../BecomesBasicLandEnchantedEffect.java | 14 +- .../BecomesBasicLandTargetEffect.java | 8 +- .../continuous/BecomesCreatureAllEffect.java | 15 +- .../BecomesCreatureAttachedEffect.java | 7 + 28 files changed, 813 insertions(+), 650 deletions(-) diff --git a/Mage.Sets/src/mage/cards/b/BloodMoon.java b/Mage.Sets/src/mage/cards/b/BloodMoon.java index 95495014518..1cd11c4e497 100644 --- a/Mage.Sets/src/mage/cards/b/BloodMoon.java +++ b/Mage.Sets/src/mage/cards/b/BloodMoon.java @@ -4,11 +4,7 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.mana.BlackManaAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.abilities.mana.GreenManaAbility; import mage.abilities.mana.RedManaAbility; -import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -38,73 +34,56 @@ public final class BloodMoon extends CardImpl { public BloodMoon copy() { return new BloodMoon(this); } -} -class BloodMoonEffect extends ContinuousEffectImpl { + static class BloodMoonEffect extends ContinuousEffectImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent(); + private static final FilterLandPermanent filter = new FilterLandPermanent(); - static { - filter.add(Predicates.not(SuperType.BASIC.getPredicate())); - } - - BloodMoonEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment); - this.staticText = "Nonbasic lands are Mountains"; - } - - BloodMoonEffect(final BloodMoonEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public BloodMoonEffect copy() { - return new BloodMoonEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (Permanent land : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { - switch (layer) { - case TypeChangingEffects_4: - // 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects - // So the ability removing has to be done before Layer 6 - //land.getSubtype(game).removeAll(SubType.getLandTypes(false)); - land.getSubtype(game).clear(); - land.getSubtype(game).add(SubType.MOUNTAIN); - land.removeAllAbilities(source.getSourceId(), game); - break; - case AbilityAddingRemovingEffects_6: - land.removeAllAbilities(source.getSourceId(), game); - if (land.getSubtype(game).contains(SubType.FOREST)) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.PLAINS)) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.ISLAND)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.SWAMP)) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - break; - } + static { + filter.add(Predicates.not(SuperType.BASIC.getPredicate())); } - return true; - } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 - || layer == Layer.TypeChangingEffects_4; + BloodMoonEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment); + this.staticText = "Nonbasic lands are Mountains"; + this.dependencyTypes.add(DependencyType.BecomeMountain); + } + + BloodMoonEffect(final BloodMoonEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public BloodMoonEffect copy() { + return new BloodMoonEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + for (Permanent land : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { + switch (layer) { + case TypeChangingEffects_4: + // 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects + // So the ability removing has to be done before Layer 6 + // Lands have their mana ability intrinsically, so that is added in layer 4 + land.getSubtype(game).removeAll(SubType.getLandTypes()); + land.getSubtype(game).add(SubType.MOUNTAIN); + land.removeAllAbilities(source.getSourceId(), game); + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + break; + } + } + return true; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4; + } } } diff --git a/Mage.Sets/src/mage/cards/c/Cloudform.java b/Mage.Sets/src/mage/cards/c/Cloudform.java index 68ae211edec..bc2d81f104f 100644 --- a/Mage.Sets/src/mage/cards/c/Cloudform.java +++ b/Mage.Sets/src/mage/cards/c/Cloudform.java @@ -26,12 +26,16 @@ public final class Cloudform extends CardImpl { public Cloudform(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}{U}"); - // When Cloudform enters the battlefield, it becomes an Aura with enchant creature. Manifest the top card of your library and attach Cloudform to it. + // When Cloudform enters the battlefield, it becomes an Aura with enchant creature. + // Manifest the top card of your library and attach Cloudform to it. this.addAbility(new EntersBattlefieldTriggeredAbility(new BecomesAuraAttachToManifestSourceEffect())); // Enchanted creature has flying and hexproof. - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA, Duration.WhileOnBattlefield)); - Effect effect = new GainAbilityAttachedEffect(HexproofAbility.getInstance(), AttachmentType.AURA, Duration.WhileOnBattlefield); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, + new GainAbilityAttachedEffect(FlyingAbility.getInstance(), + AttachmentType.AURA, Duration.WhileOnBattlefield)); + Effect effect = new GainAbilityAttachedEffect( + HexproofAbility.getInstance(), AttachmentType.AURA, Duration.WhileOnBattlefield); effect.setText("and hexproof"); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/Conspiracy.java b/Mage.Sets/src/mage/cards/c/Conspiracy.java index 2fb5ee65265..31fa1107019 100644 --- a/Mage.Sets/src/mage/cards/c/Conspiracy.java +++ b/Mage.Sets/src/mage/cards/c/Conspiracy.java @@ -17,13 +17,16 @@ import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; import mage.util.SubTypeList; - import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.Effect; /** - * @author anonymous + * @author bunchOfDevs */ public final class Conspiracy extends CardImpl { @@ -32,8 +35,11 @@ public final class Conspiracy extends CardImpl { // As Conspiracy enters the battlefield, choose a creature type. this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.Neutral))); - // Creature cards you own that aren't on the battlefield, creature spells you control, and creatures you control are the chosen type. + + // Creature cards you own that aren't on the battlefield, creature + // spells you control, and creatures you control are the chosen type. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConspiracyEffect())); + } public Conspiracy(final Conspiracy card) { @@ -44,113 +50,129 @@ public final class Conspiracy extends CardImpl { public Conspiracy copy() { return new Conspiracy(this); } -} -class ConspiracyEffect extends ContinuousEffectImpl { + static class ConspiracyEffect extends ContinuousEffectImpl { - public ConspiracyEffect() { - super(Duration.WhileOnBattlefield, Outcome.Neutral); - staticText = "Creatures you control are the chosen type. The same is true for creature spells you control and creature cards you own that aren't on the battlefield."; - } + public ConspiracyEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); + staticText = "Creatures you control are the chosen type. The same is " + + "true for creature spells you control and creature cards " + + "you own that aren't on the battlefield."; + } - public ConspiracyEffect(final ConspiracyEffect effect) { - super(effect); - } + public ConspiracyEffect(final ConspiracyEffect effect) { + super(effect); + } - @Override - public ConspiracyEffect copy() { - return new ConspiracyEffect(this); - } + @Override + public ConspiracyEffect copy() { + return new ConspiracyEffect(this); + } - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Player controller = game.getPlayer(source.getControllerId()); - SubType subType = ChooseCreatureTypeEffect.getChosenCreatureType(source.getSourceId(), game); - if (controller != null && subType != null) { - // Creature cards you own that aren't on the battlefield - // in graveyard - for (UUID cardId : controller.getGraveyard()) { - Card card = game.getCard(cardId); - if (card != null && card.isCreature()) { - setCreatureSubtype(card, subType, game); - } - } - // on Hand - for (UUID cardId : controller.getHand()) { - Card card = game.getCard(cardId); - if (card != null && card.isCreature()) { - setCreatureSubtype(card, subType, game); - } - } - // in Exile - for (Card card : game.getState().getExile().getAllCards(game)) { - if (card.isOwnedBy(controller.getId()) && card.isCreature()) { - setCreatureSubtype(card, subType, game); - } - } - // in Library (e.g. for Mystical Teachings) - for (Card card : controller.getLibrary().getCards(game)) { - if (card.isOwnedBy(controller.getId()) && card.isCreature()) { - setCreatureSubtype(card, subType, game); - } - } - // commander in command zone - for (UUID commanderId : game.getCommandersIds(controller)) { - if (game.getState().getZone(commanderId) == Zone.COMMAND) { - Card card = game.getCard(commanderId); - if (card != null && card.isCreature()) { - setCreatureSubtype(card, subType, game); + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + SubType subType = ChooseCreatureTypeEffect.getChosenCreatureType(source.getSourceId(), game); + switch (layer) { + case TypeChangingEffects_4: + if (controller != null && subType != null) { + // Creature cards you own that aren't on the battlefield + // in graveyard + for (UUID cardId : controller.getGraveyard()) { + Card card = game.getCard(cardId); + if (card != null && card.isCreature()) { + setCreatureSubtype(card, subType, game); + } + } + // on Hand + for (UUID cardId : controller.getHand()) { + Card card = game.getCard(cardId); + if (card != null && card.isCreature()) { + setCreatureSubtype(card, subType, game); + } + } + // in Exile + for (Card card : game.getState().getExile().getAllCards(game)) { + if (card.isOwnedBy(controller.getId()) && card.isCreature()) { + setCreatureSubtype(card, subType, game); + } + } + // in Library (e.g. for Mystical Teachings) + for (Card card : controller.getLibrary().getCards(game)) { + if (card.isOwnedBy(controller.getId()) && card.isCreature()) { + setCreatureSubtype(card, subType, game); + } + } + // commander in command zone + for (UUID commanderId : game.getCommandersIds(controller)) { + if (game.getState().getZone(commanderId) == Zone.COMMAND) { + Card card = game.getCard(commanderId); + if (card != null && card.isCreature()) { + setCreatureSubtype(card, subType, game); + } + } + } + // creature spells you control + for (Iterator iterator = game.getStack().iterator(); iterator.hasNext();) { + StackObject stackObject = iterator.next(); + if (stackObject instanceof Spell + && stackObject.isControlledBy(source.getControllerId()) + && stackObject.isCreature()) { + Card card = ((Spell) stackObject).getCard(); + setCreatureSubtype(card, subType, game); + } + } + // creatures you control + List creatures = game.getState().getBattlefield().getAllActivePermanents( + new FilterControlledCreaturePermanent(), source.getControllerId(), game); + for (Permanent creature : creatures) { + setCreatureSubtype(creature, subType, game); + } } + return true; + } + return false; + } + + private void setCreatureSubtype(MageObject object, SubType subtype, Game game) { + if (object != null) { + if (object instanceof Card) { + Card card = (Card) object; + setChosenSubtype( + game.getState().getCreateCardAttribute(card, game).getSubtype(), + subtype); + } else { + setChosenSubtype(object.getSubtype(game), subtype); } } - // creature spells you control - for (Iterator iterator = game.getStack().iterator(); iterator.hasNext(); ) { - StackObject stackObject = iterator.next(); - if (stackObject instanceof Spell - && stackObject.isControlledBy(source.getControllerId()) - && stackObject.isCreature()) { - Card card = ((Spell) stackObject).getCard(); - setCreatureSubtype(card, subType, game); - } - } - // creatures you control - List creatures = game.getBattlefield().getAllActivePermanents( - new FilterControlledCreaturePermanent(), source.getControllerId(), game); - for (Permanent creature : creatures) { - setCreatureSubtype(creature, subType, game); - } - return true; } - return false; - } - private void setCreatureSubtype(MageObject object, SubType subtype, Game game) { - if (object != null) { - if (object instanceof Card) { - Card card = (Card) object; - setChosenSubtype( - game.getState().getCreateCardAttribute(card, game).getSubtype(), - subtype); - } else { - setChosenSubtype(object.getSubtype(game), subtype); + private void setChosenSubtype(SubTypeList subtype, SubType choice) { + if (subtype.size() != 1 || !subtype.contains(choice)) { + subtype.clear(); + subtype.add(choice); } } - } - private void setChosenSubtype(SubTypeList subtype, SubType choice) { - if (subtype.size() != 1 || !subtype.contains(choice)) { - subtype.clear(); - subtype.add(choice); + @Override + public boolean apply(Game game, Ability source) { + return false; } - } - @Override - public boolean apply(Game game, Ability source) { - return false; - } + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4; + } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4; + @Override + public Set isDependentTo(List allEffectsInLayer) { + // the dependent classes needs to be an enclosed class for dependent check of continuous effects + return allEffectsInLayer.stream() + .filter(effect + -> mage.cards.s.StarfieldOfNyx.class.equals(effect.getClass().getEnclosingClass()) + || mage.cards.o.Opalescence.class.equals(effect.getClass().getEnclosingClass())) + .map(Effect::getId) + .collect(Collectors.toSet()); + } } } diff --git a/Mage.Sets/src/mage/cards/c/Conversion.java b/Mage.Sets/src/mage/cards/c/Conversion.java index 8731aaba764..afd4397c6a1 100644 --- a/Mage.Sets/src/mage/cards/c/Conversion.java +++ b/Mage.Sets/src/mage/cards/c/Conversion.java @@ -12,10 +12,6 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; -import mage.abilities.mana.BlackManaAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.abilities.mana.RedManaAbility; import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -80,42 +76,12 @@ public final class Conversion extends CardImpl { switch (layer) { case TypeChangingEffects_4: if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { - land.getSubtype(game).clear(); + land.getSubtype(game).removeAll(SubType.getLandTypes()); land.getSubtype(game).add(SubType.PLAINS); - game.getState().setValue("conversion" - + source.getId() - + land.getId() - + land.getZoneChangeCounter(game), - "true"); - } - break; - case AbilityAddingRemovingEffects_6: - if (game.getState().getValue("conversion" - + source.getId() - + land.getId() - + land.getZoneChangeCounter(game)) != null - && game.getState().getValue("conversion" - + source.getId() - + land.getId() - + land.getZoneChangeCounter(game)).equals("true")) { land.removeAllAbilities(source.getSourceId(), game); - if (land.getSubtype(game).contains(SubType.FOREST)) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.PLAINS)) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.ISLAND)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.SWAMP)) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + break; } - break; } } return true; @@ -123,15 +89,14 @@ public final class Conversion extends CardImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 - || layer == Layer.TypeChangingEffects_4; + return layer == Layer.TypeChangingEffects_4; } @Override public Set isDependentTo(List allEffectsInLayer) { return allEffectsInLayer .stream() - .filter(effect -> effect.getDependencyTypes().contains(DependencyType.BecomePlains)) + .filter(effect -> effect.getDependencyTypes().contains(DependencyType.BecomeMountain)) .map(Effect::getId) .collect(Collectors.toSet()); } diff --git a/Mage.Sets/src/mage/cards/e/EnchantedEvening.java b/Mage.Sets/src/mage/cards/e/EnchantedEvening.java index 894c407efe1..940296f5030 100644 --- a/Mage.Sets/src/mage/cards/e/EnchantedEvening.java +++ b/Mage.Sets/src/mage/cards/e/EnchantedEvening.java @@ -39,7 +39,7 @@ public final class EnchantedEvening extends CardImpl { } // need to be enclosed class for dependent check of continuous effects - class EnchangedEveningEffect extends ContinuousEffectImpl { + static class EnchangedEveningEffect extends ContinuousEffectImpl { private final CardType addedCardType; private final FilterPermanent filter; @@ -48,7 +48,8 @@ public final class EnchantedEvening extends CardImpl { super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); this.addedCardType = addedCardType; this.filter = filter; - dependencyTypes.add(DependencyType.EnchantmentAddingRemoving); + this.dependencyTypes.add(DependencyType.EnchantmentAddingRemoving); + this.dependencyTypes.add(DependencyType.AuraAddingRemoving); } public EnchangedEveningEffect(final EnchangedEveningEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/Frogify.java b/Mage.Sets/src/mage/cards/f/Frogify.java index 154aca2fda5..f56a580e7af 100644 --- a/Mage.Sets/src/mage/cards/f/Frogify.java +++ b/Mage.Sets/src/mage/cards/f/Frogify.java @@ -38,9 +38,8 @@ public final class Frogify extends CardImpl { Effect effect = new BecomesCreatureAttachedEffect( new CreatureToken(1, 1, "", SubType.FROG).withColor("U"), "Enchanted creature loses all abilities and is a blue Frog creature with base power and toughness 1/1", - Duration.WhileOnBattlefield, BecomesCreatureAttachedEffect.LoseType.ALL + Duration.WhileOnBattlefield, BecomesCreatureAttachedEffect.LoseType.ALL, Outcome.Detriment ); - effect.setOutcome(Outcome.Detriment); this.addAbility(new SimpleStaticAbility(effect)); } diff --git a/Mage.Sets/src/mage/cards/g/Glaciers.java b/Mage.Sets/src/mage/cards/g/Glaciers.java index 6d2ae05de93..dd24f30067c 100644 --- a/Mage.Sets/src/mage/cards/g/Glaciers.java +++ b/Mage.Sets/src/mage/cards/g/Glaciers.java @@ -18,10 +18,6 @@ import java.util.List; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; -import mage.abilities.mana.BlackManaAbility; -import mage.abilities.mana.BlueManaAbility; -import mage.abilities.mana.GreenManaAbility; -import mage.abilities.mana.RedManaAbility; /** * @@ -33,7 +29,8 @@ public final class Glaciers extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{U}"); // At the beginning of your upkeep, sacrifice Glaciers unless you pay {W}{U}. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl("{W}{U}")), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl("{W}{U}")), TargetController.YOU, false)); // All Mountains are Plains. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GlaciersEffect())); @@ -74,58 +71,30 @@ public final class Glaciers extends CardImpl { for (Permanent land : game.getBattlefield().getAllActivePermanents(CardType.LAND)) { switch (layer) { case TypeChangingEffects_4: + // the land mana ability is intrinsic, so apply at this layer not layer 6 if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { - land.getSubtype(game).clear(); + land.getSubtype(game).removeAll(SubType.getLandTypes()); land.getSubtype(game).add(SubType.PLAINS); - game.getState().setValue("glaciers" - + source.getId() - + land.getId() - + land.getZoneChangeCounter(game), "true"); - } - break; - case AbilityAddingRemovingEffects_6: - if (game.getState().getValue("glaciers" - + source.getId() - + land.getId() - + land.getZoneChangeCounter(game)) != null - && game.getState().getValue("glaciers" - + source.getId() - + land.getId() - + land.getZoneChangeCounter(game)).equals("true")) { land.removeAllAbilities(source.getSourceId(), game); - if (land.getSubtype(game).contains(SubType.FOREST)) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.PLAINS)) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.ISLAND)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (land.getSubtype(game).contains(SubType.SWAMP)) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + break; } - break; } + } return true; } @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 - || layer == Layer.TypeChangingEffects_4; + return layer == Layer.TypeChangingEffects_4; } @Override public Set isDependentTo(List allEffectsInLayer) { return allEffectsInLayer .stream() - .filter(effect -> effect.getDependencyTypes().contains(DependencyType.BecomePlains)) + .filter(effect -> effect.getDependencyTypes().contains(DependencyType.BecomeMountain)) .map(Effect::getId) .collect(Collectors.toSet()); } diff --git a/Mage.Sets/src/mage/cards/h/Humility.java b/Mage.Sets/src/mage/cards/h/Humility.java index 9399388916e..6bdb3b54918 100644 --- a/Mage.Sets/src/mage/cards/h/Humility.java +++ b/Mage.Sets/src/mage/cards/h/Humility.java @@ -1,4 +1,3 @@ - package mage.cards.h; import java.util.UUID; @@ -13,7 +12,7 @@ import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; import mage.constants.Zone; -import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -28,7 +27,8 @@ public final class Humility extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); // All creatures lose all abilities and have base power and toughness 1/1. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HumilityEffect(Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new HumilityEffect(Duration.WhileOnBattlefield))); } @@ -40,53 +40,55 @@ public final class Humility extends CardImpl { public Humility copy() { return new Humility(this); } -} -class HumilityEffect extends ContinuousEffectImpl { + static class HumilityEffect extends ContinuousEffectImpl { - public HumilityEffect(Duration duration) { - super(duration, Outcome.LoseAbility); - staticText = "All creatures lose all abilities and have base power and toughness 1/1"; - } - - public HumilityEffect(final HumilityEffect effect) { - super(effect); - } - - @Override - public HumilityEffect copy() { - return new HumilityEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - for (Permanent permanent : game.getState().getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, player.getId(), source.getSourceId(), game)) { - switch (layer) { - case AbilityAddingRemovingEffects_6: - permanent.removeAllAbilities(source.getSourceId(), game); - break; - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - permanent.getPower().setValue(1); - permanent.getToughness().setValue(1); - } - } - } - return true; + public HumilityEffect(Duration duration) { + super(duration, Outcome.LoseAbility); + staticText = "All creatures lose all abilities and have base power and toughness 1/1"; } - return false; - } - @Override - public boolean apply(Game game, Ability source) { - return false; - } + public HumilityEffect(final HumilityEffect effect) { + super(effect); + } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.PTChangingEffects_7; - } + @Override + public HumilityEffect copy() { + return new HumilityEffect(this); + } + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + for (Permanent permanent : game.getBattlefield().getActivePermanents( + new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { + switch (layer) { + case AbilityAddingRemovingEffects_6: + permanent.removeAllAbilities(source.getSourceId(), game); + break; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + permanent.getPower().setValue(1); + permanent.getToughness().setValue(1); + } + } + } + return true; + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.AbilityAddingRemovingEffects_6 + || layer == Layer.PTChangingEffects_7; + } + + } } diff --git a/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java b/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java index d61f2916e11..4fe4f00784f 100644 --- a/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java +++ b/Mage.Sets/src/mage/cards/i/IllusionaryTerrain.java @@ -20,9 +20,9 @@ import mage.cards.CardSetInfo; import mage.choices.ChoiceBasicLandType; import mage.choices.ChoiceImpl; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Layer; -import static mage.constants.Layer.AbilityAddingRemovingEffects_6; import static mage.constants.Layer.TypeChangingEffects_4; import mage.constants.Outcome; import mage.constants.SubLayer; @@ -92,43 +92,33 @@ class IllusionaryTerrainEffect extends ContinuousEffectImpl { if (land.isBasic()) { switch (layer) { case TypeChangingEffects_4: + // the land mana ability is intrinsic, so add it here, not layer 6 if (land.getSubtype(game).contains(firstChoice)) { - land.getSubtype(game).clear(); + land.getSubtype(game).removeAll(SubType.getLandTypes()); land.getSubtype(game).add(secondChoice); - game.getState().setValue("illusionaryTerrain" - + source.getId() - + land.getId() - + land.getZoneChangeCounter(game), - "true"); - } - break; - case AbilityAddingRemovingEffects_6: - if (game.getState().getValue("illusionaryTerrain" - + source.getId() - + land.getId() - + land.getZoneChangeCounter(game)) != null - && game.getState().getValue("illusionaryTerrain" - + source.getId() - + land.getId() - + land.getZoneChangeCounter(game)).equals("true")) { land.removeAllAbilities(source.getSourceId(), game); if (land.getSubtype(game).contains(SubType.FOREST)) { + this.dependencyTypes.add(DependencyType.BecomeForest); land.addAbility(new GreenManaAbility(), source.getSourceId(), game); } if (land.getSubtype(game).contains(SubType.PLAINS)) { + this.dependencyTypes.add(DependencyType.BecomePlains); land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); } if (land.getSubtype(game).contains(SubType.MOUNTAIN)) { + this.dependencyTypes.add(DependencyType.BecomeMountain); land.addAbility(new RedManaAbility(), source.getSourceId(), game); } if (land.getSubtype(game).contains(SubType.ISLAND)) { + this.dependencyTypes.add(DependencyType.BecomeIsland); land.addAbility(new BlueManaAbility(), source.getSourceId(), game); } if (land.getSubtype(game).contains(SubType.SWAMP)) { + this.dependencyTypes.add(DependencyType.BecomeSwamp); land.addAbility(new BlackManaAbility(), source.getSourceId(), game); } - break; } + break; } } } @@ -144,9 +134,7 @@ class IllusionaryTerrainEffect extends ContinuousEffectImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.TypeChangingEffects_4 - || layer == Layer.AbilityAddingRemovingEffects_6; - + return layer == Layer.TypeChangingEffects_4; } } @@ -180,19 +168,26 @@ class ChooseTwoBasicLandTypesEffect extends OneShotEffect { && mageObject != null) { ChoiceImpl choices = new ChoiceBasicLandType(); if (controller.choose(Outcome.Neutral, choices, game)) { - game.informPlayers(mageObject.getName() + ": First chosen basic land type is " + choices.getChoice()); - game.getState().setValue(mageObject.getId().toString() + "firstChoice", choices.getChoice()); - choiceOne = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "firstChoice")).getDescription(); + game.informPlayers(mageObject.getName() + + ": First chosen basic land type is " + choices.getChoice()); + game.getState().setValue(mageObject.getId().toString() + + "firstChoice", choices.getChoice()); + choiceOne = SubType.byDescription((String) game.getState().getValue( + source.getSourceId().toString() + "firstChoice")).getDescription(); } if (controller.choose(Outcome.Neutral, choices, game)) { - game.informPlayers(mageObject.getName() + ": Second chosen basic land type is " + choices.getChoice()); - game.getState().setValue(mageObject.getId().toString() + "secondChoice", choices.getChoice()); - choiceTwo = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + "secondChoice")).getDescription(); + game.informPlayers(mageObject.getName() + + ": Second chosen basic land type is " + choices.getChoice()); + game.getState().setValue(mageObject.getId().toString() + + "secondChoice", choices.getChoice()); + choiceTwo = SubType.byDescription((String) game.getState().getValue( + source.getSourceId().toString() + "secondChoice")).getDescription(); if (mageObject instanceof Permanent && choiceOne != null && choiceTwo != null) { - ((Permanent) mageObject).addInfo("Chosen Types", CardUtil.addToolTipMarkTags("First chosen basic land type: " + choiceOne - + "\n Second chosen basic land type: " + choiceTwo), game); + ((Permanent) mageObject).addInfo("Chosen Types", CardUtil + .addToolTipMarkTags("First chosen basic land type: " + choiceOne + + "\n Second chosen basic land type: " + choiceTwo), game); } return true; } diff --git a/Mage.Sets/src/mage/cards/k/KormusBell.java b/Mage.Sets/src/mage/cards/k/KormusBell.java index a642f12390b..a1d1e588577 100644 --- a/Mage.Sets/src/mage/cards/k/KormusBell.java +++ b/Mage.Sets/src/mage/cards/k/KormusBell.java @@ -1,8 +1,6 @@ - package mage.cards.k; import java.util.UUID; -import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAllEffect; @@ -10,8 +8,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.game.permanent.token.TokenImpl; -import mage.game.permanent.token.Token; import mage.game.permanent.token.custom.CreatureToken; /** @@ -27,10 +23,13 @@ public final class KormusBell extends CardImpl { // All Swamps are 1/1 black creatures that are still lands. ContinuousEffect effect = new BecomesCreatureAllEffect( new CreatureToken(1, 1, "1/1 black creature").withColor("B"), - "lands", new FilterPermanent(SubType.SWAMP, "Swamps"), Duration.WhileOnBattlefield, true); - effect.setDependedToType(DependencyType.BecomeSwamp); + "lands", new FilterPermanent(SubType.SWAMP, "Swamps"), + Duration.WhileOnBattlefield, true); + effect.addDependedToType(DependencyType.BecomeSwamp); effect.addDependedToType(DependencyType.BecomeIsland); + effect.addDependedToType(DependencyType.BecomeForest); effect.addDependedToType(DependencyType.BecomeMountain); + effect.addDependedToType(DependencyType.BecomePlains); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } diff --git a/Mage.Sets/src/mage/cards/m/MycosynthLattice.java b/Mage.Sets/src/mage/cards/m/MycosynthLattice.java index 41853268963..61ac8b0fae8 100644 --- a/Mage.Sets/src/mage/cards/m/MycosynthLattice.java +++ b/Mage.Sets/src/mage/cards/m/MycosynthLattice.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -30,8 +29,10 @@ public final class MycosynthLattice extends CardImpl { // All permanents are artifacts in addition to their other types. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PermanentsAreArtifactsEffect())); + // All cards that aren't on the battlefield, spells, and permanents are colorless. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new EverythingIsColorlessEffect())); + // Players may spend mana as though it were mana of any color. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ManaCanBeSpentAsAnyColorEffect())); } @@ -51,6 +52,7 @@ class PermanentsAreArtifactsEffect extends ContinuousEffectImpl { public PermanentsAreArtifactsEffect() { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); staticText = "All permanents are artifacts in addition to their other types"; + this.dependencyTypes.add(DependencyType.ArtifactAddingRemoving); // March of the Machines } @Override diff --git a/Mage.Sets/src/mage/cards/n/NecroticOoze.java b/Mage.Sets/src/mage/cards/n/NecroticOoze.java index 967fd0f553e..ba6b653aade 100644 --- a/Mage.Sets/src/mage/cards/n/NecroticOoze.java +++ b/Mage.Sets/src/mage/cards/n/NecroticOoze.java @@ -31,7 +31,8 @@ public final class NecroticOoze extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(3); - // As long as Necrotic Ooze is on the battlefield, it has all activated abilities of all creature cards in all graveyards + // As long as Necrotic Ooze is on the battlefield, it has all + // activated abilities of all creature cards in all graveyards this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NecroticOozeEffect())); } @@ -47,8 +48,10 @@ public final class NecroticOoze extends CardImpl { static class NecroticOozeEffect extends ContinuousEffectImpl { public NecroticOozeEffect() { - super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); - staticText = "As long as {this} is on the battlefield, it has all activated abilities of all creature cards in all graveyards"; + super(Duration.WhileOnBattlefield, + Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "As long as {this} is on the battlefield, " + + "it has all activated abilities of all creature cards in all graveyards"; } public NecroticOozeEffect(final NecroticOozeEffect effect) { @@ -65,8 +68,11 @@ public final class NecroticOoze extends CardImpl { for (Card card : player.getGraveyard().getCards(game)) { if (card.isCreature()) { for (Ability ability : card.getAbilities(game)) { - if (ability instanceof ActivatedAbility) { - perm.addAbility(ability, source.getSourceId(), game); + if (ability instanceof ActivatedAbility + && !perm.getAbilities().contains(ability)) { + if (!perm.getAbilities().contains(ability)) { + perm.addAbility(ability, source.getSourceId(), game); + } } } } diff --git a/Mage.Sets/src/mage/cards/o/Opalescence.java b/Mage.Sets/src/mage/cards/o/Opalescence.java index d011267f364..7d6d9d25be8 100644 --- a/Mage.Sets/src/mage/cards/o/Opalescence.java +++ b/Mage.Sets/src/mage/cards/o/Opalescence.java @@ -1,15 +1,14 @@ - package mage.cards.o; -import java.util.EnumSet; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.Effect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -26,9 +25,10 @@ import mage.game.permanent.Permanent; public final class Opalescence extends CardImpl { public Opalescence(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); - // Each other non-Aura enchantment is a creature with power and toughness each equal to its converted mana cost. It's still an enchantment. + // Each other non-Aura enchantment is a creature with power and + // toughness each equal to its converted mana cost. It's still an enchantment. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OpalescenceEffect())); } @@ -42,77 +42,75 @@ public final class Opalescence extends CardImpl { return new Opalescence(this); } -} + // needs to enclosed with the card class due to a dependency check by cards like Conspiracy + static class OpalescenceEffect extends ContinuousEffectImpl { -class OpalescenceEffect extends ContinuousEffectImpl { + private static final FilterEnchantmentPermanent filter + = new FilterEnchantmentPermanent("Each other non-Aura enchantment"); - private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent("Each other non-Aura enchantment"); - private static final EnumSet checkDependencyTypes; + static { + filter.add(Predicates.not(SubType.AURA.getPredicate())); + filter.add(AnotherPredicate.instance); + } - static { - filter.add(Predicates.not(SubType.AURA.getPredicate())); - filter.add(AnotherPredicate.instance); - checkDependencyTypes = EnumSet.of(DependencyType.AuraAddingRemoving, DependencyType.EnchantmentAddingRemoving); - } + public OpalescenceEffect() { + super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); + staticText = "Each other non-Aura enchantment is a creature in addition to its other " + + "types and has base power and base toughness each equal to its converted mana cost"; + } - public OpalescenceEffect() { - super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); - staticText = "Each other non-Aura enchantment is a creature in addition to its other types and has base power and base toughness each equal to its converted mana cost"; - } + public OpalescenceEffect(final OpalescenceEffect effect) { + super(effect); + } - public OpalescenceEffect(final OpalescenceEffect effect) { - super(effect); - } + @Override + public OpalescenceEffect copy() { + return new OpalescenceEffect(this); + } - @Override - public OpalescenceEffect copy() { - return new OpalescenceEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (!permanent.isCreature()) { - permanent.addCardType(CardType.CREATURE); + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + switch (layer) { + case TypeChangingEffects_4: + if (sublayer == SubLayer.NA) { + if (!permanent.isCreature()) { + permanent.addCardType(CardType.CREATURE); + } } - } - break; + break; - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - int manaCost = permanent.getConvertedManaCost(); - permanent.getPower().setValue(manaCost); - permanent.getToughness().setValue(manaCost); - } - } - - } - return true; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.PTChangingEffects_7 || layer == Layer.TypeChangingEffects_4; - } - - @Override - public Set isDependentTo(List allEffectsInLayer) { - Set dependentTo = new HashSet<>(); - for (ContinuousEffect effect : allEffectsInLayer) { - for (DependencyType dependencyType : effect.getDependencyTypes()) { - if (checkDependencyTypes.contains(dependencyType)) { - dependentTo.add(effect.getId()); + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + int manaCost = permanent.getConvertedManaCost(); + permanent.getPower().setValue(manaCost); + permanent.getToughness().setValue(manaCost); + } } + } + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.PTChangingEffects_7 + || layer == Layer.TypeChangingEffects_4; + } + + @Override + public Set isDependentTo(List allEffectsInLayer) { + // the dependent classes needs to be an enclosed class for dependent check of continuous effects + return allEffectsInLayer.stream() + .filter(effect -> effect.getDependencyTypes().contains(DependencyType.EnchantmentAddingRemoving) + || effect.getDependencyTypes().contains(DependencyType.AuraAddingRemoving)) // Cloudform + .map(Effect::getId) + .collect(Collectors.toSet()); } - return dependentTo; } } diff --git a/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java b/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java index cb425f1d88e..76b23c0cae3 100644 --- a/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java +++ b/Mage.Sets/src/mage/cards/p/PhantasmalTerrain.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -17,6 +16,7 @@ import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Layer; @@ -59,74 +59,90 @@ public final class PhantasmalTerrain extends CardImpl { public PhantasmalTerrain copy() { return new PhantasmalTerrain(this); } -} -class PhantasmalTerrainContinuousEffect extends ContinuousEffectImpl { + class PhantasmalTerrainContinuousEffect extends ContinuousEffectImpl { - public PhantasmalTerrainContinuousEffect() { - super(Duration.WhileOnBattlefield, Outcome.Neutral); - this.staticText = "enchanted land is the chosen type"; - } - - public PhantasmalTerrainContinuousEffect(final PhantasmalTerrainContinuousEffect effect) { - super(effect); - } - - @Override - public PhantasmalTerrainContinuousEffect copy() { - return new PhantasmalTerrainContinuousEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); - if (enchantment != null && enchantment.getAttachedTo() != null && choice != null) { - Permanent land = game.getPermanent(enchantment.getAttachedTo()); - if (land != null) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - land.getSubtype(game).clear(); - land.getSubtype(game).add(choice); - } - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - land.getAbilities().clear(); - if (choice.equals(SubType.FOREST)) { - land.addAbility(new GreenManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.PLAINS)) { - land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.MOUNTAIN)) { - land.addAbility(new RedManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.ISLAND)) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - if (choice.equals(SubType.SWAMP)) { - land.addAbility(new BlackManaAbility(), source.getSourceId(), game); - } - } - break; - } - return true; - } + public PhantasmalTerrainContinuousEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral); + this.staticText = "enchanted land is the chosen type"; } - return false; - } - @Override - public boolean apply(Game game, Ability source) { - return false; - } + public PhantasmalTerrainContinuousEffect(final PhantasmalTerrainContinuousEffect effect) { + super(effect); + } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 - || layer == Layer.TypeChangingEffects_4; - } + @Override + public PhantasmalTerrainContinuousEffect copy() { + return new PhantasmalTerrainContinuousEffect(this); + } + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent enchantment = game.getPermanent(source.getSourceId()); + SubType choice = SubType.byDescription((String) game.getState() + .getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); + if (choice != null) { + if (choice.equals(SubType.MOUNTAIN)) { + dependencyTypes.add(DependencyType.BecomeMountain); + } + if (choice.equals(SubType.FOREST)) { + dependencyTypes.add(DependencyType.BecomeForest); + } + if (choice.equals(SubType.SWAMP)) { + dependencyTypes.add(DependencyType.BecomeSwamp); + } + if (choice.equals(SubType.ISLAND)) { + dependencyTypes.add(DependencyType.BecomeIsland); + } + if (choice.equals(SubType.PLAINS)) { + dependencyTypes.add(DependencyType.BecomePlains); + } + } + if (enchantment != null + && enchantment.getAttachedTo() != null + && choice != null) { + Permanent land = game.getPermanent(enchantment.getAttachedTo()); + if (land != null) { + switch (layer) { + case TypeChangingEffects_4: + if (sublayer == SubLayer.NA) { + land.getSubtype(game).clear(); + land.getSubtype(game).add(choice); + land.removeAllAbilities(source.getSourceId(), game); + + // land ability is intrinsic, so apply at this layer, not layer 6 + if (choice.equals(SubType.FOREST)) { + land.addAbility(new GreenManaAbility(), source.getSourceId(), game); + } + if (choice.equals(SubType.PLAINS)) { + land.addAbility(new WhiteManaAbility(), source.getSourceId(), game); + } + if (choice.equals(SubType.MOUNTAIN)) { + land.addAbility(new RedManaAbility(), source.getSourceId(), game); + } + if (choice.equals(SubType.ISLAND)) { + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + } + if (choice.equals(SubType.SWAMP)) { + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + } + } + break; + } + return true; + } + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4; + } + } } diff --git a/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java b/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java index 1a9146fb525..5a4afebf54a 100644 --- a/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java +++ b/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java @@ -13,7 +13,6 @@ import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.DependencyType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; @@ -81,7 +80,9 @@ class QuicksilverFountainEffect extends OneShotEffect { public QuicksilverFountainEffect() { super(Outcome.Neutral); - staticText = "that player puts a flood counter on target non-Island land they control of their choice. That land is an Island for as long as it has a flood counter on it"; + staticText = "that player puts a flood counter on target non-Island land " + + "they control of their choice. That land is an Island for as " + + "long as it has a flood counter on it"; } public QuicksilverFountainEffect(final QuicksilverFountainEffect effect) { @@ -94,9 +95,11 @@ class QuicksilverFountainEffect extends OneShotEffect { if (player != null) { Permanent landChosen = game.getPermanent(source.getFirstTarget()); landChosen.addCounters(CounterType.FLOOD.createInstance(), source, game); - ContinuousEffect becomesBasicLandTargetEffect = new BecomesBasicLandTargetEffect(Duration.OneUse, SubType.ISLAND); - becomesBasicLandTargetEffect.addDependencyType(DependencyType.BecomeIsland); - ConditionalContinuousEffect effect = new ConditionalContinuousEffect(becomesBasicLandTargetEffect, new LandHasFloodCounterCondition(this), staticText); + ContinuousEffect becomesBasicLandTargetEffect + = new BecomesBasicLandTargetEffect(Duration.OneUse, false, SubType.ISLAND); + ConditionalContinuousEffect effect + = new ConditionalContinuousEffect(becomesBasicLandTargetEffect, + new LandHasFloodCounterCondition(this), staticText); this.setTargetPointer(new FixedTarget(landChosen, game)); effect.setTargetPointer(new FixedTarget(landChosen, game)); game.addEffect(effect, source); diff --git a/Mage.Sets/src/mage/cards/s/SpreadingSeas.java b/Mage.Sets/src/mage/cards/s/SpreadingSeas.java index 4df64a86582..f30cab1125c 100644 --- a/Mage.Sets/src/mage/cards/s/SpreadingSeas.java +++ b/Mage.Sets/src/mage/cards/s/SpreadingSeas.java @@ -18,6 +18,8 @@ import mage.target.TargetPermanent; import mage.target.common.TargetLandPermanent; import java.util.UUID; +import mage.abilities.effects.ContinuousEffect; +import mage.constants.DependencyType; /** * @@ -41,7 +43,9 @@ public final class SpreadingSeas extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1), false)); // Enchanted land is an Island. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BecomesBasicLandEnchantedEffect(SubType.ISLAND))); + ContinuousEffect effect = new BecomesBasicLandEnchantedEffect(SubType.ISLAND); + effect.addDependencyType(DependencyType.BecomeIsland); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } diff --git a/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java b/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java index 8a4ef440590..bf8bbbc5328 100644 --- a/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java +++ b/Mage.Sets/src/mage/cards/s/StarfieldOfNyx.java @@ -7,8 +7,6 @@ import java.util.stream.Collectors; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; @@ -17,6 +15,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; +import mage.filter.FilterPermanent; import mage.filter.common.FilterEnchantmentPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.AnotherPredicate; @@ -60,9 +59,7 @@ public final class StarfieldOfNyx extends CardImpl { // As long as you control five or more enchantments, each other non-Aura enchantment // you control is a creature in addition to its other types and has base power and // base toughness each equal to its converted mana cost. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect( - new StarfieldOfNyxEffect(), new PermanentsOnTheBattlefieldCondition( - filterEnchantmentYouControl, ComparisonType.MORE_THAN, 4), rule1); + ContinuousEffect effect = new StarfieldOfNyxEffect(); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } @@ -74,79 +71,88 @@ public final class StarfieldOfNyx extends CardImpl { public StarfieldOfNyx copy() { return new StarfieldOfNyx(this); } -} -class StarfieldOfNyxEffect extends ContinuousEffectImpl { + static class StarfieldOfNyxEffect extends ContinuousEffectImpl { - private static final FilterEnchantmentPermanent filter - = new FilterEnchantmentPermanent("Each other non-Aura enchantment you control"); - - static { - filter.add(Predicates.not(SubType.AURA.getPredicate())); - filter.add(AnotherPredicate.instance); - filter.add(TargetController.YOU.getControllerPredicate()); - } - - public StarfieldOfNyxEffect() { - super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); - staticText = "Each other non-Aura enchantment you control is a creature " - + "in addition to its other types and has base power and " - + "toughness each equal to its converted mana cost"; - } - - public StarfieldOfNyxEffect(final StarfieldOfNyxEffect effect) { - super(effect); - } - - @Override - public StarfieldOfNyxEffect copy() { - return new StarfieldOfNyxEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, - source.getControllerId(), source.getSourceId(), game)) { - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (!permanent.isCreature()) { - permanent.addCardType(CardType.CREATURE); - } - } - break; - - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - int manaCost = permanent.getConvertedManaCost(); - permanent.getPower().setValue(manaCost); - permanent.getToughness().setValue(manaCost); - } - } + private static final FilterPermanent filter + = new FilterPermanent("Each other non-Aura enchantment you control"); + private static final FilterEnchantmentPermanent filter2 + = new FilterEnchantmentPermanent(); + static { + filter2.add(TargetController.YOU.getControllerPredicate()); } - return true; - } - @Override - public boolean apply(Game game, Ability source) { - return false; - } + public StarfieldOfNyxEffect() { + super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); + staticText = "Each other non-Aura enchantment you control is a creature " + + "in addition to its other types and has base power and " + + "toughness each equal to its converted mana cost"; + } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.PTChangingEffects_7 - || layer == Layer.TypeChangingEffects_4; - } + public StarfieldOfNyxEffect(final StarfieldOfNyxEffect effect) { + super(effect); + } - @Override - public Set isDependentTo(List allEffectsInLayer) { - return allEffectsInLayer - .stream() - .filter(effect -> effect.getDependencyTypes().contains(DependencyType.AuraAddingRemoving) - || effect.getDependencyTypes().contains(DependencyType.EnchantmentAddingRemoving)) // example: Enchanted Evening - .map(Effect::getId) - .collect(Collectors.toSet()); + @Override + public StarfieldOfNyxEffect copy() { + return new StarfieldOfNyxEffect(this); + } + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + filter.add(CardType.ENCHANTMENT.getPredicate()); + filter.add(Predicates.not(SubType.AURA.getPredicate())); + filter.add(AnotherPredicate.instance); + if (game.getState().getBattlefield().getActivePermanents(filter2, + source.getControllerId(), source.getSourceId(), game).size() > 4) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, + source.getControllerId(), source.getSourceId(), game)) { + switch (layer) { + case TypeChangingEffects_4: + if (sublayer == SubLayer.NA) { + if (!permanent.isCreature() + && !permanent.getSubtype(game).contains(SubType.AURA)) { + permanent.addCardType(CardType.CREATURE); + } + } + break; + + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b + && permanent.isCreature() + && !permanent.getSubtype(game).contains(SubType.AURA)) { + int manaCost = permanent.getConvertedManaCost(); + permanent.getPower().setValue(manaCost); + permanent.getToughness().setValue(manaCost); + } + } + + } + return true; + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.PTChangingEffects_7 + || layer == Layer.TypeChangingEffects_4; + } + + @Override + public Set isDependentTo(List allEffectsInLayer) { + // the dependent classes needs to be an enclosed class for dependent check of continuous effects + return allEffectsInLayer.stream() + .filter(effect -> effect.getDependencyTypes().contains(DependencyType.EnchantmentAddingRemoving) + || effect.getDependencyTypes().contains(DependencyType.AuraAddingRemoving)) // Cloudform + .map(Effect::getId) + .collect(Collectors.toSet()); + } } } diff --git a/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java b/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java index 2f6a72f06be..40e76151eed 100644 --- a/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java +++ b/Mage.Sets/src/mage/cards/s/StormtideLeviathan.java @@ -1,5 +1,3 @@ - - package mage.cards.s; import java.util.UUID; @@ -14,6 +12,7 @@ import mage.abilities.mana.BlueManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Layer; @@ -41,7 +40,7 @@ public final class StormtideLeviathan extends CardImpl { } public StormtideLeviathan(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{U}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}{U}"); this.subtype.add(SubType.LEVIATHAN); this.power = new MageInt(8); @@ -52,7 +51,8 @@ public final class StormtideLeviathan extends CardImpl { // All lands are Islands in addition to their other types. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new StormtideLeviathanEffect())); // Creatures without flying or islandwalk can't attack. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackAnyPlayerAllEffect(Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new CantAttackAnyPlayerAllEffect(Duration.WhileOnBattlefield, filter))); } @@ -65,57 +65,50 @@ public final class StormtideLeviathan extends CardImpl { return new StormtideLeviathan(this); } -} + class StormtideLeviathanEffect extends ContinuousEffectImpl { -class StormtideLeviathanEffect extends ContinuousEffectImpl { - - public StormtideLeviathanEffect() { - super(Duration.WhileOnBattlefield, Outcome.Neutral); - staticText = "All lands are Islands in addition to their other types"; - } - - public StormtideLeviathanEffect(final StormtideLeviathanEffect effect) { - super(effect); - } - - @Override - public StormtideLeviathanEffect copy() { - return new StormtideLeviathanEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (Permanent land : game.getBattlefield().getActivePermanents(new FilterLandPermanent(), source.getControllerId(), game)) { - switch (layer) { - case TypeChangingEffects_4: - if (!land.hasSubtype(SubType.ISLAND, game)) { - land.getSubtype(game).add(SubType.ISLAND); - } - break; - case AbilityAddingRemovingEffects_6: - boolean addAbility = true; - for (Ability existingAbility : land.getAbilities()) { - if (existingAbility instanceof BlueManaAbility) { - addAbility = false; - break; - } - } - if (addAbility) { - land.addAbility(new BlueManaAbility(), source.getSourceId(), game); - } - break; - } + public StormtideLeviathanEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral); + staticText = "All lands are Islands in addition to their other types"; + this.dependencyTypes.add(DependencyType.BecomeIsland); } - return true; - } - @Override - public boolean apply(Game game, Ability source) { - return false; - } + public StormtideLeviathanEffect(final StormtideLeviathanEffect effect) { + super(effect); + } - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + @Override + public StormtideLeviathanEffect copy() { + return new StormtideLeviathanEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + for (Permanent land : game.getBattlefield().getActivePermanents( + new FilterLandPermanent(), source.getControllerId(), game)) { + switch (layer) { + case TypeChangingEffects_4: + // land abilities are intrinsic, so add them here, not in layer 6 + if (!land.hasSubtype(SubType.ISLAND, game)) { + land.getSubtype(game).add(SubType.ISLAND); + if (!land.getAbilities(game).contains(new BlueManaAbility())) { + land.addAbility(new BlueManaAbility(), source.getSourceId(), game); + } + } + break; + } + } + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4; + } } } diff --git a/Mage.Sets/src/mage/cards/u/UrborgTombOfYawgmoth.java b/Mage.Sets/src/mage/cards/u/UrborgTombOfYawgmoth.java index 00284a30f27..5e4f2e852be 100644 --- a/Mage.Sets/src/mage/cards/u/UrborgTombOfYawgmoth.java +++ b/Mage.Sets/src/mage/cards/u/UrborgTombOfYawgmoth.java @@ -1,17 +1,29 @@ - package mage.cards.u; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.continuous.AddCardSubtypeAllEffect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.Effect; import mage.abilities.mana.BlackManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.DependencyType; +import mage.constants.Duration; +import mage.constants.Layer; +import static mage.constants.Layer.TypeChangingEffects_4; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.common.FilterLandPermanent; - -import java.util.UUID; +import mage.game.Game; +import mage.game.permanent.Permanent; /** * @@ -20,13 +32,11 @@ import java.util.UUID; public final class UrborgTombOfYawgmoth extends CardImpl { public UrborgTombOfYawgmoth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); addSuperType(SuperType.LEGENDARY); // Each land is a Swamp in addition to its other land types. - Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(new BlackManaAbility(), Duration.WhileOnBattlefield, new FilterLandPermanent(), - "Each land is a Swamp in addition to its other land types")); - ability.addEffect(new AddCardSubtypeAllEffect(new FilterLandPermanent(), SubType.SWAMP, DependencyType.BecomeSwamp)); + Ability ability = new SimpleStaticAbility(new UrborgTombOfYawgmothEffect()); this.addAbility(ability); } @@ -39,4 +49,62 @@ public final class UrborgTombOfYawgmoth extends CardImpl { public UrborgTombOfYawgmoth copy() { return new UrborgTombOfYawgmoth(this); } + + class UrborgTombOfYawgmothEffect extends ContinuousEffectImpl { + + UrborgTombOfYawgmothEffect() { + super(Duration.WhileOnBattlefield, Outcome.AIDontUseIt); + this.staticText = "Each land is a Swamp in addition to its other land types"; + this.dependencyTypes.add(DependencyType.BecomeSwamp); + } + + UrborgTombOfYawgmothEffect(final UrborgTombOfYawgmothEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public UrborgTombOfYawgmothEffect copy() { + return new UrborgTombOfYawgmothEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + for (Permanent land : game.getBattlefield().getActivePermanents(new FilterLandPermanent(), source.getControllerId(), game)) { + switch (layer) { + case TypeChangingEffects_4: + // 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects + // So the ability removing has to be done before Layer 6 + // Lands have their mana ability intrinsically, so that is added in layer 4 + if (!land.getSubtype(game).contains(SubType.SWAMP)) { + land.getSubtype(game).add(SubType.SWAMP); + } + if (!land.getAbilities().containsRule(new BlackManaAbility())) { + land.addAbility(new BlackManaAbility(), source.getSourceId(), game); + } + break; + } + } + return true; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4; + } + + @Override + public Set isDependentTo(List allEffectsInLayer) { + // the dependent classes needs to be an enclosed class for dependent check of continuous effects + return allEffectsInLayer.stream() + .filter(effect -> mage.cards.b.BloodMoon.class.equals(effect.getClass().getEnclosingClass())) + .map(Effect::getId) + .collect(Collectors.toSet()); // Blood Moon affects non-basic land like Urborg + + } + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/DependentEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/DependentEffectsTest.java index e1cadb86215..d37d80f9f77 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/DependentEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/DependentEffectsTest.java @@ -21,7 +21,8 @@ public class DependentEffectsTest extends CardTestPlayerBase { */ @Test public void testLandsAreDestroyed() { - // Each other non-Aura enchantment is a creature in addition to its other types and has base power and base toughness each equal to its converted mana cost. + // Each other non-Aura enchantment is a creature in addition to its other types + // and has base power and base toughness each equal to its converted mana cost. addCard(Zone.HAND, playerA, "Opalescence", 1); // {2}{W}{W} addCard(Zone.BATTLEFIELD, playerA, "Plains", 9); @@ -34,9 +35,10 @@ public class DependentEffectsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Opalescence"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Enchanted Evening"); - + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPowerToughness(playerA, "Enchanted Evening", 5, 5); assertPowerToughness(playerA, "War Horn", 3, 3); @@ -46,7 +48,7 @@ public class DependentEffectsTest extends CardTestPlayerBase { } /** - * Opalescense is dependent on Enchanted Evening, so it will be applied + * Opalescence is dependent on Enchanted Evening, so it will be applied * after it regardless of timestamp. * * Tokens can also have mana costs, and as a consequence of that, converted @@ -81,6 +83,7 @@ public class DependentEffectsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Enchanted Evening"); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerA, "Rite of Replication", 1); assertGraveyardCount(playerB, "Raise the Alarm", 1); @@ -132,8 +135,11 @@ public class DependentEffectsTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Yixlid Jailer", 1); // Creature - {1}{B} castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yixlid Jailer"); + + setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Yixlid Jailer", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java index e5b179eaecd..224c316f9a5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java @@ -198,11 +198,14 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Kormus Bell"); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Quicksilver Fountain"); + addTarget(playerA, "Mountain"); - + + setStrictChooseMode(true); setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); - + assertAllCommandsUsed(); + assertPermanentCount(playerA, urborgtoy, 1); assertPermanentCount(playerA, "Kormus Bell", 1); assertPermanentCount(playerB, "Quicksilver Fountain", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java index b632fb13df5..bc1b83802f4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LayerTests.java @@ -16,6 +16,77 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * @author jeffwadsworth */ public class LayerTests extends CardTestPlayerBase { + + @Test + public void testMultipleLayeredDependency() { + //Conspiracy->Opalescence->Enchanted Evening + //Conspiracy is dependent on Opalescence + //Opalescence is dependent on Enchanted Evening + //So, the effects should be applied as follows: + //Enchanted Evening->Opalescence->Conspiracy + + addCard(Zone.HAND, playerA, "Conspiracy"); // creatures get chosen subtype + addCard(Zone.HAND, playerA, "Opalescence"); // enchantments become creatures P/T equal to CMC + addCard(Zone.HAND, playerA, "Enchanted Evening"); // all permanents become enchantments + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + addCard(Zone.BATTLEFIELD, playerA, "Glorious Anthem", 1); // keep lands alive // all creatures +1/+1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Conspiracy"); + setChoice(playerA, "Advisor"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Opalescence"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Enchanted Evening"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + assertType("Swamp", CardType.LAND, SubType.ADVISOR); // Subtype changed with Conspiracy + assertPowerToughness(playerA, "Swamp", 1, 1); // boosted with Glorious Anthem + assertType("Enchanted Evening", CardType.ENCHANTMENT, SubType.ADVISOR); // Subtype changed with Conspiracy + assertPowerToughness(playerA, "Enchanted Evening", 6, 6); // boosted with Glorious Anthem + + } + + @Test + public void testMycosynthLatticeAndMarchOfTheMachinesAndHumility() { + // example from Reddit + /* + This came up in a recent EDH game and we had no idea how to progress. + Player A cast a Humility, then a March of the Machines, and finally + a Mycosynth Lattice. + Does the game get stuck in an endless loop of each card gaining and + losing its respective creature-ness and abilities? + Answer: No, they all die + */ + + addCard(Zone.HAND, playerA, "Mycosynth Lattice"); // all permanents are artifacts + addCard(Zone.HAND, playerA, "March of the Machines"); // artifacts become creatures + addCard(Zone.HAND, playerA, "Humility"); // all creatures lose abilities and P/T is 1/1 + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 10); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 10); + addCard(Zone.BATTLEFIELD, playerA, "Island", 10); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Humility"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "March of the Machines"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mycosynth Lattice"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + // everything dies + assertPermanentCount(playerA, "Humility", 0); + assertPermanentCount(playerA, "March of the Machines", 0); + assertPermanentCount(playerA, "Mycosynth Lattice", 0); + assertPermanentCount(playerA, "Island", 0); + + } + @Test public void testBloodMoon_UrborgTombOfYawgmothInteraction() { // Blood Moon : Nonbasic lands are Mountains. diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java index 7e1cfad7480..fd3f20e1635 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffectImpl.java @@ -168,8 +168,12 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu case PTChangingEffects_7: this.affectedObjectsSet = true; } - } else if (hasLayer(Layer.CopyEffects_1) || hasLayer(Layer.ControlChangingEffects_2) || hasLayer(Layer.TextChangingEffects_3) - || hasLayer(Layer.TypeChangingEffects_4) || hasLayer(Layer.ColorChangingEffects_5) || hasLayer(Layer.AbilityAddingRemovingEffects_6) + } else if (hasLayer(Layer.CopyEffects_1) + || hasLayer(Layer.ControlChangingEffects_2) + || hasLayer(Layer.TextChangingEffects_3) + || hasLayer(Layer.TypeChangingEffects_4) + || hasLayer(Layer.ColorChangingEffects_5) + || hasLayer(Layer.AbilityAddingRemovingEffects_6) || hasLayer(Layer.PTChangingEffects_7)) { this.affectedObjectsSet = true; } @@ -185,7 +189,8 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu @Override public void setStartingControllerAndTurnNum(Game game, UUID startingController, UUID activePlayerId) { this.startingControllerId = startingController; - this.startingTurnWasActive = activePlayerId != null && activePlayerId.equals(startingController); // you can't use "game" for active player cause it's called from tests/cheat too + this.startingTurnWasActive = activePlayerId != null + && activePlayerId.equals(startingController); // you can't use "game" for active player cause it's called from tests/cheat too this.yourTurnNumPlayed = 0; } @@ -197,9 +202,11 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu @Override public boolean isYourNextTurn(Game game) { if (this.startingTurnWasActive) { - return yourTurnNumPlayed == 1 && game.isActivePlayer(startingControllerId); + return yourTurnNumPlayed == 1 + && game.isActivePlayer(startingControllerId); } else { - return yourTurnNumPlayed == 0 && game.isActivePlayer(startingControllerId); + return yourTurnNumPlayed == 0 + && game.isActivePlayer(startingControllerId); } } @@ -231,14 +238,18 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu switch (duration) { case UntilYourNextTurn: case UntilEndOfYourNextTurn: - canDelete = player == null || (!player.isInGame() && player.hasReachedNextTurnAfterLeaving()); + canDelete = player == null + || (!player.isInGame() + && player.hasReachedNextTurnAfterLeaving()); } // discard on another conditions (start of your turn) switch (duration) { case UntilYourNextTurn: - if (player != null && player.isInGame()) { - canDelete = canDelete || this.isYourNextTurn(game); + if (player != null + && player.isInGame()) { + canDelete = canDelete + || this.isYourNextTurn(game); } } @@ -305,7 +316,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu @Override public Set isDependentTo(List allEffectsInLayer) { - Set dependentToEffects = new HashSet(); + Set dependentToEffects = new HashSet<>(); if (dependendToTypes != null) { for (ContinuousEffect effect : allEffectsInLayer) { if (!effect.getId().equals(this.getId())) { @@ -354,7 +365,8 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu } /** - * Auto-generates dependencies on different effects (what's apply first and what's apply second) + * Auto-generates dependencies on different effects (what's apply first and + * what's apply second) */ public void generateGainAbilityDependencies(Ability abilityToGain, Filter filterToSearch) { this.addDependencyType(DependencyType.AddingAbility); @@ -369,14 +381,18 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu } private void generateGainAbilityDependenciesFromAbility(CompoundAbility compoundAbility) { - if (compoundAbility == null) return; + if (compoundAbility == null) { + return; + } for (Ability ability : compoundAbility) { generateGainAbilityDependenciesFromAbility(ability); } } private void generateGainAbilityDependenciesFromAbility(Ability ability) { - if (ability == null) return; + if (ability == null) { + return; + } // 1. "Is all type" ability (changeling) // make dependency @@ -386,7 +402,9 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu } private void generateGainAbilityDependenciesFromFilter(Filter filter) { - if (filter == null) return; + if (filter == null) { + return; + } // 1. "Is all type" ability (changeling) // wait dependency diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index 46708c0d2ac..97394349fd9 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -338,7 +338,7 @@ public class ContinuousEffects implements Serializable { } // boolean checkLKI = event.getType().equals(EventType.ZONE_CHANGE) || event.getType().equals(EventType.DESTROYED_PERMANENT); //get all applicable transient Replacement effects - for (Iterator iterator = replacementEffects.iterator(); iterator.hasNext(); ) { + for (Iterator iterator = replacementEffects.iterator(); iterator.hasNext();) { ReplacementEffect effect = iterator.next(); if (!effect.checksEventType(event, game)) { continue; @@ -371,7 +371,7 @@ public class ContinuousEffects implements Serializable { } } - for (Iterator iterator = preventionEffects.iterator(); iterator.hasNext(); ) { + for (Iterator iterator = preventionEffects.iterator(); iterator.hasNext();) { PreventionEffect effect = iterator.next(); if (!effect.checksEventType(event, game)) { continue; @@ -740,8 +740,8 @@ public class ContinuousEffects implements Serializable { * @param event * @param targetAbility ability the event is attached to. can be null. * @param game - * @param silentMode true if the event does not really happen but - * it's checked if the event would be replaced + * @param silentMode true if the event does not really happen but it's + * checked if the event would be replaced * @return */ public boolean preventedByRuleModification(GameEvent event, Ability targetAbility, Game game, boolean silentMode) { @@ -789,7 +789,7 @@ public class ContinuousEffects implements Serializable { do { Map> rEffects = getApplicableReplacementEffects(event, game); // Remove all consumed effects (ability dependant) - for (Iterator it1 = rEffects.keySet().iterator(); it1.hasNext(); ) { + for (Iterator it1 = rEffects.keySet().iterator(); it1.hasNext();) { ReplacementEffect entry = it1.next(); if (consumed.containsKey(entry.getId()) /*&& !(entry instanceof CommanderReplacementEffect) */) { // 903.9. Set consumedAbilitiesIds = consumed.get(entry.getId()); @@ -980,7 +980,7 @@ public class ContinuousEffects implements Serializable { if (!waitingEffects.isEmpty()) { // check if waiting effects can be applied now - for (Iterator>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) { + for (Iterator>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) { Map.Entry> entry = iterator.next(); if (appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself appliedAbilities = appliedEffectAbilities.get(entry.getKey()); @@ -1060,26 +1060,50 @@ public class ContinuousEffects implements Serializable { private void applyLayer(List activeLayerEffects, Layer currentLayer, Game game) { List layer = filterLayeredEffects(activeLayerEffects, currentLayer); + // layer is a list of all effects at the current layer if (!layer.isEmpty()) { int numberOfEffects = layer.size(); + // appliedEffects holds the list of effects currently applied to the layer Set appliedEffects = new HashSet<>(); + // waitingEffects holds the list of dependent effects and their independent counterparts Map> waitingEffects = new LinkedHashMap<>(); for (ContinuousEffect effect : layer) { - if (numberOfEffects > 1) { // If an effect is dependent to not applied effects yet of this layer, so wait to apply this effect + if (numberOfEffects > 1) { + // If an effect is dependent to not applied effects yet of this layer, so wait to apply this effect + // check to see if any effect is dependent to other cards indirectly due to the independent card being dependent, etc. Set dependentTo = effect.isDependentTo(layer); if (!appliedEffects.containsAll(dependentTo)) { waitingEffects.put(effect, dependentTo); continue; } } + // apply the effect applyContinuousEffect(effect, currentLayer, game); + // add it to the applied effects list appliedEffects.add(effect.getId()); + layer = getLayeredEffects(game); + + // check waiting effects to see if it has anything to check if (!waitingEffects.isEmpty()) { // check if waiting effects can be applied now for (Entry> entry : waitingEffects.entrySet()) { - if (appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself + // all dependent to effects are applied now so apply the effect itself + if (appliedEffects.containsAll(entry.getValue())) { applyContinuousEffect(entry.getKey(), currentLayer, game); + // add it to the applied effects list appliedEffects.add(entry.getKey().getId()); + layer = getLayeredEffects(game); + } + } + } + if (numberOfEffects != appliedEffects.size()) { + for (Entry> entry : waitingEffects.entrySet()) { + // all dependent to effects are applied now so apply the effect itself + if (appliedEffects.containsAll(entry.getValue())) { + applyContinuousEffect(entry.getKey(), currentLayer, game); + // add it to the applied effects list + appliedEffects.add(entry.getKey().getId()); + layer = getLayeredEffects(game); } } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java index ebf958615d5..54b223b0329 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java @@ -43,12 +43,15 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); if (permanent != null) { switch (layer) { - case AbilityAddingRemovingEffects_6: + case TypeChangingEffects_4: + // lands intrictically have the mana ability associated with their type, so added here in layer 4 + permanent.getSubtype(game).removeAll(SubType.getLandTypes()); + permanent.getSubtype(game).addAll(landTypes); permanent.removeAllAbilities(source.getSourceId(), game); for (SubType landType : landTypes) { switch (landType) { case SWAMP: - if (permanent.hasSubtype(SubType.SWAMP, game)) { // type can be removed by other effect with newer timestamp, so no ability adding + if (permanent.hasSubtype(SubType.SWAMP, game)) { permanent.addAbility(new BlackManaAbility(), source.getSourceId(), game); } break; @@ -75,11 +78,6 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { } } break; - case TypeChangingEffects_4: - // subtypes are all removed by changing the subtype to a land type. - permanent.getSubtype(game).removeAll(SubType.getLandTypes()); - permanent.getSubtype(game).addAll(landTypes); - break; } return true; } @@ -89,7 +87,7 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + return layer == Layer.TypeChangingEffects_4; } private String setText() { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java index 23de1a78d96..1dde326c6bd 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesBasicLandTargetEffect.java @@ -112,7 +112,8 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { land.addCardType(CardType.LAND); } if (loseOther) { - // 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects + // 305.7 Note that this doesn't remove any abilities + // that were granted to the land by other effects // So the ability removing has to be done before Layer 6 land.removeAllAbilities(source.getSourceId(), game); // 305.7 @@ -127,8 +128,7 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { } } } - break; - case AbilityAddingRemovingEffects_6: + // add intrinsic land abilities here not in layer 6 for (SubType landType : landTypesToAdd) { switch (landType) { case SWAMP: @@ -157,7 +157,7 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl { @Override public boolean hasLayer(Layer layer) { - return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4; + return layer == Layer.TypeChangingEffects_4; } private String setText() { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java index 709fca771a7..cec74cbc6a1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java @@ -25,15 +25,18 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { private boolean loseTypes = false; protected boolean loseName = false; - public BecomesCreatureAllEffect(Token token, String theyAreStillType, FilterPermanent filter, Duration duration, boolean loseColor) { + public BecomesCreatureAllEffect(Token token, String theyAreStillType, + FilterPermanent filter, Duration duration, boolean loseColor) { this(token, theyAreStillType, filter, duration, loseColor, false, false); } - public BecomesCreatureAllEffect(Token token, String theyAreStillType, FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName) { + public BecomesCreatureAllEffect(Token token, String theyAreStillType, + FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName) { this(token, theyAreStillType, filter, duration, loseColor, loseName, false); } - public BecomesCreatureAllEffect(Token token, String theyAreStillType, FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName, boolean loseTypes) { + public BecomesCreatureAllEffect(Token token, String theyAreStillType, + FilterPermanent filter, Duration duration, boolean loseColor, boolean loseName, boolean loseTypes) { super(duration, Outcome.BecomeCreature); this.token = token; this.theyAreStillType = theyAreStillType; @@ -57,7 +60,8 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { public void init(Ability source, Game game) { super.init(source, game); if (this.affectedObjectsSet) { - for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + for (Permanent perm : game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game)) { affectedObjectList.add(new MageObjectReference(perm, game)); } } @@ -76,7 +80,8 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { affectedPermanents.add(ref.getPermanent(game)); } } else { - affectedPermanents = new HashSet<>(game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)); + affectedPermanents = new HashSet<>(game.getBattlefield() + .getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)); } for (Permanent permanent : affectedPermanents) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java index 2a740326103..a1f9fd18bc6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java @@ -30,6 +30,13 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl { this.loseType = loseType; staticText = text; } + + public BecomesCreatureAttachedEffect(Token token, String text, Duration duration, LoseType loseType, Outcome outcome) { + super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, outcome); + this.token = token; + this.loseType = loseType; + staticText = text; + } public BecomesCreatureAttachedEffect(final BecomesCreatureAttachedEffect effect) { super(effect);