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,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;
}
}

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,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<UUID> 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<Card> 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<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);
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;
}
}

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