[OTC] Implement 2 cards, refactor some exile effects ("Gonti-like") (#12118)

[OTC] Implement Gonti, Canny Acquisitor
[OTC] Implement Dream Thief's Bandana
This commit is contained in:
Susucre 2024-04-13 12:21:05 +02:00 committed by GitHub
parent c77634c843
commit 607d55f16b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 487 additions and 1252 deletions

View file

@ -1,31 +1,20 @@
package mage.cards.b; package mage.cards.b;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObject;
import mage.constants.*;
import mage.game.Game;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.Ability;
import mage.abilities.common.FirstSpellOpponentsTurnTriggeredAbility; import mage.abilities.common.FirstSpellOpponentsTurnTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect;
import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.DeathtouchAbility;
import mage.cards.Card; import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.CastManaAdjustment;
import mage.constants.SetTargetPointer;
import mage.constants.SubType;
import java.util.UUID;
/** /**
*
* @author Xanderhall * @author Xanderhall
*/ */
public final class BlightwingBandit extends CardImpl { public final class BlightwingBandit extends CardImpl {
@ -45,7 +34,12 @@ public final class BlightwingBandit extends CardImpl {
this.addAbility(DeathtouchAbility.getInstance()); this.addAbility(DeathtouchAbility.getInstance());
// Whenever you cast your first spell during each opponent's turn, look at the top card of that player's library, then exile it face down. You may play that card for as long as it remains exiled, and mana of any type can be spent to cast it. // Whenever you cast your first spell during each opponent's turn, look at the top card of that player's library, then exile it face down. You may play that card for as long as it remains exiled, and mana of any type can be spent to cast it.
this.addAbility(new FirstSpellOpponentsTurnTriggeredAbility(new BlightwingBanditEffect(), false, SetTargetPointer.PLAYER)); this.addAbility(new FirstSpellOpponentsTurnTriggeredAbility(
new ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(false, CastManaAdjustment.AS_THOUGH_ANY_MANA_TYPE)
.setText("look at the top card of that player's library, then exile it face down. "
+ "You may play that card for as long as it remains exiled, and mana of any type can be spent to cast it."),
false, SetTargetPointer.PLAYER
));
} }
private BlightwingBandit(final BlightwingBandit card) { private BlightwingBandit(final BlightwingBandit card) {
@ -57,163 +51,3 @@ public final class BlightwingBandit extends CardImpl {
return new BlightwingBandit(this); return new BlightwingBandit(this);
} }
} }
class BlightwingBanditEffect extends OneShotEffect {
private static final String VALUE_PREFIX = "ExileZones";
public BlightwingBanditEffect() {
super(Outcome.Benefit);
this.staticText = "look at the top card of that player's library, then exile it face down. "
+ "You may play that card for as long as it remains exiled, and mana of any type can be spent to cast it.";
}
private BlightwingBanditEffect(final BlightwingBanditEffect effect) {
super(effect);
}
@Override
public BlightwingBanditEffect copy() {
return new BlightwingBanditEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source));
MageObject sourceObject = source.getSourceObject(game);
if (controller == null || opponent == null || sourceObject == null) {
return false;
}
Card topCard = opponent.getLibrary().getFromTop(game);
if (topCard == null) {
return false;
}
UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
topCard.setFaceDown(true, game);
// Move card to exile
if (controller.moveCardsToExile(topCard, source, game, false, exileZoneId, sourceObject.getIdName())) {
topCard.setFaceDown(true, game);
Set<UUID> exileZones = (Set<UUID>) game.getState().getValue(VALUE_PREFIX + source.getSourceId().toString());
if (exileZones == null) {
exileZones = new HashSet<>();
game.getState().setValue(VALUE_PREFIX + source.getSourceId().toString(), exileZones);
}
exileZones.add(exileZoneId);
// You may play the card
ContinuousEffect effect = new BlightwingBanditPlayFromExileEffect();
effect.setTargetPointer(new FixedTarget(topCard.getId(), game));
game.addEffect(effect, source);
// And you may spend mana as though it were mana of any color to cast it
effect = new BlightwingBanditSpendAnyManaEffect();
effect.setTargetPointer(new FixedTarget(topCard.getId(), game));
game.addEffect(effect, source);
// For as long as that card remains exiled, you may look at it
effect = new MayLookAtTargetCardEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(topCard.getId(), game));
game.addEffect(effect, source);
}
return true;
}
}
class BlightwingBanditPlayFromExileEffect extends AsThoughEffectImpl {
BlightwingBanditPlayFromExileEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
staticText = "You may look at and play that card for as long as it remains exiled, " +
"and you may spend mana as though it were mana of any color to cast that spell.";
}
private BlightwingBanditPlayFromExileEffect(final BlightwingBanditPlayFromExileEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public BlightwingBanditPlayFromExileEffect copy() {
return new BlightwingBanditPlayFromExileEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
UUID targetId = getTargetPointer().getFirst(game, source);
if (targetId == null) {
// card is no longer in the origin zone, effect can be discarded
this.discard();
return false;
}
Card theCard = game.getCard(objectId);
if (theCard == null ) {
return false;
}
// for split cards
objectId = theCard.getMainCard().getId();
if (objectId.equals(targetId) && affectedControllerId.equals(source.getControllerId())) {
Card card = game.getCard(objectId);
return card != null;
}
return false;
}
}
class BlightwingBanditSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public BlightwingBanditSpendAnyManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.Custom, Outcome.Benefit);
staticText = "you may spend mana as though it were mana of any color to cast that spell";
}
private BlightwingBanditSpendAnyManaEffect(final BlightwingBanditSpendAnyManaEffect effect) { super(effect); }
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public BlightwingBanditSpendAnyManaEffect copy() {
return new BlightwingBanditSpendAnyManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
Card theCard = game.getCard(objectId);
if (theCard == null) {
return false;
}
// for split cards
objectId = theCard.getMainCard().getId();
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
return source.isControlledBy(affectedControllerId);
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
// object has moved zone so effect can be discarded
this.discard();
}
return false;
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}

View file

