Merge pull request #9386 from weirddan455/braids

[DMU] Implemented Braids, Arisen Nightmare
This commit is contained in:
Daniel Bomar 2022-08-20 17:04:55 -05:00 committed by GitHub
commit c140b073ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 206 additions and 75 deletions

View file

@ -0,0 +1,129 @@
package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SharesCardTypePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetControlledPermanent;
import mage.util.CardUtil;
/**
*
* @author weirddan455
*/
public final class BraidsArisenNightmare extends CardImpl {
public BraidsArisenNightmare(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.NIGHTMARE);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// At the beginning of your end step, you may sacrifice an artifact, creature, enchantment, land, or planeswalker.
// If you do, each opponent may sacrifice a permanent that shares a card type with it.
// For each opponent who doesn't, that player loses 2 life and you draw a card.
this.addAbility(new BeginningOfYourEndStepTriggeredAbility(new BraidsArisenNightmareEffect(), true));
}
private BraidsArisenNightmare(final BraidsArisenNightmare card) {
super(card);
}
@Override
public BraidsArisenNightmare copy() {
return new BraidsArisenNightmare(this);
}
}
class BraidsArisenNightmareEffect extends OneShotEffect {
private static final FilterControlledPermanent filter = new FilterControlledPermanent("an artifact, creature, enchantment, land, or planeswalker");
static {
filter.add(Predicates.or(
CardType.ARTIFACT.getPredicate(),
CardType.CREATURE.getPredicate(),
CardType.ENCHANTMENT.getPredicate(),
CardType.LAND.getPredicate(),
CardType.PLANESWALKER.getPredicate()
));
}
public BraidsArisenNightmareEffect() {
super(Outcome.Sacrifice);
this.staticText = "you may sacrifice an artifact, creature, enchantment, land, or planeswalker. " +
"If you do, each opponent may sacrifice a permanent that shares a card type with it. " +
"For each opponent who doesn't, that player loses 2 life and you draw a card";
}
private BraidsArisenNightmareEffect(final BraidsArisenNightmareEffect effect) {
super(effect);
}
@Override
public BraidsArisenNightmareEffect copy() {
return new BraidsArisenNightmareEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true);
if (!target.canChoose(controller.getId(), source, game)) {
return false;
}
controller.chooseTarget(Outcome.Sacrifice, target, source, game);
Permanent permanent = game.getPermanent(target.getFirstTarget());
if (permanent == null) {
return false;
}
SharesCardTypePredicate predicate = new SharesCardTypePredicate(permanent.getCardType(game));
FilterControlledPermanent opponentFilter = new FilterControlledPermanent(predicate.toString());
opponentFilter.add(predicate);
if (!permanent.sacrifice(source, game)) {
return false;
}
for (UUID opponentId : game.getOpponents(controller.getId())) {
Player opponent = game.getPlayer(opponentId);
if (opponent == null) {
continue;
}
if (!braidsSacrifice(opponent, opponentFilter, game, source)) {
opponent.loseLife(2, game, source, false);
controller.drawCards(1, source, game);
}
}
return true;
}
private boolean braidsSacrifice(Player opponent, FilterControlledPermanent opponentFilter, Game game, Ability source) {
TargetControlledPermanent target = new TargetControlledPermanent(1, 1, opponentFilter, true);
if (!target.canChoose(opponent.getId(), source, game)) {
return false;
}
if (!opponent.chooseUse(Outcome.Sacrifice, "Sacrifice " + CardUtil.addArticle(opponentFilter.getMessage()) + '?', source, game)) {
return false;
}
opponent.chooseTarget(Outcome.Sacrifice, target, source, game);
Permanent permanent = game.getPermanent(target.getFirstTarget());
return permanent != null && permanent.sacrifice(source, game);
}
}

View file

@ -13,15 +13,13 @@ import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SharesCardTypePredicate;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import mage.target.targetadjustment.TargetAdjuster;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
/**
@ -85,22 +83,12 @@ enum ConfusionInTheRanksAdjuster implements TargetAdjuster {
return;
}
ability.getTargets().clear();
FilterPermanent filterTarget = new FilterPermanent();
String message = "";
SharesCardTypePredicate predicate = new SharesCardTypePredicate(enteringPermanent.getCardType(game));
FilterPermanent filterTarget = new FilterPermanent(predicate.toString() + " you don't control");
filterTarget.add(predicate);
filterTarget.add(Predicates.not(new ControllerIdPredicate(enteringPermanent.getControllerId())));
Set<CardType.CardTypePredicate> cardTypesPredicates = new HashSet<>(1);
for (CardType cardTypeEntering : enteringPermanent.getCardType(game)) {
cardTypesPredicates.add(cardTypeEntering.getPredicate());
if (!message.isEmpty()) {
message += "or ";
}
message += cardTypeEntering.toString().toLowerCase(Locale.ENGLISH) + ' ';
}
filterTarget.add(Predicates.or(cardTypesPredicates));
message += "you don't control";
filterTarget.setMessage(message);
TargetPermanent target = new TargetPermanent(filterTarget);
target.setTargetController(enteringPermanent.getControllerId());
ability.getTargets().add(target);
}
}
}

View file

@ -1,6 +1,5 @@
package mage.cards.c;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
@ -9,17 +8,14 @@ import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SharesCardTypePredicate;
import mage.game.Game;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.TargetSpell;
import mage.util.CardUtil;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author BetaSteward
@ -70,17 +66,9 @@ class CounterlashEffect extends OneShotEffect {
if (stackObject == null || controller == null) {
return false;
}
Set<Predicate<MageObject>> predicates = stackObject
.getCardType(game)
.stream()
.map(CardType::getPredicate)
.collect(Collectors.toSet());
game.getStack().counter(source.getFirstTarget(), source, game);
if (predicates.isEmpty()) {
return true;
}
FilterCard filter = new FilterCard();
filter.add(Predicates.or(predicates));
filter.add(new SharesCardTypePredicate(stackObject.getCardType(game)));
game.getStack().counter(source.getFirstTarget(), source, game);
CardUtil.castSpellWithAttributesForFree(controller, source, game, new CardsImpl(controller.getHand()), filter);
return true;
}

View file

@ -10,7 +10,7 @@ import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SharesCardTypePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
@ -112,24 +112,9 @@ class MartyrsBondEffect extends OneShotEffect {
Permanent saccedPermanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source);
Player controller = game.getPlayer(source.getControllerId());
if (controller != null && saccedPermanent != null) {
FilterControlledPermanent filter = new FilterControlledPermanent();
String message = "permanent with type (";
boolean firstType = true;
List<CardType.CardTypePredicate> cardTypes = new ArrayList<>();
for (CardType type : saccedPermanent.getCardType(game)) {
cardTypes.add(type.getPredicate());
if (firstType) {
message += type;
firstType = false;
} else {
message += " or " + type;
}
}
message += ") to sacrifice";
filter.add(Predicates.or(cardTypes));
filter.setMessage(message);
SharesCardTypePredicate predicate = new SharesCardTypePredicate(saccedPermanent.getCardType(game));
FilterControlledPermanent filter = new FilterControlledPermanent(predicate.toString());
filter.add(predicate);
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);

View file

@ -1,9 +1,7 @@
package mage.cards.s;
import java.util.HashSet;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfYourEndStepTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
@ -22,7 +20,7 @@ import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.common.FilterPermanentCard;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.mageobject.SharesCardTypePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
@ -85,8 +83,9 @@ class SpiritSistersCallDoIfEffect extends OneShotEffect {
if (card == null || game.getState().getZone(targetId) != Zone.GRAVEYARD) {
return false;
}
FilterControlledPermanent filter = new FilterControlledPermanent("a permanent that shares a card type with the chosen card");
filter.add(new SpiritSistersCallPredicate(new HashSet<CardType>(card.getCardType(game))));
SharesCardTypePredicate predicate = new SharesCardTypePredicate(card.getCardType(game));
FilterControlledPermanent filter = new FilterControlledPermanent(predicate.toString());
filter.add(predicate);
return new DoIfCostPaid(new SpiritSistersCallReturnToBattlefieldEffect(), new SacrificeTargetCost(filter)).apply(game, source);
}
}
@ -160,22 +159,3 @@ class SpiritSistersCallReplacementEffect extends ReplacementEffectImpl {
&& zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() != Zone.EXILED;
}
}
class SpiritSistersCallPredicate implements Predicate<MageObject> {
private final HashSet<CardType> cardTypes;
public SpiritSistersCallPredicate(HashSet<CardType> cardTypes) {
this.cardTypes = cardTypes;
}
@Override
public boolean apply(MageObject input, Game game) {
for (CardType type : input.getCardType(game)) {
if (cardTypes.contains(type)) {
return true;
}
}
return false;
}
}

View file

@ -31,6 +31,7 @@ public final class DominariaUnited extends ExpansionSet {
cards.add(new SetCardInfo("Archangel of Wrath", 3, Rarity.RARE, mage.cards.a.ArchangelOfWrath.class));
cards.add(new SetCardInfo("Aron, Benalia's Ruin", 292, Rarity.UNCOMMON, mage.cards.a.AronBenaliasRuin.class));
cards.add(new SetCardInfo("Benalish Sleeper", 8, Rarity.COMMON, mage.cards.b.BenalishSleeper.class));
cards.add(new SetCardInfo("Braids, Arisen Nightmare", 84, Rarity.RARE, mage.cards.b.BraidsArisenNightmare.class));
cards.add(new SetCardInfo("Caves of Koilos", 244, Rarity.RARE, mage.cards.c.CavesOfKoilos.class));
cards.add(new SetCardInfo("Charismatic Vanguard", 10, Rarity.COMMON, mage.cards.c.CharismaticVanguard.class));
cards.add(new SetCardInfo("Cut Down", 89, Rarity.UNCOMMON, mage.cards.c.CutDown.class));

View file

@ -0,0 +1,60 @@
package mage.filter.predicate.mageobject;
import mage.MageObject;
import mage.constants.CardType;
import mage.filter.predicate.Predicate;
import mage.game.Game;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Locale;
/**
*
* @author weirddan455
*/
public class SharesCardTypePredicate implements Predicate<MageObject> {
private final LinkedHashSet<CardType> cardTypes;
public SharesCardTypePredicate(Collection<CardType> cardTypes) {
this.cardTypes = new LinkedHashSet<>(cardTypes);
}
@Override
public boolean apply(MageObject input, Game game) {
for (CardType type : input.getCardType(game)) {
if (cardTypes.contains(type)) {
return true;
}
}
return false;
}
@Override
public String toString() {
Iterator<CardType> it = cardTypes.iterator();
switch(cardTypes.size()) {
case 0:
return "";
case 1:
return it.next().toString().toLowerCase(Locale.ENGLISH);
case 2:
return it.next().toString().toLowerCase(Locale.ENGLISH) + " or " + it.next().toString().toLowerCase(Locale.ENGLISH);
default: {
StringBuilder sb = new StringBuilder();
sb.append(it.next().toString().toLowerCase(Locale.ENGLISH));
while (it.hasNext()) {
CardType type = it.next();
sb.append(", ");
if (!it.hasNext()) {
sb.append("or ");
}
sb.append(type.toString().toLowerCase(Locale.ENGLISH));
}
return sb.toString();
}
}
}
}