From 4b66cd8367d5142ff6f6ebb3c2cb253cf8b46faf Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Dec 2014 20:20:23 +0100 Subject: [PATCH] Added possibility to use Morph with a land. Minor changes to some cards. --- .../src/mage/sets/alliances/ForceOfWill.java | 2 +- .../src/mage/sets/fifthdawn/FistOfSuns.java | 3 +- .../src/mage/sets/magic2013/Omniscience.java | 3 +- .../src/mage/sets/stronghold/DreamHalls.java | 3 +- Mage.Sets/src/mage/sets/tempest/Aluren.java | 3 +- .../src/mage/sets/tenth/LlanowarElves.java | 2 + Mage/src/mage/abilities/AbilitiesImpl.java | 1 + Mage/src/mage/abilities/Ability.java | 4 + Mage/src/mage/abilities/AbilityImpl.java | 75 +++--- .../common/SourceIsSpellCondition.java | 55 ++++ .../costs/common/ExileFromHandCost.java | 2 +- .../mage/abilities/keyword/MorphAbility.java | 26 +- Mage/src/mage/constants/SpellAbilityType.java | 1 + Mage/src/mage/game/stack/StackAbility.java | 6 + Mage/src/mage/players/PlayerImpl.java | 248 +++++++++++++----- 15 files changed, 323 insertions(+), 111 deletions(-) create mode 100644 Mage/src/mage/abilities/condition/common/SourceIsSpellCondition.java diff --git a/Mage.Sets/src/mage/sets/alliances/ForceOfWill.java b/Mage.Sets/src/mage/sets/alliances/ForceOfWill.java index b1740db8b48..0dc5a11ef54 100644 --- a/Mage.Sets/src/mage/sets/alliances/ForceOfWill.java +++ b/Mage.Sets/src/mage/sets/alliances/ForceOfWill.java @@ -56,7 +56,7 @@ public class ForceOfWill extends CardImpl { this.color.setBlue(true); // You may pay 1 life and exile a blue card from your hand rather than pay Force of Will's mana cost. - FilterOwnedCard filter = new FilterOwnedCard("blue card from your hand"); + FilterOwnedCard filter = new FilterOwnedCard("a blue card from your hand"); filter.add(new ColorPredicate(ObjectColor.BLUE)); filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself diff --git a/Mage.Sets/src/mage/sets/fifthdawn/FistOfSuns.java b/Mage.Sets/src/mage/sets/fifthdawn/FistOfSuns.java index 60e1e3520e6..079fa56f23a 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/FistOfSuns.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/FistOfSuns.java @@ -30,6 +30,7 @@ package mage.sets.fifthdawn; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.SourceIsSpellCondition; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffectImpl; @@ -71,7 +72,7 @@ public class FistOfSuns extends CardImpl { class FistOfSunsRuleEffect extends ContinuousEffectImpl { - static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new ManaCostsImpl("{W}{U}{B}{R}{G}")); + static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new ManaCostsImpl("{W}{U}{B}{R}{G}"), SourceIsSpellCondition.getInstance()); public FistOfSunsRuleEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); diff --git a/Mage.Sets/src/mage/sets/magic2013/Omniscience.java b/Mage.Sets/src/mage/sets/magic2013/Omniscience.java index 522c5525a0e..1689e90960f 100644 --- a/Mage.Sets/src/mage/sets/magic2013/Omniscience.java +++ b/Mage.Sets/src/mage/sets/magic2013/Omniscience.java @@ -30,6 +30,7 @@ package mage.sets.magic2013; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.SourceIsSpellCondition; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.CardImpl; @@ -73,7 +74,7 @@ public class Omniscience extends CardImpl { class OmniscienceCastingEffect extends ContinuousEffectImpl { static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility( - null, null, null, new FilterNonlandCard(), true); + null, SourceIsSpellCondition.getInstance(), null, new FilterNonlandCard(), true); public OmniscienceCastingEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); diff --git a/Mage.Sets/src/mage/sets/stronghold/DreamHalls.java b/Mage.Sets/src/mage/sets/stronghold/DreamHalls.java index 1ac81e9d2e6..4df3c458c12 100644 --- a/Mage.Sets/src/mage/sets/stronghold/DreamHalls.java +++ b/Mage.Sets/src/mage/sets/stronghold/DreamHalls.java @@ -30,6 +30,7 @@ package mage.sets.stronghold; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.SourceIsSpellCondition; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.effects.ContinuousEffectImpl; @@ -82,7 +83,7 @@ class DreamHallsEffect extends ContinuousEffectImpl { filter.add(new SharesColorWithSourcePredicate()); } - static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new DiscardCardCost(filter)); + static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(new DiscardCardCost(filter), SourceIsSpellCondition.getInstance()); public DreamHallsEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); diff --git a/Mage.Sets/src/mage/sets/tempest/Aluren.java b/Mage.Sets/src/mage/sets/tempest/Aluren.java index 350d20aba87..71cbf6cbdde 100644 --- a/Mage.Sets/src/mage/sets/tempest/Aluren.java +++ b/Mage.Sets/src/mage/sets/tempest/Aluren.java @@ -30,6 +30,7 @@ package mage.sets.tempest; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.SourceIsSpellCondition; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.Effect; @@ -103,7 +104,7 @@ class AlurenRuleEffect extends ContinuousEffectImpl { filter.add(new ConvertedManaCostPredicate(ComparisonType.LessThan, 4)); } - static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(null, null, null, filter, true); + static AlternativeCostSourceAbility alternativeCastingCostAbility = new AlternativeCostSourceAbility(null, SourceIsSpellCondition.getInstance(), null, filter, true); public AlurenRuleEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); diff --git a/Mage.Sets/src/mage/sets/tenth/LlanowarElves.java b/Mage.Sets/src/mage/sets/tenth/LlanowarElves.java index 517b33dfd00..aa37d58d379 100644 --- a/Mage.Sets/src/mage/sets/tenth/LlanowarElves.java +++ b/Mage.Sets/src/mage/sets/tenth/LlanowarElves.java @@ -49,6 +49,8 @@ public class LlanowarElves extends CardImpl { this.color.setGreen(true); this.power = new MageInt(1); this.toughness = new MageInt(1); + + // {T}: Add {G} to your mana pool. this.addAbility(new GreenManaAbility()); } diff --git a/Mage/src/mage/abilities/AbilitiesImpl.java b/Mage/src/mage/abilities/AbilitiesImpl.java index 7826656f7b1..3d15dc79c62 100644 --- a/Mage/src/mage/abilities/AbilitiesImpl.java +++ b/Mage/src/mage/abilities/AbilitiesImpl.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.UUID; import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.costs.AlternativeCost; +import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.Cost; import mage.abilities.keyword.ProtectionAbility; import mage.abilities.mana.ManaAbility; diff --git a/Mage/src/mage/abilities/Ability.java b/Mage/src/mage/abilities/Ability.java index 174550102ff..aab71dc8a90 100644 --- a/Mage/src/mage/abilities/Ability.java +++ b/Mage/src/mage/abilities/Ability.java @@ -47,6 +47,7 @@ import mage.constants.EffectType; import mage.constants.Zone; import mage.game.Controllable; import mage.game.Game; +import mage.players.Player; import mage.target.Target; import mage.target.Targets; @@ -473,4 +474,7 @@ public interface Ability extends Controllable, Serializable { * @param active execute no cost modification */ void setCostModificationActive(boolean active); + + boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game); + } diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 201d831c8c0..4a9b3b173f5 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -56,6 +56,7 @@ import java.util.List; import java.util.UUID; import mage.abilities.costs.common.TapSourceCost; import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; /** * @@ -238,36 +239,10 @@ public abstract class AbilityImpl implements Ability { // as buyback, kicker, or convoke costs (see rules 117.8 and 117.9), the player announces his // or her intentions to pay any or all of those costs (see rule 601.2e). // A player can't apply two alternative methods of casting or two alternative costs to a single spell. - if (sourceObject != null && !(this instanceof FlashbackAbility)) { - boolean alternativeCostisUsed = false; - for (Ability ability : sourceObject.getAbilities()) { - // if cast for noMana no Alternative costs are allowed - if (!noMana && ability instanceof AlternativeSourceCosts) { - AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability; - if (alternativeSpellCosts.isAvailable(this, game)) { - if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) { - // only one alternative costs may be activated - alternativeCostisUsed = true; - break; - } - } - } - if (ability instanceof OptionalAdditionalSourceCosts) { - ((OptionalAdditionalSourceCosts)ability).addOptionalAdditionalCosts(this, game); - } - } - // controller specific alternate spell costs - if (!noMana && !alternativeCostisUsed) { - if (this.getAbilityType().equals(AbilityType.SPELL)) { - for (AlternativeSourceCosts alternativeSourceCosts: controller.getAlternativeSourceCosts()) { - if (alternativeSourceCosts.isAvailable(this, game)) { - if (alternativeSourceCosts.askToActivateAlternativeCosts(this, game)) { - // only one alternative costs may be activated - break; - } - } - } - } + if (!activateAlternateOrAdditionalCosts(sourceObject, noMana, controller, game)){ + if (getAbilityType().equals(AbilityType.SPELL) + && ((SpellAbility) this).getSpellAbilityType().equals(SpellAbilityType.LAND_ALTERNATE)) { + return false; } } @@ -367,7 +342,7 @@ public abstract class AbilityImpl implements Ability { game.informPlayers(new StringBuilder(controller.getName()).append(" announces a value of ").append(xValue).append(" for ").append(variableManaCost.getText()).toString()); } activated = true; - // fire if tapped for mana (may only fires now because else costs of ability itself can be payed with mana of abilities that trigger for that event + // fire if tapped for mana (may only fire now because else costs of ability itself can be payed with mana of abilities that trigger for that event if (this.getAbilityType().equals(AbilityType.MANA)) { for (Cost cost: costs) { if (cost instanceof TapSourceCost) { @@ -384,6 +359,44 @@ public abstract class AbilityImpl implements Ability { return activated; } + @Override + public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game) { + boolean alternativeCostisUsed = false; + if (sourceObject != null && !(sourceObject instanceof Permanent) && !(this instanceof FlashbackAbility)) { + for (Ability ability : sourceObject.getAbilities()) { + // if cast for noMana no Alternative costs are allowed + if (!noMana && ability instanceof AlternativeSourceCosts) { + AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability; + if (alternativeSpellCosts.isAvailable(this, game)) { + if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) { + // only one alternative costs may be activated + alternativeCostisUsed = true; + break; + } + } + } + if (ability instanceof OptionalAdditionalSourceCosts) { + ((OptionalAdditionalSourceCosts)ability).addOptionalAdditionalCosts(this, game); + } + } + // controller specific alternate spell costs + if (!noMana && !alternativeCostisUsed) { + if (this.getAbilityType().equals(AbilityType.SPELL)) { + for (AlternativeSourceCosts alternativeSourceCosts: controller.getAlternativeSourceCosts()) { + if (alternativeSourceCosts.isAvailable(this, game)) { + if (alternativeSourceCosts.askToActivateAlternativeCosts(this, game)) { + // only one alternative costs may be activated + alternativeCostisUsed = true; + break; + } + } + } + } + } + } + return alternativeCostisUsed; + } + /** * Handles the setting of non mana X costs * @param controller * diff --git a/Mage/src/mage/abilities/condition/common/SourceIsSpellCondition.java b/Mage/src/mage/abilities/condition/common/SourceIsSpellCondition.java new file mode 100644 index 00000000000..94a27fa1807 --- /dev/null +++ b/Mage/src/mage/abilities/condition/common/SourceIsSpellCondition.java @@ -0,0 +1,55 @@ +/* + * 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.abilities.condition.common; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.constants.CardType; +import mage.game.Game; + +/** + * + * @author LevelX2 + */ + +public class SourceIsSpellCondition implements Condition { + + private static final SourceIsSpellCondition fInstance = new SourceIsSpellCondition(); + + public static Condition getInstance() { + return fInstance; + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject object = game.getObject(source.getSourceId()); + return object != null && !object.getCardType().contains(CardType.LAND); + } +} diff --git a/Mage/src/mage/abilities/costs/common/ExileFromHandCost.java b/Mage/src/mage/abilities/costs/common/ExileFromHandCost.java index 76d2861ab45..d447ab4d7c1 100644 --- a/Mage/src/mage/abilities/costs/common/ExileFromHandCost.java +++ b/Mage/src/mage/abilities/costs/common/ExileFromHandCost.java @@ -50,7 +50,7 @@ public class ExileFromHandCost extends CostImpl { public ExileFromHandCost(TargetCardInHand target) { this.addTarget(target); - this.text = "Exile " + target.getTargetName(); + this.text = "exile " + target.getTargetName(); } public ExileFromHandCost(ExileFromHandCost cost) { diff --git a/Mage/src/mage/abilities/keyword/MorphAbility.java b/Mage/src/mage/abilities/keyword/MorphAbility.java index 53b295cb29f..4e4e306da66 100644 --- a/Mage/src/mage/abilities/keyword/MorphAbility.java +++ b/Mage/src/mage/abilities/keyword/MorphAbility.java @@ -33,7 +33,6 @@ import java.util.Iterator; import java.util.List; import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.TurnFaceUpAbility; @@ -48,6 +47,7 @@ import mage.abilities.costs.mana.ManaCosts; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.continious.SourceEffect; import mage.cards.Card; +import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Layer; @@ -181,7 +181,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost @Override public boolean askToActivateAlternativeCosts(Ability ability, Game game) { - if (ability instanceof SpellAbility) { + if (ability.getAbilityType().equals(AbilityType.SPELL)) { Player player = game.getPlayer(controllerId); Spell spell = game.getStack().getSpell(ability.getId()); if (player != null && spell != null) { @@ -214,6 +214,28 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost } } } + if (ability.getAbilityType().equals(AbilityType.PLAY_LAND)) { + Player player = game.getPlayer(controllerId); + if (player != null) { + this.resetMorph(); + if (alternateCosts.canPay(ability, sourceId, controllerId, game)) { + if (player.chooseUse(Outcome.Benefit, new StringBuilder("Cast this card as a 2/2 face-down creature for ").append(getCosts().getText()).append(" ?").toString(), game)) { + activateMorph(game); + // change mana costs + ability.getManaCostsToPay().clear(); + ability.getCosts().clear(); + for (Iterator it = this.alternateCosts.iterator(); it.hasNext();) { + Cost cost = (Cost) it.next(); + if (cost instanceof ManaCost) { + ability.getManaCostsToPay().add((ManaCost)cost.copy()); + } else { + ability.getCosts().add(cost.copy()); + } + } + } + } + } + } return isActivated(ability, game); } diff --git a/Mage/src/mage/constants/SpellAbilityType.java b/Mage/src/mage/constants/SpellAbilityType.java index b07ed45c914..70af522434f 100644 --- a/Mage/src/mage/constants/SpellAbilityType.java +++ b/Mage/src/mage/constants/SpellAbilityType.java @@ -7,6 +7,7 @@ package mage.constants; public enum SpellAbilityType { BASE("Basic SpellAbility"), BASE_ALTERNATE("Basic SpellAbility Alternate"), // used for Overload, Flashback to know they must be handled as Alternate casting costs + LAND_ALTERNATE("Basic SpellAbility Alternate Land"), // used for Lands with Morph to cast as Face Down creature SPLIT("Split SpellAbility"), SPLIT_FUSED("Split SpellAbility"), SPLIT_LEFT("LeftSplit SpellAbility"), diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index bc8ba084a94..32484612eb2 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -56,6 +56,7 @@ import java.util.List; import java.util.UUID; import mage.cards.Card; import mage.constants.AbilityWord; +import mage.players.Player; /** * @@ -450,6 +451,11 @@ public class StackAbility implements StackObject, Ability { throw new UnsupportedOperationException("Not supported."); } + @Override + public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + @Override public String getGameLogMessage(Game game) { throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates. diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index f8d813548b8..fbe613214d7 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -61,6 +61,7 @@ import mage.abilities.costs.AlternativeCost; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.Cost; +import mage.abilities.costs.OptionalAdditionalSourceCosts; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.RestrictionEffect; @@ -88,6 +89,14 @@ import mage.constants.ManaType; import mage.constants.Outcome; import mage.constants.PhaseStep; import mage.constants.PlayerAction; +import static mage.constants.PlayerAction.PASS_PRIORITY_CANCEL_ALL_ACTIONS; +import static mage.constants.PlayerAction.PASS_PRIORITY_UNTIL_MY_NEXT_TURN; +import static mage.constants.PlayerAction.PASS_PRIORITY_UNTIL_NEXT_MAIN_PHASE; +import static mage.constants.PlayerAction.PASS_PRIORITY_UNTIL_NEXT_TURN; +import static mage.constants.PlayerAction.PASS_PRIORITY_UNTIL_STACK_RESOLVED; +import static mage.constants.PlayerAction.PASS_PRIORITY_UNTIL_TURN_END_STEP; +import static mage.constants.PlayerAction.PERMISSION_REQUESTS_ALLOWED_OFF; +import static mage.constants.PlayerAction.PERMISSION_REQUESTS_ALLOWED_ON; import mage.constants.RangeOfInfluence; import mage.constants.SpellAbilityType; import mage.constants.TimingRule; @@ -940,6 +949,32 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean playLand(Card card, Game game) { + // Check for alternate casting possibilities: e.g. land with Morph + ActivatedAbility playLandAbility = null; + boolean found = false; + for (Ability ability : card.getAbilities()) { + // if cast for noMana no Alternative costs are allowed + if ((ability instanceof AlternativeSourceCosts) ||(ability instanceof OptionalAdditionalSourceCosts)) { + found = true; + } + if (ability instanceof PlayLandAbility) { + playLandAbility = (ActivatedAbility) ability; + } + } + if (found) { + SpellAbility spellAbility = new SpellAbility(null, "", game.getState().getZone(card.getId()), SpellAbilityType.LAND_ALTERNATE); + spellAbility.setControllerId(this.getId()); + spellAbility.setSourceId(card.getId()); + if (cast(spellAbility, game, false)) { + return true; + } + } + if (playLandAbility == null) { + return false; + } + if (!playLandAbility.canActivate(this.playerId, game)) { + return false; + } //20091005 - 305.1 if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId))) { // int bookmark = game.bookmarkState(); @@ -1026,35 +1061,35 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean activateAbility(ActivatedAbility ability, Game game) { boolean result; - if (!ability.canActivate(this.playerId, game)) { - return false; - } - if (ability instanceof PassAbility) { pass(game); return true; } - else if (ability instanceof PlayLandAbility) { + if (ability instanceof PlayLandAbility) { Card card = hand.get(ability.getSourceId(), game); if (card == null) { card = game.getCard(ability.getSourceId()); } result = playLand(card, game); - } - else if (ability instanceof SpecialAction) { - result = specialAction((SpecialAction)ability.copy(), game); - } - else if (ability instanceof ManaAbility) { - result = playManaAbility((ManaAbility)ability.copy(), game); - } - else if (ability instanceof FlashbackAbility){ - result = playAbility(ability.copy(), game); - } - else if (ability instanceof SpellAbility) { - result = cast((SpellAbility)ability, game, false); - } - else { - result = playAbility(ability.copy(), game); + } else { + if (!ability.canActivate(this.playerId, game)) { + return false; + } + if (ability instanceof SpecialAction) { + result = specialAction((SpecialAction)ability.copy(), game); + } + else if (ability instanceof ManaAbility) { + result = playManaAbility((ManaAbility)ability.copy(), game); + } + else if (ability instanceof FlashbackAbility){ + result = playAbility(ability.copy(), game); + } + else if (ability instanceof SpellAbility) { + result = cast((SpellAbility)ability, game, false); + } + else { + result = playAbility(ability.copy(), game); + } } //if player has taken an action then reset all player passed flags @@ -1120,9 +1155,21 @@ public abstract class PlayerImpl implements Player, Serializable { protected LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game) { LinkedHashMap useable = new LinkedHashMap<>(); if (!(object instanceof Permanent) || ((Permanent)object).canUseActivatedAbilities(game)) { - for (ActivatedAbility ability: object.getAbilities().getActivatedAbilities(zone)) { - if (ability.canActivate(playerId, game)) { - useable.put(ability.getId(), ability); + for (Ability ability: object.getAbilities()) { + if (ability.getZone().match(zone)) { + if (ability instanceof ActivatedAbility) { + if (((ActivatedAbility)ability).canActivate(playerId, game)) { + useable.put(ability.getId(), (ActivatedAbility)ability); + } + } else if (ability instanceof AlternativeSourceCosts){ + if (object.getCardType().contains(CardType.LAND)) { + for (Ability ability2: object.getAbilities().copy()) { + if (ability2 instanceof PlayLandAbility) { + useable.put(ability2.getId(), (ActivatedAbility)ability2); + } + } + } + } } } if (zone != Zone.HAND) { @@ -2094,9 +2141,8 @@ public abstract class PlayerImpl implements Player, Serializable { } } } - - MageObject object = game.getObject(ability.getSourceId()); - for (Ability objectAbility :object.getAbilities()) { + // old alternate costs + for (Ability objectAbility :sourceObject.getAbilities()) { if (objectAbility instanceof AlternativeCostSourceAbility) { if (objectAbility.getCosts().canPay(ability, ability.getSourceId(), playerId, game)) { return true; @@ -2109,38 +2155,79 @@ public abstract class PlayerImpl implements Player, Serializable { return true; } } - - if (!(sourceObject instanceof Permanent)) { - for (Ability alternateSourceCostsAbility : sourceObject.getAbilities()) { - // if cast for noMana no Alternative costs are allowed - if (alternateSourceCostsAbility instanceof AlternativeSourceCosts) { - if (((AlternativeSourceCosts)alternateSourceCostsAbility).isAvailable(ability, game)) { - if (alternateSourceCostsAbility.getCosts().canPay(ability, playerId, playerId, game)) { - ManaCostsImpl manaCosts = new ManaCostsImpl(); - for(Cost cost:alternateSourceCostsAbility.getCosts()) { - if (cost instanceof ManaCost) { - manaCosts.add(cost); - } - } + // new alternate costs + if(canPlayCardByAlternateCost(card, available, ability, game)) { + return true; + } + } + return false; + } - if (manaCosts.size() == 0) { - return true; + protected boolean canPlayCardByAlternateCost(Card sourceObject, ManaOptions available, Ability ability, Game game) { + if (!(sourceObject instanceof Permanent)) { + for (Ability alternateSourceCostsAbility : sourceObject.getAbilities()) { + // if cast for noMana no Alternative costs are allowed + if (alternateSourceCostsAbility instanceof AlternativeSourceCosts) { + if (((AlternativeSourceCosts)alternateSourceCostsAbility).isAvailable(ability, game)) { + if (alternateSourceCostsAbility.getCosts().canPay(ability, playerId, playerId, game)) { + ManaCostsImpl manaCosts = new ManaCostsImpl(); + for(Cost cost:alternateSourceCostsAbility.getCosts()) { + if (cost instanceof ManaCost) { + manaCosts.add(cost); } - else { - for (Mana mana: manaCosts.getOptions()) { - for (Mana avail: available) { - if (mana.enough(avail)) { - return true; - } + } + + if (manaCosts.size() == 0) { + return true; + } + else { + for (Mana mana: manaCosts.getOptions()) { + for (Mana avail: available) { + if (mana.enough(avail)) { + return true; } } - } + } } - } + } + } + } + } + } + return false; + } + protected boolean canLandPlayAlternateSourceCostsAbility(Card sourceObject, ManaOptions available, Ability ability, Game game) { + if (!(sourceObject instanceof Permanent)) { + Ability sourceAbility = null; + for(Ability landAbility : sourceObject.getAbilities()) { + if (landAbility.getAbilityType().equals(AbilityType.PLAY_LAND)) { + sourceAbility = landAbility; + break; + } + } + if (sourceAbility != null && ((AlternativeSourceCosts)ability).isAvailable(sourceAbility, game)) { + if (ability.getCosts().canPay(ability, sourceObject.getId(), this.getId(), game)) { + ManaCostsImpl manaCosts = new ManaCostsImpl(); + for(Cost cost:ability.getCosts()) { + if (cost instanceof ManaCost) { + manaCosts.add(cost); + } + } + + if (manaCosts.size() == 0) { + return true; + } + else { + for (Mana mana: manaCosts.getOptions()) { + for (Mana avail: available) { + if (mana.enough(avail)) { + return true; + } + } + } } } } - } return false; } @@ -2156,15 +2243,19 @@ public abstract class PlayerImpl implements Player, Serializable { if (hidden) { for (Card card : hand.getUniqueCards(game)) { - for (Ability ability : card.getAbilities().getPlayableAbilities(Zone.HAND)) { // gets this activated ability from hand? (Morph?) - if (ability instanceof ActivatedAbility) { - if (ability instanceof PlayLandAbility) { - if (game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, ability.getSourceId(), ability.getSourceId(), playerId), ability, game, true)) { - break; + for (Ability ability : card.getAbilities()) { // gets this activated ability from hand? (Morph?) + if (ability.getZone().match(Zone.HAND)) { + if (ability instanceof ActivatedAbility) { + if (!(ability instanceof PlayLandAbility) || + !game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, ability.getSourceId(), ability.getSourceId(), playerId), ability, game, true)) { + if (canPlay((ActivatedAbility) ability, availableMana, card, game)) { + playable.add(ability); + } + } + } else if (card.getCardType().contains(CardType.LAND) && ability instanceof AlternativeSourceCosts) { + if (canLandPlayAlternateSourceCostsAbility(card, availableMana, ability, game)) { // e.g. Land with Morph + playable.add(ability); } - } - if (canPlay((ActivatedAbility) ability, availableMana, card, game)) { - playable.add(ability); } } } @@ -2265,23 +2356,36 @@ public abstract class PlayerImpl implements Player, Serializable { available.addMana(manaPool.getMana()); for (Card card : hand.getCards(game)) { - for (ActivatedAbility ability : card.getAbilities().getPlayableAbilities(Zone.HAND)) { - if (ability instanceof PlayLandAbility) { - if (game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, ability.getSourceId(), ability.getSourceId(), playerId), ability, game, true)) { - break; + Abilities: + for (Ability ability : card.getAbilities()) { + if (ability.getZone().match(Zone.HAND)) { + switch (ability.getAbilityType()) { + case PLAY_LAND: + if (game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, ability.getSourceId(), ability.getSourceId(), playerId), ability, game, true)) { + break; + } + if (canPlay((ActivatedAbility) ability, available, card, game)) { + playable.add(card.getId()); + break Abilities; + } + break; + case ACTIVATED: + case SPELL: + if (canPlay((ActivatedAbility) ability, available, card, game)) { + playable.add(card.getId()); + break Abilities; + } + break; + case STATIC: + if (card.getCardType().contains(CardType.LAND) && ability instanceof AlternativeSourceCosts) { + if (canLandPlayAlternateSourceCostsAbility(card, available, ability, game)) { // e.g. Land with Morph + playable.add(card.getId()); + break Abilities; + } + } } } - if (canPlay(ability, available, card, game)) { - playable.add(card.getId()); - break; - } } -// for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) { -// if (!playable.contains(ability.getSourceId()) && canPlay(ability, available, card, game)) { -// playable.add(card.getId()); -// break; -// } -// } } }