Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DSK] Implement Unable to Scream #13234

Merged
merged 6 commits into from
Jan 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to use {} all the time, even for single command

}

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 @@ -240,6 +240,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";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to write related card's ability text in the test too. I prefer to write it before addCard for each test, but it can be used before card name constant too. It helps with test readability without IDE's tools or on the github website.


/*
* 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";
}

}
Loading