- [NEO] added Kotose, the Silent Spider

This commit is contained in:
Jeff Wadsworth 2022-02-10 18:44:04 -06:00
parent 42592072c2
commit ca03d7d49d
2 changed files with 364 additions and 0 deletions

View file

@ -0,0 +1,363 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.cards.k;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalAsThoughEffect;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.AsThoughEffectType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.ManaType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.card.OwnerIdPredicate;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInOpponentsGraveyard;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import mage.watchers.Watcher;
/**
*
* @author jeffwadsworth
*/
public final class KotoseTheSilentSpider extends CardImpl {
public KotoseTheSilentSpider(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{B}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.NINJA);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// When Kotose, Silent Spider enters the battlefield, exile target card in an opponent's graveyard other than a basic land card. Search that player's graveyard, hand, and library for any number of cards with the same name as that card and exile them. For as long as you control Kotose, you may play one of the exiled cards, and you may spend mana as though it were mana of any color to cast it.
this.addAbility(new EntersBattlefieldTriggeredAbility(new KotoseTheSilentSpiderEffect()), new KotoseTheSilentSpiderWatcher(super.getId()));
}
private KotoseTheSilentSpider(final KotoseTheSilentSpider card) {
super(card);
}
@Override
public KotoseTheSilentSpider copy() {
return new KotoseTheSilentSpider(this);
}
}
class KotoseTheSilentSpiderEffect extends OneShotEffect {
private static final FilterCard filter = new FilterCard("card other than a basic land card in an opponent's graveyard");
static {
filter.add(Predicates.not(Predicates.and(CardType.LAND.getPredicate(), SuperType.BASIC.getPredicate())));
}
Cards storeCardsThatWereInExile = new CardsImpl();
Set<Card> cardsToExile = new HashSet<>();
public KotoseTheSilentSpiderEffect() {
super(Outcome.Benefit);
this.staticText = "exile target card in an opponent's graveyard other than a basic land card. Search that player's graveyard, hand, and library for any number of cards with the same name as that card and exile them. For as long as you control Kotose, you may play one of the exiled cards, and you may spend mana as though it were mana of any color to cast it.";
}
public KotoseTheSilentSpiderEffect(final KotoseTheSilentSpiderEffect effect) {
super(effect);
}
@Override
public KotoseTheSilentSpiderEffect copy() {
return new KotoseTheSilentSpiderEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
storeCardsThatWereInExile.clear();
cardsToExile.clear();
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
TargetCardInOpponentsGraveyard target = new TargetCardInOpponentsGraveyard(filter);
if (controller.choose(Outcome.Neutral, target, source.getSourceId(), game)
&& game.getCard(target.getFirstTarget()) != null) {
Card chosenCard = game.getCard(target.getFirstTarget());
controller.moveCardsToExile(chosenCard, source, game, true, exileId, game.getObject(source.getSourceId()).getIdName());
Player exiledCardOwner = game.getPlayer(game.getOwnerId(chosenCard.getId()));
if (exiledCardOwner == null) {
return false;
}
// search their graveyard
FilterCard filterCardSameNameAsTargetedExiled = new FilterCard();
filterCardSameNameAsTargetedExiled.add(new NamePredicate(chosenCard.getName()));
filterCardSameNameAsTargetedExiled.add(new OwnerIdPredicate(exiledCardOwner.getId()));
Set<Card> cardsInGraveyard = exiledCardOwner.getGraveyard().getCards(game);
Cards cards = new CardsImpl();
cards.addAll(cardsInGraveyard);
TargetCard targetCards = new TargetCard(0, Integer.MAX_VALUE, Zone.GRAVEYARD, filterCardSameNameAsTargetedExiled.withMessage("card(s) with the same name as the chosen card from the owner's graveyard"));
if (controller.choose(outcome, cards, targetCards, game)) {
for (UUID cardId : targetCards.getTargets()) {
cardsToExile.add(game.getCard(cardId));
}
}
// search their hand
Set<Card> cardsInHand = exiledCardOwner.getHand().getCards(game);
Cards cards2 = new CardsImpl();
cards2.addAll(cardsInHand);
TargetCard targetCards3 = new TargetCard(0, Integer.MAX_VALUE, Zone.HAND, filterCardSameNameAsTargetedExiled.withMessage("card(s) with the same name as the chosen card from the owner's hand"));
if (controller.choose(outcome, cards2, targetCards3, game)) {
for (UUID cardId : targetCards3.getTargets()) {
cardsToExile.add(game.getCard(cardId));
}
}
// search their library
List<Card> cardsInLibrary = exiledCardOwner.getLibrary().getCards(game);
Cards cards3 = new CardsImpl();
cards3.addAll(cardsInLibrary);
TargetCard targetCards4 = new TargetCard(0, Integer.MAX_VALUE, Zone.LIBRARY, filterCardSameNameAsTargetedExiled.withMessage("card(s) with the same name as the chosen card from the owner's library"));
if (controller.choose(outcome, cards3, targetCards4, game)) {
for (UUID cardId : targetCards4.getTargets()) {
cardsToExile.add(game.getCard(cardId));
}
}
// move all the chosen cards to exile at the same time
cardsToExile.add(chosenCard); // don't forget the chosen card!
controller.moveCardsToExile(cardsToExile, source, game, true, exileId, game.getObject(source.getSourceId()).getIdName());
}
// used as a check for any cards that were put into exile (when cast, they are moved to the stack, etc)
storeCardsThatWereInExile.addAll(cardsToExile);
KotoseTheSilentSpiderPlayEffect effect = new KotoseTheSilentSpiderPlayEffect();
ConditionalAsThoughEffect conditionalEffect = new ConditionalAsThoughEffect(effect, new KotoseTheSilentSpiderCondition());
conditionalEffect.setDuration(Duration.WhileControlled);
game.getState().setValue("Result Kotose" + source.getSourceId().toString(), Boolean.TRUE);
for (Card exiledCard : cardsToExile) {
if (game.getState().getZone(exiledCard.getId()) == Zone.EXILED) {
storeCardsThatWereInExile.add(exiledCard);
conditionalEffect.setTargetPointer(new FixedTarget(exiledCard.getId(), game));
game.addEffect(conditionalEffect, source);
}
}
game.addEffect(new KotoseTheSilentSpiderAnyColorManaEffect(), source);
game.getState().setValue("Cards Exile Kotose" + source.getSourceId().toString(), storeCardsThatWereInExile);
return true;
}
return false;
}
}
class KotoseTheSilentSpiderCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
Watcher watcher = game.getState().getWatcher(KotoseTheSilentSpiderWatcher.class);
return (watcher != null
&& watcher.conditionMet());
}
}
class KotoseTheSilentSpiderWatcher extends Watcher {
UUID sourceCardId;
public KotoseTheSilentSpiderWatcher(UUID sourceCardId) {
super(WatcherScope.GAME);
this.sourceCardId = sourceCardId;
}
@Override
public void watch(GameEvent event, Game game) {
if (game.getState().getValue("Result Kotose" + sourceCardId.toString()) != null) {
condition = (Boolean) game.getState().getValue("Result Kotose" + sourceCardId.toString());
}
UUID exileId = CardUtil.getExileZoneId(game, sourceCardId, game.getState().getZoneChangeCounter(sourceCardId));
if (game.getState().getExile().getExileZone(exileId) != null
&& game.getState().getExile().getExileZone(exileId).size() > 0) {
Cards cards = (Cards) game.getState().getValue("Cards Exile Kotose" + sourceCardId.toString());
// Cast spell
if (event.getType() == GameEvent.EventType.CAST_SPELL) {
Spell spell = (Spell) game.getObject(event.getSourceId());
if (spell != null
&& cards != null
&& !cards.isEmpty()
&& cards.contains(spell.getSourceId())) {
Ability approvingAbility = event.getAdditionalReference().getApprovingAbility();
if (approvingAbility != null
&& approvingAbility.getSourceId().equals(sourceCardId)) {
condition = false;
game.getState().setValue("Result Kotose" + sourceCardId.toString(), Boolean.FALSE);
}
}
}
// Play land
if (event.getType() == GameEvent.EventType.PLAY_LAND) {
Card land = game.getCard(event.getSourceId());
if (land != null
&& cards != null
&& !cards.isEmpty()
&& cards.contains(land.getId())) {
Ability approvingAbility = event.getAdditionalReference().getApprovingAbility();
if (approvingAbility != null
&& approvingAbility.getSourceId().equals(sourceCardId)) {
condition = false;
game.getState().setValue("Result Kotose" + sourceCardId.toString(), Boolean.FALSE);
}
}
}
}
}
@Override
public void reset() {
super.reset();
}
}
class KotoseTheSilentSpiderPlayEffect extends AsThoughEffectImpl {
KotoseTheSilentSpiderPlayEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE,
Duration.WhileControlled, Outcome.Benefit);
staticText = "";
}
private KotoseTheSilentSpiderPlayEffect(final KotoseTheSilentSpiderPlayEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public KotoseTheSilentSpiderPlayEffect copy() {
return new KotoseTheSilentSpiderPlayEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
Player controller = game.getPlayer(affectedControllerId);
if (controller == null) {
return false;
}
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
Set<Card> cards = game.getState().getExile().getExileZone(exileId).getCards(game);
List<UUID> targetsTest = getTargetPointer().getTargets(game, source);
if (cards != null
&& targetsTest != null) {
for (UUID uuid : targetsTest) {
if (!cards.contains(game.getCard(uuid))) {
getTargetPointer().getTargets(game, source).remove(uuid);
}
}
}
if (game.getState().getValue("Result Kotose" + source.getSourceId().toString()) == Boolean.FALSE) {
this.discard();
return false;
}
List<UUID> targets = getTargetPointer().getTargets(game, source);
if (targets.isEmpty()) {
this.discard();
return false;
}
UUID objectIdToCast = CardUtil.getMainCardId(game, objectId);
if (!targets.contains(objectIdToCast)) {
return false;
}
Card cardToCheck = game.getCard(objectId);
if (cardToCheck == null) {
return false;
}
// must be you
if (!affectedControllerId.equals(source.getControllerId())) {
return false;
}
// must be in exile
if (game.getState().getZone(objectId) != Zone.EXILED) {
return false;
}
// everything checks out, so allow it to be played
return true;
}
}
class KotoseTheSilentSpiderAnyColorManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
KotoseTheSilentSpiderAnyColorManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.WhileControlled, Outcome.Benefit);
this.staticText = ", and you may spend mana as though it were mana of any color to cast it";
}
private KotoseTheSilentSpiderAnyColorManaEffect(final KotoseTheSilentSpiderAnyColorManaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public KotoseTheSilentSpiderAnyColorManaEffect copy() {
return new KotoseTheSilentSpiderAnyColorManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
Cards cards = (Cards) game.getState().getValue("Cards Exile Kotose" + source.getSourceId().toString());
if (cards == null) {
return false;
}
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
ExileZone exileZone = game.getState().getExile().getExileZone(exileId);
return source.isControlledBy(affectedControllerId)
&& exileZone != null
&& !cards.isEmpty()
&& cards.contains(CardUtil.getMainCardId(game, objectId));
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}

View file

@ -164,6 +164,7 @@ public final class KamigawaNeonDynasty extends ExpansionSet {
cards.add(new SetCardInfo("Kirin-Touched Orochi", 212, Rarity.RARE, mage.cards.k.KirinTouchedOrochi.class));
cards.add(new SetCardInfo("Kitsune Ace", 22, Rarity.COMMON, mage.cards.k.KitsuneAce.class));
cards.add(new SetCardInfo("Kodama of the West Tree", 199, Rarity.MYTHIC, mage.cards.k.KodamaOfTheWestTree.class));
cards.add(new SetCardInfo("Kotose, the Silent Spider", 351, Rarity.RARE, mage.cards.k.KotoseTheSilentSpider.class));
cards.add(new SetCardInfo("Kura, the Boundless Sky", 200, Rarity.MYTHIC, mage.cards.k.KuraTheBoundlessSky.class));
cards.add(new SetCardInfo("Kyodai, Soul of Kamigawa", 23, Rarity.RARE, mage.cards.k.KyodaiSoulOfKamigawa.class));
cards.add(new SetCardInfo("Leech Gauntlet", 106, Rarity.UNCOMMON, mage.cards.l.LeechGauntlet.class));