1
0
Fork 0
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:
LevelX2 2015-10-30 00:30:53 +01:00
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

View file

@ -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);
}
}

View file

@ -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 players 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.2eg.
* 702.32a. Flashback appears on some instants and sorceries. It represents two
* static abilities: one that functions while the card is in a players
* 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.2eg.
*
* @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;
}
}