[DSK] Implement Unable to Scream (#13234)

* Introduced LoseAllAbilitiesAttachedEffect

* Added unit tests for Unable to Scream
This commit is contained in:
Marco Romano 2025-01-19 21:09:57 +00:00 committed by GitHub
parent 02599fbaa4
commit 0bcf5f9e03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 301 additions and 0 deletions

View file

@ -0,0 +1,95 @@
package mage.cards.u;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect;
import mage.abilities.effects.common.continuous.AddCardTypeAttachedEffect;
import mage.abilities.effects.common.continuous.LoseAllAbilitiesAttachedEffect;
import mage.abilities.effects.common.continuous.SetBasePowerToughnessEnchantedEffect;
import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
* @author markort147
*/
public final class UnableToScream extends CardImpl {
public UnableToScream(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}");
this.subtype.add(SubType.AURA);
// Enchant creature
TargetPermanent auraTarget = new TargetCreaturePermanent();
this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment));
this.addAbility(new EnchantAbility(auraTarget));
// Enchanted creature loses all abilities and is a Toy artifact creature with base power and toughness 0/2 in addition to its other types.
Ability ability = new SimpleStaticAbility(new LoseAllAbilitiesAttachedEffect(AttachmentType.AURA));
ability.addEffect(new AddCardSubtypeAttachedEffect(SubType.TOY, AttachmentType.AURA).setText(" and is a Toy"));
ability.addEffect(new AddCardTypeAttachedEffect(CardType.ARTIFACT, AttachmentType.AURA).setText(" artifact creature"));
ability.addEffect(new SetBasePowerToughnessEnchantedEffect(0, 2).setText(" with base power and toughness 0/2 in addition to its other types."));
this.addAbility(ability);
// As long as enchanted creature is face down, it can't be turned face up.
this.addAbility(new SimpleStaticAbility(new UnableToScreamPreventingEffect()));
}
private UnableToScream(final UnableToScream card) {
super(card);
}
@Override
public UnableToScream copy() {
return new UnableToScream(this);
}
}
class UnableToScreamPreventingEffect extends ContinuousRuleModifyingEffectImpl {
UnableToScreamPreventingEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
setText("As long as enchanted creature is face down, it can't be turned face up.");
}
private UnableToScreamPreventingEffect(final UnableToScreamPreventingEffect effect) {
super(effect);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType().equals(GameEvent.EventType.TURN_FACE_UP);
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Permanent aura = game.getPermanent(source.getSourceId());
if (aura == null) {
return false;
}
Permanent creature = game.getPermanent(aura.getAttachedTo());
if (creature == null) {
return false;
}
return creature.isFaceDown(game) && event.getTargetId().equals(creature.getId());
}
@Override
public UnableToScreamPreventingEffect copy() {
return new UnableToScreamPreventingEffect(this);
}
}

View file

