[M21] Implement sanctum cards (#6708)

This commit is contained in:
htrajan 2020-06-24 22:31:09 -07:00 committed by GitHub
parent 9b2c8c5d87
commit 7bb7afe28a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 393 additions and 13 deletions

View file

@ -1,24 +1,24 @@
package mage.cards.n;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.events.NumberOfTriggersEvent;
import java.util.UUID;
/**
*
* @author TheElk801
@ -35,7 +35,7 @@ public final class NabanDeanOfIteration extends CardImpl {
this.toughness = new MageInt(1);
// If a Wizard entering the battlefield under your control causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new NabanDeanOfIterationEffect()));
this.addAbility(new SimpleStaticAbility(new NabanDeanOfIterationEffect()));
}
public NabanDeanOfIteration(final NabanDeanOfIteration card) {
@ -84,9 +84,7 @@ class NabanDeanOfIterationEffect extends ReplacementEffectImpl {
// Only for entering artifacts or creatures
if (entersTheBattlefieldEvent.getTarget().hasSubtype(SubType.WIZARD, game)) {
// Only for triggers of permanents
if (game.getPermanent(numberOfTriggersEvent.getSourceId()) != null) {
return true;
}
return game.getPermanent(numberOfTriggersEvent.getSourceId()) != null;
}
}
}

View file

@ -0,0 +1,107 @@
package mage.cards.s;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.search.SearchLibraryGraveyardPutOntoBattlefieldEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.TargetController;
import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
*
* @author htrajan
*/
public final class SanctumOfAll extends CardImpl {
private static final FilterPermanent filter = new FilterControlledPermanent();
private static final FilterCard filterCard = new FilterCard("a Shrine card");
static final PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter);
static {
filter.add(SubType.SHRINE.getPredicate());
filterCard.add(SubType.SHRINE.getPredicate());
}
public SanctumOfAll(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}{U}{B}{R}{G}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.SHRINE);
// At the beginning of your upkeep, you may search your library and/or graveyard for a Shrine card and put it onto the battlefield. If you search your library this way, shuffle it.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SearchLibraryGraveyardPutOntoBattlefieldEffect(filterCard), TargetController.YOU, true));
// If an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time.
this.addAbility(new SimpleStaticAbility(new SanctumOfAllTriggerEffect()).addHint(new ValueHint("Shrines you control", count)));
}
private SanctumOfAll(final SanctumOfAll card) {
super(card);
}
@Override
public SanctumOfAll copy() {
return new SanctumOfAll(this);
}
}
class SanctumOfAllTriggerEffect extends ReplacementEffectImpl {
SanctumOfAllTriggerEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "if an ability of another Shrine you control triggers while you control six or more Shrines, that ability triggers an additional time";
}
private SanctumOfAllTriggerEffect(SanctumOfAllTriggerEffect effect) {
super(effect);
}
@Override
public SanctumOfAllTriggerEffect copy() {
return new SanctumOfAllTriggerEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
event.setAmount(event.getAmount() + 1);
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.NUMBER_OF_TRIGGERS;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
// Only triggers of the controller of Sanctum of All
if (source.isControlledBy(event.getPlayerId())) {
// Only trigger while you control six or more Shrines
int numShrines = SanctumOfAll.count.calculate(game, source, this);
if (numShrines >= 6) {
// Only for triggers of Shrines
Permanent permanent = game.getPermanent(event.getSourceId());
return permanent != null && permanent.hasSubtype(SubType.SHRINE, game);
}
}
return false;
}
}

View file

@ -5,12 +5,14 @@ import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.discard.DiscardControllerEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.TargetController;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledPermanent;
import java.util.UUID;
@ -21,6 +23,13 @@ import java.util.UUID;
public final class SanctumOfCalmWaters extends CardImpl {
private static final FilterPermanent filter = new FilterControlledPermanent();
private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filter);
static {
filter.add(SubType.SHRINE.getPredicate());
}
public SanctumOfCalmWaters(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}");
@ -28,9 +37,10 @@ public final class SanctumOfCalmWaters extends CardImpl {
this.subtype.add(SubType.SHRINE);
// At the beginning of your precombat main phase, you may draw X cards, where X is the number of Shrines you control. If you do, discard a card.
Ability ability = new BeginningOfPreCombatMainTriggeredAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.SHRINE)))
.setText("At the beginning of your precombat main phase, you may draw X cards, where X is the number of Shrines you control"),
TargetController.YOU, true);
Ability ability = new BeginningOfPreCombatMainTriggeredAbility(new DrawCardSourceControllerEffect(xValue)
.setText("you may draw X cards, where X is the number of Shrines you control"),
TargetController.YOU, true)
.addHint(new ValueHint("Shrines you control", xValue));
ability.addEffect(new DiscardControllerEffect(1).setText("If you do, discard a card"));
this.addAbility(ability);
}

