Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rework some effects that put cards from graveyard to battlefield with changed characteristics #11984

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
68 changes: 12 additions & 56 deletions Mage.Sets/src/mage/cards/c/ChainerDementiaMaster.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@

package mage.cards.c;

import java.util.UUID;
import mage.MageInt;
import mage.ObjectColor;
import mage.abilities.Ability;
Expand All @@ -10,38 +8,29 @@
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ExileAllEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.common.PutOntoBattlefieldTargetEffect;
import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect;
import mage.abilities.effects.common.continuous.BecomesColorTargetEffect;
import mage.abilities.effects.common.continuous.BecomesCreatureTypeTargetEffect;
import mage.abilities.effects.common.continuous.BoostAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreatureCard;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCardInGraveyard;
import mage.target.targetpointer.FixedTarget;

import java.util.UUID;

/**
*
* @author emerald000
*/
public final class ChainerDementiaMaster extends CardImpl {

private static final FilterCreaturePermanent filterCreature = new FilterCreaturePermanent("Nightmare creatures");
private static final FilterPermanent filterPermanent = new FilterPermanent("Nightmares");
static {
filterCreature.add(SubType.NIGHTMARE.getPredicate());
filterPermanent.add(SubType.NIGHTMARE.getPredicate());
}
private static final FilterCreaturePermanent filterCreature = new FilterCreaturePermanent(SubType.NIGHTMARE, "All Nightmares");
private static final FilterPermanent filterPermanent = new FilterPermanent(SubType.NIGHTMARE, "Nightmares");

public ChainerDementiaMaster(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}");
Expand All @@ -52,10 +41,14 @@ public ChainerDementiaMaster(UUID ownerId, CardSetInfo setInfo) {
this.toughness = new MageInt(3);

// Nightmare creatures get +1/+1.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filterCreature, false)));
this.addAbility(new SimpleStaticAbility(new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filterCreature, false)));

// {B}{B}{B}, Pay 3 life: Put target creature card from a graveyard onto the battlefield under your control. That creature is black and is a Nightmare in addition to its other creature types.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ChainerDementiaMasterEffect(), new ManaCostsImpl<>("{B}{B}{B}"));
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutOntoBattlefieldTargetEffect(false)
.withContinuousEffects("That creature is black and is a Nightmare in addition to its other creature types",
new BecomesColorTargetEffect(ObjectColor.BLACK, Duration.Custom),
new AddCardSubTypeTargetEffect(SubType.NIGHTMARE, Duration.Custom)),
new ManaCostsImpl<>("{B}{B}{B}"));
ability.addCost(new PayLifeCost(3));
ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE_A_GRAVEYARD));
this.addAbility(ability);
Expand All @@ -73,40 +66,3 @@ public ChainerDementiaMaster copy() {
return new ChainerDementiaMaster(this);
}
}

class ChainerDementiaMasterEffect extends OneShotEffect {

ChainerDementiaMasterEffect() {
super(Outcome.PutCreatureInPlay);
this.staticText = "Put target creature card from a graveyard onto the battlefield under your control. That creature is black and is a Nightmare in addition to its other creature types";
}

private ChainerDementiaMasterEffect(final ChainerDementiaMasterEffect effect) {
super(effect);
}

@Override
public ChainerDementiaMasterEffect copy() {
return new ChainerDementiaMasterEffect(this);
}

@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
UUID cardId = this.getTargetPointer().getFirst(game, source);
new ReturnFromGraveyardToBattlefieldTargetEffect().apply(game, source);
Permanent permanent = game.getPermanent(cardId);
if (permanent != null) {
ContinuousEffectImpl effect = new BecomesColorTargetEffect(ObjectColor.BLACK, Duration.WhileOnBattlefield);
effect.setTargetPointer(new FixedTarget(permanent, game));
game.addEffect(effect, source);
effect = new BecomesCreatureTypeTargetEffect(Duration.WhileOnBattlefield, SubType.NIGHTMARE, false);
effect.setTargetPointer(new FixedTarget(permanent, game));
game.addEffect(effect, source);
}
return true;
}
return false;
}
}
15 changes: 9 additions & 6 deletions Mage.Sets/src/mage/cards/g/GraveBetrayal.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

package mage.cards.g;

import java.util.UUID;

import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
Expand All @@ -10,7 +11,8 @@
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.continuous.AddCreatureTypeAdditionEffect;
import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect;
import mage.abilities.effects.common.continuous.BecomesColorTargetEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
Expand Down Expand Up @@ -153,10 +155,11 @@ public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget();
if (creature != null) {
creature.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game, event.getAppliedEffects());
ContinuousEffect effect = new AddCreatureTypeAdditionEffect(SubType.ZOMBIE, true);
effect.setTargetPointer(new FixedTarget(creature.getId(), creature.getZoneChangeCounter(game) + 1));
game.addEffect(effect, source);
//discard(); why?
FixedTarget fixedTarget = new FixedTarget(creature.getId(), creature.getZoneChangeCounter(game) + 1);
game.addEffect(new BecomesColorTargetEffect(ObjectColor.BLACK, true, Duration.Custom)
.setTargetPointer(fixedTarget.copy()), source);
game.addEffect(new AddCardSubTypeTargetEffect(SubType.ZOMBIE, Duration.Custom)
.setTargetPointer(fixedTarget.copy()), source);
}
return false;
}
Expand Down
13 changes: 7 additions & 6 deletions Mage.Sets/src/mage/cards/p/PortalToPhyrexia.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.common.PutOntoBattlefieldTargetEffect;
import mage.abilities.effects.common.SacrificeOpponentsEffect;
import mage.abilities.effects.common.continuous.AddCreatureTypeAdditionEffect;
import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreatureCard;
import mage.target.common.TargetCardInGraveyard;

import java.util.UUID;
Expand All @@ -33,9 +32,11 @@ public PortalToPhyrexia(UUID ownerId, CardSetInfo setInfo) {

// At the beginning of your upkeep, put target creature card from a graveyard onto the battlefield under your control. It's a Phyrexian in addition to its other types.
Ability ability = new BeginningOfUpkeepTriggeredAbility(
new ReturnFromGraveyardToBattlefieldTargetEffect(), TargetController.YOU, false
new PutOntoBattlefieldTargetEffect(false).withContinuousEffects(
"It's a Phyrexian in addition to its other types",
new AddCardSubTypeTargetEffect(SubType.PHYREXIAN, Duration.Custom)
), TargetController.YOU, false
);
ability.addEffect(new AddCreatureTypeAdditionEffect(SubType.PHYREXIAN, false));
ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE_A_GRAVEYARD));
this.addAbility(ability);
}
Expand Down
22 changes: 12 additions & 10 deletions Mage.Sets/src/mage/cards/r/RiseFromTheGrave.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@

package mage.cards.r;

import java.util.UUID;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.common.continuous.AddCreatureTypeAdditionEffect;
import mage.ObjectColor;
import mage.abilities.effects.common.PutOntoBattlefieldTargetEffect;
import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect;
import mage.abilities.effects.common.continuous.BecomesColorTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreatureCard;
import mage.target.common.TargetCardInGraveyard;

import java.util.UUID;

/**
*
* @author BetaSteward_at_googlemail.com
* @author xenohedron
*/
public final class RiseFromTheGrave extends CardImpl {

public RiseFromTheGrave(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{B}");


// Put target creature card from a graveyard onto the battlefield under your control. That creature is a black Zombie in addition to its other colors and types.
this.getSpellAbility().addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE_A_GRAVEYARD));
this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect());
this.getSpellAbility().addEffect(new AddCreatureTypeAdditionEffect(SubType.ZOMBIE, true));
this.getSpellAbility().addEffect(new PutOntoBattlefieldTargetEffect(false)
.withContinuousEffects("That creature is a black Zombie in addition to its other colors and types",
new BecomesColorTargetEffect(ObjectColor.BLACK, true, Duration.Custom),
new AddCardSubTypeTargetEffect(SubType.ZOMBIE, Duration.Custom)));
}

private RiseFromTheGrave(final RiseFromTheGrave card) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package org.mage.test.cards.single.m10;

import mage.constants.PhaseStep;
import mage.constants.SubType;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;

/**
* @author xenohedron
*/
public class RiseFromTheGraveTest extends CardTestPlayerBase {

private static final String rise = "Rise from the Grave";
// Put target creature card from a graveyard onto the battlefield under your control.
// That creature is a black Zombie in addition to its other colors and types.

private static final String looter = "Merfolk Looter";
private static final String unsummon = "Unsummon";
private static final String unconventionalTactics = "Unconventional Tactics";
// Whenever a Zombie enters the battlefield under your control, you may pay {W}. If you do, return Unconventional Tactics from your graveyard to your hand.
private static final String direUndercurrents = "Dire Undercurrents";
// Whenever a blue creature enters the battlefield under your control, you may have target player draw a card.
// Whenever a black creature enters the battlefield under your control, you may have target player discard a card.
private static final String kraken = "Kraken Hatchling";

/*
* Related ruling for Chainer, Dementia Master
* As it enters the battlefield, it is already a black Nightmare (and perhaps some other creature types);
* it doesn't enter and then become a black Nightmare. (2022-12-08)
*/

@Test
public void testGainedCharacteristicsRespectZCC() {
addCard(Zone.GRAVEYARD, playerB, looter);
addCard(Zone.HAND, playerA, rise);
addCard(Zone.HAND, playerB, unsummon);
addCard(Zone.HAND, playerA, kraken);
addCard(Zone.BATTLEFIELD, playerA, direUndercurrents);
addCard(Zone.GRAVEYARD, playerA, unconventionalTactics);
addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 6);
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);

castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rise, looter);
setChoice(playerA, "Whenever a blue"); // order triggers
addTarget(playerA, playerB); // to draw
setChoice(playerA, "Whenever a black"); // order triggers
addTarget(playerA, playerA); // to discard
setChoice(playerA, true); // pay {W}
setChoice(playerA, true); // to discard
setChoice(playerA, kraken); // discarded
setChoice(playerA, false); // no draw

checkPT("looter", 1, PhaseStep.BEGIN_COMBAT, playerA, looter, 1, 1);
checkColor("looter", 1, PhaseStep.BEGIN_COMBAT, playerA, looter, "U", true);
checkColor("looter", 1, PhaseStep.BEGIN_COMBAT, playerA, looter, "B", true);
checkSubType("looter", 1, PhaseStep.BEGIN_COMBAT, playerA, looter, SubType.MERFOLK, true);
checkSubType("looter", 1, PhaseStep.BEGIN_COMBAT, playerA, looter, SubType.ZOMBIE, true);

castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, unsummon, looter);

castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, looter);

checkPT("looter2", 2, PhaseStep.BEGIN_COMBAT, playerB, looter, 1, 1);
checkColor("looter2", 2, PhaseStep.BEGIN_COMBAT, playerB, looter, "U", true);
checkColor("looter2", 2, PhaseStep.BEGIN_COMBAT, playerB, looter, "B", false);
checkSubType("looter2", 2, PhaseStep.BEGIN_COMBAT, playerB, looter, SubType.MERFOLK, true);
checkSubType("looter2", 2, PhaseStep.BEGIN_COMBAT, playerB, looter, SubType.ZOMBIE, false);

setStrictChooseMode(true);
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
execute();

assertGraveyardCount(playerA, rise, 1);
assertGraveyardCount(playerB, unsummon, 1);
assertHandCount(playerA, unconventionalTactics, 1);
assertGraveyardCount(playerA, kraken, 1);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package mage.abilities.effects.common;

import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.replacement.EntersWithContinuousEffectsAppliedEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;

import java.util.*;
import java.util.stream.Collectors;

/**
* @author xenohedron
*/
public class PutOntoBattlefieldTargetEffect extends OneShotEffect {

private final boolean tapped;
private final List<ContinuousEffect> effects = new ArrayList<>();
private String description;

/**
* Put [target card in a graveyard] onto the battlefield under your control
*/
public PutOntoBattlefieldTargetEffect(boolean tapped) {
super(Outcome.PutCreatureInPlay);
this.tapped = tapped;
}

protected PutOntoBattlefieldTargetEffect(final PutOntoBattlefieldTargetEffect effect) {
super(effect);
this.tapped = effect.tapped;
this.effects.addAll(effect.effects);
this.description = effect.description;
}

@Override
public PutOntoBattlefieldTargetEffect copy() {
return new PutOntoBattlefieldTargetEffect(this);
}

/**
* These effects are applied as the permanent enters the battlefield. Use Duration.Custom
*/
public PutOntoBattlefieldTargetEffect withContinuousEffects(String description, ContinuousEffect... effects) {
this.description = description;
this.effects.addAll(Arrays.asList(effects));
return this;
}

@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
Set<Card> cardsToMove = getTargetPointer()
.getTargets(game, source)
.stream()
.map(game::getCard)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (cardsToMove.isEmpty()) {
return false;
}
if (!effects.isEmpty()) {
game.addEffect(new EntersWithContinuousEffectsAppliedEffect(effects)
.setTargetPointer(this.getTargetPointer().copy()), source);
}
controller.moveCards(cardsToMove, Zone.BATTLEFIELD, source, game, tapped, false, false, null);
return true;
}

@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
return "put " + getTargetPointer().describeTargets(mode.getTargets(), "that card")
+ " onto the battlefield" + (tapped ? " tapped " : " ") + "under your control"
+ (description == null || description.isEmpty() ? "" : ". " + description);
}
}