diff --git a/Mage.Sets/src/mage/cards/s/ShadowgrangeArchfiend.java b/Mage.Sets/src/mage/cards/s/ShadowgrangeArchfiend.java new file mode 100644 index 0000000000..301c228303 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShadowgrangeArchfiend.java @@ -0,0 +1,138 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.MadnessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author Alex-Vasile + */ +public final class ShadowgrangeArchfiend extends CardImpl { + public ShadowgrangeArchfiend(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{B}"); + this.subtype.add(SubType.DEMON); + + this.power = new MageInt(8); + this.toughness = new MageInt(4); + + // When Shadowgrange Archfiend enters the battlefield, + // each opponent sacrifices a creature with the greatest power among creatures they control. + // You gain life equal to the greatest power among creatures sacrificed this way. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ShadowgrangeArchfiendEffect())); + + // Madness—{2}{B}, Pay 8 life. + MadnessAbility madnessAbility = new MadnessAbility(this, new ManaCostsImpl<>("{2}{B}"), 8); + this.addAbility(madnessAbility); + } + + private ShadowgrangeArchfiend(final ShadowgrangeArchfiend card) { super(card); } + + @Override + public ShadowgrangeArchfiend copy() { return new ShadowgrangeArchfiend(this); } +} + +class ShadowgrangeArchfiendEffect extends OneShotEffect { + + public ShadowgrangeArchfiendEffect() { + super(Outcome.Benefit); + this.staticText = "each opponent sacrifices a creature with the greatest power among creatures they control. " + + "You gain life equal to the greatest power among creatures sacrificed this way"; + } + + private ShadowgrangeArchfiendEffect(final ShadowgrangeArchfiendEffect effect) { super(effect); } + + @Override + public ShadowgrangeArchfiendEffect copy() { return new ShadowgrangeArchfiendEffect(this); } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) {return false; } + + List<Permanent> toSacrifice = new ArrayList<>(); + + // Iterate through each opponent + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + if (!controller.hasOpponent(playerId, game)) { continue; } + + Player opponent = game.getPlayer(playerId); + if (opponent == null) { continue; } + + int greatestPower = Integer.MIN_VALUE; + int numberOfCreatures = 0; + Permanent creatureToSacrifice = null; + + // Iterature through each creature + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, playerId, game)) { + if (permanent.getPower().getValue() > greatestPower) { + greatestPower = permanent.getPower().getValue(); + numberOfCreatures = 1; + creatureToSacrifice = permanent; + } else if (permanent.getPower().getValue() == greatestPower) { + numberOfCreatures++; + } + } + + // If multiple creatures are tied for having the greatest power + if (numberOfCreatures > 1) { + FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent( + "creature to sacrifice with power equal to " + greatestPower); + filter.add(new PowerPredicate(ComparisonType.EQUAL_TO, greatestPower)); + Target target = new TargetControlledCreaturePermanent(filter); + if (opponent.choose(outcome, target, playerId, game)) { + creatureToSacrifice = game.getPermanent(target.getFirstTarget()); + } + } + + if (creatureToSacrifice != null) { + toSacrifice.add(creatureToSacrifice); + } + } + + int greatestPowerAmongAllCreaturesSacked = Integer.MIN_VALUE; + int powerOfCurrentCreature; + + // Sack the creatures and save the greaterest power amoung those which were sacked + for (Permanent permanent : toSacrifice) { + powerOfCurrentCreature = permanent.getPower().getValue(); + + // Try to sack it + if (permanent.sacrifice(source, game)) { + if (powerOfCurrentCreature > greatestPowerAmongAllCreaturesSacked) { + greatestPowerAmongAllCreaturesSacked = powerOfCurrentCreature; + } + } + } + + // Gain life equal to the power of greatest creature sacked, if it is positive + if (greatestPowerAmongAllCreaturesSacked > 0) { + new GainLifeEffect(greatestPowerAmongAllCreaturesSacked).apply(game, source); + } + + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/CrimsonVowCommander.java b/Mage.Sets/src/mage/sets/CrimsonVowCommander.java index b12fde7584..c07dbcfa52 100644 --- a/Mage.Sets/src/mage/sets/CrimsonVowCommander.java +++ b/Mage.Sets/src/mage/sets/CrimsonVowCommander.java @@ -131,6 +131,8 @@ public final class CrimsonVowCommander extends ExpansionSet { cards.add(new SetCardInfo("Scion of Opulence", 28, Rarity.RARE, mage.cards.s.ScionOfOpulence.class)); cards.add(new SetCardInfo("Shacklegeist", 112, Rarity.RARE, mage.cards.s.Shacklegeist.class)); cards.add(new SetCardInfo("Shadowblood Ridge", 181, Rarity.RARE, mage.cards.s.ShadowbloodRidge.class)); + cards.add(new SetCardInfo("Shadowgrange Archfiend", 22, Rarity.RARE, mage.cards.s.ShadowgrangeArchfiend.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadowgrange Archfiend", 60, Rarity.RARE, mage.cards.s.ShadowgrangeArchfiend.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sinister Waltz", 30, Rarity.RARE, mage.cards.s.SinisterWaltz.class)); cards.add(new SetCardInfo("Sire of the Storm", 113, Rarity.UNCOMMON, mage.cards.s.SireOfTheStorm.class)); cards.add(new SetCardInfo("Sky Diamond", 167, Rarity.COMMON, mage.cards.s.SkyDiamond.class)); diff --git a/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java b/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java index 65a9352d67..366da4513b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java @@ -1,11 +1,9 @@ package mage.abilities.keyword; import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.abilities.StaticAbility; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.*; import mage.abilities.condition.Condition; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.effects.OneShotEffect; @@ -52,14 +50,28 @@ public class MadnessAbility extends StaticAbility { private final String rule; - @SuppressWarnings("unchecked") - public MadnessAbility(Card card, ManaCosts madnessCost) { - super(Zone.HAND, new MadnessReplacementEffect((ManaCosts<ManaCost>) madnessCost)); - addSubAbility(new MadnessTriggeredAbility((ManaCosts<ManaCost>) madnessCost, getOriginalId())); - rule = "Madness " + madnessCost.getText() + " <i>(If you discard this card, discard it into exile. When you do, cast it for its madness cost or put it into your graveyard.)</i>"; + public MadnessAbility(Card card, ManaCosts<ManaCost> madnessCost) { + this(card, madnessCost, 0); } - public MadnessAbility(final MadnessAbility ability) { + public MadnessAbility(Card card, ManaCosts<ManaCost> madnessCost, int lifeCost) { + super(Zone.HAND, new MadnessReplacementEffect(madnessCost, lifeCost)); + addSubAbility(new MadnessTriggeredAbility(madnessCost, lifeCost, getOriginalId())); + + String costText; + + if (lifeCost > 0) { + costText = "Madness—" + madnessCost.getText() + ", Pay " + lifeCost + " life."; + } else { + costText = "Madness " + madnessCost.getText(); + } + + this.rule = costText + " <i>(If you discard this card, discard it into exile. " + + "When you do, cast it for its madness cost or put it into your graveyard.)</i>"; + } + + + private MadnessAbility(final MadnessAbility ability) { super(ability); this.rule = ability.rule; } @@ -81,12 +93,21 @@ public class MadnessAbility extends StaticAbility { class MadnessReplacementEffect extends ReplacementEffectImpl { - public MadnessReplacementEffect(ManaCosts<ManaCost> madnessCost) { + public MadnessReplacementEffect(ManaCosts<ManaCost> madnessCost, int lifeCost) { super(Duration.EndOfGame, Outcome.Benefit); - staticText = "Madness " + madnessCost.getText() + " <i>(If you discard this card, you may cast it for its madness cost instead of putting it into your graveyard.)</i>"; + + String costText; + + if (lifeCost > 0) { + costText = "Madness—" + madnessCost.getText() + ", Pay " + lifeCost + " life."; + } else { + costText = "Madness " + madnessCost.getText(); + } + + staticText = costText + " <i>(If you discard this card, you may cast it for its madness cost instead of putting it into your graveyard.)</i>"; } - public MadnessReplacementEffect(final MadnessReplacementEffect effect) { + private MadnessReplacementEffect(final MadnessReplacementEffect effect) { super(effect); } @@ -103,18 +124,19 @@ class MadnessReplacementEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Card card = game.getCard(event.getTargetId()); - if (card != null) { - if (controller.moveCardToExileWithInfo(card, source.getSourceId(), "Madness", source, game, ((ZoneChangeEvent) event).getFromZone(), true)) { - game.applyEffects(); // needed to add Madness ability to cards (e.g. by Falkenrath Gorger) - GameEvent gameEvent = new MadnessCardExiledEvent(card.getId(), source, controller.getId()); - game.fireEvent(gameEvent); - } - return true; - } + if (controller == null) { return false; } + + Card card = game.getCard(event.getTargetId()); + if (card == null) { return false; } + + // TODO, deal with deprecated call + if (controller.moveCardToExileWithInfo(card, source.getSourceId(), "Madness", source, game, ((ZoneChangeEvent) event).getFromZone(), true)) { + game.applyEffects(); // needed to add Madness ability to cards (e.g. by Falkenrath Gorger) + GameEvent gameEvent = new MadnessCardExiledEvent(card.getId(), source, controller.getId()); + game.fireEvent(gameEvent); } - return false; + + return true; } @Override @@ -125,7 +147,8 @@ class MadnessReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { return event.getTargetId().equals(source.getSourceId()) - && ((ZoneChangeEvent) event).getFromZone() == Zone.HAND && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD; + && ((ZoneChangeEvent) event).getFromZone() == Zone.HAND + && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD; } } @@ -138,13 +161,13 @@ class MadnessTriggeredAbility extends TriggeredAbilityImpl { private final UUID madnessOriginalId; - MadnessTriggeredAbility(ManaCosts<ManaCost> madnessCost, UUID madnessOriginalId) { - super(Zone.EXILED, new MadnessCastEffect(madnessCost), true); + MadnessTriggeredAbility(ManaCosts<ManaCost> madnessCost, int lifeCost, UUID madnessOriginalId) { + super(Zone.EXILED, new MadnessCastEffect(madnessCost, lifeCost), true); this.madnessOriginalId = madnessOriginalId; this.setRuleVisible(false); } - MadnessTriggeredAbility(final MadnessTriggeredAbility ability) { + private MadnessTriggeredAbility(final MadnessTriggeredAbility ability) { super(ability); this.madnessOriginalId = ability.madnessOriginalId; } @@ -161,23 +184,23 @@ class MadnessTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getSourceId().equals(madnessOriginalId); // Check that the event was from the connected replacement effect + // Check that the event was from the connected replacement effect + return event.getSourceId().equals(madnessOriginalId); } @Override public boolean resolve(Game game) { - if (!super.resolve(game)) { - Card card = game.getCard(getSourceId()); - if (card != null) { - Player owner = game.getPlayer(card.getOwnerId()); - if (owner != null) { - // if cast was not successfull, the card is moved to graveyard - owner.moveCards(card, Zone.GRAVEYARD, this, game); - } - } - return false; - } - return true; + if (super.resolve(game)) { return true; } + + Card card = game.getCard(getSourceId()); + if (card == null) { return false; } + + Player owner = game.getPlayer(card.getOwnerId()); + if (owner == null) { return false; } + + // if cast was not successfull, the card is moved to graveyard + owner.moveCards(card, Zone.GRAVEYARD, this, game); + return false; } @Override @@ -189,43 +212,52 @@ class MadnessTriggeredAbility extends TriggeredAbilityImpl { class MadnessCastEffect extends OneShotEffect { private final ManaCosts<ManaCost> madnessCost; + private final int lifeCost; - public MadnessCastEffect(ManaCosts<ManaCost> madnessCost) { + public MadnessCastEffect(ManaCosts<ManaCost> madnessCost, int lifeCost) { super(Outcome.Benefit); this.madnessCost = madnessCost; - staticText = "you may cast it by paying " + madnessCost.getText() + " instead of putting it into your graveyard"; + this.lifeCost = lifeCost; + + String costText; + + if (lifeCost > 0) { + costText = madnessCost.getText() + " and " + lifeCost + " life"; + } else { + costText = madnessCost.getText(); + } + + staticText = "you may cast it by paying " + costText + " instead of putting it into your graveyard"; } - public MadnessCastEffect(final MadnessCastEffect effect) { + private MadnessCastEffect(final MadnessCastEffect effect) { super(effect); this.madnessCost = effect.madnessCost; + this.lifeCost = effect.lifeCost; } + @Override + public MadnessCastEffect copy() { return new MadnessCastEffect(this); } + @Override public boolean apply(Game game, Ability source) { Card card = game.getCard(source.getSourceId()); - if (card == null) { - return false; - } + if (card == null) { return false; } Player owner = game.getPlayer(card.getOwnerId()); - if (owner == null) { - return false; - } + if (owner == null) { return false; } - // replace with the new cost + // Replace with the new cost SpellAbility castByMadness = card.getSpellAbility().copy(); ManaCosts<ManaCost> costRef = castByMadness.getManaCostsToPay(); castByMadness.setSpellAbilityType(SpellAbilityType.BASE_ALTERNATE); castByMadness.setSpellAbilityCastMode(SpellAbilityCastMode.MADNESS); + castByMadness.getCosts().clear(); + castByMadness.addCost(new PayLifeCost(this.lifeCost)); costRef.clear(); costRef.add(madnessCost); - return owner.cast(castByMadness, game, false, new ApprovingObject(source, game)); - } - @Override - public MadnessCastEffect copy() { - return new MadnessCastEffect(this); + return owner.cast(castByMadness, game, false, new ApprovingObject(source, game)); } } @@ -236,12 +268,10 @@ enum MadnessCondition implements Condition { @Override public boolean apply(Game game, Ability source) { MageObject madnessSpell = game.getLastKnownInformation(source.getSourceId(), Zone.STACK, source.getSourceObjectZoneChangeCounter() - 1); - if (madnessSpell instanceof Spell) { - if (((Spell) madnessSpell).getSpellAbility() != null) { - return ((Spell) madnessSpell).getSpellAbility().getSpellAbilityCastMode() == SpellAbilityCastMode.MADNESS; - } - } - return false; - } + if (!(madnessSpell instanceof Spell)) { return false; } + if (((Spell) madnessSpell).getSpellAbility() == null) { return false; } + + return ((Spell) madnessSpell).getSpellAbility().getSpellAbilityCastMode() == SpellAbilityCastMode.MADNESS; + } }