From b2ff7ac380c799b994f1a6f754f093674671506a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 20 Sep 2015 19:13:18 +0200 Subject: [PATCH] * Monocolor hybrid mana - Fixed that the payment did not always try to pay the colored cost if possible. --- .../sets/battleforzendikar/OblivionSower.java | 4 +- .../mage/sets/bornofthegods/Mindreaver.java | 33 +++++----- .../sets/journeyintonyx/DictateOfErebos.java | 9 ++- .../sets/magic2014/ChandraPyromaster.java | 38 +++++------- .../mage/test/cards/mana/HybridManaTest.java | 62 +++++++++++++++++++ .../abilities/costs/mana/ManaCostsImpl.java | 32 ++++++++++ .../common/SacrificeOpponentsEffect.java | 17 ++--- 7 files changed, 143 insertions(+), 52 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/mana/HybridManaTest.java diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/OblivionSower.java b/Mage.Sets/src/mage/sets/battleforzendikar/OblivionSower.java index bfc7d1b091..80518ad1c2 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/OblivionSower.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/OblivionSower.java @@ -106,7 +106,9 @@ class OblivionSowerEffect extends OneShotEffect { Cards exiledLands = new CardsImpl(); exiledLands.addAll(exiledCards.getCards(filter, source.getSourceId(), controller.getId(), game)); if (!exiledLands.isEmpty() && controller.chooseUse(outcome, "Put lands into play?", source, game)) { - FilterCard filterToPlay = new FilterCard("Lands owned by " + targetPlayer.getName() + " to put into play under your control"); + FilterCard filterToPlay = new FilterCard("land" + + (exiledLands.size() > 1 ? "s" : "") + " from exile owned by " + + targetPlayer.getName() + " to put into play under your control"); TargetCard targetCards = new TargetCard(0, exiledLands.size(), Zone.EXILED, filterToPlay); if (controller.chooseTarget(outcome, exiledLands, targetCards, source, game)) { controller.moveCards(new CardsImpl(targetCards.getTargets()), null, Zone.BATTLEFIELD, source, game); diff --git a/Mage.Sets/src/mage/sets/bornofthegods/Mindreaver.java b/Mage.Sets/src/mage/sets/bornofthegods/Mindreaver.java index 231854d0af..c5df6d378d 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/Mindreaver.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/Mindreaver.java @@ -58,8 +58,8 @@ import mage.target.TargetSpell; import mage.util.CardUtil; /** - import mage.constants.Outcome; -* + * import mage.constants.Outcome; + * * @author LevelX2 */ public class Mindreaver extends CardImpl { @@ -77,10 +77,10 @@ public class Mindreaver extends CardImpl { Ability ability = new HeroicAbility(new MindreaverExileEffect(), false); ability.addTarget(new TargetPlayer()); this.addAbility(ability); - - // {U}{U}, Sacrifice Mindreaver: Counter target spell with the same name as a card exiled with mindreaver. + + // {U}{U}, Sacrifice Mindreaver: Counter target spell with the same name as a card exiled with Mindreaver. ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl("{U}{U}")); - FilterSpell filter = new FilterSpell("spell with the same name as a card exiled with mindreaver"); + FilterSpell filter = new FilterSpell("spell with the same name as a card exiled with {this}"); filter.add(new MindreaverNamePredicate(this.getId())); ability.addTarget(new TargetSpell(filter)); ability.addCost(new SacrificeSourceCost()); @@ -116,12 +116,13 @@ class MindreaverExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { UUID exileId = CardUtil.getCardExileZoneId(game, source); + MageObject sourceObject = source.getSourceObject(game); Player opponent = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (opponent != null) { + if (opponent != null && sourceObject != null) { for (int i = 0; i < 3; i++) { Card card = opponent.getLibrary().getFromTop(game); if (card != null) { - card.moveToExile(exileId, "Mindreaver", source.getSourceId(), game); + card.moveToExile(exileId, sourceObject.getIdName(), source.getSourceId(), game); } } } @@ -130,29 +131,29 @@ class MindreaverExileEffect extends OneShotEffect { } class MindreaverNamePredicate implements Predicate { - + private final UUID sourceId; - + public MindreaverNamePredicate(UUID sourceId) { this.sourceId = sourceId; } - + @Override public boolean apply(MageObject input, Game game) { Set cardNames = new HashSet(); UUID exileId = CardUtil.getCardExileZoneId(game, sourceId); ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone != null) { - for(Card card : exileZone.getCards(game)) { + for (Card card : exileZone.getCards(game)) { cardNames.add(card.getName()); } } - // If a player names a card, the player may name either half of a split card, but not both. + // If a player names a card, the player may name either half of a split card, but not both. // A split card has the chosen name if one of its two names matches the chosen name. if (input instanceof SplitCard) { - return cardNames.contains(((SplitCard)input).getLeftHalfCard().getName()) || cardNames.contains(((SplitCard)input).getRightHalfCard().getName()); - } else if (input instanceof Spell && ((Spell)input).getSpellAbility().getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)){ - SplitCard card = (SplitCard) ((Spell)input).getCard(); + return cardNames.contains(((SplitCard) input).getLeftHalfCard().getName()) || cardNames.contains(((SplitCard) input).getRightHalfCard().getName()); + } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) { + SplitCard card = (SplitCard) ((Spell) input).getCard(); return cardNames.contains(card.getLeftHalfCard().getName()) || cardNames.contains(card.getRightHalfCard().getName()); } else { return cardNames.contains(input.getName()); @@ -163,4 +164,4 @@ class MindreaverNamePredicate implements Predicate { public String toString() { return "spell with the same name as a card exiled with {source}"; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/DictateOfErebos.java b/Mage.Sets/src/mage/sets/journeyintonyx/DictateOfErebos.java index 6fd46c4aaf..82b8bc6e84 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/DictateOfErebos.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/DictateOfErebos.java @@ -44,22 +44,21 @@ import mage.filter.predicate.permanent.ControllerPredicate; * @author LevelX2 */ public class DictateOfErebos extends CardImpl { - + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control"); - + static { filter.add(new ControllerPredicate(TargetController.YOU)); } - + public DictateOfErebos(UUID ownerId) { super(ownerId, 65, "Dictate of Erebos", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); this.expansionSetCode = "JOU"; - // Flash this.addAbility(FlashAbility.getInstance()); // Whenever a creature you control dies, each opponent sacrifices a creature. - this.addAbility(new DiesCreatureTriggeredAbility(new SacrificeOpponentsEffect(new FilterControlledCreaturePermanent("a creature")), false, filter)); + this.addAbility(new DiesCreatureTriggeredAbility(new SacrificeOpponentsEffect(new FilterControlledCreaturePermanent("a creature")), false, filter)); } public DictateOfErebos(final DictateOfErebos card) { diff --git a/Mage.Sets/src/mage/sets/magic2014/ChandraPyromaster.java b/Mage.Sets/src/mage/sets/magic2014/ChandraPyromaster.java index 1ce15ed3de..8c3d87f6aa 100644 --- a/Mage.Sets/src/mage/sets/magic2014/ChandraPyromaster.java +++ b/Mage.Sets/src/mage/sets/magic2014/ChandraPyromaster.java @@ -74,7 +74,6 @@ public class ChandraPyromaster extends CardImpl { this.expansionSetCode = "M14"; this.subtype.add("Chandra"); - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(4)), false)); // +1: Chandra, Pyromaster deals 1 damage to target player and 1 damage to up to one target creature that player controls. That creature can't block this turn. @@ -215,7 +214,7 @@ class ChandraPyromasterEffect2 extends OneShotEffect { Card card = library.removeFromTop(game); if (card != null) { controller.moveCardToExileWithInfo(card, source.getSourceId(), sourceObject.getIdName() + " ", source.getSourceId(), game, Zone.LIBRARY, true); - ContinuousEffect effect = new ChandraPyromasterCastFromExileEffect(); + ContinuousEffect effect = new ChandraPyromasterCastFromExileEffect(); effect.setTargetPointer(new FixedTarget(card.getId())); game.addEffect(effect, source); } @@ -273,36 +272,31 @@ class ChandraPyromasterEffect3 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player you = game.getPlayer(source.getControllerId()); - if (you == null) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { return false; } Cards cards = new CardsImpl(); - int max = Math.min(you.getLibrary().size(), 10); - for (int i = 0; i < max; i++) { - Card card = you.getLibrary().removeFromTop(game); - if (card != null) { - card.moveToExile(source.getSourceId(), "Chandra Pyromaster", source.getSourceId(), game); - cards.add(card); - } - } + cards.addAll(controller.getLibrary().getTopCards(game, 10)); + controller.moveCardsToExile(cards.getCards(game), source, game, true, source.getSourceId(), sourceObject.getIdName()); if (cards.getCards(new FilterInstantOrSorceryCard(), game).size() > 0) { TargetCard target = new TargetCard(Zone.EXILED, new FilterInstantOrSorceryCard()); - if (you.chooseTarget(Outcome.PlayForFree, cards, target, source, game)) { + if (controller.chooseTarget(Outcome.PlayForFree, cards, target, source, game)) { Card card = cards.get(target.getFirstTarget(), game); if (card != null) { - Card copy1 = card.copy(); - Card copy2 = card.copy(); - Card copy3 = card.copy(); - if (copy1 != null && you.chooseUse(outcome, "Do you wish to cast copy 1 of " + card.getName(), source, game)) { - you.cast(copy1.getSpellAbility(), game, true); + if (controller.chooseUse(outcome, "Do you wish to cast copy 1 of " + card.getName(), source, game)) { + Card copy1 = card.copy(); + controller.cast(copy1.getSpellAbility(), game, true); } - if (copy2 != null && you.chooseUse(outcome, "Do you wish to cast copy 2 of " + card.getName(), source, game)) { - you.cast(copy2.getSpellAbility(), game, true); + if (controller.chooseUse(outcome, "Do you wish to cast copy 2 of " + card.getName(), source, game)) { + Card copy2 = card.copy(); + controller.cast(copy2.getSpellAbility(), game, true); } - if (copy3 != null && you.chooseUse(outcome, "Do you wish to cast copy 3 of " + card.getName(), source, game)) { - you.cast(copy3.getSpellAbility(), game, true); + if (controller.chooseUse(outcome, "Do you wish to cast copy 3 of " + card.getName(), source, game)) { + Card copy3 = card.copy(); + controller.cast(copy3.getSpellAbility(), game, true); } return true; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/HybridManaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/HybridManaTest.java new file mode 100644 index 0000000000..d39028ab76 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/HybridManaTest.java @@ -0,0 +1,62 @@ +/* + * 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.mana; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class HybridManaTest extends CardTestPlayerBase { + + @Test + public void testCastReaperKingMonoHybrid() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + // Other Scarecrow creatures you control get +1/+1. + // Whenever another Scarecrow enters the battlefield under your control, destroy target permanent. + addCard(Zone.HAND, playerA, "Reaper King", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reaper King"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Reaper King", 1); + + } + +} diff --git a/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java index c6d7654244..5c6cb083be 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java @@ -228,30 +228,62 @@ public class ManaCostsImpl extends ArrayList implements M for (ManaCost cost : this) { if (!cost.isPaid() && cost instanceof ColoredManaCost) { cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } } } for (ManaCost cost : this) { if (!cost.isPaid() && cost instanceof HybridManaCost) { cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } } } + // Mono Hybrid mana costs + // First try only to pay colored mana with the pool + for (ManaCost cost : this) { + if (!cost.isPaid() && cost instanceof MonoHybridManaCost) { + if (((((MonoHybridManaCost) cost).containsColor(ColoredManaSymbol.W)) && pool.getWhite() > 0) + || ((((MonoHybridManaCost) cost).containsColor(ColoredManaSymbol.B)) && pool.getBlack() > 0) + || ((((MonoHybridManaCost) cost).containsColor(ColoredManaSymbol.R)) && pool.getRed() > 0) + || ((((MonoHybridManaCost) cost).containsColor(ColoredManaSymbol.G)) && pool.getGreen() > 0) + || ((((MonoHybridManaCost) cost).containsColor(ColoredManaSymbol.U)) && pool.getBlue() > 0)) { + cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } + } + } + } + // if colored didn't fit pay colorless with the mana for (ManaCost cost : this) { if (!cost.isPaid() && cost instanceof MonoHybridManaCost) { cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } } } for (ManaCost cost : this) { if (!cost.isPaid() && cost instanceof SnowManaCost) { cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } } } for (ManaCost cost : this) { if (!cost.isPaid() && cost instanceof GenericManaCost) { cost.assignPayment(game, ability, pool); + if (pool.count() == 0) { + return; + } } } diff --git a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java index 6edd6c8344..54e94ab31b 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.effects.common; import java.util.ArrayList; @@ -46,8 +45,7 @@ import mage.target.TargetPermanent; import mage.util.CardUtil; /** - * All opponents have to sacrifice [amount] permanents - * that match the [filter]. + * All opponents have to sacrifice [amount] permanents that match the [filter]. * * @author LevelX2 */ @@ -59,6 +57,7 @@ public class SacrificeOpponentsEffect extends OneShotEffect { public SacrificeOpponentsEffect(FilterPermanent filter) { this(1, filter); } + public SacrificeOpponentsEffect(int amount, FilterPermanent filter) { this(new StaticValue(amount), filter); } @@ -87,12 +86,14 @@ public class SacrificeOpponentsEffect extends OneShotEffect { filter.add(new ControllerPredicate(TargetController.YOU)); for (UUID playerId : game.getOpponents(source.getControllerId())) { Player player = game.getPlayer(playerId); - if (player != null) { + if (player != null) { int numTargets = Math.min(amount.calculate(game, source, this), game.getBattlefield().countAll(filter, player.getId(), game)); - TargetPermanent target = new TargetPermanent(numTargets, numTargets, filter, true); - if (target.canChoose(player.getId(), game)) { - player.chooseTarget(Outcome.Sacrifice, target, source, game); - perms.addAll(target.getTargets()); + if (numTargets > 0) { + TargetPermanent target = new TargetPermanent(numTargets, numTargets, filter, true); + if (target.canChoose(player.getId(), game)) { + player.chooseTarget(Outcome.Sacrifice, target, source, game); + perms.addAll(target.getTargets()); + } } } }