-
Notifications
You must be signed in to change notification settings - Fork 785
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
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
5ed28d6
[DSK] Implement Unable to Scream
markort147 f2916db
Introduced LoseAllAbilitiesAttachedEffect
markort147 2f85026
Fixed Unable to Scream abilities
markort147 be21701
Added unit tests for Unable to Scream
markort147 ce74783
Improved implementation and tests of Unable to Scream
markort147 3604855
UnableToScream.java: made copy method covariant
markort147 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
154 changes: 154 additions & 0 deletions
154
Mage.Tests/src/test/java/org/mage/test/cards/single/dsk/UnableToScreamTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
|
||
/* | ||
* 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(); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
...rc/main/java/mage/abilities/effects/common/continuous/LoseAllAbilitiesAttachedEffect.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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