From 7fc465dd3f2426823e75800309897e941b93ac5e Mon Sep 17 00:00:00 2001 From: miesma Date: Fri, 3 Mar 2023 18:06:19 +0100 Subject: [PATCH 1/5] Implemented Kinzu of the Bleak Coven. --- .../mage/cards/k/KinzuOfTheBleakCoven.java | 156 ++++++++++++++++++ .../src/mage/sets/PhyrexiaAllWillBeOne.java | 2 + 2 files changed, 158 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java diff --git a/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java new file mode 100644 index 0000000000..25447667f7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java @@ -0,0 +1,156 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.keyword.ToxicAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.PermanentCard; +import mage.players.Player; +import mage.util.CardUtil; +import java.util.UUID; + +/** + * @author miesma + */ +public final class KinzuOfTheBleakCoven extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another nontoken creature you control"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(TokenPredicate.FALSE); + } + + public KinzuOfTheBleakCoven(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.PHYREXIAN); + this.subtype.add(SubType.VAMPIRE); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever another nontoken creature you control dies, you may pay 2 Life and exile it. If you do, create a token that's a copy of that card, except it’s 1/1 and has toxic 1. + Effect effect = new DoIfCostPaid(new mage.cards.k.KinzuOfTheBleakCovenEffect(), new KinzuOfTheBleakCovenCost(2), "Pay 2 Life and exile it?"); + this.addAbility(new DiesCreatureTriggeredAbility(effect,false, filter, true)); + + } + + private KinzuOfTheBleakCoven(final mage.cards.k.KinzuOfTheBleakCoven card) { + super(card); + } + + @Override + public mage.cards.k.KinzuOfTheBleakCoven copy() { + return new mage.cards.k.KinzuOfTheBleakCoven(this); + } +} + +class KinzuOfTheBleakCovenEffect extends OneShotEffect { + + KinzuOfTheBleakCovenEffect() { + super(Outcome.Benefit); + staticText = "create a token that’s a copy of that creature" + + ", except it’s 1/1 and has toxic 1."; + } + + private KinzuOfTheBleakCovenEffect(final mage.cards.k.KinzuOfTheBleakCovenEffect effect) { + super(effect); + } + + @Override + public mage.cards.k.KinzuOfTheBleakCovenEffect copy() { + return new mage.cards.k.KinzuOfTheBleakCovenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (player == null || card == null) { + return false; + } + player.moveCards(card, Zone.EXILED, source, game); + return new CreateTokenCopyTargetEffect().setSavedPermanent( + new PermanentCard(card, source.getControllerId(), game) + ).setPermanentModifier((token, g) -> { + token.setPower(1); // 1/1 + token.setToughness(1); + token.addAbility(new ToxicAbility(1)); // Add Toxic (is additive) + }).apply(game, source); + } +} + + +class KinzuOfTheBleakCovenCost extends CostImpl { + + //New Cost as exiling the Card is part of the Cost. + + private DynamicValue amount; + + public KinzuOfTheBleakCovenCost(int amount) { + super(); + this.amount = StaticValue.get(amount).copy(); + this.text = "2 Life and exile it"; + } + + @Override + public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + int lifeToPayAmount = amount.calculate(game, ability, null); + // Paying 0 life is not considered paying any life. + if (lifeToPayAmount > 0 && !game.getPlayer(controllerId).canPayLifeCost(ability)) { + return false; + } + Card card = game.getCard((ability.getAllEffects().get(0).getTargetPointer().getFirst(game, source))); + if (card == null) { + //Need to be able to Exile the Card as it is part of the cost. Can't pay if the target is not legal. + return false; + } + System.out.println(text); + return game.getPlayer(controllerId).getLife() >= lifeToPayAmount || lifeToPayAmount == 0; + } + + @Override + public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { + Player controller = game.getPlayer(controllerId); + if (controller == null) { + return false; + } + int lifeToPayAmount = amount.calculate(game, ability, null); + this.paid = CardUtil.tryPayLife(lifeToPayAmount, controller, source, game); + return this.paid; + } + + @Override + public KinzuOfTheBleakCovenCost copy() { + return new KinzuOfTheBleakCovenCost(2); + } + + @Override + public void clearPaid() { + super.clearPaid(); + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOne.java b/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOne.java index 9d21273a66..ad880c4ee6 100644 --- a/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOne.java +++ b/Mage.Sets/src/mage/sets/PhyrexiaAllWillBeOne.java @@ -129,6 +129,8 @@ public final class PhyrexiaAllWillBeOne extends ExpansionSet { cards.add(new SetCardInfo("Kemba, Kha Enduring", 19, Rarity.RARE, mage.cards.k.KembaKhaEnduring.class)); cards.add(new SetCardInfo("Koth, Fire of Resistance", 138, Rarity.RARE, mage.cards.k.KothFireOfResistance.class)); cards.add(new SetCardInfo("Kuldotha Cackler", 139, Rarity.COMMON, mage.cards.k.KuldothaCackler.class)); + cards.add(new SetCardInfo("Kinzu of the Bleak Coven", 406, Rarity.RARE, mage.cards.k.KinzuOfTheBleakCoven.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kinzu of the Bleak Coven", 411, Rarity.RARE, mage.cards.k.KinzuOfTheBleakCoven.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lattice-Blade Mantis", 173, Rarity.COMMON, mage.cards.l.LatticeBladeMantis.class)); cards.add(new SetCardInfo("Leonin Lightbringer", 20, Rarity.COMMON, mage.cards.l.LeoninLightbringer.class)); cards.add(new SetCardInfo("Magmatic Sprinter", 140, Rarity.UNCOMMON, mage.cards.m.MagmaticSprinter.class)); From 52c23bfa847efd99e850c36550925f5407fb79d4 Mon Sep 17 00:00:00 2001 From: miesma Date: Fri, 3 Mar 2023 18:37:45 +0100 Subject: [PATCH 2/5] Added Newline --- Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java index 25447667f7..fc74e2a382 100644 --- a/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java +++ b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java @@ -153,4 +153,4 @@ class KinzuOfTheBleakCovenCost extends CostImpl { super.clearPaid(); } -} \ No newline at end of file +} From 4fe3f5480c34222b76f21d632cf67850501d302b Mon Sep 17 00:00:00 2001 From: miesma Date: Fri, 3 Mar 2023 19:04:06 +0100 Subject: [PATCH 3/5] Fixed Character --- Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java index fc74e2a382..bed4643872 100644 --- a/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java +++ b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java @@ -72,8 +72,8 @@ class KinzuOfTheBleakCovenEffect extends OneShotEffect { KinzuOfTheBleakCovenEffect() { super(Outcome.Benefit); - staticText = "create a token that’s a copy of that creature" + - ", except it’s 1/1 and has toxic 1."; + staticText = "create a token that's a copy of that creature" + + ", except it's 1/1 and has toxic 1."; } private KinzuOfTheBleakCovenEffect(final mage.cards.k.KinzuOfTheBleakCovenEffect effect) { From 846a00b472f219d193d078e947ed43c739b14d15 Mon Sep 17 00:00:00 2001 From: miesma Date: Sat, 4 Mar 2023 18:44:58 +0100 Subject: [PATCH 4/5] Removed Debug --- Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java index bed4643872..67c5ce965c 100644 --- a/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java +++ b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java @@ -92,6 +92,7 @@ class KinzuOfTheBleakCovenEffect extends OneShotEffect { if (player == null || card == null) { return false; } + //Can move to exile here - checked earlier if target is still legal player.moveCards(card, Zone.EXILED, source, game); return new CreateTokenCopyTargetEffect().setSavedPermanent( new PermanentCard(card, source.getControllerId(), game) @@ -128,7 +129,6 @@ class KinzuOfTheBleakCovenCost extends CostImpl { //Need to be able to Exile the Card as it is part of the cost. Can't pay if the target is not legal. return false; } - System.out.println(text); return game.getPlayer(controllerId).getLife() >= lifeToPayAmount || lifeToPayAmount == 0; } From 810706cad5d59d7210993a179d9fc4ee2ab964d7 Mon Sep 17 00:00:00 2001 From: miesma Date: Fri, 28 Apr 2023 12:32:33 +0200 Subject: [PATCH 5/5] Implement KinzuOfTheBleakCovenEffect handling the Cost --- .../mage/cards/k/KinzuOfTheBleakCoven.java | 84 ++++--------------- 1 file changed, 18 insertions(+), 66 deletions(-) diff --git a/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java index 67c5ce965c..00fc8934aa 100644 --- a/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java +++ b/Mage.Sets/src/mage/cards/k/KinzuOfTheBleakCoven.java @@ -4,14 +4,11 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; -import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.keyword.FlyingAbility; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.keyword.ToxicAbility; import mage.cards.Card; import mage.cards.CardImpl; @@ -24,7 +21,6 @@ import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.PermanentCard; import mage.players.Player; -import mage.util.CardUtil; import java.util.UUID; /** @@ -52,19 +48,20 @@ public final class KinzuOfTheBleakCoven extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // Whenever another nontoken creature you control dies, you may pay 2 Life and exile it. If you do, create a token that's a copy of that card, except it’s 1/1 and has toxic 1. - Effect effect = new DoIfCostPaid(new mage.cards.k.KinzuOfTheBleakCovenEffect(), new KinzuOfTheBleakCovenCost(2), "Pay 2 Life and exile it?"); + // Whenever another nontoken creature you control dies, you may pay 2 Life and exile it. + // If you do, create a token that's a copy of that card, except it’s 1/1 and has toxic 1. + Effect effect = new KinzuOfTheBleakCovenEffect(); this.addAbility(new DiesCreatureTriggeredAbility(effect,false, filter, true)); } - private KinzuOfTheBleakCoven(final mage.cards.k.KinzuOfTheBleakCoven card) { + private KinzuOfTheBleakCoven(final KinzuOfTheBleakCoven card) { super(card); } @Override - public mage.cards.k.KinzuOfTheBleakCoven copy() { - return new mage.cards.k.KinzuOfTheBleakCoven(this); + public KinzuOfTheBleakCoven copy() { + return new KinzuOfTheBleakCoven(this); } } @@ -72,17 +69,18 @@ class KinzuOfTheBleakCovenEffect extends OneShotEffect { KinzuOfTheBleakCovenEffect() { super(Outcome.Benefit); - staticText = "create a token that's a copy of that creature" + + staticText = "you may pay 2 Life and exile it. If you do, " + + "create a token that's a copy of that creature" + ", except it's 1/1 and has toxic 1."; } - private KinzuOfTheBleakCovenEffect(final mage.cards.k.KinzuOfTheBleakCovenEffect effect) { + private KinzuOfTheBleakCovenEffect(final KinzuOfTheBleakCovenEffect effect) { super(effect); } @Override - public mage.cards.k.KinzuOfTheBleakCovenEffect copy() { - return new mage.cards.k.KinzuOfTheBleakCovenEffect(this); + public KinzuOfTheBleakCovenEffect copy() { + return new KinzuOfTheBleakCovenEffect(this); } @Override @@ -92,7 +90,12 @@ class KinzuOfTheBleakCovenEffect extends OneShotEffect { if (player == null || card == null) { return false; } - //Can move to exile here - checked earlier if target is still legal + Cost cost = new PayLifeCost(2); + if (!cost.canPay(source, source, source.getControllerId(), game) + || !player.chooseUse(Outcome.Benefit,"Pay 2 Life and Exile " + card.getName() + "?",source,game) + || !cost.pay(source, game, source, source.getControllerId(), true)) { + return false; + } player.moveCards(card, Zone.EXILED, source, game); return new CreateTokenCopyTargetEffect().setSavedPermanent( new PermanentCard(card, source.getControllerId(), game) @@ -103,54 +106,3 @@ class KinzuOfTheBleakCovenEffect extends OneShotEffect { }).apply(game, source); } } - - -class KinzuOfTheBleakCovenCost extends CostImpl { - - //New Cost as exiling the Card is part of the Cost. - - private DynamicValue amount; - - public KinzuOfTheBleakCovenCost(int amount) { - super(); - this.amount = StaticValue.get(amount).copy(); - this.text = "2 Life and exile it"; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - int lifeToPayAmount = amount.calculate(game, ability, null); - // Paying 0 life is not considered paying any life. - if (lifeToPayAmount > 0 && !game.getPlayer(controllerId).canPayLifeCost(ability)) { - return false; - } - Card card = game.getCard((ability.getAllEffects().get(0).getTargetPointer().getFirst(game, source))); - if (card == null) { - //Need to be able to Exile the Card as it is part of the cost. Can't pay if the target is not legal. - return false; - } - return game.getPlayer(controllerId).getLife() >= lifeToPayAmount || lifeToPayAmount == 0; - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - Player controller = game.getPlayer(controllerId); - if (controller == null) { - return false; - } - int lifeToPayAmount = amount.calculate(game, ability, null); - this.paid = CardUtil.tryPayLife(lifeToPayAmount, controller, source, game); - return this.paid; - } - - @Override - public KinzuOfTheBleakCovenCost copy() { - return new KinzuOfTheBleakCovenCost(2); - } - - @Override - public void clearPaid() { - super.clearPaid(); - } - -}