@ -1,29 +1,19 @@
package mage.cards.d; package mage.cards.d;
import mage.MageInt; import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.cards.*; import mage.cards.AdventureCard;
import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.CastManaAdjustment;
import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.game.Game;
import mage.game.permanent.token.TreasureToken; import mage.game.permanent.token.TreasureToken;
import mage.players.Player;
import mage.target.common.TargetOpponent; import mage.target.common.TargetOpponent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
/** /**
@ -49,7 +39,11 @@ public final class DecadentDragon extends AdventureCard {
// Expensive Taste // Expensive Taste
// Exile the top two cards of target opponent's library face down. You may look at and play those cards for as long as they remain exiled. // Exile the top two cards of target opponent's library face down. You may look at and play those cards for as long as they remain exiled.
this.getSpellCard().getSpellAbility().addEffect(new ExpensiveTasteEffect()); this.getSpellCard().getSpellAbility().addEffect(
new ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(false, CastManaAdjustment.NONE, 2)
.setText("Exile the top two cards of target opponent's library face down. "
+ "You may look at and play those cards for as long as they remain exiled.")
);
this.getSpellCard().getSpellAbility().addTarget(new TargetOpponent()); this.getSpellCard().getSpellAbility().addTarget(new TargetOpponent());
this.finalizeAdventure(); this.finalizeAdventure();
@ -64,57 +58,3 @@ public final class DecadentDragon extends AdventureCard {
return new DecadentDragon(this); return new DecadentDragon(this);
} }
} }
class ExpensiveTasteEffect extends OneShotEffect {
private static final String VALUE_PREFIX = "ExileZones";
ExpensiveTasteEffect() {
super(Outcome.Benefit);
staticText = "exile the top two cards of target opponent's library face down. You may look at and play those cards for as long as they remain exiled.";
}
private ExpensiveTasteEffect(final ExpensiveTasteEffect effect) {
super(effect);
}
@Override
public ExpensiveTasteEffect copy() {
return new ExpensiveTasteEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Player opponent = game.getPlayer(source.getFirstTarget());
MageObject sourceObject = source.getSourceObject(game);
if (opponent == null || controller == null || sourceObject == null) {
return false;
}
Cards topCards = new CardsImpl();
topCards.addAllCards(opponent.getLibrary().getTopCards(game, 2));
for (Card card : topCards.getCards(game)) {
UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
card.setFaceDown(true, game);
if (controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName())) {
card.setFaceDown(true, game);
Set<UUID> exileZones = (Set<UUID>) game.getState().getValue(VALUE_PREFIX + source.getSourceId().toString());
if (exileZones == null) {
exileZones = new HashSet<>();
game.getState().setValue(VALUE_PREFIX + source.getSourceId().toString(), exileZones);
}
exileZones.add(exileZoneId);
// allow to cast the card
CardUtil.makeCardPlayable(game, source, card, false, Duration.EndOfGame, false, controller.getId(), null);
// For as long as that card remains exiled, you may look at it
ContinuousEffect effect = new MayLookAtTargetCardEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
}
}
return true;
}
}

View file

@ -0,0 +1,45 @@
package mage.cards.d;
import mage.abilities.common.DealsDamageToAPlayerAttachedTriggeredAbility;
import mage.abilities.effects.common.ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.keyword.EquipAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.CastManaAdjustment;
import mage.constants.SubType;
import java.util.UUID;
/**
* @author Susucr
*/
public final class DreamThiefsBandana extends CardImpl {
public DreamThiefsBandana(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
this.subtype.add(SubType.EQUIPMENT);
// Whenever equipped creature deals combat damage to a player, look at the top card of their library, then exile it face down. For as long as it remains exiled, you may play it, and mana of any type can be spent to cast that spell.
this.addAbility(new DealsDamageToAPlayerAttachedTriggeredAbility(
new ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(false, CastManaAdjustment.AS_THOUGH_ANY_MANA_TYPE)
.setText("look at the top card of their library, then exile it face down. "
+ "For as long as it remains exiled, you may play it, and mana of any type can be spent to cast that spell"),
"equipped creature", false, true
));
// Equip {1}
this.addAbility(new EquipAbility(1));
}
private DreamThiefsBandana(final DreamThiefsBandana card) {
super(card);
}
@Override
public DreamThiefsBandana copy() {
return new DreamThiefsBandana(this);
}
}

View file

@ -0,0 +1,55 @@
package mage.cards.g;
import mage.MageInt;
import mage.abilities.common.DealCombatDamageControlledTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import java.util.UUID;
/**
* @author Susucr
*/
public final class GontiCannyAcquisitor extends CardImpl {
private static final FilterCard filter = new FilterCard("Spells you cast but don't own");
static {
filter.add(TargetController.NOT_YOU.getOwnerPredicate());
}
public GontiCannyAcquisitor(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{G}{U}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.AETHERBORN);
this.subtype.add(SubType.ROGUE);
this.power = new MageInt(5);
this.toughness = new MageInt(5);
// Spells you cast but don't own cost {1} less to cast.
this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1)));
// Whenever one or more creatures you control deal combat damage to a player, look at the top card of that player's library, then exile it face down. You may play that card for as long as it remains exiled, and mana of any type can be spent to cast that spell.
this.addAbility(new DealCombatDamageControlledTriggeredAbility(
new ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(false, CastManaAdjustment.AS_THOUGH_ANY_MANA_TYPE)
.setText("look at the top card of that player's library, then exile it face down. "
+ "You may play that card for as long as it remains exiled, and mana of any type can be spent to cast that spell"),
SetTargetPointer.PLAYER
));
}
private GontiCannyAcquisitor(final GontiCannyAcquisitor card) {
super(card);
}
@Override
public GontiCannyAcquisitor copy() {
return new GontiCannyAcquisitor(this);
}
}

View file

@ -4,7 +4,7 @@ import mage.MageInt;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect; import mage.abilities.effects.common.ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.DeathtouchAbility;
import mage.cards.*; import mage.cards.*;
@ -56,7 +56,7 @@ class GontiLordOfLuxuryEffect extends OneShotEffect {
public GontiLordOfLuxuryEffect() { public GontiLordOfLuxuryEffect() {
super(Outcome.Benefit); super(Outcome.Benefit);
this.staticText = "look at the top four cards of target opponent's library, exile one of them face down, then put the rest on the bottom of that library in a random order. You may look at and cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any type to cast that spell"; this.staticText = "look at the top four cards of target opponent's library, exile one of them face down, then put the rest on the bottom of that library in a random order. You may cast that card for as long as it remains exiled, and mana of any type can be spent to cast it";
} }
private GontiLordOfLuxuryEffect(final GontiLordOfLuxuryEffect effect) { private GontiLordOfLuxuryEffect(final GontiLordOfLuxuryEffect effect) {

View file

@ -4,9 +4,9 @@ import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect;
import mage.abilities.effects.common.combat.CantBlockAllEffect; import mage.abilities.effects.common.combat.CantBlockAllEffect;
import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.HasteAbility;
import mage.cards.Card; import mage.cards.Card;
@ -125,7 +125,7 @@ class HeadlinerScarlettExileEffect extends OneShotEffect {
if (game.getState().getZone(card.getId()) == Zone.EXILED) { if (game.getState().getZone(card.getId()) == Zone.EXILED) {
card.setFaceDown(true, game); card.setFaceDown(true, game);
CardUtil.makeCardPlayable(game, source, card, false, Duration.EndOfTurn, false); CardUtil.makeCardPlayable(game, source, card, false, Duration.EndOfTurn, false);
ContinuousEffect effect = new HeadlinerScarlettLookEffect(controller.getId()); ContinuousEffect effect = new MayLookAtTargetCardEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(card, game)); effect.setTargetPointer(new FixedTarget(card, game));
game.addEffect(effect, source); game.addEffect(effect, source);
} }
@ -133,38 +133,3 @@ class HeadlinerScarlettExileEffect extends OneShotEffect {
} }
} }
class HeadlinerScarlettLookEffect extends AsThoughEffectImpl {
private final UUID authorizedPlayerId;
public HeadlinerScarlettLookEffect(UUID authorizedPlayerId) {
super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
this.authorizedPlayerId = authorizedPlayerId;
}
private HeadlinerScarlettLookEffect(final HeadlinerScarlettLookEffect effect) {
super(effect);
this.authorizedPlayerId = effect.authorizedPlayerId;
}
@Override
public HeadlinerScarlettLookEffect copy() {
return new HeadlinerScarlettLookEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
UUID cardId = getTargetPointer().getFirst(game, source);
if (cardId == null) {
this.discard(); // card is no longer in the origin zone, effect can be discarded
}
return affectedControllerId.equals(authorizedPlayerId)
&& objectId.equals(cardId);
}
}

View file

