Ability refactor: fixed broken effects timestamp in some use cases

This commit is contained in:
Oleg Agafonov 2020-05-28 23:07:36 +04:00
parent f3f1c29926
commit d63a3e88ce
6 changed files with 213 additions and 42 deletions

View file

@ -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);

View file

@ -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());
}
}

View file

@ -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.
*
* <p>
* 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);

View file

@ -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);

View file

@ -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);
}
}

View file

@ -57,7 +57,7 @@ public class ContinuousEffects implements Serializable {
// private final PlaneswalkerRedirectionEffect planeswalkerRedirectionEffect;
private final AuraReplacementEffect auraReplacementEffect;
private final List<ContinuousEffect> previous = new ArrayList<>();
private final Map<String, List<ContinuousEffect>> lastEffectsListOnLayer = new HashMap<>(); // helps to find out new effect timestamps
// note all effect/abilities that were only added temporary
private final Map<ContinuousEffect, Set<Ability>> temporaryEffects = new HashMap<>();
@ -178,6 +178,16 @@ public class ContinuousEffects implements Serializable {
}
public synchronized List<ContinuousEffect> 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<ContinuousEffect> getLayeredEffects(Game game, String timestampGroupName) {
List<ContinuousEffect> 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<ContinuousEffect> layerEffects) {
private synchronized void updateTimestamps(String timestampGroupName, List<ContinuousEffect> layerEffects) {
if (!lastEffectsListOnLayer.containsKey(timestampGroupName)) {
lastEffectsListOnLayer.put(timestampGroupName, new ArrayList<>());
}
List<ContinuousEffect> 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<ContinuousEffect> activeLayerEffects = getLayeredEffects(game);
List<ContinuousEffect> activeLayerEffects = getLayeredEffects(game); // main call
List<ContinuousEffect> 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<ContinuousEffect, List<Ability>> appliedEffectAbilities = new HashMap<>();
boolean done = false;
Map<ContinuousEffect, Set<UUID>> waitingEffects = new LinkedHashMap<>();
Set<UUID> 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<ContinuousEffect> activeLayerEffects, Layer currentLayer, Game game) {
private void applyLayer(List<ContinuousEffect> activeLayerEffects, Layer currentLayer, Game game, String timestampGroupName) {
List<ContinuousEffect> 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);
}
}