diff --git a/Mage.Sets/src/mage/cards/s/Showstopper.java b/Mage.Sets/src/mage/cards/s/Showstopper.java index b4c5fc75e89..c8b70747a2e 100644 --- a/Mage.Sets/src/mage/cards/s/Showstopper.java +++ b/Mage.Sets/src/mage/cards/s/Showstopper.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -34,8 +33,6 @@ public final class Showstopper extends CardImpl { public Showstopper (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{R}"); - - // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls." TriggeredAbility ability = new DiesTriggeredAbility(new DamageTargetEffect(2, "it"), false); Target target = new TargetCreaturePermanent(filter2); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainAbilitiesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainAbilitiesTest.java new file mode 100644 index 00000000000..03a690f6965 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/GainAbilitiesTest.java @@ -0,0 +1,70 @@ +package org.mage.test.cards.abilities.other; + +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class GainAbilitiesTest extends CardTestPlayerBase { + + @Test + public void test_AttachmentSingleton() { + // {2}{W} + // Enchanted creature gets +2/+2. + // Enchanted creature has vigilance as long as you control a black or green permanent. + addCard(Zone.HAND, playerA, "Abzan Runemark@attach", 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + // + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears@bear", 1); // 2/2 + + // attach all + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.1", "@bear"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.2", "@bear"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@bear", VigilanceAbility.class, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + Permanent permanent = getPermanent("Balduvian Bears"); + Assert.assertEquals("must have only 1 singleton ability instance from two attachments", + 1, permanent.getAbilities(currentGame).stream().filter(a -> a instanceof VigilanceAbility).count()); + } + + @Test + public void test_AttachmentUnique() { + // {R} + // Enchanted creature has "{R}, {T}, Discard a card: Draw a card." + addCard(Zone.HAND, playerA, "Epiphany Storm@attach", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears@bear", 1); // 2/2 + + // attach all + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.1", "@bear"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.2", "@bear"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + //checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@bear", VigilanceAbility.class, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + Permanent permanent = getPermanent("Balduvian Bears"); + Assert.assertEquals("must have 2 dynamic ability instances from two attachments", + 2, permanent.getAbilities(currentGame).stream().filter( + a -> a.getEffects().stream().anyMatch(e -> e instanceof DrawCardSourceControllerEffect) + ).count()); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java index 224c316f9a5..d011d952b30 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/LandTypeChangingEffectsTest.java @@ -14,20 +14,17 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { /** - * * Playing a commander game. Opponent had a Magus of the Moon, and I later * dropped a Chromatic Lantern. - * + *

* I was not allowed to use the Chromatic Lantern's ability. Since layers * are tricky I asked on the Judge's chat to confirm and the user "luma" * said it should work on this scenario. - * */ @Test public void testMagusOfTheMoonAndChromaticLantern() { @@ -42,8 +39,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Chromatic Lantern"); + setStrictChooseMode(true); setStopAt(2, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerB, "Chromatic Lantern", 1); @@ -66,8 +65,11 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Chromatic Lantern"); castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Magus of the Moon"); + + setStrictChooseMode(true); setStopAt(3, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerB, "Chromatic Lantern", 1); assertPermanentCount(playerA, "Magus of the Moon", 1); @@ -97,8 +99,11 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aquitect's Will", "Forbidding Watchtower"); activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{W}:"); + + setStrictChooseMode(true); setStopAt(2, PhaseStep.BEGIN_COMBAT); execute(); + assertAllCommandsUsed(); assertGraveyardCount(playerA, "Aquitect's Will", 1); @@ -128,8 +133,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bloodmoon); playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, urborgtoy); + setStrictChooseMode(true); setStopAt(2, PhaseStep.PRECOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, bloodmoon, 1); assertPermanentCount(playerA, urborgtoy, 1); @@ -157,8 +164,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, urborgtoy); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, bloodmoon); + setStrictChooseMode(true); setStopAt(2, PhaseStep.PRECOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, bloodmoon, 1); assertPermanentCount(playerA, urborgtoy, 1); @@ -176,7 +185,7 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { In terms of time-stamp order, Urborg was down first, then Kormus Bell, then Quicksilver. When I put a flood counter on a basic swamp, it would become a 0/0 instead of a 1/1 and die. */ - + @Test public void testCormusBellAfterUrborg() { // Land - Legendary @@ -198,14 +207,14 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Kormus Bell"); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Quicksilver Fountain"); - + addTarget(playerA, "Mountain"); - + setStrictChooseMode(true); setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); assertAllCommandsUsed(); - + assertPermanentCount(playerA, urborgtoy, 1); assertPermanentCount(playerA, "Kormus Bell", 1); assertPermanentCount(playerB, "Quicksilver Fountain", 1); @@ -245,8 +254,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Stormtide Leviathan"); // all lands are islands in addition to their other types addCard(Zone.BATTLEFIELD, playerA, "Darksteel Citadel"); // land has indestructible ability + setStrictChooseMode(true); setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); + assertAllCommandsUsed(); Permanent darksteel = getPermanent("Darksteel Citadel", playerA.getId()); Assert.assertNotNull(darksteel); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java index 0363ca6c1ec..a8302a41b64 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/planeswalker/GideonTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.planeswalker; import mage.abilities.keyword.IndestructibleAbility; @@ -11,7 +10,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class GideonTest extends CardTestPlayerBase { @@ -116,16 +114,23 @@ public class GideonTest extends CardTestPlayerBase { // Equip {2} addCard(Zone.BATTLEFIELD, playerB, "Stitcher's Graft", 1); + // transform attack(2, playerB, "Kytheon, Hero of Akros"); attack(2, playerB, "Silvercoat Lion"); attack(2, playerB, "Pillarfield Ox"); + checkPermanentCount("after transform", 2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Gideon, Battle-Forged", 1); + // become creature and equip activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerB, "0: Until "); + waitStackResolved(4, PhaseStep.PRECOMBAT_MAIN); activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Equip {2}", "Gideon, Battle-Forged"); + attack(4, playerB, "Gideon, Battle-Forged"); // 7 damage + setStrictChooseMode(true); setStopAt(5, PhaseStep.PRECOMBAT_MAIN); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerB, "Silvercoat Lion", 1); assertLife(playerA, 7); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java index fbbbcf42754..23e9e34c1e3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ShowstopperTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.triggers.dies; import mage.constants.PhaseStep; @@ -7,7 +6,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ @@ -16,10 +14,9 @@ public class ShowstopperTest extends CardTestPlayerBase { /** * Tests that the dies triggered ability of silvercoat lion (gained by Showstopper) * triggers as he dies from Lightning Bolt - * */ @Test - public void testDiesTriggeredAbility() { + public void test_OneTrigger() { // Showstopper Instant {1}{B}{R} // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls." addCard(Zone.HAND, playerA, "Showstopper"); @@ -30,10 +27,14 @@ public class ShowstopperTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Ornithopter", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion"); + addTarget(playerA, "Ornithopter"); + + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); @@ -43,12 +44,13 @@ public class ShowstopperTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Ornithopter", 1); } + /** * Test if Showstopper is called twice */ @Test - public void testTwoDiesTriggeredAbilities() { + public void test_TwoTriggers() { // Showstopper Instant {1}{B}{R} // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls." addCard(Zone.HAND, playerA, "Showstopper", 2); @@ -62,12 +64,16 @@ public class ShowstopperTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion"); + setChoice(playerA, "When {this} dies"); // choose from two triggers addTarget(playerA, "Ornithopter"); addTarget(playerA, "Grizzly Bears"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertLife(playerA, 20); assertLife(playerB, 20); @@ -79,4 +85,65 @@ public class ShowstopperTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Ornithopter", 1); } + @Test + public void test_TwoTriggersAndCopies() { + // Showstopper Instant {1}{B}{R} + // Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls." + addCard(Zone.HAND, playerA, "Showstopper", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + addCard(Zone.HAND, playerB, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerB, "Ornithopter", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Alchemist's Apprentice", 1); + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton", 1); + // + // When you next cast an instant spell, cast a sorcery spell, or activate a loyalty ability this turn, copy that spell or ability twice. + // You may choose new targets for the copies. + addCard(Zone.HAND, playerA, "Repeated Reverberation", 1); // {2}{R}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + + // first spell + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 2); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper"); + + // prepare copy + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 4); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Repeated Reverberation"); + + // second spell with 2x copy + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion"); + setChoice(playerA, "When {this} dies"); // choose from 4 triggers + setChoice(playerA, "When {this} dies"); // choose from 4 triggers + setChoice(playerA, "When {this} dies"); // choose from 4 triggers + addTarget(playerA, "Ornithopter"); + addTarget(playerA, "Grizzly Bears"); + addTarget(playerA, "Alchemist's Apprentice"); + addTarget(playerA, "Augmenting Automaton"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Showstopper", 2); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + + assertGraveyardCount(playerB, "Grizzly Bears", 1); + assertGraveyardCount(playerB, "Ornithopter", 1); + assertGraveyardCount(playerB, "Alchemist's Apprentice", 1); + assertGraveyardCount(playerB, "Augmenting Automaton", 1); + } + } \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index d0ec027be5f..f32f87e537b 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -57,7 +57,7 @@ public class ContinuousEffects implements Serializable { // private final PlaneswalkerRedirectionEffect planeswalkerRedirectionEffect; private final AuraReplacementEffect auraReplacementEffect; - private final List previous = new ArrayList<>(); + private final Map> lastEffectsListOnLayer = new HashMap<>(); // helps to find out new effect timestamps // note all effect/abilities that were only added temporary private final Map> temporaryEffects = new HashMap<>(); @@ -178,6 +178,16 @@ public class ContinuousEffects implements Serializable { } public synchronized List getLayeredEffects(Game game) { + return getLayeredEffects(game, "main"); + } + + /** + * Return effects list ordered by timestamps (timestamps are automaticity generates from new/old lists on same layer) + * + * @param timestampGroupName workaround to fix broken timestamps on effect's add/remove between different layers + * @return effects list ordered by timestamp + */ + public synchronized List getLayeredEffects(Game game, String timestampGroupName) { List layerEffects = new ArrayList<>(); for (ContinuousEffect effect : layeredEffects) { switch (effect.getDuration()) { @@ -202,9 +212,14 @@ public class ContinuousEffects implements Serializable { } } - updateTimestamps(layerEffects); + updateTimestamps(timestampGroupName, layerEffects); + layerEffects.sort(Comparator.comparingLong(ContinuousEffect::getOrder)); + /* debug effects apply order: + if (game.getStep() != null) System.out.println("layr - " + game.getTurnNum() + "." + game.getStep().getType() + ": layers " + layerEffects.size() + + " - " + layerEffects.stream().map(l -> l.getClass().getSimpleName()).collect(Collectors.joining(", ")) + + " - " + callName); + //*/ - Collections.sort(layerEffects, Comparator.comparingLong(ContinuousEffect::getOrder)); return layerEffects; } @@ -215,17 +230,23 @@ public class ContinuousEffects implements Serializable { * Ability.#isInUseableZone(Game, boolean) method in * #getLayeredEffects(Game). * + * It must be called with different timestamp group name (otherwise sort order will be changed for add/remove effects, see Urborg and Bloodmoon test) + * * @param layerEffects */ - private synchronized void updateTimestamps(List layerEffects) { + private synchronized void updateTimestamps(String timestampGroupName, List layerEffects) { + if (!lastEffectsListOnLayer.containsKey(timestampGroupName)) { + lastEffectsListOnLayer.put(timestampGroupName, new ArrayList<>()); + } + List prevs = lastEffectsListOnLayer.get(timestampGroupName); for (ContinuousEffect continuousEffect : layerEffects) { // check if it's new, then set order - if (!previous.contains(continuousEffect)) { + if (!prevs.contains(continuousEffect)) { setOrder(continuousEffect); } } - previous.clear(); - previous.addAll(layerEffects); + prevs.clear(); + prevs.addAll(layerEffects); } public void setOrder(ContinuousEffect effect) { @@ -425,12 +446,12 @@ public class ContinuousEffects implements Serializable { return false; } boolean exists = true; - if (!object.getAbilities().contains(ability)) { + if (!object.hasAbility(ability, game)) { exists = false; if (object instanceof PermanentCard) { PermanentCard permanent = (PermanentCard) object; if (permanent.isTransformable() && event.getType() == GameEvent.EventType.TRANSFORMED) { - exists = permanent.getCard().getAbilities().contains(ability); + exists = permanent.getCard().hasAbility(ability, game); } } } else if (object instanceof PermanentCard) { @@ -903,7 +924,7 @@ public class ContinuousEffects implements Serializable { //20091005 - 613 public synchronized void apply(Game game) { removeInactiveEffects(game); - List activeLayerEffects = getLayeredEffects(game); + List activeLayerEffects = getLayeredEffects(game); // main call List layer = filterLayeredEffects(activeLayerEffects, Layer.CopyEffects_1); for (ContinuousEffect effect : layer) { @@ -914,7 +935,7 @@ public class ContinuousEffects implements Serializable { } //Reload layerEffect if copy effects were applied if (!layer.isEmpty()) { - activeLayerEffects = getLayeredEffects(game); + activeLayerEffects = getLayeredEffects(game, "layer_1"); } layer = filterLayeredEffects(activeLayerEffects, Layer.ControlChangingEffects_2); @@ -936,16 +957,16 @@ public class ContinuousEffects implements Serializable { game.getBattlefield().resetPermanentsControl(); } - applyLayer(activeLayerEffects, Layer.TextChangingEffects_3, game); - applyLayer(activeLayerEffects, Layer.TypeChangingEffects_4, game); - applyLayer(activeLayerEffects, Layer.ColorChangingEffects_5, game); + applyLayer(activeLayerEffects, Layer.TextChangingEffects_3, game, "layer_3"); + applyLayer(activeLayerEffects, Layer.TypeChangingEffects_4, game, "layer_4"); + applyLayer(activeLayerEffects, Layer.ColorChangingEffects_5, game, "layer_5"); Map> appliedEffectAbilities = new HashMap<>(); boolean done = false; Map> waitingEffects = new LinkedHashMap<>(); Set appliedEffects = new HashSet<>(); applyCounters.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, null, game); - activeLayerEffects = getLayeredEffects(game); + activeLayerEffects = getLayeredEffects(game, "layer_6"); while (!done) { // loop needed if a added effect adds again an effect (e.g. Level 5- of Joraga Treespeaker) done = true; @@ -1000,7 +1021,7 @@ public class ContinuousEffects implements Serializable { effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game); done = false; // list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities) - activeLayerEffects = getLayeredEffects(game); + activeLayerEffects = getLayeredEffects(game, "apply"); } appliedEffects.add(effect.getId()); @@ -1027,7 +1048,7 @@ public class ContinuousEffects implements Serializable { entry.getKey().apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game); done = false; // list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities) - activeLayerEffects = getLayeredEffects(game); + activeLayerEffects = getLayeredEffects(game, "apply"); } appliedEffects.add(entry.getKey().getId()); iterator.remove(); @@ -1083,10 +1104,10 @@ public class ContinuousEffects implements Serializable { private boolean abilityActive(Ability ability, Game game) { MageObject object = game.getObject(ability.getSourceId()); - return object != null && object.hasAbility(ability.getId(), game); + return object != null && object.hasAbility(ability, game); } - private void applyLayer(List activeLayerEffects, Layer currentLayer, Game game) { + private void applyLayer(List activeLayerEffects, Layer currentLayer, Game game, String timestampGroupName) { List layer = filterLayeredEffects(activeLayerEffects, currentLayer); // layer is a list of all effects at the current layer if (!layer.isEmpty()) { @@ -1109,7 +1130,7 @@ public class ContinuousEffects implements Serializable { applyContinuousEffect(effect, currentLayer, game); // add it to the applied effects list appliedEffects.add(effect.getId()); - layer = getLayeredEffects(game); + layer = getLayeredEffects(game, timestampGroupName); // check waiting effects to see if it has anything to check if (!waitingEffects.isEmpty()) { @@ -1120,7 +1141,7 @@ public class ContinuousEffects implements Serializable { applyContinuousEffect(entry.getKey(), currentLayer, game); // add it to the applied effects list appliedEffects.add(entry.getKey().getId()); - layer = getLayeredEffects(game); + layer = getLayeredEffects(game, timestampGroupName); } } } @@ -1131,7 +1152,7 @@ public class ContinuousEffects implements Serializable { applyContinuousEffect(entry.getKey(), currentLayer, game); // add it to the applied effects list appliedEffects.add(entry.getKey().getId()); - layer = getLayeredEffects(game); + layer = getLayeredEffects(game, timestampGroupName); } } } @@ -1154,7 +1175,7 @@ public class ContinuousEffects implements Serializable { if (!(effect instanceof BecomesFaceDownCreatureEffect) && (effect != null && !effect.getDuration().equals(Duration.Custom))) { // Custom effects do not depend on the creating permanent if (card != null) { - return card.getAbilities(game).contains(ability); + return card.hasAbility(ability, game); } }