[WHO] Implement Weeping Angel (#12236)

This commit is contained in:
jimga150 2024-05-20 23:51:48 -04:00 committed by GitHub
parent 1b5f8ce1af
commit f8a159839e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 213 additions and 0 deletions

View file

@ -0,0 +1,151 @@
package mage.cards.w;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.common.SpellCastOpponentTriggeredAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.PreventionEffectImpl;
import mage.cards.Card;
import mage.constants.*;
import mage.abilities.keyword.FlashAbility;
import mage.abilities.keyword.FirstStrikeAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.DamagePermanentEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author jimga150
*/
public final class WeepingAngel extends CardImpl {
public WeepingAngel(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{U}{B}");
this.subtype.add(SubType.ALIEN);
this.subtype.add(SubType.ANGEL);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Flash
this.addAbility(FlashAbility.getInstance());
// First strike
this.addAbility(FirstStrikeAbility.getInstance());
// Vigilance
this.addAbility(VigilanceAbility.getInstance());
// Whenever an opponent casts a creature spell, Weeping Angel isn't a creature until end of turn.
this.addAbility(new SpellCastOpponentTriggeredAbility(
new WeepingAngelMarbleizeEffect(), StaticFilters.FILTER_SPELL_A_CREATURE, false
));
// If Weeping Angel would deal combat damage to a creature, prevent that damage and that creature's owner shuffles it into their library.
this.addAbility(new SimpleStaticAbility(new WeepingAngelDamageEffect()));
}
private WeepingAngel(final WeepingAngel card) {
super(card);
}
@Override
public WeepingAngel copy() {
return new WeepingAngel(this);
}
}
// Adapted from LoseCreatureTypeSourceEffect
class WeepingAngelMarbleizeEffect extends ContinuousEffectImpl {
WeepingAngelMarbleizeEffect() {
super(Duration.EndOfTurn, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment);
staticText = "{this} isn't a creature until end of turn.";
}
private WeepingAngelMarbleizeEffect(final WeepingAngelMarbleizeEffect effect) {
super(effect);
}
@Override
public WeepingAngelMarbleizeEffect copy() {
return new WeepingAngelMarbleizeEffect(this);
}
@Override
public void init(Ability source, Game game) {
super.init(source, game);
if (duration.isOnlyValidIfNoZoneChange()) {
// If source permanent is no longer onto battlefield discard the effect
if (source.getSourcePermanentIfItStillExists(game) == null) {
discard();
}
}
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
return false;
}
permanent.removeCardType(game, CardType.CREATURE);
if (!permanent.isTribal(game)) {
permanent.removeAllCreatureTypes(game);
}
if (permanent.isAttacking() || permanent.getBlocking() > 0) {
permanent.removeFromCombat(game);
}
return true;
}
}
// Based on PreventDamageAndRemoveCountersEffect
class WeepingAngelDamageEffect extends PreventionEffectImpl {
WeepingAngelDamageEffect() {
super(Duration.WhileOnBattlefield, Integer.MAX_VALUE, true, false);
staticText = "If {this} would deal combat damage to a creature, " +
"prevent that damage and that creature's owner shuffles it into their library.";
}
private WeepingAngelDamageEffect(final WeepingAngelDamageEffect effect) {
super(effect);
}
@Override
public WeepingAngelDamageEffect copy() {
return new WeepingAngelDamageEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
game.preventDamage(event, source, game, Integer.MAX_VALUE);
Card card = game.getPermanent(event.getTargetId());
if (card == null) {
return false;
}
Player owner = game.getPlayer(card.getOwnerId());
if (owner != null) {
owner.shuffleCardsToLibrary(card, game, source);
}
return false;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null || !permanent.isCreature(game)){
return false;
}
return event.getSourceId().equals(source.getSourceId()) && ((DamagePermanentEvent) event).isCombatDamage();
}
}

View file

@ -284,6 +284,7 @@ public final class DoctorWho extends ExpansionSet {
cards.add(new SetCardInfo("Waterlogged Grove", 331, Rarity.RARE, mage.cards.w.WaterloggedGrove.class));
cards.add(new SetCardInfo("Wayfarer's Bauble", 256, Rarity.COMMON, mage.cards.w.WayfarersBauble.class));
cards.add(new SetCardInfo("Wedding Ring", 213, Rarity.MYTHIC, mage.cards.w.WeddingRing.class));
cards.add(new SetCardInfo("Weeping Angel", 168, Rarity.RARE, mage.cards.w.WeepingAngel.class));
cards.add(new SetCardInfo("Wibbly-wobbly, Timey-wimey", 62, Rarity.COMMON, mage.cards.w.WibblyWobblyTimeyWimey.class));
cards.add(new SetCardInfo("Wound Reflection", 223, Rarity.RARE, mage.cards.w.WoundReflection.class));
cards.add(new SetCardInfo("Wreck and Rebuild", 169, Rarity.UNCOMMON, mage.cards.w.WreckAndRebuild.class));

View file

@ -0,0 +1,61 @@
package org.mage.test.cards.single.who;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author jimga150
*/
public class WeepingAngelTests extends CardTestPlayerBase {
@Test
public void testCreatureTypeLoss() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Weeping Angel");
addCard(Zone.HAND, playerB, "Memnite");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Memnite", true);
setStrictChooseMode(true);
setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute();
assertNotType("Weeping Angel", CardType.CREATURE);
}
@Test
public void testCreatureTypeRegain() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Weeping Angel");
addCard(Zone.HAND, playerB, "Memnite");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Memnite", true);
setStrictChooseMode(true);
setStopAt(3, PhaseStep.UPKEEP);
execute();
assertType("Weeping Angel", CardType.CREATURE, true);
}
@Test
public void testDamageEffect() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Weeping Angel");
addCard(Zone.BATTLEFIELD, playerB, "Impervious Greatwurm");
attack(1, playerA, "Weeping Angel", playerB);
block(1, playerB, "Impervious Greatwurm", "Weeping Angel");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertLibraryCount(playerB, "Impervious Greatwurm", 1);
assertPermanentCount(playerB, "Impervious Greatwurm", 0);
assertPermanentCount(playerA, "Weeping Angel", 1);
}
}