mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
Recover abilities - fixed that it doesn't ask to pay a cost on multiple triggers;
This commit is contained in:
parent
6d55e4b9e6
commit
57ef74da90
3 changed files with 120 additions and 38 deletions
|
|
@ -61,15 +61,16 @@ public class ExploitTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerB, "Lightning Bolt", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Thundering Giant"); // 4/3
|
||||
|
||||
setStrictChooseMode(true);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silumgar Butcher");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1);
|
||||
setChoice(playerA, true); // Choose to exploit
|
||||
setChoice(playerA, "Silvercoat Lion"); // sacrifice to Exploit
|
||||
|
||||
// kill butcher before exploit trigger resolve, so no exploits trigger with target
|
||||
// if you failed here then something wrong with isInUseableZone
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", "Silumgar Butcher");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.cards.abilities.keywords;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
|
@ -7,8 +6,7 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
* @author LevelX2, JayDi85
|
||||
*/
|
||||
public class RecoverTest extends CardTestPlayerBase {
|
||||
|
||||
|
|
@ -20,7 +18,7 @@ public class RecoverTest extends CardTestPlayerBase {
|
|||
* Otherwise, exile this card.”
|
||||
*/
|
||||
@Test
|
||||
public void testReturnToHand() {
|
||||
public void test_Normal_ToHand() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
|
||||
// You gain 4 life.
|
||||
// Recover {1}{W}
|
||||
|
|
@ -35,6 +33,7 @@ public class RecoverTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion");
|
||||
setChoice(playerA, true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
|
|
@ -49,7 +48,7 @@ public class RecoverTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testGoingToExile() {
|
||||
public void test_Normal_ToExile() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
|
||||
// You gain 4 life.
|
||||
// Recover {1}{W}
|
||||
|
|
@ -64,6 +63,7 @@ public class RecoverTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion");
|
||||
setChoice(playerA, false);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
|
|
@ -74,6 +74,111 @@ public class RecoverTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||
|
||||
assertLife(playerA, 24);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DieOther_Single_CanRecover() {
|
||||
addCustomEffect_TargetDestroy(playerA, 1);
|
||||
|
||||
// Recover—Pay half your life, rounded up.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Garza's Assassin");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy");
|
||||
addTarget(playerA, "Silvercoat Lion");
|
||||
setChoice(playerA, true); // pay half life
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertHandCount(playerA, "Garza's Assassin", 1); // after recover
|
||||
assertLife(playerA, 20 / 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DieOther_Multiple_CanRecover() {
|
||||
// ruling from wiki:
|
||||
// If multiple creatures are put into your graveyard from the battlefield at the same time, the recover
|
||||
// ability of a card already in your graveyard triggers that many times. Only the first one to resolve
|
||||
// will cause the card to move somewhere. By the time any of the other triggers resolve, the card won't be
|
||||
// in your graveyard anymore. You can still pay the recover cost, but nothing else will happen.
|
||||
|
||||
addCustomEffect_TargetDestroy(playerA, 2);
|
||||
|
||||
// Recover—Pay half your life, rounded up.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Garza's Assassin");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
|
||||
|
||||
// raise 2 recover triggers, pay second trigger - it will be fizzled
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy");
|
||||
addTarget(playerA, "Silvercoat Lion^Grizzly Bears");
|
||||
setChoice(playerA, "Recover—Pay half your life"); // x2 triggers order
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1);
|
||||
checkStackObject("on recover triggers", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Recover—Pay half your life", 2);
|
||||
setChoice(playerA, false); // first trigger resolve - do not pay and exile
|
||||
setChoice(playerA, true); // second trigger resolve - pay and fizzle
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertExileCount(playerA, "Garza's Assassin", 1); // after first unpayed trigger
|
||||
assertLife(playerA, 20 / 2); // after second unpayed trigger
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DieItself_MustNotWork() {
|
||||
// ruling from wiki:
|
||||
// If a creature with recover is put into your graveyard from the battlefield, it doesn't cause its
|
||||
// own recover ability to trigger. Similarly, if another creature is put into your graveyard from
|
||||
// the battlefield at the same time that a card with recover is put there, it won't cause that
|
||||
// recover ability to trigger.
|
||||
|
||||
addCustomEffect_TargetDestroy(playerA, 1);
|
||||
|
||||
// Recover—Pay half your life, rounded up.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Garza's Assassin");
|
||||
|
||||
// no recover
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy");
|
||||
addTarget(playerA, "Garza's Assassin");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Garza's Assassin", 1);
|
||||
assertLife(playerA, 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DieItselfAndMultiple_MustNotWork() {
|
||||
// ruling from wiki:
|
||||
// If a creature with recover is put into your graveyard from the battlefield, it doesn't cause its
|
||||
// own recover ability to trigger. Similarly, if another creature is put into your graveyard from
|
||||
// the battlefield at the same time that a card with recover is put there, it won't cause that
|
||||
// recover ability to trigger.
|
||||
|
||||
// reason: it's leaves-the-battlefield trigger and look back in time (source was on battlefield in that time, so no trigger)
|
||||
|
||||
addCustomEffect_TargetDestroy(playerA, 2);
|
||||
|
||||
// Recover—Pay half your life, rounded up.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Garza's Assassin");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
|
||||
// no recover (if you catch recover dialog then something wrong with isInUseableZone)
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy");
|
||||
addTarget(playerA, "Garza's Assassin^Silvercoat Lion");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Garza's Assassin", 1);
|
||||
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||
assertLife(playerA, 20);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,16 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.ExileSourceEffect;
|
||||
import mage.abilities.effects.common.ReturnToHandSourceEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* 702.58a Recover is a triggered ability that functions only while the card
|
||||
|
|
@ -28,7 +24,8 @@ import mage.players.Player;
|
|||
public class RecoverAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public RecoverAbility(Cost cost, Card card) {
|
||||
super(Zone.GRAVEYARD, new RecoverEffect(cost, card.isCreature()), false);
|
||||
super(Zone.GRAVEYARD, new RecoverEffect(cost, card), false);
|
||||
setLeavesTheBattlefieldTrigger(true);
|
||||
}
|
||||
|
||||
protected RecoverAbility(final RecoverAbility ability) {
|
||||
|
|
@ -64,19 +61,15 @@ public class RecoverAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class RecoverEffect extends OneShotEffect {
|
||||
class RecoverEffect extends DoIfCostPaid {
|
||||
|
||||
protected Cost cost;
|
||||
|
||||
public RecoverEffect(Cost cost, boolean creature) {
|
||||
super(Outcome.ReturnToHand);
|
||||
this.cost = cost;
|
||||
this.staticText = setText(cost, creature);
|
||||
public RecoverEffect(Cost cost, Card card) {
|
||||
super(new ReturnToHandSourceEffect(), new ExileSourceEffect(), cost);
|
||||
this.staticText = setText(cost, card.isCreature());
|
||||
}
|
||||
|
||||
protected RecoverEffect(final RecoverEffect effect) {
|
||||
super(effect);
|
||||
this.cost = effect.cost.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -84,23 +77,6 @@ class RecoverEffect extends OneShotEffect {
|
|||
return new RecoverEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Card sourceCard = game.getCard(source.getSourceId());
|
||||
if (controller != null && sourceCard != null
|
||||
&& game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) {
|
||||
if (controller.chooseUse(Outcome.Damage, "Pay " + cost.getText() + " to recover " + sourceCard.getLogName() + "? (Otherwise the card will be exiled)", source, game)) {
|
||||
cost.clearPaid();
|
||||
if (cost.pay(source, game, source, controller.getId(), false, null)) {
|
||||
return new ReturnToHandSourceEffect().apply(game, source);
|
||||
}
|
||||
}
|
||||
return new ExileSourceEffect().apply(game, source);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String setText(Cost cost, boolean creature) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Recover");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue