mirror of
https://github.com/correl/mage.git
synced 2025-03-07 20:53:18 -10:00
* Soulfire Grand Master - Fixed that a countered spell did wrongly return to hand if second ability was used.
This commit is contained in:
parent
2d3c98a7b4
commit
47b1753519
5 changed files with 102 additions and 37 deletions
|
@ -89,27 +89,26 @@ class CurseOfInertiaTriggeredAbility extends TriggeredAbilityImpl {
|
|||
public CurseOfInertiaTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new CurseOfInertiaTapOrUntapTargetEffect(), false);
|
||||
}
|
||||
|
||||
public CurseOfInertiaTriggeredAbility(Effect effect, boolean optional, String text) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
}
|
||||
|
||||
|
||||
public CurseOfInertiaTriggeredAbility(final CurseOfInertiaTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType().equals(EventType.DECLARED_ATTACKERS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getType().equals(EventType.DECLARED_ATTACKERS)) {
|
||||
Permanent enchantment = game.getPermanent(this.getSourceId());
|
||||
if (enchantment != null
|
||||
&& enchantment.getAttachedTo() != null
|
||||
&& game.getCombat().getPlayerDefenders(game).contains(enchantment.getAttachedTo())) {
|
||||
for (Effect effect: this.getEffects()) {
|
||||
effect.setTargetPointer(new FixedTarget(game.getCombat().getAttackerId()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Permanent enchantment = game.getPermanent(this.getSourceId());
|
||||
if (enchantment != null
|
||||
&& enchantment.getAttachedTo() != null
|
||||
&& game.getCombat().getPlayerDefenders(game).contains(enchantment.getAttachedTo())) {
|
||||
TargetPermanent target = new TargetPermanent();
|
||||
target.setTargetController(game.getCombat().getAttackerId());
|
||||
addTarget(target);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -138,24 +137,20 @@ class CurseOfInertiaTapOrUntapTargetEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(this.getTargetPointer().getFirst(game, source));
|
||||
Player player = game.getPlayer(source.getTargets().get(0).getTargetController());
|
||||
if (player != null) {
|
||||
Target target = new TargetPermanent();
|
||||
if (target.canChoose(source.getSourceId(), player.getId(), game)
|
||||
&& player.choose(outcome, target, source.getSourceId(), game)) {
|
||||
Permanent targetPermanent = game.getPermanent(target.getFirstTarget());
|
||||
if (targetPermanent != null) {
|
||||
if (targetPermanent.isTapped()) {
|
||||
if (player.chooseUse(Outcome.Untap, "Untap that permanent?", game)) {
|
||||
targetPermanent.untap(game);
|
||||
}
|
||||
} else {
|
||||
if (player.chooseUse(Outcome.Tap, "Tap that permanent?", game)) {
|
||||
targetPermanent.tap(game);
|
||||
}
|
||||
Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (targetPermanent != null) {
|
||||
if (targetPermanent.isTapped()) {
|
||||
if (player.chooseUse(Outcome.Untap, "Untap that permanent?", game)) {
|
||||
targetPermanent.untap(game);
|
||||
}
|
||||
} else {
|
||||
if (player.chooseUse(Outcome.Tap, "Tap that permanent?", game)) {
|
||||
targetPermanent.tap(game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -48,14 +48,12 @@ import mage.constants.Layer;
|
|||
import mage.constants.Outcome;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.FilterObject;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.filter.predicate.permanent.ControllerPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
|
@ -230,7 +228,10 @@ class SoulfireGrandMasterCastFromHandReplacementEffect extends ReplacementEffect
|
|||
if (zEvent.getFromZone() == Zone.STACK &&
|
||||
zEvent.getToZone() == Zone.GRAVEYARD &&
|
||||
event.getTargetId().equals(spellId)) {
|
||||
return true;
|
||||
Spell spell = game.getStack().getSpell(spellId);
|
||||
if (spell != null && !spell.isCountered()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -30,7 +30,6 @@ package org.mage.test.cards.abilities.other;
|
|||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
|
@ -42,7 +41,7 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase {
|
|||
|
||||
/**
|
||||
* Soulfire Grand Master
|
||||
* Creature — Human Monk 2/2, 1W (2)
|
||||
* Creature - Human Monk 2/2, 1W (2)
|
||||
* 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
|
||||
|
@ -233,5 +232,67 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase {
|
|||
assertLife(playerA, 20);
|
||||
|
||||
}
|
||||
/**
|
||||
* I activated the ability of Soulfire grand master, it resolved, then i cast Stoke the Flames
|
||||
* on Whisperwood Elemental, my opponenet sacrificed the elemental, so stoke didnt resolve,
|
||||
* but i still got the life from lifelink.
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testSoulfireStokeTheFlames() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8);
|
||||
|
||||
addCard(Zone.HAND, playerA, "Stoke the Flames");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Whisperwood Elemental", 1);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U/R}{U/R}:");
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Stoke the Flames", "Whisperwood Elemental");
|
||||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Sacrifice {this}", null ,"{this} deals 4 damage");
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Stoke the Flames", 1); // no legal target left so the spell is countered and goes to graveyard
|
||||
assertGraveyardCount(playerB, "Whisperwood Elemental", 1);
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertLife(playerA, 20);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if second ability resolved, the next spell that is counterer
|
||||
* won't go to hand back because it did not resolve
|
||||
*
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testSoulfireCounteredSpellDontGoesBack() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8);
|
||||
|
||||
addCard(Zone.HAND, playerA, "Stoke the Flames");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
|
||||
addCard(Zone.HAND, playerB, "Counterspell", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Whisperwood Elemental", 1);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U/R}{U/R}:");
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Stoke the Flames", "Whisperwood Elemental");
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Counterspell", "Stoke the Flames");
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Counterspell", 1);
|
||||
assertGraveyardCount(playerA, "Stoke the Flames", 1); // no legal target left so the spell is countered and goes to graveyard
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertLife(playerA, 20);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ public class Spell implements StackObject, Card {
|
|||
private UUID controllerId;
|
||||
private boolean copiedSpell;
|
||||
private boolean faceDown;
|
||||
private boolean countered;
|
||||
|
||||
public Spell(Card card, SpellAbility ability, UUID controllerId, Zone fromZone) {
|
||||
this.card = card;
|
||||
|
@ -100,6 +101,7 @@ public class Spell implements StackObject, Card {
|
|||
}
|
||||
this.controllerId = controllerId;
|
||||
this.fromZone = fromZone;
|
||||
this.countered = false;
|
||||
}
|
||||
|
||||
public Spell(final Spell spell) {
|
||||
|
@ -521,6 +523,7 @@ public class Spell implements StackObject, Card {
|
|||
|
||||
@Override
|
||||
public void counter(UUID sourceId, Game game) {
|
||||
this.countered = true;
|
||||
if (!isCopiedSpell()) {
|
||||
card.moveToZone(Zone.GRAVEYARD, sourceId, game, false);
|
||||
}
|
||||
|
@ -1002,4 +1005,8 @@ public class Spell implements StackObject, Card {
|
|||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
public boolean isCountered() {
|
||||
return countered;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -97,9 +97,10 @@ public class SpellStack extends ArrayDeque<StackObject> {
|
|||
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) {
|
||||
if ( stackObject instanceof Spell ) {
|
||||
game.rememberLKI(objectId, Zone.STACK, (Spell)stackObject);
|
||||
} else {
|
||||
this.remove(stackObject);
|
||||
}
|
||||
this.remove(stackObject);
|
||||
stackObject.counter(sourceId, game); // tries to move to graveyard
|
||||
stackObject.counter(sourceId, game);
|
||||
if (!game.isSimulation())
|
||||
game.informPlayers(counteredObjectName + " is countered by " + sourceObject.getLogName());
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId()));
|
||||
|
|
Loading…
Add table
Reference in a new issue