This commit is contained in:
Evan Kranzler 2018-09-22 11:15:59 -04:00
commit 4928c52204
8 changed files with 275 additions and 16 deletions

View file

@ -1,7 +1,6 @@
package mage.cards.f;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.abilities.common.BecomesTargetTriggeredAbility;
@ -15,13 +14,11 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterSpell;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
/**
@ -32,7 +29,6 @@ public final class ForceProjection extends CardImpl {
public ForceProjection(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}{U}");
// Create a token that is a copy of target creature you control except that it is an Illusion in addition to its other types and gains "When this creature becomes the target of a spell, sacrifice it."
this.getSpellAbility().addEffect(new ForceProjectionEffect());
@ -56,8 +52,8 @@ class ForceProjectionEffect extends OneShotEffect {
public ForceProjectionEffect() {
super(Outcome.Copy);
this.staticText = "Create a token that is a copy of target creature you control except that it is an Illusion " +
"in addition to its other types and gains \"When this creature becomes the target of a spell, sacrifice it.\"";
this.staticText = "Create a token that is a copy of target creature you control except that it is an Illusion "
+ "in addition to its other types and gains \"When this creature becomes the target of a spell, sacrifice it.\"";
}
public ForceProjectionEffect(final ForceProjectionEffect effect) {

View file

@ -0,0 +1,260 @@
package mage.cards.t;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.FlyingAbility;
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.Zone;
import mage.filter.FilterCard;
import mage.game.ExileZone;
import mage.game.Game;
import mage.players.ManaPoolItem;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
*
* @author LevelX2
*/
public final class ThiefOfSanity extends CardImpl {
protected static final String VALUE_PREFIX = "ExileZones";
public ThiefOfSanity(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}");
this.subtype.add(SubType.SPECTER);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever Thief of Sanity deals combat damage to a player, 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.
this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new ThiefOfSanityEffect(), false, true));
Ability ability = new SimpleStaticAbility(Zone.ALL, new ThiefOfSanityLookEffect());
ability.setRuleVisible(false);
this.addAbility(ability);
}
public ThiefOfSanity(final ThiefOfSanity card) {
super(card);
}
@Override
public ThiefOfSanity copy() {
return new ThiefOfSanity(this);
}
}
class ThiefOfSanityEffect extends OneShotEffect {
public ThiefOfSanityEffect() {
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. 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";
}
public ThiefOfSanityEffect(final ThiefOfSanityEffect effect) {
super(effect);
}
@Override
public ThiefOfSanityEffect copy() {
return new ThiefOfSanityEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Player damagedPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && damagedPlayer != null && sourceObject != null) {
Cards topCards = new CardsImpl();
topCards.addAll(damagedPlayer.getLibrary().getTopCards(game, 3));
TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to exile face down"));
if (controller.choose(outcome, topCards, target, game)) {
Card card = game.getCard(target.getFirstTarget());
if (card != null) {
topCards.remove(card);
// move card to exile
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(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);
// allow to cast the card
ContinuousEffect effect = new ThiefOfSanityCastFromExileEffect();
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();
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
}
}
}
// put the rest into their graveyard
controller.moveCards(topCards, Zone.GRAVEYARD, source, game);
return true;
}
return false;
}
}
class ThiefOfSanityCastFromExileEffect extends AsThoughEffectImpl {
public ThiefOfSanityCastFromExileEffect() {
super(AsThoughEffectType.PLAY_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";
}
public ThiefOfSanityCastFromExileEffect(final ThiefOfSanityCastFromExileEffect effect) {
super(effect);
}
@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 targetId = getTargetPointer().getFirst(game, source);
if (targetId == null) {
this.discard();
} else if (objectId.equals(targetId)
&& affectedControllerId.equals(source.getControllerId())) {
Card card = game.getCard(objectId);
// TODO: Allow to cast Zoetic Cavern face down
return card != null && !card.isLand();
}
return false;
}
}
class ThiefOfSanitySpendAnyManaEffect extends AsThoughEffectImpl implements AsThoughManaEffect {
public ThiefOfSanitySpendAnyManaEffect() {
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";
}
public ThiefOfSanitySpendAnyManaEffect(final ThiefOfSanitySpendAnyManaEffect effect) {
super(effect);
}
@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) {
if (objectId.equals(((FixedTarget) getTargetPointer()).getTarget())
&& game.getState().getZoneChangeCounter(objectId) <= ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
if (affectedControllerId.equals(source.getControllerId())) {
// if the card moved from exile to spell the zone change counter is increased by 1
if (game.getState().getZoneChangeCounter(objectId) == ((FixedTarget) getTargetPointer()).getZoneChangeCounter() + 1) {
return true;
}
}
} else if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) {
// object has moved zone so effect can be discarted
this.discard();
}
return false;
}
@Override
public ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game) {
return mana.getFirstAvailable();
}
}
class ThiefOfSanityLookEffect extends AsThoughEffectImpl {
public ThiefOfSanityLookEffect() {
super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
staticText = "You may look at the cards exiled with {this}";
}
public ThiefOfSanityLookEffect(final ThiefOfSanityLookEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public ThiefOfSanityLookEffect copy() {
return new ThiefOfSanityLookEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (affectedControllerId.equals(source.getControllerId()) && game.getState().getZone(objectId) == Zone.EXILED) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
Card card = game.getCard(objectId);
if (card != null && card.isFaceDown(game)) {
Set<UUID> exileZones = (Set<UUID>) game.getState().getValue(ThiefOfSanity.VALUE_PREFIX + source.getSourceId().toString());
if (exileZones != null) {
for (ExileZone exileZone : game.getExile().getExileZones()) {
if (exileZone.contains(objectId)) {
if (!exileZones.contains(exileZone.getId())) {
return false;
}
}
}
return true;
}
}
}
}
return false;
}
}

