mirror of
https://github.com/correl/mage.git
synced 2024-12-25 03:00:15 +00:00
[NEO] Implemented Myojin of Cryptic Dreams and Myojin of Grim Betrayal (#8680)
* - Implemented Myojin of Cryptic Dreams and Myojin of Grim Betrayal. - Updated CardsPutIntoGraveyardWatcher to keep track of all cards that entered the graveyard. - Added documentation to CardsPutIntoGraveyardWatcher. * Fixed add indestructible counter ability
This commit is contained in:
parent
8010ce50e4
commit
3709b5c098
7 changed files with 297 additions and 28 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
79
Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java
Normal file
79
Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java
Normal file
|
@ -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. <i>(The copies become tokens.)</i>"),
|
||||
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<StackObject> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(StackObject input, Game game) {
|
||||
return input.isPermanent(game);
|
||||
}
|
||||
}
|
101
Mage.Sets/src/mage/cards/m/MyojinOfGrimBetrayal.java
Normal file
101
Mage.Sets/src/mage/cards/m/MyojinOfGrimBetrayal.java
Normal file
|
@ -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); }
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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<Card> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Card input, Game game) {
|
||||
CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class);
|
||||
|
||||
return watcher != null && watcher.checkCardFromAnywhere(input, game);
|
||||
}
|
||||
}
|
|
@ -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<UUID, Integer> amountOfCardsThisTurn = new HashMap<>();
|
||||
private final Set<MageObjectReference> cardsPutToGraveyardFromBattlefield = new HashSet<>();
|
||||
// UUID of cards that entered the graveyard from the battlefield
|
||||
private final Set<MageObjectReference> cardsPutIntoGraveyardFromBattlefield = new HashSet<>();
|
||||
// UUID of cards that entered the graveyard from everywhere other than the battlefield
|
||||
private final Set<MageObjectReference> 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<Card> 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<Card> 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<Card> 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<Card> getCardsPutIntoGraveyardFromAnywhere(Game game) {
|
||||
Set<Card> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue