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
This commit is contained in:
Evan Kranzler 2022-10-05 12:00:59 -04:00 committed by GitHub
parent d21f2e43dd
commit 5a4d755dba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 197 additions and 154 deletions

View file

@ -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());
@ -46,7 +42,7 @@ public final class BriselaVoiceOfNightmares extends MeldCard {
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) {

View file

@ -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();

View file

@ -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 <i>(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);
}

View file

@ -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) {

View file

@ -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) {

View file

@ -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);
}

View file

@ -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));

View file

@ -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,8 +27,6 @@ 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());
@ -36,7 +34,9 @@ public final class HanweirTheWrithingTownship extends MeldCard {
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) {

View file

@ -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);

View file

@ -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));

View file

@ -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)));
}

View file

@ -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()));

View file

@ -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;
Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
return sourcePermanent != null
&& sourcePermanent.isControlledBy(source.getControllerId())
&& sourcePermanent.isOwnedBy(source.getControllerId())
&& game.getBattlefield().contains(filter, source, game, 1);
}
}
return false;
@Override
public String toString() {
return message;
}
}

View file

@ -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,36 +50,50 @@ public class MeldEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
if (controller == null
|| sourcePermanent == null
|| !sourcePermanent.isControlledBy(controller.getId())
|| !sourcePermanent.isOwnedBy(controller.getId())) {
return false;
}
// Find the two permanents to meld.
UUID sourceId = source != null ? source.getSourceId() : null;
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature named " + meldWithName);
FilterPermanent filter = new FilterControlledPermanent("permanent named " + meldWithName);
filter.add(new NamePredicate(meldWithName));
TargetPermanent target = new TargetControlledCreaturePermanent(filter);
Set<UUID> meldWithList = target.possibleTargets(source.getControllerId(), source, game);
if (meldWithList.isEmpty()) {
return false; // possible permanent has left the battlefield meanwhile
filter.add(TargetController.YOU.getOwnerPredicate());
if (!game.getBattlefield().contains(filter, source, game, 1)) {
return false;
}
UUID meldWithId = null;
if (meldWithList.size() == 1) {
meldWithId = meldWithList.iterator().next();
} else {
if (controller.choose(Outcome.BoostCreature, target, source, game)) {
meldWithId = target.getFirstTarget();
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;
}
}
// Exile the two permanents to meld.
Permanent sourcePermanent = game.getPermanent(sourceId);
Permanent meldWithPermanent = game.getPermanent(meldWithId);
if (sourcePermanent != null && meldWithPermanent != null) {
Set<Card> toExile = new HashSet<>();
toExile.add(sourcePermanent);
toExile.add(meldWithPermanent);
controller.moveCards(toExile, Zone.EXILED, source, game);
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 = game.getExile().getCard(sourceId, game);
Card meldWithCard = game.getExile().getCard(meldWithId, game);
if (sourceCard != null && !sourceCard.isCopy() && meldWithCard != null && !meldWithCard.isCopy()) {
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<CardInfo> 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);
@ -84,10 +102,6 @@ public class MeldEffect extends OneShotEffect {
game.getState().addCard(meldCard);
meldCard.setZone(Zone.EXILED, game);
controller.moveCards(meldCard, Zone.BATTLEFIELD, source, game);
}
return true;
}
}
return false;
}
}

View file

@ -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);

View file

@ -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<? extends Card> secondSideCardClazz;
protected Class<? extends Card> 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;

View file

@ -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) {

View file

@ -31,6 +31,7 @@ public class CardCriteria {
private final List<Rarity> 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) {