@ -4,11 +4,13 @@ import mage.MageInt;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.SourceRemainsInZoneCondition;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -16,11 +18,10 @@ import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil; import mage.util.CardUtil;
import java.util.UUID; import java.util.UUID;
import mage.abilities.condition.common.SourceRemainsInZoneCondition;
import mage.target.targetpointer.FixedTarget;
/** /**
* @author fireshoes * @author fireshoes
@ -39,14 +40,9 @@ public final class IntetTheDreamer extends CardImpl {
// Flying // Flying
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
// Whenever Intet, the Dreamer deals combat damage to a player, you may pay {2}{U}. If you do, exile the top card of your library face down. // Whenever Intet, the Dreamer deals combat damage to a player, you may pay {2}{U}. If you do, exile the top card of your library face down. You may look at that card for as long as it remains exiled. You may play that card without paying its mana cost for as long as Intet remains on the battlefield.
// You may play that card without paying its mana cost for as long as Intet remains on the battlefield.
this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(
new DoIfCostPaid(new IntetTheDreamerExileEffect(), new ManaCostsImpl<>("{2}{U}")), false, true)); new DoIfCostPaid(new IntetTheDreamerExileEffect(), new ManaCostsImpl<>("{2}{U}")), false, true));
// You may look at that card for as long as it remains exiled.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new IntetTheDreamerLookEffect()));
} }
private IntetTheDreamer(final IntetTheDreamer card) { private IntetTheDreamer(final IntetTheDreamer card) {
@ -63,7 +59,9 @@ class IntetTheDreamerExileEffect extends OneShotEffect {
IntetTheDreamerExileEffect() { IntetTheDreamerExileEffect() {
super(Outcome.Benefit); super(Outcome.Benefit);
staticText = "exile the top card of your library face down. You may play that card without paying its mana cost for as long as Intet remains on the battlefield"; staticText = "exile the top card of your library face down. "
+ "You may look at that card for as long as it remains exiled. "
+ "You may play that card without paying its mana cost for as long as Intet remains on the battlefield";
} }
private IntetTheDreamerExileEffect(final IntetTheDreamerExileEffect effect) { private IntetTheDreamerExileEffect(final IntetTheDreamerExileEffect effect) {
@ -73,24 +71,29 @@ class IntetTheDreamerExileEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null) { if (controller == null) {
return false;
}
Card card = controller.getLibrary().getFromTop(game); Card card = controller.getLibrary().getFromTop(game);
MageObject sourceObject = source.getSourceObject(game); MageObject sourceObject = source.getSourceObject(game);
if (card != null && sourceObject != null) { if (card == null || sourceObject == null) {
return false;
}
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), sourceObject.getZoneChangeCounter(game));
String exileName = sourceObject.getIdName() + " (" + sourceObject.getZoneChangeCounter(game) + ")";
card.setFaceDown(true, game); card.setFaceDown(true, game);
controller.moveCardsToExile(card, source, game, false, if (controller.moveCardsToExile(card, source, game, false, exileId, exileName)) {
CardUtil.getExileZoneId(game, source.getSourceId(), sourceObject.getZoneChangeCounter(game)), // sourceObject must be used due to source not working correctly
sourceObject.getIdName() + " (" + sourceObject.getZoneChangeCounter(game) + ")");
card.setFaceDown(true, game); card.setFaceDown(true, game);
IntetTheDreamerAsThoughEffect effect = new IntetTheDreamerAsThoughEffect(); ContinuousEffect effect = new IntetTheDreamerAsThoughEffect();
effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId()))); effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId())));
game.getState().addEffect(effect, source); game.getState().addEffect(effect, source);
game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), Boolean.TRUE); // TODO This value will never be removed effect = new MayLookAtTargetCardEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(card, game));
game.addEffect(effect, source);
}
return true; return true;
} }
}
return false;
}
@Override @Override
public IntetTheDreamerExileEffect copy() { public IntetTheDreamerExileEffect copy() {
@ -148,7 +151,7 @@ class IntetTheDreamerAsThoughEffect extends AsThoughEffectImpl {
allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game); allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game);
// while Intet remains on battlefield // while Intet remains on battlefield
if(!(new SourceRemainsInZoneCondition(Zone.BATTLEFIELD).apply(game, source))) { if (!(new SourceRemainsInZoneCondition(Zone.BATTLEFIELD).apply(game, source))) {
this.discard(); this.discard();
return false; return false;
} }
@ -157,46 +160,3 @@ class IntetTheDreamerAsThoughEffect extends AsThoughEffectImpl {
return false; return false;
} }
} }
class IntetTheDreamerLookEffect extends AsThoughEffectImpl {
IntetTheDreamerLookEffect() {
super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
staticText = "You may look at that card for as long as it remains exiled";
}
private IntetTheDreamerLookEffect(final IntetTheDreamerLookEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public IntetTheDreamerLookEffect copy() {
return new IntetTheDreamerLookEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (affectedControllerId.equals(source.getControllerId())) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Card card = game.getCard(objectId);
if (card != null) {
if (card.isFaceDown(game)
&& game.getExile().containsId(card.getId(), game)
&& Boolean.TRUE.equals(game.getState().getValue("Exiled_IntetTheDreamer" + card.getId()))) {
return true;
} else {
this.discard();
game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), null);
}
}
}
}
return false;
}
}

View file

@ -1,26 +1,19 @@
package mage.cards.i; package mage.cards.i;
import mage.MageInt; import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility; import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.ToxicAbility; import mage.abilities.keyword.ToxicAbility;
import mage.abilities.keyword.VigilanceAbility; import mage.abilities.keyword.VigilanceAbility;
import mage.cards.Card; import mage.cards.*;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
import mage.counters.CounterType; import mage.counters.CounterType;
import mage.game.Game; import mage.game.Game;
import mage.players.ManaPoolItem;
import mage.players.Player; import mage.players.Player;
import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTargets;
import mage.util.CardUtil;
import java.util.UUID; import java.util.UUID;
@ -83,158 +76,22 @@ class IxhelScionOfAtraxaEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
MageObject sourceObject = source.getSourceObject(game); Cards cards = new CardsImpl();
UUID exileZoneId = CardUtil.getExileZoneId(game, sourceObject.getId(), sourceObject.getZoneChangeCounter(game));
for (UUID opponentId : game.getOpponents(source.getControllerId(), true)) { for (UUID opponentId : game.getOpponents(source.getControllerId(), true)) {
Player opponent = game.getPlayer(opponentId); Player opponent = game.getPlayer(opponentId);
if (opponent == null || opponent.getCounters().getCount(CounterType.POISON) < 3 || !opponent.getLibrary().hasCards()) { if (opponent == null || opponent.getCounters().getCount(CounterType.POISON) < 3 || !opponent.getLibrary().hasCards()) {
continue; continue;
} }
// move card to exile Card card = opponent.getLibrary().getFromTop(game);
Card topCard = opponent.getLibrary().getFromTop(game); if (card != null) {
topCard.setFaceDown(true, game); cards.add(card);
if (opponent.moveCardsToExile(topCard, source, game, false, exileZoneId, sourceObject.getIdName())) {
topCard.setFaceDown(true, game);
} }
game.getState().setValue(topCard.getId().toString() + game.getState().getZoneChangeCounter(topCard.getId()), exileZoneId);
// you may play that card
ContinuousEffect effect = new IxhelScionOfAtraxaPlayFromExileEffect();
effect.setTargetPointer(new FixedTarget(topCard.getId(), game));
game.addEffect(effect, source);
// you may spend mana as though it were many of any color to cast it
effect = new IxhelScionOfAtraxaSpendAnyManaEffect();
effect.setTargetPointer(new FixedTarget(topCard.getId(), game));
game.addEffect(effect, source);
// for as long as that card remains exiled, you may look at it
effect = new IxhelScionOfAtraxaLookEffect(source.getControllerId());
effect.setTargetPointer(new FixedTarget(topCard.getId(), game));
game.addEffect(effect, source);
} }
return true; if (cards.isEmpty()) {
} return false;
} }
return new ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect(false, CastManaAdjustment.AS_THOUGH_ANY_MANA_COLOR)
class IxhelScionOfAtraxaPlayFromExileEffect extends AsThoughEffectImpl { .setTargetPointer(new FixedTargets(cards, game))
.apply(game, source);
IxhelScionOfAtraxaPlayFromExileEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
}
private IxhelScionOfAtraxaPlayFromExileEffect(final IxhelScionOfAtraxaPlayFromExileEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public IxhelScionOfAtraxaPlayFromExileEffect copy() {
return new IxhelScionOfAtraxaPlayFromExileEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
UUID targetId = getTargetPointer().getFirst(game, source);
if (targetId == null) {
// card is no longer in the origin zone, effect can be discarded
this.discard();
return false;
}
Card theCard = game.getCard(objectId);
if (theCard == null ) {
return false;
}
// for split cards
objectId = theCard.getMainCard().getId();
if (objectId.equals(targetId) && affectedControllerId.equals(source.getControllerId())) {
Card card = game.getCard(objectId);
return card != null;
}
return false;
}
}
class IxhelScionOfAtraxaSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public IxhelScionOfAtraxaSpendAnyManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.Custom, Outcome.Benefit);
}
private IxhelScionOfAtraxaSpendAnyManaEffect(final IxhelScionOfAtraxaSpendAnyManaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public IxhelScionOfAtraxaSpendAnyManaEffect copy() {
return new IxhelScionOfAtraxaSpendAnyManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
Card theCard = game.getCard(objectId);
if (theCard == null) {
return false;
}
// for split cards
objectId = theCard.getMainCard().getId();
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
return source.isControlledBy(affectedControllerId);
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
// object has moved zone so effect can be discarded
this.discard();
}
return false;
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}
class IxhelScionOfAtraxaLookEffect extends AsThoughEffectImpl {
private final UUID authorizedPlayerId;
public IxhelScionOfAtraxaLookEffect(UUID authorizedPlayerId) {
super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
this.authorizedPlayerId = authorizedPlayerId;
}
private IxhelScionOfAtraxaLookEffect(final IxhelScionOfAtraxaLookEffect effect) {
super(effect);
this.authorizedPlayerId = effect.authorizedPlayerId;
}
@Override
public boolean apply(Game game, Ability source) { return true; }
@Override
public IxhelScionOfAtraxaLookEffect copy() { return new IxhelScionOfAtraxaLookEffect(this); }
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
UUID cardId = getTargetPointer().getFirst(game, source);
// card is no longer in the origin zone, effect can be discarded
if (cardId == null) {
this.discard();
}
return affectedControllerId.equals(authorizedPlayerId) && objectId.equals(cardId);
} }
} }

View file

@ -3,15 +3,21 @@ package mage.cards.m;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.MutatesSourceTriggeredAbility; import mage.abilities.common.MutatesSourceTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.MutateAbility; import mage.abilities.keyword.MutateAbility;
import mage.cards.*; import mage.cards.CardImpl;
import mage.constants.*; import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.CastManaAdjustment;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTargets;
import java.util.UUID; import java.util.UUID;
@ -82,93 +88,8 @@ class MindleecherEffect extends OneShotEffect {
if (cards.isEmpty()) { if (cards.isEmpty()) {
return false; return false;
} }
controller.moveCards(cards, Zone.EXILED, source, game); return new ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect(false, CastManaAdjustment.NONE)
cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); .setTargetPointer(new FixedTargets(cards, game))
if (cards.isEmpty()) { .apply(game, source);
return false;
}
cards.getCards(game).stream().forEach(card -> card.setFaceDown(true, game));
for (Card card : cards.getCards(game)) {
game.addEffect(new MindleecherCastFromExileEffect(controller.getId())
.setTargetPointer(new FixedTarget(card, game)), source);
game.addEffect(new MindleecherLookEffect(controller.getId())
.setTargetPointer(new FixedTarget(card, game)), source);
}
return true;
}
}
class MindleecherCastFromExileEffect extends AsThoughEffectImpl {
private final UUID authorizedPlayerId;
MindleecherCastFromExileEffect(UUID authorizedPlayerId) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
this.authorizedPlayerId = authorizedPlayerId;
staticText = "For as long as that card remains exiled, you may play it";
}
private MindleecherCastFromExileEffect(final MindleecherCastFromExileEffect effect) {
super(effect);
this.authorizedPlayerId = effect.authorizedPlayerId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public MindleecherCastFromExileEffect copy() {
return new MindleecherCastFromExileEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
UUID cardId = getTargetPointer().getFirst(game, source);
if (cardId == null) {
this.discard(); // card is no longer in the origin zone, effect can be discarded
return false;
}
return objectId.equals(cardId)
&& affectedControllerId.equals(authorizedPlayerId)
&& game.getCard(objectId) != null;
}
}
class MindleecherLookEffect extends AsThoughEffectImpl {
private final UUID authorizedPlayerId;
MindleecherLookEffect(UUID authorizedPlayerId) {
super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
this.authorizedPlayerId = authorizedPlayerId;
staticText = "For as long as that card remains exiled, you may look at it";
}
private MindleecherLookEffect(final MindleecherLookEffect effect) {
super(effect);
this.authorizedPlayerId = effect.authorizedPlayerId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public MindleecherLookEffect copy() {
return new MindleecherLookEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
UUID cardId = getTargetPointer().getFirst(game, source);
if (cardId == null) {
this.discard(); // card is no longer in the origin zone, effect can be discarded
return false;
}
return affectedControllerId.equals(authorizedPlayerId)
&& objectId.equals(cardId);
} }
} }

View file

@ -1,23 +1,13 @@
package mage.cards.o; package mage.cards.o;
import mage.MageObject; import mage.abilities.dynamicvalue.common.ManacostVariableValue;
import mage.abilities.Ability; import mage.abilities.effects.common.ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect;
import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.CastManaAdjustment;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetOpponent; import mage.target.common.TargetOpponent;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
/** /**
@ -28,10 +18,14 @@ public final class OutrageousRobbery extends CardImpl {
public OutrageousRobbery(UUID ownerId, CardSetInfo setInfo) { public OutrageousRobbery(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{B}{B}"); super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{B}{B}");
// Target opponent exiles the top X cards of their library face down. // Target opponent exiles the top X cards of their library face down. You may look at and play those cards for as long as they remain exiled. If you cast a spell this way, you may spend mana as though it were mana of any type to cast it.
// You may look at and play those cards for as long as they remain exiled. this.getSpellAbility().addEffect(
// If you cast a spell this way, you may spend mana as though it were mana of any type to cast it. new ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(
this.getSpellAbility().addEffect(new OutrageousRobberyEffect()); false, CastManaAdjustment.AS_THOUGH_ANY_MANA_TYPE, ManacostVariableValue.REGULAR
).setText("Target opponent exiles the top X cards of their library face down. "
+ "You may look at and play those cards for as long as they remain exiled. "
+ "If you cast a spell this way, you may spend mana as though it were mana of any type to cast it.")
);
this.getSpellAbility().addTarget(new TargetOpponent()); this.getSpellAbility().addTarget(new TargetOpponent());
} }
@ -44,56 +38,3 @@ public final class OutrageousRobbery extends CardImpl {
return new OutrageousRobbery(this); return new OutrageousRobbery(this);
} }
} }
class OutrageousRobberyEffect extends OneShotEffect {
OutrageousRobberyEffect() {
super(Outcome.Benefit);
this.staticText = "Target opponent exiles the top X cards of their library face down. " +
"You may look at and play those cards for as long as they remain exiled. " +
"If you cast a spell this way, you may spend mana as though it were mana of any type to cast it.";
}
private OutrageousRobberyEffect(final OutrageousRobberyEffect effect) {
super(effect);
}
@Override
public OutrageousRobberyEffect copy() {
return new OutrageousRobberyEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Player opponent = game.getPlayer(source.getFirstTarget());
MageObject sourceObject = source.getSourceObject(game);
if (controller == null || opponent == null || sourceObject == null) {
return false;
}
int xValue = source.getManaCostsToPay().getX();
if (xValue == 0) {
return false;
}
Set<Card> cards = opponent.getLibrary().getTopCards(game, xValue);
cards.forEach(card -> card.setFaceDown(true, game));
UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
if (cards.size() > 0 && opponent.moveCardsToExile(cards, source, game, false, exileZoneId,
sourceObject.getIdName() + " (" + controller.getName() + ")")) {
for (Card card : cards) {
card.setFaceDown(true, game);
ContinuousEffect effect = new MayLookAtTargetCardEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
CardUtil.makeCardPlayable(
game, source, card, false, Duration.Custom, true,
source.getControllerId(), null);
}
}
return true;
}
}

View file

@ -2,18 +2,19 @@ package mage.cards.p;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.CardType;
import mage.game.ExileZone; import mage.constants.CastManaAdjustment;
import mage.constants.Outcome;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCardInLibrary;
import mage.target.common.TargetOpponent; import mage.target.common.TargetOpponent;
import mage.util.CardUtil; import mage.target.targetpointer.FixedTarget;
import java.util.UUID; import java.util.UUID;
@ -25,8 +26,7 @@ public final class PraetorsGrasp extends CardImpl {
public PraetorsGrasp(UUID ownerId, CardSetInfo setInfo) { public PraetorsGrasp(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}");
// Search target opponent's library for a card and exile it face down. Then that player // Search target opponent's library for a card and exile it face down. Then that player shuffles their library. You may look at and play that card for as long as it remains exiled.
//shuffles their library. You may look at and play that card for as long as it remains exiled.
this.getSpellAbility().addEffect(new PraetorsGraspEffect()); this.getSpellAbility().addEffect(new PraetorsGraspEffect());
this.getSpellAbility().addTarget(new TargetOpponent()); this.getSpellAbility().addTarget(new TargetOpponent());
} }
@ -74,17 +74,9 @@ class PraetorsGraspEffect extends OneShotEffect {
if (card == null) { if (card == null) {
return false; return false;
} }
// account for card going into exile from the library new ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect(false, CastManaAdjustment.NONE)
final int zcc = game.getState().getZoneChangeCounter(card.getId()) + 1; .setTargetPointer(new FixedTarget(card, game))
UUID exileId = CardUtil.getExileZoneId(card.getId().toString() + zcc, game); .apply(game, source);
if (exileId != null) {
game.informPlayers(controller.getLogName() + " moves the searched "
+ "card face down to exile");
card.moveToExile(exileId, sourceObject.getIdName(), source, game);
card.setFaceDown(true, game);
game.addEffect(new PraetorsGraspPlayEffect(card.getId()), source);
game.addEffect(new PraetorsGraspRevealEffect(card.getId()), source);
}
} }
opponent.shuffleLibrary(source, game); opponent.shuffleLibrary(source, game);
return true; return true;
@ -92,103 +84,3 @@ class PraetorsGraspEffect extends OneShotEffect {
return false; return false;
} }
} }
class PraetorsGraspPlayEffect extends AsThoughEffectImpl {
private UUID cardId;
public PraetorsGraspPlayEffect(UUID cardId) {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit);
this.cardId = cardId;
staticText = "You may look at and play that card for as long as it remains exiled";
}
private PraetorsGraspPlayEffect(final PraetorsGraspPlayEffect effect) {
super(effect);
this.cardId = effect.cardId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public PraetorsGraspPlayEffect copy() {
return new PraetorsGraspPlayEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (objectId.equals(cardId)
&& affectedControllerId.equals(source.getControllerId())) {
Player controller = game.getPlayer(source.getControllerId());
UUID exileId = CardUtil.getExileZoneId(cardId.toString() + game.getState().getZoneChangeCounter(cardId), game);
if (exileId != null
&& controller != null) {
ExileZone exileZone = game.getExile().getExileZone(exileId);
if (exileZone != null
&& exileZone.contains(cardId)) {
return true;
} else {
discard();
}
}
}
return false;
}
}
class PraetorsGraspRevealEffect extends AsThoughEffectImpl {
private final UUID cardId;
public PraetorsGraspRevealEffect(UUID cardId) {
super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
this.cardId = cardId;
staticText = "You may look at and play that card for as long as it remains exiled";
}
private PraetorsGraspRevealEffect(final PraetorsGraspRevealEffect effect) {
super(effect);
this.cardId = effect.cardId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public PraetorsGraspRevealEffect copy() {
return new PraetorsGraspRevealEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (objectId.equals(cardId)
&& affectedControllerId.equals(source.getControllerId())) {
MageObject sourceObject = source.getSourceObject(game);
UUID exileId = CardUtil.getExileZoneId(cardId.toString() + game.getState().getZoneChangeCounter(cardId), game);
if (exileId != null
&& sourceObject != null) {
ExileZone exileZone = game.getExile().getExileZone(exileId);
if (exileZone != null
&& exileZone.contains(cardId)) {
Player controller = game.getPlayer(source.getControllerId());
Card card = game.getCard(cardId);
if (controller != null
&& card != null
&& game.getState().getZone(cardId) == Zone.EXILED) {
return true;
}
} else {
discard();
}
}
}
return false;
}
}

View file

@ -1,26 +1,16 @@
package mage.cards.p; package mage.cards.p;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.MenaceAbility;
import mage.cards.*; import mage.cards.CardImpl;
import mage.constants.*; import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.CastManaAdjustment;
import mage.constants.Duration;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.game.Game;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
/** /**
@ -29,7 +19,7 @@ import java.util.UUID;
public final class PredatorsHour extends CardImpl { public final class PredatorsHour extends CardImpl {
public PredatorsHour(UUID ownerId, CardSetInfo setInfo) { public PredatorsHour(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}");
// Until end of turn, creatures you control gain menace // Until end of turn, creatures you control gain menace
this.getSpellAbility().addEffect(new GainAbilityControlledEffect( this.getSpellAbility().addEffect(new GainAbilityControlledEffect(
@ -43,178 +33,30 @@ public final class PredatorsHour extends CardImpl {
// exile the top card of that player's library face down. // exile the top card of that player's library face down.
// You may look at and play that card for as long as it remains exiled, // You may look at and play that card for as long as it remains exiled,
// and you may spend mana as though it were mana of any color to cast that spell. // and you may spend mana as though it were mana of any color to cast that spell.
this.getSpellAbility().addEffect(new GainAbilityControlledEffect( this.getSpellAbility().addEffect(
new GainAbilityControlledEffect(
new DealsCombatDamageToAPlayerTriggeredAbility( new DealsCombatDamageToAPlayerTriggeredAbility(
new PredatorsHourEffect(), new ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(false, CastManaAdjustment.AS_THOUGH_ANY_MANA_COLOR)
false, .setText("exile the top card of that player's library face down. "
true), + "You may look at and play that card for as long as it remains exiled, "
+ "and you may spend mana as though it were mana of any color to cast that spell."),
false, true),
Duration.EndOfTurn, Duration.EndOfTurn,
StaticFilters.FILTER_CONTROLLED_CREATURES StaticFilters.FILTER_CONTROLLED_CREATURES
).setText("\"Whenever this creature deals combat damage to a player, " + ).setText("\"Whenever this creature deals combat damage to a player, " +
"exile the top card of that player's library face down. " + "exile the top card of that player's library face down. " +
"You may look at and play that card for as long as it remains exiled, " + "You may look at and play that card for as long as it remains exiled, " +
"and you may spend mana as though it were mana of any color to cast that spell.\"") "and you may spend mana as though it were mana of any color to cast that spell.\""
.concatBy("and") ).concatBy("and")
); );
} }
private PredatorsHour(final PredatorsHour card) { super(card); } private PredatorsHour(final PredatorsHour card) {
super(card);
}
@Override @Override
public PredatorsHour copy() { return new PredatorsHour(this); } public PredatorsHour copy() {
} return new PredatorsHour(this);
// Based on Gonti, Lord of Luxury
class PredatorsHourEffect extends OneShotEffect {
private static final String VALUE_PREFIX = "ExileZones";
public PredatorsHourEffect() {
super(Outcome.Benefit);
this.staticText = "exile the top card of that player's library face down. " +
"You may look at and play that card for as long as it remains exiled, " +
"and you may spend mana as though it were mana of any color to cast that spell.";
}
private PredatorsHourEffect(final PredatorsHourEffect effect) { super(effect); }
@Override
public PredatorsHourEffect copy() { return new PredatorsHourEffect(this); }
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source));
if (opponent == null) {
return false;
}
MageObject sourceObject = source.getSourceObject(game);
if (sourceObject == null) {
return false;
}
Card topCard = opponent.getLibrary().getFromTop(game);
if (topCard == null) {
return false;
}
UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
topCard.setFaceDown(true, game);
// Move card to exile
if (controller.moveCardsToExile(topCard, source, game, false, exileZoneId, sourceObject.getIdName())) {
topCard.setFaceDown(true, game);
Set<UUID> exileZones = (Set<UUID>) game.getState().getValue(VALUE_PREFIX + source.getSourceId().toString());
if (exileZones == null) {
exileZones = new HashSet<>();
game.getState().setValue(VALUE_PREFIX + source.getSourceId().toString(), exileZones);
}
exileZones.add(exileZoneId);
// You may play the card
ContinuousEffect effect = new PredatorsHourPlayFromExileEffect();
effect.setTargetPointer(new FixedTarget(topCard.getId(), game));
game.addEffect(effect, source);
// And you may spend mana as though it were mana of any color to cast it
effect = new PredatorsHourSpendAnyManaEffect();
effect.setTargetPointer(new FixedTarget(topCard.getId(), game));
game.addEffect(effect, source);
// For as long as that card remains exiled, you may look at it
effect = new MayLookAtTargetCardEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(topCard.getId(), game));
game.addEffect(effect, source);
}
return true;
}
}
class PredatorsHourPlayFromExileEffect extends AsThoughEffectImpl {
PredatorsHourPlayFromExileEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
staticText = "You may look at and play that card for as long as it remains exiled, " +
"and you may spend mana as though it were mana of any color to cast that spell.";
}
private PredatorsHourPlayFromExileEffect(final PredatorsHourPlayFromExileEffect effect) { super(effect); }
@Override
public boolean apply(Game game, Ability source) { return true; }
@Override
public PredatorsHourPlayFromExileEffect copy() { return new PredatorsHourPlayFromExileEffect(this); }
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
UUID targetId = getTargetPointer().getFirst(game, source);
if (targetId == null) {
// card is no longer in the origin zone, effect can be discarded
this.discard();
return false;
}
Card theCard = game.getCard(objectId);
if (theCard == null ) {
return false;
}
// for split cards
objectId = theCard.getMainCard().getId();
if (objectId.equals(targetId) && affectedControllerId.equals(source.getControllerId())) {
Card card = game.getCard(objectId);
return card != null;
}
return false;
}
}
class PredatorsHourSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public PredatorsHourSpendAnyManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.Custom, Outcome.Benefit);
staticText = "you may spend mana as though it were mana of any color to cast that spell";
}
private PredatorsHourSpendAnyManaEffect(final PredatorsHourSpendAnyManaEffect effect) { super(effect); }
@Override
public boolean apply(Game game, Ability source) { return true; }
@Override
public PredatorsHourSpendAnyManaEffect copy() { return new PredatorsHourSpendAnyManaEffect(this); }
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
Card theCard = game.getCard(objectId);
if (theCard == null) {
return false;
}
// for split cards
objectId = theCard.getMainCard().getId();
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
return source.isControlledBy(affectedControllerId);
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
// object has moved zone so effect can be discarded
this.discard();
}
return false;
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
} }
} }

View file

@ -3,25 +3,21 @@ package mage.cards.s;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect;
import mage.abilities.keyword.FlashbackAbility; import mage.abilities.keyword.FlashbackAbility;
import mage.cards.*; import mage.cards.*;
import mage.constants.*; import mage.constants.CardType;
import mage.constants.CastManaAdjustment;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.game.Game; import mage.game.Game;
import mage.players.ManaPoolItem;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetCard; import mage.target.TargetCard;
import mage.target.common.TargetOpponent; import mage.target.common.TargetOpponent;
import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
/** /**
@ -88,122 +84,12 @@ class SiphonInsightEffect extends OneShotEffect {
controller.putCardsOnBottomOfLibrary(topCards, game, source, false); controller.putCardsOnBottomOfLibrary(topCards, game, source, false);
return true; return true;
} }
topCards.remove(card); new ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect(false, CastManaAdjustment.AS_THOUGH_ANY_MANA_COLOR)
// move card to exile .setTargetPointer(new FixedTarget(card, game))
UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); .apply(game, source);
card.setFaceDown(true, game); topCards.retainZone(Zone.LIBRARY, game);
if (controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName())) {
card.setFaceDown(true, game);
Set<UUID> exileZones = (Set<UUID>) game.getState().getValue(VALUE_PREFIX + source.getSourceId().toString());
if (exileZones == null) {
exileZones = new HashSet<>();
game.getState().setValue(VALUE_PREFIX + source.getSourceId().toString(), exileZones);
}
exileZones.add(exileZoneId);
// allow to cast the card
ContinuousEffect effect = new SiphonInsightCastFromExileEffect();
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
// and you may spend mana as though it were mana of any color to cast it
effect = new SiphonInsightSpendAnyManaEffect();
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
// For as long as that card remains exiled, you may look at it
effect = new MayLookAtTargetCardEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
}
// then put the rest on the bottom of that library in a random order // then put the rest on the bottom of that library in a random order
controller.putCardsOnBottomOfLibrary(topCards, game, source, false); controller.putCardsOnBottomOfLibrary(topCards, game, source, false);
return true; return true;
} }
} }
class SiphonInsightCastFromExileEffect extends AsThoughEffectImpl {
SiphonInsightCastFromExileEffect() {
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
staticText = "You may cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast that spell";
}
private SiphonInsightCastFromExileEffect(final SiphonInsightCastFromExileEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public SiphonInsightCastFromExileEffect copy() {
return new SiphonInsightCastFromExileEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
UUID targetId = getTargetPointer().getFirst(game, source);
if (targetId == null) {
this.discard(); // card is no longer in the origin zone, effect can be discarded
return false;
}
Card theCard = game.getCard(objectId);
if (theCard == null) {
return false;
}
objectId = theCard.getMainCard().getId(); // for split cards
if (objectId.equals(targetId)
&& affectedControllerId.equals(source.getControllerId())) {
Card card = game.getCard(objectId);
// TODO: Allow to cast Zoetic Cavern face down
return card != null;
}
return false;
}
}
class SiphonInsightSpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public SiphonInsightSpendAnyManaEffect() {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.Custom, Outcome.Benefit);
staticText = "you may spend mana as though it were mana of any color to cast it";
}
private SiphonInsightSpendAnyManaEffect(final SiphonInsightSpendAnyManaEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public SiphonInsightSpendAnyManaEffect copy() {
return new SiphonInsightSpendAnyManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
Card theCard = game.getCard(objectId);
if (theCard == null) {
return false;
}
objectId = theCard.getMainCard().getId(); // for split cards
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
return source.isControlledBy(affectedControllerId);
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
// object has moved zone so effect can be discarded
this.discard();
}
return false;
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}

View file

@ -4,24 +4,17 @@ import mage.MageInt;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.*; import mage.cards.*;
import mage.constants.*; import mage.constants.*;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.game.Game; import mage.game.Game;
import mage.players.ManaPoolItem;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetCard; import mage.target.TargetCard;
import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
/** /**
@ -29,8 +22,6 @@ import java.util.UUID;
*/ */
public final class ThiefOfSanity extends CardImpl { public final class ThiefOfSanity extends CardImpl {
protected static final String VALUE_PREFIX = "ExileZones";
public ThiefOfSanity(UUID ownerId, CardSetInfo setInfo) { public ThiefOfSanity(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}");
@ -60,7 +51,7 @@ class ThiefOfSanityEffect extends OneShotEffect {
ThiefOfSanityEffect() { ThiefOfSanityEffect() {
super(Outcome.Benefit); super(Outcome.Benefit);
this.staticText = "look at the top three cards of that player's library, exile one of them face down, then put the rest into their graveyard. " this.staticText = "look at the top three cards of that player's library, exile one of them face down, then put the rest into their graveyard. "
+ "For as long as that card remains exiled, you may look at it, you may cast it, and you may spend mana as though it were mana of any type to cast it"; + "You may cast that card for as long as it remains exiled, and mana of any type can be spent to cast it";
} }
private ThiefOfSanityEffect(final ThiefOfSanityEffect effect) { private ThiefOfSanityEffect(final ThiefOfSanityEffect effect) {
@ -77,139 +68,24 @@ class ThiefOfSanityEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
Player damagedPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); Player damagedPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
MageObject sourceObject = source.getSourceObject(game); MageObject sourceObject = source.getSourceObject(game);
if (controller != null && damagedPlayer != null && sourceObject != null) { if (controller == null || damagedPlayer == null || sourceObject == null) {
return false;
}
Cards topCards = new CardsImpl(); Cards topCards = new CardsImpl();
topCards.addAllCards(damagedPlayer.getLibrary().getTopCards(game, 3)); topCards.addAllCards(damagedPlayer.getLibrary().getTopCards(game, 3));
TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to exile face down")); TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to exile face down"));
if (controller.choose(outcome, topCards, target, source, game)) { controller.choose(outcome, topCards, target, source, game);
Card card = game.getCard(target.getFirstTarget()); Card card = game.getCard(target.getFirstTarget());
if (card != null) { if (card == null) {
topCards.remove(card); controller.moveCards(topCards, Zone.GRAVEYARD, source, game);
// move card to exile return true;
UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
card.setFaceDown(true, game);
if (controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName() + " (" + controller.getName() + ")")) {
card.setFaceDown(true, game);
Set<UUID> exileZones = (Set<UUID>) game.getState().getValue(ThiefOfSanity.VALUE_PREFIX + source.getSourceId().toString());
if (exileZones == null) {
exileZones = new HashSet<>();
game.getState().setValue(ThiefOfSanity.VALUE_PREFIX + source.getSourceId().toString(), exileZones);
}
exileZones.add(exileZoneId);
// rule information: https://blogs.magicjudges.org/rulestips/2018/11/thief-of-sanity-and-control-changing/
// allow to cast the card
ContinuousEffect effect = new ThiefOfSanityCastFromExileEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
// and you may spend mana as though it were mana of any color to cast it
effect = new ThiefOfSanitySpendAnyManaEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
// For as long as that card remains exiled, you may look at it
effect = new MayLookAtTargetCardEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
}
}
} }
new ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect(true, CastManaAdjustment.AS_THOUGH_ANY_MANA_TYPE)
.setTargetPointer(new FixedTarget(card, game))
.apply(game, source);
topCards.retainZone(Zone.LIBRARY, game);
// put the rest into their graveyard // put the rest into their graveyard
controller.moveCards(topCards, Zone.GRAVEYARD, source, game); controller.moveCards(topCards, Zone.GRAVEYARD, source, game);
return true; return true;
} }
return false;
}
}
class ThiefOfSanityCastFromExileEffect extends AsThoughEffectImpl {
private final UUID authorizedPlayerId;
public ThiefOfSanityCastFromExileEffect(UUID authorizedPlayerId) {
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
this.authorizedPlayerId = authorizedPlayerId;
staticText = "For as long as that card remains exiled, you may cast it";
}
private ThiefOfSanityCastFromExileEffect(final ThiefOfSanityCastFromExileEffect effect) {
super(effect);
this.authorizedPlayerId = effect.authorizedPlayerId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public ThiefOfSanityCastFromExileEffect copy() {
return new ThiefOfSanityCastFromExileEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
UUID cardId = getTargetPointer().getFirst(game, source);
if (cardId == null) {
this.discard(); // card is no longer in the origin zone, effect can be discarded
return false;
}
Card theCard = game.getCard(objectId);
if (theCard == null || theCard.isLand(game)) {
return false;
}
objectId = theCard.getMainCard().getId(); // for split cards
if (objectId.equals(cardId)
&& affectedControllerId.equals(authorizedPlayerId)) {
Card card = game.getCard(objectId);
// TODO: Allow to cast Zoetic Cavern face down
return card != null;
}
return false;
}
}
class ThiefOfSanitySpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
private final UUID authorizedPlayerId;
public ThiefOfSanitySpendAnyManaEffect(UUID authorizedPlayerId) {
super(AsThoughEffectType.SPEND_OTHER_MANA, Duration.Custom, Outcome.Benefit);
this.authorizedPlayerId = authorizedPlayerId;
staticText = "For as long as that card remains exiled, you may spend mana as though it were mana of any color to cast it";
}
private ThiefOfSanitySpendAnyManaEffect(final ThiefOfSanitySpendAnyManaEffect effect) {
super(effect);
this.authorizedPlayerId = effect.authorizedPlayerId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public ThiefOfSanitySpendAnyManaEffect copy() {
return new ThiefOfSanitySpendAnyManaEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
// if the card moved from exile to spell the zone change counter is increased by 1 (effect must applies before and on stack, use isCheckPlayableMode?)
return affectedControllerId.equals(authorizedPlayerId);
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
// object has moved zone so effect can be discarded
this.discard();
}
return false;
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
} }

