mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
implement [MH3] Sorin of House Markov // Sorin, Ravenous Neonate
This commit is contained in:
parent
8002cdf88f
commit
79165f269c
7 changed files with 338 additions and 0 deletions
68
Mage.Sets/src/mage/cards/s/SorinOfHouseMarkov.java
Normal file
68
Mage.Sets/src/mage/cards/s/SorinOfHouseMarkov.java
Normal 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);
|
||||
}
|
||||
}
|
||||
109
Mage.Sets/src/mage/cards/s/SorinRavenousNeonate.java
Normal file
109
Mage.Sets/src/mage/cards/s/SorinRavenousNeonate.java
Normal 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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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 owner’s 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue