mirror of
https://github.com/correl/mage.git
synced 2025-04-09 09:11:05 -09:00
* Fixed that for spells cast with flashback values calculated from the paid mana (e.g. Converge) did not work correctly.
This commit is contained in:
parent
c71e89c441
commit
2153d5ccf5
2 changed files with 72 additions and 69 deletions
Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords
Mage/src/mage/abilities/keyword
|
@ -49,76 +49,103 @@ public class FlashbackTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
|
||||
addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
|
||||
|
||||
|
||||
// Destroy all artifacts and enchantments. You gain 2 life for each permanent destroyed this way.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Fracturing Gust");
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Berserkers' Onslaught", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Darksteel Citadel", 1);
|
||||
|
||||
|
||||
// When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost.
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage");
|
||||
setChoice(playerA, "Fracturing Gust");
|
||||
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback {2}{G/W}{G/W}{G/W}"); // now snapcaster mage is died so -13/-13
|
||||
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Snapcaster Mage", 1);
|
||||
assertGraveyardCount(playerA, "Berserkers' Onslaught", 1);
|
||||
|
||||
|
||||
assertPermanentCount(playerB, "Darksteel Citadel", 1);
|
||||
|
||||
|
||||
assertExileCount("Fracturing Gust", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* My opponent put Iona on the battlefield using Unburial Rites, but my game
|
||||
* log didn't show me the color he has chosen.
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testUnburialRites() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 8);
|
||||
// Return target creature card from your graveyard to the battlefield.
|
||||
// Flashback {3}{W}
|
||||
// Flashback {3}{W}
|
||||
addCard(Zone.HAND, playerA, "Unburial Rites", 1); // Sorcery - {4}{B}
|
||||
|
||||
|
||||
// Flying
|
||||
// As Iona, Shield of Emeria enters the battlefield, choose a color.
|
||||
// Your opponents can't cast spells of the chosen color.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Iona, Shield of Emeria");
|
||||
|
||||
|
||||
// As Lurebound Scarecrow enters the battlefield, choose a color.
|
||||
// When you control no permanents of the chosen color, sacrifice Lurebound Scarecrow.
|
||||
// When you control no permanents of the chosen color, sacrifice Lurebound Scarecrow.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Lurebound Scarecrow"); // Enchantment - {2}{U}
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt", 1);
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt", 1);
|
||||
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unburial Rites", "Iona, Shield of Emeria");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unburial Rites", "Iona, Shield of Emeria");
|
||||
setChoice(playerA, "Red");
|
||||
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback {3}{W}");
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback {3}{W}");
|
||||
addTarget(playerA, "Lurebound Scarecrow");
|
||||
setChoice(playerA, "White");
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", playerA);
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", playerA);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Iona, Shield of Emeria", 1);
|
||||
assertPermanentCount(playerA, "Lurebound Scarecrow", 1);
|
||||
|
||||
|
||||
assertHandCount(playerB, "Lightning Bolt", 1);
|
||||
|
||||
|
||||
assertExileCount("Unburial Rites", 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testFlashbackWithConverge() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
|
||||
|
||||
// Converge - Put a 1/1 white Kor Ally creature token onto the battlefield for each color of mana spent to cast Unified Front.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Unified Front"); // {3}{W}
|
||||
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Add {W}");
|
||||
// When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost.
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage");
|
||||
setChoice(playerA, "Unified Front");
|
||||
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback {3}{W}");
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Snapcaster Mage", 1);
|
||||
assertPermanentCount(playerA, "Kor Ally", 4);
|
||||
assertExileCount("Unified Front", 1);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,18 +29,12 @@ package mage.abilities.keyword;
|
|||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.DelayedTriggeredAbility;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCost;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
|
||||
import mage.abilities.effects.common.ExileSourceEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SpellAbilityType;
|
||||
|
@ -54,13 +48,14 @@ import mage.players.Player;
|
|||
/**
|
||||
* 702.32. Flashback
|
||||
*
|
||||
* 702.32a. Flashback appears on some instants and sorceries. It represents two static abilities:
|
||||
* one that functions while the card is in a player‘s graveyard and the other that functions
|
||||
* while the card is on the stack. Flashback [cost] means, "You may cast this card from your
|
||||
* graveyard by paying [cost] rather than paying its mana cost" and, "If the flashback cost
|
||||
* was paid, exile this card instead of putting it anywhere else any time it would leave the
|
||||
* stack." Casting a spell using its flashback ability follows the rules for paying alternative
|
||||
* costs in rules 601.2b and 601.2e–g.
|
||||
* 702.32a. Flashback appears on some instants and sorceries. It represents two
|
||||
* static abilities: one that functions while the card is in a player‘s
|
||||
* graveyard and the other that functions while the card is on the stack.
|
||||
* Flashback [cost] means, "You may cast this card from your graveyard by paying
|
||||
* [cost] rather than paying its mana cost" and, "If the flashback cost was
|
||||
* paid, exile this card instead of putting it anywhere else any time it would
|
||||
* leave the stack." Casting a spell using its flashback ability follows the
|
||||
* rules for paying alternative costs in rules 601.2b and 601.2e–g.
|
||||
*
|
||||
* @author nantuko
|
||||
*/
|
||||
|
@ -92,10 +87,10 @@ public class FlashbackAbility extends SpellAbility {
|
|||
if (card != null) {
|
||||
// Flashback can never cast a split card by Fuse, because Fuse only works from hand
|
||||
if (card.isSplitCard()) {
|
||||
if (((SplitCard)card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||
return ((SplitCard)card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
} else if (((SplitCard)card).getRightHalfCard().getName().equals(abilityName)) {
|
||||
return ((SplitCard)card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
|
||||
return ((SplitCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
|
||||
return ((SplitCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
|
||||
}
|
||||
}
|
||||
return card.getSpellAbility().canActivate(playerId, game);
|
||||
|
@ -108,7 +103,7 @@ public class FlashbackAbility extends SpellAbility {
|
|||
public FlashbackAbility copy() {
|
||||
return new FlashbackAbility(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getRule(boolean all) {
|
||||
return this.getRule();
|
||||
|
@ -176,45 +171,26 @@ class FlashbackEffect extends OneShotEffect {
|
|||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
SpellAbility spellAbility;
|
||||
switch(((FlashbackAbility) source).getSpellAbilityType()) {
|
||||
switch (((FlashbackAbility) source).getSpellAbilityType()) {
|
||||
case SPLIT_LEFT:
|
||||
spellAbility = ((SplitCard)card).getLeftHalfCard().getSpellAbility();
|
||||
spellAbility = ((SplitCard) card).getLeftHalfCard().getSpellAbility();
|
||||
break;
|
||||
case SPLIT_RIGHT:
|
||||
spellAbility = ((SplitCard)card).getRightHalfCard().getSpellAbility();
|
||||
spellAbility = ((SplitCard) card).getRightHalfCard().getSpellAbility();
|
||||
break;
|
||||
default:
|
||||
spellAbility = card.getSpellAbility();
|
||||
}
|
||||
|
||||
spellAbility.clear();
|
||||
// used if flashbacked spell has a {X} cost
|
||||
int amount = source.getManaCostsToPay().getX();
|
||||
if (amount == 0) {
|
||||
// add variable cost like Discard X cards to get the X value to the spell
|
||||
// because there is currently no way to set the x value in anotehr way, it's set for the
|
||||
// x mana value to be known by the spell
|
||||
for (Cost cost:source.getCosts()) {
|
||||
if (cost instanceof VariableCost && cost.isPaid()) {
|
||||
amount = ((VariableCost) cost).getAmount();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// set the payed flashback costs to the spell ability so abilities like Converge or calculation of {X} values work
|
||||
spellAbility.getManaCostsToPay().clear();
|
||||
spellAbility.getManaCostsToPay().addAll(source.getManaCostsToPay());
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(controller.getLogName() + " flashbacks " + card.getLogName());
|
||||
}
|
||||
if (amount > 0) {
|
||||
// multiplier must be taken into account because if the base spell has {X}{X} the x value would be wrongly halfed
|
||||
for (VariableCost variableCost: spellAbility.getManaCostsToPay().getVariableCosts()) {
|
||||
if (variableCost instanceof VariableManaCost) {
|
||||
amount = amount * ((VariableManaCost)variableCost).getMultiplier();
|
||||
break;
|
||||
}
|
||||
}
|
||||
spellAbility.getManaCostsToPay().setX(amount);
|
||||
}
|
||||
if (!game.isSimulation())
|
||||
game.informPlayers(new StringBuilder(controller.getLogName()).append(" flashbacks ").append(card.getName()).toString());
|
||||
spellAbility.setCostModificationActive(false); // prevents to apply cost modification twice for flashbacked spells
|
||||
if (controller.cast(spellAbility, game, true)) {
|
||||
if (controller.cast(spellAbility, game, false)) {
|
||||
game.addEffect(new FlashbackReplacementEffect(), source);
|
||||
return true;
|
||||
}
|
||||
|
@ -252,7 +228,7 @@ class FlashbackReplacementEffect extends ReplacementEffectImpl {
|
|||
if (controller != null) {
|
||||
Card card = game.getCard(event.getTargetId());
|
||||
if (card != null) {
|
||||
return controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, game.getState().getZone(card.getId()), true);
|
||||
return controller.moveCards(card, Zone.EXILED, source, game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -265,8 +241,8 @@ class FlashbackReplacementEffect extends ReplacementEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return event.getTargetId().equals(source.getSourceId())
|
||||
&& ((ZoneChangeEvent)event).getFromZone() == Zone.STACK
|
||||
&& ((ZoneChangeEvent)event).getToZone() != Zone.EXILED;
|
||||
return event.getTargetId().equals(source.getSourceId())
|
||||
&& ((ZoneChangeEvent) event).getFromZone() == Zone.STACK
|
||||
&& ((ZoneChangeEvent) event).getToZone() != Zone.EXILED;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue