mirror of
https://github.com/correl/mage.git
synced 2024-11-14 19:19:32 +00:00
parent
c89e5077af
commit
b1b78d6db0
6 changed files with 398 additions and 604 deletions
|
@ -1,64 +1,40 @@
|
||||||
package mage.cards.a;
|
package mage.cards.a;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.MageObjectReference;
|
import mage.abilities.common.AnimateDeadTriggeredAbility;
|
||||||
import mage.abilities.Ability;
|
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
|
||||||
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
|
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.condition.common.SourceOnBattlefieldCondition;
|
import mage.abilities.effects.common.AttachEffect;
|
||||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
|
||||||
import mage.abilities.effects.ContinuousEffect;
|
|
||||||
import mage.abilities.effects.ContinuousEffectImpl;
|
|
||||||
import mage.abilities.effects.OneShotEffect;
|
|
||||||
import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
|
import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
|
||||||
import mage.abilities.effects.common.continuous.SourceEffect;
|
|
||||||
import mage.abilities.keyword.EnchantAbility;
|
import mage.abilities.keyword.EnchantAbility;
|
||||||
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.constants.Outcome;
|
||||||
|
import mage.constants.SubType;
|
||||||
import mage.filter.common.FilterCreatureCard;
|
import mage.filter.common.FilterCreatureCard;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
|
||||||
import mage.filter.predicate.permanent.PermanentIdPredicate;
|
|
||||||
import mage.game.Game;
|
|
||||||
import mage.game.permanent.Permanent;
|
|
||||||
import mage.players.Player;
|
|
||||||
import mage.target.Target;
|
|
||||||
import mage.target.common.TargetCardInGraveyard;
|
import mage.target.common.TargetCardInGraveyard;
|
||||||
import mage.target.common.TargetCreaturePermanent;
|
|
||||||
import mage.target.targetpointer.FixedTarget;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @author LevelX2, awjackson
|
||||||
* @author LevelX2
|
|
||||||
*/
|
*/
|
||||||
public final class AnimateDead extends CardImpl {
|
public final class AnimateDead extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterCreatureCard filter = new FilterCreatureCard("creature card in a graveyard");
|
||||||
|
|
||||||
public AnimateDead(UUID ownerId, CardSetInfo setInfo) {
|
public AnimateDead(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
|
||||||
this.subtype.add(SubType.AURA);
|
this.subtype.add(SubType.AURA);
|
||||||
|
|
||||||
// Enchant creature card in a graveyard
|
// Enchant creature card in a graveyard
|
||||||
TargetCardInGraveyard auraTarget = new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard"));
|
TargetCardInGraveyard auraTarget = new TargetCardInGraveyard(filter);
|
||||||
this.getSpellAbility().addTarget(auraTarget);
|
this.getSpellAbility().addTarget(auraTarget);
|
||||||
this.getSpellAbility().addEffect(new AnimateDeadAttachEffect(Outcome.PutCreatureInPlay));
|
this.getSpellAbility().addEffect(new AttachEffect(Outcome.PutCreatureInPlay));
|
||||||
Ability enchantAbility = new EnchantAbility(auraTarget.getTargetName());
|
this.addAbility(new EnchantAbility(auraTarget.getTargetName()));
|
||||||
this.addAbility(enchantAbility);
|
|
||||||
// When Animate Dead enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard"
|
this.addAbility(new AnimateDeadTriggeredAbility());
|
||||||
// and gains "enchant creature put onto the battlefield with Animate Dead." Return enchanted creature card to the battlefield
|
|
||||||
// under your control and attach Animate Dead to it. When Animate Dead leaves the battlefield, that creature's controller sacrifices it.
|
|
||||||
Ability ability = new ConditionalInterveningIfTriggeredAbility(
|
|
||||||
new EntersBattlefieldTriggeredAbility(new AnimateDeadReAttachEffect(), false),
|
|
||||||
SourceOnBattlefieldCondition.instance,
|
|
||||||
"When {this} enters the battlefield, if it's on the battlefield, it loses \"enchant creature card in a graveyard\" and gains \"enchant creature put onto the battlefield with {this}.\" Return enchanted creature card to the battlefield under your control and attach {this} to it.");
|
|
||||||
ability.addEffect(new AnimateDeadChangeAbilityEffect());
|
|
||||||
this.addAbility(ability);
|
|
||||||
this.addAbility(new LeavesBattlefieldTriggeredAbility(new AnimateDeadLeavesBattlefieldTriggeredEffect(), false));
|
|
||||||
|
|
||||||
// Enchanted creature gets -1/-0.
|
// Enchanted creature gets -1/-0.
|
||||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(-1, 0, Duration.WhileOnBattlefield)));
|
this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(-1, 0)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnimateDead(final AnimateDead card) {
|
private AnimateDead(final AnimateDead card) {
|
||||||
|
@ -70,205 +46,3 @@ public final class AnimateDead extends CardImpl {
|
||||||
return new AnimateDead(this);
|
return new AnimateDead(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AnimateDeadReAttachEffect extends OneShotEffect {
|
|
||||||
|
|
||||||
public AnimateDeadReAttachEffect() {
|
|
||||||
super(Outcome.Benefit);
|
|
||||||
this.staticText = "return enchanted creature card to the battlefield under your control and attach {this} to it";
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnimateDeadReAttachEffect(final AnimateDeadReAttachEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AnimateDeadReAttachEffect copy() {
|
|
||||||
return new AnimateDeadReAttachEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
|
||||||
Permanent animateDead = game.getPermanent(source.getSourceId());
|
|
||||||
|
|
||||||
if (controller != null && animateDead != null) {
|
|
||||||
Card cardInGraveyard = game.getCard(animateDead.getAttachedTo());
|
|
||||||
if (cardInGraveyard == null || game.getState().getZone(cardInGraveyard.getId()) != Zone.GRAVEYARD) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// put card into play from Graveyard
|
|
||||||
controller.moveCards(cardInGraveyard, Zone.BATTLEFIELD, source, game);
|
|
||||||
Permanent enchantedCreature = game.getPermanent(cardInGraveyard.getId());
|
|
||||||
|
|
||||||
if (enchantedCreature != null) {
|
|
||||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("enchant creature put onto the battlefield with Animate Dead");
|
|
||||||
filter.add(new PermanentIdPredicate(cardInGraveyard.getId()));
|
|
||||||
Target target = new TargetCreaturePermanent(filter);
|
|
||||||
target.setNotTarget(true); // Bug #7772
|
|
||||||
target.addTarget(enchantedCreature.getId(), source, game);
|
|
||||||
animateDead.getSpellAbility().getTargets().clear();
|
|
||||||
animateDead.getSpellAbility().getTargets().add(target);
|
|
||||||
enchantedCreature.addAttachment(animateDead.getId(), source, game);
|
|
||||||
ContinuousEffect effect = new AnimateDeadAttachToPermanentEffect();
|
|
||||||
effect.setTargetPointer(new FixedTarget(enchantedCreature, game));
|
|
||||||
game.addEffect(effect, source);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AnimateDeadLeavesBattlefieldTriggeredEffect extends OneShotEffect {
|
|
||||||
|
|
||||||
public AnimateDeadLeavesBattlefieldTriggeredEffect() {
|
|
||||||
super(Outcome.Benefit);
|
|
||||||
this.staticText = "enchanted creature's controller sacrifices it";
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnimateDeadLeavesBattlefieldTriggeredEffect(final AnimateDeadLeavesBattlefieldTriggeredEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AnimateDeadLeavesBattlefieldTriggeredEffect copy() {
|
|
||||||
return new AnimateDeadLeavesBattlefieldTriggeredEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
|
||||||
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
|
|
||||||
if (controller != null && sourcePermanent != null) {
|
|
||||||
if (sourcePermanent.getAttachedTo() != null) {
|
|
||||||
Permanent attachedTo = game.getPermanent(sourcePermanent.getAttachedTo());
|
|
||||||
if (attachedTo != null && attachedTo.getZoneChangeCounter(game) == sourcePermanent.getAttachedToZoneChangeCounter()) {
|
|
||||||
attachedTo.sacrifice(source, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AnimateDeadAttachEffect extends OneShotEffect {
|
|
||||||
|
|
||||||
public AnimateDeadAttachEffect(Outcome outcome) {
|
|
||||||
super(outcome);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnimateDeadAttachEffect(Outcome outcome, String rule) {
|
|
||||||
super(outcome);
|
|
||||||
staticText = rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnimateDeadAttachEffect(final AnimateDeadAttachEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AnimateDeadAttachEffect copy() {
|
|
||||||
return new AnimateDeadAttachEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
Card card = game.getCard(source.getFirstTarget());
|
|
||||||
if (card != null && game.getState().getZone(source.getFirstTarget()) == Zone.GRAVEYARD) {
|
|
||||||
// Card have no attachedTo attribute yet so write ref only to enchantment now
|
|
||||||
Permanent enchantment = game.getPermanent(source.getSourceId());
|
|
||||||
if (enchantment != null) {
|
|
||||||
enchantment.attachTo(card.getId(), source, game);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class AnimateDeadChangeAbilityEffect extends ContinuousEffectImpl implements SourceEffect {
|
|
||||||
|
|
||||||
private static final Ability newAbility = new EnchantAbility("creature put onto the battlefield with Animate Dead");
|
|
||||||
|
|
||||||
static {
|
|
||||||
newAbility.setRuleAtTheTop(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnimateDeadChangeAbilityEffect() {
|
|
||||||
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
|
|
||||||
staticText = "it loses \"enchant creature card in a graveyard\" and gains \"enchant creature put onto the battlefield with Animate Dead\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnimateDeadChangeAbilityEffect(final AnimateDeadChangeAbilityEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AnimateDeadChangeAbilityEffect copy() {
|
|
||||||
return new AnimateDeadChangeAbilityEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(Ability source, Game game) {
|
|
||||||
super.init(source, game);
|
|
||||||
affectedObjectList.add(new MageObjectReference(source.getSourceId(), game));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
Permanent permanent = affectedObjectList.get(0).getPermanent(game);
|
|
||||||
if (permanent != null) {
|
|
||||||
Ability abilityToRemove = null;
|
|
||||||
for (Ability ability : permanent.getAbilities()) {
|
|
||||||
if (ability instanceof EnchantAbility) {
|
|
||||||
abilityToRemove = ability;
|
|
||||||
ability.getTargets().clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
permanent.removeAbility(abilityToRemove, source.getSourceId(), game);
|
|
||||||
permanent.addAbility(newAbility, source.getSourceId(), game);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AnimateDeadAttachToPermanentEffect extends ContinuousEffectImpl {
|
|
||||||
|
|
||||||
public AnimateDeadAttachToPermanentEffect() {
|
|
||||||
super(Duration.Custom, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnimateDeadAttachToPermanentEffect(final AnimateDeadAttachToPermanentEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AnimateDeadAttachToPermanentEffect copy() {
|
|
||||||
return new AnimateDeadAttachToPermanentEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
Permanent animateDead = game.getPermanent(source.getSourceId());
|
|
||||||
if (animateDead != null) {
|
|
||||||
// The target has to be changed to CreaturePermanent because the reset from card resets it to Card in Graveyard
|
|
||||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("enchant creature put onto the battlefield with Animate Dead");
|
|
||||||
filter.add(new PermanentIdPredicate(getTargetPointer().getFirst(game, source)));
|
|
||||||
Target target = new TargetCreaturePermanent(filter);
|
|
||||||
target.setNotTarget(true); // Bug #7772
|
|
||||||
target.addTarget(((FixedTarget) getTargetPointer()).getTarget(), source, game);
|
|
||||||
animateDead.getSpellAbility().getTargets().clear();
|
|
||||||
animateDead.getSpellAbility().getTargets().add(target);
|
|
||||||
}
|
|
||||||
if (animateDead == null) {
|
|
||||||
discard();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,79 +1,60 @@
|
||||||
|
|
||||||
package mage.cards.d;
|
package mage.cards.d;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.MageObjectReference;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.Mode;
|
import mage.abilities.Mode;
|
||||||
|
import mage.abilities.common.AnimateDeadTriggeredAbility;
|
||||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
|
||||||
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
|
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.condition.common.SourceOnBattlefieldCondition;
|
|
||||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
|
||||||
import mage.abilities.effects.ContinuousEffectImpl;
|
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.common.AttachEffect;
|
||||||
import mage.abilities.effects.common.DoIfCostPaid;
|
import mage.abilities.effects.common.DoIfCostPaid;
|
||||||
import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect;
|
import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect;
|
||||||
import mage.abilities.effects.common.UntapEnchantedEffect;
|
import mage.abilities.effects.common.UntapEnchantedEffect;
|
||||||
import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
|
import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
|
||||||
import mage.abilities.effects.common.continuous.SourceEffect;
|
|
||||||
import mage.abilities.keyword.EnchantAbility;
|
import mage.abilities.keyword.EnchantAbility;
|
||||||
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.constants.Outcome;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.TargetController;
|
||||||
import mage.filter.common.FilterCreatureCard;
|
import mage.filter.common.FilterCreatureCard;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
|
||||||
import mage.filter.predicate.permanent.PermanentIdPredicate;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.Target;
|
|
||||||
import mage.target.common.TargetCardInGraveyard;
|
import mage.target.common.TargetCardInGraveyard;
|
||||||
import mage.target.common.TargetCreaturePermanent;
|
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @author LevelX2, awjackson
|
||||||
* @author LevelX2
|
|
||||||
*/
|
*/
|
||||||
public final class DanceOfTheDead extends CardImpl {
|
public final class DanceOfTheDead extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterCreatureCard filter = new FilterCreatureCard("creature card in a graveyard");
|
||||||
|
|
||||||
public DanceOfTheDead(UUID ownerId, CardSetInfo setInfo) {
|
public DanceOfTheDead(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
|
||||||
this.subtype.add(SubType.AURA);
|
this.subtype.add(SubType.AURA);
|
||||||
|
|
||||||
// Enchant creature card in a graveyard
|
// Enchant creature card in a graveyard
|
||||||
TargetCardInGraveyard auraTarget = new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard"));
|
TargetCardInGraveyard auraTarget = new TargetCardInGraveyard(filter);
|
||||||
this.getSpellAbility().addTarget(auraTarget);
|
this.getSpellAbility().addTarget(auraTarget);
|
||||||
this.getSpellAbility().addEffect(new DanceOfTheDeadAttachEffect(Outcome.PutCreatureInPlay));
|
this.getSpellAbility().addEffect(new AttachEffect(Outcome.PutCreatureInPlay));
|
||||||
Ability enchantAbility = new EnchantAbility(auraTarget.getTargetName());
|
this.addAbility(new EnchantAbility(auraTarget.getTargetName()));
|
||||||
this.addAbility(enchantAbility);
|
|
||||||
// When Dance of the Dead enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard" and gains "enchant creature put onto the battlefield with Dance of the Dead." Put enchanted creature card to the battlefield tapped under your control and attach Dance of the Dead to it. When Dance of the Dead leaves the battlefield, that creature's controller sacrifices it.
|
this.addAbility(new AnimateDeadTriggeredAbility(false, true));
|
||||||
Ability ability = new ConditionalInterveningIfTriggeredAbility(
|
|
||||||
new EntersBattlefieldTriggeredAbility(new DanceOfTheDeadReAttachEffect(), false),
|
|
||||||
SourceOnBattlefieldCondition.instance,
|
|
||||||
"When {this} enters the battlefield, if it's on the battlefield, "
|
|
||||||
+ "it loses \"enchant creature card in a graveyard\" and gains "
|
|
||||||
+ "\"enchant creature put onto the battlefield with {this}.\" "
|
|
||||||
+ "Return enchanted creature card to the battlefield tapped under your control and attach {this} to it.");
|
|
||||||
ability.addEffect(new DanceOfTheDeadChangeAbilityEffect());
|
|
||||||
this.addAbility(ability);
|
|
||||||
this.addAbility(new LeavesBattlefieldTriggeredAbility(new DanceOfTheDeadLeavesBattlefieldTriggeredEffect(), false));
|
|
||||||
|
|
||||||
// Enchanted creature gets +1/+1 and doesn't untap during its controller's untap step.
|
// Enchanted creature gets +1/+1 and doesn't untap during its controller's untap step.
|
||||||
ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield));
|
Ability ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 1));
|
||||||
Effect effect = new DontUntapInControllersUntapStepEnchantedEffect();
|
Effect effect = new DontUntapInControllersUntapStepEnchantedEffect();
|
||||||
effect.setText("and doesn't untap during its controller's untap step");
|
effect.setText("and doesn't untap during its controller's untap step");
|
||||||
ability.addEffect(effect);
|
ability.addEffect(effect);
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
// At the beginning of the upkeep of enchanted creature's controller, that player may pay {1}{B}. If they do, untap that creature.
|
// At the beginning of the upkeep of enchanted creature's controller, that player may pay {1}{B}. If they do, untap that creature.
|
||||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new DanceOfTheDeadDoIfCostPaidEffect(), TargetController.CONTROLLER_ATTACHED_TO, false));
|
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DanceOfTheDeadDoIfCostPaidEffect(), TargetController.CONTROLLER_ATTACHED_TO, false));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DanceOfTheDead(final DanceOfTheDead card) {
|
private DanceOfTheDead(final DanceOfTheDead card) {
|
||||||
|
@ -86,169 +67,6 @@ public final class DanceOfTheDead extends CardImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DanceOfTheDeadReAttachEffect extends OneShotEffect {
|
|
||||||
|
|
||||||
public DanceOfTheDeadReAttachEffect() {
|
|
||||||
super(Outcome.Benefit);
|
|
||||||
this.staticText = "Return enchanted creature card to the battlefield under your control and attach {this} to it";
|
|
||||||
}
|
|
||||||
|
|
||||||
public DanceOfTheDeadReAttachEffect(final DanceOfTheDeadReAttachEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DanceOfTheDeadReAttachEffect copy() {
|
|
||||||
return new DanceOfTheDeadReAttachEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
|
||||||
Permanent enchantment = game.getPermanent(source.getSourceId());
|
|
||||||
|
|
||||||
if (controller != null && enchantment != null) {
|
|
||||||
Card cardInGraveyard = game.getCard(enchantment.getAttachedTo());
|
|
||||||
if (cardInGraveyard == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// put card into play
|
|
||||||
controller.moveCards(cardInGraveyard, Zone.BATTLEFIELD, source, game, true, false, false, null);
|
|
||||||
Permanent enchantedCreature = game.getPermanent(cardInGraveyard.getId());
|
|
||||||
|
|
||||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("enchant creature put onto the battlefield with Dance of the Dead");
|
|
||||||
filter.add(new PermanentIdPredicate(cardInGraveyard.getId()));
|
|
||||||
Target target = new TargetCreaturePermanent(filter);
|
|
||||||
//enchantAbility.setTargetName(target.getTargetName());
|
|
||||||
if (enchantedCreature != null) {
|
|
||||||
target.addTarget(enchantedCreature.getId(), source, game);
|
|
||||||
enchantment.getSpellAbility().getTargets().clear();
|
|
||||||
enchantment.getSpellAbility().getTargets().add(target);
|
|
||||||
enchantedCreature.addAttachment(enchantment.getId(), source, game);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DanceOfTheDeadLeavesBattlefieldTriggeredEffect extends OneShotEffect {
|
|
||||||
|
|
||||||
public DanceOfTheDeadLeavesBattlefieldTriggeredEffect() {
|
|
||||||
super(Outcome.Benefit);
|
|
||||||
this.staticText = "enchanted creature's controller sacrifices it";
|
|
||||||
}
|
|
||||||
|
|
||||||
public DanceOfTheDeadLeavesBattlefieldTriggeredEffect(final DanceOfTheDeadLeavesBattlefieldTriggeredEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DanceOfTheDeadLeavesBattlefieldTriggeredEffect copy() {
|
|
||||||
return new DanceOfTheDeadLeavesBattlefieldTriggeredEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
|
||||||
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
|
|
||||||
if (controller != null && sourcePermanent != null) {
|
|
||||||
if (sourcePermanent.getAttachedTo() != null) {
|
|
||||||
Permanent attachedTo = game.getPermanent(sourcePermanent.getAttachedTo());
|
|
||||||
if (attachedTo != null && attachedTo.getZoneChangeCounter(game) == sourcePermanent.getAttachedToZoneChangeCounter()) {
|
|
||||||
attachedTo.sacrifice(source, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DanceOfTheDeadAttachEffect extends OneShotEffect {
|
|
||||||
|
|
||||||
public DanceOfTheDeadAttachEffect(Outcome outcome) {
|
|
||||||
super(outcome);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DanceOfTheDeadAttachEffect(Outcome outcome, String rule) {
|
|
||||||
super(outcome);
|
|
||||||
staticText = rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DanceOfTheDeadAttachEffect(final DanceOfTheDeadAttachEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DanceOfTheDeadAttachEffect copy() {
|
|
||||||
return new DanceOfTheDeadAttachEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
Card card = game.getCard(source.getFirstTarget());
|
|
||||||
if (card != null && game.getState().getZone(source.getFirstTarget()) == Zone.GRAVEYARD) {
|
|
||||||
// Card have no attachedTo attribute yet so write ref only to enchantment now
|
|
||||||
Permanent enchantment = game.getPermanent(source.getSourceId());
|
|
||||||
if (enchantment != null) {
|
|
||||||
enchantment.attachTo(card.getId(), source, game);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class DanceOfTheDeadChangeAbilityEffect extends ContinuousEffectImpl implements SourceEffect {
|
|
||||||
|
|
||||||
private static final Ability newAbility = new EnchantAbility("creature put onto the battlefield with Dance of the Dead");
|
|
||||||
|
|
||||||
static {
|
|
||||||
newAbility.setRuleAtTheTop(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DanceOfTheDeadChangeAbilityEffect() {
|
|
||||||
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
|
|
||||||
staticText = "it loses \"enchant creature card in a graveyard\" and gains \"enchant creature put onto the battlefield with Dance of the Dead\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
public DanceOfTheDeadChangeAbilityEffect(final DanceOfTheDeadChangeAbilityEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DanceOfTheDeadChangeAbilityEffect copy() {
|
|
||||||
return new DanceOfTheDeadChangeAbilityEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(Ability source, Game game) {
|
|
||||||
super.init(source, game);
|
|
||||||
affectedObjectList.add(new MageObjectReference(source.getSourceId(), game));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
Permanent permanent = affectedObjectList.get(0).getPermanent(game);
|
|
||||||
if (permanent != null) {
|
|
||||||
Ability abilityToRemove = null;
|
|
||||||
for (Ability ability : permanent.getAbilities()) {
|
|
||||||
if (ability instanceof EnchantAbility) {
|
|
||||||
abilityToRemove = ability;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
permanent.removeAbility(abilityToRemove, source.getSourceId(), game);
|
|
||||||
permanent.addAbility(newAbility, source.getSourceId(), game);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DanceOfTheDeadDoIfCostPaidEffect extends DoIfCostPaid {
|
class DanceOfTheDeadDoIfCostPaidEffect extends DoIfCostPaid {
|
||||||
|
|
||||||
public DanceOfTheDeadDoIfCostPaidEffect() {
|
public DanceOfTheDeadDoIfCostPaidEffect() {
|
||||||
|
|
|
@ -1,40 +1,27 @@
|
||||||
|
|
||||||
package mage.cards.n;
|
package mage.cards.n;
|
||||||
|
|
||||||
import mage.MageObjectReference;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.AnimateDeadTriggeredAbility;
|
||||||
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
|
|
||||||
import mage.abilities.common.SacrificeIfCastAtInstantTimeTriggeredAbility;
|
import mage.abilities.common.SacrificeIfCastAtInstantTimeTriggeredAbility;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.condition.common.SourceOnBattlefieldCondition;
|
|
||||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
|
||||||
import mage.abilities.effects.ContinuousEffectImpl;
|
|
||||||
import mage.abilities.effects.OneShotEffect;
|
|
||||||
import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashSourceEffect;
|
import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashSourceEffect;
|
||||||
import mage.abilities.effects.common.continuous.SourceEffect;
|
|
||||||
import mage.abilities.keyword.EnchantAbility;
|
|
||||||
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.constants.Duration;
|
||||||
|
import mage.constants.Zone;
|
||||||
import mage.filter.common.FilterCreatureCard;
|
import mage.filter.common.FilterCreatureCard;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
|
||||||
import mage.filter.predicate.permanent.PermanentIdPredicate;
|
|
||||||
import mage.game.Game;
|
|
||||||
import mage.game.permanent.Permanent;
|
|
||||||
import mage.players.Player;
|
|
||||||
import mage.target.Target;
|
|
||||||
import mage.target.common.TargetCardInGraveyard;
|
import mage.target.common.TargetCardInGraveyard;
|
||||||
import mage.target.common.TargetCreaturePermanent;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author LevelX2
|
* @author LevelX2, awjackson
|
||||||
*/
|
*/
|
||||||
public final class Necromancy extends CardImpl {
|
public final class Necromancy extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterCreatureCard filter = new FilterCreatureCard("creature card in a graveyard");
|
||||||
|
|
||||||
public Necromancy(UUID ownerId, CardSetInfo setInfo) {
|
public Necromancy(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}");
|
||||||
|
|
||||||
|
@ -42,16 +29,9 @@ public final class Necromancy extends CardImpl {
|
||||||
this.addAbility(new SimpleStaticAbility(Zone.ALL, new CastAsThoughItHadFlashSourceEffect(Duration.EndOfGame)));
|
this.addAbility(new SimpleStaticAbility(Zone.ALL, new CastAsThoughItHadFlashSourceEffect(Duration.EndOfGame)));
|
||||||
this.addAbility(new SacrificeIfCastAtInstantTimeTriggeredAbility());
|
this.addAbility(new SacrificeIfCastAtInstantTimeTriggeredAbility());
|
||||||
|
|
||||||
// When Necromancy enters the battlefield, if it's on the battlefield, it becomes an Aura with "enchant creature put onto the battlefield with Necromancy."
|
Ability ability = new AnimateDeadTriggeredAbility(true);
|
||||||
// Put target creature card from a graveyard onto the battlefield under your control and attach Necromancy to it.
|
ability.addTarget(new TargetCardInGraveyard(filter));
|
||||||
// When Necromancy leaves the battlefield, that creature's controller sacrifices it.
|
|
||||||
Ability ability = new ConditionalInterveningIfTriggeredAbility(
|
|
||||||
new EntersBattlefieldTriggeredAbility(new NecromancyReAttachEffect(), false),
|
|
||||||
SourceOnBattlefieldCondition.instance,
|
|
||||||
"When {this} enters the battlefield, if it's on the battlefield, it becomes an Aura with \"enchant creature put onto the battlefield with {this}.\" Put target creature card from a graveyard onto the battlefield under your control and attach {this} to it.");
|
|
||||||
ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard")));
|
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
this.addAbility(new LeavesBattlefieldTriggeredAbility(new NecromancyLeavesBattlefieldTriggeredEffect(), false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Necromancy(final Necromancy card) {
|
private Necromancy(final Necromancy card) {
|
||||||
|
@ -63,139 +43,3 @@ public final class Necromancy extends CardImpl {
|
||||||
return new Necromancy(this);
|
return new Necromancy(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NecromancyReAttachEffect extends OneShotEffect {
|
|
||||||
|
|
||||||
public NecromancyReAttachEffect() {
|
|
||||||
super(Outcome.Benefit);
|
|
||||||
this.staticText = "it becomes an Aura with \"enchant creature put onto the battlefield with {this}\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
public NecromancyReAttachEffect(final NecromancyReAttachEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NecromancyReAttachEffect copy() {
|
|
||||||
return new NecromancyReAttachEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
|
||||||
Permanent enchantment = game.getPermanent(source.getSourceId());
|
|
||||||
Card cardInGraveyard = game.getCard(getTargetPointer().getFirst(game, source));
|
|
||||||
if (controller != null && enchantment != null && cardInGraveyard != null) {
|
|
||||||
controller.moveCards(cardInGraveyard, Zone.BATTLEFIELD, source, game);
|
|
||||||
Permanent enchantedCreature = game.getPermanent(cardInGraveyard.getId());
|
|
||||||
if (enchantedCreature != null) {
|
|
||||||
enchantedCreature.addAttachment(enchantment.getId(), source, game);
|
|
||||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("enchant creature put onto the battlefield with " + enchantment.getIdName());
|
|
||||||
filter.add(new PermanentIdPredicate(cardInGraveyard.getId()));
|
|
||||||
Target target = new TargetCreaturePermanent(filter);
|
|
||||||
target.addTarget(enchantedCreature.getId(), source, game);
|
|
||||||
game.addEffect(new NecromancyChangeAbilityEffect(target), source);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NecromancyLeavesBattlefieldTriggeredEffect extends OneShotEffect {
|
|
||||||
|
|
||||||
public NecromancyLeavesBattlefieldTriggeredEffect() {
|
|
||||||
super(Outcome.Benefit);
|
|
||||||
this.staticText = "enchanted creature's controller sacrifices it";
|
|
||||||
}
|
|
||||||
|
|
||||||
public NecromancyLeavesBattlefieldTriggeredEffect(final NecromancyLeavesBattlefieldTriggeredEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NecromancyLeavesBattlefieldTriggeredEffect copy() {
|
|
||||||
return new NecromancyLeavesBattlefieldTriggeredEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
|
||||||
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
|
|
||||||
if (controller != null && sourcePermanent != null) {
|
|
||||||
if (sourcePermanent.getAttachedTo() != null) {
|
|
||||||
Permanent attachedTo = game.getPermanent(sourcePermanent.getAttachedTo());
|
|
||||||
if (attachedTo != null && attachedTo.getZoneChangeCounter(game) == sourcePermanent.getAttachedToZoneChangeCounter()) {
|
|
||||||
attachedTo.sacrifice(source, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NecromancyChangeAbilityEffect extends ContinuousEffectImpl implements SourceEffect {
|
|
||||||
|
|
||||||
private static final Ability newAbility = new EnchantAbility("creature put onto the battlefield with Necromancy");
|
|
||||||
|
|
||||||
static {
|
|
||||||
newAbility.setRuleAtTheTop(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Target target;
|
|
||||||
|
|
||||||
public NecromancyChangeAbilityEffect(Target target) {
|
|
||||||
super(Duration.Custom, Outcome.AddAbility);
|
|
||||||
staticText = "it becomes an Aura with \"enchant creature put onto the battlefield with {this}\"";
|
|
||||||
this.target = target;
|
|
||||||
dependencyTypes.add(DependencyType.AuraAddingRemoving);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NecromancyChangeAbilityEffect(final NecromancyChangeAbilityEffect effect) {
|
|
||||||
super(effect);
|
|
||||||
this.target = effect.target;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NecromancyChangeAbilityEffect copy() {
|
|
||||||
return new NecromancyChangeAbilityEffect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(Ability source, Game game) {
|
|
||||||
super.init(source, game);
|
|
||||||
affectedObjectList.add(new MageObjectReference(source.getSourceId(), game));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
|
||||||
Permanent permanent = affectedObjectList.get(0).getPermanent(game);
|
|
||||||
if (permanent != null) {
|
|
||||||
switch (layer) {
|
|
||||||
case TypeChangingEffects_4:
|
|
||||||
permanent.addSubType(game, SubType.AURA);
|
|
||||||
break;
|
|
||||||
case AbilityAddingRemovingEffects_6:
|
|
||||||
permanent.addAbility(newAbility, source.getSourceId(), game);
|
|
||||||
permanent.getSpellAbility().getTargets().clear();
|
|
||||||
permanent.getSpellAbility().getTargets().add(target);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
this.discard();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(Game game, Ability source) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasLayer(Layer layer) {
|
|
||||||
return layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.TypeChangingEffects_4;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package org.mage.test.cards.abilities.other;
|
package org.mage.test.cards.abilities.other;
|
||||||
|
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
|
@ -125,4 +124,55 @@ public class NecromancyTest extends CardTestPlayerBase {
|
||||||
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||||
assertGraveyardCount(playerB, "Silvercoat Lion", 1);
|
assertGraveyardCount(playerB, "Silvercoat Lion", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNecromancyWithYarok() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Yarok, the Desecrated");
|
||||||
|
addCard(Zone.HAND, playerA, "Necromancy");
|
||||||
|
addCard(Zone.GRAVEYARD, playerA, "Craw Wurm");
|
||||||
|
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy");
|
||||||
|
addTarget(playerA, "Craw Wurm");
|
||||||
|
addTarget(playerA, "Silvercoat Lion");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerA, "Necromancy", 1);
|
||||||
|
assertPermanentCount(playerA, "Craw Wurm", 1);
|
||||||
|
assertPermanentCount(playerA, "Silvercoat Lion", 1);
|
||||||
|
assertGraveyardCount(playerA, "Craw Wurm", 0);
|
||||||
|
assertGraveyardCount(playerA, "Silvercoat Lion", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNecromancyLeavesWithYarok() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Yarok, the Desecrated");
|
||||||
|
addCard(Zone.HAND, playerA, "Necromancy");
|
||||||
|
addCard(Zone.HAND, playerA, "Disenchant");
|
||||||
|
addCard(Zone.GRAVEYARD, playerA, "Craw Wurm");
|
||||||
|
addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy");
|
||||||
|
addTarget(playerA, "Craw Wurm");
|
||||||
|
addTarget(playerA, "Silvercoat Lion");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Disenchant", "Necromancy");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerA, "Yarok, the Desecrated", 1);
|
||||||
|
assertPermanentCount(playerA, "Necromancy", 0);
|
||||||
|
assertPermanentCount(playerA, "Craw Wurm", 0);
|
||||||
|
assertPermanentCount(playerA, "Silvercoat Lion", 0);
|
||||||
|
assertGraveyardCount(playerA, "Disenchant", 1);
|
||||||
|
assertGraveyardCount(playerA, "Necromancy", 1);
|
||||||
|
assertGraveyardCount(playerA, "Craw Wurm", 1);
|
||||||
|
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,4 +202,19 @@ public class AnimateDeadTest extends CardTestPlayerBase {
|
||||||
assertPermanentCount(playerA, "Animate Dead", 1);
|
assertPermanentCount(playerA, "Animate Dead", 1);
|
||||||
assertPermanentCount(playerA, "Dragonlord Atarka", 1);
|
assertPermanentCount(playerA, "Dragonlord Atarka", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAnimateMDFC() {
|
||||||
|
addCard(Zone.GRAVEYARD, playerA, "Blackbloom Rogue");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||||
|
addCard(Zone.HAND, playerA, "Animate Dead");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Blackbloom Rogue");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPowerToughness(playerA, "Blackbloom Rogue", 1, 3);
|
||||||
|
assertPermanentCount(playerA, "Animate Dead", 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,293 @@
|
||||||
|
package mage.abilities.common;
|
||||||
|
|
||||||
|
import mage.MageObjectReference;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.DelayedTriggeredAbility;
|
||||||
|
import mage.abilities.effects.ContinuousEffectImpl;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.SacrificeTargetEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.SourceEffect;
|
||||||
|
import mage.abilities.keyword.EnchantAbility;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
|
import mage.filter.predicate.Predicate;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.events.GameEvent;
|
||||||
|
import mage.game.events.ZoneChangeEvent;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.common.TargetCreaturePermanent;
|
||||||
|
import mage.target.targetpointer.FixedTarget;
|
||||||
|
import mage.util.CardUtil;
|
||||||
|
import mage.watchers.Watcher;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author LevelX2, awjackson
|
||||||
|
*/
|
||||||
|
public class AnimateDeadTriggeredAbility extends EntersBattlefieldTriggeredAbility {
|
||||||
|
|
||||||
|
public AnimateDeadTriggeredAbility() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnimateDeadTriggeredAbility(boolean becomesAura) {
|
||||||
|
this(becomesAura, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnimateDeadTriggeredAbility(boolean becomesAura, boolean tapped) {
|
||||||
|
super(new AnimateDeadReplaceAbilityEffect(becomesAura));
|
||||||
|
addEffect(new AnimateDeadPutOntoBattlefieldEffect(becomesAura, tapped));
|
||||||
|
addWatcher(new AnimateDeadWatcher());
|
||||||
|
setTriggerPhrase("When {this} enters the battlefield, if it's on the battlefield, ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnimateDeadTriggeredAbility(final AnimateDeadTriggeredAbility ability) {
|
||||||
|
super(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnimateDeadTriggeredAbility copy() {
|
||||||
|
return new AnimateDeadTriggeredAbility(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkInterveningIfClause(Game game) {
|
||||||
|
return getSourcePermanentIfItStillExists(game) != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimateDeadReplaceAbilityEffect extends ContinuousEffectImpl implements SourceEffect {
|
||||||
|
|
||||||
|
private final boolean becomesAura;
|
||||||
|
private Ability newAbility;
|
||||||
|
private TargetCreaturePermanent newTarget;
|
||||||
|
|
||||||
|
public AnimateDeadReplaceAbilityEffect(boolean becomesAura) {
|
||||||
|
super(Duration.Custom, Outcome.AddAbility);
|
||||||
|
this.becomesAura = becomesAura;
|
||||||
|
staticText = (becomesAura ? "it becomes an Aura with" :
|
||||||
|
"it loses \"enchant creature card in a graveyard\" and gains"
|
||||||
|
) + " \"enchant creature put onto the battlefield with {this}.\"";
|
||||||
|
if (becomesAura) {
|
||||||
|
dependencyTypes.add(DependencyType.AuraAddingRemoving);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnimateDeadReplaceAbilityEffect(final AnimateDeadReplaceAbilityEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
this.becomesAura = effect.becomesAura;
|
||||||
|
this.newAbility = effect.newAbility;
|
||||||
|
this.newTarget = effect.newTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnimateDeadReplaceAbilityEffect copy() {
|
||||||
|
return new AnimateDeadReplaceAbilityEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Ability source, Game game) {
|
||||||
|
super.init(source, game);
|
||||||
|
affectedObjectList.add(new MageObjectReference(source.getSourceId(), game));
|
||||||
|
|
||||||
|
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature put onto the battlefield with {this}");
|
||||||
|
filter.add(new AnimateDeadPredicate(source.getSourceId()));
|
||||||
|
newTarget = new TargetCreaturePermanent(filter);
|
||||||
|
newAbility = new EnchantAbility(newTarget.getTargetName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||||
|
Permanent permanent = affectedObjectList.get(0).getPermanent(game);
|
||||||
|
if (permanent == null) {
|
||||||
|
discard();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
switch (layer) {
|
||||||
|
case TypeChangingEffects_4:
|
||||||
|
if (becomesAura) {
|
||||||
|
permanent.addSubType(game, SubType.AURA);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AbilityAddingRemovingEffects_6:
|
||||||
|
if (!becomesAura) {
|
||||||
|
List<Ability> toRemove = new ArrayList<>();
|
||||||
|
for (Ability ability : permanent.getAbilities(game)) {
|
||||||
|
if (ability instanceof EnchantAbility &&
|
||||||
|
ability.getRule().equals("Enchant creature card in a graveyard")) {
|
||||||
|
toRemove.add(ability);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
permanent.removeAbilities(toRemove, source.getSourceId(), game);
|
||||||
|
}
|
||||||
|
permanent.addAbility(newAbility, source.getSourceId(), game);
|
||||||
|
permanent.getSpellAbility().getTargets().clear();
|
||||||
|
permanent.getSpellAbility().getTargets().add(newTarget);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasLayer(Layer layer) {
|
||||||
|
return Layer.AbilityAddingRemovingEffects_6 == layer || becomesAura && Layer.TypeChangingEffects_4 == layer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimateDeadPutOntoBattlefieldEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
private final boolean becomesAura;
|
||||||
|
private final boolean tapped;
|
||||||
|
|
||||||
|
public AnimateDeadPutOntoBattlefieldEffect(boolean becomesAura, boolean tapped) {
|
||||||
|
super(Outcome.PutCreatureInPlay);
|
||||||
|
this.becomesAura = becomesAura;
|
||||||
|
this.tapped = tapped;
|
||||||
|
StringBuilder sb = new StringBuilder(becomesAura || tapped ? "put " : "return ");
|
||||||
|
sb.append(becomesAura ? "target creature card from a graveyard" : "enchanted creature card");
|
||||||
|
sb.append(becomesAura || tapped ? " onto" : " to");
|
||||||
|
sb.append(" the battlefield ");
|
||||||
|
if (tapped) {
|
||||||
|
sb.append("tapped ");
|
||||||
|
}
|
||||||
|
sb.append("under your control and attach {this} to it. When {this} leaves the battlefield, that creature's controller sacrifices it");
|
||||||
|
staticText = sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnimateDeadPutOntoBattlefieldEffect(final AnimateDeadPutOntoBattlefieldEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
this.becomesAura = effect.becomesAura;
|
||||||
|
this.tapped = effect.tapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnimateDeadPutOntoBattlefieldEffect copy() {
|
||||||
|
return new AnimateDeadPutOntoBattlefieldEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Player player = game.getPlayer(source.getControllerId());
|
||||||
|
Permanent enchantment = source.getSourcePermanentIfItStillExists(game);
|
||||||
|
if (player == null || enchantment == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Card card = game.getCard(becomesAura ? getTargetPointer().getFirst(game, source) : enchantment.getAttachedTo());
|
||||||
|
// If this ability was copied or made to trigger an additional time, the card might no longer be in the graveyard
|
||||||
|
// See https://github.com/magefree/mage/issues/8253
|
||||||
|
if (card == null || game.getState().getZone(card.getId()) != Zone.GRAVEYARD) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Put card onto the battlefield under your control...
|
||||||
|
player.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, false, null);
|
||||||
|
game.getState().processAction(game);
|
||||||
|
|
||||||
|
Permanent creature = game.getPermanent(CardUtil.getDefaultCardSideForBattlefield(game, card).getId());
|
||||||
|
if (creature == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// ...and attach {this} to it
|
||||||
|
creature.addAttachment(enchantment.getId(), source, game);
|
||||||
|
// When {this} leaves the battlefield, that creature's controller sacrifices it
|
||||||
|
game.addDelayedTriggeredAbility(new AnimateDeadDelayedTriggeredAbility(new FixedTarget(creature, game)), source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimateDeadDelayedTriggeredAbility extends DelayedTriggeredAbility {
|
||||||
|
|
||||||
|
public AnimateDeadDelayedTriggeredAbility(FixedTarget fixedTarget) {
|
||||||
|
super(new SacrificeTargetEffect("that creature's controller sacrifices it"));
|
||||||
|
setTriggerPhrase("When {this} leaves the battlefield, ");
|
||||||
|
getEffects().setTargetPointer(fixedTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnimateDeadDelayedTriggeredAbility(final AnimateDeadDelayedTriggeredAbility ability) {
|
||||||
|
super(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnimateDeadDelayedTriggeredAbility copy() {
|
||||||
|
return new AnimateDeadDelayedTriggeredAbility(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkEventType(GameEvent event, Game game) {
|
||||||
|
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkTrigger(GameEvent event, Game game) {
|
||||||
|
return getSourceId().equals(event.getTargetId()) && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimateDeadPredicate implements Predicate<Permanent> {
|
||||||
|
|
||||||
|
private final UUID sourceId;
|
||||||
|
|
||||||
|
public AnimateDeadPredicate(UUID sourceId) {
|
||||||
|
this.sourceId = sourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Permanent input, Game game) {
|
||||||
|
AnimateDeadWatcher watcher = game.getState().getWatcher(AnimateDeadWatcher.class, sourceId);
|
||||||
|
return watcher != null && watcher.check(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AnimateDeadPredicate(" + sourceId + ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimateDeadWatcher extends Watcher {
|
||||||
|
|
||||||
|
private final Set<UUID> putBySource = new HashSet<>();
|
||||||
|
|
||||||
|
public AnimateDeadWatcher() {
|
||||||
|
super(WatcherScope.CARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void watch(GameEvent event, Game game) {
|
||||||
|
switch (event.getType()) {
|
||||||
|
case ZONE_CHANGE:
|
||||||
|
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||||
|
if (sourceId.equals(zEvent.getTargetId())) {
|
||||||
|
// clear all data when source enchantment changes zones
|
||||||
|
putBySource.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (zEvent.getToZone() != Zone.BATTLEFIELD) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sourceId.equals(zEvent.getSourceId())) {
|
||||||
|
putBySource.add(event.getTargetId());
|
||||||
|
} else {
|
||||||
|
putBySource.remove(event.getTargetId());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case BEGINNING_PHASE_PRE:
|
||||||
|
if (game.getTurnNum() == 1) {
|
||||||
|
putBySource.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean check(Permanent permanent) {
|
||||||
|
return putBySource.contains(permanent.getId());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue