diff --git a/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java b/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java
index ba9584e3488..0079b741b7d 100644
--- a/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java
+++ b/Mage.Sets/src/mage/cards/c/CryOfTheCarnarium.java
@@ -64,15 +64,14 @@ class CryOfTheCarnariumExileEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
- Player player = game.getPlayer(source.getControllerId());
+ Player controller = game.getPlayer(source.getControllerId());
CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class);
- if (player == null || watcher == null) {
- return false;
- }
- Cards cards = new CardsImpl(watcher.getCardsPutToGraveyardFromBattlefield(game));
+ if (controller == null || watcher == null) { return false; }
+
+ Cards cards = new CardsImpl(watcher.getCardsPutIntoGraveyardFromBattlefield(game));
cards.removeIf(uuid -> !game.getCard(uuid).isCreature(game));
- player.moveCards(cards, Zone.EXILED, source, game);
- return true;
+
+ return controller.moveCards(cards, Zone.EXILED, source, game);
}
}
diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java b/Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java
new file mode 100644
index 00000000000..da089a8c8c3
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java
@@ -0,0 +1,79 @@
+package mage.cards.m;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
+import mage.abilities.costs.common.RemoveCountersSourceCost;
+import mage.abilities.effects.common.CopyTargetSpellEffect;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.TargetController;
+import mage.counters.CounterType;
+import mage.filter.FilterSpell;
+import mage.filter.predicate.Predicate;
+import mage.game.Game;
+import mage.game.stack.StackObject;
+import mage.target.TargetSpell;
+import mage.watchers.common.CastFromHandWatcher;
+
+import java.util.UUID;
+
+/**
+ * @author Alex-Vasile
+ */
+public class MyojinOfCrypticDreams extends CardImpl {
+
+ private static final FilterSpell permanentSpellFilter = new FilterSpell("permanent spell you control");
+ static {
+ permanentSpellFilter.add(TargetController.YOU.getControllerPredicate());
+ permanentSpellFilter.add(MyojinOfCrypticDreamsPredicate.instance);
+ }
+
+ public MyojinOfCrypticDreams(UUID ownderId, CardSetInfo setInfo) {
+ super(ownderId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}{U}");
+
+ this.addSuperType(SuperType.LEGENDARY);
+ this.subtype.add(SubType.SPIRIT);
+ this.power = new MageInt(3);
+ this.toughness = new MageInt(3);
+
+ // Myojin of Cryptic Dreams enters the battlefield with an indestructible counter on it if you cast it from your hand.
+ this.addAbility(new EntersBattlefieldAbility(
+ new AddCountersSourceEffect(CounterType.INDESTRUCTIBLE.createInstance()),
+ CastFromHandSourcePermanentCondition.instance, null,
+ "with an indestructible counter on it if you cast it from your hand"
+ ), new CastFromHandWatcher());
+
+ // Remove an indestructible counter from Myojin of Cryptic Dreams:
+ // Copy target permanent spell you control three times. (The copies become tokens.)
+ Ability ability = new SimpleActivatedAbility(
+ new CopyTargetSpellEffect(false, false, false)
+ .setText("Copy target permanent spell you control three times. (The copies become tokens.)"),
+ new RemoveCountersSourceCost(CounterType.INDESTRUCTIBLE.createInstance())
+ );
+ ability.addEffect(new CopyTargetSpellEffect(false, false, false).setText(" "));
+ ability.addEffect(new CopyTargetSpellEffect(false, false, false).setText(" "));
+ ability.addTarget(new TargetSpell(permanentSpellFilter));
+ this.addAbility(ability);
+ }
+
+ private MyojinOfCrypticDreams(final MyojinOfCrypticDreams card) { super(card); }
+
+ @Override
+ public MyojinOfCrypticDreams copy() { return new MyojinOfCrypticDreams(this); }
+}
+
+enum MyojinOfCrypticDreamsPredicate implements Predicate {
+ instance;
+
+ @Override
+ public boolean apply(StackObject input, Game game) {
+ return input.isPermanent(game);
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfGrimBetrayal.java b/Mage.Sets/src/mage/cards/m/MyojinOfGrimBetrayal.java
new file mode 100644
index 00000000000..21cc2a0feb6
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/m/MyojinOfGrimBetrayal.java
@@ -0,0 +1,101 @@
+package mage.cards.m;
+
+import mage.MageInt;
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.condition.common.CastFromHandSourcePermanentCondition;
+import mage.abilities.costs.common.RemoveCountersSourceCost;
+import mage.abilities.dynamicvalue.DynamicValue;
+import mage.abilities.dynamicvalue.common.CardsInAllGraveyardsCount;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.counter.AddCountersSourceEffect;
+import mage.abilities.hint.Hint;
+import mage.abilities.hint.ValueHint;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.cards.Cards;
+import mage.cards.CardsImpl;
+import mage.constants.*;
+import mage.counters.CounterType;
+import mage.filter.common.FilterCreatureCard;
+import mage.filter.predicate.card.PutIntoGraveFromAnywhereThisTurnPredicate;
+import mage.game.Game;
+import mage.players.Player;
+import mage.watchers.common.CardsPutIntoGraveyardWatcher;
+import mage.watchers.common.CastFromHandWatcher;
+
+import java.util.UUID;
+
+/**
+* @author Alex-Vasile
+*/
+public class MyojinOfGrimBetrayal extends CardImpl {
+
+ private static final FilterCreatureCard filter = new FilterCreatureCard();
+ static { filter.add(PutIntoGraveFromAnywhereThisTurnPredicate.instance); }
+ private static final DynamicValue xValue = new CardsInAllGraveyardsCount(filter);
+ private static final Hint hint = new ValueHint("Permanents put into the graveyard this turn", xValue);
+
+ public MyojinOfGrimBetrayal(UUID ownderId, CardSetInfo setInfo) {
+ super(ownderId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}{B}");
+
+ this.addSuperType(SuperType.LEGENDARY);
+ this.subtype.add(SubType.SPIRIT);
+ this.power = new MageInt(5);
+ this.toughness = new MageInt(2);
+
+ // Myojin of Grim Betrayal enters the battlefield with an indestructible counter on it if you cast it from your hand.
+ this.addAbility(new EntersBattlefieldAbility(
+ new AddCountersSourceEffect(CounterType.INDESTRUCTIBLE.createInstance()),
+ CastFromHandSourcePermanentCondition.instance, null,
+ "with an indestructible counter on it if you cast it from your hand"
+ ), new CastFromHandWatcher());
+
+ // Remove an indestructible counter from Myojin of Grim Betrayal:
+ // Put onto the battlefield under your control all creature cards in all graveyards that were put there from anywhere this turn.
+ Ability ability = new SimpleActivatedAbility(
+ new MyojinOfGrimBetrayalEffect(filter),
+ new RemoveCountersSourceCost(CounterType.INDESTRUCTIBLE.createInstance())
+ ).addHint(hint);
+ ability.addWatcher(new CardsPutIntoGraveyardWatcher());
+ this.addAbility(ability);
+ }
+
+ private MyojinOfGrimBetrayal(final MyojinOfGrimBetrayal card) { super(card); }
+
+ @Override
+ public MyojinOfGrimBetrayal copy() {return new MyojinOfGrimBetrayal(this); }
+}
+
+class MyojinOfGrimBetrayalEffect extends OneShotEffect {
+
+ private final FilterCreatureCard filter;
+
+ MyojinOfGrimBetrayalEffect(FilterCreatureCard filter) {
+ super(Outcome.PutCardInPlay);
+ this.filter = filter;
+ this.staticText = "Put onto the battlefield under your control all creature cards in all graveyards " +
+ "that were put there from anywhere this turn";
+ }
+
+ private MyojinOfGrimBetrayalEffect(final MyojinOfGrimBetrayalEffect effect) {
+ super(effect);
+ this.filter = effect.filter;
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ Player controller = game.getPlayer(source.getControllerId());
+ CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class);
+ if (controller == null || watcher == null) { return false; }
+
+ Cards cards = new CardsImpl(watcher.getCardsPutIntoGraveyardFromBattlefield(game));
+ cards.removeIf(uuid -> !game.getCard(uuid).isCreature(game));
+
+ return controller.moveCards(cards, Zone.BATTLEFIELD, source, game);
+ }
+
+ @Override
+ public MyojinOfGrimBetrayalEffect copy() { return new MyojinOfGrimBetrayalEffect(this); }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/t/ThrillingEncore.java b/Mage.Sets/src/mage/cards/t/ThrillingEncore.java
index 865b21b9ea5..81e837ea137 100644
--- a/Mage.Sets/src/mage/cards/t/ThrillingEncore.java
+++ b/Mage.Sets/src/mage/cards/t/ThrillingEncore.java
@@ -67,17 +67,12 @@ class ThrillingEncoreEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
- if (controller == null) {
- return false;
- }
- Cards cards = new CardsImpl();
- for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
- Player player = game.getPlayer(playerId);
- if (player == null) {
- continue;
- }
- cards.addAll(player.getGraveyard().getCards(filter, source.getSourceId(), playerId, game));
- }
+ CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class);
+ if (controller == null || watcher == null) { return false; }
+
+ Cards cards = new CardsImpl(watcher.getCardsPutIntoGraveyardFromBattlefield(game));
+ cards.removeIf(uuid -> !game.getCard(uuid).isCreature(game));
+
return controller.moveCards(cards, Zone.BATTLEFIELD, source, game);
}
}
diff --git a/Mage.Sets/src/mage/sets/NeonDynastyCommander.java b/Mage.Sets/src/mage/sets/NeonDynastyCommander.java
index 0a569ce25a3..33a97a80050 100644
--- a/Mage.Sets/src/mage/sets/NeonDynastyCommander.java
+++ b/Mage.Sets/src/mage/sets/NeonDynastyCommander.java
@@ -77,6 +77,8 @@ public final class NeonDynastyCommander extends ExpansionSet {
cards.add(new SetCardInfo("Mirage Mirror", 154, Rarity.RARE, mage.cards.m.MirageMirror.class));
cards.add(new SetCardInfo("Mossfire Valley", 171, Rarity.RARE, mage.cards.m.MossfireValley.class));
cards.add(new SetCardInfo("Myojin of Blooming Dawn", 31, Rarity.RARE, mage.cards.m.MyojinOfBloomingDawn.class));
+ cards.add(new SetCardInfo("Myojin of Cryptic Dreams", 33, Rarity.RARE, mage.cards.m.MyojinOfCrypticDreams.class));
+ cards.add(new SetCardInfo("Myojin of Grim Betrayal", 34, Rarity.RARE, mage.cards.m.MyojinOfGrimBetrayal.class));
cards.add(new SetCardInfo("Myojin of Roaring Blades", 36, Rarity.RARE, mage.cards.m.MyojinOfRoaringBlades.class));
cards.add(new SetCardInfo("Myojin of Towering Might", 38, Rarity.RARE, mage.cards.m.MyojinOfToweringMight.class));
cards.add(new SetCardInfo("Myrsmith", 86, Rarity.UNCOMMON, mage.cards.m.Myrsmith.class));
diff --git a/Mage/src/main/java/mage/filter/predicate/card/PutIntoGraveFromAnywhereThisTurnPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/PutIntoGraveFromAnywhereThisTurnPredicate.java
new file mode 100644
index 00000000000..a6fdaab53d7
--- /dev/null
+++ b/Mage/src/main/java/mage/filter/predicate/card/PutIntoGraveFromAnywhereThisTurnPredicate.java
@@ -0,0 +1,20 @@
+package mage.filter.predicate.card;
+
+import mage.cards.Card;
+import mage.filter.predicate.Predicate;
+import mage.game.Game;
+import mage.watchers.common.CardsPutIntoGraveyardWatcher;
+
+/**
+ * @author Alex-Vasile
+ */
+public enum PutIntoGraveFromAnywhereThisTurnPredicate implements Predicate {
+ instance;
+
+ @Override
+ public boolean apply(Card input, Game game) {
+ CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class);
+
+ return watcher != null && watcher.checkCardFromAnywhere(input, game);
+ }
+}
diff --git a/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java b/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java
index 75ed33cdb59..2c3211c1daa 100644
--- a/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java
+++ b/Mage/src/main/java/mage/watchers/common/CardsPutIntoGraveyardWatcher.java
@@ -13,16 +13,20 @@ import java.util.*;
import java.util.stream.Collectors;
/**
- * Counts amount of cards put into graveyards of players during the current
- * turn. Also the UUIDs of cards that went to graveyard from Battlefield this
- * turn.
+ * Counts how many cards are put into each player's graveyard this turn.
+ * Keeps track of the UUIDs of the cards that went to graveyard this turn.
+ * from the battlefield, from anywhere other both from anywhere and from only the battlefield.
*
* @author LevelX2
*/
public class CardsPutIntoGraveyardWatcher extends Watcher {
+ // Number of cards that have entered each players graveyards
private final Map amountOfCardsThisTurn = new HashMap<>();
- private final Set cardsPutToGraveyardFromBattlefield = new HashSet<>();
+ // UUID of cards that entered the graveyard from the battlefield
+ private final Set cardsPutIntoGraveyardFromBattlefield = new HashSet<>();
+ // UUID of cards that entered the graveyard from everywhere other than the battlefield
+ private final Set cardsPutIntoGraveyardFromEverywhereElse = new HashSet<>();
public CardsPutIntoGraveyardWatcher() {
super(WatcherScope.GAME);
@@ -34,33 +38,102 @@ public class CardsPutIntoGraveyardWatcher extends Watcher {
|| ((ZoneChangeEvent) event).getToZone() != Zone.GRAVEYARD) {
return;
}
+
UUID playerId = event.getPlayerId();
if (playerId == null || game.getCard(event.getTargetId()) == null) {
return;
}
+
amountOfCardsThisTurn.compute(playerId, (k, amount) -> amount == null ? 1 : Integer.sum(amount, 1));
+
if (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) {
- cardsPutToGraveyardFromBattlefield.add(new MageObjectReference(((ZoneChangeEvent) event).getTarget(), game, 1));
+ cardsPutIntoGraveyardFromBattlefield.add(new MageObjectReference(((ZoneChangeEvent) event).getTarget(), game, 1));
+ } else {
+ cardsPutIntoGraveyardFromEverywhereElse.add(new MageObjectReference(((ZoneChangeEvent) event).getTarget(), game, 1));
}
}
+ /**
+ * The number of cards that were put into a specific player's graveyard this turn.
+ *
+ * @param playerId The player's UUID.
+ * @return The number of cards.
+ */
public int getAmountCardsPutToGraveyard(UUID playerId) {
return amountOfCardsThisTurn.getOrDefault(playerId, 0);
}
- public Set getCardsPutToGraveyardFromBattlefield(Game game) {
- return cardsPutToGraveyardFromBattlefield.stream().map(mor -> mor.getCard(game)).filter(Objects::nonNull).collect(Collectors.toSet());
+ /**
+ * The cards put into any graveyard from the battelfield this turn.
+ *
+ * @param game The game to check for.
+ * @return A set containing the card objects.
+ */
+ public Set getCardsPutIntoGraveyardFromBattlefield(Game game) {
+ return cardsPutIntoGraveyardFromBattlefield.stream().map(mor -> mor.getCard(game)).filter(Objects::nonNull).collect(Collectors.toSet());
}
+ /**
+ * The cards put into any graveyard from anywhere other than the battelfield this turn.
+ *
+ * @param game The game to check for.
+ * @return A set containing the card objects.
+ */
+ public Set getCardsPutIntoGraveyardNotFromBattlefield(Game game) {
+ return cardsPutIntoGraveyardFromEverywhereElse.stream().map(mor -> mor.getCard(game)).filter(Objects::nonNull).collect(Collectors.toSet());
+ }
+
+ /**
+ * The cards put into any graveyard from anywhere this turn.
+ *
+ * @param game The game to check for.
+ * @return A set containing the card objects.
+ */
+ public Set getCardsPutIntoGraveyardFromAnywhere(Game game) {
+ Set cardsPutIntoGraveyardFromAnywhere = getCardsPutIntoGraveyardFromBattlefield(game);
+ cardsPutIntoGraveyardFromAnywhere.addAll(getCardsPutIntoGraveyardNotFromBattlefield(game));
+
+ return cardsPutIntoGraveyardFromAnywhere;
+ }
+
+ /**
+ * Check if the passed card was put into the graveyard from the battlefield this turn.
+ *
+ * @param card The card to check.
+ * @param game The game to check for.
+ * @return Boolean indicating if the card was put into the graveyard from the battlefield this turn.
+ */
public boolean checkCardFromBattlefield(Card card, Game game) {
- return cardsPutToGraveyardFromBattlefield.stream().anyMatch(mor -> mor.refersTo(card, game));
+ return cardsPutIntoGraveyardFromBattlefield.stream().anyMatch(mor -> mor.refersTo(card, game));
+ }
+
+ /**
+ * Check if the passed card was put into the graveyard from anywhere other than the battlefield this turn.
+ *
+ * @param card The card to check.
+ * @param game The game to check for.
+ * @return Boolean indicating if the card was put into the graveyard from anywhere other than the battlefield this turn.
+ */
+ public boolean checkCardNotFromBattlefield(Card card, Game game) {
+ return cardsPutIntoGraveyardFromEverywhereElse.stream().anyMatch(mor -> mor.refersTo(card, game));
+ }
+
+ /**
+ * Check if the passed card was put into the graveyard from anywhere this turn.
+ *
+ * @param card The card to check.
+ * @param game The game to check for.
+ * @return Boolean indicating if the card was put into the graveyard from anywhere this turn.
+ */
+ public boolean checkCardFromAnywhere(Card card, Game game) {
+ return checkCardFromBattlefield(card, game) || checkCardNotFromBattlefield(card, game);
}
@Override
public void reset() {
super.reset();
amountOfCardsThisTurn.clear();
- cardsPutToGraveyardFromBattlefield.clear();
+ cardsPutIntoGraveyardFromBattlefield.clear();
+ cardsPutIntoGraveyardFromEverywhereElse.clear();
}
-
}