* Commander: fixed duplicated triggers after play (example: Esika, God of the Tree, see #7501, #7503, #7505);

This commit is contained in:
Oleg Agafonov 2021-02-06 10:51:41 +04:00
parent aa13b06af9
commit ac98a3a31a
6 changed files with 108 additions and 19 deletions

View file

@ -57,9 +57,7 @@ public final class EsikaGodOfTheTree extends ModalDoubleFacesCard {
// Legendary Enchantment
this.getRightHalfCard().addSuperType(SuperType.LEGENDARY);
// At the beginning of your upkeep, reveal cards from the top of your library until you reveal
// a creature or planeswalker card. Put that card onto the battlefield and the rest
// on the bottom of your library in a random order.
// At the beginning of your upkeep, reveal cards from the top of your library until you reveal a creature or planeswalker card. Put that card onto the battlefield and the rest on the bottom of your library in a random order.
this.getRightHalfCard().addAbility(new BeginningOfUpkeepTriggeredAbility(
Zone.BATTLEFIELD, new PrismaticBridgeEffect(), TargetController.YOU, false, false
));

View file

@ -0,0 +1,75 @@
package org.mage.test.cards.cost.modaldoublefaces;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestCommanderDuelBase;
/**
* @author JayDi85
*/
public class ModalDoubleFacesCardsInCommanderTest extends CardTestCommanderDuelBase {
@Test
public void test_Triggers_MustAddTriggersOneTimeOnly() {
// possible bug: duplicated triggers from same card
// https://github.com/magefree/mage/issues/7501
removeAllCardsFromHand(playerA);
removeAllCardsFromLibrary(playerA);
skipInitShuffling();
// Esika, God of the Tree
// creature, 1/4
// Landfall Whenever a land enters the battlefield under your control, Kazandu Mammoth gets +2/+2 until end of turn.
//
// The Prismatic Bridge
// Enchantment
// At the beginning of your upkeep, reveal cards from the top of your library until you reveal a creature or
// planeswalker card. Put that card onto the battlefield and the rest on the bottom of your library in a random order.
addCard(Zone.COMMAND, playerA, "Esika, God of the Tree"); // {W}{U}{B}{R}{G}
addCard(Zone.BATTLEFIELD, playerA, "Plains");
addCard(Zone.BATTLEFIELD, playerA, "Island");
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.BATTLEFIELD, playerA, "Forest");
//
// last goes to top library
addCard(Zone.LIBRARY, playerA, "Forest");
addCard(Zone.LIBRARY, playerA, "Grizzly Bears");
addCard(Zone.LIBRARY, playerA, "Forest");
addCard(Zone.LIBRARY, playerA, "Grizzly Bears");
addCard(Zone.LIBRARY, playerA, "Forest");
addCard(Zone.LIBRARY, playerA, "Grizzly Bears");
addCard(Zone.LIBRARY, playerA, "Forest");
addCard(Zone.LIBRARY, playerA, "Grizzly Bears");
addCard(Zone.LIBRARY, playerA, "Forest");
addCard(Zone.LIBRARY, playerA, "Grizzly Bears");
addCard(Zone.LIBRARY, playerA, "Forest");
//
// Exile target artifact or enchantment.
addCard(Zone.HAND, playerB, "Ironwright's Cleansing"); // {2}{W}
addCard(Zone.BATTLEFIELD, playerB, "Plains");
// prepare mdf
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "The Prismatic Bridge");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "The Prismatic Bridge", 1);
checkLibraryCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 5);
// possible bug: you can catch choose dialog for duplicated upkeep triggers
// turn 3, first upkeep and bear move
checkLibraryCount("after upkeep 1", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 5 - 1);
checkPermanentCount("after upkeep 1", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1);
// turn 5, second upkeep and bear move
checkLibraryCount("after upkeep 2", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 5 - 2);
checkPermanentCount("after upkeep 2", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 2);
setStrictChooseMode(true);
setStopAt(5, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
}
}

View file

@ -925,7 +925,7 @@ public class TestPlayer implements Player {
// show library
if (params[0].equals(SHOW_COMMAND_LIBRARY) && params.length == 1) {
printStart(action.getActionName());
printCards(computerPlayer.getLibrary().getCards(game));
printCards(computerPlayer.getLibrary().getCards(game), false); // do not sort
printEnd();
actions.remove(action);
wasProccessed = true;
@ -973,7 +973,7 @@ public class TestPlayer implements Player {
printStart(action.getActionName());
printCards(game.getExile().getAllCards(game).stream()
.filter(card -> card.isOwnedBy(computerPlayer.getId()))
.collect(Collectors.toList()));
.collect(Collectors.toList()), true);
printEnd();
actions.remove(action);
wasProccessed = true;
@ -1108,16 +1108,23 @@ public class TestPlayer implements Player {
}
private void printCards(Set<Card> cards) {
printCards(new ArrayList<>(cards));
printCards(new ArrayList<>(cards), true);
}
private void printCards(List<Card> cards) {
private void printCards(List<Card> cards, boolean sorted) {
System.out.println("Total cards: " + cards.size());
List<String> data = cards.stream()
.map(Card::getIdName)
.sorted()
.collect(Collectors.toList());
List<String> data;
if (sorted) {
data = cards.stream()
.map(Card::getIdName)
.sorted()
.collect(Collectors.toList());
} else {
data = cards.stream()
.map(Card::getIdName)
.collect(Collectors.toList());
}
for (String s : data) {
System.out.println(s);
@ -1365,7 +1372,7 @@ public class TestPlayer implements Player {
if (foundCount != count) {
printStart("Exile cards");
printCards(game.getExile().getAllCards(game));
printCards(game.getExile().getAllCards(game), true);
printEnd();
Assert.fail(action.getActionName() + " - exile zone must have " + count + " cards with name " + permanentName + ", but found " + foundCount);
}

View file

@ -598,6 +598,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
/**
* Add a card to specified zone of specified player.
* <p>
* Zone.LIBRARY - adding by put on top (e.g. last added card goes to top of the library)
*
* @param gameZone {@link mage.constants.Zone} to add cards to.
* @param player {@link Player} to add cards for. Use either playerA or

View file

@ -630,10 +630,6 @@ public class GameState implements Serializable, Copyable<GameState> {
}
}
private void addTrigger(TriggeredAbility ability, MageObject attachedTo) {
addTrigger(ability, null, attachedTo);
}
private void addTrigger(TriggeredAbility ability, UUID sourceId, MageObject attachedTo) {
if (sourceId == null) {
triggers.add(ability, attachedTo);

View file

@ -73,10 +73,21 @@ public class Commander implements CommandObject {
// other abilities
for (Ability ability : card.getAbilities()) {
if (!(ability instanceof SpellAbility) && !(ability instanceof PlayLandAbility)) {
Ability newAbility = ability.copy();
abilities.add(newAbility);
// skip already added above
if (ability instanceof SpellAbility || ability instanceof PlayLandAbility) {
continue;
}
// skip triggers
// workaround to fix double triggers for commanders on battlefield (example: Esika, God of the Tree)
// TODO: is commanders on command zone can have triggers (is there a card with triggered ability in all zones)?
if (ability instanceof TriggeredAbility) {
continue;
}
// OK, can add it (example: activated, static, alternative cost, etc)
Ability newAbility = ability.copy();
abilities.add(newAbility);
}
}