@ -241,6 +241,7 @@ public final class DuskmournHouseOfHorror extends ExpansionSet {
cards.add(new SetCardInfo("Twist Reality", 77, Rarity.COMMON, mage.cards.t.TwistReality.class));
cards.add(new SetCardInfo("Twitching Doll", 201, Rarity.RARE, mage.cards.t.TwitchingDoll.class));
cards.add(new SetCardInfo("Tyvar, the Pummeler", 202, Rarity.MYTHIC, mage.cards.t.TyvarThePummeler.class));
cards.add(new SetCardInfo("Unable to Scream", 78, Rarity.COMMON, mage.cards.u.UnableToScream.class));
cards.add(new SetCardInfo("Under the Skin", 203, Rarity.UNCOMMON, mage.cards.u.UnderTheSkin.class));
cards.add(new SetCardInfo("Unidentified Hovership", 37, Rarity.RARE, mage.cards.u.UnidentifiedHovership.class));
cards.add(new SetCardInfo("Unnerving Grasp", 80, Rarity.UNCOMMON, mage.cards.u.UnnervingGrasp.class));

View file

@ -0,0 +1,154 @@
package org.mage.test.cards.single.dsk;
import mage.abilities.Ability;
import mage.abilities.mana.GreenManaAbility;
import mage.cards.Card;
import mage.constants.*;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.stream.Collectors;
/**
* @author markort147
*/
public class UnableToScreamTest extends CardTestPlayerBase {
/*
* Unable to Scream {U}
* Enchantment - Aura
* Enchant creature
* Enchanted creature loses all abilities and is a Toy artifact creature with base power and toughness 0/2 in addition to its other types.
* As long as enchanted creature is face down, it can't be turned face up.
*/
public static final String UNABLE_TO_SCREAM = "Unable to Scream";
/*
* Appetite for the Unnatural {2}{G}
* Instant
* Destroy target artifact or enchantment. You gain 2 life.
*/
public static final String APPETITE_FOR_THE_UNNATURAL = "Appetite for the Unnatural";
// Cast a spell that would turn the creature face up, but Unable to Scream negates the effect.
// After removing Unable to Scream, cast the spell again and the effect applies correctly.
@Test
public void preventFromTurningFaceUpTest() {
/*
* Akroma, Angel of Fury {5}{R}{R}{R}
* Creature - Legendary Angel
* 6/6
* Akroma, Angel of Fury can't be countered.
* Flying
* Trample
* Protection from white and from blue.
* {R}: Akroma, Angel of Fury gets +1/+0 until end of turn.
* Morph {3}{R}{R}{R}
*/
final String akromaAngelOfFury = "Akroma, Angel of Fury";
/*
* Break Open {1}{R}
* Instant
* Turn target face-down creature an opponent controls face up.
*/
final String breakOpen = "Break Open";
setStrictChooseMode(true);
addCard(Zone.HAND, playerB, akromaAngelOfFury);
addCard(Zone.HAND, playerB, APPETITE_FOR_THE_UNNATURAL);
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 6);
addCard(Zone.BATTLEFIELD, playerB, "Forest", 3);
addCard(Zone.HAND, playerA, UNABLE_TO_SCREAM);
addCard(Zone.HAND, playerA, breakOpen, 2);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
// prepare the face down creature
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, akromaAngelOfFury + " using Morph");
// attach Unable to Scream to it
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, UNABLE_TO_SCREAM, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), true);
// cast a spell that would turn the creature face up
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, breakOpen, EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
// the creature is still face down
checkPermanentCount("Face down creature is on the battlefield", 3, PhaseStep.BEGIN_COMBAT, playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
checkPermanentCount("Akroma is not on the battlefield", 3, PhaseStep.BEGIN_COMBAT, playerB, akromaAngelOfFury, 0);
// destroy Unable to Scream
castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, APPETITE_FOR_THE_UNNATURAL, UNABLE_TO_SCREAM);
// cast a spell that would turn the creature face up
castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, breakOpen, EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
// now the creature is turned face up
checkPermanentCount("Face down creature is not on the battlefield", 5, PhaseStep.BEGIN_COMBAT, playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
checkPermanentCount("Akroma is on the battlefield", 5, PhaseStep.BEGIN_COMBAT, playerB, akromaAngelOfFury, 1);
setStopAt(5, PhaseStep.BEGIN_COMBAT);
execute();
}
// Unable to Scream changes type, subtype and base stats of a creature.
// After removing Unable to Scream, the creature restores its type, subtype, and base stats.
@Test
public void becomesEffectTest() {
/*
* Llanowar Elves {G}
* Creature - Elf Druid
* 1/1
* {T}: Add {G}.
*/
final String llanowarElves = "Llanowar Elves";
setStrictChooseMode(true);
addCard(Zone.HAND, playerB, APPETITE_FOR_THE_UNNATURAL);
addCard(Zone.BATTLEFIELD, playerB, llanowarElves);
addCard(Zone.BATTLEFIELD, playerB, "Forest", 3);
addCard(Zone.HAND, playerA, UNABLE_TO_SCREAM);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
// attach Unable to Scream to the creature
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, UNABLE_TO_SCREAM, llanowarElves);
// the creature is an artifact creature, toy elf druid with base stats 0/2
runCode("The creature is an artifact creature, toy elf druid with base stats 0/2", 1, PhaseStep.BEGIN_COMBAT, playerB,
(info, player, game) -> {
Card elves = game.getBattlefield().getAllActivePermanents().stream().filter(p -> p.getName().equals(llanowarElves)).findAny().orElse(null);
Assert.assertNotNull("The creature must be on the battlefield", elves);
Assert.assertEquals("The creature must have base power 0", 0, elves.getPower().getValue());
Assert.assertEquals("The creature must have base toughness 2", 2, elves.getToughness().getValue());
Assert.assertTrue("The creature must have lost its abilities", elves.getAbilities(game).isEmpty());
Assert.assertEquals("The creature must be an artifact creature", new HashSet<>(Arrays.asList(CardType.ARTIFACT, CardType.CREATURE)), new HashSet<>(elves.getCardType(game)));
Assert.assertEquals("The creature must be a Toy Elf Druid", new HashSet<>(Arrays.asList(SubType.TOY, SubType.ELF, SubType.DRUID)), new HashSet<>(elves.getSubtype(game)));
}
);
// destroy Unable to Scream
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, APPETITE_FOR_THE_UNNATURAL, UNABLE_TO_SCREAM);
// the creature is a creature, elf druid with base stats 1/1
runCode("The creature is creature, toy elf druid with base stats 1/1", 2, PhaseStep.BEGIN_COMBAT, playerB,
(info, player, game) -> {
Card elves = game.getBattlefield().getAllActivePermanents().stream().filter(p -> p.getName().equals(llanowarElves)).findAny().orElse(null);
Assert.assertNotNull("The creature must be on the battlefield", elves);
Assert.assertEquals("The creature must have base power 1", 1, elves.getPower().getValue());
Assert.assertEquals("The creature must have base toughness 1", 1, elves.getToughness().getValue());
Assert.assertTrue("The creature must have restored its abilities", elves.getAbilities(game).stream().map(Ability::getClass).collect(Collectors.toList()).contains(GreenManaAbility.class));
Assert.assertEquals("The creature must be just a creature", Collections.singleton(CardType.CREATURE), new HashSet<>(elves.getCardType(game)));
Assert.assertEquals("The creature must be an Elf Druid", new HashSet<>(Arrays.asList(SubType.ELF, SubType.DRUID)), new HashSet<>(elves.getSubtype(game)));
}
);
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
}
}

View file

@ -0,0 +1,51 @@
package mage.abilities.effects.common.continuous;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
* @author markort147
*/
public class LoseAllAbilitiesAttachedEffect extends ContinuousEffectImpl {
protected AttachmentType attachmentType;
public LoseAllAbilitiesAttachedEffect(AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.LoseAbility);
this.attachmentType = attachmentType;
setText();
}
protected LoseAllAbilitiesAttachedEffect(final LoseAllAbilitiesAttachedEffect effect) {
super(effect);
this.attachmentType = effect.attachmentType;
}
@Override
public LoseAllAbilitiesAttachedEffect copy() {
return new LoseAllAbilitiesAttachedEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent equipment = game.getPermanent(source.getSourceId());
if (equipment != null && equipment.getAttachedTo() != null) {
Permanent creature = game.getPermanent(equipment.getAttachedTo());
if (creature != null) {
creature.removeAllAbilities(source.getSourceId(), game);
return true;
}
}
return false;
}
private void setText() {
staticText = attachmentType.verb() + " creature loses all abilities";
}
}