* Fixed that Soulfire Grand Master did not work for adventure or split cards (fixes #6182).

This commit is contained in:
LevelX2 2020-01-14 23:26:04 +01:00
parent afbae25fd1
commit 7575510c85
3 changed files with 111 additions and 51 deletions

View file

@ -1,5 +1,6 @@
package mage.cards.s; package mage.cards.s;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
@ -10,9 +11,11 @@ import mage.abilities.effects.Effect;
import mage.abilities.effects.GainAbilitySpellsEffect; import mage.abilities.effects.GainAbilitySpellsEffect;
import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.keyword.LifelinkAbility; import mage.abilities.keyword.LifelinkAbility;
import mage.cards.AdventureCardSpell;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.cards.SplitCardHalf;
import mage.constants.*; import mage.constants.*;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.FilterObject; import mage.filter.FilterObject;
@ -23,8 +26,6 @@ import mage.game.events.ZoneChangeEvent;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.players.Player; import mage.players.Player;
import java.util.UUID;
/** /**
* @author LevelX2 * @author LevelX2
*/ */
@ -99,12 +100,10 @@ class SoulfireGrandMasterCastFromHandReplacementEffect extends ReplacementEffect
@Override @Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) { public boolean replaceEvent(GameEvent event, Ability source, Game game) {
MageObject mageObject = game.getObject(spellId); Spell spell = (Spell) game.getStack().getFirst();
if (!(mageObject instanceof Spell) || mageObject.isCopy()) { if (!spell.isCopy() && !spell.isCountered()) {
return false;
} else {
Card sourceCard = game.getCard(spellId); Card sourceCard = game.getCard(spellId);
if (sourceCard != null) { if (sourceCard != null && Zone.STACK.equals(game.getState().getZone(spellId))) {
Player player = game.getPlayer(sourceCard.getOwnerId()); Player player = game.getPlayer(sourceCard.getOwnerId());
if (player != null) { if (player != null) {
player.moveCards(sourceCard, Zone.HAND, source, game); player.moveCards(sourceCard, Zone.HAND, source, game);
@ -141,9 +140,17 @@ class SoulfireGrandMasterCastFromHandReplacementEffect extends ReplacementEffect
if (zEvent.getFromZone() == Zone.STACK if (zEvent.getFromZone() == Zone.STACK
&& zEvent.getToZone() == Zone.GRAVEYARD && zEvent.getToZone() == Zone.GRAVEYARD
&& event.getTargetId().equals(spellId)) { && event.getTargetId().equals(spellId)) {
Spell spell = game.getStack().getSpell(spellId); if (game.getStack().getFirst() instanceof Spell) {
if (spell != null && !spell.isCountered()) { Card cardOfSpell = ((Spell) game.getStack().getFirst()).getCard();
return true; if (cardOfSpell instanceof SplitCardHalf) {
return ((SplitCardHalf) cardOfSpell).getParentCard().getId().equals(spellId);
} else if (cardOfSpell instanceof AdventureCardSpell) {
return (((AdventureCardSpell) cardOfSpell).getParentCard().getId().equals(spellId));
} else {
if (cardOfSpell.getId().equals(spellId)) {
return true;
}
}
} }
} }
} }

View file

@ -0,0 +1,52 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.test.cards.cost.splitcards;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class SplitCardsTest extends CardTestPlayerBase {
@Test
public void testReturnCardFromSoulfireGrandMaster() {
// Total CMC of Failure // Comply is 3, so should be exiled by Transgress the Mind.
addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 6);
// Lifelink
// Instant and sorcery spells you control have lifelink.
// {2}{U/R}{U/R}: The next time you cast an instant or sorcery spell from your hand this turn, put that card into your hand instead of your graveyard as it resolves.
addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master");
// Fire - Instant {1}{R}
// Fire deals 2 damage divided as you choose among one or two target creatures and/or players.
// Ice - Instant {1}{U}
// Tap target permanent.
// Draw a card.
addCard(Zone.HAND, playerA, "Fire // Ice");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U/R}{U/R}");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Fire", playerB);
addTargetAmount(playerA, "targetPlayer=PlayerB", 2);
setStopAt(1, PhaseStep.END_TURN);
setStrictChooseMode(true);
execute();
assertAllCommandsUsed();
assertLife(playerA, 22);
assertLife(playerB, 18);
assertHandCount(playerA, "Fire // Ice", 1);
}
}

View file

@ -1,5 +1,10 @@
package org.mage.test.player; package org.mage.test.player;
import java.io.Serializable;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import mage.MageItem; import mage.MageItem;
import mage.MageObject; import mage.MageObject;
import mage.MageObjectReference; import mage.MageObjectReference;
@ -56,13 +61,6 @@ import mage.util.CardUtil;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Ignore; import org.junit.Ignore;
import java.io.Serializable;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*; import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*;
/** /**
@ -179,7 +177,7 @@ public class TestPlayer implements Player {
/** /**
* @param maxCallsWithoutAction max number of priority passes a player may * @param maxCallsWithoutAction max number of priority passes a player may
* have for this test (default = 100) * have for this test (default = 100)
*/ */
public void setMaxCallsWithoutAction(int maxCallsWithoutAction) { public void setMaxCallsWithoutAction(int maxCallsWithoutAction) {
this.maxCallsWithoutAction = maxCallsWithoutAction; this.maxCallsWithoutAction = maxCallsWithoutAction;
@ -333,6 +331,9 @@ public class TestPlayer implements Player {
if (ability.getTargets().isEmpty()) { if (ability.getTargets().isEmpty()) {
throw new UnsupportedOperationException("Ability has no targets, but there is a player target set - " + ability.toString()); throw new UnsupportedOperationException("Ability has no targets, but there is a player target set - " + ability.toString());
} }
if (ability.getTargets().get(0) instanceof TargetAmount) {
return true; // targetAmount have to be set by setTargetAmount in the test script
}
ability.getTargets().get(0).addTarget(player.getId(), ability, game); ability.getTargets().get(0).addTarget(player.getId(), ability, game);
targetsSet++; targetsSet++;
break; break;
@ -900,12 +901,12 @@ public class TestPlayer implements Player {
List<String> data = cards.stream() List<String> data = cards.stream()
.map(c -> (((c instanceof PermanentToken) ? "[T] " : "[C] ") .map(c -> (((c instanceof PermanentToken) ? "[T] " : "[C] ")
+ c.getIdName() + c.getIdName()
+ (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "") + (c.isCopy() ? " [copy of " + c.getCopyFrom().getId().toString().substring(0, 3) + "]" : "")
+ " - " + c.getPower().getValue() + "/" + c.getToughness().getValue() + " - " + c.getPower().getValue() + "/" + c.getToughness().getValue()
+ (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "") + (c.isPlaneswalker() ? " - L" + c.getCounters(game).getCount(CounterType.LOYALTY) : "")
+ ", " + (c.isTapped() ? "Tapped" : "Untapped") + ", " + (c.isTapped() ? "Tapped" : "Untapped")
+ (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName()))) + (c.getAttachedTo() == null ? "" : ", attached to " + game.getPermanent(c.getAttachedTo()).getIdName())))
.sorted() .sorted()
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -929,11 +930,11 @@ public class TestPlayer implements Player {
List<String> data = abilities.stream() List<String> data = abilities.stream()
.map(a -> (a.getZone() + " -> " .map(a -> (a.getZone() + " -> "
+ a.getSourceObject(game).getIdName() + " -> " + a.getSourceObject(game).getIdName() + " -> "
+ (a.toString().length() > 0 + (a.toString().length() > 0
? a.toString().substring(0, Math.min(20, a.toString().length()) - 1) ? a.toString().substring(0, Math.min(20, a.toString().length()) - 1)
: a.getClass().getSimpleName()) : a.getClass().getSimpleName())
+ "...")) + "..."))
.sorted() .sorted()
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -1287,7 +1288,7 @@ public class TestPlayer implements Player {
UUID defenderId = null; UUID defenderId = null;
boolean mustAttackByAction = false; boolean mustAttackByAction = false;
boolean madeAttackByAction = false; boolean madeAttackByAction = false;
for (Iterator<org.mage.test.player.PlayerAction> it = actions.iterator(); it.hasNext(); ) { for (Iterator<org.mage.test.player.PlayerAction> it = actions.iterator(); it.hasNext();) {
PlayerAction action = it.next(); PlayerAction action = it.next();
if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) { if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) {
mustAttackByAction = true; mustAttackByAction = true;
@ -1769,7 +1770,7 @@ public class TestPlayer implements Player {
// skip targets // skip targets
if (targets.get(0).equals(TARGET_SKIP)) { if (targets.get(0).equals(TARGET_SKIP)) {
Assert.assertTrue("found skip target, but it require more targets, needs " Assert.assertTrue("found skip target, but it require more targets, needs "
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more", + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
target.getTargets().size() >= target.getMinNumberOfTargets()); target.getTargets().size() >= target.getMinNumberOfTargets());
targets.remove(0); targets.remove(0);
return true; return true;
@ -3243,7 +3244,7 @@ public class TestPlayer implements Player {
@Override @Override
public boolean choose(Outcome outcome, Target target, public boolean choose(Outcome outcome, Target target,
UUID sourceId, Game game UUID sourceId, Game game
) { ) {
// needed to call here the TestPlayer because it's overwitten // needed to call here the TestPlayer because it's overwitten
return choose(outcome, target, sourceId, game, null); return choose(outcome, target, sourceId, game, null);
@ -3251,7 +3252,7 @@ public class TestPlayer implements Player {
@Override @Override
public boolean choose(Outcome outcome, Cards cards, public boolean choose(Outcome outcome, Cards cards,
TargetCard target, Game game TargetCard target, Game game
) { ) {
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
for (String choose2 : choices) { for (String choose2 : choices) {
@ -3287,7 +3288,7 @@ public class TestPlayer implements Player {
@Override @Override
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, public boolean chooseTargetAmount(Outcome outcome, TargetAmount target,
Ability source, Game game Ability source, Game game
) { ) {
// chooseTargetAmount calls for EACH target cycle (e.g. one target per click, see TargetAmount) // chooseTargetAmount calls for EACH target cycle (e.g. one target per click, see TargetAmount)
// if use want to stop choosing then chooseTargetAmount must return false (example: up to xxx) // if use want to stop choosing then chooseTargetAmount must return false (example: up to xxx)
@ -3299,7 +3300,7 @@ public class TestPlayer implements Player {
// skip targets // skip targets
if (targets.get(0).equals(TARGET_SKIP)) { if (targets.get(0).equals(TARGET_SKIP)) {
Assert.assertTrue("found skip target, but it require more targets, needs " Assert.assertTrue("found skip target, but it require more targets, needs "
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more", + (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
target.getTargets().size() >= target.getMinNumberOfTargets()); target.getTargets().size() >= target.getMinNumberOfTargets());
targets.remove(0); targets.remove(0);
return false; // false in chooseTargetAmount = stop to choose return false; // false in chooseTargetAmount = stop to choose
@ -3352,15 +3353,15 @@ public class TestPlayer implements Player {
@Override @Override
public boolean choosePile(Outcome outcome, String message, public boolean choosePile(Outcome outcome, String message,
List<? extends Card> pile1, List<? extends Card> pile2, List<? extends Card> pile1, List<? extends Card> pile2,
Game game Game game
) { ) {
return computerPlayer.choosePile(outcome, message, pile1, pile2, game); return computerPlayer.choosePile(outcome, message, pile1, pile2, game);
} }
@Override @Override
public boolean playMana(Ability ability, ManaCost unpaid, public boolean playMana(Ability ability, ManaCost unpaid,
String promptText, Game game String promptText, Game game
) { ) {
groupsForTargetHandling = null; groupsForTargetHandling = null;
return computerPlayer.playMana(ability, unpaid, promptText, game); return computerPlayer.playMana(ability, unpaid, promptText, game);
@ -3374,15 +3375,15 @@ public class TestPlayer implements Player {
@Override @Override
public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup, public UUID chooseBlockerOrder(List<Permanent> blockers, CombatGroup combatGroup,
List<UUID> blockerOrder, Game game List<UUID> blockerOrder, Game game
) { ) {
return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
} }
@Override @Override
public void assignDamage(int damage, List<UUID> targets, public void assignDamage(int damage, List<UUID> targets,
String singleTargetName, UUID sourceId, String singleTargetName, UUID sourceId,
Game game Game game
) { ) {
computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game); computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game);
} }
@ -3401,14 +3402,14 @@ public class TestPlayer implements Player {
@Override @Override
public void pickCard(List<Card> cards, Deck deck, public void pickCard(List<Card> cards, Deck deck,
Draft draft Draft draft
) { ) {
computerPlayer.pickCard(cards, deck, draft); computerPlayer.pickCard(cards, deck, draft);
} }
@Override @Override
public boolean scry(int value, Ability source, public boolean scry(int value, Ability source,
Game game Game game
) { ) {
// Don't scry at the start of the game. // Don't scry at the start of the game.
if (game.getTurnNum() == 1 && game.getStep() == null) { if (game.getTurnNum() == 1 && game.getStep() == null) {
@ -3419,44 +3420,44 @@ public class TestPlayer implements Player {
@Override @Override
public boolean surveil(int value, Ability source, public boolean surveil(int value, Ability source,
Game game Game game
) { ) {
return computerPlayer.surveil(value, source, game); return computerPlayer.surveil(value, source, game);
} }
@Override @Override
public boolean moveCards(Card card, Zone toZone, public boolean moveCards(Card card, Zone toZone,
Ability source, Game game Ability source, Game game
) { ) {
return computerPlayer.moveCards(card, toZone, source, game); return computerPlayer.moveCards(card, toZone, source, game);
} }
@Override @Override
public boolean moveCards(Card card, Zone toZone, public boolean moveCards(Card card, Zone toZone,
Ability source, Game game, Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
) { ) {
return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); return computerPlayer.moveCards(card, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
} }
@Override @Override
public boolean moveCards(Cards cards, Zone toZone, public boolean moveCards(Cards cards, Zone toZone,
Ability source, Game game Ability source, Game game
) { ) {
return computerPlayer.moveCards(cards, toZone, source, game); return computerPlayer.moveCards(cards, toZone, source, game);
} }
@Override @Override
public boolean moveCards(Set<Card> cards, Zone toZone, public boolean moveCards(Set<Card> cards, Zone toZone,
Ability source, Game game Ability source, Game game
) { ) {
return computerPlayer.moveCards(cards, toZone, source, game); return computerPlayer.moveCards(cards, toZone, source, game);
} }
@Override @Override
public boolean moveCards(Set<Card> cards, Zone toZone, public boolean moveCards(Set<Card> cards, Zone toZone,
Ability source, Game game, Ability source, Game game,
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
) { ) {
return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
} }