diff --git a/Mage.Sets/src/mage/cards/c/CloudstoneCurio.java b/Mage.Sets/src/mage/cards/c/CloudstoneCurio.java index 4f4bda9cfc..d4413d6c57 100644 --- a/Mage.Sets/src/mage/cards/c/CloudstoneCurio.java +++ b/Mage.Sets/src/mage/cards/c/CloudstoneCurio.java @@ -61,7 +61,7 @@ public class CloudstoneCurio extends CardImpl { } public CloudstoneCurio(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // Whenever a nonartifact permanent enters the battlefield under your control, you may return another permanent you control that shares a card type with it to its owner's hand. this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new CloudstoneCurioEffect(), filter, true, SetTargetPointer.PERMANENT, "", true)); @@ -82,7 +82,7 @@ class CloudstoneCurioEffect extends OneShotEffect { public CloudstoneCurioEffect() { super(Outcome.ReturnToHand); - this.staticText = "you may return another permanent you control that shares a card type with it to its owner's hand"; + this.staticText = "you may return another permanent you control that shares a permanent type with it to its owner's hand"; } public CloudstoneCurioEffect(final CloudstoneCurioEffect effect) { @@ -103,12 +103,14 @@ class CloudstoneCurioEffect extends OneShotEffect { triggeringCreature = (Permanent) game.getLastKnownInformation(getTargetPointer().getFirst(game, source), Zone.BATTLEFIELD); } if (triggeringCreature != null) { - FilterPermanent filter = new FilterPermanent("another permanent you control that shares a card type with " + triggeringCreature.getName()); + FilterPermanent filter = new FilterPermanent("another permanent you control that shares a permanent type with " + triggeringCreature.getName()); filter.add(Predicates.not(new PermanentIdPredicate(triggeringCreature.getId()))); filter.add(new ControllerPredicate(TargetController.YOU)); Set cardTypes = new HashSet<>(); for (CardType cardType : triggeringCreature.getCardType()) { - cardTypes.add(new CardTypePredicate(cardType)); + if (cardType.isPermanentType()) { + cardTypes.add(new CardTypePredicate(cardType)); + } } filter.add(Predicates.or(cardTypes)); TargetPermanent target = new TargetPermanent(1, 1, filter, true); diff --git a/Mage.Sets/src/mage/cards/m/MuldrothaTheGravetide.java b/Mage.Sets/src/mage/cards/m/MuldrothaTheGravetide.java new file mode 100644 index 0000000000..d10fa039b4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MuldrothaTheGravetide.java @@ -0,0 +1,230 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.m; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Set; +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.AsThoughEffectType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.watchers.Watcher; + +/** + * + * @author LevelX2 + */ +public class MuldrothaTheGravetide extends CardImpl { + + public MuldrothaTheGravetide(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{G}{U}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.AVATAR); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // During each of your turns, you may play up to one permanent card of each permanent type from your graveyard. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MuldrothaTheGravetideCastFromGraveyardEffect()), new MuldrothaTheGravetideWatcher()); + } + + public MuldrothaTheGravetide(final MuldrothaTheGravetide card) { + super(card); + } + + @Override + public MuldrothaTheGravetide copy() { + return new MuldrothaTheGravetide(this); + } +} + +class MuldrothaTheGravetideCastFromGraveyardEffect extends AsThoughEffectImpl { + + public MuldrothaTheGravetideCastFromGraveyardEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "During each of your turns, you may play up to one permanent card of each permanent type from your graveyard. " + + "(If a card has multiple permanent types, choose one as you play it.)"; + } + + public MuldrothaTheGravetideCastFromGraveyardEffect(final MuldrothaTheGravetideCastFromGraveyardEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public MuldrothaTheGravetideCastFromGraveyardEffect copy() { + return new MuldrothaTheGravetideCastFromGraveyardEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (source.getControllerId().equals(affectedControllerId) && Zone.GRAVEYARD.equals(game.getState().getZone(objectId))) { + MuldrothaTheGravetideWatcher watcher = (MuldrothaTheGravetideWatcher) game.getState().getWatchers().get(MuldrothaTheGravetideWatcher.class.getSimpleName()); + MageObject mageObject = game.getObject(objectId); + if (mageObject != null && watcher != null) { + for (CardType cardType : mageObject.getCardType()) { + if (cardType.isPermanentType()) { + if (!watcher.permanentTypePlayedFromGraveyard(affectedControllerId, cardType)) { + return true; + } + } + } + } + } + return false; + } +} + +class MuldrothaTheGravetideWatcher extends Watcher { + + final HashMap> playerPlayedPermanentTypes = new HashMap<>(); // player that played permanent types from graveyard + private Zone fromZone; + + public MuldrothaTheGravetideWatcher() { + super(MuldrothaTheGravetideWatcher.class.getSimpleName(), WatcherScope.GAME); + } + + public MuldrothaTheGravetideWatcher(final MuldrothaTheGravetideWatcher watcher) { + super(watcher); + playerPlayedPermanentTypes.putAll(watcher.playerPlayedPermanentTypes); + } + + @Override + public MuldrothaTheGravetideWatcher copy() { + return new MuldrothaTheGravetideWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.PLAY_LAND) { + fromZone = game.getState().getZone(event.getTargetId()); // Remember the Zone the land came from + } + if (event.getType() == GameEvent.EventType.LAND_PLAYED && fromZone.equals(Zone.GRAVEYARD)) { + addPermanentTypes(game.getPermanentOrLKIBattlefield(event.getTargetId()), game); + } + + if (event.getType() == GameEvent.EventType.SPELL_CAST) { + Spell spell = (Spell) game.getObject(event.getTargetId()); + if (spell.getFromZone().equals(Zone.GRAVEYARD)) { + addPermanentTypes(spell, game); + } + } + } + + private void addPermanentTypes(Card mageObject, Game game) { + if (mageObject != null) { + UUID playerId = null; + if (mageObject instanceof Spell) { + playerId = ((Spell) mageObject).getControllerId(); + } else if (mageObject instanceof Permanent) { + playerId = ((Permanent) mageObject).getControllerId(); + } + if (playerId != null) { + Set permanentTypes = playerPlayedPermanentTypes.get(playerId); + if (permanentTypes == null) { + permanentTypes = EnumSet.noneOf(CardType.class); + playerPlayedPermanentTypes.put(playerId, permanentTypes); + } + Set typesNotCast = EnumSet.noneOf(CardType.class); + for (CardType cardType : mageObject.getCardType()) { + if (cardType.isPermanentType()) { + if (!permanentTypes.contains(cardType)) { + typesNotCast.add(cardType); + } + } + } + if (typesNotCast.size() <= 1) { + permanentTypes.addAll(typesNotCast); + } else { + Player player = game.getPlayer(playerId); + if (player != null) { + Choice typeChoice = new ChoiceImpl(true); + typeChoice.setMessage("Choose permanent type you consume for casting from graveyard."); + for (CardType cardType : typesNotCast) { + typeChoice.getChoices().add(cardType.toString()); + } + if (player.choose(Outcome.Detriment, typeChoice, game)) { + String typeName = typeChoice.getChoice(); + CardType chosenType = null; + for (CardType cardType : CardType.values()) { + if (cardType.toString().equals(typeName)) { + chosenType = cardType; + break; + } + } + if (chosenType != null) { + permanentTypes.add(chosenType); + } + } + } + } + } + } + } + + @Override + public void reset() { + playerPlayedPermanentTypes.clear(); + super.reset(); + } + + public boolean permanentTypePlayedFromGraveyard(UUID playerId, CardType cardType) { + Set permanentTypes = playerPlayedPermanentTypes.get(playerId); + if (permanentTypes != null) { + return permanentTypes.contains(cardType); + } + return false; + } + +} diff --git a/Mage.Sets/src/mage/sets/Dominaria.java b/Mage.Sets/src/mage/sets/Dominaria.java index 9740c616fe..936796e4b7 100644 --- a/Mage.Sets/src/mage/sets/Dominaria.java +++ b/Mage.Sets/src/mage/sets/Dominaria.java @@ -96,7 +96,7 @@ public class Dominaria extends ExpansionSet { cards.add(new SetCardInfo("Cloudreader Sphinx", 47, Rarity.COMMON, mage.cards.c.CloudreaderSphinx.class)); cards.add(new SetCardInfo("Cold-Water Snapper", 48, Rarity.COMMON, mage.cards.c.ColdWaterSnapper.class)); cards.add(new SetCardInfo("Curator's Ward", 49, Rarity.UNCOMMON, mage.cards.c.CuratorsWard.class)); - cards.add(new SetCardInfo("Corrosive Ooze", 158, Rarity.COMMON, mage.cards.c.CorrosiveOoze.class)); + cards.add(new SetCardInfo("Corrosive Ooze", 158, Rarity.COMMON, mage.cards.c.CorrosiveOoze.class)); cards.add(new SetCardInfo("D'Avenant Trapper", 11, Rarity.COMMON, mage.cards.d.DAvenantTrapper.class)); cards.add(new SetCardInfo("Damping Sphere", 213, Rarity.UNCOMMON, mage.cards.d.DampingSphere.class)); cards.add(new SetCardInfo("Danitha Capashen, Paragon", 12, Rarity.UNCOMMON, mage.cards.d.DanithaCapashenParagon.class)); @@ -199,6 +199,7 @@ public class Dominaria extends ExpansionSet { cards.add(new SetCardInfo("Mountain", 263, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 264, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 265, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Muldrotha, the Gravetide", 199, Rarity.MYTHIC, mage.cards.m.MuldrothaTheGravetide.class)); cards.add(new SetCardInfo("Multani, Yavimaya's Avatar", 174, Rarity.MYTHIC, mage.cards.m.MultaniYavimayasAvatar.class)); cards.add(new SetCardInfo("Mox Amber", 224, Rarity.MYTHIC, mage.cards.m.MoxAmber.class)); cards.add(new SetCardInfo("Naban, Dean of Iteration", 58, Rarity.RARE, mage.cards.n.NabanDeanOfIteration.class)); @@ -225,7 +226,7 @@ public class Dominaria extends ExpansionSet { cards.add(new SetCardInfo("Radiating Lightning", 138, Rarity.COMMON, mage.cards.r.RadiatingLightning.class)); cards.add(new SetCardInfo("Raff Capashen, Ship's Mage", 202, Rarity.UNCOMMON, mage.cards.r.RaffCapashenShipsMage.class)); cards.add(new SetCardInfo("Rampaging Cyclops", 139, Rarity.COMMON, mage.cards.r.RampagingCyclops.class)); - cards.add(new SetCardInfo("Rat Colony", 101, Rarity.COMMON, mage.cards.r.RatColony.class)); + cards.add(new SetCardInfo("Rat Colony", 101, Rarity.COMMON, mage.cards.r.RatColony.class)); cards.add(new SetCardInfo("Relic Runner", 62, Rarity.COMMON, mage.cards.r.RelicRunner.class)); cards.add(new SetCardInfo("Rescue", 63, Rarity.COMMON, mage.cards.r.Rescue.class)); cards.add(new SetCardInfo("Rite of Belzenlok", 102, Rarity.RARE, mage.cards.r.RiteOfBelzenlok.class)); @@ -321,4 +322,3 @@ public class Dominaria extends ExpansionSet { cards.add(new SetCardInfo("Zhalfirin Void", 249, Rarity.UNCOMMON, mage.cards.z.ZhalfirinVoid.class)); } } - diff --git a/Mage/src/main/java/mage/constants/CardType.java b/Mage/src/main/java/mage/constants/CardType.java index 8b0e464abe..d060f5aa04 100644 --- a/Mage/src/main/java/mage/constants/CardType.java +++ b/Mage/src/main/java/mage/constants/CardType.java @@ -8,20 +8,22 @@ import java.util.EnumSet; * @author North */ public enum CardType { - ARTIFACT("Artifact"), - CONSPIRACY("Conspiracy"), - CREATURE("Creature"), - ENCHANTMENT("Enchantment"), - INSTANT("Instant"), - LAND("Land"), - PLANESWALKER("Planeswalker"), - SORCERY("Sorcery"), - TRIBAL("Tribal"); + ARTIFACT("Artifact", true), + CONSPIRACY("Conspiracy", false), + CREATURE("Creature", true), + ENCHANTMENT("Enchantment", true), + INSTANT("Instant", false), + LAND("Land", true), + PLANESWALKER("Planeswalker", true), + SORCERY("Sorcery", false), + TRIBAL("Tribal", false); private final String text; + private final boolean permanentType; - CardType(String text) { + CardType(String text, boolean permanentType) { this.text = text; + this.permanentType = permanentType; } @Override @@ -29,9 +31,17 @@ public enum CardType { return text; } + public boolean isPermanentType() { + return permanentType; + } + /** * Returns all of the card types from two lists of card types. Duplicates * are eliminated. + * + * @param a + * @param b + * @return */ public static CardType[] mergeTypes(CardType[] a, CardType[] b) { EnumSet cardTypes = EnumSet.noneOf(CardType.class);