View file

@ -0,0 +1,57 @@
package mage.cards.s;
import mage.Mana;
import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.mana.DynamicManaEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.TargetController;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledPermanent;
import java.util.UUID;
/**
*
* @author htrajan
*/
public final class SanctumOfFruitfulHarvest extends CardImpl {
private static final FilterPermanent filter = new FilterControlledPermanent();
private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filter);
static {
filter.add(SubType.SHRINE.getPredicate());
}
public SanctumOfFruitfulHarvest(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.SHRINE);
// At the beginning of your precombat main phase, add X mana of any one color, where X is the number of Shrines you control.
this.addAbility(new BeginningOfPreCombatMainTriggeredAbility(
new DynamicManaEffect(
Mana.AnyMana(1),
xValue,
"add X mana of any one color, where X is the number of Shrines you control",
true),
TargetController.YOU, false)
.addHint(new ValueHint("Shrines you control", xValue)));
}
private SanctumOfFruitfulHarvest(final SanctumOfFruitfulHarvest card) {
super(card);
}
@Override
public SanctumOfFruitfulHarvest copy() {
return new SanctumOfFruitfulHarvest(this);
}
}

View file

@ -6,6 +6,7 @@ import mage.abilities.costs.common.DiscardTargetCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
@ -13,9 +14,11 @@ import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.Predicates;
import mage.target.common.TargetCardInHand;
import mage.target.common.TargetCreatureOrPlaneswalker;
import java.util.UUID;
@ -26,9 +29,12 @@ import java.util.UUID;
public final class SanctumOfShatteredHeights extends CardImpl {
private static final FilterCard filter = new FilterCard("a land card or Shrine card");
private static final FilterPermanent filterShrinesOnly = new FilterControlledPermanent("Shrine you control");
private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filterShrinesOnly);
static {
filter.add(Predicates.or(CardType.LAND.getPredicate(), SubType.SHRINE.getPredicate()));
filterShrinesOnly.add(SubType.SHRINE.getPredicate());
}
public SanctumOfShatteredHeights(UUID ownerId, CardSetInfo setInfo) {
@ -38,10 +44,12 @@ public final class SanctumOfShatteredHeights extends CardImpl {
this.subtype.add(SubType.SHRINE);
// {1}, Discard a land card or Shrine card: Sanctum of Shattered Heights deals X damage to target creature or planeswalker, where X is the number of Shrines you control.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.SHRINE)))
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(xValue)
.setText("Sanctum of Shattered Heights deals X damage to target creature or planeswalker, where X is the number of Shrines you control"),
new ManaCostsImpl("{1}"));
new ManaCostsImpl<>("{1}"))
.addHint(new ValueHint("Shrines you control", xValue));
ability.addCost(new DiscardTargetCost(new TargetCardInHand(filter)));
ability.addTarget(new TargetCreatureOrPlaneswalker());
this.addAbility(ability);
}

View file

@ -0,0 +1,56 @@
package mage.cards.s;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.TargetController;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledPermanent;
import java.util.UUID;
/**
*
* @author htrajan
*/
public final class SanctumOfStoneFangs extends CardImpl {
private static final FilterPermanent filter = new FilterControlledPermanent("");
private static final PermanentsOnBattlefieldCount xValue = new PermanentsOnBattlefieldCount(filter, null);
static {
filter.add(SubType.SHRINE.getPredicate());
}
public SanctumOfStoneFangs(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.SHRINE);
// At the beginning of your precombat main phase, each opponent loses X life and you gain X life, where X is the number of Shrines you control.
Ability ability = new BeginningOfPreCombatMainTriggeredAbility(
new LoseLifeOpponentsEffect(xValue).setText("each opponent loses X life"),
TargetController.YOU, false)
.addHint(new ValueHint("Shrines you control", xValue));
ability.addEffect(new GainLifeEffect(xValue).setText("and you gain X life, where X is the number of Shrines you control"));
this.addAbility(ability);
}
private SanctumOfStoneFangs(final SanctumOfStoneFangs card) {
super(card);
}
@Override
public SanctumOfStoneFangs copy() {
return new SanctumOfStoneFangs(this);
}
}

View file

@ -0,0 +1,76 @@
package mage.cards.s;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.common.TapTargetEffect;
import mage.abilities.effects.common.cost.CostModificationSourceEffect;
import mage.abilities.hint.ValueHint;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledPermanent;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author htrajan
*/
public final class SanctumOfTranquilLight extends CardImpl {
private static final FilterPermanent filter = new FilterControlledPermanent("Shrine you control");
private static final PermanentsOnBattlefieldCount count = new PermanentsOnBattlefieldCount(filter);
static {
filter.add(SubType.SHRINE.getPredicate());
}
public SanctumOfTranquilLight(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.SHRINE);
// {5}{W}: Tap target creature. This ability costs {1} less to activate for each Shrine you control.
Ability ability = new SimpleActivatedAbility(new SanctumOfTranquilLightActivatedAbility());
ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability);
this.addAbility(new SimpleStaticAbility(Zone.ALL, new CostModificationSourceEffect(Duration.EndOfGame, Outcome.Benefit, SanctumOfTranquilLightActivatedAbility.class, count, true).setText(""))
.addHint(new ValueHint("Shrines you control", count)));
}
private SanctumOfTranquilLight(final SanctumOfTranquilLight card) {
super(card);
}
@Override
public SanctumOfTranquilLight copy() {
return new SanctumOfTranquilLight(this);
}
}
class SanctumOfTranquilLightActivatedAbility extends SimpleActivatedAbility {
SanctumOfTranquilLightActivatedAbility() {
super(new TapTargetEffect().setText("target creature. This ability costs {1} less to activate for each Shrine you control"), new ManaCostsImpl<>("{5}{W}"));
}
private SanctumOfTranquilLightActivatedAbility(SanctumOfTranquilLightActivatedAbility ability) {
super(ability);
}
@Override
public SanctumOfTranquilLightActivatedAbility copy() {
return new SanctumOfTranquilLightActivatedAbility(this);
}
}

View file

@ -217,8 +217,12 @@ public final class CoreSet2021 extends ExpansionSet {
cards.add(new SetCardInfo("Run Afoul", 201, Rarity.COMMON, mage.cards.r.RunAfoul.class));
cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class));
cards.add(new SetCardInfo("Sabertooth Mauler", 202, Rarity.COMMON, mage.cards.s.SabertoothMauler.class));
cards.add(new SetCardInfo("Sanctum of All", 225, Rarity.RARE, mage.cards.s.SanctumOfAll.class));
cards.add(new SetCardInfo("Sanctum of Calm Waters", 68, Rarity.UNCOMMON, mage.cards.s.SanctumOfCalmWaters.class));
cards.add(new SetCardInfo("Sanctum of Fruitful Harvest", 203, Rarity.UNCOMMON, mage.cards.s.SanctumOfFruitfulHarvest.class));
cards.add(new SetCardInfo("Sanctum of Shattered Heights", 157, Rarity.UNCOMMON, mage.cards.s.SanctumOfShatteredHeights.class));
cards.add(new SetCardInfo("Sanctum of Stone Fangs", 120, Rarity.UNCOMMON, mage.cards.s.SanctumOfStoneFangs.class));
cards.add(new SetCardInfo("Sanctum of Tranquil Light", 33, Rarity.UNCOMMON, mage.cards.s.SanctumOfTranquilLight.class));
cards.add(new SetCardInfo("Sanguine Indulgence", 121, Rarity.COMMON, mage.cards.s.SanguineIndulgence.class));
cards.add(new SetCardInfo("Scavenging Ooze", 204, Rarity.RARE, mage.cards.s.ScavengingOoze.class));
cards.add(new SetCardInfo("Scorching Dragonfire", 158, Rarity.COMMON, mage.cards.s.ScorchingDragonfire.class));

View file

@ -0,0 +1,64 @@
package mage.abilities.effects.common.cost;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.constants.CostModificationType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.UUID;
public class CostModificationSourceEffect extends CostModificationEffectImpl {
private final Class<? extends Ability> abilityType;
private final DynamicValue value;
private final boolean reducesCost;
public CostModificationSourceEffect(Duration duration, Outcome outcome, Class<? extends Ability> abilityType, DynamicValue value, boolean reducesCost) {
super(duration, outcome, reducesCost ? CostModificationType.REDUCE_COST : CostModificationType.INCREASE_COST);
this.abilityType = abilityType;
this.value = value;
this.reducesCost = reducesCost;
this.staticText = "this ability costs {1} " + (reducesCost ? "less" : "more") + " to activate for each " + value.getMessage();
}
private CostModificationSourceEffect(CostModificationSourceEffect effect) {
super(effect);
this.abilityType = effect.abilityType;
this.value = effect.value;
this.reducesCost = effect.reducesCost;
}
@Override
public CostModificationSourceEffect copy() {
return new CostModificationSourceEffect(this);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
int count = value.calculate(game, source, this);
if (reducesCost) {
CardUtil.reduceCost(abilityToModify, count);
} else {
CardUtil.increaseCost(abilityToModify, count);
}
}
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
return (abilityToModify.getClass().isAssignableFrom(abilityType)) && abilityToModify.getSourceId().equals(source.getSourceId());
}
@Override
public String getText(Mode mode) {
return super.getText(mode);
}
}