From 64f9b95e10c8276ff90c21f9edeb5183155cf69e Mon Sep 17 00:00:00 2001 From: Thomas Winwood Date: Mon, 8 Apr 2019 05:09:42 +0100 Subject: [PATCH 1/6] Implement Infernal Spawn of Evil --- .../src/mage/cards/i/InfernalSpawnOfEvil.java | 104 ++++++++++++++++++ Mage.Sets/src/mage/sets/Unglued.java | 1 + 2 files changed, 105 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/InfernalSpawnOfEvil.java diff --git a/Mage.Sets/src/mage/cards/i/InfernalSpawnOfEvil.java b/Mage.Sets/src/mage/cards/i/InfernalSpawnOfEvil.java new file mode 100644 index 00000000000..c346be1d1a0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InfernalSpawnOfEvil.java @@ -0,0 +1,104 @@ + +package mage.cards.i; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; +import mage.abilities.condition.common.IsStepCondition; +import mage.abilities.costs.CompositeCost; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.abilities.costs.common.RevealSourceFromYourHandCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponentOrPlaneswalker; + +/** + * + * @author Ketsuban + */ +public final class InfernalSpawnOfEvil extends CardImpl { + + public InfernalSpawnOfEvil(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{6}{B}{B}{B}"); + this.subtype.add(SubType.BEAST); + + this.power = new MageInt(7); + this.toughness = new MageInt(7); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // 1B, Reveal Infernal Spawn of Evil from your hand, Say "It's coming!": + // Infernal Spawn of Evil deals 1 damage to target opponent or planeswalker. + // Activate this ability only during your upkeep and only once each turn. + Ability ability = new LimitedTimesPerTurnActivatedAbility(Zone.HAND, new DamageTargetEffect(1), + new CompositeCost( + new ManaCostsImpl("{1}{B}"), + new CompositeCost( + new RevealSourceFromYourHandCost(), + new SayCost("It's coming!"), + "Reveal {this} from your hand, Say \"It's coming!\""), + "{1}{B}, Reveal {this} from your hand, Say \"It's coming!\""), + 1, new IsStepCondition(PhaseStep.UPKEEP, true)); + ability.addTarget(new TargetOpponentOrPlaneswalker()); + this.addAbility(ability); + } + + public InfernalSpawnOfEvil(final InfernalSpawnOfEvil card) { + super(card); + } + + @Override + public InfernalSpawnOfEvil copy() { + return new InfernalSpawnOfEvil(this); + } +} + +class SayCost extends CostImpl { + + private String message; + + public SayCost(String message) { + this.message = message; + } + + public SayCost(SayCost cost) { + super(cost); + this.message = cost.message; + } + + @Override + public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { + return true; + } + + @Override + public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { + Player controller = game.getPlayer(controllerId); + if (controller == null) { + return false; + } + game.informPlayers(controller.getLogName() + ": " + message); + return true; + } + + @Override + public Cost copy() { + return new SayCost(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Unglued.java b/Mage.Sets/src/mage/sets/Unglued.java index b432e5833fc..10a415d0878 100644 --- a/Mage.Sets/src/mage/sets/Unglued.java +++ b/Mage.Sets/src/mage/sets/Unglued.java @@ -39,6 +39,7 @@ public final class Unglued extends ExpansionSet { cards.add(new SetCardInfo("Growth Spurt", 61, Rarity.COMMON, mage.cards.g.GrowthSpurt.class)); cards.add(new SetCardInfo("Hungry Hungry Heifer", 63, Rarity.UNCOMMON, mage.cards.h.HungryHungryHeifer.class)); cards.add(new SetCardInfo("Incoming!", 64, Rarity.RARE, mage.cards.i.Incoming.class)); + cards.add(new SetCardInfo("Infernal Spawn of Evil", 33, Rarity.RARE, mage.cards.i.InfernalSpawnOfEvil.class)); cards.add(new SetCardInfo("Island", 85, Rarity.LAND, mage.cards.basiclands.Island.class, new CardGraphicInfo(FrameStyle.UGL_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Jack-in-the-Mox", 75, Rarity.RARE, mage.cards.j.JackInTheMox.class)); cards.add(new SetCardInfo("Jalum Grifter", 47, Rarity.RARE, mage.cards.j.JalumGrifter.class)); From 44214d65fc6c8e5bb67f31c7cd07d60abee34926 Mon Sep 17 00:00:00 2001 From: Thomas Winwood Date: Mon, 8 Apr 2019 05:10:32 +0100 Subject: [PATCH 2/6] Implement Miss Demeanour --- Mage.Sets/src/mage/cards/m/MissDemeanour.java | 83 +++++++++++++++++++ Mage.Sets/src/mage/sets/Unglued.java | 1 + .../BeginningOfUpkeepTriggeredAbility.java | 10 +++ .../src/main/java/mage/constants/SubType.java | 1 + 4 files changed, 95 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/m/MissDemeanour.java diff --git a/Mage.Sets/src/mage/cards/m/MissDemeanour.java b/Mage.Sets/src/mage/cards/m/MissDemeanour.java new file mode 100644 index 00000000000..87520d10958 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MissDemeanour.java @@ -0,0 +1,83 @@ + +package mage.cards.m; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +/** + * + * @author Ketsuban + */ +public final class MissDemeanour extends CardImpl { + + public MissDemeanour(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.LADYOFPROPERETIQUETTE); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // At the beginning of each other player's upkeep, you may compliment that player on their game play. If you don't, sacrifice Miss Demeanour. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new MissDemeanourEffect(), TargetController.NOT_YOU, false, true)); + } + + public MissDemeanour(final MissDemeanour card) { + super(card); + } + + @Override + public MissDemeanour copy() { + return new MissDemeanour(this); + } +} + +class MissDemeanourEffect extends OneShotEffect { + + public MissDemeanourEffect() { + super(Outcome.Sacrifice); + this.staticText = "you may compliment that player on their game play. If you don't, sacrifice {this}"; + } + + public MissDemeanourEffect(final MissDemeanourEffect effect) { + super(effect); + } + + @Override + public MissDemeanourEffect copy() { + return new MissDemeanourEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent sourceObject = (Permanent) source.getSourceObjectIfItStillExists(game); + if (sourceObject != null) { + if (!controller.chooseUse(outcome, "Compliment " + game.getPlayer(game.getActivePlayerId()).getName() + " on their game play?", source, game)) { + sourceObject.sacrifice(source.getSourceId(), game); + } + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/Unglued.java b/Mage.Sets/src/mage/sets/Unglued.java index 10a415d0878..9157469f183 100644 --- a/Mage.Sets/src/mage/sets/Unglued.java +++ b/Mage.Sets/src/mage/sets/Unglued.java @@ -46,6 +46,7 @@ public final class Unglued extends ExpansionSet { cards.add(new SetCardInfo("Jumbo Imp", 34, Rarity.UNCOMMON, mage.cards.j.JumboImp.class)); cards.add(new SetCardInfo("Krazy Kow", 48, Rarity.COMMON, mage.cards.k.KrazyKow.class)); cards.add(new SetCardInfo("Mine, Mine, Mine!", 65, Rarity.RARE, mage.cards.m.MineMineMine.class)); + cards.add(new SetCardInfo("Miss Demeanour", 10, Rarity.UNCOMMON, mage.cards.m.MissDemeanour.class)); cards.add(new SetCardInfo("Mountain", 87, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UGL_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Once More with Feeling", 11, Rarity.RARE, mage.cards.o.OnceMoreWithFeeling.class)); cards.add(new SetCardInfo("Paper Tiger", 78, Rarity.COMMON, mage.cards.p.PaperTiger.class)); diff --git a/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java index 49d7c0a70e3..87ed534a99f 100644 --- a/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java @@ -70,6 +70,16 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { } } return yours; + case NOT_YOU: + boolean notYours = !event.getPlayerId().equals(this.controllerId); + if (notYours && setTargetPointer) { + if (getTargets().isEmpty()) { + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getPlayerId())); + } + } + } + return notYours; case OPPONENT: if (game.getPlayer(this.controllerId).hasOpponent(event.getPlayerId(), game)) { if (setTargetPointer && getTargets().isEmpty()) { diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 7c29322629f..733c23fa967 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -201,6 +201,7 @@ public enum SubType { KOR("Kor", SubTypeSet.CreatureType), KRAKEN("Kraken", SubTypeSet.CreatureType), // L + LADYOFPROPERETIQUETTE("Lady of Proper Etiquette", SubTypeSet.CreatureType), // Unglued LAMIA("Lamia", SubTypeSet.CreatureType), LAMMASU("Lammasu", SubTypeSet.CreatureType), LEECH("Leech", SubTypeSet.CreatureType), From c7016c7e22357225d567a37334476c2e861df0d3 Mon Sep 17 00:00:00 2001 From: Thomas Winwood Date: Mon, 8 Apr 2019 18:54:08 +0100 Subject: [PATCH 3/6] Add compliment to Miss Demeanour's effect --- Mage.Sets/src/mage/cards/m/MissDemeanour.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/cards/m/MissDemeanour.java b/Mage.Sets/src/mage/cards/m/MissDemeanour.java index 87520d10958..dee0ece787e 100644 --- a/Mage.Sets/src/mage/cards/m/MissDemeanour.java +++ b/Mage.Sets/src/mage/cards/m/MissDemeanour.java @@ -72,8 +72,12 @@ class MissDemeanourEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent sourceObject = (Permanent) source.getSourceObjectIfItStillExists(game); + String activePlayerName = game.getPlayer(game.getActivePlayerId()).getName(); if (sourceObject != null) { - if (!controller.chooseUse(outcome, "Compliment " + game.getPlayer(game.getActivePlayerId()).getName() + " on their game play?", source, game)) { + if (controller.chooseUse(outcome, "Compliment " + activePlayerName + " on their game play?", source, game)) { + // TODO(Ketsuban): this could probably stand to be randomly chosen from a pool of compliments + game.informPlayers(controller.getLogName() + ": That's a well-built deck and you pilot it well, " + activePlayerName + "."); + } else { sourceObject.sacrifice(source.getSourceId(), game); } return true; From 26bb21918314e35385a3942c5ea99c8671a7f8df Mon Sep 17 00:00:00 2001 From: Thomas Winwood Date: Mon, 8 Apr 2019 19:04:45 +0100 Subject: [PATCH 4/6] Fix spelling of Miss Demeanor's name I could have sworn I checked the spelling on Scryfall before I started but here we are. I blame Noah Webster. --- .../{MissDemeanour.java => MissDemeanor.java} | 22 +++++++++---------- Mage.Sets/src/mage/sets/Unglued.java | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) rename Mage.Sets/src/mage/cards/m/{MissDemeanour.java => MissDemeanor.java} (80%) diff --git a/Mage.Sets/src/mage/cards/m/MissDemeanour.java b/Mage.Sets/src/mage/cards/m/MissDemeanor.java similarity index 80% rename from Mage.Sets/src/mage/cards/m/MissDemeanour.java rename to Mage.Sets/src/mage/cards/m/MissDemeanor.java index dee0ece787e..e262d64ff87 100644 --- a/Mage.Sets/src/mage/cards/m/MissDemeanour.java +++ b/Mage.Sets/src/mage/cards/m/MissDemeanor.java @@ -23,9 +23,9 @@ import mage.players.Player; * * @author Ketsuban */ -public final class MissDemeanour extends CardImpl { +public final class MissDemeanor extends CardImpl { - public MissDemeanour(UUID ownerId, CardSetInfo setInfo) { + public MissDemeanor(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.LADYOFPROPERETIQUETTE); @@ -39,33 +39,33 @@ public final class MissDemeanour extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // At the beginning of each other player's upkeep, you may compliment that player on their game play. If you don't, sacrifice Miss Demeanour. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new MissDemeanourEffect(), TargetController.NOT_YOU, false, true)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new MissDemeanorEffect(), TargetController.NOT_YOU, false, true)); } - public MissDemeanour(final MissDemeanour card) { + public MissDemeanor(final MissDemeanor card) { super(card); } @Override - public MissDemeanour copy() { - return new MissDemeanour(this); + public MissDemeanor copy() { + return new MissDemeanor(this); } } -class MissDemeanourEffect extends OneShotEffect { +class MissDemeanorEffect extends OneShotEffect { - public MissDemeanourEffect() { + public MissDemeanorEffect() { super(Outcome.Sacrifice); this.staticText = "you may compliment that player on their game play. If you don't, sacrifice {this}"; } - public MissDemeanourEffect(final MissDemeanourEffect effect) { + public MissDemeanorEffect(final MissDemeanorEffect effect) { super(effect); } @Override - public MissDemeanourEffect copy() { - return new MissDemeanourEffect(this); + public MissDemeanorEffect copy() { + return new MissDemeanorEffect(this); } @Override diff --git a/Mage.Sets/src/mage/sets/Unglued.java b/Mage.Sets/src/mage/sets/Unglued.java index 9157469f183..bfda5c412ed 100644 --- a/Mage.Sets/src/mage/sets/Unglued.java +++ b/Mage.Sets/src/mage/sets/Unglued.java @@ -46,7 +46,7 @@ public final class Unglued extends ExpansionSet { cards.add(new SetCardInfo("Jumbo Imp", 34, Rarity.UNCOMMON, mage.cards.j.JumboImp.class)); cards.add(new SetCardInfo("Krazy Kow", 48, Rarity.COMMON, mage.cards.k.KrazyKow.class)); cards.add(new SetCardInfo("Mine, Mine, Mine!", 65, Rarity.RARE, mage.cards.m.MineMineMine.class)); - cards.add(new SetCardInfo("Miss Demeanour", 10, Rarity.UNCOMMON, mage.cards.m.MissDemeanour.class)); + cards.add(new SetCardInfo("Miss Demeanor", 10, Rarity.UNCOMMON, mage.cards.m.MissDemeanor.class)); cards.add(new SetCardInfo("Mountain", 87, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UGL_FULL_ART_BASIC, false))); cards.add(new SetCardInfo("Once More with Feeling", 11, Rarity.RARE, mage.cards.o.OnceMoreWithFeeling.class)); cards.add(new SetCardInfo("Paper Tiger", 78, Rarity.COMMON, mage.cards.p.PaperTiger.class)); From 4881b8b08b1d80a7e6f9e291605896a917d58206 Mon Sep 17 00:00:00 2001 From: Thomas Winwood Date: Mon, 8 Apr 2019 19:24:42 +0100 Subject: [PATCH 5/6] Set customSet=true for Lady of Proper Etiquette type --- Mage/src/main/java/mage/constants/SubType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 733c23fa967..778cc48ea49 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -201,7 +201,7 @@ public enum SubType { KOR("Kor", SubTypeSet.CreatureType), KRAKEN("Kraken", SubTypeSet.CreatureType), // L - LADYOFPROPERETIQUETTE("Lady of Proper Etiquette", SubTypeSet.CreatureType), // Unglued + LADYOFPROPERETIQUETTE("Lady of Proper Etiquette", SubTypeSet.CreatureType, true), // Unglued LAMIA("Lamia", SubTypeSet.CreatureType), LAMMASU("Lammasu", SubTypeSet.CreatureType), LEECH("Leech", SubTypeSet.CreatureType), From 5a83b2727173cfb208589cbc3dd380a93e8912cb Mon Sep 17 00:00:00 2001 From: Thomas Winwood Date: Tue, 9 Apr 2019 01:13:56 +0100 Subject: [PATCH 6/6] Fix verify bug --- Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index d2656c2a4d2..3e6594dbd17 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -87,6 +87,7 @@ public class VerifyCardDataTest { // subtype skipListCreate("SUBTYPE"); + skipListAddName("SUBTYPE", "UGL", "Miss Demeanor"); // number skipListCreate("NUMBER");