From 7a6b8a1540bc997e45e978478842ede77bdd0fdf Mon Sep 17 00:00:00 2001 From: Mark Langen Date: Tue, 4 Apr 2017 18:28:30 -0600 Subject: [PATCH] Final Aftermath implementation stuff * Fixed PlayerImpl::getPlayable() to support aftermath (Needs PLAY_FROM_NOT_OWN_HAND_ZONE at the granularity of each card half rather than the whole card) * Added tests for Dusk // Dawn to make sure there are no regressions on Aftermath. --- .../test/cards/single/akh/DuskDawnTest.java | 92 ++++++++++--------- .../main/java/mage/players/PlayerImpl.java | 40 +++++--- 2 files changed, 76 insertions(+), 56 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/akh/DuskDawnTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/akh/DuskDawnTest.java index dd40cd4c054..6dd662ff135 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/akh/DuskDawnTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/akh/DuskDawnTest.java @@ -13,67 +13,73 @@ public class DuskDawnTest extends CardTestPlayerBase { @Test public void testCastDusk() { - addCard(Zone.BATTLEFIELD, playerB, "Tarmogoyf"); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + //Cast dusk from hand + addCard(Zone.BATTLEFIELD, playerB, "Watchwolf"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); addCard(Zone.HAND, playerA, "Dusk // Dawn"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dusk // Dawn"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dusk"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTappedCount("Plains", true, 4); // check that we paid the right side's mana + assertPermanentCount(playerB, "Watchwolf", 0); + assertGraveyardCount(playerB, "Watchwolf", 1); + assertGraveyardCount(playerA, "Dusk // Dawn", 1); } @Test - public void testCastDawnFail() { + public void testCastDuskFromGraveyardFail() { + //Fail to cast dusk from graveyard + addCard(Zone.BATTLEFIELD, playerB, "Watchwolf"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.GRAVEYARD, playerA, "Dusk // Dawn"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dusk"); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Watchwolf", 1); + assertGraveyardCount(playerB, "Watchwolf", 0); + assertGraveyardCount(playerA, "Dusk // Dawn", 1); } @Test public void testCastDawnFromGraveyard() { + addCard(Zone.GRAVEYARD, playerA, "Dusk // Dawn"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.GRAVEYARD, playerA, "Devoted Hero"); + addCard(Zone.GRAVEYARD, playerA, "Watchwolf"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dawn"); - } - - /* - @Test - public void testThatNoncreatureSpellsCannotBeCast() { - addCard(Zone.BATTLEFIELD, playerA, "Hope of Ghirapur"); - - addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); - addCard(Zone.HAND, playerB, "Shock"); - - attack(1, playerA, "Hope of Ghirapur"); - activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sacrifice", playerB); - - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock", playerA); - - setStopAt(2, PhaseStep.BEGIN_COMBAT); + setStopAt(1, PhaseStep.END_TURN); execute(); - assertLife(playerA, 20); - assertLife(playerB, 19); - assertPermanentCount(playerA, "Hope of Ghirapur", 0); + // Dusk dawn should have been cast and exiled + // devoted hero should be in the hand + // watchwolf should still be in the yard + assertHandCount(playerA, "Devoted Hero", 1); + assertGraveyardCount(playerA, "Devoted Hero", 0); + assertGraveyardCount(playerA, "Watchwolf", 1); + assertExileCount(playerA, "Dusk // Dawn", 1); + assertGraveyardCount(playerA, "Dusk // Dawn", 0); } - // Test that ability cannot be activated if after damage Hope of Ghirapur was removed - // from the battlefield and returned back. @Test - public void testWhenHopeOfGhirapurWasRemovedAndReturnedBack() { - addCard(Zone.BATTLEFIELD, playerA, "Hope of Ghirapur"); - addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); - addCard(Zone.HAND, playerA, "Cloudshift"); + public void testCastDawnFail() { + // Fail to cast dawn from hand + addCard(Zone.HAND, playerA, "Dusk // Dawn"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.GRAVEYARD, playerA, "Devoted Hero"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dawn"); - addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); - addCard(Zone.HAND, playerB, "Shock"); - - attack(1, playerA, "Hope of Ghirapur"); - castSpell(1, PhaseStep.END_COMBAT, playerA, "Cloudshift", "Hope of Ghirapur"); - activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sacrifice", playerB); - - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Shock", playerA); - - setStopAt(2, PhaseStep.BEGIN_COMBAT); + setStopAt(1, PhaseStep.END_TURN); execute(); - assertLife(playerA, 18); - assertLife(playerB, 19); - assertPermanentCount(playerA, "Hope of Ghirapur", 1); + // Dusk dawn shouldn't have been cast and devoted hero should still be in the yard + assertHandCount(playerA, "Dusk // Dawn", 1); + assertGraveyardCount(playerA, "Devoted Hero", 1); } - */ + } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index f30ed2bcbbd..1630782d5d4 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2609,6 +2609,23 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } + private void getPlayableFromGraveyardCard(Game game, Card card, Abilities candidateAbilities, ManaOptions availableMana, List output) { + boolean asThoughtCast = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game); + for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) { + boolean possible = false; + if (ability.getZone().match(Zone.GRAVEYARD)) { + possible = true; + } else if (ability.getZone().match(Zone.HAND) && (ability instanceof SpellAbility || ability instanceof PlayLandAbility)) { + if (asThoughtCast || canPlayCardsFromGraveyard()) { + possible = true; + } + } + if (possible && canPlay(ability, availableMana, card, game)) { + output.add(ability); + } + } + } + @Override public List getPlayable(Game game, boolean hidden) { List playable = new ArrayList<>(); @@ -2649,20 +2666,17 @@ public abstract class PlayerImpl implements Player, Serializable { } } for (Card card : graveyard.getUniqueCards(game)) { - boolean asThoughtCast = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game); - for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.ALL)) { - boolean possible = false; - if (ability.getZone().match(Zone.GRAVEYARD)) { - possible = true; - } else if (ability.getZone().match(Zone.HAND) && (ability instanceof SpellAbility || ability instanceof PlayLandAbility)) { - if (asThoughtCast || canPlayCardsFromGraveyard()) { - possible = true; - } - } - if (possible && canPlay(ability, availableMana, card, game)) { - playable.add(ability); - } + // Handle split cards in graveyard to support Aftermath + if (card instanceof SplitCard) { + SplitCard splitCard = (SplitCard) card; + getPlayableFromGraveyardCard(game, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(), availableMana, playable); + getPlayableFromGraveyardCard(game, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(), availableMana, playable); + getPlayableFromGraveyardCard(game, splitCard, splitCard.getSharedAbilities(), availableMana, playable); + } else { + getPlayableFromGraveyardCard(game, card, card.getAbilities(), availableMana, playable); } + + // Other activated abilities LinkedHashMap useable = new LinkedHashMap<>(); getOtherUseableActivatedAbilities(card, Zone.GRAVEYARD, game, useable); for (Ability ability : useable.values()) {