From 5a4d755dbaa1c9fcbd0b7a6cc3c86a959bd93b84 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Wed, 5 Oct 2022 12:00:59 -0400 Subject: [PATCH] Some changes to meld implementation (#9620) * some surface-level tweaks to meld implementation * add default implementation for meldsWith method * move nightcard declaration to meldcard constructor * remove unused variable declaration --- .../cards/b/BriselaVoiceOfNightmares.java | 8 +- .../src/mage/cards/b/BrunaTheFadingLight.java | 1 + .../src/mage/cards/c/ChitteringHost.java | 30 +++-- .../mage/cards/g/GiselaTheBrokenBlade.java | 24 ++-- Mage.Sets/src/mage/cards/g/GrafRats.java | 19 +-- .../src/mage/cards/h/HanweirBattlements.java | 27 ++-- .../src/mage/cards/h/HanweirGarrison.java | 5 +- .../cards/h/HanweirTheWrithingTownship.java | 14 +-- .../src/mage/cards/m/MidnightScavengers.java | 9 +- .../cards/t/TheMightstoneAndWeakstone.java | 2 + .../src/mage/cards/u/UrzaLordProtector.java | 18 +-- .../src/mage/cards/u/UrzaPlaneswalker.java | 1 - .../condition/common/MeldCondition.java | 47 ++++--- .../abilities/effects/common/MeldEffect.java | 118 ++++++++++-------- Mage/src/main/java/mage/cards/Card.java | 4 + Mage/src/main/java/mage/cards/CardImpl.java | 14 ++- Mage/src/main/java/mage/cards/MeldCard.java | 1 + .../mage/cards/repository/CardCriteria.java | 9 +- 18 files changed, 197 insertions(+), 154 deletions(-) diff --git a/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java b/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java index e6cce7f879..b96918913b 100644 --- a/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java +++ b/Mage.Sets/src/mage/cards/b/BriselaVoiceOfNightmares.java @@ -1,4 +1,3 @@ - package mage.cards.b; import mage.MageInt; @@ -20,7 +19,6 @@ import mage.game.stack.Spell; import java.util.UUID; /** - * * @author LevelX2 */ public final class BriselaVoiceOfNightmares extends MeldCard { @@ -31,8 +29,6 @@ public final class BriselaVoiceOfNightmares extends MeldCard { this.power = new MageInt(9); this.toughness = new MageInt(10); - this.nightCard = true;// Meld card - // Flying this.addAbility(FlyingAbility.getInstance()); @@ -44,9 +40,9 @@ public final class BriselaVoiceOfNightmares extends MeldCard { // Lifelink this.addAbility(LifelinkAbility.getInstance()); - + // Your opponents can't cast spells with converted mana cost 3 or less. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BriselaVoiceOfNightmaresCantCastEffect())); + this.addAbility(new SimpleStaticAbility(new BriselaVoiceOfNightmaresCantCastEffect())); } private BriselaVoiceOfNightmares(final BriselaVoiceOfNightmares card) { diff --git a/Mage.Sets/src/mage/cards/b/BrunaTheFadingLight.java b/Mage.Sets/src/mage/cards/b/BrunaTheFadingLight.java index 967037b12e..5e438f6572 100644 --- a/Mage.Sets/src/mage/cards/b/BrunaTheFadingLight.java +++ b/Mage.Sets/src/mage/cards/b/BrunaTheFadingLight.java @@ -42,6 +42,7 @@ public final class BrunaTheFadingLight extends CardImpl { this.subtype.add(SubType.ANGEL, SubType.HORROR); this.power = new MageInt(5); this.toughness = new MageInt(7); + this.meldsWithClazz = mage.cards.g.GiselaTheBrokenBlade.class; // When you cast Bruna, the Fading Light, you may return target Angel or Human creature card from your graveyard to the battlefield. Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(); diff --git a/Mage.Sets/src/mage/cards/c/ChitteringHost.java b/Mage.Sets/src/mage/cards/c/ChitteringHost.java index b043ce4f79..c8dc964b1e 100644 --- a/Mage.Sets/src/mage/cards/c/ChitteringHost.java +++ b/Mage.Sets/src/mage/cards/c/ChitteringHost.java @@ -1,27 +1,26 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.MenaceAbility; import mage.cards.CardSetInfo; import mage.cards.MeldCard; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ChitteringHost extends MeldCard { + public ChitteringHost(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); this.subtype.add(SubType.ELDRAZI); @@ -29,21 +28,20 @@ public final class ChitteringHost extends MeldCard { this.power = new MageInt(5); this.toughness = new MageInt(6); - this.nightCard = true; // Meld card - // Haste this.addAbility(HasteAbility.getInstance()); - // Menace (This creature can't be blocked except by two or more creatures. + // Menace this.addAbility(new MenaceAbility()); // When Chittering Host enters the battlefield, other creatures you control get +1/+0 and gain menace until end of turn. - Effect effect = new BoostControlledEffect(1, 0, Duration.EndOfTurn, true); - effect.setText("other creatures you control get +1/+0"); - Ability ability = new EntersBattlefieldTriggeredAbility(effect, false); - effect = new GainAbilityAllEffect(new MenaceAbility(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("other creatures"), true); - effect.setText("and gain menace until end of turn"); - ability.addEffect(effect); + Ability ability = new EntersBattlefieldTriggeredAbility(new BoostControlledEffect( + 1, 0, Duration.EndOfTurn, true + ).setText("other creatures you control get +1/+0"), false); + ability.addEffect(new GainAbilityControlledEffect( + new MenaceAbility(false), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE, true + ).setText("and gain menace until end of turn")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GiselaTheBrokenBlade.java b/Mage.Sets/src/mage/cards/g/GiselaTheBrokenBlade.java index 0b582a198d..90038a94d1 100644 --- a/Mage.Sets/src/mage/cards/g/GiselaTheBrokenBlade.java +++ b/Mage.Sets/src/mage/cards/g/GiselaTheBrokenBlade.java @@ -1,12 +1,9 @@ - package mage.cards.g; -import java.util.UUID; - import mage.MageInt; import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.MeldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.MeldEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.FlyingAbility; @@ -15,15 +12,18 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Rarity; import mage.constants.SuperType; import mage.constants.TargetController; +import java.util.UUID; + /** * @author LevelX2 */ public final class GiselaTheBrokenBlade extends CardImpl { + private static final Condition condition = new MeldCondition("Bruna, the Fading Light"); + public GiselaTheBrokenBlade(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); addSuperType(SuperType.LEGENDARY); @@ -31,6 +31,7 @@ public final class GiselaTheBrokenBlade extends CardImpl { this.subtype.add(SubType.HORROR); this.power = new MageInt(4); this.toughness = new MageInt(3); + this.meldsWithClazz = mage.cards.b.BrunaTheFadingLight.class; // Flying this.addAbility(FlyingAbility.getInstance()); @@ -42,14 +43,9 @@ public final class GiselaTheBrokenBlade extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // At the beginning of your end step, if you both own and control Gisela, the Broken Blade and a creature named Bruna, the Fading Light, exile them, then meld them into Brisela, Voice of Nightmares. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility( - new MeldEffect("Bruna, the Fading Light", - new mage.cards.b.BriselaVoiceOfNightmares(ownerId, - new CardSetInfo("Brisela, Voice of Nightmares", "EMN", "15", Rarity.MYTHIC))), TargetController.YOU, false), - new MeldCondition("Bruna, the Fading Light"), - "At the beginning of your end step, if you both own and control {this} and a creature named Bruna, the Fading Light, exile them, " - + "then meld them into Brisela, Voice of Nightmares.")); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new MeldEffect( + "Bruna, the Fading Light", "Brisela, Voice of Nightmares" + ).setText("exile them, then meld them into Brisela, Voice of Nightmares"), TargetController.YOU, condition, false)); } private GiselaTheBrokenBlade(final GiselaTheBrokenBlade card) { @@ -60,4 +56,4 @@ public final class GiselaTheBrokenBlade extends CardImpl { public GiselaTheBrokenBlade copy() { return new GiselaTheBrokenBlade(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/g/GrafRats.java b/Mage.Sets/src/mage/cards/g/GrafRats.java index 50f88272ce..b3b3eb1078 100644 --- a/Mage.Sets/src/mage/cards/g/GrafRats.java +++ b/Mage.Sets/src/mage/cards/g/GrafRats.java @@ -1,10 +1,8 @@ - package mage.cards.g; -import java.util.UUID; - import mage.MageInt; import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.MeldCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.MeldEffect; @@ -12,25 +10,32 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Rarity; import mage.constants.TargetController; +import java.util.UUID; + /** * @author emerald000 */ public final class GrafRats extends CardImpl { + private static final Condition condition = new MeldCondition("Midnight Scavengers"); + public GrafRats(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.RAT); this.power = new MageInt(2); this.toughness = new MageInt(1); + this.meldsWithClazz = mage.cards.m.MidnightScavengers.class; // At the beginning of combat on your turn, if you both own and control Graf Rats and a creature named Midnight Scavengers, exile them, then meld them into Chittering Host. this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility(new MeldEffect("Midnight Scavengers", new mage.cards.c.ChitteringHost(ownerId, new CardSetInfo("Chittering Host", "EMN", "96", Rarity.COMMON))), TargetController.YOU, false), - new MeldCondition("Midnight Scavengers"), - "At the beginning of combat on your turn, if you both own and control {this} and a creature named Midnight Scavengers, exile them, then meld them into Chittering Host.")); + new BeginningOfCombatTriggeredAbility(new MeldEffect( + "Midnight Scavengers", "Chittering Host" + ), TargetController.YOU, false), condition, "At the beginning " + + "of combat on your turn, if you both own and control {this} and a creature " + + "named Midnight Scavengers, exile them, then meld them into Chittering Host." + )); } private GrafRats(final GrafRats card) { diff --git a/Mage.Sets/src/mage/cards/h/HanweirBattlements.java b/Mage.Sets/src/mage/cards/h/HanweirBattlements.java index 9400f36b7b..d3e30ca1bd 100644 --- a/Mage.Sets/src/mage/cards/h/HanweirBattlements.java +++ b/Mage.Sets/src/mage/cards/h/HanweirBattlements.java @@ -1,9 +1,8 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.MeldCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; @@ -16,33 +15,39 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Rarity; import mage.constants.Zone; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class HanweirBattlements extends CardImpl { + private static final Condition condition = new MeldCondition("Hanweir Garrison"); + public HanweirBattlements(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + this.meldsWithClazz = mage.cards.h.HanweirGarrison.class; // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); + // {R},{T}: Target creature gains haste until end of turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl<>("{R}")); + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ), new ManaCostsImpl<>("{R}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // {3}{R}{R},{T}: If you both own and control Hanweir Battlements and a creature named Hanweir Garrison, exile them, then meld them into Hanweir, the Writhing Township. - ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, - new MeldEffect("Hanweir Garrison", - new HanweirTheWrithingTownship(ownerId, new CardSetInfo("Hanweir, the Writhing Township", "EMN", "130", Rarity.RARE))), - new ManaCostsImpl<>("{3}{R}{R}"), new MeldCondition("Hanweir Garrison"), - "{3}{R}{R}, {T}: If you both own and control {this} and a creature named Hanweir Garrison, exile them, then meld them into Hanweir, the Writhing Township."); + ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, new MeldEffect("Hanweir Garrison", "Hanweir, the Writhing Township"), + new ManaCostsImpl<>("{3}{R}{R}"), condition, "{3}{R}{R}, {T}: If you both own and control {this} " + + "and a creature named Hanweir Garrison, exile them, then meld them into Hanweir, the Writhing Township." + ); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HanweirGarrison.java b/Mage.Sets/src/mage/cards/h/HanweirGarrison.java index 670b6be286..fd1c762372 100644 --- a/Mage.Sets/src/mage/cards/h/HanweirGarrison.java +++ b/Mage.Sets/src/mage/cards/h/HanweirGarrison.java @@ -1,7 +1,6 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -14,8 +13,9 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.game.permanent.token.RedHumanToken; +import java.util.UUID; + /** - * * @author fireshoes */ public final class HanweirGarrison extends CardImpl { @@ -26,6 +26,7 @@ public final class HanweirGarrison extends CardImpl { this.subtype.add(SubType.SOLDIER); this.power = new MageInt(2); this.toughness = new MageInt(3); + this.meldsWithClazz = mage.cards.h.HanweirBattlements.class; // Whenever Hanweir Garrison attacks, create two 1/1 red Human creature tokens tapped and attacking. this.addAbility(new AttacksTriggeredAbility(new CreateTokenEffect(new RedHumanToken(), 2, true, true), false)); diff --git a/Mage.Sets/src/mage/cards/h/HanweirTheWrithingTownship.java b/Mage.Sets/src/mage/cards/h/HanweirTheWrithingTownship.java index 48aeebada1..253558c471 100644 --- a/Mage.Sets/src/mage/cards/h/HanweirTheWrithingTownship.java +++ b/Mage.Sets/src/mage/cards/h/HanweirTheWrithingTownship.java @@ -1,7 +1,5 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; @@ -14,11 +12,13 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.game.permanent.token.EldraziHorrorToken; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class HanweirTheWrithingTownship extends MeldCard { + public HanweirTheWrithingTownship(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); addSuperType(SuperType.LEGENDARY); @@ -27,16 +27,16 @@ public final class HanweirTheWrithingTownship extends MeldCard { this.power = new MageInt(7); this.toughness = new MageInt(4); - this.nightCard = true;// Meld card - // Trample this.addAbility(TrampleAbility.getInstance()); // Haste this.addAbility(HasteAbility.getInstance()); - + // Whenever Hanweir, the Writhing Township attacks, create two 3/2 colorless Eldrazi Horror creature tokens tapped and attacking. - this.addAbility(new AttacksTriggeredAbility(new CreateTokenEffect(new EldraziHorrorToken(), 2, true, true), false)); + this.addAbility(new AttacksTriggeredAbility(new CreateTokenEffect( + new EldraziHorrorToken(), 2, true, true + ), false)); } private HanweirTheWrithingTownship(final HanweirTheWrithingTownship card) { diff --git a/Mage.Sets/src/mage/cards/m/MidnightScavengers.java b/Mage.Sets/src/mage/cards/m/MidnightScavengers.java index c6ac8ba52f..a1f2d7ff2b 100644 --- a/Mage.Sets/src/mage/cards/m/MidnightScavengers.java +++ b/Mage.Sets/src/mage/cards/m/MidnightScavengers.java @@ -1,7 +1,6 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -11,16 +10,17 @@ import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ComparisonType; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author fireshoes */ public final class MidnightScavengers extends CardImpl { @@ -32,11 +32,12 @@ public final class MidnightScavengers extends CardImpl { } public MidnightScavengers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ROGUE); this.power = new MageInt(3); this.toughness = new MageInt(3); + this.meldsWithClazz = mage.cards.g.GrafRats.class; // When Midnight Scavengers enters the battlefield, you may return target creature card with converted mana cost 3 or less from your graveyard to your hand. Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect(), true); diff --git a/Mage.Sets/src/mage/cards/t/TheMightstoneAndWeakstone.java b/Mage.Sets/src/mage/cards/t/TheMightstoneAndWeakstone.java index b373808f61..274b595573 100644 --- a/Mage.Sets/src/mage/cards/t/TheMightstoneAndWeakstone.java +++ b/Mage.Sets/src/mage/cards/t/TheMightstoneAndWeakstone.java @@ -30,6 +30,8 @@ public final class TheMightstoneAndWeakstone extends CardImpl { this.addSuperType(SuperType.LEGENDARY); this.subtype.add(SubType.POWERSTONE); + this.meldsWithClazz = mage.cards.u.UrzaLordProtector.class; + // When The Mightstone and Weakstone enters the battlefield, choose one -- // * Draw two cards. Ability ability = new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)); diff --git a/Mage.Sets/src/mage/cards/u/UrzaLordProtector.java b/Mage.Sets/src/mage/cards/u/UrzaLordProtector.java index 03665b1cc6..79bce4ef25 100644 --- a/Mage.Sets/src/mage/cards/u/UrzaLordProtector.java +++ b/Mage.Sets/src/mage/cards/u/UrzaLordProtector.java @@ -12,7 +12,6 @@ import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Rarity; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterCard; @@ -35,7 +34,9 @@ public final class UrzaLordProtector extends CardImpl { )); } - private static final Condition condition = new MeldCondition("The Mightstone and Weakstone"); + private static final Condition condition = new MeldCondition( + "The Mightstone and Weakstone", CardType.ARTIFACT + ); public UrzaLordProtector(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); @@ -45,22 +46,15 @@ public final class UrzaLordProtector extends CardImpl { this.subtype.add(SubType.ARTIFICER); this.power = new MageInt(2); this.toughness = new MageInt(4); + this.meldsWithClazz = mage.cards.t.TheMightstoneAndWeakstone.class; // Artifact, instant, and sorcery spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); // {7}: If you both own and control Urza, Lord Protector and an artifact named The Mightstone and Weakstone, exile them, then meld them into Urza, Planeswalker. Activate only as a sorcery. this.addAbility(new SimpleActivatedAbility(new ConditionalOneShotEffect( - new MeldEffect( - "The Mightstone and Weakstone", - new UrzaPlaneswalker( - ownerId, - new CardSetInfo( - "Urza, Planeswalker", setInfo.getExpansionSetCode(), - "238b", Rarity.MYTHIC - ) - ) - ), condition, "If you both own and control {this} and an artifact named " + + new MeldEffect("The Mightstone and Weakstone", "Urza, Planeswalker"), + condition, "If you both own and control {this} and an artifact named " + "The Mightstone and Weakstone, exile them, then meld them into Urza, Planeswalker" ), new GenericManaCost(7))); } diff --git a/Mage.Sets/src/mage/cards/u/UrzaPlaneswalker.java b/Mage.Sets/src/mage/cards/u/UrzaPlaneswalker.java index 55c2111d00..1a09de8a0f 100644 --- a/Mage.Sets/src/mage/cards/u/UrzaPlaneswalker.java +++ b/Mage.Sets/src/mage/cards/u/UrzaPlaneswalker.java @@ -53,7 +53,6 @@ public final class UrzaPlaneswalker extends MeldCard { this.color.setWhite(true); this.color.setBlue(true); - this.nightCard = true; // You may activate the loyalty abilities of Urza, Planeswalker twice each turn rather than only once. this.addAbility(new SimpleStaticAbility(new UrzaPlaneswalkerEffect())); diff --git a/Mage/src/main/java/mage/abilities/condition/common/MeldCondition.java b/Mage/src/main/java/mage/abilities/condition/common/MeldCondition.java index 23cf52129b..d23f718be0 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/MeldCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/MeldCondition.java @@ -1,40 +1,49 @@ - package mage.abilities.condition.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.condition.Condition; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.NamePredicate; -import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; /** - * * @author emerald000 */ public class MeldCondition implements Condition { - private final String meldWithName; + private final String message; + private final FilterPermanent filter; public MeldCondition(String meldWithName) { - this.meldWithName = meldWithName; + this(meldWithName, CardType.CREATURE); + } + + public MeldCondition(String meldWithName, CardType cardType) { + this.message = "you both own and control {this} and " + + CardUtil.addArticle(cardType.toString().toLowerCase()) + + " named " + meldWithName; + this.filter = new FilterControlledPermanent(); + this.filter.add(TargetController.YOU.getOwnerPredicate()); + this.filter.add(cardType.getPredicate()); + this.filter.add(new NamePredicate(meldWithName)); } @Override public boolean apply(Game game, Ability source) { - MageObject sourceMageObject = source.getSourceObjectIfItStillExists(game); - if (sourceMageObject instanceof Permanent) { - Permanent sourcePermanent = (Permanent) sourceMageObject; - if (sourcePermanent.isControlledBy(source.getControllerId()) - && sourcePermanent.isOwnedBy(source.getControllerId())) { - FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); - filter.add(new NamePredicate(this.meldWithName)); - filter.add(new OwnerIdPredicate(source.getControllerId())); - return game.getBattlefield().count(filter, source.getControllerId(), source, game) > 0; - } - } - return false; + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + return sourcePermanent != null + && sourcePermanent.isControlledBy(source.getControllerId()) + && sourcePermanent.isOwnedBy(source.getControllerId()) + && game.getBattlefield().contains(filter, source, game, 1); + } + + @Override + public String toString() { + return message; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java b/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java index 21cc64104a..9d1edb5a3b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java @@ -1,41 +1,45 @@ package mage.abilities.effects.common; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.cards.MeldCard; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.NamePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; -import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.List; /** - * * @author emerald000 */ public class MeldEffect extends OneShotEffect { private final String meldWithName; - private final MeldCard meldCard; + private final String meldIntoName; - public MeldEffect(String meldWithName, MeldCard meldCard) { + public MeldEffect(String meldWithName, String meldIntoName) { super(Outcome.Benefit); this.meldWithName = meldWithName; - this.meldCard = meldCard; + this.meldIntoName = meldIntoName; } public MeldEffect(final MeldEffect effect) { super(effect); this.meldWithName = effect.meldWithName; - this.meldCard = effect.meldCard; + this.meldIntoName = effect.meldIntoName; } @Override @@ -46,48 +50,58 @@ public class MeldEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - // Find the two permanents to meld. - UUID sourceId = source != null ? source.getSourceId() : null; - FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature named " + meldWithName); - filter.add(new NamePredicate(meldWithName)); - TargetPermanent target = new TargetControlledCreaturePermanent(filter); - Set meldWithList = target.possibleTargets(source.getControllerId(), source, game); - if (meldWithList.isEmpty()) { - return false; // possible permanent has left the battlefield meanwhile - } - UUID meldWithId = null; - if (meldWithList.size() == 1) { - meldWithId = meldWithList.iterator().next(); - } else { - if (controller.choose(Outcome.BoostCreature, target, source, game)) { - meldWithId = target.getFirstTarget(); - } - } - // Exile the two permanents to meld. - Permanent sourcePermanent = game.getPermanent(sourceId); - Permanent meldWithPermanent = game.getPermanent(meldWithId); - if (sourcePermanent != null && meldWithPermanent != null) { - Set toExile = new HashSet<>(); - toExile.add(sourcePermanent); - toExile.add(meldWithPermanent); - controller.moveCards(toExile, Zone.EXILED, source, game); - // Create the meld card and move it to the battlefield. - Card sourceCard = game.getExile().getCard(sourceId, game); - Card meldWithCard = game.getExile().getCard(meldWithId, game); - if (sourceCard != null && !sourceCard.isCopy() && meldWithCard != null && !meldWithCard.isCopy()) { - meldCard.setOwnerId(controller.getId()); - meldCard.setTopHalfCard(meldWithCard, game); - meldCard.setBottomHalfCard(sourceCard, game); - meldCard.setMelded(true, game); - game.addMeldCard(meldCard.getId(), meldCard); - game.getState().addCard(meldCard); - meldCard.setZone(Zone.EXILED, game); - controller.moveCards(meldCard, Zone.BATTLEFIELD, source, game); - } - return true; - } + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (controller == null + || sourcePermanent == null + || !sourcePermanent.isControlledBy(controller.getId()) + || !sourcePermanent.isOwnedBy(controller.getId())) { + return false; } - return false; + // Find the two permanents to meld. + FilterPermanent filter = new FilterControlledPermanent("permanent named " + meldWithName); + filter.add(new NamePredicate(meldWithName)); + filter.add(TargetController.YOU.getOwnerPredicate()); + if (!game.getBattlefield().contains(filter, source, game, 1)) { + return false; + } + TargetPermanent target = new TargetPermanent(filter); + target.setNotTarget(true); + controller.choose(outcome, target, source, game); + + Permanent meldWithPermanent = game.getPermanent(target.getFirstTarget()); + if (sourcePermanent == null || meldWithPermanent == null) { + return false; + } + Cards cards = new CardsImpl(sourcePermanent); + cards.add(meldWithPermanent); + controller.moveCards(cards, Zone.EXILED, source, game); + // Create the meld card and move it to the battlefield. + Card sourceCard = cards.get(sourcePermanent.getId(), game); + Card meldWithCard = cards.get(meldWithPermanent.getId(), game); + if (sourceCard == null + || meldWithCard == null + || !sourceCard.meldsWith(meldWithCard) + || !meldWithCard.meldsWith(sourceCard)) { + return true; + } + List cardInfoList = CardRepository.instance.findCards( + new CardCriteria() + .name(meldIntoName) + .setCodes(sourceCard.getExpansionSetCode()) + .nightCard(true) + ); + if (cardInfoList.isEmpty()) { + return false; + } + MeldCard meldCard = (MeldCard) cardInfoList.get(0).getCard().copy(); + meldCard.setOwnerId(controller.getId()); + meldCard.setTopHalfCard(meldWithCard, game); + meldCard.setBottomHalfCard(sourceCard, game); + meldCard.setMelded(true, game); + game.addMeldCard(meldCard.getId(), meldCard); + game.getState().addCard(meldCard); + meldCard.setZone(Zone.EXILED, game); + controller.moveCards(meldCard, Zone.BATTLEFIELD, source, game); + return true; } } diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index 628eacd57c..a035a256fa 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -76,6 +76,10 @@ public interface Card extends MageObject { boolean isNightCard(); + default boolean meldsWith(Card card) { + return false; + } + void assignNewId(); void addInfo(String key, String value, Game game); diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 4757dcc29a..b035daed60 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -6,7 +6,10 @@ import mage.Mana; import mage.abilities.*; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.HasSubtypesSourceEffect; -import mage.abilities.keyword.*; +import mage.abilities.keyword.ChangelingAbility; +import mage.abilities.keyword.FlashbackAbility; +import mage.abilities.keyword.ReconfigureAbility; +import mage.abilities.keyword.SunburstAbility; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.repository.PluginClassloaderRegistery; import mage.constants.*; @@ -42,7 +45,8 @@ public abstract class CardImpl extends MageObjectImpl implements Card { protected String tokenSetCode; protected String tokenDescriptor; protected Rarity rarity; - protected Class secondSideCardClazz; + protected Class secondSideCardClazz; + protected Class meldsWithClazz; protected Card secondSideCard; protected boolean nightCard; protected SpellAbility spellAbility; @@ -122,6 +126,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { secondSideCardClazz = card.secondSideCardClazz; secondSideCard = null; // will be set on first getSecondCardFace call if card has one nightCard = card.nightCard; + meldsWithClazz = card.meldsWithClazz; spellAbility = null; // will be set on first getSpellAbility call if card has one flipCard = card.flipCard; @@ -643,6 +648,11 @@ public abstract class CardImpl extends MageObjectImpl implements Card { return secondFace.getSpellAbility(); } + @Override + public boolean meldsWith(Card card) { + return this.meldsWithClazz != null && this.meldsWithClazz.isInstance(card.getMainCard()); + } + @Override public boolean isNightCard() { return this.nightCard; diff --git a/Mage/src/main/java/mage/cards/MeldCard.java b/Mage/src/main/java/mage/cards/MeldCard.java index 7cc5988110..c8a8ee3a3e 100644 --- a/Mage/src/main/java/mage/cards/MeldCard.java +++ b/Mage/src/main/java/mage/cards/MeldCard.java @@ -25,6 +25,7 @@ public abstract class MeldCard extends CardImpl { public MeldCard(UUID ownerId, CardSetInfo setInfo, CardType[] cardTypes, String costs) { super(ownerId, setInfo, cardTypes, costs); halves = new CardsImpl(); + this.nightCard = true; } public MeldCard(final MeldCard card) { diff --git a/Mage/src/main/java/mage/cards/repository/CardCriteria.java b/Mage/src/main/java/mage/cards/repository/CardCriteria.java index b7553e0b7e..776de0daba 100644 --- a/Mage/src/main/java/mage/cards/repository/CardCriteria.java +++ b/Mage/src/main/java/mage/cards/repository/CardCriteria.java @@ -31,6 +31,7 @@ public class CardCriteria { private final List rarities; private Boolean doubleFaced; private Boolean modalDoubleFaced; + private boolean nightCard; private boolean black; private boolean blue; private boolean green; @@ -54,6 +55,7 @@ public class CardCriteria { this.supertypes = new ArrayList<>(); this.notSupertypes = new ArrayList<>(); this.subtypes = new ArrayList<>(); + this.nightCard = false; this.black = true; this.blue = true; @@ -106,6 +108,11 @@ public class CardCriteria { return this; } + public CardCriteria nightCard(boolean nightCard) { + this.nightCard = nightCard; + return this; + } + public CardCriteria name(String name) { this.name = name; return this; @@ -200,7 +207,7 @@ public class CardCriteria { optimize(); Where where = qb.where(); - where.eq("nightCard", false); + where.eq("nightCard", nightCard); where.eq("splitCardHalf", false); int clausesCount = 2; if (name != null) {