diff --git a/Mage.Sets/src/mage/sets/avacynrestored/AngelOfJubilation.java b/Mage.Sets/src/mage/sets/avacynrestored/AngelOfJubilation.java new file mode 100644 index 00000000000..b612d2dd964 --- /dev/null +++ b/Mage.Sets/src/mage/sets/avacynrestored/AngelOfJubilation.java @@ -0,0 +1,113 @@ +/* + * 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.avacynrestored; + +import mage.Constants; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.continious.BoostControlledEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author noxx + */ +public class AngelOfJubilation extends CardImpl { + + private final static FilterCreaturePermanent filterNonBlack = new FilterCreaturePermanent("nonblack creatures"); + + static { + filterNonBlack.setUseColor(true); + filterNonBlack.setColor(ObjectColor.BLACK); + filterNonBlack.setNotColor(true); + } + + public AngelOfJubilation(UUID ownerId) { + super(ownerId, 2, "Angel of Jubilation", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{1}{W}{W}{W}"); + this.expansionSetCode = "AVR"; + this.subtype.add("Angel"); + + this.color.setWhite(true); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + this.addAbility(FlyingAbility.getInstance()); + + // Other nonblack creatures you control get +1/+1. + this.addAbility(new SimpleStaticAbility(Constants.Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Constants.Duration.WhileOnBattlefield, filterNonBlack, true))); + + // Players can't pay life or sacrifice creatures to cast spells or activate abilities. + this.addAbility(new SimpleStaticAbility(Constants.Zone.BATTLEFIELD, new AngelOfJubilationEffect(Constants.Duration.WhileOnBattlefield))); + } + + public AngelOfJubilation(final AngelOfJubilation card) { + super(card); + } + + @Override + public AngelOfJubilation copy() { + return new AngelOfJubilation(this); + } +} + +class AngelOfJubilationEffect extends ContinuousEffectImpl { + + public AngelOfJubilationEffect(Constants.Duration duration) { + super(duration, Constants.Layer.PlayerEffects, Constants.SubLayer.NA, Constants.Outcome.Detriment); + staticText = "Players can't pay life or sacrifice creatures to cast spells or activate abilities"; + } + + public AngelOfJubilationEffect(final AngelOfJubilationEffect effect) { + super(effect); + } + + @Override + public AngelOfJubilationEffect copy() { + return new AngelOfJubilationEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Player player : game.getPlayers().values()) { + player.setCanPayLifeCost(false); + player.setCanPaySacrificeCost(false); + } + return true; + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/AngelOfJubilationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/AngelOfJubilationTest.java new file mode 100644 index 00000000000..8bec8bb0379 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/AngelOfJubilationTest.java @@ -0,0 +1,61 @@ +package org.mage.test.cards.continuous; + +import mage.Constants; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * Angel of Jubilation + * Other nonblack creatures you control get +1/+1. + * Players can't pay life or sacrifice creatures to cast spells or activate abilities + * + * @author noxx + */ +public class AngelOfJubilationTest extends CardTestPlayerBase { + + /** + * Tests boosting other non black creatures + */ + @Test + public void testBoost() { + addCard(Constants.Zone.BATTLEFIELD, playerA, "Angel of Jubilation"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Devout Chaplain"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Corpse Traders"); + + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPowerToughness(playerA, "Angel of Jubilation", 3, 3); + assertPowerToughness(playerA, "Devout Chaplain", 3, 3); + assertPowerToughness(playerA, "Corpse Traders", 3, 3); + } + + /** + * Tests boost disappeared on leaving battlefield + */ + @Test + public void testNoBoostOnBattlefieldLeave() { + addCard(Constants.Zone.BATTLEFIELD, playerA, "Angel of Jubilation"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Devout Chaplain"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Corpse Traders"); + + addCard(Constants.Zone.HAND, playerA, "Lightning Bolt"); + addCard(Constants.Zone.BATTLEFIELD, playerA, "Mountain"); + + castSpell(1, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Angel of Jubilation"); + + setStopAt(1, Constants.PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerA, "Angel of Jubilation", 0); + assertPowerToughness(playerA, "Devout Chaplain", 2, 2); + assertPowerToughness(playerA, "Corpse Traders", 3, 3); + } + +} diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 762db65cbbe..fc33a0df358 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -28,10 +28,6 @@ package mage.abilities; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - import mage.Constants.AbilityType; import mage.Constants.EffectType; import mage.Constants.Outcome; @@ -42,20 +38,19 @@ import mage.abilities.costs.mana.KickerManaCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.Effects; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.PostResolveEffect; +import mage.abilities.effects.*; import mage.abilities.mana.ManaAbility; import mage.choices.Choice; import mage.choices.Choices; import mage.game.Game; import mage.target.Target; import mage.target.Targets; - import org.apache.log4j.Logger; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** * * @author BetaSteward_at_googlemail.com @@ -465,6 +460,7 @@ public abstract class AbilityImpl> implements Ability { @Override public boolean isInUseableZone(Game game) { // try LKI first + MageObject lkiTest = game.getLastKnownInformation(getSourceId(), zone); if (lkiTest != null) { return true; diff --git a/Mage/src/mage/abilities/costs/common/PayLifeCost.java b/Mage/src/mage/abilities/costs/common/PayLifeCost.java index 3180f8e2f04..6e6e00c0d75 100644 --- a/Mage/src/mage/abilities/costs/common/PayLifeCost.java +++ b/Mage/src/mage/abilities/costs/common/PayLifeCost.java @@ -28,11 +28,12 @@ package mage.abilities.costs.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.CostImpl; import mage.game.Game; +import java.util.UUID; + /** * * @author BetaSteward_at_googlemail.com @@ -53,9 +54,9 @@ public class PayLifeCost extends CostImpl { @Override public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - if (amount > 0 && !game.getPlayer(controllerId).isLifeTotalCanChange()) { - return false; - } + if (amount > 0 && !game.getPlayer(controllerId).canPayLifeCost()) { + return false; + } return game.getPlayer(controllerId).getLife() > amount; } diff --git a/Mage/src/mage/abilities/costs/common/SacrificeSourceCost.java b/Mage/src/mage/abilities/costs/common/SacrificeSourceCost.java index 87096c540b1..9b7cc358288 100644 --- a/Mage/src/mage/abilities/costs/common/SacrificeSourceCost.java +++ b/Mage/src/mage/abilities/costs/common/SacrificeSourceCost.java @@ -28,12 +28,13 @@ package mage.abilities.costs.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.CostImpl; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** * * @author BetaSteward_at_googlemail.com @@ -59,7 +60,10 @@ public class SacrificeSourceCost extends CostImpl { @Override public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - Permanent permanent = game.getPermanent(sourceId); + if (!game.getPlayer(controllerId).canPaySacrificeCost()) { + return false; + } + Permanent permanent = game.getPermanent(sourceId); if (permanent != null) { return true; } diff --git a/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java b/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java index eef4de0b656..e288fa84851 100644 --- a/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java +++ b/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java @@ -28,9 +28,6 @@ package mage.abilities.costs.common; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.Constants.Outcome; import mage.abilities.Ability; import mage.abilities.costs.CostImpl; @@ -38,6 +35,10 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledPermanent; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** * * @author BetaSteward_at_googlemail.com @@ -74,7 +75,10 @@ public class SacrificeTargetCost extends CostImpl { @Override public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - return targets.canChoose(controllerId, game); + if (!game.getPlayer(controllerId).canPaySacrificeCost()) { + return false; + } + return targets.canChoose(controllerId, game); } @Override diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index 1555755a16f..431ec5ed9be 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -28,11 +28,6 @@ package mage.players; -import java.io.Serializable; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; import mage.Constants.Outcome; import mage.Constants.RangeOfInfluence; import mage.MageItem; @@ -60,6 +55,12 @@ import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; import mage.util.Copyable; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + /** * * @author BetaSteward_at_googlemail.com @@ -83,6 +84,10 @@ public interface Player extends MageItem, Copyable { public boolean isCanGainLife(); public void setCanGainLife(boolean canGainLife); public boolean isLifeTotalCanChange(); + public void setCanPayLifeCost(boolean canPayLifeCost); + public boolean canPayLifeCost(); + public void setCanPaySacrificeCost(boolean canPaySacrificeCost); + public boolean canPaySacrificeCost(); public void setLifeTotalCanChange(boolean lifeTotalCanChange); public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable); public Cards getHand(); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index edf88e10206..4dc0d1b2cf3 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -102,6 +102,8 @@ public abstract class PlayerImpl> implements Player, Ser protected boolean isTestMode = false; protected boolean canGainLife = true; protected boolean canLoseLife = true; + protected boolean canPayLifeCost = true; + protected boolean canPaySacrificeCost = true; protected boolean isGameUnderControl = true; protected UUID turnController; protected Set playersUnderYourControl = new HashSet(); @@ -156,6 +158,8 @@ public abstract class PlayerImpl> implements Player, Ser this.attachments.addAll(player.attachments); this.inRange.addAll(player.inRange); this.userData = player.userData; + this.canPayLifeCost = player.canPayLifeCost; + this.canPaySacrificeCost = player.canPaySacrificeCost; } @Override @@ -195,6 +199,8 @@ public abstract class PlayerImpl> implements Player, Ser this.maxHandSize = 7; this.canGainLife = true; this.canLoseLife = true; + this.canPayLifeCost = true; + this.canPaySacrificeCost = true; this.topCardRevealed = false; } @@ -1405,4 +1411,23 @@ public abstract class PlayerImpl> implements Player, Ser // do nothing } + @Override + public boolean canPayLifeCost() { + return isLifeTotalCanChange() && canPayLifeCost; + } + + @Override + public void setCanPayLifeCost(boolean canPayLifeCost) { + this.canPayLifeCost = canPayLifeCost; + } + + @Override + public boolean canPaySacrificeCost() { + return canPaySacrificeCost; + } + + @Override + public void setCanPaySacrificeCost(boolean canPaySacrificeCost) { + this.canPaySacrificeCost = canPaySacrificeCost; + } }