View file

@ -259,6 +259,7 @@ public final class GuildsOfRavnica extends ExpansionSet {
cards.add(new SetCardInfo("Take Heart", 28, Rarity.COMMON, mage.cards.t.TakeHeart.class));
cards.add(new SetCardInfo("Temple Garden", 258, Rarity.RARE, mage.cards.t.TempleGarden.class));
cards.add(new SetCardInfo("Tenth District Guard", 29, Rarity.COMMON, mage.cards.t.TenthDistrictGuard.class));
cards.add(new SetCardInfo("Thief of Sanity", 205, Rarity.RARE, mage.cards.t.ThiefOfSanity.class));
cards.add(new SetCardInfo("Thought Erasure", 206, Rarity.UNCOMMON, mage.cards.t.ThoughtErasure.class));
cards.add(new SetCardInfo("Thoughtbound Phantasm", 55, Rarity.UNCOMMON, mage.cards.t.ThoughtboundPhantasm.class));
cards.add(new SetCardInfo("Torch Courier", 119, Rarity.COMMON, mage.cards.t.TorchCourier.class));

View file

@ -12,7 +12,7 @@ import mage.game.Game;
*/
public interface AsThoughEffect extends ContinuousEffect {
boolean applies(UUID sourceId, Ability affectedAbility, Ability source, Game game);
boolean applies(UUID sourceId, Ability affectedAbility, Ability source, Game game, UUID playerId);
boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game);

View file

@ -1,4 +1,3 @@
package mage.abilities.effects;
import java.util.UUID;
@ -29,8 +28,12 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements
}
@Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game) {
return applies(objectId, source, affectedAbility.getControllerId(), game);
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
if (getAsThoughEffectType().equals(AsThoughEffectType.LOOK_AT_FACE_DOWN)) {
return applies(objectId, source, playerId, game);
} else {
return applies(objectId, source, affectedAbility.getControllerId(), game);
}
}
@Override

View file

@ -1,4 +1,3 @@
package mage.abilities.effects;
import java.io.Serializable;
@ -496,7 +495,7 @@ public class ContinuousEffects implements Serializable {
if (effect.applies(objectId, ability, controllerId, game)) {
return new MageObjectReference(ability.getSourceObject(game), game);
}
} else if (effect.applies(objectId, affectedAbility, ability, game)) {
} else if (effect.applies(objectId, affectedAbility, ability, game, controllerId)) {
return new MageObjectReference(ability.getSourceObject(game), game);
}
}
@ -512,7 +511,7 @@ public class ContinuousEffects implements Serializable {
Set<Ability> abilities = asThoughEffectsMap.get(AsThoughEffectType.SPEND_ONLY_MANA).getAbility(effect.getId());
for (Ability ability : abilities) {
if ((affectedAbility == null && effect.applies(objectId, ability, controllerId, game))
|| effect.applies(objectId, affectedAbility, ability, game)) {
|| effect.applies(objectId, affectedAbility, ability, game, controllerId)) {
if (((AsThoughManaEffect) effect).getAsThoughManaType(manaType, mana, controllerId, ability, game) == null) {
return null;
}
@ -525,7 +524,7 @@ public class ContinuousEffects implements Serializable {
Set<Ability> abilities = asThoughEffectsMap.get(AsThoughEffectType.SPEND_OTHER_MANA).getAbility(effect.getId());
for (Ability ability : abilities) {
if ((affectedAbility == null && effect.applies(objectId, ability, controllerId, game))
|| effect.applies(objectId, affectedAbility, ability, game)) {
|| effect.applies(objectId, affectedAbility, ability, game, controllerId)) {
ManaType usableManaType = ((AsThoughManaEffect) effect).getAsThoughManaType(manaType, mana, controllerId, ability, game);
if (usableManaType != null) {
return usableManaType;

View file

@ -43,7 +43,7 @@ public class ActivateAbilitiesAnyTimeYouCouldCastInstantEffect extends AsThoughE
}
@Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game) {
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
return affectedAbility.isControlledBy(source.getControllerId())
&& activatedAbility.isInstance(affectedAbility);
}

View file

@ -104,7 +104,7 @@ class OfferingAsThoughEffect extends AsThoughEffectImpl {
}
@Override
public boolean applies(UUID sourceId, Ability affectedAbility, Ability source, Game game) {
public boolean applies(UUID sourceId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
if (sourceId.equals(source.getSourceId())) {
Card card = game.getCard(sourceId);
if (!card.isOwnedBy(source.getControllerId())) {