diff --git a/Mage/src/mage/abilities/SpellAbility.java b/Mage/src/mage/abilities/SpellAbility.java index a33ec526f8..dcbc1c5602 100644 --- a/Mage/src/mage/abilities/SpellAbility.java +++ b/Mage/src/mage/abilities/SpellAbility.java @@ -39,6 +39,7 @@ import mage.constants.SpellAbilityType; import mage.constants.TimingRule; import mage.constants.Zone; import mage.game.Game; +import mage.game.events.GameEvent; /** * @@ -97,6 +98,14 @@ public class SpellAbility extends ActivatedAbilityImpl { if (this.getManaCosts().isEmpty() && this.getCosts().isEmpty()) { return false; } + // Check if rule modifying events prevent to cast the spell in check playable mode + if (this.isCheckPlayableMode()) { + if (game.getContinuousEffects().preventedByRuleModification( + GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, this.getId(), this.getSourceId(), playerId), game, true)) { + return false; + } + } + if (costs.canPay(sourceId, controllerId, game)) { if (getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) { SplitCard splitCard = (SplitCard) game.getCard(getSourceId()); diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 82dd5c88d2..e86c2c9068 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -80,6 +80,7 @@ public class ContinuousEffects implements Serializable { //transient Continuous effects private ContinuousEffectsList layeredEffects = new ContinuousEffectsList<>(); + private ContinuousEffectsList continuousRuleModifyingEffects = new ContinuousEffectsList<>(); private ContinuousEffectsList replacementEffects = new ContinuousEffectsList<>(); private ContinuousEffectsList preventionEffects = new ContinuousEffectsList<>(); private ContinuousEffectsList requirementEffects = new ContinuousEffectsList<>(); @@ -111,6 +112,7 @@ public class ContinuousEffects implements Serializable { this.planeswalkerRedirectionEffect = effect.planeswalkerRedirectionEffect.copy(); this.auraReplacementEffect = effect.auraReplacementEffect.copy(); layeredEffects = effect.layeredEffects.copy(); + continuousRuleModifyingEffects = effect.continuousRuleModifyingEffects.copy(); replacementEffects = effect.replacementEffects.copy(); preventionEffects = effect.preventionEffects.copy(); requirementEffects = effect.requirementEffects.copy(); @@ -130,7 +132,8 @@ public class ContinuousEffects implements Serializable { } private void collectAllEffects() { - allEffectsLists.add(layeredEffects); + allEffectsLists.add(layeredEffects); + allEffectsLists.add(continuousRuleModifyingEffects); allEffectsLists.add(replacementEffects); allEffectsLists.add(preventionEffects); allEffectsLists.add(requirementEffects); @@ -157,6 +160,7 @@ public class ContinuousEffects implements Serializable { public void removeEndOfCombatEffects() { layeredEffects.removeEndOfCombatEffects(); + continuousRuleModifyingEffects.removeEndOfCombatEffects(); replacementEffects.removeEndOfCombatEffects(); preventionEffects.removeEndOfCombatEffects(); requirementEffects.removeEndOfCombatEffects(); @@ -170,6 +174,7 @@ public class ContinuousEffects implements Serializable { public void removeEndOfTurnEffects() { layeredEffects.removeEndOfTurnEffects(); + continuousRuleModifyingEffects.removeEndOfTurnEffects(); replacementEffects.removeEndOfTurnEffects(); preventionEffects.removeEndOfTurnEffects(); requirementEffects.removeEndOfTurnEffects(); @@ -183,6 +188,7 @@ public class ContinuousEffects implements Serializable { public void removeInactiveEffects(Game game) { layeredEffects.removeInactiveEffects(game); + continuousRuleModifyingEffects.removeInactiveEffects(game); replacementEffects.removeInactiveEffects(game); preventionEffects.removeInactiveEffects(game); requirementEffects.removeInactiveEffects(game); @@ -320,8 +326,7 @@ public class ContinuousEffects implements Serializable { * @param game * @return a list of all {@link ReplacementEffect} that apply to the current event */ - private HashMap> getApplicableReplacementEffects(GameEvent event, Game game) { - // List replaceEffects = new ArrayList(); + private HashMap> getApplicableReplacementEffects(GameEvent event, Game game) { HashMap> replaceEffects = new HashMap<>(); if (planeswalkerRedirectionEffect.applies(event, null, game)) { replaceEffects.put(planeswalkerRedirectionEffect, null); @@ -588,8 +593,34 @@ public class ContinuousEffects implements Serializable { } } - + public boolean preventedByRuleModification(GameEvent event, Game game, boolean checkPlayableMode) { + for (ContinuousRuleModifiyingEffect effect: continuousRuleModifyingEffects) { + for (Ability ability : continuousRuleModifyingEffects.getAbility(effect.getId())) { + if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) { + if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { + if (effect.applies(event, ability, checkPlayableMode, game)) { + if (!checkPlayableMode) { + String message = effect.getInfoMessage(ability, game); + if (message != null && !message.isEmpty()) { + Player player = game.getPlayer(event.getPlayerId()); + if (player != null) { + game.informPlayer(player, message); + } + } + } + return true; + } + } + } + } + } + return false; + } + public boolean replaceEvent(GameEvent event, Game game) { + if (preventedByRuleModification(event, game, false)) { + return true; + } boolean caught = false; HashMap> consumed = new HashMap<>(); do { @@ -860,6 +891,10 @@ public class ContinuousEffects implements Serializable { SpliceCardEffect newSpliceCardEffect = (SpliceCardEffect)effect; spliceCardEffects.addEffect(newSpliceCardEffect, source); break; + case CONTINUOUS_RULE_MODIFICATION: + ContinuousRuleModifiyingEffect newContinuousRuleModifiyingEffect = (ContinuousRuleModifiyingEffect)effect; + continuousRuleModifyingEffects.addEffect(newContinuousRuleModifiyingEffect, source); + break; default: layeredEffects.addEffect(effect, source); break; diff --git a/Mage/src/mage/abilities/effects/ContinuousRuleModifiyingEffect.java b/Mage/src/mage/abilities/effects/ContinuousRuleModifiyingEffect.java new file mode 100644 index 0000000000..2e86f239ee --- /dev/null +++ b/Mage/src/mage/abilities/effects/ContinuousRuleModifiyingEffect.java @@ -0,0 +1,59 @@ +/* +* 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.effects; + +import mage.abilities.Ability; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author LevelX2 + */ +public interface ContinuousRuleModifiyingEffect extends ContinuousEffect { + + /** + * + * @param event the event to check if it may happen + * @param source the ability of the effect + * @param checkPlayableMode is the call for checking playables or to prevent a real event + * @param game the game + * @return + */ + boolean applies(GameEvent event, Ability source, boolean checkPlayableMode, Game game); + + /** + * Returns a message text that informs the player why he can't do something. + * + * @param source the ability of the effect + * @param game the game + * @return + */ + String getInfoMessage(Ability source, Game game); +} diff --git a/Mage/src/mage/abilities/effects/ContinuousRuleModifiyingEffectImpl.java b/Mage/src/mage/abilities/effects/ContinuousRuleModifiyingEffectImpl.java new file mode 100644 index 0000000000..c0f5477b56 --- /dev/null +++ b/Mage/src/mage/abilities/effects/ContinuousRuleModifiyingEffectImpl.java @@ -0,0 +1,86 @@ +/* +* 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.effects; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.constants.Duration; +import mage.constants.EffectType; +import mage.constants.Outcome; +import mage.game.Game; + +/** + * + * @author LevelX2 + */ +public abstract class ContinuousRuleModifiyingEffectImpl extends ContinuousEffectImpl implements ContinuousRuleModifiyingEffect { + + protected final String infoMessage; + + // 613.10 + // Some continuous effects affect game rules rather than objects. For example, effects may modify + // a playerÂ’s maximum hand size, or say that a creature must attack this turn if able. These effects + // are applied after all other continuous effects have been applied. Continuous effects that affect + // the costs of spells or abilities are applied according to the order specified in rule 601.2e. + // All other such effects are applied in timestamp order. See also the rules for timestamp order + // and dependency (rules 613.6 and 613.7). + + // Some of this rule modifying effects are implemented as normal CONTINUOUS effects using the Layer.RulesEffects. + // But if the rule change can be implemented simply by preventing an event from happening, CONTINUOUS_RULE_MODIFICATION effects can be used. + // They work technical like a replacement effect that replaces the event completely. + // But player isn't asked to choose order of effects if multiple are applied to the same event. + + public ContinuousRuleModifiyingEffectImpl(Duration duration, Outcome outcome) { + super(duration, outcome); + this.effectType = EffectType.CONTINUOUS_RULE_MODIFICATION; + this.infoMessage = null; + } + + public ContinuousRuleModifiyingEffectImpl(final ContinuousRuleModifiyingEffectImpl effect) { + super(effect); + this.infoMessage = effect.infoMessage; + } + + @Override + public String getInfoMessage(Ability source, Game game) { + if (infoMessage == null) { + String message = null; + MageObject object = game.getObject(source.getSourceId()); + if (object != null) { + message = source.getRule(object.getLogName()); + } else { + message = source.getRule(); + } + return message; + } else { + return infoMessage; + } + } + +} diff --git a/Mage/src/mage/constants/EffectType.java b/Mage/src/mage/constants/EffectType.java index bfe01d4ec0..327646c4bb 100644 --- a/Mage/src/mage/constants/EffectType.java +++ b/Mage/src/mage/constants/EffectType.java @@ -8,6 +8,7 @@ public enum EffectType { ONESHOT("One Shot Effect"), CONTINUOUS("Continuous Effect"), + CONTINUOUS_RULE_MODIFICATION("Layered rule modification"), REPLACEMENT("Replacement Effect"), PREVENTION("Prevention Effect"), REDIRECTION("Redirection Effect"), @@ -18,7 +19,7 @@ public enum EffectType { COSTMODIFICATION("Cost Modification Effect"), SPLICE("Splice Card Effect"); - private String text; + private final String text; EffectType(String text) { this.text = text;