From 7cc6de86addaa8ecfba9851c99eec60fe55f1064 Mon Sep 17 00:00:00 2001 From: drmDev Date: Sun, 6 Mar 2016 08:40:32 -0500 Subject: [PATCH 1/2] Card implementation and tests for CircleOfAffliction --- .../src/mage/sets/odyssey/NantukoMentor.java | 45 +++--- .../sets/planarchaos/CircleOfAffliction.java | 122 +++++++++++++++ .../enchantments/CircleOfAfflictionTest.java | 143 ++++++++++++++++++ 3 files changed, 287 insertions(+), 23 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/planarchaos/CircleOfAffliction.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/enchantments/CircleOfAfflictionTest.java diff --git a/Mage.Sets/src/mage/sets/odyssey/NantukoMentor.java b/Mage.Sets/src/mage/sets/odyssey/NantukoMentor.java index eb8438997f..5a2d44a114 100644 --- a/Mage.Sets/src/mage/sets/odyssey/NantukoMentor.java +++ b/Mage.Sets/src/mage/sets/odyssey/NantukoMentor.java @@ -78,29 +78,28 @@ public class NantukoMentor extends CardImpl { class NantukoMentorBoostTargetEffect extends ContinuousEffectImpl { -public NantukoMentorBoostTargetEffect() { - super(Duration.EndOfTurn, Outcome.BoostCreature); - staticText = "Target creature gets +X/+X until end of turn, where X is that creature's power."; -} - -public NantukoMentorBoostTargetEffect(final NantukoMentorBoostTargetEffect effect) { - super(effect); -} - -@Override -public NantukoMentorBoostTargetEffect copy() { - return new NantukoMentorBoostTargetEffect(this); -} - -@Override -public boolean apply(Game game, Ability source){ - Permanent target = game.getPermanent(source.getFirstTarget()); - if (target != null) { - MageInt power = target.getPower(); - target.addPower(power.getValue()); - target.addToughness(power.getValue()); + public NantukoMentorBoostTargetEffect() { + super(Duration.EndOfTurn, Outcome.BoostCreature); + staticText = "Target creature gets +X/+X until end of turn, where X is that creature's power."; } - return true; -} + public NantukoMentorBoostTargetEffect(final NantukoMentorBoostTargetEffect effect) { + super(effect); + } + + @Override + public NantukoMentorBoostTargetEffect copy() { + return new NantukoMentorBoostTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source){ + Permanent target = game.getPermanent(source.getFirstTarget()); + if (target != null) { + MageInt power = target.getPower(); + target.addPower(power.getValue()); + target.addToughness(power.getValue()); + } + return true; + } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/planarchaos/CircleOfAffliction.java b/Mage.Sets/src/mage/sets/planarchaos/CircleOfAffliction.java new file mode 100644 index 0000000000..0305173fe9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/planarchaos/CircleOfAffliction.java @@ -0,0 +1,122 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.planarchaos; + +import java.util.UUID; +import mage.MageObject; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.ChooseColorEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPlayer; + +/** + * + * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) + */ +public class CircleOfAffliction extends CardImpl { + + public CircleOfAffliction(UUID ownerId) { + super(ownerId, 66, "Circle of Affliction", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + this.expansionSetCode = "PLC"; + + // As Circle of Affliction enters the battlefield, choose a color. + this.addAbility(new EntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral))); + + // Whenever a source of the chosen color deals damage to you, you may pay {1}. If you do, target player loses 1 life and you gain 1 life. + Ability ability = new CircleOfAfflictionTriggeredAbility(); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + } + + public CircleOfAffliction(final CircleOfAffliction card) { + super(card); + } + + @Override + public CircleOfAffliction copy() { + return new CircleOfAffliction(this); + } +} + +class CircleOfAfflictionTriggeredAbility extends TriggeredAbilityImpl { + + public CircleOfAfflictionTriggeredAbility() { + super(Zone.BATTLEFIELD, new DoIfCostPaid(new LoseLifeTargetEffect(1), new GenericManaCost(1)), false); + ((DoIfCostPaid) getEffects().get(0)).addEffect(new GainLifeEffect(1)); + } + + public CircleOfAfflictionTriggeredAbility(final CircleOfAfflictionTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent circleOfAffliction = game.getPermanentOrLKIBattlefield(getSourceId()); + if (circleOfAffliction != null) { + ObjectColor chosenColor = (ObjectColor) game.getState().getValue(circleOfAffliction.getId() + "_color"); + if (chosenColor != null) { + MageObject damageSource = game.getObject(event.getSourceId()); + if (damageSource != null) { + if ( damageSource.getColor(game).shares(chosenColor) ) { + return true; + } + } + } + } + return false; + } + + @Override + public CircleOfAfflictionTriggeredAbility copy() { + return new CircleOfAfflictionTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever a source of the chosen color deals damage to you, you may pay {1}. If you do, target player loses 1 life and you gain 1 life."; + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/CircleOfAfflictionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/CircleOfAfflictionTest.java new file mode 100644 index 0000000000..a23b7690da --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/CircleOfAfflictionTest.java @@ -0,0 +1,143 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.enchantments; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class CircleOfAfflictionTest extends CardTestPlayerBase { + + /** + * + */ + @Test + public void testOneAttackerDamage() { + + // Enchantment - {1}{B} + // As Circle of Affliction enters the battlefield, choose a color. + // Whenever a source of the chosen color deals damage to you, you may pay {1}. If you do, target player loses 1 life and you gain 1 life. + addCard(Zone.HAND, playerA, "Circle of Affliction", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + + addCard(Zone.BATTLEFIELD, playerB, "Hill Giant", 1); // 3/3 {3}{R} + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Circle of Affliction"); + setChoice(playerA, "Red"); + + attack(2, playerB, "Hill Giant"); + addTarget(playerA, playerB); // Circle of Affliction drain ability + setChoice(playerA, "Yes"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 18); + assertLife(playerB, 19); + } + + /** + * + */ + @Test + public void testTwoAttackersDamage() { + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 2); // {1}{W} 2/2 + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + // As Circle of Affliction enters the battlefield, choose a color. + // Whenever a source of the chosen color deals damage to you, you may pay {1}. If you do, target player loses 1 life and you gain 1 life. + addCard(Zone.HAND, playerA, "Circle of Affliction", 1);// {1}{B} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Circle of Affliction"); + setChoice(playerA, "White"); + + attack(2, playerB, "Silvercoat Lion"); + attack(2, playerB, "Silvercoat Lion"); + addTarget(playerA, playerB); + setChoice(playerA, "Yes"); + addTarget(playerA, playerB); + setChoice(playerA, "Yes"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Circle of Affliction", 1); + + assertLife(playerA, 18); + assertLife(playerB, 18); + } + + /** + * + */ + @Test + public void testMixOfSpellsAndCombatDamage() { + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + // As Circle of Affliction enters the battlefield, choose a color. + // Whenever a source of the chosen color deals damage to you, you may pay {1}. If you do, target player loses 1 life and you gain 1 life. + addCard(Zone.HAND, playerA, "Circle of Affliction", 1);// {1}{B} + + addCard(Zone.HAND, playerB, "Lava Spike", 2); // {R} deals 3 damage to target player + addCard(Zone.BATTLEFIELD, playerB, "Hill Giant", 2); // {3}{R} 3/3 + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Circle of Affliction"); + setChoice(playerA, "Red"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Lava Spike", playerA); + addTarget(playerA, playerB); + setChoice(playerA, "Yes"); + + attack(2, playerB, "Hill Giant"); + attack(2, playerB, "Hill Giant"); + addTarget(playerA, playerB); + setChoice(playerA, "Yes"); + addTarget(playerA, playerB); + setChoice(playerA, "Yes"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lava Spike", playerA); + addTarget(playerA, playerB); + setChoice(playerA, "Yes"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Circle of Affliction", 1); + + assertLife(playerA, 12); // 12 damage - 4 drains = 8 net life total loss + assertLife(playerB, 16); // 4 drains + } +} From 3e12590d9bf465c9feeca6eb1177e9d37942a919 Mon Sep 17 00:00:00 2001 From: drmDev Date: Sun, 6 Mar 2016 08:57:37 -0500 Subject: [PATCH 2/2] Additional test for sources of differing colors --- .../enchantments/CircleOfAfflictionTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/CircleOfAfflictionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/CircleOfAfflictionTest.java index a23b7690da..04a8ec1532 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/CircleOfAfflictionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/CircleOfAfflictionTest.java @@ -140,4 +140,37 @@ public class CircleOfAfflictionTest extends CardTestPlayerBase { assertLife(playerA, 12); // 12 damage - 4 drains = 8 net life total loss assertLife(playerB, 16); // 4 drains } + + /** + * + */ + @Test + public void testTwoAttackersDamageDifferentColors() { + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); // {1}{W} 2/2 + addCard(Zone.BATTLEFIELD, playerB, "Hill Giant", 1); // {3}{R} 3/3 + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + // As Circle of Affliction enters the battlefield, choose a color. + // Whenever a source of the chosen color deals damage to you, you may pay {1}. If you do, target player loses 1 life and you gain 1 life. + addCard(Zone.HAND, playerA, "Circle of Affliction", 1);// {1}{B} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Circle of Affliction"); + setChoice(playerA, "White"); + + attack(2, playerB, "Silvercoat Lion"); + attack(2, playerB, "Hill Giant"); + addTarget(playerA, playerB); + setChoice(playerA, "Yes"); + addTarget(playerA, playerB); // should not be able to drain Hill Giant with white selected + setChoice(playerA, "Yes"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Circle of Affliction", 1); + + assertLife(playerA, 16); // 5 life loss from combat - 1 drain = 4 net life total loss + assertLife(playerB, 19); + } }