Implemented Kathril, Aspect Warper

This commit is contained in:
Evan Kranzler 2020-04-04 21:25:06 -04:00
parent 3632eba3e8
commit 381de68563
14 changed files with 229 additions and 38 deletions

View file

@ -0,0 +1,163 @@
package mage.cards.k;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.*;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
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.Collection;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author TheElk801
*/
public final class KathrilAspectWarper extends CardImpl {
public KathrilAspectWarper(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}{G}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.NIGHTMARE);
this.subtype.add(SubType.INSECT);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// When Kathril, Aspect Warper enters the battlefield, put a flying counter on any creature you control if a creature card in your graveyard has flying. Repeat this process for first strike, double strike, deathtouch, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance. Then put a +1/+1 counter on Kathril for each counter put on a creature this way.
this.addAbility(new EntersBattlefieldTriggeredAbility(new KathrilAspectWarperEffect()));
}
private KathrilAspectWarper(final KathrilAspectWarper card) {
super(card);
}
@Override
public KathrilAspectWarper copy() {
return new KathrilAspectWarper(this);
}
}
class KathrilAspectWarperEffect extends OneShotEffect {
private static class AbilityTracker {
}
KathrilAspectWarperEffect() {
super(Outcome.Benefit);
staticText = "put a flying counter on any creature you control if a creature card in your graveyard has flying. " +
"Repeat this process for first strike, double strike, deathtouch, hexproof, " +
"indestructible, lifelink, menace, reach, trample, and vigilance. " +
"Then put a +1/+1 counter on {this} for each counter put on a creature this way.";
}
private KathrilAspectWarperEffect(final KathrilAspectWarperEffect effect) {
super(effect);
}
@Override
public KathrilAspectWarperEffect copy() {
return new KathrilAspectWarperEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null || game.getBattlefield().countAll(
StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game
) == 0) {
return false;
}
Set<CounterType> counterSet = player
.getGraveyard()
.getCards(StaticFilters.FILTER_CARD_CREATURE, game)
.stream()
.map(Card::getAbilities)
.flatMap(Collection::stream)
.map(this::checkAbility)
.collect(Collectors.toSet());
if (counterSet == null || counterSet.size() == 0) {
return false;
}
int countersAdded = 0;
for (CounterType counterType : counterSet) {
if (counterType == null) {
continue;
}
Target target = new TargetControlledCreaturePermanent();
target.setNotTarget(true);
if (!player.choose(outcome, target, source.getSourceId(), game)) {
continue;
}
Permanent permanent = game.getPermanent(target.getFirstTarget());
if (permanent == null) {
continue;
}
if (permanent.addCounters(counterType.createInstance(), source, game)) {
countersAdded++;
}
}
if (countersAdded == 0) {
return false;
}
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
return true;
}
permanent.addCounters(CounterType.P1P1.createInstance(countersAdded), source, game);
return true;
}
private CounterType checkAbility(Ability ability) {
if (ability instanceof FlyingAbility) {
return CounterType.FLYING;
}
if (ability instanceof FirstStrikeAbility) {
return CounterType.FIRST_STRIKE;
}
if (ability instanceof DoubleStrikeAbility) {
return CounterType.DOUBLE_STRIKE;
}
if (ability instanceof DeathtouchAbility) {
return CounterType.DEATHTOUCH;
}
if (ability instanceof HexproofInterface) {
return CounterType.HEXPROOF;
}
if (ability instanceof IndestructibleAbility) {
return CounterType.INDESTRUCTIBLE;
}
if (ability instanceof LifelinkAbility) {
return CounterType.LIFELINK;
}
if (ability instanceof MenaceAbility) {
return CounterType.MENACE;
}
if (ability instanceof ReachAbility) {
return CounterType.REACH;
}
if (ability instanceof TrampleAbility) {
return CounterType.TRAMPLE;
}
if (ability instanceof VigilanceAbility) {
return CounterType.VIGILANCE;
}
return null;
}
}

View file

@ -75,7 +75,7 @@ class MajesticMyriarchEffect extends OneShotEffect {
filterDeathtouch.add(new AbilityPredicate(DeathtouchAbility.class));
filterDoubleStrike.add(new AbilityPredicate(DoubleStrikeAbility.class));
filterHaste.add(new AbilityPredicate(HasteAbility.class));
filterHexproof.add(new AbilityPredicate(HexproofAbility.class));
filterHexproof.add(new AbilityPredicate(HexproofInterface.class));
filterIndestructible.add(new AbilityPredicate(IndestructibleAbility.class));
filterLifelink.add(new AbilityPredicate(LifelinkAbility.class));
filterMenace.add(new AbilityPredicate(MenaceAbility.class));

View file

@ -1,7 +1,6 @@
package mage.cards.o;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfCombatTriggeredAbility;
@ -15,14 +14,15 @@ import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.game.Game;
import java.util.UUID;
/**
*
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public final class OdricLunarchMarshal extends CardImpl {
public OdricLunarchMarshal(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.SOLDIER);
@ -44,34 +44,34 @@ public final class OdricLunarchMarshal extends CardImpl {
}
class OdricLunarchMarshalEffect extends OneShotEffect {
private static final FilterControlledCreaturePermanent filterFirstStrike = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterFlying = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterFlying = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterDeathtouch = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterDoubleStrike = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterHaste = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterHexproof = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterHexproof = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterIndestructible = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterLifelink = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterMenace = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterReach = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterSkulk = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterSkulk = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterTrample = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterVigilance = new FilterControlledCreaturePermanent();
private static final FilterControlledCreaturePermanent filterCreatures = new FilterControlledCreaturePermanent();
static {
static {
filterFirstStrike.add(new AbilityPredicate(FirstStrikeAbility.class));
filterFlying.add(new AbilityPredicate(FlyingAbility.class));
filterDeathtouch.add(new AbilityPredicate(DeathtouchAbility.class));
filterDoubleStrike.add(new AbilityPredicate(DoubleStrikeAbility.class));
filterHaste.add(new AbilityPredicate(HasteAbility.class));
filterHexproof.add(new AbilityPredicate(HexproofAbility.class));
filterHexproof.add(new AbilityPredicate(HexproofInterface.class));
filterIndestructible.add(new AbilityPredicate(IndestructibleAbility.class));
filterLifelink.add(new AbilityPredicate(LifelinkAbility.class));
filterMenace.add(new AbilityPredicate(MenaceAbility.class));
filterReach.add(new AbilityPredicate(ReachAbility.class));
filterSkulk.add(new AbilityPredicate(SkulkAbility.class));
filterSkulk.add(new AbilityPredicate(SkulkAbility.class));
filterTrample.add(new AbilityPredicate(TrampleAbility.class));
filterVigilance.add(new AbilityPredicate(VigilanceAbility.class));
}
@ -92,17 +92,17 @@ class OdricLunarchMarshalEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
// First strike
if (game.getBattlefield().contains(filterFirstStrike, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Flying
if (game.getBattlefield().contains(filterFlying, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Deathtouch
if (game.getBattlefield().contains(filterDeathtouch, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
@ -112,37 +112,37 @@ class OdricLunarchMarshalEffect extends OneShotEffect {
if (game.getBattlefield().contains(filterDoubleStrike, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Haste
if (game.getBattlefield().contains(filterHaste, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Hexproof
if (game.getBattlefield().contains(filterHexproof, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(HexproofAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Indestructible
if (game.getBattlefield().contains(filterIndestructible, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Lifelink
if (game.getBattlefield().contains(filterLifelink, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Menace
if (game.getBattlefield().contains(filterMenace, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(new MenaceAbility(), Duration.EndOfTurn, filterCreatures), source);
}
// Reach
if (game.getBattlefield().contains(filterReach, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(ReachAbility.getInstance(), Duration.EndOfTurn, filterCreatures), source);
}
// Skulk
if (game.getBattlefield().contains(filterSkulk, source.getControllerId(), 1, game)) {
game.addEffect(new GainAbilityControlledEffect(new SkulkAbility(), Duration.EndOfTurn, filterCreatures), source);

View file

@ -85,7 +85,7 @@ class RayamiFirstOfTheFallenEffect extends ContinuousEffectImpl {
|| ability instanceof DoubleStrikeAbility
|| ability instanceof DeathtouchAbility
|| ability instanceof HasteAbility
|| ability instanceof HexproofAbility
|| ability instanceof HexproofInterface
|| ability instanceof IndestructibleAbility
|| ability instanceof LifelinkAbility
|| ability instanceof MenaceAbility

View file

@ -1,8 +1,5 @@
package mage.cards.s;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageInt;
import mage.MageObjectReference;
import mage.abilities.Ability;
@ -19,6 +16,10 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* @author LevelX2
*/
@ -106,7 +107,7 @@ class SoulflayerEffect extends ContinuousEffectImpl implements SourceEffect {
if (cardAbility instanceof HasteAbility) {
abilitiesToAdd.add(HasteAbility.getInstance());
}
if (cardAbility instanceof HexproofAbility) {
if (cardAbility instanceof HexproofInterface) {
abilitiesToAdd.add(HexproofAbility.getInstance());
}
if (cardAbility instanceof IndestructibleAbility) {

View file

@ -29,6 +29,7 @@ public final class Commander2020Edition extends ExpansionSet {
cards.add(new SetCardInfo("Deadly Tempest", 131, Rarity.RARE, mage.cards.d.DeadlyTempest.class));
cards.add(new SetCardInfo("Flawless Maneuver", 26, Rarity.RARE, mage.cards.f.FlawlessManeuver.class));
cards.add(new SetCardInfo("Jace, Architect of Thought", 114, Rarity.MYTHIC, mage.cards.j.JaceArchitectOfThought.class));
cards.add(new SetCardInfo("Kathril, Aspect Warper", 10, Rarity.MYTHIC, mage.cards.k.KathrilAspectWarper.class));
cards.add(new SetCardInfo("Lifecrafter's Bestiary", 244, Rarity.RARE, mage.cards.l.LifecraftersBestiary.class));
cards.add(new SetCardInfo("Masked Admirers", 163, Rarity.RARE, mage.cards.m.MaskedAdmirers.class));
cards.add(new SetCardInfo("Netherborn Altar", 45, Rarity.RARE, mage.cards.n.NetherbornAltar.class));

View file

@ -1,17 +1,18 @@
package mage.abilities.keyword;
import java.io.ObjectStreamException;
import mage.abilities.MageSingleton;
import mage.abilities.common.SimpleStaticAbility;
import mage.constants.Zone;
import java.io.ObjectStreamException;
/**
* Hexproof (This creature or player can't be the target of spells or abilities
* your opponents control.)
*
* @author loki
*/
public class HexproofAbility extends SimpleStaticAbility implements MageSingleton {
public class HexproofAbility extends SimpleStaticAbility implements MageSingleton, HexproofInterface {
private static final HexproofAbility instance;

View file

@ -1,17 +1,18 @@
package mage.abilities.keyword;
import java.io.ObjectStreamException;
import mage.abilities.MageSingleton;
import mage.abilities.common.SimpleStaticAbility;
import mage.constants.Zone;
import java.io.ObjectStreamException;
/**
* Hexproof from black (This creature or player can't be the target of black
* spells or abilities your opponents control.)
*
* @author igoudt
*/
public class HexproofFromBlackAbility extends SimpleStaticAbility implements MageSingleton {
public class HexproofFromBlackAbility extends SimpleStaticAbility implements MageSingleton, HexproofInterface {
private static final HexproofFromBlackAbility instance;

View file

@ -12,7 +12,7 @@ import java.io.ObjectStreamException;
*
* @author igoudt
*/
public class HexproofFromBlueAbility extends SimpleStaticAbility implements MageSingleton {
public class HexproofFromBlueAbility extends SimpleStaticAbility implements MageSingleton, HexproofInterface {
private static final HexproofFromBlueAbility instance;

View file

@ -12,7 +12,7 @@ import java.io.ObjectStreamException;
*
* @author TheElk801
*/
public class HexproofFromMonocoloredAbility extends SimpleStaticAbility implements MageSingleton {
public class HexproofFromMonocoloredAbility extends SimpleStaticAbility implements MageSingleton, HexproofInterface {
private static final HexproofFromMonocoloredAbility instance;

View file

@ -1,17 +1,18 @@
package mage.abilities.keyword;
import java.io.ObjectStreamException;
import mage.abilities.MageSingleton;
import mage.abilities.common.SimpleStaticAbility;
import mage.constants.Zone;
import java.io.ObjectStreamException;
/**
* Hexproof from white (This creature or player can't be the target of white
* spells or abilities your opponents control.)
*
* @author igoudt
*/
public class HexproofFromWhiteAbility extends SimpleStaticAbility implements MageSingleton {
public class HexproofFromWhiteAbility extends SimpleStaticAbility implements MageSingleton, HexproofInterface {
private static final HexproofFromWhiteAbility instance;

View file

@ -0,0 +1,9 @@
package mage.abilities.keyword;
/**
* an interface for hexproof abilities
*
* @author TheElk801
*/
public interface HexproofInterface {
}

View file

@ -37,6 +37,7 @@ public enum CounterType {
DEVOTION("devotion"),
DIVINITY("divinity"),
DOOM("doom"),
DOUBLE_STRIKE("double strike"),
DREAM("dream"),
ECHO("echo"),
EGG("egg"),
@ -49,6 +50,7 @@ public enum CounterType {
FATE("fate"),
FEATHER("feather"),
FILIBUSTER("filibuster"),
FIRST_STRIKE("first strike"),
FLOOD("flood"),
FLYING("flying"),
FUNK("funk"),
@ -69,6 +71,7 @@ public enum CounterType {
HOURGLASS("hourglass"),
HUNGER("hunger"),
ICE("ice"),
INDESTRUCTIBLE("indestructible"),
INFECTION("infection"),
INTERVENTION("intervention"),
ISOLATION("isolation"),
@ -115,6 +118,7 @@ public enum CounterType {
PRESSURE("pressure"),
PREY("prey"),
PUPA("pupa"),
REACH("reach"),
REPAIR("repair"),
RUST("rust"),
QUEST("quest"),
@ -145,6 +149,7 @@ public enum CounterType {
UNITY("unity"),
VELOCITY("velocity"),
VERSE("verse"),
VIGILANCE("vigilance"),
VITALITY("vitality"),
VORTEX("vortex"),
WAGE("wage"),
@ -203,16 +208,26 @@ public enum CounterType {
return new BoostCounter(-2, -2, amount);
case DEATHTOUCH:
return new AbilityCounter(DeathtouchAbility.getInstance());
case DOUBLE_STRIKE:
return new AbilityCounter(DoubleStrikeAbility.getInstance());
case FIRST_STRIKE:
return new AbilityCounter(FirstStrikeAbility.getInstance());
case FLYING:
return new AbilityCounter(FlyingAbility.getInstance());
case HEXPROOF:
return new AbilityCounter(HexproofAbility.getInstance());
case INDESTRUCTIBLE:
return new AbilityCounter(IndestructibleAbility.getInstance());
case LIFELINK:
return new AbilityCounter(LifelinkAbility.getInstance());
case MENACE:
return new AbilityCounter(new MenaceAbility());
case REACH:
return new AbilityCounter(ReachAbility.getInstance());
case TRAMPLE:
return new AbilityCounter(TrampleAbility.getInstance());
case VIGILANCE:
return new AbilityCounter(VigilanceAbility.getInstance());
default:
return new Counter(name, amount);
}

View file

@ -9,7 +9,6 @@ import mage.filter.predicate.Predicate;
import mage.game.Game;
/**
*
* @author North
*/
public class AbilityPredicate implements Predicate<MageObject> {
@ -23,12 +22,12 @@ public class AbilityPredicate implements Predicate<MageObject> {
@Override
public boolean apply(MageObject input, Game game) {
Abilities<Ability> abilities;
if (input instanceof Card){
abilities = ((Card)input).getAbilities(game);
if (input instanceof Card) {
abilities = ((Card) input).getAbilities(game);
} else {
abilities = input.getAbilities();
}
return abilities.stream().anyMatch(ability -> ability.getClass().equals(abilityClass));
return abilities.stream().anyMatch(abilityClass::isInstance);
}