mirror of
https://github.com/correl/mage.git
synced 2024-12-26 11:09:27 +00:00
Fixed a bug that fizzling spell copies let also wrongly fizzle the original spell on the stack the copy was made from.
This commit is contained in:
parent
2d20045b61
commit
c042d50ec7
5 changed files with 106 additions and 28 deletions
|
@ -64,7 +64,7 @@ public class AtraxaPraetorsVoice extends CardImpl {
|
||||||
// Lifelink
|
// Lifelink
|
||||||
this.addAbility(LifelinkAbility.getInstance());
|
this.addAbility(LifelinkAbility.getInstance());
|
||||||
|
|
||||||
// At the beginning of your end step, proliferate.
|
// At the beginning of your end step, proliferate. (You choose any number of permanents and/or players with counters on them, then give each another counter of a kind already there.)
|
||||||
this.addAbility(new BeginningOfEndStepTriggeredAbility(new ProliferateEffect(), TargetController.YOU, false));
|
this.addAbility(new BeginningOfEndStepTriggeredAbility(new ProliferateEffect(), TargetController.YOU, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,8 +90,9 @@ public class CopySpellTest extends CardTestPlayerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reported bug: "Silverfur Partisan and fellow wolves did not trigger off of copies of Strength of Arms made by Zada, Hedron Grinder.
|
* Reported bug: "Silverfur Partisan and fellow wolves did not trigger off
|
||||||
* Not sure about other spells, but I imagine similar results."
|
* of copies of Strength of Arms made by Zada, Hedron Grinder. Not sure
|
||||||
|
* about other spells, but I imagine similar results."
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void ZadaHedronSilverfurPartisan() {
|
public void ZadaHedronSilverfurPartisan() {
|
||||||
|
@ -206,12 +207,13 @@ public class CopySpellTest extends CardTestPlayerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {4}{U} Enchantment (Enchant Player)
|
* {4}{U} Enchantment (Enchant Player) Whenever enchanted player casts an
|
||||||
* Whenever enchanted player casts an instant or sorcery spell, each other player may copy that spell
|
* instant or sorcery spell, each other player may copy that spell and may
|
||||||
* and may choose new targets for the copy he or she controls.
|
* choose new targets for the copy he or she controls.
|
||||||
*
|
*
|
||||||
* Reported bug: "A player with Curse of Echoes attached to them played Bribery and the player who controlled the curse had control
|
* Reported bug: "A player with Curse of Echoes attached to them played
|
||||||
* of all 3 copies. This seems to be the case for all spells."
|
* Bribery and the player who controlled the curse had control of all 3
|
||||||
|
* copies. This seems to be the case for all spells."
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testCurseOfEchoes() {
|
public void testCurseOfEchoes() {
|
||||||
|
@ -236,4 +238,56 @@ public class CopySpellTest extends CardTestPlayerBase {
|
||||||
assertLife(playerB, 17); // copy redirected
|
assertLife(playerB, 17); // copy redirected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What happened was my opponent had an Atraxa, Praetors' Voice and a
|
||||||
|
* Walking Ballista with 2 counters in play. On my turn, I cast Flame Slash
|
||||||
|
* targeting Atraxa and holding priority, then I cast Dualcaster Mage. I
|
||||||
|
* change the target of the Flame Slash copy to Walking Ballista. My
|
||||||
|
* opponent removes the counters from Ballista to kill a 2/2 creature of
|
||||||
|
* mine. Game log says both Flame Slashes fizzle, and Atraxa ends up still
|
||||||
|
* being in play at the end of it all. Only the Flame Slash targeting
|
||||||
|
* Walking Ballista should have fizzled.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testOnlyCopyFizzles() {
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||||
|
// Flying, vigilance, deathtouch, lifelink
|
||||||
|
// At the beginning of your end step, proliferate.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Atraxa, Praetors' Voice", 4);
|
||||||
|
// Walking Ballista enters the battlefield with X +1/+1 counters on it.
|
||||||
|
// {4}: Put a +1/+1 counter on Walking Ballista.
|
||||||
|
// Remove a +1/+1 counter from Walking Ballista: It deals 1 damage to target creature or player.
|
||||||
|
addCard(Zone.HAND, playerA, "Walking Ballista"); // {X}{X}
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
|
||||||
|
// Flame Slash deals 4 damage to target creature.
|
||||||
|
addCard(Zone.HAND, playerB, "Flame Slash"); // Sorcery {R}
|
||||||
|
// Flash
|
||||||
|
// When Dualcaster Mage enters the battlefield, copy target instant or sorcery spell. You may choose new targets for the copy.
|
||||||
|
addCard(Zone.HAND, playerB, "Dualcaster Mage"); // Creature {1}{R}{R}
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 4);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Walking Ballista");
|
||||||
|
setChoice(playerA, "X=1");
|
||||||
|
setChoice(playerA, "Walking Ballista"); // for proliferate
|
||||||
|
|
||||||
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Flame Slash", "Atraxa, Praetors' Voice");
|
||||||
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Dualcaster Mage");
|
||||||
|
addTarget(playerB, "Flame Slash"); // original target
|
||||||
|
setChoice(playerB, "Yes");
|
||||||
|
addTarget(playerB, "Walking Ballista");
|
||||||
|
|
||||||
|
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Remove a", "Silvercoat Lion", "Flame Slash", StackClause.WHILE_COPY_ON_STACK);
|
||||||
|
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Remove a", "Silvercoat Lion", "Flame Slash", StackClause.WHILE_COPY_ON_STACK);
|
||||||
|
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertGraveyardCount(playerB, "Silvercoat Lion", 1);
|
||||||
|
assertPermanentCount(playerB, "Dualcaster Mage", 1);
|
||||||
|
assertPermanentCount(playerA, "Atraxa, Praetors' Voice", 0);
|
||||||
|
assertPermanentCount(playerA, "Walking Ballista", 0);
|
||||||
|
assertGraveyardCount(playerB, "Flame Slash", 1);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,6 +190,16 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
} else if (groups[2].startsWith("spellCopyOnStack=")) {
|
||||||
|
String spellOnStack = groups[2].substring(17);
|
||||||
|
for (StackObject stackObject : game.getStack()) {
|
||||||
|
if (stackObject.getStackAbility().toString().contains(spellOnStack)) {
|
||||||
|
if (stackObject.isCopy()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
} else if (groups[2].startsWith("!spellOnStack=")) {
|
} else if (groups[2].startsWith("!spellOnStack=")) {
|
||||||
String spellNotOnStack = groups[2].substring(14);
|
String spellNotOnStack = groups[2].substring(14);
|
||||||
for (StackObject stackObject : game.getStack()) {
|
for (StackObject stackObject : game.getStack()) {
|
||||||
|
@ -223,7 +233,7 @@ public class TestPlayer implements Player {
|
||||||
boolean result = true;
|
boolean result = true;
|
||||||
for (int i = 1; i < groupsForTargetHandling.length; i++) {
|
for (int i = 1; i < groupsForTargetHandling.length; i++) {
|
||||||
String group = groupsForTargetHandling[i];
|
String group = groupsForTargetHandling[i];
|
||||||
if (group.startsWith("spellOnStack") || group.startsWith("spellOnTopOfStack") || group.startsWith("!spellOnStack") || group.startsWith("target=null") || group.startsWith("manaInPool=")) {
|
if (group.startsWith("spell") || group.startsWith("!spell") || group.startsWith("target=null") || group.startsWith("manaInPool=")) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) {
|
if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) {
|
||||||
|
|
|
@ -1022,6 +1022,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
public enum StackClause {
|
public enum StackClause {
|
||||||
|
|
||||||
WHILE_ON_STACK,
|
WHILE_ON_STACK,
|
||||||
|
WHILE_COPY_ON_STACK,
|
||||||
WHILE_NOT_ON_STACK
|
WHILE_NOT_ON_STACK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1105,6 +1106,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
* @param targetName if not target has to be defined use the constant
|
* @param targetName if not target has to be defined use the constant
|
||||||
* NO_TARGET
|
* NO_TARGET
|
||||||
* @param spellOnStack
|
* @param spellOnStack
|
||||||
|
* @param clause
|
||||||
*/
|
*/
|
||||||
public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack, StackClause clause) {
|
public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack, StackClause clause) {
|
||||||
StringBuilder sb = new StringBuilder("activate:").append(ability);
|
StringBuilder sb = new StringBuilder("activate:").append(ability);
|
||||||
|
@ -1112,7 +1114,19 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
sb.append("$target=").append(targetName);
|
sb.append("$target=").append(targetName);
|
||||||
}
|
}
|
||||||
if (spellOnStack != null && !spellOnStack.isEmpty()) {
|
if (spellOnStack != null && !spellOnStack.isEmpty()) {
|
||||||
sb.append('$').append(StackClause.WHILE_ON_STACK.equals(clause) ? "" : "!").append("spellOnStack=").append(spellOnStack);
|
sb.append('$');
|
||||||
|
switch (clause) {
|
||||||
|
case WHILE_ON_STACK:
|
||||||
|
sb.append("spellOnStack=");
|
||||||
|
break;
|
||||||
|
case WHILE_NOT_ON_STACK:
|
||||||
|
sb.append("!spellOnStack=");
|
||||||
|
break;
|
||||||
|
case WHILE_COPY_ON_STACK:
|
||||||
|
sb.append("spellCopyOnStack=");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.append(spellOnStack);
|
||||||
}
|
}
|
||||||
player.addAction(turnNum, step, sb.toString());
|
player.addAction(turnNum, step, sb.toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
*/
|
*/
|
||||||
package mage.game.stack;
|
package mage.game.stack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.Mana;
|
import mage.Mana;
|
||||||
|
@ -63,11 +67,6 @@ import mage.game.permanent.PermanentCard;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.util.GameLog;
|
import mage.util.GameLog;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
|
@ -394,7 +393,8 @@ public class Spell extends StackObjImpl implements Card {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
card.removeFromZone(game, Zone.STACK, sourceId);
|
// Copied spell, only remove from stack
|
||||||
|
game.getStack().remove(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue