mirror of
https://github.com/correl/mage.git
synced 2024-11-15 03:00:16 +00:00
Fix behavior for Garruk's Horde, Melek, W6.
This commit is contained in:
parent
19ca9f555c
commit
cd890d329a
5 changed files with 192 additions and 35 deletions
|
@ -41,6 +41,7 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
assertActionsCount(playerA, 1);
|
||||||
assertHandCount(playerA, 0);
|
assertHandCount(playerA, 0);
|
||||||
assertPermanentCount(playerA, "Food", 1);
|
assertPermanentCount(playerA, "Food", 1);
|
||||||
assertExileCount(playerA, "Curious Pair", 1);
|
assertExileCount(playerA, "Curious Pair", 1);
|
||||||
|
@ -335,7 +336,7 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAdventurePermanentText() {
|
public void testRimrockKnightPermanentText() {
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||||
addCard(Zone.HAND, playerA, "Rimrock Knight");
|
addCard(Zone.HAND, playerA, "Rimrock Knight");
|
||||||
|
@ -354,4 +355,144 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
||||||
Permanent rimrock = getPermanent("Rimrock Knight");
|
Permanent rimrock = getPermanent("Rimrock Knight");
|
||||||
Assert.assertEquals(rimrock.getRules(currentGame).get(0), "{this} can't block.");
|
Assert.assertEquals(rimrock.getRules(currentGame).get(0), "{this} can't block.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tests for Rule 601.3e:
|
||||||
|
* 601.3e If a rule or effect states that only an alternative set of characteristics or a subset of characteristics
|
||||||
|
* are considered to determine if a card or copy of a card is legal to cast, those alternative characteristics
|
||||||
|
* replace the object’s characteristics prior to determining whether the player may begin to cast it.
|
||||||
|
* Example: Garruk’s Horde says, in part, “You may cast the top card of your library if it’s a creature card.” If
|
||||||
|
* you control Garruk’s Horde and the top card of your library is a noncreature card with morph, you may cast it
|
||||||
|
* using its morph ability.
|
||||||
|
* Example: Melek, Izzet Paragon says, in part, “You may cast the top card of your library if it’s an instant or
|
||||||
|
* sorcery card.” If you control Melek, Izzet Paragon and the top card of your library is Giant Killer, an
|
||||||
|
* adventurer creature card whose Adventure is an instant named Chop Down, you may cast Chop Down but not Giant
|
||||||
|
* Killer. If instead you control Garruk’s Horde and the top card of your library is Giant Killer, you may cast
|
||||||
|
* Giant Killer but not Chop Down.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCastTreatsToShareWithMelek() {
|
||||||
|
/*
|
||||||
|
* Melek, Izzet Paragon {4}{U}{R}
|
||||||
|
* Legendary Creature — Weird Wizard
|
||||||
|
* Play with the top card of your library revealed.
|
||||||
|
* You may cast the top card of your library if it's an instant or sorcery card.
|
||||||
|
* Whenever you cast an instant or sorcery spell from your library, copy it. You may choose new targets for the copy.
|
||||||
|
* 2/4
|
||||||
|
*/
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Melek, Izzet Paragon");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest");
|
||||||
|
removeAllCardsFromLibrary(playerA);
|
||||||
|
addCard(Zone.LIBRARY, playerA, "Curious Pair");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
assertHandCount(playerA, 0);
|
||||||
|
assertPermanentCount(playerA, 4);
|
||||||
|
assertPermanentCount(playerA, "Food", 2);
|
||||||
|
assertPermanentCount(playerA, "Curious Pair", 0);
|
||||||
|
assertExileCount(playerA, "Curious Pair", 1);
|
||||||
|
assertGraveyardCount(playerA, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCantCastCuriousPairWithMelek() {
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Melek, Izzet Paragon");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
|
||||||
|
removeAllCardsFromLibrary(playerA);
|
||||||
|
addCard(Zone.LIBRARY, playerA, "Curious Pair");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertActionsCount(playerA, 1);
|
||||||
|
assertPermanentCount(playerA, "Curious Pair", 0);
|
||||||
|
assertLibraryCount(playerA, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCastCuriousPairWithGarruksHorde() {
|
||||||
|
/*
|
||||||
|
* Garruk's Horde {5}{G}{G}
|
||||||
|
* Creature — Beast
|
||||||
|
* Trample
|
||||||
|
* Play with the top card of your library revealed.
|
||||||
|
* You may cast the top card of your library if it's a creature card.
|
||||||
|
* 7/7
|
||||||
|
*/
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Garruk's Horde");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
|
||||||
|
removeAllCardsFromLibrary(playerA);
|
||||||
|
addCard(Zone.LIBRARY, playerA, "Curious Pair");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
assertHandCount(playerA, 0);
|
||||||
|
assertPermanentCount(playerA, "Food", 0);
|
||||||
|
assertPermanentCount(playerA, "Curious Pair", 1);
|
||||||
|
assertExileCount(playerA, 0);
|
||||||
|
assertGraveyardCount(playerA, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCantCastTreatsToShareWithGarruksHorde() {
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Garruk's Horde");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest");
|
||||||
|
removeAllCardsFromLibrary(playerA);
|
||||||
|
addCard(Zone.LIBRARY, playerA, "Curious Pair");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertActionsCount(playerA, 1);
|
||||||
|
assertPermanentCount(playerA, "Food", 0);
|
||||||
|
assertLibraryCount(playerA, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCastTreatsToShareWithWrennAndSixEmblem() {
|
||||||
|
/*
|
||||||
|
* Melek, Izzet Paragon {4}{U}{R}
|
||||||
|
* Legendary Creature — Weird Wizard
|
||||||
|
* Play with the top card of your library revealed.
|
||||||
|
* You may cast the top card of your library if it's an instant or sorcery card.
|
||||||
|
* Whenever you cast an instant or sorcery spell from your library, copy it. You may choose new targets for the copy.
|
||||||
|
* 2/4
|
||||||
|
*/
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Melek, Izzet Paragon");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest");
|
||||||
|
removeAllCardsFromLibrary(playerA);
|
||||||
|
addCard(Zone.LIBRARY, playerA, "Curious Pair");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
assertHandCount(playerA, 0);
|
||||||
|
assertPermanentCount(playerA, 4);
|
||||||
|
assertPermanentCount(playerA, "Food", 2);
|
||||||
|
assertPermanentCount(playerA, "Curious Pair", 0);
|
||||||
|
assertExileCount(playerA, "Curious Pair", 1);
|
||||||
|
assertGraveyardCount(playerA, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
package mage.abilities.effects.common.continuous;
|
package mage.abilities.effects.common.continuous;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import mage.MageObject;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.SpellAbility;
|
||||||
import mage.abilities.effects.AsThoughEffectImpl;
|
import mage.abilities.effects.AsThoughEffectImpl;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.constants.AsThoughEffectType;
|
import mage.constants.AsThoughEffectType;
|
||||||
|
@ -47,12 +50,27 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
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 cardOnTop = game.getCard(objectId);
|
Card cardOnTop = game.getCard(objectId);
|
||||||
|
Card cardToCheckProperties = cardOnTop;
|
||||||
|
|
||||||
|
// Check each ability individually, as e.g. Adventures and associated creatures may get different results from the filter.
|
||||||
|
if (affectedAbility != null) {
|
||||||
|
MageObject sourceObject = affectedAbility.getSourceObject(game);
|
||||||
|
if (sourceObject != null && sourceObject instanceof Card) {
|
||||||
|
cardToCheckProperties = (Card) sourceObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cardOnTop != null
|
if (cardOnTop != null
|
||||||
&& affectedControllerId.equals(source.getControllerId())
|
&& playerId.equals(source.getControllerId())
|
||||||
&& cardOnTop.isOwnedBy(source.getControllerId())
|
&& cardOnTop.isOwnedBy(source.getControllerId())
|
||||||
&& (!cardOnTop.getManaCost().isEmpty() || cardOnTop.isLand())
|
&& (!cardToCheckProperties.getManaCost().isEmpty() || cardToCheckProperties.isLand())
|
||||||
&& filter.match(cardOnTop, game)) {
|
&& filter.match(cardToCheckProperties, game)) {
|
||||||
Player player = game.getPlayer(cardOnTop.getOwnerId());
|
Player player = game.getPlayer(cardOnTop.getOwnerId());
|
||||||
if (player != null && cardOnTop.equals(player.getLibrary().getFromTop(game))) {
|
if (player != null && cardOnTop.equals(player.getLibrary().getFromTop(game))) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -21,7 +21,9 @@ public abstract class AdventureCard extends CardImpl {
|
||||||
public AdventureCard(UUID ownerId, CardSetInfo setInfo, CardType[] types, CardType[] typesSpell, String costs, String adventureName, String costsSpell) {
|
public AdventureCard(UUID ownerId, CardSetInfo setInfo, CardType[] types, CardType[] typesSpell, String costs, String adventureName, String costsSpell) {
|
||||||
super(ownerId, setInfo, types, costs);
|
super(ownerId, setInfo, types, costs);
|
||||||
spellCard = new AdventureCardSpellImpl(ownerId, setInfo, adventureName, typesSpell, costsSpell, this);
|
spellCard = new AdventureCardSpellImpl(ownerId, setInfo, adventureName, typesSpell, costsSpell, this);
|
||||||
this.addAbility(spellCard.getSpellAbility());
|
Ability adventureAbility = spellCard.getSpellAbility();
|
||||||
|
this.addAbility(adventureAbility);
|
||||||
|
adventureAbility.setSourceId(spellCard.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public AdventureCard(AdventureCard card) {
|
public AdventureCard(AdventureCard card) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.effects.ContinuousEffectImpl;
|
import mage.abilities.effects.ContinuousEffectImpl;
|
||||||
import mage.abilities.keyword.RetraceAbility;
|
import mage.abilities.keyword.RetraceAbility;
|
||||||
|
import mage.cards.AdventureCard;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.constants.*;
|
import mage.constants.*;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
@ -45,6 +46,10 @@ class WrennAndSixEmblemEffect extends ContinuousEffectImpl {
|
||||||
if (card == null || !card.isInstantOrSorcery()) {
|
if (card == null || !card.isInstantOrSorcery()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (card instanceof AdventureCard) {
|
||||||
|
// Adventure cards are castable per https://twitter.com/elishffrn/status/1179047911729946624
|
||||||
|
card = ((AdventureCard) card).getSpellCard();
|
||||||
|
}
|
||||||
Ability ability = new RetraceAbility(card);
|
Ability ability = new RetraceAbility(card);
|
||||||
ability.setSourceId(cardId);
|
ability.setSourceId(cardId);
|
||||||
ability.setControllerId(card.getOwnerId());
|
ability.setControllerId(card.getOwnerId());
|
||||||
|
|
|
@ -3218,6 +3218,24 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Ability> cardPlayableAbilities(Game game, Card card) {
|
||||||
|
List<Ability> playable = new ArrayList();
|
||||||
|
if (card != null) {
|
||||||
|
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
|
||||||
|
if (ability instanceof SpellAbility
|
||||||
|
&& null != game.getContinuousEffects().asThough(card.getId(),
|
||||||
|
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, ability, getId(), game)) {
|
||||||
|
playable.add(ability);
|
||||||
|
} else if (ability instanceof PlayLandAbility
|
||||||
|
&& null != game.getContinuousEffects().asThough(card.getId(),
|
||||||
|
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), getId(), game)) {
|
||||||
|
playable.add(ability);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return playable;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Ability> getPlayable(Game game, boolean hidden) {
|
public List<Ability> getPlayable(Game game, boolean hidden) {
|
||||||
return getPlayable(game, hidden, Zone.ALL, true);
|
return getPlayable(game, hidden, Zone.ALL, true);
|
||||||
|
@ -3294,20 +3312,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (fromAll || fromZone == Zone.EXILED) {
|
if (fromAll || fromZone == Zone.EXILED) {
|
||||||
for (ExileZone exile : game.getExile().getExileZones()) {
|
for (ExileZone exile : game.getExile().getExileZones()) {
|
||||||
for (Card card : exile.getCards(game)) {
|
for (Card card : exile.getCards(game)) {
|
||||||
if (null != game.getContinuousEffects().asThough(card.getId(),
|
playable.addAll(cardPlayableAbilities(game, card));
|
||||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game)) {
|
|
||||||
for (Ability ability : card.getAbilities()) {
|
|
||||||
if (ability.getZone().match(Zone.HAND)) {
|
|
||||||
ability.setControllerId(this.getId()); // controller must be set for case owner != caster
|
|
||||||
if (ability instanceof ActivatedAbility) {
|
|
||||||
if (((ActivatedAbility) ability).canActivate(playerId, game).canActivate()) {
|
|
||||||
playable.add(ability);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ability.setControllerId(card.getOwnerId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3316,14 +3321,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (fromAll) {
|
if (fromAll) {
|
||||||
for (Cards revealedCards : game.getState().getRevealed().values()) {
|
for (Cards revealedCards : game.getState().getRevealed().values()) {
|
||||||
for (Card card : revealedCards.getCards(game)) {
|
for (Card card : revealedCards.getCards(game)) {
|
||||||
if (null != game.getContinuousEffects().asThough(card.getId(),
|
playable.addAll(cardPlayableAbilities(game, card));
|
||||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game)) {
|
|
||||||
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
|
|
||||||
if (ability instanceof SpellAbility || ability instanceof PlayLandAbility) {
|
|
||||||
playable.add(ability);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3335,14 +3333,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
if (/*player.isTopCardRevealed() &&*/player.getLibrary().hasCards()) {
|
if (/*player.isTopCardRevealed() &&*/player.getLibrary().hasCards()) {
|
||||||
Card card = player.getLibrary().getFromTop(game);
|
Card card = player.getLibrary().getFromTop(game);
|
||||||
if (card != null && null != game.getContinuousEffects().asThough(card.getId(),
|
playable.addAll(cardPlayableAbilities(game, card));
|
||||||
AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), getId(), game)) {
|
|
||||||
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
|
|
||||||
if (ability instanceof SpellAbility || ability instanceof PlayLandAbility) {
|
|
||||||
playable.add(ability);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue