Skip to content

Commit

Permalink
[DSK] Implement Unable to Scream (#13234)
Browse files Browse the repository at this point in the history
* Introduced LoseAllAbilitiesAttachedEffect

* Added unit tests for Unable to Scream
  • Loading branch information
markort147 authored Jan 19, 2025
1 parent 02599fb commit 0bcf5f9
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 0 deletions.
95 changes: 95 additions & 0 deletions Mage.Sets/src/mage/cards/u/UnableToScream.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
1 change: 1 addition & 0 deletions Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ private DuskmournHouseOfHorror() {
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));
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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";
}

}

0 comments on commit 0bcf5f9

Please sign in to comment.