diff --git a/Mage.Sets/src/mage/sets/fatereforged/SoulfireGrandMaster.java b/Mage.Sets/src/mage/sets/fatereforged/SoulfireGrandMaster.java index b42fce09390..03c29db4dfd 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/SoulfireGrandMaster.java +++ b/Mage.Sets/src/mage/sets/fatereforged/SoulfireGrandMaster.java @@ -52,7 +52,7 @@ import mage.constants.TargetController; import mage.constants.WatcherScope; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.FilterSpell; +import mage.filter.FilterObject; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.permanent.ControllerPredicate; @@ -71,11 +71,10 @@ import mage.watchers.Watcher; */ public class SoulfireGrandMaster extends CardImpl { - private static final FilterSpell filter = new FilterSpell("instant and sorcery spells you control"); + private static final FilterObject filter = new FilterObject("instant and sorcery spells you control"); static { filter.add(Predicates.or(new CardTypePredicate(CardType.INSTANT), new CardTypePredicate(CardType.SORCERY))); - filter.add(new ControllerPredicate(TargetController.YOU)); } public SoulfireGrandMaster(UUID ownerId) { @@ -112,9 +111,9 @@ public class SoulfireGrandMaster extends CardImpl { class GainAbilitySpellsEffect extends ContinuousEffectImpl { private final Ability ability; - private final FilterSpell filter; + private final FilterObject filter; - public GainAbilitySpellsEffect(Ability ability, FilterSpell filter) { + public GainAbilitySpellsEffect(Ability ability, FilterObject filter) { super(Duration.Custom, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); this.ability = ability; this.filter = filter; @@ -139,10 +138,9 @@ class GainAbilitySpellsEffect extends ContinuousEffectImpl { if (player != null && permanent != null) { for (Iterator iterator = game.getStack().iterator(); iterator.hasNext();) { StackObject stackObject = iterator.next(); - if (stackObject instanceof Spell && filter.match(stackObject, source.getControllerId(), game)) { - Spell spell = (Spell) stackObject; - Card card = spell.getCard(); - if (card != null) { + if (stackObject.getControllerId().equals(source.getControllerId())) { + Card card = game.getCard(stackObject.getSourceId()); + if (card != null && filter.match(card, game)) { if (!card.getAbilities().contains(ability)) { game.getState().addOtherAbility(card, ability); SoulfireGrandMasterLeavesStackWatcher watcher = (SoulfireGrandMasterLeavesStackWatcher) game.getState().getWatchers().get("SoulfireGrandMasterLeavesStackWatcher"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java index 656fa20f114..ab4a90cf9cd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/SoulfireGrandMasterTest.java @@ -50,8 +50,7 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { * graveyard as it resolves. * */ - - @Ignore // at this time player.getPlayable() does not account for spells that gain abilities + @Test public void testSpellsGainLifelink() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); @@ -70,7 +69,6 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { } - @Ignore // at this time player.getPlayable() does not account for spells that gain abilities @Test public void testSpellsReturnToHand() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); @@ -78,9 +76,10 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Lightning Bolt"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U/R}{U/R}:"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); - setStopAt(1, PhaseStep.BEGIN_COMBAT); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + setStopAt(1, PhaseStep.END_TURN); execute(); assertGraveyardCount(playerA, "Lightning Bolt", 0); @@ -89,5 +88,69 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase { assertLife(playerB, 17); } + /** + * Test with Searing Blood + * If the delayed triggered ability triggers, it has to give + * life from lifelink because the source is still Searing Blood + */ + // @Ignore // Does not work because as the delayed triggered ability resolves, the source card is no longer on the stack and + @Test + public void testSearingBlood1() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // Searing Blood {R}{R} + // Searing Blood deals 2 damage to target creature. When that creature dies this turn, Searing Blood deals 3 damage to that creature's controller. + addCard(Zone.HAND, playerA, "Searing Blood"); + addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Searing Blood", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Soulfire Grand Master", 1); + assertGraveyardCount(playerA, "Searing Blood", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + assertLife(playerB, 17); // -3 by Searing blood because Silvercoat Lion dies + assertLife(playerA, 25); // +2 from damage to Silvercoat Lion + 3 from damage to Player B + + } + + /** + * Test with Searing Blood + * If the delayed triggered ability triggers, it has to give + * life from lifelink because the source is still Searing Blood + */ + @Test + public void testSearinBlood2() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Searing Blood {R}{R} + // Searing Blood deals 2 damage to target creature. When that creature dies this turn, Searing Blood deals 3 damage to that creature's controller. + addCard(Zone.HAND, playerA, "Searing Blood"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master", 1); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Searing Blood", "Pillarfield Ox"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Pillarfield Ox"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Soulfire Grand Master", 1); + assertGraveyardCount(playerA, "Searing Blood", 1); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertGraveyardCount(playerB, "Pillarfield Ox", 1); + + assertLife(playerB, 17); + assertLife(playerA, 28); // +2 from damage to Silvercoat Lion + 3 from Lighning Bolt + 3 from damage to Player B from Searing Blood + + } + + // Test copied spell + + // Test damage of activated ability of a permanent does not gain lifelink } diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index a408978198f..eea868fd4d8 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -39,7 +39,6 @@ import java.util.UUID; import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Abilities; -import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; @@ -65,8 +64,8 @@ import mage.constants.Zone; import mage.counters.Counter; import mage.counters.CounterType; import mage.counters.Counters; -import mage.game.CardState; import mage.game.Game; +import mage.game.command.CommandObject; import mage.game.events.DamageCreatureEvent; import mage.game.events.DamagePlaneswalkerEvent; import mage.game.events.DamagedCreatureEvent; @@ -75,6 +74,7 @@ import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; /** @@ -741,23 +741,37 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } if (damageDone > 0) { UUID sourceControllerId = null; + Abilities sourceAbilities = null; MageObject source = game.getPermanentOrLKIBattlefield(sourceId); if (source == null) { - source = game.getObject(sourceId); + StackObject stackObject = game.getStack().getStackObject(sourceId); + if (stackObject != null) { + source = stackObject.getStackAbility().getSourceObject(game); + } else { + source = game.getObject(sourceId); + } if (source instanceof Spell) { + sourceAbilities = ((Spell) source).getAbilities(game); sourceControllerId = ((Spell) source).getControllerId(); + } else if (source instanceof Card) { + sourceAbilities = ((Card) source).getAbilities(game); + sourceControllerId = ((Card) source).getOwnerId(); + } else if (source instanceof CommandObject){ + sourceControllerId = ((CommandObject) source).getControllerId(); + sourceAbilities = ((CommandObject) source).getAbilities(); } else { source = null; } } else { + sourceAbilities = ((Permanent) source).getAbilities(game); sourceControllerId = ((Permanent) source).getControllerId(); } - if (source != null) { - if (source.getAbilities().containsKey(LifelinkAbility.getInstance().getId())) { + if (source != null && sourceAbilities!= null) { + if (sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) { Player player = game.getPlayer(sourceControllerId); player.gainLife(damageAmount, game); } - if (source.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) { + if (sourceAbilities.containsKey(DeathtouchAbility.getInstance().getId())) { deathtouched = true; } if (dealtDamageByThisTurn == null) { diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 07ad58025ac..327e8c90127 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1610,21 +1610,32 @@ public abstract class PlayerImpl implements Player, Serializable { int actualDamage = event.getAmount(); if (actualDamage > 0) { UUID sourceControllerId = null; + Abilities sourceAbilities = null; MageObject source = game.getPermanentOrLKIBattlefield(sourceId); if (source == null) { - source = game.getObject(sourceId); - if (source instanceof Card && !CardUtil.isPermanentCard((Card) source)) { - source = game.getLastKnownInformation(sourceId, Zone.STACK); + StackObject stackObject = game.getStack().getStackObject(sourceId); + if (stackObject != null) { + source = stackObject.getStackAbility().getSourceObject(game); + } else { + source = game.getObject(sourceId); } if (source instanceof Spell) { + sourceAbilities = ((Spell) source).getAbilities(game); sourceControllerId = ((Spell) source).getControllerId(); + } else if (source instanceof Card) { + sourceAbilities = ((Card) source).getAbilities(game); + sourceControllerId = ((Card) source).getOwnerId(); + } else if (source instanceof CommandObject){ + sourceControllerId = ((CommandObject) source).getControllerId(); + sourceAbilities = ((CommandObject) source).getAbilities(); } else { source = null; } } else { + sourceAbilities = ((Permanent) source).getAbilities(game); sourceControllerId = ((Permanent) source).getControllerId(); } - if (source != null && (source.getAbilities().containsKey(InfectAbility.getInstance().getId()))) { + if (sourceAbilities != null && sourceAbilities.containsKey(InfectAbility.getInstance().getId())) { addCounters(CounterType.POISON.createInstance(actualDamage), game); } else { GameEvent damageToLifeLossEvent = new GameEvent(EventType.DAMAGE_CAUSES_LIFE_LOSS, playerId, sourceId, playerId, actualDamage, combatDamage); @@ -1632,7 +1643,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.loseLife(damageToLifeLossEvent.getAmount(), game); } } - if (source != null && source.getAbilities().containsKey(LifelinkAbility.getInstance().getId())) { + if (sourceAbilities != null && sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) { Player player = game.getPlayer(sourceControllerId); player.gainLife(actualDamage, game); }