Implemented Xyris and Kalamax (#6430)

* Implemented Xyris, the Writhing Storm

* Name change for Xyris's draw ability.

* Implemented Kalamax, the Stormsire.

* Added Kalamax and Xyris to Commander2020Edition Set.

* Updated XyrisTheWrithingStorm drawCards implementation.

* Fixed bug where "First card drawn" was not enforced.

* Removed unnecessary Predicates.or, and replaced custom effect with CreateTokenEffect
This commit is contained in:
AsterAether 2020-04-24 15:39:53 +02:00 committed by GitHub
parent 02730a194c
commit 1804b8df01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 295 additions and 0 deletions

View file

@ -0,0 +1,137 @@
package mage.cards.k;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CopyTargetSpellEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.common.FilterInstantSpell;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.target.targetpointer.FixedTarget;
import mage.watchers.common.SpellsCastWatcher;
import java.util.List;
import java.util.UUID;
/**
* @author AsterAether
*/
public final class KalamaxTheStormsire extends CardImpl {
public KalamaxTheStormsire(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}{R}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.ELEMENTAL);
this.subtype.add(SubType.DINOSAUR);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Whenever you cast your first instant spell each turn, if Kalamax, the Stormsire is tapped, copy that spell. You may choose new targets for the copy.
this.addAbility(new KalamaxTheStormsireSpellCastAbility(), new SpellsCastWatcher());
// Whenever you copy an instant spell, put a +1/+1 counter on Kalamax.
this.addAbility(new KalamaxTheStormsireCopyTriggeredAbility());
}
private KalamaxTheStormsire(final KalamaxTheStormsire card) {
super(card);
}
@Override
public KalamaxTheStormsire copy() {
return new KalamaxTheStormsire(this);
}
}
class KalamaxTheStormsireSpellCastAbility extends SpellCastControllerTriggeredAbility {
KalamaxTheStormsireSpellCastAbility() {
super(new CopyTargetSpellEffect(true), new FilterInstantSpell(), false);
}
KalamaxTheStormsireSpellCastAbility(KalamaxTheStormsireSpellCastAbility ability) {
super(ability);
}
@Override
public KalamaxTheStormsireSpellCastAbility copy() {
return new KalamaxTheStormsireSpellCastAbility(this);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (super.checkTrigger(event, game)) {
SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class);
if (watcher != null) {
List<Spell> spells = watcher.getSpellsCastThisTurn(event.getPlayerId());
if (spells != null && spells.stream().filter(MageObject::isInstant).count() == 1) {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell != null && spell.isInstant()) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
}
return true;
}
}
}
}
return false;
}
@Override
public boolean checkInterveningIfClause(Game game) {
Permanent permanent = game.getPermanent(getSourceId());
return permanent != null && permanent.isTapped();
}
@Override
public String getRule() {
return "Whenever you cast your first instant spell each turn, " +
"if Kalamax, the Stormsire is tapped, " +
"copy that spell. You may choose new targets for the copy.";
}
}
class KalamaxTheStormsireCopyTriggeredAbility extends TriggeredAbilityImpl {
KalamaxTheStormsireCopyTriggeredAbility() {
super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false);
}
private KalamaxTheStormsireCopyTriggeredAbility(final KalamaxTheStormsireCopyTriggeredAbility effect) {
super(effect);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.COPIED_STACKOBJECT;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Spell spell = game.getSpell(event.getTargetId());
return spell != null && spell.isControlledBy(getControllerId()) && spell.isInstant();
}
@Override
public KalamaxTheStormsireCopyTriggeredAbility copy() {
return new KalamaxTheStormsireCopyTriggeredAbility(this);
}
@Override
public String getRule() {
return "Whenever you copy an instant spell, put a +1/+1 counter on {this}.";
}
}

View file

@ -0,0 +1,129 @@
package mage.cards.x;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.token.SnakeToken;
import mage.game.permanent.token.Token;
import mage.players.Player;
import mage.watchers.common.CardsDrawnDuringDrawStepWatcher;
import java.util.UUID;
/**
* @author AsterAether
*/
public final class XyrisTheWrithingStorm extends CardImpl {
public XyrisTheWrithingStorm(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}{R}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.SNAKE);
this.subtype.add(SubType.LEVIATHAN);
this.power = new MageInt(3);
this.toughness = new MageInt(5);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever an opponent draws a card except the first one they draw in each of their draw steps, create a 1/1 green Snake creature token.
this.addAbility(new XyrisTheWrithingStormDrawAbility(), new CardsDrawnDuringDrawStepWatcher());
// Whenever Xyris, the Writhing Storm deals combat damage to a player, you and that player each draw that many cards.
this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new XyrisTheWrithingStormCombatDamageEffect(), false, true));
}
private XyrisTheWrithingStorm(final XyrisTheWrithingStorm card) {
super(card);
}
@Override
public XyrisTheWrithingStorm copy() {
return new XyrisTheWrithingStorm(this);
}
}
class XyrisTheWrithingStormDrawAbility extends TriggeredAbilityImpl {
public XyrisTheWrithingStormDrawAbility() {
super(Zone.BATTLEFIELD, new CreateTokenEffect(new SnakeToken(), 1), false);
}
public XyrisTheWrithingStormDrawAbility(final XyrisTheWrithingStormDrawAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DREW_CARD;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (game.getPlayer(this.getControllerId()).hasOpponent(event.getPlayerId(), game)) {
if (game.isActivePlayer(event.getPlayerId())
&& game.getPhase().getStep().getType() == PhaseStep.DRAW) {
CardsDrawnDuringDrawStepWatcher watcher = game.getState().getWatcher(CardsDrawnDuringDrawStepWatcher.class);
if (watcher != null && watcher.getAmountCardsDrawn(event.getPlayerId()) > 1) {
return true;
}
} else {
return true;
}
}
return false;
}
@Override
public TriggeredAbility copy() {
return new XyrisTheWrithingStormDrawAbility(this);
}
@Override
public String getRule() {
return "Whenever an opponent draws a card except the first one they draw in each of their draw steps, create a 1/1 green Snake creature token.";
}
}
class XyrisTheWrithingStormCombatDamageEffect extends OneShotEffect {
public XyrisTheWrithingStormCombatDamageEffect() {
super(Outcome.DrawCard);
this.staticText = "you and that player each draw that many cards.";
}
public XyrisTheWrithingStormCombatDamageEffect(final XyrisTheWrithingStormCombatDamageEffect effect) {
super(effect);
}
@Override
public XyrisTheWrithingStormCombatDamageEffect copy() {
return new XyrisTheWrithingStormCombatDamageEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player sourceController = game.getPlayer(source.getControllerId());
Player damagedPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source));
if (sourceController != null && damagedPlayer != null) {
int amount = (Integer) getValue("damage");
if (amount > 0) {
sourceController.drawCards(amount, source.getSourceId(), game);
damagedPlayer.drawCards(amount, source.getSourceId(), game);
return true;
}
}
return false;
}
}

View file

@ -176,6 +176,7 @@ public final class Commander2020Edition extends ExpansionSet {
cards.add(new SetCardInfo("Izzet Signet", 243, Rarity.UNCOMMON, mage.cards.i.IzzetSignet.class));
cards.add(new SetCardInfo("Jace, Architect of Thought", 114, Rarity.MYTHIC, mage.cards.j.JaceArchitectOfThought.class));
cards.add(new SetCardInfo("Jirina Kudro", 8, Rarity.MYTHIC, mage.cards.j.JirinaKudro.class));
cards.add(new SetCardInfo("Kalamax, the Stormsire", 9, Rarity.MYTHIC, mage.cards.k.KalamaxTheStormsire.class));
cards.add(new SetCardInfo("Kalemne's Captain", 92, Rarity.RARE, mage.cards.k.KalemnesCaptain.class));
cards.add(new SetCardInfo("Karametra, God of Harvests", 218, Rarity.MYTHIC, mage.cards.k.KarametraGodOfHarvests.class));
cards.add(new SetCardInfo("Kathril, Aspect Warper", 10, Rarity.MYTHIC, mage.cards.k.KathrilAspectWarper.class));
@ -343,6 +344,7 @@ public final class Commander2020Edition extends ExpansionSet {
cards.add(new SetCardInfo("Wydwen, the Biting Gale", 235, Rarity.RARE, mage.cards.w.WydwenTheBitingGale.class));
cards.add(new SetCardInfo("Xathrid Necromancer", 141, Rarity.RARE, mage.cards.x.XathridNecromancer.class));
cards.add(new SetCardInfo("Yannik, Scavenging Sentinel", 19, Rarity.MYTHIC, mage.cards.y.YannikScavengingSentinel.class));
cards.add(new SetCardInfo("Xyris, the Writhing Storm", 18, Rarity.MYTHIC, mage.cards.x.XyrisTheWrithingStorm.class));
cards.add(new SetCardInfo("Yavimaya Coast", 322, Rarity.RARE, mage.cards.y.YavimayaCoast.class));
cards.add(new SetCardInfo("Yavimaya Dryad", 197, Rarity.UNCOMMON, mage.cards.y.YavimayaDryad.class));
cards.add(new SetCardInfo("Zaxara, the Exemplary", 20, Rarity.MYTHIC, mage.cards.z.ZaxaraTheExemplary.class));

View file

@ -0,0 +1,27 @@
package mage.filter.common;
import mage.constants.CardType;
import mage.filter.FilterSpell;
import mage.filter.predicate.Predicates;
public class FilterInstantSpell extends FilterSpell {
public FilterInstantSpell() {
this("instant spell");
}
public FilterInstantSpell(String name) {
super(name);
this.add(CardType.INSTANT.getPredicate());
}
public FilterInstantSpell(final FilterInstantSpell filter) {
super(filter);
}
@Override
public FilterInstantSpell copy() {
return new FilterInstantSpell(this);
}
}