diff --git a/Mage.Sets/src/mage/cards/b/BaithookAngler.java b/Mage.Sets/src/mage/cards/b/BaithookAngler.java index 1c3126ea98f8..79634c2327a1 100644 --- a/Mage.Sets/src/mage/cards/b/BaithookAngler.java +++ b/Mage.Sets/src/mage/cards/b/BaithookAngler.java @@ -1,10 +1,9 @@ package mage.cards.b; -import mage.MageInt; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.keyword.DisturbAbility; -import mage.cards.CardImpl; +import mage.abilities.keyword.FlyingAbility; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; import mage.constants.SubType; @@ -13,19 +12,31 @@ /** * @author TheElk801 */ -public final class BaithookAngler extends CardImpl { +public final class BaithookAngler extends TransformingDoubleFacedCard { public BaithookAngler(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.PEASANT}, "{1}{U}", + "Hook-Haunt Drifter", + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.SPIRIT}, "U" + ); + this.getLeftHalfCard().setPT(2, 1); + this.getRightHalfCard().setPT(1, 2); - this.subtype.add(SubType.HUMAN); - this.subtype.add(SubType.PEASANT); - this.power = new MageInt(2); - this.toughness = new MageInt(1); this.secondSideCardClazz = mage.cards.h.HookHauntDrifter.class; // Disturb {1}{U} - this.addAbility(new DisturbAbility(this, "{1}{U}")); + this.getLeftHalfCard().addAbility(new DisturbAbility(this, "{1}{U}")); + + // Hook-Haunt Drifter + // Flying + this.getRightHalfCard().addAbility(FlyingAbility.getInstance()); + + // If Hook-Haunt Drifter would be put into a graveyard from anywhere, exile it instead. + this.getRightHalfCard().addAbility(DisturbAbility.makeBackAbility()); + + this.finalizeDFC(); } private BaithookAngler(final BaithookAngler card) { diff --git a/Mage.Sets/src/mage/cards/b/BelenonWarAnthem.java b/Mage.Sets/src/mage/cards/b/BelenonWarAnthem.java index 432533a985b3..11cb7ef7333c 100644 --- a/Mage.Sets/src/mage/cards/b/BelenonWarAnthem.java +++ b/Mage.Sets/src/mage/cards/b/BelenonWarAnthem.java @@ -1,11 +1,9 @@ package mage.cards.b; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.constants.Duration; import java.util.UUID; @@ -16,12 +14,8 @@ public final class BelenonWarAnthem extends CardImpl { public BelenonWarAnthem(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, ""); - - this.color.setWhite(true); this.nightCard = true; - - // Creatures you control get +1/+1. - this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield))); + TransformingDoubleFacedCard.copyToBackFace(new mage.cards.i.InvasionOfBelenon(ownerId, setInfo), this); } private BelenonWarAnthem(final BelenonWarAnthem card) { diff --git a/Mage.Sets/src/mage/cards/b/BladewheelChariot.java b/Mage.Sets/src/mage/cards/b/BladewheelChariot.java index aa6b22cf8aad..d9f916154ac3 100644 --- a/Mage.Sets/src/mage/cards/b/BladewheelChariot.java +++ b/Mage.Sets/src/mage/cards/b/BladewheelChariot.java @@ -1,20 +1,9 @@ package mage.cards.b; -import mage.MageInt; -import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.common.TapTargetCost; -import mage.abilities.effects.common.continuous.AddCardTypeSourceEffect; -import mage.abilities.keyword.CrewAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.filter.common.FilterControlledArtifactPermanent; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.filter.predicate.permanent.TappedPredicate; -import mage.target.common.TargetControlledPermanent; import java.util.UUID; @@ -23,30 +12,10 @@ */ public final class BladewheelChariot extends CardImpl { - private static final FilterControlledPermanent filter - = new FilterControlledArtifactPermanent("other untapped artifacts you control"); - - static { - filter.add(AnotherPredicate.instance); - filter.add(TappedPredicate.UNTAPPED); - } - public BladewheelChariot(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, ""); - - this.subtype.add(SubType.VEHICLE); - this.power = new MageInt(5); - this.toughness = new MageInt(5); this.nightCard = true; - this.color.setWhite(true); - - // Tap two other untapped artifacts you control: Bladewheel Chariot becomes an artifact creature until end of turn. - this.addAbility(new SimpleActivatedAbility(new AddCardTypeSourceEffect( - Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE - ).setText("{this} becomes an artifact creature until end of turn"), new TapTargetCost(new TargetControlledPermanent(2, filter)))); - - // Crew 1 - this.addAbility(new CrewAbility(1)); + TransformingDoubleFacedCard.copyToBackFace(new mage.cards.s.SpringLoadedSawblades(ownerId, setInfo), this); } private BladewheelChariot(final BladewheelChariot card) { diff --git a/Mage.Sets/src/mage/cards/c/CloisteredYouth.java b/Mage.Sets/src/mage/cards/c/CloisteredYouth.java index def252da59e3..4d245a1ab48c 100644 --- a/Mage.Sets/src/mage/cards/c/CloisteredYouth.java +++ b/Mage.Sets/src/mage/cards/c/CloisteredYouth.java @@ -1,34 +1,41 @@ - package mage.cards.c; -import java.util.UUID; - -import mage.MageInt; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; -import mage.cards.CardImpl; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** * @author Loki */ -public final class CloisteredYouth extends CardImpl { +public final class CloisteredYouth extends TransformingDoubleFacedCard { public CloisteredYouth(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); - this.subtype.add(SubType.HUMAN); - - this.power = new MageInt(1); - this.toughness = new MageInt(1); + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN}, "{1}{W}", + "Unholy Fiend", + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HORROR}, "B" + ); + this.getLeftHalfCard().setPT(1, 1); + this.getRightHalfCard().setPT(3, 3); this.secondSideCardClazz = mage.cards.u.UnholyFiend.class; // At the beginning of your upkeep, you may transform Cloistered Youth. - this.addAbility(new TransformAbility()); - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(), true)); + this.getLeftHalfCard().addAbility(new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect(), true)); + + // Unholy Fiend + // At the beginning of your end step, you lose 1 life. + this.getRightHalfCard().addAbility(new BeginningOfEndStepTriggeredAbility(new LoseLifeSourceControllerEffect(1))); + + this.finalizeDFC(); } private CloisteredYouth(final CloisteredYouth card) { diff --git a/Mage.Sets/src/mage/cards/g/GarrukRelentless.java b/Mage.Sets/src/mage/cards/g/GarrukRelentless.java index b22ae5c3b93c..2edf1a9da75f 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukRelentless.java +++ b/Mage.Sets/src/mage/cards/g/GarrukRelentless.java @@ -3,19 +3,29 @@ import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.StateTriggeredAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.keyword.TransformAbility; -import mage.cards.CardImpl; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.TrampleAbility; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.*; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.WolfToken; +import mage.game.permanent.token.WolfTokenWithDeathtouch; +import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -23,29 +33,57 @@ /** * @author nantuko */ -public final class GarrukRelentless extends CardImpl { +public final class GarrukRelentless extends TransformingDoubleFacedCard { + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE); public GarrukRelentless(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{G}"); - this.supertype.add(SuperType.LEGENDARY); - this.subtype.add(SubType.GARRUK); + super( + ownerId, setInfo, + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.PLANESWALKER}, new SubType[]{SubType.GARRUK}, "{3}{G}", + "Garruk, the Veil-Cursed", + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.PLANESWALKER}, new SubType[]{SubType.GARRUK}, "BG" + ); + this.getLeftHalfCard().setStartingLoyalty(3); this.secondSideCardClazz = mage.cards.g.GarrukTheVeilCursed.class; - this.setStartingLoyalty(3); - // When Garruk Relentless has two or fewer loyalty counters on him, transform him. - this.addAbility(new TransformAbility()); - this.addAbility(new GarrukRelentlessStateTrigger()); + this.getLeftHalfCard().addAbility(new GarrukRelentlessStateTrigger()); // 0: Garruk Relentless deals 3 damage to target creature. That creature deals damage equal to its power to him Ability ability = new LoyaltyAbility(new DamageTargetEffect(3), 0); ability.addEffect(new GarrukRelentlessDamageEffect()); ability.addTarget(new TargetCreaturePermanent()); - this.addAbility(ability); + this.getLeftHalfCard().addAbility(ability); // 0: Create a 2/2 green Wolf creature token. - this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new WolfToken()), 0)); + this.getLeftHalfCard().addAbility(new LoyaltyAbility(new CreateTokenEffect(new WolfToken()), 0)); + + // Garruk, the Veil-Cursed + // +1 : Create a 1/1 black Wolf creature token with deathtouch. + this.getRightHalfCard().addAbility(new LoyaltyAbility(new CreateTokenEffect(new WolfTokenWithDeathtouch()), 1)); + + // -1 : Sacrifice a creature. If you do, search your library for a creature card, reveal it, put it into your hand, then shuffle your library. + this.getRightHalfCard().addAbility(new LoyaltyAbility(new DoIfCostPaid( + new SearchLibraryPutInHandEffect(new TargetCardInLibrary( + StaticFilters.FILTER_CARD_CREATURE_A + ), true), + null, + new SacrificeTargetCost(StaticFilters.FILTER_PERMANENT_CREATURE), + false + ), -1)); + + // -3 : Creatures you control gain trample and get +X/+X until end of turn, where X is the number of creature cards in your graveyard. + ability = new LoyaltyAbility(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURE + ).setText("creatures you control gain trample"), -3); + ability.addEffect(new BoostControlledEffect( + xValue, xValue, Duration.EndOfTurn + ).setText("and get +X/+X until end of turn, where X is the number of creature cards in your graveyard")); + this.getRightHalfCard().addAbility(ability); + + this.finalizeDFC(); } private GarrukRelentless(final GarrukRelentless card) { diff --git a/Mage.Sets/src/mage/cards/g/GarrukTheVeilCursed.java b/Mage.Sets/src/mage/cards/g/GarrukTheVeilCursed.java index 4bd063d6f6ff..1299315a3459 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukTheVeilCursed.java +++ b/Mage.Sets/src/mage/cards/g/GarrukTheVeilCursed.java @@ -1,25 +1,9 @@ package mage.cards.g; -import mage.abilities.Ability; -import mage.abilities.LoyaltyAbility; -import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; -import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; -import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; -import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.filter.StaticFilters; -import mage.game.permanent.token.WolfTokenWithDeathtouch; -import mage.target.common.TargetCardInLibrary; import java.util.UUID; @@ -28,40 +12,10 @@ */ public final class GarrukTheVeilCursed extends CardImpl { - private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE); - public GarrukTheVeilCursed(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, ""); - this.supertype.add(SuperType.LEGENDARY); - this.subtype.add(SubType.GARRUK); - - // this card is the second face of double-faced card this.nightCard = true; - - this.color.setGreen(true); - this.color.setBlack(true); - - // +1 : Create a 1/1 black Wolf creature token with deathtouch. - this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new WolfTokenWithDeathtouch()), 1)); - - // -1 : Sacrifice a creature. If you do, search your library for a creature card, reveal it, put it into your hand, then shuffle your library. - this.addAbility(new LoyaltyAbility(new DoIfCostPaid( - new SearchLibraryPutInHandEffect(new TargetCardInLibrary( - StaticFilters.FILTER_CARD_CREATURE_A - ), true), - null, - new SacrificeTargetCost(StaticFilters.FILTER_PERMANENT_CREATURE), - false - ), -1)); - - // -3 : Creatures you control gain trample and get +X/+X until end of turn, where X is the number of creature cards in your graveyard. - Ability ability = new LoyaltyAbility(new GainAbilityControlledEffect( - TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURE - ).setText("creatures you control gain trample"), -3); - ability.addEffect(new BoostControlledEffect( - xValue, xValue, Duration.EndOfTurn - ).setText("and get +X/+X until end of turn, where X is the number of creature cards in your graveyard")); - this.addAbility(ability); + TransformingDoubleFacedCard.copyToBackFace(new mage.cards.g.GarrukRelentless(ownerId, setInfo), this); } private GarrukTheVeilCursed(final GarrukTheVeilCursed card) { diff --git a/Mage.Sets/src/mage/cards/g/GoldbugHumanitysAlly.java b/Mage.Sets/src/mage/cards/g/GoldbugHumanitysAlly.java index 5b81a1d80909..ff34c545f802 100644 --- a/Mage.Sets/src/mage/cards/g/GoldbugHumanitysAlly.java +++ b/Mage.Sets/src/mage/cards/g/GoldbugHumanitysAlly.java @@ -1,55 +1,77 @@ package mage.cards.g; -import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.CastSecondSpellTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CantBeCounteredControlledEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.PreventAllDamageToAllEffect; import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.LivingMetalAbility; import mage.abilities.keyword.MoreThanMeetsTheEyeAbility; -import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.cards.TransformingDoubleFacedCard; +import mage.constants.*; import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.AttackingPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import java.util.Objects; import java.util.UUID; /** * @author TheElk801 */ -public final class GoldbugHumanitysAlly extends CardImpl { +public final class GoldbugHumanitysAlly extends TransformingDoubleFacedCard { private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.HUMAN, "attacking Humans you control"); + private static final FilterSpell filter2 = new FilterSpell("Human spells"); static { filter.add(AttackingPredicate.instance); + filter2.add(SubType.HUMAN.getPredicate()); } public GoldbugHumanitysAlly(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{W}{U}"); - - this.supertype.add(SuperType.LEGENDARY); - this.subtype.add(SubType.ROBOT); - this.power = new MageInt(3); - this.toughness = new MageInt(3); + super( + ownerId, setInfo, + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, new SubType[]{SubType.ROBOT}, "{1}{W}{U}", + "Goldbug, Scrappy Scout", + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.ARTIFACT}, new SubType[]{SubType.VEHICLE}, "UW" + ); + this.getLeftHalfCard().setPT(3, 3); + this.getRightHalfCard().setPT(1, 3); this.secondSideCardClazz = mage.cards.g.GoldbugScrappyScout.class; // More Than Meets the Eye {W}{U} - this.addAbility(new MoreThanMeetsTheEyeAbility(this, "{W}{U}")); + this.getLeftHalfCard().addAbility(new MoreThanMeetsTheEyeAbility(this, "{W}{U}")); // Prevent all combat damage that would be dealt to attacking Humans you control. - this.addAbility(new SimpleStaticAbility(new PreventAllDamageToAllEffect( + this.getLeftHalfCard().addAbility(new SimpleStaticAbility(new PreventAllDamageToAllEffect( Duration.WhileOnBattlefield, filter, true ))); // Whenever you cast your second spell each turn, convert Goldbug. - this.addAbility(new CastSecondSpellTriggeredAbility(new TransformSourceEffect().setText("convert {this}"))); + this.getLeftHalfCard().addAbility(new CastSecondSpellTriggeredAbility(new TransformSourceEffect().setText("convert {this}"))); + + // Goldbug, Scrappy Scout + // Living metal + this.getRightHalfCard().addAbility(new LivingMetalAbility()); + + // Human spells you control can't be countered. + this.getRightHalfCard().addAbility(new SimpleStaticAbility( + new CantBeCounteredControlledEffect(filter2, Duration.WhileOnBattlefield) + )); + + // Whenever Goldbug and at least one Human attack, draw a card and convert Goldbug. + this.getRightHalfCard().addAbility(new GoldbugScrappyScoutTriggeredAbility()); + + this.finalizeDFC(); } private GoldbugHumanitysAlly(final GoldbugHumanitysAlly card) { @@ -61,3 +83,45 @@ public GoldbugHumanitysAlly copy() { return new GoldbugHumanitysAlly(this); } } + +class GoldbugScrappyScoutTriggeredAbility extends TriggeredAbilityImpl { + + GoldbugScrappyScoutTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1)); + this.addEffect(new TransformSourceEffect()); + } + + private GoldbugScrappyScoutTriggeredAbility(final GoldbugScrappyScoutTriggeredAbility ability) { + super(ability); + } + + @Override + public GoldbugScrappyScoutTriggeredAbility copy() { + return new GoldbugScrappyScoutTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return game + .getCombat() + .getAttackers() + .contains(getSourceId()) + && game + .getCombat() + .getAttackers() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .anyMatch(permanent -> permanent.hasSubtype(SubType.HUMAN, game)); + } + + @Override + public String getRule() { + return "Whenever {this} and at least one Human attack, draw a card and convert {this}."; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoldbugScrappyScout.java b/Mage.Sets/src/mage/cards/g/GoldbugScrappyScout.java index 4cd192a781ee..62f206f5c207 100644 --- a/Mage.Sets/src/mage/cards/g/GoldbugScrappyScout.java +++ b/Mage.Sets/src/mage/cards/g/GoldbugScrappyScout.java @@ -1,20 +1,17 @@ package mage.cards.g; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CantBeCounteredControlledEffect; -import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.LivingMetalAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterSpell; -import mage.game.Game; -import mage.game.events.GameEvent; -import java.util.Objects; import java.util.UUID; /** @@ -60,45 +57,3 @@ public GoldbugScrappyScout copy() { return new GoldbugScrappyScout(this); } } - -class GoldbugScrappyScoutTriggeredAbility extends TriggeredAbilityImpl { - - GoldbugScrappyScoutTriggeredAbility() { - super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1)); - this.addEffect(new TransformSourceEffect()); - } - - private GoldbugScrappyScoutTriggeredAbility(final GoldbugScrappyScoutTriggeredAbility ability) { - super(ability); - } - - @Override - public GoldbugScrappyScoutTriggeredAbility copy() { - return new GoldbugScrappyScoutTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return game - .getCombat() - .getAttackers() - .contains(getSourceId()) - && game - .getCombat() - .getAttackers() - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .anyMatch(permanent -> permanent.hasSubtype(SubType.HUMAN, game)); - } - - @Override - public String getRule() { - return "Whenever {this} and at least one Human attack, draw a card and convert {this}."; - } -} diff --git a/Mage.Sets/src/mage/cards/h/HavengulLaboratory.java b/Mage.Sets/src/mage/cards/h/HavengulLaboratory.java index 766da704cd24..de73f6a1d113 100644 --- a/Mage.Sets/src/mage/cards/h/HavengulLaboratory.java +++ b/Mage.Sets/src/mage/cards/h/HavengulLaboratory.java @@ -1,53 +1,80 @@ package mage.cards.h; +import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.TransformIntoSourceTriggeredAbility; import mage.abilities.condition.Condition; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.effects.keyword.InvestigateEffect; -import mage.abilities.keyword.TransformAbility; +import mage.abilities.mana.BlackManaAbility; import mage.abilities.mana.ColorlessManaAbility; -import mage.cards.CardImpl; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.Card; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; import mage.util.CardUtil; import mage.watchers.Watcher; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; /** * @author TheElk801 */ -public final class HavengulLaboratory extends CardImpl { +public final class HavengulLaboratory extends TransformingDoubleFacedCard { public HavengulLaboratory(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - this.supertype.add(SuperType.LEGENDARY); + super( + ownerId, setInfo, + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.LAND}, new SubType[]{}, "", + "Havengul Mystery", + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.LAND}, new SubType[]{}, "" + ); this.secondSideCardClazz = mage.cards.h.HavengulMystery.class; // {T}: Add {C}. - this.addAbility(new ColorlessManaAbility()); + this.getLeftHalfCard().addAbility(new ColorlessManaAbility()); // {4}, {T}: Investigate. Ability ability = new SimpleActivatedAbility(new InvestigateEffect(), new GenericManaCost(4)); ability.addCost(new TapSourceCost()); - this.addAbility(ability); + this.getLeftHalfCard().addAbility(ability); // At the beginning of your end step, if you sacrificed three or more Clues this turn, transform Havengul Laboratory. - this.addAbility(new TransformAbility()); - this.addAbility(new BeginningOfEndStepTriggeredAbility( + this.getLeftHalfCard().addAbility(new BeginningOfEndStepTriggeredAbility( TargetController.YOU, new TransformSourceEffect(), false, HavengulLaboratoryCondition.instance ), new HavengulLaboratoryWatcher()); + + // When this land transforms into Havengul Mystery, return target creature card from your graveyard to the battlefield. + ability = new TransformIntoSourceTriggeredAbility(new HavengulMysteryEffect()) + .setTriggerPhrase("When this land transforms into {this}, "); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.getRightHalfCard().addAbility(ability); + + // When the creature put onto the battlefield with Havengul Mystery leaves the battlefield, transform Havengul Mystery. + this.getRightHalfCard().addAbility(new HavengulMysteryLeavesAbility()); + + // {T}, Pay 1 life: Add {B}. + ability = new BlackManaAbility(); + ability.addCost(new PayLifeCost(1)); + this.getRightHalfCard().addAbility(ability); + + this.finalizeDFC(); } private HavengulLaboratory(final HavengulLaboratory card) { @@ -58,6 +85,10 @@ private HavengulLaboratory(final HavengulLaboratory card) { public HavengulLaboratory copy() { return new HavengulLaboratory(this); } + + static String makeKey(Ability source, Game game) { + return "HavengulMystery_" + source.getSourceId() + '_' + CardUtil.getActualSourceObjectZoneChangeCounter(game, source); + } } enum HavengulLaboratoryCondition implements Condition { @@ -108,3 +139,85 @@ static boolean checkPlayer(UUID playerId, Game game) { .getOrDefault(playerId, 0) >= 3; } } + +class HavengulMysteryEffect extends OneShotEffect { + + HavengulMysteryEffect() { + super(Outcome.Benefit); + staticText = "return target creature card from your graveyard to the battlefield"; + } + + private HavengulMysteryEffect(final HavengulMysteryEffect effect) { + super(effect); + } + + @Override + public HavengulMysteryEffect copy() { + return new HavengulMysteryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent == null) { + return false; + } + String key = HavengulLaboratory.makeKey(source, game); + Set morSet; + if (game.getState().getValue(key) != null) { + morSet = (Set) game.getState().getValue(key); + } else { + morSet = new HashSet<>(); + game.getState().setValue(key, morSet); + } + morSet.add(new MageObjectReference(permanent, game)); + return true; + } +} + +class HavengulMysteryLeavesAbility extends TriggeredAbilityImpl { + + HavengulMysteryLeavesAbility() { + super(Zone.BATTLEFIELD, new TransformSourceEffect()); + setLeavesTheBattlefieldTrigger(true); + } + + private HavengulMysteryLeavesAbility(final HavengulMysteryLeavesAbility ability) { + super(ability); + } + + @Override + public HavengulMysteryLeavesAbility copy() { + return new HavengulMysteryLeavesAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.getFromZone() != Zone.BATTLEFIELD) { + return false; + } + + String key = HavengulLaboratory.makeKey(this, game); + Set morSet = (Set) game.getState().getValue(key); + return morSet != null + && !morSet.isEmpty() + && morSet.stream().anyMatch(mor -> mor.refersTo(zEvent.getTarget(), game)); + } + + @Override + public String getRule() { + return "When the creature put onto the battlefield with {this} leaves the battlefield, transform {this}."; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HavengulMystery.java b/Mage.Sets/src/mage/cards/h/HavengulMystery.java index 24cca60c3b16..78105bba7eec 100644 --- a/Mage.Sets/src/mage/cards/h/HavengulMystery.java +++ b/Mage.Sets/src/mage/cards/h/HavengulMystery.java @@ -1,31 +1,10 @@ package mage.cards.h; -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.TransformIntoSourceTriggeredAbility; -import mage.abilities.costs.common.PayLifeCost; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.TransformSourceEffect; -import mage.abilities.mana.BlackManaAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetCardInYourGraveyard; -import mage.util.CardUtil; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; /** @@ -35,22 +14,8 @@ public final class HavengulMystery extends CardImpl { public HavengulMystery(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - this.supertype.add(SuperType.LEGENDARY); this.nightCard = true; - - // When this land transforms into Havengul Mystery, return target creature card from your graveyard to the battlefield. - Ability ability = new TransformIntoSourceTriggeredAbility(new HavengulMysteryEffect()) - .setTriggerPhrase("When this land transforms into {this}, "); - ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); - this.addAbility(ability); - - // When the creature put onto the battlefield with Havengul Mystery leaves the battlefield, transform Havengul Mystery. - this.addAbility(new HavengulMysteryLeavesAbility()); - - // {T}, Pay 1 life: Add {B}. - Ability ability2 = new BlackManaAbility(); - ability2.addCost(new PayLifeCost(1)); - this.addAbility(ability2); + TransformingDoubleFacedCard.copyToBackFace(new mage.cards.h.HavengulLaboratory(ownerId, setInfo), this); } private HavengulMystery(final HavengulMystery card) { @@ -61,90 +26,4 @@ private HavengulMystery(final HavengulMystery card) { public HavengulMystery copy() { return new HavengulMystery(this); } - - static String makeKey(Ability source, Game game) { - return "HavengulMystery_" + source.getSourceId() + '_' + CardUtil.getActualSourceObjectZoneChangeCounter(game, source); - } -} - -class HavengulMysteryEffect extends OneShotEffect { - - HavengulMysteryEffect() { - super(Outcome.Benefit); - staticText = "return target creature card from your graveyard to the battlefield"; - } - - private HavengulMysteryEffect(final HavengulMysteryEffect effect) { - super(effect); - } - - @Override - public HavengulMysteryEffect copy() { - return new HavengulMysteryEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Card card = game.getCard(getTargetPointer().getFirst(game, source)); - if (player == null || card == null) { - return false; - } - player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); - if (permanent == null) { - return false; - } - String key = HavengulMystery.makeKey(source, game); - Set morSet; - if (game.getState().getValue(key) != null) { - morSet = (Set) game.getState().getValue(key); - } else { - morSet = new HashSet<>(); - game.getState().setValue(key, morSet); - } - morSet.add(new MageObjectReference(permanent, game)); - return true; - } -} - -class HavengulMysteryLeavesAbility extends TriggeredAbilityImpl { - - HavengulMysteryLeavesAbility() { - super(Zone.BATTLEFIELD, new TransformSourceEffect()); - setLeavesTheBattlefieldTrigger(true); - } - - private HavengulMysteryLeavesAbility(final HavengulMysteryLeavesAbility ability) { - super(ability); - } - - @Override - public HavengulMysteryLeavesAbility copy() { - return new HavengulMysteryLeavesAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getFromZone() != Zone.BATTLEFIELD) { - return false; - } - - String key = HavengulMystery.makeKey(this, game); - Set morSet = (Set) game.getState().getValue(key); - return morSet != null - && !morSet.isEmpty() - && morSet.stream().anyMatch(mor -> mor.refersTo(zEvent.getTarget(), game)); - } - - @Override - public String getRule() { - return "When the creature put onto the battlefield with {this} leaves the battlefield, transform {this}."; - } } diff --git a/Mage.Sets/src/mage/cards/i/InvasionOfBelenon.java b/Mage.Sets/src/mage/cards/i/InvasionOfBelenon.java index 717ac38bdc85..dede7ee934ed 100644 --- a/Mage.Sets/src/mage/cards/i/InvasionOfBelenon.java +++ b/Mage.Sets/src/mage/cards/i/InvasionOfBelenon.java @@ -2,10 +2,13 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SiegeAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CreateTokenEffect; -import mage.cards.CardImpl; +import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.SubType; import mage.game.permanent.token.KnightWhiteBlueToken; @@ -14,20 +17,30 @@ /** * @author TheElk801 */ -public final class InvasionOfBelenon extends CardImpl { +public final class InvasionOfBelenon extends TransformingDoubleFacedCard { public InvasionOfBelenon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.BATTLE}, "{2}{W}"); + super( + ownerId, setInfo, + new CardType[]{CardType.BATTLE}, new SubType[]{SubType.SIEGE}, "{2}{W}", + "Belenon War Anthem", + new CardType[]{CardType.ENCHANTMENT}, new SubType[]{}, "W" + ); + this.getLeftHalfCard().setStartingDefense(5); - this.subtype.add(SubType.SIEGE); - this.setStartingDefense(5); this.secondSideCardClazz = mage.cards.b.BelenonWarAnthem.class; // (As a Siege enters, choose an opponent to protect it. You and others can attack it. When it's defeated, exile it, then cast it transformed.) - this.addAbility(new SiegeAbility()); + this.getLeftHalfCard().addAbility(new SiegeAbility()); // When Invasion of Belenon enters the battlefield, create a 2/2 white and blue Knight creature token with vigilance. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KnightWhiteBlueToken()))); + this.getLeftHalfCard().addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new KnightWhiteBlueToken()))); + + // Belenon War Anthem + // Creatures you control get +1/+1. + this.getRightHalfCard().addAbility(new SimpleStaticAbility(new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield))); + + this.finalizeDFC(); } private InvasionOfBelenon(final InvasionOfBelenon card) { diff --git a/Mage.Sets/src/mage/cards/i/InvasionOfDominaria.java b/Mage.Sets/src/mage/cards/i/InvasionOfDominaria.java index be85c55fce31..3cf06a83097d 100644 --- a/Mage.Sets/src/mage/cards/i/InvasionOfDominaria.java +++ b/Mage.Sets/src/mage/cards/i/InvasionOfDominaria.java @@ -5,8 +5,10 @@ import mage.abilities.common.SiegeAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; -import mage.cards.CardImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; import mage.constants.SubType; @@ -15,22 +17,36 @@ /** * @author TheElk801 */ -public final class InvasionOfDominaria extends CardImpl { +public final class InvasionOfDominaria extends TransformingDoubleFacedCard { public InvasionOfDominaria(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.BATTLE}, "{2}{W}"); + super( + ownerId, setInfo, + new CardType[]{CardType.BATTLE}, new SubType[]{SubType.SIEGE}, "{2}{W}", + "Serra Faithkeeper", + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.ANGEL}, "W" + ); + this.getLeftHalfCard().setStartingDefense(5); + this.getRightHalfCard().setPT(4, 4); - this.subtype.add(SubType.SIEGE); - this.setStartingDefense(5); this.secondSideCardClazz = mage.cards.s.SerraFaithkeeper.class; // (As a Siege enters, choose an opponent to protect it. You and others can attack it. When it's defeated, exile it, then cast it transformed.) - this.addAbility(new SiegeAbility()); + this.getLeftHalfCard().addAbility(new SiegeAbility()); // When Invasion of Dominaria enters the battlefield, you gain 4 life and draw a card. Ability ability = new EntersBattlefieldTriggeredAbility(new GainLifeEffect(4)); ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and")); - this.addAbility(ability); + this.getLeftHalfCard().addAbility(ability); + + // Serra Faithkeeper + // Flying + this.getRightHalfCard().addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.getRightHalfCard().addAbility(VigilanceAbility.getInstance()); + + this.finalizeDFC(); } private InvasionOfDominaria(final InvasionOfDominaria card) { diff --git a/Mage.Sets/src/mage/cards/i/InvasionOfKylem.java b/Mage.Sets/src/mage/cards/i/InvasionOfKylem.java index 9fe9458f3a38..3b41b0ed2899 100644 --- a/Mage.Sets/src/mage/cards/i/InvasionOfKylem.java +++ b/Mage.Sets/src/mage/cards/i/InvasionOfKylem.java @@ -3,14 +3,16 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SiegeAbility; +import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.VigilanceAbility; -import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; import mage.constants.SubType; +import mage.game.permanent.token.ValorsReachTagTeamToken; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -18,17 +20,21 @@ /** * @author TheElk801 */ -public final class InvasionOfKylem extends CardImpl { +public final class InvasionOfKylem extends TransformingDoubleFacedCard { public InvasionOfKylem(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.BATTLE}, "{2}{R}{W}"); + super( + ownerId, setInfo, + new CardType[]{CardType.BATTLE}, new SubType[]{SubType.SIEGE}, "{2}{R}{W}", + "Valor's Reach Tag Team", + new CardType[]{CardType.SORCERY}, new SubType[]{}, "RW" + ); + this.getLeftHalfCard().setStartingDefense(5); - this.subtype.add(SubType.SIEGE); - this.setStartingDefense(5); this.secondSideCardClazz = mage.cards.v.ValorsReachTagTeam.class; // (As a Siege enters, choose an opponent to protect it. You and others can attack it. When it's defeated, exile it, then cast it transformed.) - this.addAbility(new SiegeAbility()); + this.getLeftHalfCard().addAbility(new SiegeAbility()); // When Invasion of Kylem enters the battlefield, up to two target creatures each get +2/+0 and gain vigilance and haste until end of turn. Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(2, 0) @@ -38,7 +44,13 @@ public InvasionOfKylem(UUID ownerId, CardSetInfo setInfo) { ability.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance()) .setText("and haste until end of turn")); ability.addTarget(new TargetCreaturePermanent(0, 2)); - this.addAbility(ability); + this.getLeftHalfCard().addAbility(ability); + + // Valor's Reach Tag Team + // Create two 3/2 red and white Warrior creature tokens with "Whenever this creature and at least one other creature token attack, put a +1/+1 counter on this creature." + this.getRightHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new ValorsReachTagTeamToken(), 2)); + + this.finalizeDFC(); } private InvasionOfKylem(final InvasionOfKylem card) { diff --git a/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java b/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java index 66e1b793d620..a13662074d39 100644 --- a/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java +++ b/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java @@ -1,20 +1,9 @@ package mage.cards.j; -import mage.abilities.Ability; -import mage.abilities.LoyaltyAbility; -import mage.abilities.effects.common.GetEmblemEffect; -import mage.abilities.effects.common.MayCastTargetCardEffect; -import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.filter.StaticFilters; -import mage.game.command.emblems.JaceTelepathUnboundEmblem; -import mage.target.common.TargetCardInYourGraveyard; -import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -25,28 +14,8 @@ public final class JaceTelepathUnbound extends CardImpl { public JaceTelepathUnbound(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, ""); - this.supertype.add(SuperType.LEGENDARY); - this.subtype.add(SubType.JACE); - - this.color.setBlue(true); this.nightCard = true; - - this.setStartingLoyalty(5); - - // +1: Up to one target creature gets -2/-0 until your next turn. - Ability ability = new LoyaltyAbility(new BoostTargetEffect( - -2, 0, Duration.UntilYourNextTurn - ).setText("Up to one target creature gets -2/-0 until your next turn"), 1); - ability.addTarget(new TargetCreaturePermanent(0, 1)); - this.addAbility(ability); - - // -3: You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead. - ability = new LoyaltyAbility(new MayCastTargetCardEffect(Duration.EndOfTurn, true), -3); - ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD)); - this.addAbility(ability); - - // −9: You get an emblem with "Whenever you cast a spell, target opponent mills five cards." - this.addAbility(new LoyaltyAbility(new GetEmblemEffect(new JaceTelepathUnboundEmblem()), -9)); + TransformingDoubleFacedCard.copyToBackFace(new mage.cards.j.JaceVrynsProdigy(ownerId, setInfo), this); } private JaceTelepathUnbound(final JaceTelepathUnbound card) { diff --git a/Mage.Sets/src/mage/cards/j/JaceVrynsProdigy.java b/Mage.Sets/src/mage/cards/j/JaceVrynsProdigy.java index 7c8eb7210119..7e4ed7219478 100644 --- a/Mage.Sets/src/mage/cards/j/JaceVrynsProdigy.java +++ b/Mage.Sets/src/mage/cards/j/JaceVrynsProdigy.java @@ -1,7 +1,7 @@ package mage.cards.j; -import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.CardsInControllerGraveyardCondition; @@ -9,39 +9,64 @@ import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.abilities.effects.common.ExileAndReturnSourceEffect; -import mage.abilities.keyword.TransformAbility; -import mage.cards.CardImpl; +import mage.abilities.effects.common.GetEmblemEffect; +import mage.abilities.effects.common.MayCastTargetCardEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.command.emblems.JaceTelepathUnboundEmblem; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** * @author LevelX2 */ -public final class JaceVrynsProdigy extends CardImpl { +public final class JaceVrynsProdigy extends TransformingDoubleFacedCard { private static final Condition condition = new CardsInControllerGraveyardCondition(5); public JaceVrynsProdigy(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); - this.supertype.add(SuperType.LEGENDARY); - this.subtype.add(SubType.HUMAN); - this.subtype.add(SubType.WIZARD); - this.power = new MageInt(0); - this.toughness = new MageInt(2); + super( + ownerId, setInfo, + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.WIZARD}, "{1}{U}", + "Jace, Telepath Unbound", + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.PLANESWALKER}, new SubType[]{SubType.JACE}, "U" + ); + this.getLeftHalfCard().setPT(0, 2); + this.getRightHalfCard().setStartingLoyalty(5); this.secondSideCardClazz = mage.cards.j.JaceTelepathUnbound.class; // {T}: Draw a card, then discard a card. If there are five or more cards in your graveyard, exile Jace, Vryn's Prodigy, then return him to the battefield transformed under his owner's control. - this.addAbility(new TransformAbility()); Ability ability = new SimpleActivatedAbility( new DrawDiscardControllerEffect(1, 1), new TapSourceCost() ); ability.addEffect(new ConditionalOneShotEffect( new ExileAndReturnSourceEffect(PutCards.BATTLEFIELD_TRANSFORMED, Pronoun.HE), condition )); - this.addAbility(ability); + this.getLeftHalfCard().addAbility(ability); + + // Jace, Telepath Unbound + // +1: Up to one target creature gets -2/-0 until your next turn. + ability = new LoyaltyAbility(new BoostTargetEffect( + -2, 0, Duration.UntilYourNextTurn + ).setText("Up to one target creature gets -2/-0 until your next turn"), 1); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.getRightHalfCard().addAbility(ability); + + // -3: You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead. + ability = new LoyaltyAbility(new MayCastTargetCardEffect(Duration.EndOfTurn, true), -3); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD)); + this.getRightHalfCard().addAbility(ability); + + // −9: You get an emblem with "Whenever you cast a spell, target opponent mills five cards." + this.getRightHalfCard().addAbility(new LoyaltyAbility(new GetEmblemEffect(new JaceTelepathUnboundEmblem()), -9)); + + this.finalizeDFC(); } private JaceVrynsProdigy(final JaceVrynsProdigy card) { diff --git a/Mage.Sets/src/mage/cards/o/OrmendahlProfanePrince.java b/Mage.Sets/src/mage/cards/o/OrmendahlProfanePrince.java index 1df4cf81eeb0..2cd5f55a9737 100644 --- a/Mage.Sets/src/mage/cards/o/OrmendahlProfanePrince.java +++ b/Mage.Sets/src/mage/cards/o/OrmendahlProfanePrince.java @@ -1,43 +1,21 @@ - package mage.cards.o; -import java.util.UUID; -import mage.MageInt; -import mage.abilities.keyword.FlyingAbility; -import mage.abilities.keyword.HasteAbility; -import mage.abilities.keyword.IndestructibleAbility; -import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.SuperType; + +import java.util.UUID; /** - * * @author fireshoes */ public final class OrmendahlProfanePrince extends CardImpl { public OrmendahlProfanePrince(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); - this.supertype.add(SuperType.LEGENDARY); - this.subtype.add(SubType.DEMON); - this.power = new MageInt(9); - this.toughness = new MageInt(7); - this.color.setBlack(true); - - // this card is the second face of double-faced card + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.nightCard = true; - - // Flying - this.addAbility(FlyingAbility.getInstance()); - // Lifelink - this.addAbility(LifelinkAbility.getInstance()); - // Indestructible - this.addAbility(IndestructibleAbility.getInstance()); - // Haste - this.addAbility(HasteAbility.getInstance()); + TransformingDoubleFacedCard.copyToBackFace(new mage.cards.w.WestvaleAbbey(ownerId, setInfo), this); } private OrmendahlProfanePrince(final OrmendahlProfanePrince card) { diff --git a/Mage.Sets/src/mage/cards/p/PersistentNightmare.java b/Mage.Sets/src/mage/cards/p/PersistentNightmare.java index d1825f3f44b6..aac7331b061d 100644 --- a/Mage.Sets/src/mage/cards/p/PersistentNightmare.java +++ b/Mage.Sets/src/mage/cards/p/PersistentNightmare.java @@ -1,13 +1,9 @@ package mage.cards.p; -import mage.MageInt; -import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.common.ReturnToHandSourceEffect; -import mage.abilities.keyword.SkulkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.constants.SubType; import java.util.UUID; @@ -18,21 +14,8 @@ public final class PersistentNightmare extends CardImpl { public PersistentNightmare(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); - this.subtype.add(SubType.NIGHTMARE); - this.power = new MageInt(1); - this.toughness = new MageInt(1); - this.color.setBlue(true); - - // this card is the second face of double-faced card this.nightCard = true; - - // Skulk - this.addAbility(new SkulkAbility()); - - // When Persistent Nightmare deals combat damage to a player, return it to its owner's hand. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( - new ReturnToHandSourceEffect(), false - ).setTriggerPhrase("When {this} deals combat damage to a player, ")); + TransformingDoubleFacedCard.copyToBackFace(new mage.cards.s.StartledAwake(ownerId, setInfo), this); } private PersistentNightmare(final PersistentNightmare card) { diff --git a/Mage.Sets/src/mage/cards/s/SerraFaithkeeper.java b/Mage.Sets/src/mage/cards/s/SerraFaithkeeper.java index 21a7c0814018..607b8534cab2 100644 --- a/Mage.Sets/src/mage/cards/s/SerraFaithkeeper.java +++ b/Mage.Sets/src/mage/cards/s/SerraFaithkeeper.java @@ -1,12 +1,9 @@ package mage.cards.s; -import mage.MageInt; -import mage.abilities.keyword.FlyingAbility; -import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.constants.SubType; import java.util.UUID; @@ -17,18 +14,8 @@ public final class SerraFaithkeeper extends CardImpl { public SerraFaithkeeper(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); - - this.subtype.add(SubType.ANGEL); - this.power = new MageInt(4); - this.toughness = new MageInt(4); - this.color.setWhite(true); this.nightCard = true; - - // Flying - this.addAbility(FlyingAbility.getInstance()); - - // Vigilance - this.addAbility(VigilanceAbility.getInstance()); + TransformingDoubleFacedCard.copyToBackFace(new mage.cards.i.InvasionOfDominaria(ownerId, setInfo), this); } private SerraFaithkeeper(final SerraFaithkeeper card) { diff --git a/Mage.Sets/src/mage/cards/s/SpringLoadedSawblades.java b/Mage.Sets/src/mage/cards/s/SpringLoadedSawblades.java index d9c7374a10f2..1bd92619c4f0 100644 --- a/Mage.Sets/src/mage/cards/s/SpringLoadedSawblades.java +++ b/Mage.Sets/src/mage/cards/s/SpringLoadedSawblades.java @@ -2,45 +2,77 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapTargetCost; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.AddCardTypeSourceEffect; import mage.abilities.keyword.CraftAbility; +import mage.abilities.keyword.CrewAbility; import mage.abilities.keyword.FlashAbility; -import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterOpponentsCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TappedPredicate; import mage.target.TargetPermanent; +import mage.target.common.TargetControlledPermanent; import java.util.UUID; /** * @author TheElk801 */ -public final class SpringLoadedSawblades extends CardImpl { +public final class SpringLoadedSawblades extends TransformingDoubleFacedCard { private static final FilterPermanent filter = new FilterOpponentsCreaturePermanent("tapped creature an opponent controls"); + private static final FilterControlledPermanent filter2 + = new FilterControlledArtifactPermanent("other untapped artifacts you control"); static { filter.add(TappedPredicate.TAPPED); + filter2.add(AnotherPredicate.instance); + filter2.add(TappedPredicate.UNTAPPED); } public SpringLoadedSawblades(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}"); + super( + ownerId, setInfo, + new CardType[]{CardType.ARTIFACT}, new SubType[]{}, "{1}{W}", + "Bladewheel Chariot", + new CardType[]{CardType.ARTIFACT}, new SubType[]{SubType.VEHICLE}, "W" + ); + this.getRightHalfCard().setPT(5, 5); + this.secondSideCardClazz = mage.cards.b.BladewheelChariot.class; // Flash - this.addAbility(FlashAbility.getInstance()); + this.getLeftHalfCard().addAbility(FlashAbility.getInstance()); // When Spring-Loaded Sawblades enters the battlefield, it deals 5 damage to target tapped creature an opponent controls. Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(5, "it")); ability.addTarget(new TargetPermanent(filter)); - this.addAbility(ability); + this.getLeftHalfCard().addAbility(ability); // Craft with artifact {3}{W} - this.addAbility(new CraftAbility("{3}{W}")); + this.getLeftHalfCard().addAbility(new CraftAbility("{3}{W}")); + + // Bladewheel Chariot + // Tap two other untapped artifacts you control: Bladewheel Chariot becomes an artifact creature until end of turn. + this.getRightHalfCard().addAbility(new SimpleActivatedAbility(new AddCardTypeSourceEffect( + Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE + ).setText("{this} becomes an artifact creature until end of turn"), new TapTargetCost(new TargetControlledPermanent(2, filter2)))); + + // Crew 1 + this.getRightHalfCard().addAbility(new CrewAbility(1)); + + this.finalizeDFC(); } private SpringLoadedSawblades(final SpringLoadedSawblades card) { diff --git a/Mage.Sets/src/mage/cards/s/StartledAwake.java b/Mage.Sets/src/mage/cards/s/StartledAwake.java index d9e1a5b049b0..ef59f5eb6747 100644 --- a/Mage.Sets/src/mage/cards/s/StartledAwake.java +++ b/Mage.Sets/src/mage/cards/s/StartledAwake.java @@ -1,17 +1,20 @@ package mage.cards.s; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.MillCardsTargetEffect; +import mage.abilities.effects.common.ReturnToHandSourceEffect; +import mage.abilities.keyword.SkulkAbility; import mage.abilities.keyword.TransformAbility; import mage.cards.Card; -import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; @@ -22,22 +25,38 @@ /** * @author LevelX2 */ -public final class StartledAwake extends CardImpl { +public final class StartledAwake extends TransformingDoubleFacedCard { public StartledAwake(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}"); + super( + ownerId, setInfo, + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{2}{U}{U}", + "Persistent Nightmare", + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.NIGHTMARE}, "U" + ); + this.getRightHalfCard().setPT(1, 1); this.secondSideCardClazz = mage.cards.p.PersistentNightmare.class; // Target opponent puts the top thirteen cards of their library into their graveyard. - this.getSpellAbility().addTarget(new TargetOpponent()); - this.getSpellAbility().addEffect(new MillCardsTargetEffect(13)); + this.getLeftHalfCard().getSpellAbility().addEffect(new MillCardsTargetEffect(13)); + this.getLeftHalfCard().getSpellAbility().addTarget(new TargetOpponent()); // {3}{U}{U}: Put Startled Awake from your graveyard onto the battlefield transformed. Activate this ability only any time you could cast a sorcery. - this.addAbility(new TransformAbility()); - this.addAbility(new ActivateAsSorceryActivatedAbility( + this.getLeftHalfCard().addAbility(new ActivateAsSorceryActivatedAbility( Zone.GRAVEYARD, new StartledAwakeReturnTransformedEffect(), new ManaCostsImpl<>("{3}{U}{U}") )); + + // Persistent Nightmare + // Skulk + this.getRightHalfCard().addAbility(new SkulkAbility()); + + // When Persistent Nightmare deals combat damage to a player, return it to its owner's hand. + this.getRightHalfCard().addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new ReturnToHandSourceEffect(), false + ).setTriggerPhrase("When {this} deals combat damage to a player, ")); + + this.finalizeDFC(); } private StartledAwake(final StartledAwake card) { diff --git a/Mage.Sets/src/mage/cards/t/TavernRuffian.java b/Mage.Sets/src/mage/cards/t/TavernRuffian.java index 804e75510831..a9c8753f223d 100644 --- a/Mage.Sets/src/mage/cards/t/TavernRuffian.java +++ b/Mage.Sets/src/mage/cards/t/TavernRuffian.java @@ -1,9 +1,9 @@ package mage.cards.t; -import mage.MageInt; import mage.abilities.keyword.DayboundAbility; -import mage.cards.CardImpl; +import mage.abilities.keyword.NightboundAbility; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; import mage.constants.SubType; @@ -12,21 +12,27 @@ /** * @author TheElk801 */ -public final class TavernRuffian extends CardImpl { +public final class TavernRuffian extends TransformingDoubleFacedCard { public TavernRuffian(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); - - this.subtype.add(SubType.HUMAN); - this.subtype.add(SubType.WARRIOR); - this.subtype.add(SubType.WEREWOLF); - this.power = new MageInt(2); - this.toughness = new MageInt(5); + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.WARRIOR, SubType.WEREWOLF}, "{3}{R}", + "Tavern Smasher", + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.WEREWOLF}, "R" + ); + this.getLeftHalfCard().setPT(2, 5); + this.getRightHalfCard().setPT(6, 5); this.secondSideCardClazz = mage.cards.t.TavernSmasher.class; // Daybound - this.addAbility(new DayboundAbility()); + this.getLeftHalfCard().addAbility(new DayboundAbility()); + + // Nightbound + this.getRightHalfCard().addAbility(new NightboundAbility()); + + this.finalizeDFC(); } private TavernRuffian(final TavernRuffian card) { diff --git a/Mage.Sets/src/mage/cards/t/TavernSmasher.java b/Mage.Sets/src/mage/cards/t/TavernSmasher.java index 23e61cd013e9..877829de62c0 100644 --- a/Mage.Sets/src/mage/cards/t/TavernSmasher.java +++ b/Mage.Sets/src/mage/cards/t/TavernSmasher.java @@ -1,11 +1,9 @@ package mage.cards.t; -import mage.MageInt; -import mage.abilities.keyword.NightboundAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.constants.SubType; import java.util.UUID; @@ -16,18 +14,8 @@ public final class TavernSmasher extends CardImpl { public TavernSmasher(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); - - this.subtype.add(SubType.WEREWOLF); - - this.color.setRed(true); - this.nightCard = true; - - this.power = new MageInt(6); - this.toughness = new MageInt(5); - - // Nightbound - this.addAbility(new NightboundAbility()); + TransformingDoubleFacedCard.copyToBackFace(new mage.cards.t.TavernRuffian(ownerId, setInfo), this); } private TavernSmasher(final TavernSmasher card) { diff --git a/Mage.Sets/src/mage/cards/u/UnholyFiend.java b/Mage.Sets/src/mage/cards/u/UnholyFiend.java index 726ca0db1bb2..b5adeb307269 100644 --- a/Mage.Sets/src/mage/cards/u/UnholyFiend.java +++ b/Mage.Sets/src/mage/cards/u/UnholyFiend.java @@ -1,33 +1,21 @@ - package mage.cards.u; -import java.util.UUID; -import mage.MageInt; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; -import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.constants.SubType; + +import java.util.UUID; /** - * * @author Loki */ public final class UnholyFiend extends CardImpl { public UnholyFiend(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},""); - this.subtype.add(SubType.HORROR); - - this.color.setBlack(true); - - this.power = new MageInt(3); - this.toughness = new MageInt(3); - + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.nightCard = true; - - this.addAbility(new BeginningOfEndStepTriggeredAbility(new LoseLifeSourceControllerEffect(1))); + TransformingDoubleFacedCard.copyToBackFace(new mage.cards.c.CloisteredYouth(ownerId, setInfo), this); } private UnholyFiend(final UnholyFiend card) { diff --git a/Mage.Sets/src/mage/cards/v/ValorsReachTagTeam.java b/Mage.Sets/src/mage/cards/v/ValorsReachTagTeam.java index b01d199a7c87..f4801058eaa0 100644 --- a/Mage.Sets/src/mage/cards/v/ValorsReachTagTeam.java +++ b/Mage.Sets/src/mage/cards/v/ValorsReachTagTeam.java @@ -1,10 +1,9 @@ package mage.cards.v; -import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.game.permanent.token.ValorsReachTagTeamToken; import java.util.UUID; @@ -15,13 +14,8 @@ public final class ValorsReachTagTeam extends CardImpl { public ValorsReachTagTeam(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, ""); - - this.color.setWhite(true); - this.color.setRed(true); this.nightCard = true; - - // Create two 3/2 red and white Warrior creature tokens with "Whenever this creature and at least one other creature token attack, put a +1/+1 counter on this creature." - this.getSpellAbility().addEffect(new CreateTokenEffect(new ValorsReachTagTeamToken(), 2)); + TransformingDoubleFacedCard.copyToBackFace(new mage.cards.i.InvasionOfKylem(ownerId, setInfo), this); } private ValorsReachTagTeam(final ValorsReachTagTeam card) { diff --git a/Mage.Sets/src/mage/cards/w/WestvaleAbbey.java b/Mage.Sets/src/mage/cards/w/WestvaleAbbey.java index f618bb62b615..c5bfb06c439b 100644 --- a/Mage.Sets/src/mage/cards/w/WestvaleAbbey.java +++ b/Mage.Sets/src/mage/cards/w/WestvaleAbbey.java @@ -1,8 +1,5 @@ - package mage.cards.w; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.PayLifeCost; @@ -12,43 +9,67 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.effects.common.UntapSourceEffect; -import mage.abilities.keyword.TransformAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.LifelinkAbility; import mage.abilities.mana.ColorlessManaAbility; -import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.TransformingDoubleFacedCard; import mage.constants.CardType; -import mage.constants.Zone; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.permanent.token.HumanClericToken; -import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; /** * @author fireshoes */ -public final class WestvaleAbbey extends CardImpl { +public final class WestvaleAbbey extends TransformingDoubleFacedCard { public WestvaleAbbey(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + super( + ownerId, setInfo, + new SuperType[]{}, new CardType[]{CardType.LAND}, new SubType[]{}, "", + "Ormendahl, Profane Prince", + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.DEMON}, "B" + ); + this.getRightHalfCard().setPT(9, 7); this.secondSideCardClazz = mage.cards.o.OrmendahlProfanePrince.class; // {T}: Add {C}. - this.addAbility(new ColorlessManaAbility()); + this.getLeftHalfCard().addAbility(new ColorlessManaAbility()); // {5}, {T}, Pay 1 life: Create a 1/1 white and black Human Cleric creature token. Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new HumanClericToken()), new GenericManaCost(5)); ability.addCost(new TapSourceCost()); ability.addCost(new PayLifeCost(1)); - this.addAbility(ability); + this.getLeftHalfCard().addAbility(ability); // {5}, {T}, Sacrifice five creatures: Transform Westvale Abbey and untap it. - this.addAbility(new TransformAbility()); ability = new SimpleActivatedAbility(new TransformSourceEffect(), new GenericManaCost(5)); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeTargetCost(5, StaticFilters.FILTER_PERMANENT_CREATURES)); ability.addEffect(new UntapSourceEffect().setText("untap it").concatBy(", then")); - this.addAbility(ability); + this.getLeftHalfCard().addAbility(ability); + + // Ormendahl, Profane Prince + // Flying + this.getRightHalfCard().addAbility(FlyingAbility.getInstance()); + + // Lifelink + this.getRightHalfCard().addAbility(LifelinkAbility.getInstance()); + + // Indestructible + this.getRightHalfCard().addAbility(IndestructibleAbility.getInstance()); + + // Haste + this.getRightHalfCard().addAbility(HasteAbility.getInstance()); + + this.finalizeDFC(); } private WestvaleAbbey(final WestvaleAbbey card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java index ad94dfaed49c..1981f44a3f8e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/TransformTest.java @@ -1,8 +1,9 @@ package org.mage.test.cards.abilities.keywords; -import mage.cards.s.SpringOfEternalPeace; +import mage.ObjectColor; import mage.constants.CardType; import mage.constants.PhaseStep; +import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; import org.junit.Test; @@ -519,6 +520,54 @@ public void testMoonmistHuntmasterDressdown() { assertLife(playerB, 20); assertGraveyardCount(playerA, "Dress Down", 1); assertPermanentCount(playerA, "Huntmaster of the Fells", 1); - assertPermanentCount(playerA, 6+1+1); + assertPermanentCount(playerA, 6 + 1 + 1); + } + + /** + * The following tests exist to make sure the TDFC refactor workaround functions correctly. + * They should eventually not be necessary + */ + + private static final String youth = "Cloistered Youth"; + + @Test + public void testWorkaroundCloisteredYouth() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.HAND, playerA, youth); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, youth); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + execute(); + + assertPowerToughness(playerA, youth, 1, 1); + assertColor(playerA, youth, ObjectColor.WHITE, true); + assertColor(playerA, youth, ObjectColor.BLACK, false); + assertSubtype(youth, SubType.HUMAN); + assertNotSubtype(youth, SubType.HORROR); + } + + private static final String fiend = "Unholy Fiend"; + + @Test + public void testWorkaroundUnholyFiend() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.HAND, playerA, youth); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, youth); + setChoice(playerA, true); + setStopAt(3, PhaseStep.END_TURN); + + setStrictChooseMode(true); + execute(); + + assertPowerToughness(playerA, fiend, 3, 3); + assertColor(playerA, fiend, ObjectColor.BLACK, true); + assertColor(playerA, fiend, ObjectColor.WHITE, false); + assertSubtype(fiend, SubType.HORROR); + assertNotSubtype(fiend, SubType.HUMAN); + assertLife(playerA, 20 - 1); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/battle/BattleBaseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/battle/BattleBaseTest.java index 9b078cee3edf..b7de713b2b2f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/battle/BattleBaseTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/battle/BattleBaseTest.java @@ -19,6 +19,7 @@ public class BattleBaseTest extends CardTestPlayerBaseWithAIHelps { protected static final String impact = "Explosive Impact"; protected static final String stifle = "Stifle"; protected static final String fayden = "Dack Fayden"; + protected static final String kylem = "Invasion of Kylem"; protected void assertBattle(Player controller, Player protector, String name) { assertPermanentCount(controller, name, 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/battle/BattleDuelTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/battle/BattleDuelTest.java index 0655ccc4a0a6..2f50e4e69933 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/battle/BattleDuelTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/battle/BattleDuelTest.java @@ -4,6 +4,7 @@ import mage.constants.Zone; import mage.counters.CounterType; import org.junit.Test; +import org.mage.test.player.TestPlayer; /** * @author TheElk801, JayDi85 @@ -264,4 +265,24 @@ public void test_AI_MustNotAttackMultipleTargets() { assertCounterCount(belenon, CounterType.DEFENSE, 5); assertCounterCount(fayden, CounterType.LOYALTY, 3 - 2); } + + @Test + public void testInvasionOfKylem() { + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 4 + 6); + addCard(Zone.HAND, playerA, kylem); + addCard(Zone.HAND, playerA, impact); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, kylem); + addTarget(playerA, TestPlayer.TARGET_SKIP); // don't choose any targets for etb trigger + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, impact, kylem); + setChoice(playerA, true); // yes to cast it transformed + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, kylem, 0); + assertPermanentCount(playerA, "Warrior Token", 2); + } } diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index 8882c14869de..c645b504fea0 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -1,5 +1,6 @@ package mage.cards; +import mage.MageInt; import mage.MageObject; import mage.Mana; import mage.abilities.Abilities; @@ -17,6 +18,7 @@ import mage.game.Ownerable; import mage.game.permanent.Permanent; import mage.util.ManaUtil; +import mage.watchers.Watcher; import mage.watchers.common.CommanderPlaysCountWatcher; import java.util.List; @@ -154,6 +156,11 @@ default boolean isExtraDeckCard() { Counters getCounters(GameState state); + default void addAbility(Ability ability, Watcher watcher) { + addAbility(ability); + ability.addWatcher(watcher); + } + void addAbility(Ability ability); void looseAllAbilities(Game game); @@ -250,6 +257,10 @@ default int removeAllCounters(String counterName, Ability source, Game game) { List getAttachments(); + void setPT(int power, int toughness); + + void setPT(MageInt power, MageInt toughness); + /** * @param attachment can be any object: card, permanent, token * @param source can be null for default checks like state base diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 90acef7cccec..78b3e1580caf 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -1,5 +1,6 @@ package mage.cards; +import mage.MageInt; import mage.MageObject; import mage.MageObjectImpl; import mage.Mana; @@ -25,7 +26,6 @@ import mage.util.CardUtil; import mage.util.GameLog; import mage.util.ManaUtil; -import mage.watchers.Watcher; import org.apache.log4j.Logger; import java.lang.reflect.Constructor; @@ -356,11 +356,6 @@ public void addAbility(Ability ability) { } } - protected void addAbility(Ability ability, Watcher watcher) { - addAbility(ability); - ability.addWatcher(watcher); - } - public void replaceSpellAbility(SpellAbility newAbility) { SpellAbility oldAbility = this.getSpellAbility(); while (oldAbility != null) { @@ -393,6 +388,17 @@ public void setOwnerId(UUID ownerId) { this.abilities.setControllerId(ownerId); } + @Override + public void setPT(int power, int toughness) { + this.setPT(new MageInt(power), new MageInt(toughness)); + } + + @Override + public void setPT(MageInt power, MageInt toughness) { + this.power = power; + this.toughness = toughness; + } + @Override public UUID getControllerOrOwnerId() { return getOwnerId(); @@ -947,7 +953,7 @@ public boolean cantBeAttachedBy(MageObject attachment, Ability source, Game game } } } - if (controller != null && spellAbility != null && !spellAbility.getTargets().isEmpty()){ + if (controller != null && spellAbility != null && !spellAbility.getTargets().isEmpty()) { // Line of code below functionally gets the target of the aura's Enchant ability, then compares to this permanent. Enchant improperly implemented in XMage, see #9583 // Note: stillLegalTarget used exclusively to account for Dream Leash. Can be made canTarget in the event that that card is rewritten (and "stillLegalTarget" removed from TargetImpl). canAttach &= spellAbility.getTargets().get(0).copy().withNotTarget(true).stillLegalTarget(controller, this.getId(), source, game); diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacedCardHalf.java b/Mage/src/main/java/mage/cards/ModalDoubleFacedCardHalf.java index 78cba6bd6060..b317bc8fca24 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacedCardHalf.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacedCardHalf.java @@ -1,7 +1,5 @@ package mage.cards; -import mage.MageInt; - /** * @author JayDi85 */ @@ -9,8 +7,4 @@ public interface ModalDoubleFacedCardHalf extends SubCard @Override ModalDoubleFacedCardHalf copy(); - - void setPT(int power, int toughness); - - void setPT(MageInt power, MageInt toughness); } diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacedCardHalfImpl.java b/Mage/src/main/java/mage/cards/ModalDoubleFacedCardHalfImpl.java index 2521b78aee86..848ffbe7a407 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacedCardHalfImpl.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacedCardHalfImpl.java @@ -114,17 +114,6 @@ public ModalDoubleFacedCard getParentCard() { return this.parentCard; } - @Override - public void setPT(int power, int toughness) { - this.setPT(new MageInt(power), new MageInt(toughness)); - } - - @Override - public void setPT(MageInt power, MageInt toughness) { - this.power = power; - this.toughness = toughness; - } - @Override public String getIdName() { // id must send to main card (popup card hint in game logs) diff --git a/Mage/src/main/java/mage/cards/TransformingDoubleFacedCard.java b/Mage/src/main/java/mage/cards/TransformingDoubleFacedCard.java new file mode 100644 index 000000000000..67f4f2f19651 --- /dev/null +++ b/Mage/src/main/java/mage/cards/TransformingDoubleFacedCard.java @@ -0,0 +1,125 @@ +package mage.cards; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.effects.Effect; +import mage.abilities.keyword.TransformAbility; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.target.Target; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public abstract class TransformingDoubleFacedCard extends CardImpl { + + protected TransformingDoubleFacedCardHalfImpl leftHalfCard; // main card in all zone + protected TransformingDoubleFacedCardHalfImpl rightHalfCard; // second side card, can be only in stack and battlefield zones + private boolean finalized = false; + + public TransformingDoubleFacedCard( + UUID ownerId, CardSetInfo setInfo, + CardType[] typesLeft, SubType[] subTypesLeft, String costsLeft, + String secondSideName, + CardType[] typesRight, SubType[] subTypesRight, String colorRight + ) { + this( + ownerId, setInfo, + new SuperType[]{}, typesLeft, subTypesLeft, costsLeft, + secondSideName, + new SuperType[]{}, typesRight, subTypesRight, colorRight + ); + } + + public TransformingDoubleFacedCard( + UUID ownerId, CardSetInfo setInfo, + SuperType[] superTypesLeft, CardType[] typesLeft, SubType[] subTypesLeft, String costsLeft, + String secondSideName, + SuperType[] superTypesRight, CardType[] typesRight, SubType[] subTypesRight, String colorRight + ) { + super(ownerId, setInfo, typesLeft, costsLeft); + this.leftHalfCard = new TransformingDoubleFacedCardHalfImpl(ownerId, setInfo, typesLeft, costsLeft); + this.rightHalfCard = new TransformingDoubleFacedCardHalfImpl(ownerId, setInfo, typesRight, ""); + for (SuperType superType : superTypesLeft) { + this.getLeftHalfCard().getSuperType().add(superType); + } + this.getLeftHalfCard().getSubtype().add(subTypesLeft); + for (SuperType superType : superTypesRight) { + this.getRightHalfCard().getSuperType().add(superType); + } + this.getRightHalfCard().setName(secondSideName); + this.getRightHalfCard().addSubType(subTypesRight); + this.getRightHalfCard().getColor().addColor(new ObjectColor(colorRight)); + } + + protected TransformingDoubleFacedCard(final TransformingDoubleFacedCard card) { + super(card); + this.leftHalfCard = card.leftHalfCard.copy(); + this.rightHalfCard = card.rightHalfCard.copy(); + if (!card.finalized) { + throw new IllegalStateException("Error, finalizeDFC needs to be called"); + } + this.finalized = true; + } + + public Card getLeftHalfCard() { + return leftHalfCard; + } + + public Card getRightHalfCard() { + return rightHalfCard; + } + + protected void finalizeDFC() { + this.getSuperType().addAll(this.getLeftHalfCard().getSuperType()); + this.getSubtype().addAll(this.getLeftHalfCard().getSubtype()); + for (Ability ability : this.getLeftHalfCard().getAbilities()) { + if (!ability.equals(this.getLeftHalfCard().getSpellAbility())) { + this.addAbility(ability); + } + } + if (!this.getAbilities().containsClass(TransformAbility.class)) { + this.addAbility(new TransformAbility()); + } + if (this.getLeftHalfCard().getSpellAbility() != null) { + for (Effect effect : this.getLeftHalfCard().getSpellAbility().getEffects()) { + this.getSpellAbility().addEffect(effect); + } + for (Target target : this.getLeftHalfCard().getSpellAbility().getTargets()) { + this.getSpellAbility().addTarget(target); + } + this.getSpellAbility().setTargetAdjuster(this.getLeftHalfCard().getSpellAbility().getTargetAdjuster()); + } + this.power = this.getLeftHalfCard().getPower().copy(); + this.toughness = this.getLeftHalfCard().getToughness().copy(); + this.startingLoyalty = this.getLeftHalfCard().getStartingLoyalty(); + this.startingDefense = this.getLeftHalfCard().getStartingDefense(); + this.finalized = true; + } + + public static void copyToBackFace(TransformingDoubleFacedCard tdfc, Card card) { + card.getColor().setColor(tdfc.getRightHalfCard().getColor()); + card.getSuperType().addAll(tdfc.getRightHalfCard().getSuperType()); + card.getSubtype().addAll(tdfc.getRightHalfCard().getSubtype()); + for (Ability ability : tdfc.getRightHalfCard().getAbilities()) { + if (!ability.equals(tdfc.getRightHalfCard().getSpellAbility())) { + card.addAbility(ability); + } + } + if (tdfc.getRightHalfCard().getSpellAbility() != null) { + for (Effect effect : tdfc.getRightHalfCard().getSpellAbility().getEffects()) { + card.getSpellAbility().addEffect(effect); + } + for (Target target : tdfc.getRightHalfCard().getSpellAbility().getTargets()) { + card.getSpellAbility().addTarget(target); + } + card.getSpellAbility().setTargetAdjuster(tdfc.getRightHalfCard().getSpellAbility().getTargetAdjuster()); + } + card.setPT(tdfc.getRightHalfCard().getPower().copy(), tdfc.getRightHalfCard().getToughness().copy()); + card.setStartingLoyalty(tdfc.getRightHalfCard().getStartingLoyalty()); + card.setStartingDefense(tdfc.getRightHalfCard().getStartingDefense()); + } +} diff --git a/Mage/src/main/java/mage/cards/TransformingDoubleFacedCardHalfImpl.java b/Mage/src/main/java/mage/cards/TransformingDoubleFacedCardHalfImpl.java new file mode 100644 index 000000000000..0f44ad512beb --- /dev/null +++ b/Mage/src/main/java/mage/cards/TransformingDoubleFacedCardHalfImpl.java @@ -0,0 +1,24 @@ +package mage.cards; + +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class TransformingDoubleFacedCardHalfImpl extends CardImpl { + + TransformingDoubleFacedCardHalfImpl(UUID ownerId, CardSetInfo setInfo, CardType[] cardTypes, String costs) { + super(ownerId, setInfo, cardTypes, costs); + } + + private TransformingDoubleFacedCardHalfImpl(final TransformingDoubleFacedCardHalfImpl card) { + super(card); + } + + @Override + public TransformingDoubleFacedCardHalfImpl copy() { + return new TransformingDoubleFacedCardHalfImpl(this); + } +} diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 7cc905df6b60..24a34b321fdb 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -777,6 +777,16 @@ public void setControllerId(UUID controllerId) { public void setOwnerId(UUID controllerId) { } + @Override + public void setPT(int power, int toughness) { + throw new UnsupportedOperationException("Unsupported operation"); + } + + @Override + public void setPT(MageInt power, MageInt toughness) { + throw new UnsupportedOperationException("Unsupported operation"); + } + @Override public List getRules() { return card.getRules();