mirror of
https://github.com/correl/mage.git
synced 2025-01-11 19:13:02 +00:00
* Modal Double Faces cards - fixed that a second side can't be played from non hand zones in some cases (#7200);
Play lands from graveyard effect - reworked as AsThoughEffect;
This commit is contained in:
parent
24e2728fca
commit
c1e7a08c68
9 changed files with 210 additions and 62 deletions
|
@ -4,7 +4,7 @@ import mage.MageInt;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.ruleModifying.PlayLandsFromGraveyardEffect;
|
||||
import mage.abilities.effects.common.ruleModifying.PlayLandsFromGraveyardControllerEffect;
|
||||
import mage.abilities.keyword.ReachAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
@ -35,7 +35,7 @@ public final class AncientGreenwarden extends CardImpl {
|
|||
this.addAbility(ReachAbility.getInstance());
|
||||
|
||||
// You may play lands from your graveyard.
|
||||
this.addAbility(new SimpleStaticAbility(new PlayLandsFromGraveyardEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new PlayLandsFromGraveyardControllerEffect()));
|
||||
|
||||
// If a land entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
|
||||
this.addAbility(new SimpleStaticAbility(new AncientGreenwardenEffect()));
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
|
||||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.ruleModifying.PlayLandsFromGraveyardEffect;
|
||||
import mage.abilities.effects.common.ruleModifying.PlayLandsFromGraveyardControllerEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Plopman
|
||||
*/
|
||||
public final class CrucibleOfWorlds extends CardImpl {
|
||||
|
||||
public CrucibleOfWorlds(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
|
||||
|
||||
// You may play land cards from your graveyard.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayLandsFromGraveyardEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayLandsFromGraveyardControllerEffect()));
|
||||
}
|
||||
|
||||
public CrucibleOfWorlds(final CrucibleOfWorlds card) {
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.UUID;
|
|||
* @author arcox
|
||||
*/
|
||||
public final class RadhaHeartOfKeld extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterLandCard("play land cards");
|
||||
|
||||
public RadhaHeartOfKeld(UUID ownerId, CardSetInfo setInfo) {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
|
||||
package mage.cards.r;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.ruleModifying.PlayLandsFromGraveyardEffect;
|
||||
import mage.abilities.effects.common.ruleModifying.PlayLandsFromGraveyardControllerEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author fireshoes
|
||||
*/
|
||||
|
@ -25,7 +25,7 @@ public final class RamunapExcavator extends CardImpl {
|
|||
this.toughness = new MageInt(3);
|
||||
|
||||
// You may play land cards from your graveyard.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayLandsFromGraveyardEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new PlayLandsFromGraveyardControllerEffect()));
|
||||
}
|
||||
|
||||
public RamunapExcavator(final RamunapExcavator card) {
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package org.mage.test.cards.asthough;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class PlayLandsFromGraveyardTest extends CardTestPlayerBaseWithAIHelps {
|
||||
|
||||
@Test
|
||||
public void test_CrucibleOfWorlds() {
|
||||
removeAllCardsFromHand(playerA);
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
|
||||
// You may play lands from your graveyard.
|
||||
addCard(Zone.HAND, playerA, "Crucible of Worlds"); // {3}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
//
|
||||
addCard(Zone.GRAVEYARD, playerA, "Island", 1);
|
||||
//
|
||||
addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
|
||||
checkGraveyardCount("graveyard before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
|
||||
checkGraveyardCount("graveyard before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Island", 1);
|
||||
checkPlayableAbility("can't play as creature", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Balduvian Bears", false);
|
||||
checkPlayableAbility("can't play as land", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Island", false);
|
||||
|
||||
// play artifact and apply play from graveyard effect
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crucible of Worlds");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkPlayableAbility("can't play as creature", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Balduvian Bears", false);
|
||||
checkPlayableAbility("can play as land", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Island", true);
|
||||
|
||||
// play land
|
||||
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Island");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Island", 1);
|
||||
}
|
||||
}
|
|
@ -118,8 +118,8 @@ public class ModalDoubleFacesCardsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerA, "Bolas's Citadel");
|
||||
|
||||
checkLibraryCount("library before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1);
|
||||
checkPlayableAbility("can play as land", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", true);
|
||||
checkPlayableAbility("can play as creature", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true);
|
||||
checkPlayableAbility("can play as creature", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", true);
|
||||
checkPlayableAbility("can play as land", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true);
|
||||
|
||||
// play as creature
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior");
|
||||
|
@ -136,6 +136,64 @@ public class ModalDoubleFacesCardsTest extends CardTestPlayerBase {
|
|||
assertLife(playerA, 20 - 6); // creature life pay instead mana
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PlayFromNonHand_SecondSideAsLand_ByRadhaHeartOfKeld() {
|
||||
removeAllCardsFromHand(playerA);
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
|
||||
// Akoum Warrior {5}{R} - creature
|
||||
// Akoum Teeth - land
|
||||
addCard(Zone.LIBRARY, playerA, "Akoum Warrior");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
|
||||
//
|
||||
// You may look at the top card of your library any time, and you may play lands from the top of your library.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Radha, Heart of Keld");
|
||||
|
||||
checkLibraryCount("library before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1);
|
||||
checkPlayableAbility("can't play as creature", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", false);
|
||||
checkPlayableAbility("can play as land", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true);
|
||||
|
||||
// play as land
|
||||
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Teeth");
|
||||
checkLibraryCount("library after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0);
|
||||
checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0);
|
||||
checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Teeth", 1);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PlayFromNonHand_SecondSideAsLand_CrucibleOfWorlds() {
|
||||
removeAllCardsFromHand(playerA);
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
|
||||
// Akoum Warrior {5}{R} - creature
|
||||
// Akoum Teeth - land
|
||||
addCard(Zone.GRAVEYARD, playerA, "Akoum Warrior");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
|
||||
//
|
||||
// You may play lands from your graveyard.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Crucible of Worlds");
|
||||
|
||||
checkGraveyardCount("graveyard before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 1);
|
||||
checkPlayableAbility("can't play as creature", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Akoum Warrior", false);
|
||||
checkPlayableAbility("can play as land", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Akoum Teeth", true);
|
||||
|
||||
// play as land
|
||||
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Teeth");
|
||||
checkLibraryCount("library after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0);
|
||||
checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Warrior", 0);
|
||||
checkPermanentCount("after cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akoum Teeth", 1);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PlayFromNonHand_GraveyardByYawgmothsAgenda() {
|
||||
removeAllCardsFromHand(playerA);
|
||||
|
|
|
@ -515,7 +515,15 @@ public class ContinuousEffects implements Serializable {
|
|||
UUID idToCheck;
|
||||
if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof ModalDoubleFacesCardHalf) {
|
||||
} else if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof ModalDoubleFacesCardHalf
|
||||
&& !type.needPlayCardAbility()) {
|
||||
// each mdf side uses own characteristics to check for playing, all other cases must use main card
|
||||
// rules:
|
||||
// "If an effect allows you to play a land or cast a spell from among a group of cards,
|
||||
// you may play or cast a modal double-faced card with any face that fits the criteria
|
||||
// of that effect. For example, if Sejiri Shelter / Sejiri Glacier is in your graveyard
|
||||
// and an effect allows you to play lands from your graveyard, you could play Sejiri Glacier.
|
||||
// That effect doesn't allow you to cast Sejiri Shelter."
|
||||
idToCheck = ((ModalDoubleFacesCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof AdventureCardSpell
|
||||
&& !type.needPlayCardAbility()) {
|
||||
|
@ -525,7 +533,9 @@ public class ContinuousEffects implements Serializable {
|
|||
Card card = game.getCard(objectId);
|
||||
if (card instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) card).getParentCard().getId();
|
||||
} else if (card instanceof ModalDoubleFacesCardHalf) {
|
||||
} else if (card instanceof ModalDoubleFacesCardHalf
|
||||
&& !type.needPlayCardAbility()) {
|
||||
// each mdf side uses own characteristics to check for playing, all other cases must use main card
|
||||
idToCheck = ((ModalDoubleFacesCardHalf) card).getParentCard().getId();
|
||||
} else if (card instanceof AdventureCardSpell
|
||||
&& !type.needPlayCardAbility()) {
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package mage.abilities.effects.common.ruleModifying;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterLandCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
|
||||
|
||||
private final FilterCard filter;
|
||||
|
||||
public PlayLandsFromGraveyardControllerEffect() {
|
||||
this(new FilterLandCard("lands"));
|
||||
}
|
||||
|
||||
public PlayLandsFromGraveyardControllerEffect(FilterCard filter) {
|
||||
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
|
||||
this.filter = filter;
|
||||
staticText = "You may play " + filter.getMessage() + " from your graveyard";
|
||||
}
|
||||
|
||||
public PlayLandsFromGraveyardControllerEffect(final PlayLandsFromGraveyardControllerEffect effect) {
|
||||
super(effect);
|
||||
this.filter = effect.filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PlayLandsFromGraveyardControllerEffect copy() {
|
||||
return new PlayLandsFromGraveyardControllerEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
return applies(objectId, null, source, game, affectedControllerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
Card cardToCheck = game.getCard(objectId);
|
||||
objectId = CardUtil.getMainCardId(game, objectId); // for split cards
|
||||
if (cardToCheck == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player player = game.getPlayer(cardToCheck.getOwnerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UUID needCardId = objectId;
|
||||
if (player.getGraveyard().getCards(game).stream().noneMatch(c -> c.getId().equals(needCardId))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return playerId.equals(source.getControllerId())
|
||||
&& cardToCheck.isOwnedBy(source.getControllerId())
|
||||
&& (!cardToCheck.getManaCost().isEmpty() || cardToCheck.isLand())
|
||||
&& filter.match(cardToCheck, game);
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package mage.abilities.effects.common.ruleModifying;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.PlayLandFromGraveyardAbility;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
public class PlayLandsFromGraveyardEffect extends ContinuousEffectImpl {
|
||||
|
||||
public PlayLandsFromGraveyardEffect() {
|
||||
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
|
||||
this.staticText = "You may play lands from your graveyard";
|
||||
}
|
||||
|
||||
public PlayLandsFromGraveyardEffect(final PlayLandsFromGraveyardEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayLandsFromGraveyardEffect copy() {
|
||||
return new PlayLandsFromGraveyardEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
for (Card card : player.getGraveyard().getCards(game)) {
|
||||
if (card != null && card.isLand()) {
|
||||
PlayLandFromGraveyardAbility ability = new PlayLandFromGraveyardAbility(card.getName());
|
||||
ability.setSourceId(card.getId());
|
||||
ability.setControllerId(card.getOwnerId());
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue