implement [MH3] Sorin of House Markov // Sorin, Ravenous Neonate

This commit is contained in:
Susucre 2024-05-16 21:17:21 +02:00
parent 8002cdf88f
commit 79165f269c
7 changed files with 338 additions and 0 deletions

View file

@ -0,0 +1,68 @@
package mage.cards.s;
import mage.MageInt;
import mage.abilities.Pronoun;
import mage.abilities.common.BeginningOfPostCombatMainTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.YouGainedLifeCondition;
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
import mage.abilities.effects.common.ExileAndReturnSourceEffect;
import mage.abilities.hint.ConditionHint;
import mage.abilities.hint.Hint;
import mage.abilities.keyword.ExtortAbility;
import mage.abilities.keyword.LifelinkAbility;
import mage.abilities.keyword.TransformAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.watchers.common.PlayerGainedLifeWatcher;
import java.util.UUID;
/**
* @author Susucr
*/
public final class SorinOfHouseMarkov extends CardImpl {
private static final Condition condition = new YouGainedLifeCondition(ComparisonType.OR_GREATER, 3);
private static final Hint hint = new ConditionHint(condition, "You gained 3 or more life this turn");
public SorinOfHouseMarkov(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.NOBLE);
this.power = new MageInt(1);
this.toughness = new MageInt(4);
this.secondSideCardClazz = SorinRavenousNeonate.class;
// Flying
this.addAbility(LifelinkAbility.getInstance());
// Extort
this.addAbility(new ExtortAbility());
// At the beginning of your postcombat main phase, if you gained 3 or more life this turn, exile Sorin of House Markov, then return him to the battlefield transformed under his owner's control.
this.addAbility(new TransformAbility());
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
new BeginningOfPostCombatMainTriggeredAbility(
new ExileAndReturnSourceEffect(PutCards.BATTLEFIELD_TRANSFORMED, Pronoun.SHE),
TargetController.YOU,
false
), condition, "At the beginning of your postcombat main phase, "
+ "if you gained 3 or more life this turn, exile {this}, "
+ "then return him to the battlefield transformed under his owner's control."
).addHint(hint), new PlayerGainedLifeWatcher());
}
private SorinOfHouseMarkov(final SorinOfHouseMarkov card) {
super(card);
}
@Override
public SorinOfHouseMarkov copy() {
return new SorinOfHouseMarkov(this);
}
}

View file

@ -0,0 +1,109 @@
package mage.cards.s;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.dynamicvalue.common.ControllerGainedLifeCount;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.continuous.BecomesCreatureTypeTargetEffect;
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.abilities.hint.ConditionHint;
import mage.abilities.hint.Hint;
import mage.abilities.keyword.ExtortAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.FoodToken;
import mage.target.common.TargetAnyTarget;
import mage.target.common.TargetCreaturePermanent;
import mage.watchers.common.PlayerGainedLifeWatcher;
import java.util.UUID;
/**
* @author Susucr
*/
public final class SorinRavenousNeonate extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("white permanent other than that creature or {this}");
private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, true);
private static final Hint hint = new ConditionHint(condition, "you control another white permanent");
static {
filter.add(new ColorPredicate(ObjectColor.WHITE));
filter.add(AnotherPredicate.instance);
filter.add(SorinRavenousNeonatePredicate.instance);
}
public SorinRavenousNeonate(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.SORIN);
this.setStartingLoyalty(3);
this.color.setWhite(true);
this.color.setBlack(true);
this.nightCard = true;
// Extort
this.addAbility(new ExtortAbility());
// +2: Create a Food token.
this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new FoodToken()), 2));
// -1: Sorin, Ravenous Neonate deals damage equal to the amount of life you gained this turn to any target.
Ability ability = new LoyaltyAbility(new DamageTargetEffect(ControllerGainedLifeCount.instance), -1);
ability.addTarget(new TargetAnyTarget());
this.addAbility(ability.addHint(ControllerGainedLifeCount.getHint()), new PlayerGainedLifeWatcher());
// -6: Gain control of target creature. It becomes a Vampire in addition to its other types. Put a lifelink counter on it if you control a white permanent other than that creature or Sorin.
ability = new LoyaltyAbility(new GainControlTargetEffect(Duration.EndOfGame), -6);
ability.addTarget(new TargetCreaturePermanent());
ability.addEffect(new BecomesCreatureTypeTargetEffect(Duration.EndOfGame, SubType.VAMPIRE, false)
.setText("It becomes a Vampire in addition to its other types"));
ability.addEffect(new ConditionalOneShotEffect(
new AddCountersTargetEffect(CounterType.LIFELINK.createInstance()),
condition, "Put a lifelink counter on it if you control a white permanent other than that creature or {this}"
));
this.addAbility(ability.addHint(hint));
}
private SorinRavenousNeonate(final SorinRavenousNeonate card) {
super(card);
}
@Override
public SorinRavenousNeonate copy() {
return new SorinRavenousNeonate(this);
}
}
enum SorinRavenousNeonatePredicate implements ObjectSourcePlayerPredicate<Permanent> {
instance;
@Override
public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
Ability source = input.getSource();
Permanent permanent = input.getObject();
return source != null
&& permanent != null
&& !permanent.getId().equals(source.getFirstTarget());
}
}

View file

@ -77,6 +77,8 @@ public final class ModernHorizons3 extends ExpansionSet {
cards.add(new SetCardInfo("Serum Visionary", 69, Rarity.COMMON, mage.cards.s.SerumVisionary.class));
cards.add(new SetCardInfo("Six", 169, Rarity.RARE, mage.cards.s.Six.class));
cards.add(new SetCardInfo("Snow-Covered Wastes", 229, Rarity.UNCOMMON, mage.cards.s.SnowCoveredWastes.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Sorin of House Markov", 245, Rarity.MYTHIC, mage.cards.s.SorinOfHouseMarkov.class));
cards.add(new SetCardInfo("Sorin, Ravenous Neonate", 245, Rarity.MYTHIC, mage.cards.s.SorinRavenousNeonate.class));
cards.add(new SetCardInfo("Spawn-Gang Commander", 140, Rarity.UNCOMMON, mage.cards.s.SpawnGangCommander.class));
cards.add(new SetCardInfo("Swamp", 306, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS));
cards.add(new SetCardInfo("Sylvan Safekeeper", 287, Rarity.RARE, mage.cards.s.SylvanSafekeeper.class));

View file

@ -0,0 +1,150 @@
package org.mage.test.cards.single.mh3;
import mage.constants.PhaseStep;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class SorinOfHouseMarkovTest extends CardTestPlayerBase {
/**
* {@link mage.cards.s.SorinOfHouseMarkov Sorin of House Markov} {1}{B}
* Legendary Creature Human Noble
* Lifelink
* Extort (Whenever you cast a spell, you may pay {W/B}. If you do, each opponent loses 1 life and you gain that much life.)
* At the beginning of your postcombat main phase, if you gained 3 or more life this turn, exile Sorin of House Markov, then him to the battlefield transformed under his owners control.
* 1/4
* // {@link mage.cards.s.SorinRavenousNeonate Sorin, Ravenous Neonate}
* Legendary Planeswalker Sorin
* Extort (Whenever you cast a spell, you may pay {W/B}. If you do, each opponent loses 1 life and you gain that much life.)
* +2: Create a Food token.
* 1: Sorin, Ravenous Neonate deals damage equal to the amount of life you gained this turn to any target.
* 6: Gain control of target creature. It becomes a Vampire in addition to its other types. Put a lifelink counter on it if you control a white permanent other than that creature or Sorin.
* Loyalty: 3
*/
private static final String sorin = "Sorin of House Markov";
private static final String sorinPW = "Sorin, Ravenous Neonate";
@Test
public void test_Gain2Life_NoTrigger() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, sorin);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.HAND, playerA, "Courier Griffin"); // {3}{W} etb, gain 2 life
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Courier Griffin");
setChoice(playerA, false); // no to Extort trigger
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Courier Griffin", 1);
assertPermanentCount(playerA, sorin, 1);
assertLife(playerA, 20 + 2);
}
@Test
public void test_Gain3Life_Trigger_ThenMinus1() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, sorin);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
addCard(Zone.HAND, playerA, "Courier Griffin"); // {3}{W} etb, gain 2 life
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Courier Griffin");
setChoice(playerA, true); // pay for Extort
checkPermanentCount("sorin did not transform yet", 1, PhaseStep.BEGIN_COMBAT, playerA, sorin, 1);
// Sorin triggers post combat
waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN, playerA, true);
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "-1", playerB);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Courier Griffin", 1);
assertLife(playerA, 20 + 3);
assertLife(playerB, 20 - 1 - 3);
assertPermanentCount(playerA, sorinPW, 1);
assertCounterCount(playerA, sorinPW, CounterType.LOYALTY, 3 - 1);
}
@Test
public void test_Plus2_Plus2_Minus1() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, sorinPW);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+2");
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "+2");
// Activate both foods
activateAbility(5, PhaseStep.UPKEEP, playerA, "{2}, {T}, Sacrifice this artifact: You gain 3 life");
activateAbility(5, PhaseStep.UPKEEP, playerA, "{2}, {T}, Sacrifice this artifact: You gain 3 life");
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "-1");
addTarget(playerA, playerB);
setStopAt(5, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20 + 6);
assertLife(playerB, 20 - 6);
assertPermanentCount(playerA, sorinPW, 1);
assertCounterCount(playerA, sorinPW, CounterType.LOYALTY, 3 + 2 + 2 - 1);
}
@Test
public void test_Minus6_NoOtherWhite() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, sorinPW);
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); // notably not white
addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard");
addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, sorinPW, CounterType.LOYALTY, 4);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-6");
addTarget(playerA, "Elite Vanguard");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Elite Vanguard", 1);
assertSubtype("Elite Vanguard", SubType.HUMAN);
assertSubtype("Elite Vanguard", SubType.VAMPIRE); // Vampire in addition
assertCounterCount(playerA, "Elite Vanguard", CounterType.LIFELINK, 0);
assertCounterCount(playerA, sorinPW, CounterType.LOYALTY, 1);
}
@Test
public void test_Minus6_OtherWhite() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, sorinPW);
addCard(Zone.BATTLEFIELD, playerA, "Baneslayer Angel"); // notably white
addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard");
addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, sorinPW, CounterType.LOYALTY, 4);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-6");
addTarget(playerA, "Elite Vanguard");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Elite Vanguard", 1);
assertSubtype("Elite Vanguard", SubType.HUMAN);
assertSubtype("Elite Vanguard", SubType.VAMPIRE); // Vampire in addition
assertCounterCount(playerA, "Elite Vanguard", CounterType.LIFELINK, 1);
assertCounterCount(playerA, sorinPW, CounterType.LOYALTY, 1);
}
}

View file

@ -7,6 +7,8 @@ import mage.game.Game;
import mage.watchers.common.PlayerGainedLifeWatcher;
/**
* Needs PlayerGainedLifeWatcher to work
* <p>
* Created by IGOUDT on 5-4-2017.
*/
public class YouGainedLifeCondition extends IntCompareCondition {

View file

@ -9,6 +9,7 @@ import mage.game.Game;
import mage.watchers.common.PlayerGainedLifeWatcher;
/**
* Needs PlayerGainedLifeWatcher to work.
* Amount of life the controller gained this turn.
*
* @author LevelX2

View file

@ -515,4 +515,10 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
}
return true;
}
@Override
public ContinuousEffect setText(String staticText) {
super.setText(staticText);
return this;
}
}