View file

@ -95,6 +95,7 @@ public final class OutlawsOfThunderJunctionCommander extends ExpansionSet {
cards.add(new SetCardInfo("Dire Fleet Ravager", 132, Rarity.MYTHIC, mage.cards.d.DireFleetRavager.class)); cards.add(new SetCardInfo("Dire Fleet Ravager", 132, Rarity.MYTHIC, mage.cards.d.DireFleetRavager.class));
cards.add(new SetCardInfo("Discreet Retreat", 20, Rarity.RARE, mage.cards.d.DiscreetRetreat.class)); cards.add(new SetCardInfo("Discreet Retreat", 20, Rarity.RARE, mage.cards.d.DiscreetRetreat.class));
cards.add(new SetCardInfo("Dragonskull Summit", 289, Rarity.RARE, mage.cards.d.DragonskullSummit.class)); cards.add(new SetCardInfo("Dragonskull Summit", 289, Rarity.RARE, mage.cards.d.DragonskullSummit.class));
cards.add(new SetCardInfo("Dream-Thief's Bandana", 38, Rarity.RARE, mage.cards.d.DreamThiefsBandana.class));
cards.add(new SetCardInfo("Drowned Catacomb", 290, Rarity.RARE, mage.cards.d.DrownedCatacomb.class)); cards.add(new SetCardInfo("Drowned Catacomb", 290, Rarity.RARE, mage.cards.d.DrownedCatacomb.class));
cards.add(new SetCardInfo("Dune Chanter", 31, Rarity.RARE, mage.cards.d.DuneChanter.class)); cards.add(new SetCardInfo("Dune Chanter", 31, Rarity.RARE, mage.cards.d.DuneChanter.class));
cards.add(new SetCardInfo("Dunes of the Dead", 291, Rarity.UNCOMMON, mage.cards.d.DunesOfTheDead.class)); cards.add(new SetCardInfo("Dunes of the Dead", 291, Rarity.UNCOMMON, mage.cards.d.DunesOfTheDead.class));
@ -130,6 +131,7 @@ public final class OutlawsOfThunderJunctionCommander extends ExpansionSet {
cards.add(new SetCardInfo("Ghostly Pilferer", 98, Rarity.RARE, mage.cards.g.GhostlyPilferer.class)); cards.add(new SetCardInfo("Ghostly Pilferer", 98, Rarity.RARE, mage.cards.g.GhostlyPilferer.class));
cards.add(new SetCardInfo("Glittering Stockpile", 167, Rarity.UNCOMMON, mage.cards.g.GlitteringStockpile.class)); cards.add(new SetCardInfo("Glittering Stockpile", 167, Rarity.UNCOMMON, mage.cards.g.GlitteringStockpile.class));
cards.add(new SetCardInfo("Goblin Electromancer", 228, Rarity.COMMON, mage.cards.g.GoblinElectromancer.class)); cards.add(new SetCardInfo("Goblin Electromancer", 228, Rarity.COMMON, mage.cards.g.GoblinElectromancer.class));
cards.add(new SetCardInfo("Gonti, Canny Acquisitor", 1, Rarity.MYTHIC, mage.cards.g.GontiCannyAcquisitor.class));
cards.add(new SetCardInfo("Gonti, Lord of Luxury", 135, Rarity.RARE, mage.cards.g.GontiLordOfLuxury.class)); cards.add(new SetCardInfo("Gonti, Lord of Luxury", 135, Rarity.RARE, mage.cards.g.GontiLordOfLuxury.class));
cards.add(new SetCardInfo("Graywater's Fixer", 36, Rarity.RARE, mage.cards.g.GraywatersFixer.class)); cards.add(new SetCardInfo("Graywater's Fixer", 36, Rarity.RARE, mage.cards.g.GraywatersFixer.class));
cards.add(new SetCardInfo("Grenzo, Havoc Raiser", 168, Rarity.RARE, mage.cards.g.GrenzoHavocRaiser.class)); cards.add(new SetCardInfo("Grenzo, Havoc Raiser", 168, Rarity.RARE, mage.cards.g.GrenzoHavocRaiser.class));

View file

@ -0,0 +1,41 @@
package org.mage.test.cards.single.otc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class GontiCannyAcquisitorTest extends CardTestPlayerBase {
/**
* {@link mage.cards.g.GontiCannyAcquisitor Gonti, Canny Acquisitor} {2}{B}{G}{U}
* Legendary Creature Aetherborn Rogue
* Spells you cast but dont own cost {1} less to cast.
* Whenever one or more creatures you control deal combat damage to a player, look at the top card of that players library, then exile it face down. You may play that card for as long as it remains exiled, and mana of any type can be spent to cast that spell.
* 5/5
*/
private static final String gonti = "Gonti, Canny Acquisitor";
@Test
public void test_Simple() {
setStrictChooseMode(true);
skipInitShuffling();
addCard(Zone.BATTLEFIELD, playerA, gonti);
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.LIBRARY, playerB, "Grizzly Bears", 1); // {1}{G}
attack(1, playerA, gonti, playerB);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Grizzly Bears", 1);
assertTapped("Mountain", true);
}
}

View file

@ -0,0 +1,47 @@
package org.mage.test.cards.single.woc;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class BlightwingBanditTest extends CardTestPlayerBase {
/**
* {@link mage.cards.b.BlightwingBandit Blightwing Bandit} {3}{B}
* Creature Faerie Rogue
* Flying, deathtouch
* Whenever you cast your first spell during each opponents turn, look at the top card of that players library, then exile it face down. You may play that card for as long as it remains exiled, and mana of any type can be spent to cast it.
* 2/2
*/
private static final String bandit = "Blightwing Bandit";
@Test
public void test_Simple() {
setStrictChooseMode(true);
skipInitShuffling();
addCard(Zone.BATTLEFIELD, playerA, bandit);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
addCard(Zone.HAND, playerA, "Lightning Bolt");
addCard(Zone.LIBRARY, playerB, "Fire // Ice", 1); // split card just to check it works on both faces
castSpell(2, PhaseStep.UPKEEP, playerA, "Lightning Bolt", playerB);
waitStackResolved(2, PhaseStep.UPKEEP); // resolve trigger from bandit
checkPlayableAbility("can cast fire", 2, PhaseStep.UPKEEP, playerA, "Cast Fire", true);
checkPlayableAbility("can cast ice", 2, PhaseStep.UPKEEP, playerA, "Cast Ice", true);
castSpell(2, PhaseStep.UPKEEP, playerA, "Ice", bandit);
setStopAt(2, PhaseStep.DRAW);
execute();
assertGraveyardCount(playerB, "Fire // Ice", 1);
assertTappedCount("Mountain", true, 3);
assertHandCount(playerA, 1); // draw from Ice
assertTapped(bandit, true); // tapped from Ice
assertLife(playerB, 20 - 3);
}
}

View file

@ -0,0 +1,55 @@
package org.mage.test.cards.single.woe;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class DecadentDragonTest extends CardTestPlayerBase {
/**
* {@link mage.cards.d.DecadentDragon Decadent Dragon} {2}{R}{R}
* Creature Dragon
* Flying, trample
* Whenever Decadent Dragon attacks, create a Treasure token.
* 4/4
* Expensive Taste {2}{B}
* Instant Adventure
* Exile the top two cards of target opponents library face down. You may look at and play those cards for as long as they remain exiled.
*/
private static final String dragon = "Decadent Dragon";
@Test
public void test_MDFC_And_Split() {
setStrictChooseMode(true);
skipInitShuffling();
addCard(Zone.HAND, playerA, dragon);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3 + 4);
addCard(Zone.LIBRARY, playerB, "Pain // Suffering", 1); // can cast Pain, but Suffering cost {3}{R} so can't be cast
addCard(Zone.LIBRARY, playerB, "Blackbloom Rogue", 1); // mdfc {2}{B} with land Blackbloom Bog on the back.
addCard(Zone.HAND, playerB, "Abandon Hope"); // to discard to Pain
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Expensive Taste", playerB);
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
checkPlayableAbility("can cast Pain", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Pain", true);
checkPlayableAbility("can not cast Suffering", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Suffering", false);
checkPlayableAbility("can cast Blackbloom Rogue", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Blackbloom Rogue", true);
checkPlayableAbility("can play Blackbloom Bog", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Blackbloom Bog", true);
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blackbloom Bog");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pain", playerB);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerB, "Pain // Suffering", 1);
assertGraveyardCount(playerB, "Abandon Hope", 1);
assertTappedCount("Blackbloom Bog", true, 1);
assertTappedCount("Swamp", true, 4);
}
}

View file

@ -0,0 +1,74 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CastManaAdjustment;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
import mage.target.targetpointer.FixedTargets;
/**
* This exiles the target players' top N cards of library.
* Each card can be looked at by the source's controller.
* For each card exiled this way, that player may play|cast that card as long as it stays exiled. (+ mana adjustement)
* e.g. [[Gonti, Canny Acquisitor]]
*
* @author Susucr
*/
public class ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect extends OneShotEffect {
private final boolean useCastSpellOnly;
private final CastManaAdjustment manaAdjustment;
private final DynamicValue value;
public ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(boolean useCastSpellOnly, CastManaAdjustment manaAdjustment) {
this(useCastSpellOnly, manaAdjustment, 1);
}
public ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(boolean useCastSpellOnly, CastManaAdjustment manaAdjustment, int amount) {
this(useCastSpellOnly, manaAdjustment, StaticValue.get(amount));
}
public ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(boolean useCastSpellOnly, CastManaAdjustment manaAdjustment, DynamicValue value) {
super(Outcome.Exile);
this.value = value;
this.useCastSpellOnly = useCastSpellOnly;
this.manaAdjustment = manaAdjustment;
}
private ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(final ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect effect) {
super(effect);
this.value = effect.value;
this.useCastSpellOnly = effect.useCastSpellOnly;
this.manaAdjustment = effect.manaAdjustment;
}
@Override
public ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect copy() {
return new ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
if (player == null) {
return false;
}
int amount = value.calculate(game, source, this);
Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, amount));
if (cards.isEmpty()) {
return false;
}
return new ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect(useCastSpellOnly, manaAdjustment)
.setTargetPointer(new FixedTargets(cards, game))
.apply(game, source);
}
}

View file

@ -1,7 +1,9 @@
package mage.abilities.effects; package mage.abilities.effects.common;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect; import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.Cards; import mage.cards.Cards;