diff --git a/Mage/src/mage/Mana.java b/Mage/src/mage/Mana.java index b9b3e04c8fa..e5dace32df1 100644 --- a/Mage/src/mage/Mana.java +++ b/Mage/src/mage/Mana.java @@ -259,7 +259,7 @@ public class Mana implements Comparable, Serializable, Copyable { @Override public String toString() { - StringBuilder sbMana = threadLocalBuilder.get(); + StringBuilder sbMana = new StringBuilder(); if (colorless > 0) { sbMana.append("{").append(Integer.toString(colorless)).append("}"); } diff --git a/Mage/src/mage/abilities/Ability.java b/Mage/src/mage/abilities/Ability.java index 81393981ce2..fedff00e85d 100644 --- a/Mage/src/mage/abilities/Ability.java +++ b/Mage/src/mage/abilities/Ability.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * 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; import java.io.Serializable; @@ -54,48 +53,54 @@ import mage.target.Targets; import mage.watchers.Watcher; /** - * Practically everything in the game is started from an Ability. This - * interface describes what an Ability is composed of at the highest level. + * Practically everything in the game is started from an Ability. This interface + * describes what an Ability is composed of at the highest level. */ public interface Ability extends Controllable, Serializable { /** * Gets the globally unique id of the ability contained within the game. - * - * @return A {@link java.util.UUID} which the game will use to store and retrieve - * the exact instance of this ability. + * + * @return A {@link java.util.UUID} which the game will use to store and + * retrieve the exact instance of this ability. */ @Override UUID getId(); /** * Assigns a new {@link java.util.UUID} - * - * @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility, mage.game.Game) - * @see mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility) - * @see mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility) + * + * @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility, + * mage.game.Game) + * @see + * mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility) + * @see + * mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility) */ void newId(); /** * Assigns a new {@link java.util.UUID} - * - * @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility, mage.game.Game) - * @see mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility) - * @see mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility) + * + * @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility, + * mage.game.Game) + * @see + * mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility) + * @see + * mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility) */ void newOriginalId(); /** * Gets the {@link AbilityType} of this ability. - * + * * @return The {@link AbilityType type} of this ability. */ AbilityType getAbilityType(); /** * Gets the id of the player in control of this ability. - * + * * @return The {@link java.util.UUID} of the controlling player. */ @Override @@ -103,28 +108,29 @@ public interface Ability extends Controllable, Serializable { /** * Sets the id of the controller of this ability. - * + * * @param controllerId The {@link java.util.UUID} of the controller. */ void setControllerId(UUID controllerId); /** * Gets the id of the object which put this ability in motion. - * - * @return The {@link java.util.UUID} of the object this ability is associated with. + * + * @return The {@link java.util.UUID} of the object this ability is + * associated with. */ UUID getSourceId(); /** * Sets the id of the object which this ability orignates from. - * + * * @param sourceID {@link java.util.UUID} the source id to set. */ void setSourceId(UUID sourceID); /** * Gets all {@link Costs} associated with this ability. - * + * * @return All {@link Costs} associated with this ability. */ Costs getCosts(); @@ -132,25 +138,25 @@ public interface Ability extends Controllable, Serializable { /** * Adds a {@link Cost} to this ability that must be paid before this ability * is activated. - * + * * @param cost The {@link Cost} to add. */ void addCost(Cost cost); /** - * Gets all {@link ManaCosts} associated with this ability. These returned + * Gets all {@link ManaCosts} associated with this ability. These returned * costs should never be modified as they represent the base costs before * any modifications. - * + * * @return All {@link ManaCosts} that must be paid. */ ManaCosts getManaCosts(); /** * Gets all the {@link ManaCosts} that must be paid before activating this - * ability. These costs should be modified by any modification effects currently - * present within the game state. - * + * ability. These costs should be modified by any modification effects + * currently present within the game state. + * * @return All {@link ManaCosts} that must be paid. */ ManaCosts getManaCostsToPay(); @@ -158,52 +164,61 @@ public interface Ability extends Controllable, Serializable { /** * Adds a {@link ManaCost} to this ability that must be paid before this * ability is activated. - * + * * @param cost The {@link ManaCost} to add. */ void addManaCost(ManaCost cost); /** * Gets all {@link AlternativeCost} associated with this ability. - * - * @return All {@link AlternativeCost}'s that can be paid instead of the {@link ManaCosts} + * + * @return All {@link AlternativeCost}'s that can be paid instead of the + * {@link ManaCosts} */ List getAlternativeCosts(); /** * Adds an {@link AlternativeCost} this ability that may be paid instead of * any other cost. - * + * * @param cost The {@link AlternativeCost} to add. */ void addAlternativeCost(AlternativeCost cost); /** * TODO Method is unused, keep it around? - * - * Gets all costs that are optional to this ability. These costs can be paid + * + * Gets all costs that are optional to this ability. These costs can be paid * in addition to other costs to have other effects put into place. - * + * * @return All {@link Costs} that can be paid above and beyond other costs. */ Costs getOptionalCosts(); /** * Adds a {@link Cost} that is optional to this ability. - * + * * @param cost The {@link Cost} to add to the optional costs. */ void addOptionalCost(Cost cost); /** - * Retrieves the effects that are put into the place by the resolution of this - * ability. - * + * Retrieves the effects that are put into the place by the resolution of + * this ability. + * * @return All {@link Effects} that will be put into place by the resolution * of this ability. */ Effects getEffects(); + /** + * Retrieves all effects of an ability. That means effects from all modes + * event those modes that are not seleced (yet). + * + * @return All {@link Effects} of this ability. + */ + Effects getAllEffects(); + /** * Retrieves the effects of the specified {@link EffectType type} that are * put into place by the resolution of this ability. @@ -216,26 +231,28 @@ public interface Ability extends Controllable, Serializable { /** * Adds an effect to this ability. - * + * * @param effect The {@link Effect} to add. */ void addEffect(Effect effect); /** - * Retrieves all targets that must be satisfied before this ability is + * Retrieves all targets that must be satisfied before this ability is put + * onto the stack. + * + * @return All {@link Targets} that must be satisfied before this ability is * put onto the stack. - * - * @return All {@link Targets} that must be satisfied before this ability is put onto - * the stack. */ Targets getTargets(); /** - * Retrieves the {@link Target} located at the 0th index in the {@link Targets}. - * A call to the method is equivalent to {@link #getTargets()}.get(0).getFirstTarget(). - * - * @return The {@link java.util.UUID} of the first target within the targets list. - * + * Retrieves the {@link Target} located at the 0th index in the + * {@link Targets}. A call to the method is equivalent to + * {@link #getTargets()}.get(0).getFirstTarget(). + * + * @return The {@link java.util.UUID} of the first target within the targets + * list. + * * @see mage.target.Target */ UUID getFirstTarget(); @@ -243,53 +260,54 @@ public interface Ability extends Controllable, Serializable { /** * Adds a target to this ability that must be satisfied before this ability * is put onto the stack. - * + * * @param target The {@link Target} to add. */ void addTarget(Target target); /** * Choices - * - * @return + * + * @return */ Choices getChoices(); /** * TODO: Javadoc me - * - * @param choice + * + * @param choice */ void addChoice(Choice choice); /** * Retrieves the {@link Zone} that this ability is active within. - * - * @return + * + * @return */ Zone getZone(); /** * Retrieves whether or not this abilities activation will use the stack. - * - * @return + * + * @return */ boolean isUsesStack(); /** * Retrieves a human readable string representing what the ability states it - * accomplishes. This call is equivalent to {@link #getRule(boolean) getRule(false)} - * + * accomplishes. This call is equivalent to + * {@link #getRule(boolean) getRule(false)} + * * @return A human readable string representing what the ability states it * accomplishes */ String getRule(); /** - * Retrieves a human readable string including any costs associated with this - * ability if the all parameter is true, and just the abilities rule text if - * the all parameter is false. - * + * Retrieves a human readable string including any costs associated with + * this ability if the all parameter is true, and just the abilities rule + * text if the all parameter is false. + * * @param all True if costs are desired in the output, false otherwise. * @return */ @@ -297,51 +315,60 @@ public interface Ability extends Controllable, Serializable { /** * Retrieves the rule associated with the given source. - * + * * @param source - * @return + * @return */ String getRule(String source); /** * Activates this ability prompting the controller to pay any mandatory * {@link Costs} or {@link AlternativeCost} associated with this ability. - * - * @param game A reference the {@link Game} for which this ability should be activated within. + * + * @param game A reference the {@link Game} for which this ability should be + * activated within. * @param noMana Whether or not {@link ManaCosts} have to be paid. * @return True if this ability was successfully activated. - * - * @see mage.players.PlayerImpl#cast(mage.abilities.SpellAbility, mage.game.Game, boolean) - * @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility, mage.game.Game) - * @see mage.players.PlayerImpl#triggerAbility(mage.abilities.TriggeredAbility, mage.game.Game) + * + * @see mage.players.PlayerImpl#cast(mage.abilities.SpellAbility, + * mage.game.Game, boolean) + * @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility, + * mage.game.Game) + * @see + * mage.players.PlayerImpl#triggerAbility(mage.abilities.TriggeredAbility, + * mage.game.Game) */ boolean activate(Game game, boolean noMana); boolean isActivated(); - + /** - * Resolves this ability and puts any effects it produces into play. This - * method should only be called if the {@link #activate(mage.game.Game, boolean)} - * method returned true. - * + * Resolves this ability and puts any effects it produces into play. This + * method should only be called if the + * {@link #activate(mage.game.Game, boolean)} method returned true. + * * @param game The {@link Game} for which this ability resolves within. * @return Whether or not this ability successfully resolved. - * - * @see mage.players.PlayerImpl#playManaAbility(mage.abilities.mana.ManaAbility, mage.game.Game) - * @see mage.players.PlayerImpl#specialAction(mage.abilities.SpecialAction, mage.game.Game) + * + * @see + * mage.players.PlayerImpl#playManaAbility(mage.abilities.mana.ManaAbility, + * mage.game.Game) + * @see mage.players.PlayerImpl#specialAction(mage.abilities.SpecialAction, + * mage.game.Game) */ boolean resolve(Game game); /** * Used to reset the state of this ability. - * - * @param game + * + * @param game */ void reset(Game game); /** - * Overridden by triggered abilities with intervening if clauses - rule 20110715 - 603.4 - * + * Overridden by triggered abilities with intervening if clauses - rule + * 20110715 - 603.4 + * * @param game * @return Whether or not the intervening if clause is satisfied */ @@ -349,7 +376,7 @@ public interface Ability extends Controllable, Serializable { /** * Creates a fresh copy of this ability. - * + * * @return A new copy of this ability. */ Ability copy(); @@ -364,43 +391,47 @@ public interface Ability extends Controllable, Serializable { /** * Gets the list of sub-abilities associated with this ability. - * @return + * + * @return */ List getSubAbilities(); /** * Adds a sub-ability to this ability. - * + * * @param ability The {@link Ability} to add. */ void addSubAbility(Ability ability); List getWatchers(); + void addWatcher(Watcher watcher); - + /** * Returns true if this abilities source is in the zone for the ability - * + * * @param game * @param source * @param event - * @return + * @return */ boolean isInUseableZone(Game game, MageObject source, GameEvent event); /** - * Returns true if the source object has currently the ability - * (e.g. The object can have lost all or some abilities for some time (e.g. Turn to Frog) - * + * Returns true if the source object has currently the ability (e.g. The + * object can have lost all or some abilities for some time (e.g. Turn to + * Frog) + * * @param game * @param source * @param event - * @return + * @return */ boolean hasSourceObjectAbility(Game game, MageObject source, GameEvent event); /** - * Returns true if this ability has to be shown as topmost of all the rules of the object + * Returns true if this ability has to be shown as topmost of all the rules + * of the object * * @return */ @@ -415,10 +446,9 @@ public interface Ability extends Controllable, Serializable { */ void setRuleAtTheTop(boolean ruleAtTheTop); - /** - * Returns true if this ability has to work also with face down object - * (set to not visible normally). + * Returns true if this ability has to work also with face down object (set + * to not visible normally). * * @return */ @@ -439,20 +469,20 @@ public interface Ability extends Controllable, Serializable { * @return */ boolean getRuleVisible(); - /** * Sets the value for the ruleVisible attribute * - * true = rule will be shown for the card / permanent - * false = rule won't be shown + * true = rule will be shown for the card / permanent false = rule won't be + * shown * * @param ruleVisible */ void setRuleVisible(boolean ruleVisible); /** - * Returns true if the additional costs of the abilitiy should be visible on the tooltip text + * Returns true if the additional costs of the abilitiy should be visible on + * the tooltip text * * @return */ @@ -461,35 +491,34 @@ public interface Ability extends Controllable, Serializable { /** * Sets the value for the additional costs rule attribute * - * true = rule will be shown for the card / permanent - * false = rule won't be shown + * true = rule will be shown for the card / permanent false = rule won't be + * shown * * @param ruleAdditionalCostsVisible */ void setAdditionalCostsRuleVisible(boolean ruleAdditionalCostsVisible); - /** * Get the originalId of the ability - * + * * @return originalId */ UUID getOriginalId(); - + /** - * Sets the ability word for the given ability. - * An ability word is a word that, in essence, groups, and reminds players of, cards - * that have a common functionality and does not imply any particular rules. - * + * Sets the ability word for the given ability. An ability word is a word + * that, in essence, groups, and reminds players of, cards that have a + * common functionality and does not imply any particular rules. + * * --- Not usable yet for rule text generation of triggered abilities --- - * - * @param abilityWord + * + * @param abilityWord */ void setAbilityWord(AbilityWord abilityWord); /** - * Creates the message about the ability casting/triggering/activating to post in the game log - * before the ability resolves. + * Creates the message about the ability casting/triggering/activating to + * post in the game log before the ability resolves. * * @param game * @return @@ -497,46 +526,46 @@ public interface Ability extends Controllable, Serializable { String getGameLogMessage(Game game); /** - * Used to deactivate cost modification logic of ability activation for some special handling - * (e.g. FlashbackAbility gets cost modifiaction twice because of how it's handled now) + * Used to deactivate cost modification logic of ability activation for some + * special handling (e.g. FlashbackAbility gets cost modifiaction twice + * because of how it's handled now) * * @param active execute no cost modification */ void setCostModificationActive(boolean active); boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game); - + /** - * Sets the object that actually existed while a ability triggerd or - * an ability was activated. - * - * @param mageObject - * @param game + * Sets the object that actually existed while a ability triggerd or an + * ability was activated. + * + * @param mageObject + * @param game */ void setSourceObject(MageObject mageObject, Game game); - + /** - * Returns the object that actually existed while a ability triggerd or - * an ability was activated. - * If not set yet, the current object will be retrieved from the game. - * + * Returns the object that actually existed while a ability triggerd or an + * ability was activated. If not set yet, the current object will be + * retrieved from the game. + * * @param game - * @return + * @return */ MageObject getSourceObject(Game game); int getSourceObjectZoneChangeCounter(); - + /** - * Returns the object that actually existed while a ability triggerd or - * an ability was activated only if it has not changed zone meanwhile. - * If not set yet, the current object will be retrieved from the game. - * + * Returns the object that actually existed while a ability triggerd or an + * ability was activated only if it has not changed zone meanwhile. If not + * set yet, the current object will be retrieved from the game. + * * @param game - * @return + * @return */ - MageObject getSourceObjectIfItStillExists(Game game); - + String getTargetDescription(Targets targets, Game game); } diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 50746636057..659d4987fb1 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -445,25 +445,36 @@ public abstract class AbilityImpl implements Ability { 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; + Abilities abilities = null; + if (sourceObject instanceof Card) { + abilities = ((Card) sourceObject).getAbilities(game); + } else { + sourceObject.getAbilities(); + } + if (abilities != null) { + for (Ability ability : abilities) { + // 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); + if (ability instanceof OptionalAdditionalSourceCosts) { + ((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game); + } } } // controller specific alternate spell costs if (!noMana && !alternativeCostisUsed) { - if (this.getAbilityType().equals(AbilityType.SPELL)) { + if (this.getAbilityType().equals(AbilityType.SPELL) + // 117.9a Only one alternative cost can be applied to any one spell as it’s being cast. + // So an alternate spell ability can't be paid with Omniscience + && !((SpellAbility) this).getSpellAbilityType().equals(SpellAbilityType.BASE_ALTERNATE)) { for (AlternativeSourceCosts alternativeSourceCosts : controller.getAlternativeSourceCosts()) { if (alternativeSourceCosts.isAvailable(this, game)) { if (alternativeSourceCosts.askToActivateAlternativeCosts(this, game)) { @@ -673,6 +684,15 @@ public abstract class AbilityImpl implements Ability { return modes.getMode().getEffects(); } + @Override + public Effects getAllEffects() { + Effects allEffects = new Effects(); + for (Mode mode : getModes().values()) { + allEffects.addAll(mode.getEffects()); + } + return allEffects; + } + @Override public Effects getEffects(Game game, EffectType effectType) { Effects typedEffects = new Effects(); @@ -935,7 +955,10 @@ public abstract class AbilityImpl implements Ability { // for singleton abilities like Flying we can't rely on abilities' source because it's only once in continuous effects // so will use the sourceId of the object itself that came as a parameter if it is not null if (object == null) { - object = game.getObject(getSourceId()); + object = game.getPermanentEntering(getSourceId()); + if (object == null) { + object = game.getObject(getSourceId()); + } } if (object != null && !object.getAbilities().contains(this)) { if (object instanceof Permanent) { diff --git a/Mage/src/mage/abilities/SpellAbility.java b/Mage/src/mage/abilities/SpellAbility.java index 1dedf4b81e7..5106b8dc61c 100644 --- a/Mage/src/mage/abilities/SpellAbility.java +++ b/Mage/src/mage/abilities/SpellAbility.java @@ -99,10 +99,6 @@ public class SpellAbility extends ActivatedAbilityImpl { && !controllerId.equals(playerId)) { return false; } - // Check if spell has no costs (not {0} mana costs), than it's not castable. E.g. for spells like Living End, that only can be cast by Suspend Ability. - 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( diff --git a/Mage/src/mage/abilities/TriggeredAbilities.java b/Mage/src/mage/abilities/TriggeredAbilities.java index 599e44068c0..2a9d07b1c99 100644 --- a/Mage/src/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/mage/abilities/TriggeredAbilities.java @@ -95,7 +95,7 @@ public class TriggeredAbilities extends ConcurrentHashMap cards = new LinkedList<>(exile); - for (UUID cardId : cards) { - Card card = game.getCard(cardId); - if (card != null) { - Player owner = game.getPlayer(card.getOwnerId()); - if (owner != null && owner.isInGame()) { - owner.putOntoBattlefieldWithInfo(card, game, Zone.EXILED, source.getSourceId()); - } - } - } - exile.clear(); + controller.moveCards(new LinkedHashSet<>(exile.getCards(game)), Zone.BATTLEFIELD, source, game, false, false, true, null); } return true; } diff --git a/Mage/src/mage/abilities/condition/common/CastFromHandCondition.java b/Mage/src/mage/abilities/condition/common/CastFromHandCondition.java index 27811130bdc..1abc7306f5f 100644 --- a/Mage/src/mage/abilities/condition/common/CastFromHandCondition.java +++ b/Mage/src/mage/abilities/condition/common/CastFromHandCondition.java @@ -12,10 +12,14 @@ import mage.watchers.Watcher; * @author Loki */ public class CastFromHandCondition implements Condition { + @Override public boolean apply(Game game, Ability source) { - Permanent p = game.getPermanent(source.getSourceId()); - if (p != null) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + permanent = game.getPermanentEntering(source.getSourceId()); + } + if (permanent != null) { Watcher watcher = game.getState().getWatchers().get("CastFromHand", source.getSourceId()); if (watcher != null && watcher.conditionMet()) { return true; @@ -29,5 +33,4 @@ public class CastFromHandCondition implements Condition { return "you cast it from your hand"; } - } diff --git a/Mage/src/mage/abilities/condition/common/EnchantedTargetCondition.java b/Mage/src/mage/abilities/condition/common/EnchantedTargetCondition.java index ccdc65b27aa..98cd2632909 100644 --- a/Mage/src/mage/abilities/condition/common/EnchantedTargetCondition.java +++ b/Mage/src/mage/abilities/condition/common/EnchantedTargetCondition.java @@ -1,8 +1,6 @@ - package mage.abilities.condition.common; import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.constants.CardType; @@ -15,8 +13,8 @@ import mage.target.Target; * @author Jeff */ public class EnchantedTargetCondition implements Condition { - - private static EnchantedTargetCondition fInstance = new EnchantedTargetCondition(); + + private static final EnchantedTargetCondition fInstance = new EnchantedTargetCondition(); public static Condition getInstance() { return fInstance; diff --git a/Mage/src/mage/abilities/condition/common/MorbidCondition.java b/Mage/src/mage/abilities/condition/common/MorbidCondition.java index 04de2a9aec9..22a4bc7a74c 100644 --- a/Mage/src/mage/abilities/condition/common/MorbidCondition.java +++ b/Mage/src/mage/abilities/condition/common/MorbidCondition.java @@ -49,4 +49,9 @@ public class MorbidCondition implements Condition { return watcher.conditionMet(); } + @Override + public String toString() { + return "if a creature died this turn"; + } + } diff --git a/Mage/src/mage/abilities/condition/common/NotMyTurnCondition.java b/Mage/src/mage/abilities/condition/common/NotMyTurnCondition.java index c7940eeed00..6a1755e773c 100644 --- a/Mage/src/mage/abilities/condition/common/NotMyTurnCondition.java +++ b/Mage/src/mage/abilities/condition/common/NotMyTurnCondition.java @@ -33,7 +33,7 @@ import mage.abilities.condition.Condition; import mage.game.Game; public class NotMyTurnCondition implements Condition { - + private static final NotMyTurnCondition fInstance = new NotMyTurnCondition(); public static Condition getInstance() { @@ -48,4 +48,9 @@ public class NotMyTurnCondition implements Condition { } return false; } + + @Override + public String toString() { + return "if it's not your turn"; + } } diff --git a/Mage/src/mage/abilities/condition/common/OpponentControllsMoreCondition.java b/Mage/src/mage/abilities/condition/common/OpponentControlsMoreCondition.java similarity index 92% rename from Mage/src/mage/abilities/condition/common/OpponentControllsMoreCondition.java rename to Mage/src/mage/abilities/condition/common/OpponentControlsMoreCondition.java index 8a8183af340..f4a0ccc106e 100644 --- a/Mage/src/mage/abilities/condition/common/OpponentControllsMoreCondition.java +++ b/Mage/src/mage/abilities/condition/common/OpponentControlsMoreCondition.java @@ -39,11 +39,11 @@ import mage.game.Game; * @author LevelX2 */ -public class OpponentControllsMoreCondition implements Condition { +public class OpponentControlsMoreCondition implements Condition { private final FilterPermanent filter; - public OpponentControllsMoreCondition(FilterPermanent filter) { + public OpponentControlsMoreCondition(FilterPermanent filter) { this.filter = filter; } diff --git a/Mage/src/mage/abilities/costs/AlternativeCost2Impl.java b/Mage/src/mage/abilities/costs/AlternativeCost2Impl.java index b6b73034a93..b69b73b278c 100644 --- a/Mage/src/mage/abilities/costs/AlternativeCost2Impl.java +++ b/Mage/src/mage/abilities/costs/AlternativeCost2Impl.java @@ -25,20 +25,18 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.costs; import mage.game.Game; /** * Alternative costs - * + * * @author LevelX2 - * + * * @param */ - -public class AlternativeCost2Impl > extends CostsImpl implements AlternativeCost2 { +public class AlternativeCost2Impl> extends CostsImpl implements AlternativeCost2 { protected String name; protected String reminderText; @@ -72,6 +70,7 @@ public class AlternativeCost2Impl > extends Co public String getName() { return this.name; } + /** * Returns the complete text for the addional cost or if onlyCost is true * only the pure text for the included native cost @@ -84,7 +83,7 @@ public class AlternativeCost2Impl > extends Co if (onlyCost) { return getText(); } else { - return new StringBuffer(name != null ? name: "").append(delimiter != null ? delimiter: "").append(getText()).toString(); + return (name != null ? name : "") + (delimiter != null ? delimiter : "") + getText(); } } @@ -103,20 +102,20 @@ public class AlternativeCost2Impl > extends Co } /** - * Returns a text suffix for the game log, that can be added to - * the cast message. + * Returns a text suffix for the game log, that can be added to the cast + * message. * - * @param position - if there are multiple costs, it's the postion the cost is set (starting with 0) + * @param position - if there are multiple costs, it's the postion the cost + * is set (starting with 0) * @return */ @Override public String getCastSuffixMessage(int position) { - StringBuilder sb = new StringBuilder(position > 0 ? " and ":"").append(" with "); + StringBuilder sb = new StringBuilder(position > 0 ? " and " : "").append(" with "); sb.append(name); - return sb.toString(); + return sb.toString(); } - /** * If the player intends to pay the cost, the cost will be activated * @@ -124,7 +123,7 @@ public class AlternativeCost2Impl > extends Co @Override public void activate() { activated = true; - }; + } /** * Reset the activate and count information @@ -142,9 +141,11 @@ public class AlternativeCost2Impl > extends Co * @return */ @Override - public boolean isActivated(Game game){ + public boolean isActivated(Game game) { return activated; - }; + } + + ; @Override public AlternativeCost2Impl copy() { diff --git a/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java b/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java index 5fb7fd20f90..be4893b1b45 100644 --- a/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java +++ b/Mage/src/mage/abilities/costs/AlternativeCostSourceAbility.java @@ -29,6 +29,7 @@ package mage.abilities.costs; import java.util.Iterator; import mage.abilities.Ability; +import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.mana.ManaCost; @@ -39,6 +40,7 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; +import mage.util.CardUtil; /** * @@ -145,28 +147,39 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter } Player player = game.getPlayer(ability.getControllerId()); if (player != null) { - Costs alternativeCosts; + Costs alternativeCostsToCheck; if (dynamicCost != null) { - alternativeCosts = new CostsImpl<>(); - alternativeCosts.add(convertToAlternativeCost(dynamicCost.getCost(ability, game))); + alternativeCostsToCheck = new CostsImpl<>(); + alternativeCostsToCheck.add(convertToAlternativeCost(dynamicCost.getCost(ability, game))); } else { - alternativeCosts = this.alternateCosts; + alternativeCostsToCheck = this.alternateCosts; } String costChoiceText; if (dynamicCost != null) { costChoiceText = dynamicCost.getText(ability, game); } else { - costChoiceText = alternativeCosts.isEmpty() ? "Cast without paying its mana cost?" : "Pay alternative costs? (" + alternativeCosts.getText() + ")"; + costChoiceText = alternativeCostsToCheck.isEmpty() ? "Cast without paying its mana cost?" : "Pay alternative costs? (" + alternativeCostsToCheck.getText() + ")"; } - if (alternativeCosts.canPay(ability, ability.getSourceId(), ability.getControllerId(), game) + if (alternativeCostsToCheck.canPay(ability, ability.getSourceId(), ability.getControllerId(), game) && player.chooseUse(Outcome.Benefit, costChoiceText, this, game)) { - ability.getManaCostsToPay().clear(); + if (ability instanceof SpellAbility) { + for (Iterator iterator = ability.getManaCostsToPay().iterator(); iterator.hasNext();) { + ManaCost manaCost = iterator.next(); + if (manaCost instanceof VariableCost) { + iterator.remove(); + } + } + CardUtil.reduceCost((SpellAbility) ability, ability.getManaCosts()); + + } else { + ability.getManaCostsToPay().clear(); + } if (!onlyMana) { ability.getCosts().clear(); } - for (Cost cost : alternativeCosts) { + for (Cost cost : alternativeCostsToCheck) { AlternativeCost2 alternateCost = (AlternativeCost2) cost; alternateCost.activate(); for (Iterator it = ((Costs) alternateCost).iterator(); it.hasNext();) { @@ -190,14 +203,14 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter @Override public boolean isActivated(Ability source, Game game) { - Costs alternativeCosts; + Costs alternativeCostsToCheck; if (dynamicCost != null) { - alternativeCosts = new CostsImpl<>(); - alternativeCosts.add(convertToAlternativeCost(dynamicCost.getCost(source, game))); + alternativeCostsToCheck = new CostsImpl<>(); + alternativeCostsToCheck.add(convertToAlternativeCost(dynamicCost.getCost(source, game))); } else { - alternativeCosts = this.alternateCosts; + alternativeCostsToCheck = this.alternateCosts; } - for (AlternativeCost2 cost : alternativeCosts) { + for (AlternativeCost2 cost : alternativeCostsToCheck) { if (cost.isActivated(game)) { return true; } diff --git a/Mage/src/mage/abilities/costs/CompositeCost.java b/Mage/src/mage/abilities/costs/CompositeCost.java index f76693fb185..1d497d43793 100644 --- a/Mage/src/mage/abilities/costs/CompositeCost.java +++ b/Mage/src/mage/abilities/costs/CompositeCost.java @@ -1,14 +1,14 @@ package mage.abilities.costs; +import java.util.UUID; import mage.abilities.Ability; import mage.game.Game; import mage.target.Targets; -import java.util.UUID; - public class CompositeCost implements Cost { - private Cost firstCost; - private Cost secondCost; + + private final Cost firstCost; + private final Cost secondCost; private String description; public CompositeCost(Cost firstCost, Cost secondCost, String description) { @@ -28,6 +28,11 @@ public class CompositeCost implements Cost { throw new RuntimeException("Not supported method"); } + @Override + public void setText(String text) { + this.description = text; + } + @Override public String getText() { return description; diff --git a/Mage/src/mage/abilities/costs/Cost.java b/Mage/src/mage/abilities/costs/Cost.java index cd8696cd138..885ff8376fd 100644 --- a/Mage/src/mage/abilities/costs/Cost.java +++ b/Mage/src/mage/abilities/costs/Cost.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * 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.costs; import java.io.Serializable; @@ -37,12 +36,21 @@ import mage.target.Targets; public interface Cost extends Serializable { UUID getId(); + String getText(); + + void setText(String text); + boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game); + boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana); + boolean isPaid(); + void clearPaid(); + void setPaid(); + Targets getTargets(); Cost copy(); diff --git a/Mage/src/mage/abilities/costs/CostImpl.java b/Mage/src/mage/abilities/costs/CostImpl.java index e423f30fed4..57f6c5a7471 100644 --- a/Mage/src/mage/abilities/costs/CostImpl.java +++ b/Mage/src/mage/abilities/costs/CostImpl.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * 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.costs; import java.util.UUID; @@ -57,6 +56,7 @@ public abstract class CostImpl implements Cost { return text; } + @Override public void setText(String text) { this.text = text; } diff --git a/Mage/src/mage/abilities/costs/CostsImpl.java b/Mage/src/mage/abilities/costs/CostsImpl.java index a2624f3f29d..df64087a373 100644 --- a/Mage/src/mage/abilities/costs/CostsImpl.java +++ b/Mage/src/mage/abilities/costs/CostsImpl.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * 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.costs; import java.util.ArrayList; @@ -44,12 +43,16 @@ import mage.target.Targets; */ public class CostsImpl extends ArrayList implements Costs { - public CostsImpl() {} + protected String text = null; + + public CostsImpl() { + } public CostsImpl(final CostsImpl costs) { - for (Cost cost: costs) { - this.add((T)cost.copy()); + for (Cost cost : costs) { + this.add((T) cost.copy()); } + this.text = costs.text; } @Override @@ -57,20 +60,28 @@ public class CostsImpl extends ArrayList implements Costs throw new RuntimeException("Not supported method"); } + @Override + public void setText(String text) { + this.text = text; + } + @Override public String getText() { + if (text != null) { + return text; + } if (this.size() == 0) { return ""; } StringBuilder sbText = new StringBuilder(); - for (T cost: this) { - String text = cost.getText(); - if (text != null && !text.isEmpty()) { - sbText.append(Character.toUpperCase(text.charAt(0))).append(text.substring(1)).append(", "); + for (T cost : this) { + String textCost = cost.getText(); + if (textCost != null && !textCost.isEmpty()) { + sbText.append(Character.toUpperCase(textCost.charAt(0))).append(textCost.substring(1)).append(", "); } } - if (sbText.length() > 1){ + if (sbText.length() > 1) { sbText.setLength(sbText.length() - 2); } return sbText.toString(); @@ -78,7 +89,7 @@ public class CostsImpl extends ArrayList implements Costs @Override public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { - for (T cost: this) { + for (T cost : this) { if (!cost.canPay(ability, sourceId, controllerId, game)) { return false; } @@ -101,7 +112,7 @@ public class CostsImpl extends ArrayList implements Costs @Override public boolean isPaid() { - for (T cost: this) { + for (T cost : this) { if (!(cost instanceof VariableManaCost) && !cost.isPaid()) { return false; } @@ -111,14 +122,14 @@ public class CostsImpl extends ArrayList implements Costs @Override public void clearPaid() { - for (T cost: this) { + for (T cost : this) { cost.clearPaid(); } } @Override public void setPaid() { - for (T cost: this) { + for (T cost : this) { cost.setPaid(); } } @@ -126,7 +137,7 @@ public class CostsImpl extends ArrayList implements Costs @Override public Costs getUnpaid() { Costs unpaid = new CostsImpl<>(); - for (T cost: this) { + for (T cost : this) { if (!cost.isPaid()) { unpaid.add(cost); } @@ -140,17 +151,17 @@ public class CostsImpl extends ArrayList implements Costs return unpaid.get(0); } return null; - } + } @Override public List getVariableCosts() { List variableCosts = new ArrayList<>(); - for (T cost: this) { + for (T cost : this) { if (cost instanceof VariableCost) { variableCosts.add((VariableCost) cost); } if (cost instanceof ManaCosts) { - variableCosts.addAll(((ManaCosts)cost).getVariableCosts()); + variableCosts.addAll(((ManaCosts) cost).getVariableCosts()); } } return variableCosts; @@ -159,7 +170,7 @@ public class CostsImpl extends ArrayList implements Costs @Override public Targets getTargets() { Targets targets = new Targets(); - for (T cost: this) { + for (T cost : this) { if (cost.getTargets() != null) { targets.addAll(cost.getTargets()); } diff --git a/Mage/src/mage/abilities/costs/OptionalAdditionalCostImpl.java b/Mage/src/mage/abilities/costs/OptionalAdditionalCostImpl.java index 489d8690ea7..3646a9c3ec0 100644 --- a/Mage/src/mage/abilities/costs/OptionalAdditionalCostImpl.java +++ b/Mage/src/mage/abilities/costs/OptionalAdditionalCostImpl.java @@ -50,7 +50,7 @@ public class OptionalAdditionalCostImpl ex this.activated = false; this.name = name; this.delimiter = delimiter; - this.reminderText = new StringBuilder("").append(reminderText).append("").toString(); + this.reminderText = "(" + reminderText + ")"; this.activatedCounter = 0; this.add((Cost) cost); } @@ -81,7 +81,7 @@ public class OptionalAdditionalCostImpl ex if (onlyCost) { return getText(); } else { - return new StringBuffer(name).append(delimiter).append(getText()).toString(); + return name + delimiter + getText(); } } diff --git a/Mage/src/mage/abilities/costs/OrCost.java b/Mage/src/mage/abilities/costs/OrCost.java index 2c5a7740e7c..0707741244f 100644 --- a/Mage/src/mage/abilities/costs/OrCost.java +++ b/Mage/src/mage/abilities/costs/OrCost.java @@ -37,8 +37,8 @@ import mage.target.Targets; public class OrCost implements Cost { - private Cost firstCost; - private Cost secondCost; + private final Cost firstCost; + private final Cost secondCost; private String description; // which cost was slected to pay private Cost selectedCost; @@ -61,6 +61,11 @@ public class OrCost implements Cost { throw new RuntimeException("Not supported method"); } + @Override + public void setText(String text) { + this.description = text; + } + @Override public String getText() { return description; diff --git a/Mage/src/mage/abilities/costs/VariableCostImpl.java b/Mage/src/mage/abilities/costs/VariableCostImpl.java index c020d7922ba..e1002b91eb4 100644 --- a/Mage/src/mage/abilities/costs/VariableCostImpl.java +++ b/Mage/src/mage/abilities/costs/VariableCostImpl.java @@ -77,6 +77,11 @@ public abstract class VariableCostImpl implements Cost, VariableCost { this.amountPaid = cost.amountPaid; } + @Override + public void setText(String text) { + this.text = text; + } + @Override public String getText() { return text; @@ -157,8 +162,7 @@ public abstract class VariableCostImpl implements Cost, VariableCost { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { xValue = controller.announceXCost(getMinValue(source, game), getMaxValue(source, game), - new StringBuilder("Announce the number of ").append(actionText).toString(), - game, source, this); + "Announce the number of " + actionText, game, source, this); } return xValue; } diff --git a/Mage/src/mage/abilities/costs/common/DiscardTargetCost.java b/Mage/src/mage/abilities/costs/common/DiscardTargetCost.java index 69fb9b62298..da388060961 100644 --- a/Mage/src/mage/abilities/costs/common/DiscardTargetCost.java +++ b/Mage/src/mage/abilities/costs/common/DiscardTargetCost.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,21 +20,20 @@ * 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.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; import mage.cards.Card; +import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInHand; @@ -44,47 +43,46 @@ import mage.target.common.TargetCardInHand; * @author BetaSteward_at_googlemail.com */ public class DiscardTargetCost extends CostImpl { - + List cards = new ArrayList<>(); protected boolean randomDiscard; public DiscardTargetCost(TargetCardInHand target) { this(target, false); } - + public DiscardTargetCost(TargetCardInHand target, boolean randomDiscard) { this.addTarget(target); this.randomDiscard = randomDiscard; - this.text = "Discard " + target.getTargetName(); + this.text = "discard " + target.getTargetName(); } public DiscardTargetCost(DiscardTargetCost cost) { super(cost); - for (Card card: cost.cards) { - this.cards.add(card.copy()); - } + this.cards.addAll(cost.cards); this.randomDiscard = cost.randomDiscard; } @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) { this.cards.clear(); + this.targets.clearChosen();; Player player = game.getPlayer(controllerId); if (player == null) { return false; } int amount = this.getTargets().get(0).getNumberOfTargets(); - if (randomDiscard) { + if (randomDiscard) { this.cards.addAll(player.discard(amount, true, ability, game).getCards(game)); } else { - if (targets.choose(Outcome.Discard, controllerId, sourceId, game)) { - for (UUID targetId: targets.get(0).getTargets()) { + if (targets.choose(Outcome.Discard, controllerId, sourceId, game)) { + for (UUID targetId : targets.get(0).getTargets()) { Card card = player.getHand().get(targetId, game); if (card == null) { return false; } player.discard(card, ability, game); - this.cards.add(card.copy()); + this.cards.add(card); } } } @@ -94,8 +92,9 @@ public class DiscardTargetCost extends CostImpl { @Override public void clearPaid() { - super.clearPaid(); - cards.clear(); + super.clearPaid(); + this.cards.clear(); + this.targets.clearChosen(); } @Override diff --git a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java index 85f7676eb46..0167f67807f 100644 --- a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java +++ b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java @@ -84,6 +84,9 @@ public class RemoveCounterCost extends CostImpl { int countersRemoved = 0; Player controller = game.getPlayer(controllerId); if (controller != null) { + if (countersToRemove == 0) { // Can happen when used for X costs where X = 0; + return paid = true; + } target.clearChosen(); if (target.choose(Outcome.UnboostCreature, controllerId, sourceId, game)) { for (UUID targetId : target.getTargets()) { diff --git a/Mage/src/mage/abilities/costs/common/TapTargetCost.java b/Mage/src/mage/abilities/costs/common/TapTargetCost.java index 749eea7aff5..8f448eaa093 100644 --- a/Mage/src/mage/abilities/costs/common/TapTargetCost.java +++ b/Mage/src/mage/abilities/costs/common/TapTargetCost.java @@ -76,7 +76,7 @@ public class TapTargetCost extends CostImpl { @Override public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) { - return target.canChoose(controllerId, game); + return target.canChoose(sourceId, controllerId, game); } @Override diff --git a/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java index 5c6cb083be8..ac056ba437a 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java @@ -51,6 +51,7 @@ import mage.util.ManaUtil; public class ManaCostsImpl extends ArrayList implements ManaCosts { protected UUID id; + protected String text = null; private static Map costs = new HashMap<>(); @@ -372,8 +373,16 @@ public class ManaCostsImpl extends ArrayList implements M return this.id; } + @Override + public void setText(String text) { + this.text = text; + } + @Override public String getText() { + if (text != null) { + return text; + } if (this.size() == 0) { return ""; } diff --git a/Mage/src/mage/abilities/decorator/ConditionalContinuousRuleModifyingEffect.java b/Mage/src/mage/abilities/decorator/ConditionalContinuousRuleModifyingEffect.java index 8028e727066..c289550a5ff 100644 --- a/Mage/src/mage/abilities/decorator/ConditionalContinuousRuleModifyingEffect.java +++ b/Mage/src/mage/abilities/decorator/ConditionalContinuousRuleModifyingEffect.java @@ -41,7 +41,7 @@ import mage.game.events.GameEvent; * * @author LevelX2 */ -public class ConditionalContinuousRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl { +public class ConditionalContinuousRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl { protected ContinuousRuleModifyingEffect effect; protected ContinuousRuleModifyingEffect otherwiseEffect; @@ -88,7 +88,6 @@ public class ConditionalContinuousRuleModifyingEffect extends ContinuousRuleModi initDone = true; } - @Override public boolean isDiscarded() { return effect.isDiscarded() || (otherwiseEffect != null && otherwiseEffect.isDiscarded()); @@ -136,4 +135,20 @@ public class ConditionalContinuousRuleModifyingEffect extends ContinuousRuleModi public ConditionalContinuousRuleModifyingEffect copy() { return new ConditionalContinuousRuleModifyingEffect(this); } + + @Override + public boolean sendMessageToGameLog() { + return effect.sendMessageToGameLog(); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean sendMessageToUser() { + return effect.sendMessageToUser(); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public String getInfoMessage(Ability source, GameEvent event, Game game) { + return effect.getInfoMessage(source, event, game); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/Mage/src/mage/abilities/dynamicvalue/common/CreaturesDiedThisTurnCount.java b/Mage/src/mage/abilities/dynamicvalue/common/CreaturesDiedThisTurnCount.java new file mode 100644 index 00000000000..5f596b83536 --- /dev/null +++ b/Mage/src/mage/abilities/dynamicvalue/common/CreaturesDiedThisTurnCount.java @@ -0,0 +1,65 @@ +/* + * 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.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.game.Game; +import mage.watchers.common.CreaturesDiedWatcher; + +/** + * @author LoneFox + */ +public class CreaturesDiedThisTurnCount implements DynamicValue { + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + CreaturesDiedWatcher watcher = (CreaturesDiedWatcher)game.getState().getWatchers().get("CreaturesDiedWatcher"); + if (watcher != null) { + return watcher.getAmountOfCreaturesDiesThisTurn(); + } + return 0; + } + + @Override + public CreaturesDiedThisTurnCount copy() { + return new CreaturesDiedThisTurnCount(); + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "creature that died this turn"; + } + +} diff --git a/Mage/src/mage/abilities/effects/AsThoughEffect.java b/Mage/src/mage/abilities/effects/AsThoughEffect.java index c65b2638e78..a134aa53b72 100644 --- a/Mage/src/mage/abilities/effects/AsThoughEffect.java +++ b/Mage/src/mage/abilities/effects/AsThoughEffect.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,17 +20,16 @@ * 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 java.util.UUID; -import mage.constants.AsThoughEffectType; import mage.abilities.Ability; +import mage.constants.AsThoughEffectType; import mage.game.Game; /** @@ -40,7 +39,9 @@ import mage.game.Game; public interface AsThoughEffect extends ContinuousEffect { boolean applies(UUID sourceId, Ability affectedAbility, Ability source, Game game); + boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game); + AsThoughEffectType getAsThoughEffectType(); @Override diff --git a/Mage/src/mage/abilities/effects/AsThoughEffectImpl.java b/Mage/src/mage/abilities/effects/AsThoughEffectImpl.java index 5b2812b01d5..bd1448d8e9f 100644 --- a/Mage/src/mage/abilities/effects/AsThoughEffectImpl.java +++ b/Mage/src/mage/abilities/effects/AsThoughEffectImpl.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,12 +20,11 @@ * 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 java.util.UUID; @@ -54,7 +53,7 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements super(effect); this.type = effect.type; } - + @Override public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game) { return applies(objectId, source, affectedAbility.getControllerId(), game); diff --git a/Mage/src/mage/abilities/effects/AsThoughManaEffect.java b/Mage/src/mage/abilities/effects/AsThoughManaEffect.java new file mode 100644 index 00000000000..e6ba34f0f3d --- /dev/null +++ b/Mage/src/mage/abilities/effects/AsThoughManaEffect.java @@ -0,0 +1,45 @@ +/* + * 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 java.util.UUID; +import mage.abilities.Ability; +import mage.constants.ManaType; +import mage.game.Game; +import mage.players.ManaPoolItem; + +/** + * + * @author LevelX2 + */ +public interface AsThoughManaEffect extends AsThoughEffect { + + // return a mana type that can be used to pay a mana cost instead of the normally needed mana type + ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game); + +} diff --git a/Mage/src/mage/abilities/effects/AuraReplacementEffect.java b/Mage/src/mage/abilities/effects/AuraReplacementEffect.java index eabe831a800..116930ba282 100644 --- a/Mage/src/mage/abilities/effects/AuraReplacementEffect.java +++ b/Mage/src/mage/abilities/effects/AuraReplacementEffect.java @@ -36,13 +36,13 @@ import mage.cards.Card; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SpellAbilityType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; -import mage.game.stack.Spell; import mage.game.stack.StackAbility; import mage.players.Player; import mage.target.Target; @@ -103,12 +103,12 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { UUID targetId = null; MageObject sourceObject = game.getObject(sourceId); boolean enchantCardInGraveyard = false; - if (sourceObject instanceof Spell) { - if (fromZone.equals(Zone.EXILED)) { - // cast from exile (e.g. Neightveil Spector) -> no replacement - return false; - } - } +// if (sourceObject instanceof Spell) { +// if (fromZone.equals(Zone.EXILED)) { +// // cast from exile (e.g. Neightveil Spector) -> no replacement +// return false; +// } +// } if (sourceObject instanceof StackAbility) { StackAbility stackAbility = (StackAbility) sourceObject; if (!stackAbility.getEffects().isEmpty()) { @@ -118,25 +118,34 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { game.applyEffects(); // So continuousEffects are removed if previous effect of the same ability did move objects that cuase continuous effects if (targetId == null) { - Target target = card.getSpellAbility().getTargets().get(0).copy(); + SpellAbility spellAbility = card.getSpellAbility(); + if (spellAbility.getTargets().isEmpty()) { + for (Ability ability : card.getAbilities(game)) { + if ((ability instanceof SpellAbility) + && SpellAbilityType.BASE_ALTERNATE.equals(((SpellAbility) ability).getSpellAbilityType()) + && !ability.getTargets().isEmpty()) { + spellAbility = (SpellAbility) ability; + break; + } + } + } + if (spellAbility.getTargets().isEmpty()) { + return false; + } + Target target = spellAbility.getTargets().get(0).copy(); + Outcome auraOutcome = Outcome.BoostCreature; + for (Effect effect : spellAbility.getEffects()) { + if (effect instanceof AttachEffect) { + auraOutcome = effect.getOutcome(); + break; + } + } enchantCardInGraveyard = target instanceof TargetCardInGraveyard; if (target != null) { target.setNotTarget(true); // always not target because this way it's not handled targeted target.clearChosen(); // neccessary if e.g. aura is blinked multiple times } Player player = game.getPlayer(card.getOwnerId()); - Outcome auraOutcome = Outcome.BoostCreature; - Ability: - for (Ability ability : card.getAbilities()) { - if (ability instanceof SpellAbility) { - for (Effect effect : ability.getEffects()) { - if (effect instanceof AttachEffect) { - auraOutcome = effect.getOutcome(); - break Ability; - } - } - } - } if (target != null && player != null && player.choose(auraOutcome, target, card.getId(), game)) { targetId = target.getFirstTarget(); } @@ -151,44 +160,27 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { } Player targetPlayer = game.getPlayer(targetId); if (targetCard != null || targetPermanent != null || targetPlayer != null) { - switch (fromZone) { - case EXILED: - game.getExile().removeCard(card, game); - break; - case GRAVEYARD: - game.getPlayer(card.getOwnerId()).removeFromGraveyard(card, game); - break; - case HAND: - game.getPlayer(card.getOwnerId()).removeFromHand(card, game); - break; - case LIBRARY: - game.getPlayer(card.getOwnerId()).removeFromLibrary(card, game); - break; - default: - } - game.rememberLKI(card.getId(), fromZone, card); - + card.removeFromZone(game, fromZone, sourceId); + card.updateZoneChangeCounter(game); PermanentCard permanent = new PermanentCard(card, card.getOwnerId(), game); game.getBattlefield().addPermanent(permanent); card.setZone(Zone.BATTLEFIELD, game); - boolean entered = permanent.entersBattlefield(event.getSourceId(), game, fromZone, true); - game.applyEffects(); - if (!entered) { - return false; - } - game.fireEvent(new ZoneChangeEvent(permanent, controllerId, fromZone, Zone.BATTLEFIELD)); + if (permanent.entersBattlefield(event.getSourceId(), game, fromZone, true)) { + if (targetCard != null) { + permanent.attachTo(targetCard.getId(), game); + } else if (targetPermanent != null) { + targetPermanent.addAttachment(permanent.getId(), game); + } else if (targetPlayer != null) { + targetPlayer.addAttachment(permanent.getId(), game); + } + game.applyEffects(); - if (targetCard != null) { - permanent.attachTo(targetCard.getId(), game); - } - if (targetPermanent != null) { - targetPermanent.addAttachment(permanent.getId(), game); - } - if (targetPlayer != null) { - targetPlayer.addAttachment(permanent.getId(), game); + game.fireEvent(new ZoneChangeEvent(permanent, controllerId, fromZone, Zone.BATTLEFIELD)); + return true; } + } - return true; + return false; } @Override @@ -199,7 +191,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (((ZoneChangeEvent) event).getToZone().equals(Zone.BATTLEFIELD) - && !(((ZoneChangeEvent) event).getFromZone().equals(Zone.HAND))) { + && !(((ZoneChangeEvent) event).getFromZone().equals(Zone.STACK))) { Card card = game.getCard(event.getTargetId()); if (card != null && card.getCardType().contains(CardType.ENCHANTMENT) && card.hasSubtype("Aura")) { return true; diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 29bf3662dac..2f35dbd4901 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -55,6 +55,7 @@ import mage.constants.CostModificationType; import mage.constants.Duration; import mage.constants.EffectType; import mage.constants.Layer; +import mage.constants.ManaType; import mage.constants.Outcome; import mage.constants.SpellAbilityType; import mage.constants.SubLayer; @@ -70,6 +71,7 @@ import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.game.stack.Spell; +import mage.players.ManaPoolItem; import mage.players.Player; import mage.target.common.TargetCardInHand; import org.apache.log4j.Logger; @@ -546,6 +548,37 @@ public class ContinuousEffects implements Serializable { } + public ManaType asThoughMana(ManaType manaType, ManaPoolItem mana, UUID objectId, Ability affectedAbility, UUID controllerId, Game game) { + // First check existing only effects + List asThoughEffectsList = getApplicableAsThoughEffects(AsThoughEffectType.SPEND_ONLY_MANA, game); + for (AsThoughEffect effect : asThoughEffectsList) { + HashSet abilities = asThoughEffectsMap.get(AsThoughEffectType.SPEND_ONLY_MANA).getAbility(effect.getId()); + for (Ability ability : abilities) { + if ((affectedAbility == null && effect.applies(objectId, ability, controllerId, game)) + || effect.applies(objectId, affectedAbility, ability, game)) { + if (((AsThoughManaEffect) effect).getAsThoughManaType(manaType, mana, controllerId, ability, game) == null) { + return null; + } + } + } + } + // then check effects that allow to use other mana types to pay the current mana type to pay + asThoughEffectsList = getApplicableAsThoughEffects(AsThoughEffectType.SPEND_OTHER_MANA, game); + for (AsThoughEffect effect : asThoughEffectsList) { + HashSet abilities = asThoughEffectsMap.get(AsThoughEffectType.SPEND_OTHER_MANA).getAbility(effect.getId()); + for (Ability ability : abilities) { + if ((affectedAbility == null && effect.applies(objectId, ability, controllerId, game)) + || effect.applies(objectId, affectedAbility, ability, game)) { + ManaType usableManaType = ((AsThoughManaEffect) effect).getAsThoughManaType(manaType, mana, controllerId, ability, game); + if (usableManaType != null) { + return usableManaType; + } + } + } + } + return manaType; + } + /** * Filters out asThough effects that are not active. * @@ -843,8 +876,10 @@ public class ContinuousEffects implements Serializable { } } // Must be called here for some effects to be able to work correctly - // TODO: add info which effects need that call - game.applyEffects(); + // For example: Vesuva copying a Dark Depth (VesuvaTest:testDarkDepth) + // This call should be removed if possible as replacement effects of EntersTheBattlefield events + // do no longer work correctly because the entering permanents are not yet on the battlefield (before they were). + // game.applyEffects(); } while (true); return caught; } @@ -1114,18 +1149,18 @@ public class ContinuousEffects implements Serializable { } } - private void setControllerForEffect(ContinuousEffectsList effects, UUID cardId, UUID controllerId) { + private void setControllerForEffect(ContinuousEffectsList effects, UUID sourceId, UUID controllerId) { for (Effect effect : effects) { HashSet abilities = effects.getAbility(effect.getId()); if (abilities != null) { for (Ability ability : abilities) { if (ability.getSourceId() != null) { - if (ability.getSourceId().equals(cardId)) { + if (ability.getSourceId().equals(sourceId)) { ability.setControllerId(controllerId); } } else { if (!ability.getZone().equals(Zone.COMMAND)) { - logger.fatal(new StringBuilder("No sourceId Ability: ").append(ability)); + logger.fatal("Continuous effect for ability with no sourceId Ability: " + ability); } } } @@ -1213,7 +1248,7 @@ public class ContinuousEffects implements Serializable { HashSet abilities = preventionEffects.getAbility(effect.getId()); for (Ability ability : abilities) { if (ability.getSourceId().equals(sourceId)) { - if (controllerFound == null || controllerFound == ability.getControllerId()) { + if (controllerFound == null || controllerFound.equals(ability.getControllerId())) { controllerFound = ability.getControllerId(); } else { // not unique controller - No solution yet @@ -1227,7 +1262,7 @@ public class ContinuousEffects implements Serializable { for (Ability ability : abilities) { if (ability.getSourceId() != null) { if (ability.getSourceId().equals(sourceId)) { - if (controllerFound == null || controllerFound == ability.getControllerId()) { + if (controllerFound == null || controllerFound.equals(ability.getControllerId())) { controllerFound = ability.getControllerId(); } else { // not unique controller - No solution yet diff --git a/Mage/src/mage/abilities/effects/ContinuousEffectsList.java b/Mage/src/mage/abilities/effects/ContinuousEffectsList.java index cbad22a927f..234a30e4564 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffectsList.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffectsList.java @@ -1,30 +1,30 @@ /* -* Copyright 2012 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. -*/ + * Copyright 2012 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 java.util.ArrayList; @@ -46,18 +46,19 @@ import org.apache.log4j.Logger; * @param */ public class ContinuousEffectsList extends ArrayList { - + private static final Logger logger = Logger.getLogger(ContinuousEffectsList.class); // the effectAbilityMap holds for each effect all abilities that are connected (used) with this effect private final Map> effectAbilityMap = new HashMap<>(); - public ContinuousEffectsList() { } + public ContinuousEffectsList() { + } public ContinuousEffectsList(final ContinuousEffectsList effects) { this.ensureCapacity(effects.size()); - for (ContinuousEffect cost: effects) { - this.add((T)cost.copy()); + for (ContinuousEffect cost : effects) { + this.add((T) cost.copy()); } for (Map.Entry> entry : effects.effectAbilityMap.entrySet()) { HashSet newSet = new HashSet<>(); @@ -113,12 +114,12 @@ public class ContinuousEffectsList extends ArrayList Ability ability = it.next(); if (ability == null) { it.remove(); - } else if (ability instanceof MageSingleton) { + } else if (ability instanceof MageSingleton) { return false; - } else if (effect.isDiscarded()) { + } else if (effect.isDiscarded()) { it.remove(); } else { - switch(effect.getDuration()) { + switch (effect.getDuration()) { case WhileOnBattlefield: case WhileInGraveyard: case WhileOnStack: @@ -133,8 +134,8 @@ public class ContinuousEffectsList extends ArrayList break; case Custom: case UntilYourNextTurn: - if (effect.isInactive(ability , game)) { - it.remove(); + if (effect.isInactive(ability, game)) { + it.remove(); } } } @@ -143,9 +144,9 @@ public class ContinuousEffectsList extends ArrayList } /** - * Adds an effect and its connected ability to the list. - * For each effect will be stored, which abilities are connected to the effect. - * So an effect can be connected to multiple abilities. + * Adds an effect and its connected ability to the list. For each effect + * will be stored, which abilities are connected to the effect. So an effect + * can be connected to multiple abilities. * * @param effect - effect to add * @param source - connected ability @@ -153,8 +154,8 @@ public class ContinuousEffectsList extends ArrayList public void addEffect(T effect, Ability source) { if (effectAbilityMap.containsKey(effect.getId())) { HashSet set = effectAbilityMap.get(effect.getId()); - for (Ability ability: set) { - if (ability.getId().equals(source.getId()) && ability.getSourceId().equals(source.getSourceId()) ) { + for (Ability ability : set) { + if (ability.getId().equals(source.getId()) && ability.getSourceId().equals(source.getSourceId())) { return; } } diff --git a/Mage/src/mage/abilities/effects/ContinuousRuleModifyingEffectImpl.java b/Mage/src/mage/abilities/effects/ContinuousRuleModifyingEffectImpl.java index 5dcf58d5e90..541873718fc 100644 --- a/Mage/src/mage/abilities/effects/ContinuousRuleModifyingEffectImpl.java +++ b/Mage/src/mage/abilities/effects/ContinuousRuleModifyingEffectImpl.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * 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; @@ -45,20 +44,18 @@ public abstract class ContinuousRuleModifyingEffectImpl extends ContinuousEffect protected final boolean messageToUser; protected final boolean messageToGameLog; protected final String infoMessage; - + // 613.10 - // Some continuous effects affect game rules rather than objects. For example, effects may modify + // 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 + // 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. + // 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 ContinuousRuleModifyingEffectImpl(Duration duration, Outcome outcome) { this(duration, outcome, true, false); } @@ -67,11 +64,13 @@ public abstract class ContinuousRuleModifyingEffectImpl extends ContinuousEffect * * @param duration * @param outcome - * @param messageToUser - Every time the effect replaces an event, the user gets a message in a dialog window. - * Don't set it to true if the event happens regularly or very often. The message itself can be - * changed by overriding the getInfoMessage method. - * @param messageToLog - Every time the effect replaces an event, a message is posted to the game log. The message - * can be changed by overriding the getInfoMessage method. + * @param messageToUser - Every time the effect replaces an event, the user + * gets a message in a dialog window. Don't set it to true if the event + * happens regularly or very often. The message itself can be changed by + * overriding the getInfoMessage method. + * @param messageToLog - Every time the effect replaces an event, a message + * is posted to the game log. The message can be changed by overriding the + * getInfoMessage method. */ public ContinuousRuleModifyingEffectImpl(Duration duration, Outcome outcome, boolean messageToUser, boolean messageToLog) { super(duration, outcome); @@ -89,9 +88,11 @@ public abstract class ContinuousRuleModifyingEffectImpl extends ContinuousEffect } /** - * An early check for the event types this effect applies to. This check was added - * to speed up event handling. Once all existing ContinuousRuleModifiyingEffects have - * implemented this method, the method should be changed to abstract here or removed. + * An early check for the event types this effect applies to. This check was + * added to speed up event handling. Once all existing + * ContinuousRuleModifiyingEffects have implemented this method, the method + * should be changed to abstract here or removed. + * * @param event * @param game * @return @@ -112,10 +113,10 @@ public abstract class ContinuousRuleModifyingEffectImpl extends ContinuousEffect String message; MageObject object = game.getObject(source.getSourceId()); if (object != null) { - message = source.getRule(object.getLogName()); + message = source.getRule(messageToUser ? object.getIdName() : object.getLogName()); } else { message = source.getRule(); - } + } return message; } else { return infoMessage; @@ -132,5 +133,4 @@ public abstract class ContinuousRuleModifyingEffectImpl extends ContinuousEffect return messageToGameLog; } - } diff --git a/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java b/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java index 90bdfaf14c3..052d4ee5d4b 100644 --- a/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java +++ b/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java @@ -1,42 +1,43 @@ /* -* 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. -*/ - + * 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.constants.Duration; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.condition.Condition; +import mage.constants.Duration; +import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; /** @@ -64,10 +65,6 @@ public class EntersBattlefieldEffect extends ReplacementEffectImpl { this(baseEffect, null, text, true, optional); } - public EntersBattlefieldEffect(Effect baseEffect, Condition condition, String text) { - this(baseEffect, condition, text, true, false); - } - public EntersBattlefieldEffect(Effect baseEffect, Condition condition, String text, boolean selfScope, boolean optional) { super(Duration.WhileOnBattlefield, baseEffect.getOutcome(), selfScope); this.baseEffects.add(baseEffect); @@ -111,24 +108,29 @@ public class EntersBattlefieldEffect extends ReplacementEffectImpl { if (controller == null || object == null) { return false; } - if (!controller.chooseUse(outcome, new StringBuilder("Use effect of ").append(object.getLogName()).append("?").toString(), source, game)) { + if (!controller.chooseUse(outcome, "Use effect of " + object.getLogName() + "?", source, game)) { return false; } } Spell spell = game.getStack().getSpell(event.getSourceId()); - for (Effect effect: baseEffects) { - if (source.activate(game, false)) { - if (effect instanceof ContinuousEffect) { - game.addEffect((ContinuousEffect) effect, source); - } - else { - if (spell != null) { - effect.setValue(SOURCE_CAST_SPELL_ABILITY, spell.getSpellAbility()); - } - effect.apply(game, source); - } + if (spell == null) { + StackObject stackObject = (StackObject) game.getLastKnownInformation(event.getSourceId(), Zone.STACK); + if (stackObject instanceof Spell) { + spell = (Spell) stackObject; } } + for (Effect effect : baseEffects) { + // if (source.activate(game, false)) { // Why is this needed???? + if (effect instanceof ContinuousEffect) { + game.addEffect((ContinuousEffect) effect, source); + } else { + if (spell != null) { + effect.setValue(SOURCE_CAST_SPELL_ABILITY, spell.getSpellAbility()); + } + effect.apply(game, source); + } + // } + } return false; } diff --git a/Mage/src/mage/abilities/effects/RedirectionEffect.java b/Mage/src/mage/abilities/effects/RedirectionEffect.java index 6c339460edf..7979ad8f1b1 100644 --- a/Mage/src/mage/abilities/effects/RedirectionEffect.java +++ b/Mage/src/mage/abilities/effects/RedirectionEffect.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,12 +20,11 @@ * 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; @@ -46,36 +45,67 @@ import mage.target.Target; public abstract class RedirectionEffect extends ReplacementEffectImpl { protected Target redirectTarget; + protected int amountToRedirect; + protected boolean oneUsage; public RedirectionEffect(Duration duration) { + this(duration, Integer.MAX_VALUE, false); + } + + public RedirectionEffect(Duration duration, int amountToRedirect, boolean oneUsage) { super(duration, Outcome.RedirectDamage); this.effectType = EffectType.REDIRECTION; + this.amountToRedirect = amountToRedirect; + this.oneUsage = oneUsage; } public RedirectionEffect(final RedirectionEffect effect) { super(effect); this.redirectTarget = effect.redirectTarget; + this.amountToRedirect = effect.amountToRedirect; + this.oneUsage = effect.oneUsage; } @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - DamageEvent damageEvent = (DamageEvent)event; - Permanent permanent = game.getPermanent(redirectTarget.getFirstTarget()); - if (permanent != null) { - permanent.damage(damageEvent.getAmount(), event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); - return true; - } - Player player = game.getPlayer(redirectTarget.getFirstTarget()); - if (player != null) { - player.damage(damageEvent.getAmount(), event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); - return true; + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_CREATURE: + case DAMAGE_PLAYER: + case DAMAGE_PLANESWALKER: + return true; } return false; } + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + String sourceLogName = source != null ? game.getObject(source.getSourceId()).getLogName() + ": " : ""; + DamageEvent damageEvent = (DamageEvent) event; + int restDamage = 0; + int damageToRedirect = event.getAmount(); + if (damageEvent.getAmount() > amountToRedirect) { + restDamage = damageEvent.getAmount() - amountToRedirect; + damageToRedirect = amountToRedirect; + } + if (damageToRedirect > 0 && oneUsage) { + this.discard(); + } + Permanent permanent = game.getPermanent(redirectTarget.getFirstTarget()); + if (permanent != null) { + permanent.damage(damageToRedirect, event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); + game.informPlayers(sourceLogName + "Redirected " + damageToRedirect + " damage to " + permanent.getLogName()); + } else { + Player player = game.getPlayer(redirectTarget.getFirstTarget()); + if (player != null) { + player.damage(damageToRedirect, event.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), event.getAppliedEffects()); + game.informPlayers(sourceLogName + "Redirected " + damageToRedirect + " damage to " + player.getLogName()); + } + } + if (restDamage > 0) { + damageEvent.setAmount(restDamage); + return false; + } + return true; + } + } diff --git a/Mage/src/mage/abilities/effects/common/AddManaInAnyCombinationEffect.java b/Mage/src/mage/abilities/effects/common/AddManaInAnyCombinationEffect.java index 4a87a976677..722fc0468d9 100644 --- a/Mage/src/mage/abilities/effects/common/AddManaInAnyCombinationEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddManaInAnyCombinationEffect.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; @@ -62,17 +61,17 @@ public class AddManaInAnyCombinationEffect extends ManaEffect { this.amount = amount; this.staticText = setText(); } - + public AddManaInAnyCombinationEffect(int amount, String text) { this(amount); this.staticText = text; } - + public AddManaInAnyCombinationEffect(int amount, String text, ColoredManaSymbol... coloredManaSymbols) { this(amount, coloredManaSymbols); this.staticText = text; } - + public AddManaInAnyCombinationEffect(DynamicValue amount, String text, ColoredManaSymbol... coloredManaSymbols) { this(amount, coloredManaSymbols); this.staticText = text; @@ -92,13 +91,13 @@ public class AddManaInAnyCombinationEffect extends ManaEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null){ + if (player != null) { Mana mana = new Mana(); int amountOfManaLeft = amount.calculate(game, source, this); while (amountOfManaLeft > 0 && player.canRespond()) { - for (ColoredManaSymbol coloredManaSymbol: manaSymbols) { - int number = player.getAmount(0, amountOfManaLeft, new StringBuilder("How many ").append(coloredManaSymbol.name()).append(" mana?").toString(), game); + for (ColoredManaSymbol coloredManaSymbol : manaSymbols) { + int number = player.getAmount(0, amountOfManaLeft, "How many " + coloredManaSymbol.getColorName() + " mana?", game); if (number > 0) { for (int i = 0; i < number; i++) { mana.add(new Mana(coloredManaSymbol)); @@ -111,7 +110,7 @@ public class AddManaInAnyCombinationEffect extends ManaEffect { } } checkToFirePossibleEvents(mana, game, source); - player.getManaPool().addMana(mana, game, source); + player.getManaPool().addMana(mana, game, source); return true; } return false; @@ -130,7 +129,7 @@ public class AddManaInAnyCombinationEffect extends ManaEffect { sb.append("colors"); } else { int i = 0; - for (ColoredManaSymbol coloredManaSymbol: manaSymbols) { + for (ColoredManaSymbol coloredManaSymbol : manaSymbols) { i++; if (i > 1) { sb.append(" and/or "); diff --git a/Mage/src/mage/abilities/effects/common/AmplifyEffect.java b/Mage/src/mage/abilities/effects/common/AmplifyEffect.java index a06e008ad7a..b05ddd626c1 100644 --- a/Mage/src/mage/abilities/effects/common/AmplifyEffect.java +++ b/Mage/src/mage/abilities/effects/common/AmplifyEffect.java @@ -19,6 +19,7 @@ import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; @@ -27,15 +28,13 @@ import mage.target.common.TargetCardInHand; /** * Effect for the AmplifyAbility * - * 702.37. Amplify - * 702.37a Amplify is a static ability. “Amplify N” means “As - * this object enters the battlefield, reveal any number of cards from your hand - * that share a creature type with it. This permanent enters the battlefield - * with N +1/+1 counters on it for each card revealed this way. You can’t reveal - * this card or any other cards that are entering the battlefield at the same - * time as this card.” - * 702.37b If a creature has multiple instances of amplify, - * each one works separately. + * 702.37. Amplify 702.37a Amplify is a static ability. “Amplify N” means “As + * this object enters the battlefield, reveal any number of cards from your hand + * that share a creature type with it. This permanent enters the battlefield + * with N +1/+1 counters on it for each card revealed this way. You can’t reveal + * this card or any other cards that are entering the battlefield at the same + * time as this card.” 702.37b If a creature has multiple instances of amplify, + * each one works separately. * * * @author FenrisulfrX @@ -45,6 +44,7 @@ public class AmplifyEffect extends ReplacementEffectImpl { private final AmplifyFactor amplifyFactor; public enum AmplifyFactor { + Amplify1("Amplify 1", "put one +1/+1 counters on it", 1), Amplify2("Amplify 2", "put two +1/+1 counters on it", 2), Amplify3("Amplify 3", "put three +1/+1 counters on it", 3); @@ -95,7 +95,7 @@ public class AmplifyEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent sourceCreature = game.getPermanent(event.getTargetId()); + Permanent sourceCreature = ((EntersTheBattlefieldEvent) event).getTarget(); Player controller = game.getPlayer(source.getControllerId()); if (controller != null && sourceCreature != null) { FilterCreatureCard filter = new FilterCreatureCard("creatures cards to reveal"); @@ -108,7 +108,7 @@ public class AmplifyEffect extends ReplacementEffectImpl { } else if (filterSubtypes.size() == 1) { filter.add(filterSubtypes.get(0)); } - if (controller.getHand().count(filter, source.getSourceId(), source.getControllerId(), game) > 0){ + if (controller.getHand().count(filter, source.getSourceId(), source.getControllerId(), game) > 0) { if (controller.chooseUse(outcome, "Reveal cards to Amplify?", source, game)) { TargetCardInHand target = new TargetCardInHand(0, Integer.MAX_VALUE, filter); if (controller.chooseTarget(outcome, target, source, game) && !target.getTargets().isEmpty()) { @@ -116,7 +116,7 @@ public class AmplifyEffect extends ReplacementEffectImpl { cards.addAll(target.getTargets()); int amountCounters = cards.size() * amplifyFactor.getFactor(); sourceCreature.addCounters(CounterType.P1P1.createInstance(amountCounters), game); - controller.revealCards(sourceCreature.getName(), cards, game); + controller.revealCards(sourceCreature.getIdName(), cards, game); } } } @@ -128,11 +128,11 @@ public class AmplifyEffect extends ReplacementEffectImpl { public String getText(Mode mode) { StringBuilder sb = new StringBuilder(amplifyFactor.toString()); sb.append("(As this enter the battlefield, "); - sb.append(amplifyFactor.getRuleText()).append(" for each card" + - " you reveal that shares a type with it in your hand.)"); + sb.append(amplifyFactor.getRuleText()).append(" for each card" + + " you reveal that shares a type with it in your hand.)"); return sb.toString(); } - + @Override public AmplifyEffect copy() { return new AmplifyEffect(this); diff --git a/Mage/src/mage/abilities/effects/common/BasicManaEffect.java b/Mage/src/mage/abilities/effects/common/BasicManaEffect.java index b98d1dd7efb..95e7faa8eac 100644 --- a/Mage/src/mage/abilities/effects/common/BasicManaEffect.java +++ b/Mage/src/mage/abilities/effects/common/BasicManaEffect.java @@ -6,18 +6,19 @@ import mage.abilities.Ability; import mage.game.Game; public class BasicManaEffect extends ManaEffect { + protected Mana mana; public BasicManaEffect(Mana mana) { super(); this.mana = mana; - staticText = "Add " + mana.toString() + " to your mana pool"; + staticText = "add " + mana.toString() + " to your mana pool"; } public BasicManaEffect(ConditionalMana conditionalMana) { super(); this.mana = conditionalMana; - staticText = "Add " + mana.toString() + " to your mana pool. " + conditionalMana.getDescription(); + staticText = "add " + mana.toString() + " to your mana pool. " + conditionalMana.getDescription(); } public BasicManaEffect(final BasicManaEffect effect) { diff --git a/Mage/src/mage/abilities/effects/common/ChangeATargetOfTargetSpellAbilityToSourceEffect.java b/Mage/src/mage/abilities/effects/common/ChangeATargetOfTargetSpellAbilityToSourceEffect.java new file mode 100644 index 00000000000..e660cd19671 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/ChangeATargetOfTargetSpellAbilityToSourceEffect.java @@ -0,0 +1,130 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackAbility; +import mage.game.stack.StackObject; +import mage.players.Player; +import mage.target.Target; +import mage.target.Targets; + +/** + * + * @author LevelX2 + */ +public class ChangeATargetOfTargetSpellAbilityToSourceEffect extends OneShotEffect { + + public ChangeATargetOfTargetSpellAbilityToSourceEffect() { + super(Outcome.Neutral); + staticText = "Change a target of target spell or ability to {this}"; + } + + public ChangeATargetOfTargetSpellAbilityToSourceEffect(final ChangeATargetOfTargetSpellAbilityToSourceEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (stackObject != null && sourceObject != null) { + Targets targets = new Targets(); + Ability sourceAbility; + String oldTargetName = null; + if (stackObject instanceof Spell) { + Spell spell = (Spell) stackObject; + sourceAbility = spell.getSpellAbility(); + } else if (stackObject instanceof StackAbility) { + StackAbility stackAbility = (StackAbility) stackObject; + sourceAbility = stackAbility; + } else { + return false; + } + for (UUID modeId : sourceAbility.getModes().getSelectedModes()) { + sourceAbility.getModes().setActiveMode(modeId); + targets.addAll(sourceAbility.getTargets()); + } + + boolean twoTimesTarget = false; + if (targets.size() == 1 && targets.get(0).getTargets().size() == 1) { + Target target = targets.get(0); + if (target.canTarget(stackObject.getControllerId(), source.getSourceId(), sourceAbility, game)) { + oldTargetName = getTargetName(targets.getFirstTarget(), game); + target.clearChosen(); + // The source is still the spell on the stack + target.addTarget(source.getSourceId(), stackObject.getStackAbility(), game); + } + } else { + Player controller = game.getPlayer(source.getControllerId()); + boolean validTargets = false; + do { + for (Target target : targets) { + for (UUID targetId : target.getTargets()) { + String name = getTargetName(targets.getFirstTarget(), game); + if (!targetId.equals(source.getSourceId()) && target.getTargets().contains(source.getSourceId())) { + // you can't change this target to source because the source is already another targetId of that target. + twoTimesTarget = true; + continue; + } + if (target.canTarget(stackObject.getControllerId(), source.getSourceId(), sourceAbility, game)) { + validTargets = true; + if (name != null + && controller.chooseUse(Outcome.Neutral, "Change target from " + name + " to " + sourceObject.getLogName() + "?", source, game)) { + oldTargetName = getTargetName(targetId, game); + target.remove(targetId); + // The source is still the spell on the stack + target.addTarget(source.getSourceId(), stackObject.getStackAbility(), game); + break; + } + } + } + if (oldTargetName != null) { + break; + } + } + if (oldTargetName == null) { + game.informPlayer(controller, "You have to select at least one target to change to " + sourceObject.getIdName() + "!"); + } + } while (validTargets && oldTargetName == null); + } + if (oldTargetName != null) { + game.informPlayers(sourceObject.getLogName() + ": Changed target of " + stackObject.getLogName() + " from " + oldTargetName + " to " + sourceObject.getLogName()); + } else { + if (twoTimesTarget) { + game.informPlayers(sourceObject.getLogName() + ": Target not changed to " + sourceObject.getLogName() + " because its not valid to target it twice for " + stackObject.getLogName()); + } else { + game.informPlayers(sourceObject.getLogName() + ": Target not changed to " + sourceObject.getLogName() + " because its no valid target for " + stackObject.getLogName()); + } + } + return true; + } + return false; + } + + @Override + public ChangeATargetOfTargetSpellAbilityToSourceEffect copy() { + return new ChangeATargetOfTargetSpellAbilityToSourceEffect(this); + } + + private String getTargetName(UUID objectId, Game game) { + MageObject object = game.getObject(objectId); + if (object != null) { + return object.getLogName(); + } + Player player = game.getPlayer(objectId); + if (player != null) { + return player.getLogName(); + } + return null; + } +} diff --git a/Mage/src/mage/abilities/effects/common/ChooseBasicLandTypeEffect.java b/Mage/src/mage/abilities/effects/common/ChooseBasicLandTypeEffect.java new file mode 100644 index 00000000000..5d73e8dd40e --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/ChooseBasicLandTypeEffect.java @@ -0,0 +1,67 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.choices.ChoiceImpl; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author LevelX2 + */ +public class ChooseBasicLandTypeEffect extends OneShotEffect { + + public static String VALUE_KEY = "BasicLandType"; + + public ChooseBasicLandTypeEffect(Outcome outcome) { + super(outcome); + this.staticText = "Choose a basic land type"; + } + + public ChooseBasicLandTypeEffect(final ChooseBasicLandTypeEffect effect) { + super(effect); + } + + @Override + public ChooseBasicLandTypeEffect copy() { + return new ChooseBasicLandTypeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + if (controller != null && mageObject != null) { + ChoiceImpl choices = new ChoiceImpl(true); + choices.setMessage("Choose basic land type"); + choices.isRequired(); + choices.getChoices().add("Forest"); + choices.getChoices().add("Plains"); + choices.getChoices().add("Mountain"); + choices.getChoices().add("Island"); + choices.getChoices().add("Swamp"); + if (controller.choose(Outcome.Neutral, choices, game)) { + game.informPlayers(mageObject.getName() + ": Chosen basic land type is " + choices.getChoice()); + game.getState().setValue(mageObject.getId().toString() + VALUE_KEY, choices.getChoice()); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("chosen color", CardUtil.addToolTipMarkTags("Chosen basic land type: " + choices.getChoice()), game); + } + return true; + } + } + return false; + } +} diff --git a/Mage/src/mage/abilities/effects/common/ChooseColorEffect.java b/Mage/src/mage/abilities/effects/common/ChooseColorEffect.java index 8f22a961ad7..161dec6767e 100644 --- a/Mage/src/mage/abilities/effects/common/ChooseColorEffect.java +++ b/Mage/src/mage/abilities/effects/common/ChooseColorEffect.java @@ -25,9 +25,9 @@ * 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 mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.choices.ChoiceColor; @@ -55,8 +55,11 @@ public class ChooseColorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (controller != null && permanent != null) { + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + if (controller != null && mageObject != null) { ChoiceColor choice = new ChoiceColor(); while (!choice.isChosen()) { controller.choose(outcome, choice, game); @@ -65,10 +68,12 @@ public class ChooseColorEffect extends OneShotEffect { } } if (!game.isSimulation()) { - game.informPlayers(permanent.getLogName()+": "+controller.getLogName()+" has chosen "+choice.getChoice()); + game.informPlayers(mageObject.getLogName() + ": " + controller.getLogName() + " has chosen " + choice.getChoice()); + } + game.getState().setValue(mageObject.getId() + "_color", choice.getColor()); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("chosen color", CardUtil.addToolTipMarkTags("Chosen color: " + choice.getChoice()), game); } - game.getState().setValue(source.getSourceId() + "_color", choice.getColor()); - permanent.addInfo("chosen color", CardUtil.addToolTipMarkTags("Chosen color: " + choice.getChoice()), game); return true; } return false; @@ -79,4 +84,4 @@ public class ChooseColorEffect extends OneShotEffect { return new ChooseColorEffect(this); } -} \ No newline at end of file +} diff --git a/Mage/src/mage/abilities/effects/common/ChooseCreatureTypeEffect.java b/Mage/src/mage/abilities/effects/common/ChooseCreatureTypeEffect.java index 2ebd99cc9d2..b1d4b0e6f62 100644 --- a/Mage/src/mage/abilities/effects/common/ChooseCreatureTypeEffect.java +++ b/Mage/src/mage/abilities/effects/common/ChooseCreatureTypeEffect.java @@ -27,6 +27,7 @@ */ package mage.abilities.effects.common; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.repository.CardRepository; @@ -42,7 +43,6 @@ import mage.util.CardUtil; * * @author LevelX2 */ - public class ChooseCreatureTypeEffect extends OneShotEffect { public ChooseCreatureTypeEffect(Outcome outcome) { @@ -57,8 +57,11 @@ public class ChooseCreatureTypeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (controller != null && permanent != null) { + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + if (controller != null && mageObject != null) { Choice typeChoice = new ChoiceImpl(true); typeChoice.setMessage("Choose creature type"); typeChoice.setChoices(CardRepository.instance.getCreatureTypes()); @@ -68,10 +71,12 @@ public class ChooseCreatureTypeEffect extends OneShotEffect { } } if (!game.isSimulation()) { - game.informPlayers(permanent.getName() + ": " + controller.getLogName() + " has chosen " + typeChoice.getChoice()); + game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has chosen " + typeChoice.getChoice()); + } + game.getState().setValue(mageObject.getId() + "_type", typeChoice.getChoice()); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("chosen type", CardUtil.addToolTipMarkTags("Chosen type: " + typeChoice.getChoice()), game); } - game.getState().setValue(permanent.getId() + "_type", typeChoice.getChoice()); - permanent.addInfo("chosen type", CardUtil.addToolTipMarkTags("Chosen type: " + typeChoice.getChoice()), game); } return false; } diff --git a/Mage/src/mage/abilities/effects/common/ChooseLandTypeEffect.java b/Mage/src/mage/abilities/effects/common/ChooseLandTypeEffect.java index 0480effa493..bd59c25c334 100644 --- a/Mage/src/mage/abilities/effects/common/ChooseLandTypeEffect.java +++ b/Mage/src/mage/abilities/effects/common/ChooseLandTypeEffect.java @@ -5,6 +5,7 @@ */ package mage.abilities.effects.common; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.repository.CardRepository; @@ -34,8 +35,11 @@ public class ChooseLandTypeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(source.getSourceId()); - if (controller != null && permanent != null) { + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + if (controller != null && mageObject != null) { Choice typeChoice = new ChoiceImpl(true); typeChoice.setMessage("Choose land type"); typeChoice.setChoices(CardRepository.instance.getLandTypes()); @@ -45,10 +49,12 @@ public class ChooseLandTypeEffect extends OneShotEffect { } } if (!game.isSimulation()) { - game.informPlayers(permanent.getName() + ": " + controller.getLogName() + " has chosen " + typeChoice.getChoice()); + game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has chosen " + typeChoice.getChoice()); + } + game.getState().setValue(mageObject.getId() + "_type", typeChoice.getChoice()); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("chosen type", CardUtil.addToolTipMarkTags("Chosen type: " + typeChoice.getChoice()), game); } - game.getState().setValue(permanent.getId() + "_type", typeChoice.getChoice()); - permanent.addInfo("chosen type", CardUtil.addToolTipMarkTags("Chosen type: " + typeChoice.getChoice()), game); } return false; } diff --git a/Mage/src/mage/abilities/effects/common/ChooseModeEffect.java b/Mage/src/mage/abilities/effects/common/ChooseModeEffect.java index 796f3e7fce7..bdfac9f3216 100644 --- a/Mage/src/mage/abilities/effects/common/ChooseModeEffect.java +++ b/Mage/src/mage/abilities/effects/common/ChooseModeEffect.java @@ -69,6 +69,9 @@ public class ChooseModeEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + if (sourcePermanent == null) { + sourcePermanent = game.getPermanentEntering(source.getSourceId()); + } if (controller != null) { Choice choice = new ChoiceImpl(true); choice.setMessage(choiceMessage); @@ -80,8 +83,9 @@ public class ChooseModeEffect extends OneShotEffect { controller.choose(Outcome.Neutral, choice, game); } if (choice.isChosen()) { - if (!game.isSimulation()) - game.informPlayers(new StringBuilder(sourcePermanent.getLogName()).append(": ").append(controller.getLogName()).append(" has chosen ").append(choice.getChoice()).toString()); + if (!game.isSimulation()) { + game.informPlayers(sourcePermanent.getLogName() + ": " + controller.getLogName() + " has chosen " + choice.getChoice()); + } game.getState().setValue(source.getSourceId() + "_modeChoice", choice.getChoice()); sourcePermanent.addInfo("_modeChoice", "Chosen mode: " + choice.getChoice() + "", game); } @@ -93,7 +97,7 @@ public class ChooseModeEffect extends OneShotEffect { private String setText() { StringBuilder sb = new StringBuilder("choose "); int count = 0; - for (String choice: modes) { + for (String choice : modes) { count++; sb.append(choice); if (count + 1 < modes.size()) { diff --git a/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java b/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java index 265b66e9cdd..35ac9818cd5 100644 --- a/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java @@ -1,38 +1,36 @@ /* -* 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. -*/ - + * 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.common; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; -import mage.filter.Filter; +import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.stack.StackObject; @@ -46,21 +44,22 @@ public class ChooseNewTargetsTargetEffect extends OneShotEffect { private boolean forceChange; private boolean onlyOneTarget; private FilterPermanent filterNewTarget; - + public ChooseNewTargetsTargetEffect() { this(false, false); } + public ChooseNewTargetsTargetEffect(boolean forceChange, boolean onlyOneTarget) { this(forceChange, onlyOneTarget, null); } /** * - * @param forceChange forces the user to choose another target (only targets with maxtargets = 1 supported) + * @param forceChange forces the user to choose another target (only targets + * with maxtargets = 1 supported) * @param onlyOneTarget only one target can be selected for the change * @param filterNewTarget restriction to the new target */ - public ChooseNewTargetsTargetEffect(boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { super(Outcome.Benefit); this.forceChange = forceChange; diff --git a/Mage/src/mage/abilities/effects/common/ChooseOpponentEffect.java b/Mage/src/mage/abilities/effects/common/ChooseOpponentEffect.java new file mode 100644 index 00000000000..bdb32a34a13 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/ChooseOpponentEffect.java @@ -0,0 +1,63 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetOpponent; +import mage.util.CardUtil; + +/** + * + * @author LevelX2 + */ +public class ChooseOpponentEffect extends OneShotEffect { + + public static String VALUE_KEY = "_opponent"; + + public ChooseOpponentEffect(Outcome outcome) { + super(outcome); + this.staticText = "choose an opponent"; + } + + public ChooseOpponentEffect(final ChooseOpponentEffect effect) { + super(effect); + } + + @Override + public ChooseOpponentEffect copy() { + return new ChooseOpponentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + if (controller != null && mageObject != null) { + TargetOpponent target = new TargetOpponent(true); + if (controller.choose(this.outcome, target, source.getSourceId(), game)) { + Player chosenPlayer = game.getPlayer(target.getFirstTarget()); + if (chosenPlayer != null) { + game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has chosen " + chosenPlayer.getLogName()); + game.getState().setValue(mageObject.getId() + VALUE_KEY, target.getFirstTarget()); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("chosen opponent", CardUtil.addToolTipMarkTags("Chosen player: " + chosenPlayer.getLogName()), game); + } + return true; + } + } + } + return false; + } +} diff --git a/Mage/src/mage/abilities/effects/common/ChoosePlayerEffect.java b/Mage/src/mage/abilities/effects/common/ChoosePlayerEffect.java new file mode 100644 index 00000000000..7f8ce4c72cc --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/ChoosePlayerEffect.java @@ -0,0 +1,61 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.util.CardUtil; + +/** + * + * @author LevelX2 + */ +public class ChoosePlayerEffect extends OneShotEffect { + + public ChoosePlayerEffect(Outcome outcome) { + super(outcome); + this.staticText = "choose a player"; + } + + public ChoosePlayerEffect(final ChoosePlayerEffect effect) { + super(effect); + } + + @Override + public ChoosePlayerEffect copy() { + return new ChoosePlayerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); + if (mageObject == null) { + mageObject = game.getObject(source.getSourceId()); + } + if (controller != null && mageObject != null) { + TargetPlayer target = new TargetPlayer(1, 1, true); + if (controller.choose(this.outcome, target, source.getSourceId(), game)) { + Player chosenPlayer = game.getPlayer(target.getFirstTarget()); + if (chosenPlayer != null) { + game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has chosen " + chosenPlayer.getLogName()); + game.getState().setValue(mageObject.getId() + "_player", target.getFirstTarget()); + if (mageObject instanceof Permanent) { + ((Permanent) mageObject).addInfo("chosen player", CardUtil.addToolTipMarkTags("Chosen player: " + chosenPlayer.getLogName()), game); + } + return true; + } + } + } + return false; + } +} diff --git a/Mage/src/mage/abilities/effects/common/CopyEffect.java b/Mage/src/mage/abilities/effects/common/CopyEffect.java index 9d1194fda2a..9b6becb7198 100644 --- a/Mage/src/mage/abilities/effects/common/CopyEffect.java +++ b/Mage/src/mage/abilities/effects/common/CopyEffect.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,21 +20,20 @@ * 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.common; - import java.util.UUID; import mage.MageObject; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.Card; +import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Layer; @@ -56,35 +55,47 @@ public class CopyEffect extends ContinuousEffectImpl { /** * Object we copy from */ - private MageObject target; - - private UUID sourceId; - private ApplyToPermanent applier; - - public CopyEffect(MageObject target, UUID sourceId) { - this(Duration.Custom, target, sourceId); + protected MageObject copyFromObject; + + protected UUID copyToObjectId; + protected ApplyToPermanent applier; + + public CopyEffect(MageObject copyFromObject, UUID copyToObjectId) { + this(Duration.Custom, copyFromObject, copyToObjectId); } - - public CopyEffect(Duration duration, MageObject target, UUID sourceId) { + + public CopyEffect(Duration duration, MageObject copyFromObject, UUID copyToObjectId) { super(duration, Layer.CopyEffects_1, SubLayer.NA, Outcome.BecomeCreature); - this.target = target; - this.sourceId = sourceId; + this.copyFromObject = copyFromObject; + this.copyToObjectId = copyToObjectId; } public CopyEffect(final CopyEffect effect) { super(effect); - this.target = effect.target.copy(); - this.sourceId = effect.sourceId; + this.copyFromObject = effect.copyFromObject.copy(); + this.copyToObjectId = effect.copyToObjectId; this.applier = effect.applier; } @Override public void init(Ability source, Game game) { super.init(source, game); - if (!(target instanceof Permanent) && (target instanceof Card)) { - this.target = new PermanentCard((Card)target, source.getControllerId(), game); + if (!(copyFromObject instanceof Permanent) && (copyFromObject instanceof Card)) { + this.copyFromObject = new PermanentCard((Card) copyFromObject, source.getControllerId(), game); + } + Permanent permanent = game.getPermanent(copyToObjectId); + if (permanent != null) { + affectedObjectList.add(new MageObjectReference(permanent, game)); + } else if (source.getAbilityType().equals(AbilityType.STATIC)) { + // for replacement effects that let a permanent enter the battlefield as a copy of another permanent we need to apply that copy + // before the permanent is added to the battlefield + permanent = game.getPermanentEntering(copyToObjectId); + if (permanent != null) { + copyToPermanent(permanent, game, source); + // set reference to the permanent later on the battlefield so we have to add already one to the zone change counter + affectedObjectList.add(new MageObjectReference(permanent.getId(), game.getState().getZoneChangeCounter(copyToObjectId) + 1, game)); + } } - affectedObjectList.add(new MageObjectReference(getSourceId(), game)); } @Override @@ -98,32 +109,36 @@ public class CopyEffect extends ContinuousEffectImpl { return false; } } + return copyToPermanent(permanent, game, source); + } + + protected boolean copyToPermanent(Permanent permanent, Game game, Ability source) { permanent.setCopy(true); - permanent.setName(target.getName()); - permanent.getColor(game).setColor(target.getColor(game)); + permanent.setName(copyFromObject.getName()); + permanent.getColor(game).setColor(copyFromObject.getColor(game)); permanent.getManaCost().clear(); - permanent.getManaCost().add(target.getManaCost()); + permanent.getManaCost().add(copyFromObject.getManaCost()); permanent.getCardType().clear(); - for (CardType type: target.getCardType()) { + for (CardType type : copyFromObject.getCardType()) { permanent.getCardType().add(type); } permanent.getSubtype().clear(); - for (String type: target.getSubtype()) { + for (String type : copyFromObject.getSubtype()) { permanent.getSubtype().add(type); } permanent.getSupertype().clear(); - for (String type: target.getSupertype()) { + for (String type : copyFromObject.getSupertype()) { permanent.getSupertype().add(type); } permanent.removeAllAbilities(source.getSourceId(), game); - for (Ability ability: target.getAbilities()) { - permanent.addAbility(ability, getSourceId(), game, false); // no new Id so consumed replacement effects are known while new continuousEffects.apply happen. + for (Ability ability : copyFromObject.getAbilities()) { + permanent.addAbility(ability, getSourceId(), game, false); // no new Id so consumed replacement effects are known while new continuousEffects.apply happen. } - permanent.getPower().setValue(target.getPower().getValue()); - permanent.getToughness().setValue(target.getToughness().getValue()); - if (target instanceof Permanent) { - Permanent targetPermanent = (Permanent) target; + permanent.getPower().setValue(copyFromObject.getPower().getValue()); + permanent.getToughness().setValue(copyFromObject.getToughness().getValue()); + if (copyFromObject instanceof Permanent) { + Permanent targetPermanent = (Permanent) copyFromObject; permanent.setTransformed(targetPermanent.isTransformed()); permanent.setSecondCardFace(targetPermanent.getSecondCardFace()); permanent.setFlipCard(targetPermanent.isFlipCard()); @@ -131,13 +146,13 @@ public class CopyEffect extends ContinuousEffectImpl { } // to get the image of the copied permanent copy number und expansionCode - if (target instanceof PermanentCard) { - permanent.setCardNumber(((PermanentCard) target).getCard().getCardNumber()); - permanent.setExpansionSetCode(((PermanentCard) target).getCard().getExpansionSetCode()); - } else if (target instanceof PermanentToken || target instanceof Card) { - permanent.setCardNumber(((Card) target).getCardNumber()); - permanent.setExpansionSetCode(((Card) target).getExpansionSetCode()); - } + if (copyFromObject instanceof PermanentCard) { + permanent.setCardNumber(((PermanentCard) copyFromObject).getCard().getCardNumber()); + permanent.setExpansionSetCode(((PermanentCard) copyFromObject).getCard().getExpansionSetCode()); + } else if (copyFromObject instanceof PermanentToken || copyFromObject instanceof Card) { + permanent.setCardNumber(((Card) copyFromObject).getCardNumber()); + permanent.setExpansionSetCode(((Card) copyFromObject).getExpansionSetCode()); + } return true; } @@ -147,15 +162,15 @@ public class CopyEffect extends ContinuousEffectImpl { } public MageObject getTarget() { - return target; + return copyFromObject; } public void setTarget(MageObject target) { - this.target = target; + this.copyFromObject = target; } public UUID getSourceId() { - return sourceId; + return copyToObjectId; } public ApplyToPermanent getApplier() { @@ -165,5 +180,5 @@ public class CopyEffect extends ContinuousEffectImpl { public void setApplier(ApplyToPermanent applier) { this.applier = applier; } - + } diff --git a/Mage/src/mage/abilities/effects/common/CopyPermanentEffect.java b/Mage/src/mage/abilities/effects/common/CopyPermanentEffect.java index 7860911a2dd..865adb292e8 100644 --- a/Mage/src/mage/abilities/effects/common/CopyPermanentEffect.java +++ b/Mage/src/mage/abilities/effects/common/CopyPermanentEffect.java @@ -1,16 +1,16 @@ /* * Copyright 2011 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 @@ -20,13 +20,14 @@ * 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.common; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; @@ -66,12 +67,13 @@ public class CopyPermanentEffect extends OneShotEffect { public CopyPermanentEffect(FilterPermanent filter, ApplyToPermanent applier) { this(filter, applier, false); } + public CopyPermanentEffect(FilterPermanent filter, ApplyToPermanent applier, boolean useTarget) { super(Outcome.Copy); this.applier = applier; this.filter = filter; this.useTargetOfAbility = useTarget; - this.staticText = "You may have {this} enter the battlefield as a copy of any " + filter.getMessage() + " on the battlefield"; + this.staticText = "as a copy of any " + filter.getMessage() + " on the battlefield"; } public CopyPermanentEffect(final CopyPermanentEffect effect) { @@ -85,21 +87,24 @@ public class CopyPermanentEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (player != null && sourcePermanent != null) { + MageObject sourceObject = game.getPermanentEntering(source.getSourceId()); + if (sourceObject == null) { + sourceObject = game.getObject(source.getSourceId()); + } + if (player != null && sourceObject != null) { Permanent copyFromPermanent = null; if (useTargetOfAbility) { copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); } else { Target target = new TargetPermanent(filter); target.setNotTarget(true); - if (target.canChoose(source.getControllerId(), game)) { + if (target.canChoose(source.getSourceId(), player.getId(), game)) { player.choose(Outcome.Copy, target, source.getSourceId(), game); copyFromPermanent = game.getPermanent(target.getFirstTarget()); } } if (copyFromPermanent != null) { - bluePrintPermanent = game.copyPermanent(copyFromPermanent, sourcePermanent, source, applier); + bluePrintPermanent = game.copyPermanent(copyFromPermanent, sourceObject.getId(), source, applier); } return true; } diff --git a/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java b/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java index d23e2c8f2ff..01e36d989b4 100644 --- a/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java +++ b/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java @@ -28,7 +28,6 @@ package mage.abilities.effects.common; import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; @@ -56,7 +55,8 @@ public class CounterTargetWithReplacementEffect extends OneShotEffect { /** * * @param targetZone - * @param flag use to specify when moving card to library
  • true = put on top
  • false = put on bottom
+ * @param flag use to specify when moving card to library
  • true = put + * on top
  • false = put on bottom
*/ public CounterTargetWithReplacementEffect(Zone targetZone, boolean flag) { super(Outcome.Detriment); @@ -83,33 +83,14 @@ public class CounterTargetWithReplacementEffect extends OneShotEffect { if (controller != null) { StackObject stackObject = game.getStack().getStackObject(objectId); if (stackObject != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { - boolean spell = false; if (stackObject instanceof Spell) { - game.rememberLKI(objectId, Zone.STACK, stackObject); - spell = true; - } - game.getStack().remove(stackObject); - if (spell && !((Spell) stackObject).isCopiedSpell()) { - MageObject mageObject = game.getObject(stackObject.getSourceId()); - if (mageObject instanceof Card) { - Card card = (Card) mageObject; - switch (targetZone) { - case LIBRARY: - controller.moveCardToLibraryWithInfo(card, sourceId, game, Zone.STACK, flag, true); - break; - case EXILED: - controller.moveCardToExileWithInfo(card, null, "", sourceId, game, Zone.STACK, true); - break; - default: - controller.moveCards(card, Zone.STACK, targetZone, source, game); - } - } else { - return false; - } + controller.moveCards((Card) stackObject, null, targetZone, source, game); + } else { + game.getStack().remove(stackObject); } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); return true; - } + } } return false; } diff --git a/Mage/src/mage/abilities/effects/common/DevourEffect.java b/Mage/src/mage/abilities/effects/common/DevourEffect.java index 1d02ab1733d..18432616aa8 100644 --- a/Mage/src/mage/abilities/effects/common/DevourEffect.java +++ b/Mage/src/mage/abilities/effects/common/DevourEffect.java @@ -39,6 +39,7 @@ import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; @@ -47,30 +48,32 @@ import mage.target.common.TargetControlledCreaturePermanent; /** * Effect for the DevourAbility - * - * 702.81. Devour - * 702.81a Devour is a static ability. "Devour N" means "As this object enters the battlefield, - * you may sacrifice any number of creatures. This permanent enters the battlefield with N +1/+1 - * counters on it for each creature sacrificed this way." - * 702.81b Some objects have abilities that refer to the number of creatures the permanent devoured. - * "It devoured" means "sacrificed as a result of its devour ability as it entered the battlefield." * - * + * 702.81. Devour 702.81a Devour is a static ability. "Devour N" means "As this + * object enters the battlefield, you may sacrifice any number of creatures. + * This permanent enters the battlefield with N +1/+1 counters on it for each + * creature sacrificed this way." 702.81b Some objects have abilities that refer + * to the number of creatures the permanent devoured. "It devoured" means + * "sacrificed as a result of its devour ability as it entered the battlefield." + * + * * @author LevelX2 */ public class DevourEffect extends ReplacementEffectImpl { private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures to devour"); + static { filter.add(new AnotherPredicate()); } private final DevourFactor devourFactor; public enum DevourFactor { - Devour1 ("Devour 1", "that many +1/+1 counters on it", 1), - Devour2 ("Devour 2", "twice that many +1/+1 counters on it", 2), - Devour3 ("Devour 3", "three times that many +1/+1 counters on it", 3), - DevourX ("Devour X, where X is the number of creatures devoured this way", "X +1/+1 counters on it for each of those creatures", Integer.MAX_VALUE); + + Devour1("Devour 1", "that many +1/+1 counters on it", 1), + Devour2("Devour 2", "twice that many +1/+1 counters on it", 2), + Devour3("Devour 3", "three times that many +1/+1 counters on it", 3), + DevourX("Devour X, where X is the number of creatures devoured this way", "X +1/+1 counters on it for each of those creatures", Integer.MAX_VALUE); private final String text; private final String ruleText; @@ -114,9 +117,9 @@ public class DevourEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (event.getTargetId().equals(source.getSourceId())) { - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - game.getState().setValue(sourcePermanent.getId().toString() + "devoured", null); - return true; + Permanent sourcePermanent = ((EntersTheBattlefieldEvent) event).getTarget(); + game.getState().setValue(sourcePermanent.getId().toString() + "devoured", null); + return true; } return false; } @@ -128,7 +131,7 @@ public class DevourEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent creature = game.getPermanent(event.getTargetId()); + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); Player controller = game.getPlayer(source.getControllerId()); if (creature != null && controller != null) { Target target = new TargetControlledCreaturePermanent(1, Integer.MAX_VALUE, filter, true); @@ -141,9 +144,10 @@ public class DevourEffect extends ReplacementEffectImpl { if (target.getTargets().size() > 0) { List> cardSubtypes = new ArrayList<>(); int devouredCreatures = target.getTargets().size(); - if (!game.isSimulation()) - game.informPlayers(new StringBuilder(creature.getName()).append(" devours ").append(devouredCreatures).append(" creatures").toString()); - for (UUID targetId: target.getTargets()) { + if (!game.isSimulation()) { + game.informPlayers(creature.getLogName() + " devours " + devouredCreatures + " creatures"); + } + for (UUID targetId : target.getTargets()) { Permanent targetCreature = game.getPermanent(targetId); if (targetCreature != null) { cardSubtypes.add((ArrayList) targetCreature.getSubtype()); @@ -172,7 +176,7 @@ public class DevourEffect extends ReplacementEffectImpl { StringBuilder sb = new StringBuilder(devourFactor.toString()); sb.append(" (As this enters the battlefield, you may sacrifice any number of creatures. This creature enters the battlefield with "); sb.append(devourFactor.getRuleText()).append(")"); - return sb.toString(); + return sb.toString(); } public List> getSubtypes(Game game, UUID permanentId) { diff --git a/Mage/src/mage/abilities/effects/common/DiscardOntoBattlefieldEffect.java b/Mage/src/mage/abilities/effects/common/DiscardOntoBattlefieldEffect.java index 137b33c5145..1a82af11cdf 100644 --- a/Mage/src/mage/abilities/effects/common/DiscardOntoBattlefieldEffect.java +++ b/Mage/src/mage/abilities/effects/common/DiscardOntoBattlefieldEffect.java @@ -34,8 +34,8 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent.EventType; import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.stack.StackObject; import mage.players.Player; @@ -85,7 +85,7 @@ public class DiscardOntoBattlefieldEffect extends ReplacementEffectImpl { if (card != null) { Player owner = game.getPlayer(card.getOwnerId()); if (owner != null) { - if (owner.putOntoBattlefieldWithInfo(card, game, Zone.HAND, source.getSourceId())) { + if (owner.moveCards(card, Zone.BATTLEFIELD, source, game)) { return true; } } diff --git a/Mage/src/mage/abilities/effects/common/DontUntapInControllersUntapStepSourceEffect.java b/Mage/src/mage/abilities/effects/common/DontUntapInControllersUntapStepSourceEffect.java index 576628205dc..fd5d40ed8ae 100644 --- a/Mage/src/mage/abilities/effects/common/DontUntapInControllersUntapStepSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/DontUntapInControllersUntapStepSourceEffect.java @@ -27,11 +27,11 @@ */ package mage.abilities.effects.common; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.PhaseStep; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -44,7 +44,11 @@ import mage.game.permanent.Permanent; public class DontUntapInControllersUntapStepSourceEffect extends ContinuousRuleModifyingEffectImpl { public DontUntapInControllersUntapStepSourceEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment, false, true); + this(false, true); + } + + public DontUntapInControllersUntapStepSourceEffect(boolean messageToUser, boolean messageToLog) { + super(Duration.WhileOnBattlefield, Outcome.Detriment, messageToUser, messageToLog); staticText = "{this} doesn't untap during your untap step"; } @@ -78,4 +82,5 @@ public class DontUntapInControllersUntapStepSourceEffect extends ContinuousRuleM } return false; } + } diff --git a/Mage/src/mage/abilities/effects/common/DrawCardTargetEffect.java b/Mage/src/mage/abilities/effects/common/DrawCardTargetEffect.java index 6443cc2efbc..7d7556fc7cf 100644 --- a/Mage/src/mage/abilities/effects/common/DrawCardTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/DrawCardTargetEffect.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,12 +20,11 @@ * 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.common; import mage.abilities.Ability; @@ -51,6 +50,7 @@ public class DrawCardTargetEffect extends OneShotEffect { public DrawCardTargetEffect(int amount) { this(new StaticValue(amount)); } + public DrawCardTargetEffect(int amount, boolean optional) { this(new StaticValue(amount), optional); } @@ -117,7 +117,7 @@ public class DrawCardTargetEffect extends OneShotEffect { if (upTo) { sb.append("up to "); } - sb.append(CardUtil.numberToText(amount.toString())).append(" card"); + sb.append(CardUtil.numberToText(amount.toString(), "a")).append(" card"); try { if (Integer.parseInt(amount.toString()) > 1) { sb.append("s"); @@ -133,5 +133,4 @@ public class DrawCardTargetEffect extends OneShotEffect { return sb.toString(); } - } diff --git a/Mage/src/mage/abilities/effects/common/EntersBattlefieldWithXCountersEffect.java b/Mage/src/mage/abilities/effects/common/EntersBattlefieldWithXCountersEffect.java index 999e70423dc..9c704852225 100644 --- a/Mage/src/mage/abilities/effects/common/EntersBattlefieldWithXCountersEffect.java +++ b/Mage/src/mage/abilities/effects/common/EntersBattlefieldWithXCountersEffect.java @@ -31,6 +31,7 @@ import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.effects.EntersBattlefieldEffect; import mage.abilities.effects.OneShotEffect; +import mage.constants.AbilityType; import mage.constants.Outcome; import mage.counters.Counter; import mage.game.Game; @@ -59,11 +60,16 @@ public class EntersBattlefieldWithXCountersEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + if (permanent == null && source.getAbilityType().equals(AbilityType.STATIC)) { + permanent = game.getPermanentEntering(source.getSourceId()); + } + } if (permanent != null) { SpellAbility spellAbility = (SpellAbility) getValue(EntersBattlefieldEffect.SOURCE_CAST_SPELL_ABILITY); if (spellAbility != null && spellAbility.getSourceId().equals(source.getSourceId()) - && permanent.getZoneChangeCounter(game) - 1 == spellAbility.getSourceObjectZoneChangeCounter()) { + && permanent.getZoneChangeCounter(game) == spellAbility.getSourceObjectZoneChangeCounter()) { if (spellAbility.getSourceId().equals(source.getSourceId())) { // put into play by normal cast int amount = spellAbility.getManaCostsToPay().getX(); if (amount > 0) { diff --git a/Mage/src/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java b/Mage/src/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java index 6e99909a49e..6f3170a0700 100644 --- a/Mage/src/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileAndReturnTransformedSourceEffect.java @@ -61,12 +61,11 @@ public class ExileAndReturnTransformedSourceEffect extends OneShotEffect { Permanent sourceObject = game.getPermanent(source.getSourceId()); Player controller = game.getPlayer(source.getControllerId()); if (sourceObject != null && controller != null && sourceObject.getZoneChangeCounter(game) == source.getSourceObjectZoneChangeCounter()) { - Card card = (Card) sourceObject; - if (controller.moveCards(card, Zone.BATTLEFIELD, Zone.EXILED, source, game)) { - Player owner = game.getPlayer(card.getOwnerId()); - if (owner != null) { - game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); - owner.putOntoBattlefieldWithInfo(card, game, Zone.EXILED, source.getSourceId()); + if (controller.moveCards(sourceObject, Zone.EXILED, source, game)) { + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); + Card cardFromExile = game.getCard(source.getSourceId()); + if (cardFromExile != null) { + controller.moveCards(cardFromExile, Zone.BATTLEFIELD, source, game, false, false, true, null); if (additionalEffect != null) { if (additionalEffect instanceof ContinuousEffect) { game.addEffect((ContinuousEffect) additionalEffect, source); diff --git a/Mage/src/mage/abilities/effects/common/ExileFromZoneTargetEffect.java b/Mage/src/mage/abilities/effects/common/ExileFromZoneTargetEffect.java index 48be98f6899..97ecc487a04 100644 --- a/Mage/src/mage/abilities/effects/common/ExileFromZoneTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileFromZoneTargetEffect.java @@ -1,16 +1,16 @@ /* * Copyright 2011 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 @@ -20,7 +20,7 @@ * 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. @@ -28,17 +28,18 @@ package mage.abilities.effects.common; import java.util.UUID; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; /** * @@ -90,16 +91,11 @@ public class ExileFromZoneTargetEffect extends OneShotEffect { default: } if (target != null && target.canChoose(source.getSourceId(), player.getId(), game)) { - if (target.choose(Outcome.Exile, player.getId(), source.getSourceId(), game)) { - for (UUID cardId : target.getTargets()) { - Card card = game.getCard(cardId); - if (card != null) { - card.moveToExile(exileId, exileName, source.getSourceId(), game); - } - } - return true; + if (target.chooseTarget(Outcome.Exile, player.getId(), source, game)) { + player.moveCardsToExile(new CardsImpl(target.getTargets()).getCards(game), source, game, true, exileId, exileName); } } + return true; } return false; } @@ -110,10 +106,6 @@ public class ExileFromZoneTargetEffect extends OneShotEffect { } private void setText() { - if (amount == 1) { - staticText = "Target player exiles a " + filter.getMessage() + " from his or her " + zone.toString().toLowerCase(); - } else { - staticText = "Target player exiles " + amount + " " + filter.getMessage() + " from his or her " + zone.toString().toLowerCase(); - } + staticText = "target player exiles " + CardUtil.numberToText(amount, "a") + " " + filter.getMessage() + " from his or her " + zone.toString().toLowerCase(); } } diff --git a/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java b/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java index f60cf67ba16..60efb497e97 100644 --- a/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,12 +20,11 @@ * 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.common; import java.util.UUID; @@ -49,11 +48,12 @@ public class ExileSourceEffect extends OneShotEffect { public ExileSourceEffect() { this(false); } - + /** - * - * @param toUniqueExileZone moves the card to a source object dependant unique exile zone, so another - * effect of the same source object (e.g. Deadeye Navigator) can identify the card + * + * @param toUniqueExileZone moves the card to a source object dependant + * unique exile zone, so another effect of the same source object (e.g. + * Deadeye Navigator) can identify the card */ public ExileSourceEffect(boolean toUniqueExileZone) { super(Outcome.Exile); @@ -72,10 +72,10 @@ public class ExileSourceEffect extends OneShotEffect { } @Override - public boolean apply(Game game, Ability source) { + public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); if (sourceObject instanceof Card) { UUID exileZoneId = null; String exileZoneName = ""; @@ -84,7 +84,7 @@ public class ExileSourceEffect extends OneShotEffect { exileZoneName = sourceObject.getName(); } Card sourceCard = (Card) sourceObject; - return controller.moveCardToExileWithInfo(sourceCard, exileZoneId, exileZoneName, source.getSourceId(), game, game.getState().getZone(sourceCard.getId()), true); + return controller.moveCardsToExile(sourceCard, source, game, true, exileZoneId, exileZoneName); } return true; } diff --git a/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java b/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java index df19c8af57e..2725630dc30 100644 --- a/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * 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.common; import mage.abilities.Ability; @@ -58,14 +57,14 @@ public class ExileSpellEffect extends OneShotEffect implements MageSingleton { public ExileSpellEffect copy() { return fINSTANCE; } - + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Card spellCard = game.getStack().getSpell(source.getSourceId()).getCard(); if (spellCard != null) { - controller.moveCardToExileWithInfo(spellCard, null, "", source.getSourceId(), game, Zone.STACK, true); + controller.moveCards(spellCard, null, Zone.EXILED, source, game); } return true; } diff --git a/Mage/src/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java b/Mage/src/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java index 89b065c0306..529760e039b 100644 --- a/Mage/src/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java +++ b/Mage/src/mage/abilities/effects/common/LookLibraryAndPickControllerEffect.java @@ -29,7 +29,6 @@ */ package mage.abilities.effects.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.dynamicvalue.DynamicValue; @@ -128,28 +127,13 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff if (!optional || player.chooseUse(Outcome.DrawCard, getMayText(), source, game)) { FilterCard pickFilter = filter.copy(); pickFilter.setMessage(getPickText()); - TargetCard target = new TargetCard((upTo ? 0 : numberToPick.calculate(game, source, this)), numberToPick.calculate(game, source, this), Zone.PICK, pickFilter); + TargetCard target = new TargetCard((upTo ? 0 : numberToPick.calculate(game, source, this)), numberToPick.calculate(game, source, this), Zone.LIBRARY, pickFilter); if (player.choose(Outcome.DrawCard, cards, target, game)) { - Cards reveal = new CardsImpl(); - for (UUID cardId : target.getTargets()) { - Card card = cards.get(cardId, game); - if (card != null) { - cards.remove(card); - if (targetZoneLookedCards.equals(Zone.BATTLEFIELD)) { - player.putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, source.getSourceId()); - } else { - card.moveToZone(targetPickedCards, source.getSourceId(), game, false); - if (!game.isSimulation()) { - game.informPlayers(player.getLogName() + " moves a card to " + targetPickedCards.toString().toLowerCase()); - } - } - if (revealPickedCards) { - reveal.add(card); - } - } - } + Cards pickedCards = new CardsImpl(target.getTargets()); + cards.removeAll(pickedCards); + player.moveCards(pickedCards.getCards(game), targetPickedCards, source, game); if (revealPickedCards) { - player.revealCards(windowName, reveal, game); + player.revealCards(windowName, pickedCards, game); } } diff --git a/Mage/src/mage/abilities/effects/common/NameACardEffect.java b/Mage/src/mage/abilities/effects/common/NameACardEffect.java index 0123f9fd6f6..7cf32682fa6 100644 --- a/Mage/src/mage/abilities/effects/common/NameACardEffect.java +++ b/Mage/src/mage/abilities/effects/common/NameACardEffect.java @@ -71,7 +71,10 @@ public class NameACardEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); + MageObject sourceObject = game.getPermanentEntering(source.getSourceId()); + if (sourceObject == null) { + sourceObject = game.getObject(source.getSourceId()); + } if (controller != null && sourceObject != null) { Choice cardChoice = new ChoiceImpl(); switch (typeOfName) { diff --git a/Mage/src/mage/abilities/effects/common/PutLandFromHandOntoBattlefieldEffect.java b/Mage/src/mage/abilities/effects/common/PutLandFromHandOntoBattlefieldEffect.java index 8d819291cc3..c08da1ba5b3 100644 --- a/Mage/src/mage/abilities/effects/common/PutLandFromHandOntoBattlefieldEffect.java +++ b/Mage/src/mage/abilities/effects/common/PutLandFromHandOntoBattlefieldEffect.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 mage.abilities.Ability; @@ -50,10 +49,11 @@ public class PutLandFromHandOntoBattlefieldEffect extends OneShotEffect { public PutLandFromHandOntoBattlefieldEffect() { this(false); } + public PutLandFromHandOntoBattlefieldEffect(boolean tapped) { super(Outcome.PutLandInPlay); this.tapped = tapped; - staticText = "you may put a land card from your hand onto the battlefield" + (tapped ? " tapped":""); + staticText = "you may put a land card from your hand onto the battlefield" + (tapped ? " tapped" : ""); } public PutLandFromHandOntoBattlefieldEffect(final PutLandFromHandOntoBattlefieldEffect effect) { @@ -66,12 +66,12 @@ public class PutLandFromHandOntoBattlefieldEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { Target target = new TargetCardInHand(new FilterLandCard("land card")); - if (target.canChoose(source.getSourceId(), source.getControllerId(), game) && - controller.chooseUse(outcome, "Put land onto battlefield?", source, game) && - controller.choose(outcome, target, source.getSourceId(), game)) { + if (target.canChoose(source.getSourceId(), source.getControllerId(), game) + && controller.chooseUse(outcome, "Put land onto battlefield?", source, game) + && controller.choose(outcome, target, source.getSourceId(), game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { - controller.putOntoBattlefieldWithInfo(card, game, Zone.HAND, source.getSourceId(), tapped); + controller.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, false, null); } } return true; @@ -85,4 +85,4 @@ public class PutLandFromHandOntoBattlefieldEffect extends OneShotEffect { return new PutLandFromHandOntoBattlefieldEffect(this); } - } +} diff --git a/Mage/src/mage/abilities/effects/common/PutPermanentOnBattlefieldEffect.java b/Mage/src/mage/abilities/effects/common/PutPermanentOnBattlefieldEffect.java index 12a208ab190..f559340ecce 100644 --- a/Mage/src/mage/abilities/effects/common/PutPermanentOnBattlefieldEffect.java +++ b/Mage/src/mage/abilities/effects/common/PutPermanentOnBattlefieldEffect.java @@ -48,10 +48,9 @@ public class PutPermanentOnBattlefieldEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player; - if(useTargetController) { + if (useTargetController) { player = game.getPlayer(getTargetPointer().getFirst(game, source)); - } - else { + } else { player = game.getPlayer(source.getControllerId()); } String choiceText = "Put " + filter.getMessage() + " from your hand onto the battlefield?"; @@ -63,23 +62,21 @@ public class PutPermanentOnBattlefieldEffect extends OneShotEffect { if (player.choose(Outcome.PutCreatureInPlay, target, source.getSourceId(), game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { - player.putOntoBattlefieldWithInfo(card, game, Zone.HAND, source.getSourceId()); - return true; + return player.moveCards(card, Zone.BATTLEFIELD, source, game); } } - return false; + return true; } @Override public String getText(Mode mode) { - if(this.staticText != null && !this.staticText.isEmpty()) { + if (this.staticText != null && !this.staticText.isEmpty()) { return staticText; } - if(useTargetController) { + if (useTargetController) { return "that player may put " + filter.getMessage() + " from his or her hand onto the battlefield"; - } - else { + } else { return "you may put " + filter.getMessage() + " from your hand onto the battlefield"; } } diff --git a/Mage/src/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java b/Mage/src/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java new file mode 100644 index 00000000000..61b35d63abd --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/RedirectDamageFromSourceToTargetEffect.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.RedirectionEffect; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author LevelX2 + */ +public class RedirectDamageFromSourceToTargetEffect extends RedirectionEffect { + + public RedirectDamageFromSourceToTargetEffect(Duration duration, int amountToRedirect, boolean oneUsage) { + super(duration, amountToRedirect, oneUsage); + staticText = "The next " + amountToRedirect + " damage that would be dealt to {this} this turn is dealt to target creature you control instead."; + } + + public RedirectDamageFromSourceToTargetEffect(final RedirectDamageFromSourceToTargetEffect effect) { + super(effect); + } + + @Override + public RedirectDamageFromSourceToTargetEffect copy() { + return new RedirectDamageFromSourceToTargetEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getTargetId().equals(source.getSourceId())) { + this.redirectTarget = source.getTargets().get(0); + return true; + } + return false; + } +} diff --git a/Mage/src/mage/abilities/effects/common/RemoveAllCountersSourceEffect.java b/Mage/src/mage/abilities/effects/common/RemoveAllCountersSourceEffect.java new file mode 100644 index 00000000000..abc70475f47 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/RemoveAllCountersSourceEffect.java @@ -0,0 +1,73 @@ +/* + * 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.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LoneFox + */ +public class RemoveAllCountersSourceEffect extends OneShotEffect { + + private final CounterType counterType; + + public RemoveAllCountersSourceEffect(CounterType counterType) { + super(Outcome.Neutral); + this.counterType = counterType; + staticText = "remove all " + counterType.getName() + " counters from it."; + } + + public RemoveAllCountersSourceEffect(final RemoveAllCountersSourceEffect effect) { + super(effect); + this.counterType = effect.counterType; + } + + @Override + public RemoveAllCountersSourceEffect copy() { + return new RemoveAllCountersSourceEffect(this); + } + + @java.lang.Override + public boolean apply(Game game, Ability source) { + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + if(sourcePermanent != null) { + int count = sourcePermanent.getCounters().getCount(counterType); + sourcePermanent.getCounters().removeCounter(counterType, count); + return true; + } + return false; + } +} + diff --git a/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java b/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java index 181bb35f94c..1276b0acdff 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java @@ -30,7 +30,6 @@ package mage.abilities.effects.common; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.constants.Outcome; import mage.constants.Zone; import static mage.constants.Zone.BATTLEFIELD; @@ -103,14 +102,9 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect { ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter)); if (exile != null) { // null is valid if source left battlefield before enters the battlefield effect resolved if (returnToZone.equals(Zone.BATTLEFIELD)) { - for (Card card : exile.getCards(game)) { - Player owner = game.getPlayer(card.getOwnerId()); - if (owner != null) { - owner.putOntoBattlefieldWithInfo(card, game, Zone.EXILED, source.getSourceId()); - } - } + controller.moveCards(exile.getCards(game), returnToZone, source, game, false, false, true, null); } else { - controller.moveCards(exile, Zone.EXILED, returnToZone, source, game); + controller.moveCards(exile, null, returnToZone, source, game); } } return true; diff --git a/Mage/src/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java b/Mage/src/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java index da7960e7437..dd2e2797041 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnFromGraveyardToBattlefieldTargetEffect.java @@ -73,7 +73,7 @@ public class ReturnFromGraveyardToBattlefieldTargetEffect extends OneShotEffect for (UUID targetId : getTargetPointer().getTargets(game, source)) { Card card = game.getCard(targetId); if (card != null) { - controller.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId(), tapped); + controller.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, false, null); } } return true; diff --git a/Mage/src/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java b/Mage/src/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java index a87f0360ff8..a1c3ebe28ab 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,19 +20,18 @@ * 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.common; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.players.Player; @@ -54,6 +53,7 @@ public class ReturnSourceFromGraveyardToBattlefieldEffect extends OneShotEffect this.tapped = tapped; setText(); } + public ReturnSourceFromGraveyardToBattlefieldEffect(boolean tapped, boolean ownerControl) { super(Outcome.PutCreatureInPlay); this.tapped = tapped; @@ -76,32 +76,31 @@ public class ReturnSourceFromGraveyardToBattlefieldEffect extends OneShotEffect public boolean apply(Game game, Ability source) { if (!game.getState().getZone(source.getSourceId()).equals(Zone.GRAVEYARD)) { return false; - } + } Card card = game.getCard(source.getSourceId()); if (card == null) { return false; } - - Player player; + + Player player; if (ownerControl) { player = game.getPlayer(card.getOwnerId()); } else { player = game.getPlayer(source.getControllerId()); - } - if (player == null) { + } + if (player == null) { return false; - } - - return player.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId(), tapped); + } + return player.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, true, null); } private void setText() { StringBuilder sb = new StringBuilder("return {this} from your graveyard to the battlefield"); if (tapped) { sb.append(" tapped"); - } + } if (ownerControl) { - sb.append(" under its owner's control"); + sb.append(" under its owner's control"); } staticText = sb.toString(); } diff --git a/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java b/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java index d386e7d944f..650e1c32cbe 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlTargetEffect.java @@ -88,8 +88,7 @@ public class ReturnToBattlefieldUnderYourControlTargetEffect extends OneShotEffe card = game.getCard(getTargetPointer().getFirst(game, source)); } if (card != null) { - Zone currentZone = game.getState().getZone(card.getId()); - controller.putOntoBattlefieldWithInfo(card, game, currentZone, source.getSourceId()); + controller.moveCards(card, Zone.BATTLEFIELD, source, game); } return true; } diff --git a/Mage/src/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java index b36191ecbc3..f0904271903 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java @@ -1,29 +1,26 @@ package mage.abilities.effects.common; -import mage.MageObject; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.costs.Cost; import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.util.CardUtil; /** - * Created by IntelliJ IDEA. - * User: Loki - * Date: 21.12.10 - * Time: 9:21 + * Created by IntelliJ IDEA. User: Loki Date: 21.12.10 Time: 9:21 */ public class SacrificeSourceUnlessPaysEffect extends OneShotEffect { + protected Cost cost; public SacrificeSourceUnlessPaysEffect(Cost cost) { super(Outcome.Sacrifice); this.cost = cost; - } + } public SacrificeSourceUnlessPaysEffect(final SacrificeSourceUnlessPaysEffect effect) { super(effect); @@ -33,14 +30,13 @@ public class SacrificeSourceUnlessPaysEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - MageObject sourceObject = source.getSourceObject(game); - if (sourceObject != null && player != null && permanent != null) { + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (player != null && sourcePermanent != null) { StringBuilder sb = new StringBuilder(cost.getText()).append("?"); - if (!sb.toString().toLowerCase().startsWith("exile ") && !sb.toString().toLowerCase().startsWith("return ") ) { + if (!sb.toString().toLowerCase().startsWith("exile ") && !sb.toString().toLowerCase().startsWith("return ")) { sb.insert(0, "Pay "); } - String message = CardUtil.replaceSourceName(sb.toString(), sourceObject.getLogName()); + String message = CardUtil.replaceSourceName(sb.toString(), sourcePermanent.getLogName()); message = Character.toUpperCase(message.charAt(0)) + message.substring(1); if (player.chooseUse(Outcome.Benefit, message, source, game)) { cost.clearPaid(); @@ -48,7 +44,7 @@ public class SacrificeSourceUnlessPaysEffect extends OneShotEffect { return true; } } - permanent.sacrifice(source.getSourceId(), game); + sourcePermanent.sacrifice(source.getSourceId(), game); return true; } return false; @@ -61,7 +57,7 @@ public class SacrificeSourceUnlessPaysEffect extends OneShotEffect { @Override public String getText(Mode mode) { - if(staticText != null && !staticText.isEmpty()) { + if (staticText != null && !staticText.isEmpty()) { return staticText; } @@ -74,11 +70,10 @@ public class SacrificeSourceUnlessPaysEffect extends OneShotEffect { || costText.toLowerCase().startsWith("sacrifice")) { sb.append(costText.substring(0, 1).toLowerCase()); sb.append(costText.substring(1)); - } - else { + } else { sb.append("pay ").append(costText); } return sb.toString(); } - } +} diff --git a/Mage/src/mage/abilities/effects/common/ShuffleSpellEffect.java b/Mage/src/mage/abilities/effects/common/ShuffleSpellEffect.java index 47b4322115a..fa8e72af499 100644 --- a/Mage/src/mage/abilities/effects/common/ShuffleSpellEffect.java +++ b/Mage/src/mage/abilities/effects/common/ShuffleSpellEffect.java @@ -1,37 +1,35 @@ /* -* 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. -*/ - + * 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.common; import mage.abilities.Ability; import mage.abilities.MageSingleton; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; @@ -59,15 +57,15 @@ public class ShuffleSpellEffect extends OneShotEffect implements MageSingleton { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Spell spell = game.getStack().getSpell(source.getSourceId()); + // We have to use the spell id because in case of copied spells, the sourceId can be multiple times on the stack + Spell spell = game.getStack().getSpell(source.getId()); if (spell != null) { - Card spellCard = spell.getCard(); - if (spellCard != null) { - Player owner = game.getPlayer(spellCard.getOwnerId()); + if (controller.moveCards(spell, Zone.LIBRARY, source, game) && !spell.isCopy()) { + Player owner = game.getPlayer(spell.getCard().getOwnerId()); if (owner != null) { - controller.moveCardToLibraryWithInfo(spellCard, source.getSourceId(), game, Zone.STACK, true, true); owner.shuffleLibrary(game); } + } } return true; diff --git a/Mage/src/mage/abilities/effects/common/TapSourceEffect.java b/Mage/src/mage/abilities/effects/common/TapSourceEffect.java index fcbc92c1e4c..e510ae4844c 100644 --- a/Mage/src/mage/abilities/effects/common/TapSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/TapSourceEffect.java @@ -1,36 +1,35 @@ /* -* 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. -*/ - + * 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.common; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; @@ -39,6 +38,7 @@ import mage.game.permanent.Permanent; * @author BetaSteward_at_googlemail.com */ public class TapSourceEffect extends OneShotEffect { + private boolean withoutTrigger; public TapSourceEffect() { @@ -64,6 +64,9 @@ public class TapSourceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null) { + permanent = game.getPermanentEntering(source.getSourceId()); + } if (permanent != null) { if (withoutTrigger) { permanent.setTapped(true); diff --git a/Mage/src/mage/abilities/effects/common/TapSourceUnlessPaysEffect.java b/Mage/src/mage/abilities/effects/common/TapSourceUnlessPaysEffect.java index 55bc3ad99ed..15fbc93dbff 100644 --- a/Mage/src/mage/abilities/effects/common/TapSourceUnlessPaysEffect.java +++ b/Mage/src/mage/abilities/effects/common/TapSourceUnlessPaysEffect.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,17 +20,17 @@ * 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.common; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.effects.OneShotEffect; +import mage.constants.AbilityType; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; @@ -59,7 +59,10 @@ public class TapSourceUnlessPaysEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getSourceId()); - if (player != null && permanent != null) { + if (permanent == null && source.getAbilityType().equals(AbilityType.STATIC)) { + permanent = game.getPermanentEntering(source.getSourceId()); + } + if (player != null && permanent != null) { if (cost.canPay(source, source.getSourceId(), source.getControllerId(), game) && player.chooseUse(Outcome.Benefit, cost.getText() + "? (otherwise " + permanent.getName() + " becomes tapped)", source, game)) { cost.clearPaid(); @@ -78,5 +81,4 @@ public class TapSourceUnlessPaysEffect extends OneShotEffect { return new TapSourceUnlessPaysEffect(this); } - } diff --git a/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java new file mode 100644 index 00000000000..ec697e3ba89 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByAllTargetEffect.java @@ -0,0 +1,51 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.effects.common.combat; + +import mage.abilities.Ability; +import mage.abilities.effects.RestrictionEffect; +import mage.constants.Duration; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class CantBeBlockedByAllTargetEffect extends RestrictionEffect { + + private final FilterCreaturePermanent filterBlockedBy; + + public CantBeBlockedByAllTargetEffect(FilterCreaturePermanent filterBlockedBy, Duration duration) { + super(Duration.WhileOnBattlefield); + this.filterBlockedBy = filterBlockedBy; + staticText = "Target creature" + + " can't be blocked " + + (filterBlockedBy.getMessage().startsWith("except by") ? "" : "by ") + + filterBlockedBy.getMessage(); + } + + public CantBeBlockedByAllTargetEffect(final CantBeBlockedByAllTargetEffect effect) { + super(effect); + this.filterBlockedBy = effect.filterBlockedBy; + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return getTargetPointer().getTargets(game, source).contains(permanent.getId()); + } + + @Override + public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) { + return !filterBlockedBy.match(blocker, source.getSourceId(), source.getControllerId(), game); + } + + @Override + public CantBeBlockedByAllTargetEffect copy() { + return new CantBeBlockedByAllTargetEffect(this); + } +} diff --git a/Mage/src/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java index 475a7f8d6b6..e4ed2543d96 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/AddCardSubTypeTargetEffect.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.continuous; import mage.abilities.Ability; @@ -42,7 +41,7 @@ import mage.game.permanent.Permanent; * @author nantuko */ public class AddCardSubTypeTargetEffect extends ContinuousEffectImpl { - + private final String addedSubType; public AddCardSubTypeTargetEffect(String addedSubType, Duration duration) { diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java index 41f4c112c21..536b87db161 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java @@ -36,7 +36,6 @@ import mage.abilities.mana.BlueManaAbility; import mage.abilities.mana.GreenManaAbility; import mage.abilities.mana.RedManaAbility; import mage.abilities.mana.WhiteManaAbility; -import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; @@ -46,6 +45,24 @@ import mage.game.permanent.Permanent; public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { + protected final static ArrayList allLandTypes = new ArrayList<>(); + + static { // 205.3i + allLandTypes.add("Forest"); + allLandTypes.add("Swamp"); + allLandTypes.add("Plains"); + allLandTypes.add("Mountains"); + allLandTypes.add("Island"); + allLandTypes.add("Urza's"); + allLandTypes.add("Mine"); + allLandTypes.add("Power-Plant"); + allLandTypes.add("Tower"); + allLandTypes.add("Desert"); + allLandTypes.add("Gate"); + allLandTypes.add("Lair"); + allLandTypes.add("Locus"); + } + protected ArrayList landTypes = new ArrayList<>(); public BecomesBasicLandEnchantedEffect(String... landNames) { @@ -76,13 +93,6 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { Permanent permanent = game.getPermanent(enchantment.getAttachedTo()); if (permanent != null) { switch (layer) { - case ColorChangingEffects_5: - permanent.getColor(game).setWhite(false); - permanent.getColor(game).setGreen(false); - permanent.getColor(game).setBlack(false); - permanent.getColor(game).setBlue(false); - permanent.getColor(game).setRed(false); - break; case AbilityAddingRemovingEffects_6: permanent.removeAllAbilities(source.getSourceId(), game); for (String landType : landTypes) { @@ -106,9 +116,8 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { } break; case TypeChangingEffects_4: - permanent.getCardType().clear(); - permanent.getCardType().add(CardType.LAND); - permanent.getSubtype().clear(); + // subtypes are all removed by changing the subtype to a land type. + permanent.getSubtype().removeAll(allLandTypes); permanent.getSubtype().addAll(landTypes); break; } diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java index dcdbe3ef9f2..c8fc6e2e6c2 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesColorOrColorsTargetEffect.java @@ -44,14 +44,14 @@ import mage.target.targetpointer.FixedTarget; * * @author LevelX2 */ - public class BecomesColorOrColorsTargetEffect extends OneShotEffect { Duration duration; + /** * This effect let the controller choose one or more colors the target will - * become to. - * Use effect.setText() if case you use a targetPointer, otherwise the rule text will be empty. + * become to. Use effect.setText() if case you use a targetPointer, + * otherwise the rule text will be empty. * * @param duration */ @@ -81,8 +81,9 @@ public class BecomesColorOrColorsTargetEffect extends OneShotEffect { if (!controller.canRespond()) { return false; } - if (!game.isSimulation()) + if (!game.isSimulation()) { game.informPlayers(target.getName() + ": " + controller.getLogName() + " has chosen " + choiceColor.getChoice()); + } if (choiceColor.getColor().isBlack()) { sb.append("B"); } else if (choiceColor.getColor().isBlue()) { @@ -119,7 +120,7 @@ public class BecomesColorOrColorsTargetEffect extends OneShotEffect { StringBuilder sb = new StringBuilder(); if (mode.getTargets().size() > 0) { sb.append("target "); - sb.append(mode.getTargets().get(0).getMessage()); + sb.append(mode.getTargets().get(0).getFilter().getMessage()); sb.append(" becomes the color or colors of your choice"); if (duration.toString().length() > 0) { sb.append(" ").append(duration.toString()); diff --git a/Mage/src/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java b/Mage/src/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java index c2cc74dd6e3..cc5e2c0ad7f 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/CommanderReplacementEffect.java @@ -108,6 +108,12 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl { } case GRAVEYARD: case EXILED: + if (((ZoneChangeEvent) event).getFromZone().equals(Zone.STACK)) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null && commanderId.equals(spell.getSourceId())) { + return true; + } + } if (commanderId.equals(event.getTargetId())) { return true; } diff --git a/Mage/src/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java b/Mage/src/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java index 81fc17c970c..609ad65d290 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/GainAbilitySourceEffect.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,7 +20,7 @@ * 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. @@ -88,12 +88,17 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl implements Sou public GainAbilitySourceEffect copy() { return new GainAbilitySourceEffect(this); } - + @Override public void init(Ability source, Game game) { super.init(source, game); if (affectedObjectsSet) { - affectedObjectList.add(new MageObjectReference(source.getSourceId(), game)); + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + if (permanent != null) { + affectedObjectList.add(new MageObjectReference(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()) + 1, game)); + } else { + affectedObjectList.add(new MageObjectReference(source.getSourceId(), game)); + } } } diff --git a/Mage/src/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java b/Mage/src/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java index 2c898399a06..785420e329c 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/LoseAbilitySourceEffect.java @@ -17,36 +17,36 @@ import mage.game.permanent.Permanent; * @author Noahsark */ public class LoseAbilitySourceEffect extends ContinuousEffectImpl{ - + protected Ability ability; - + public LoseAbilitySourceEffect(Ability ability){ this(ability, Duration.WhileOnBattlefield); } - + public LoseAbilitySourceEffect(Ability ability, Duration duration){ super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.LoseAbility); this.ability = ability; - staticText = "{this} loses " + ability.getRule() + duration.toString(); + staticText = "{this} loses " + ability.getRule() + " " + duration.toString(); } - + public LoseAbilitySourceEffect(final LoseAbilitySourceEffect effect){ super(effect); this.ability = effect.ability.copy(); } - + @Override public LoseAbilitySourceEffect copy(){ return new LoseAbilitySourceEffect(this); } - + @Override public boolean apply(Game game, Ability source){ Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null){ // 112.10 while (permanent.getAbilities().remove(ability)) { - + } } return true; diff --git a/Mage/src/mage/abilities/effects/common/continuous/SetPowerToughnessSourceEffect.java b/Mage/src/mage/abilities/effects/common/continuous/SetPowerToughnessSourceEffect.java index add455f4b14..1c2e3dbc87a 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/SetPowerToughnessSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/SetPowerToughnessSourceEffect.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,12 +20,11 @@ * 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.common.continuous; import mage.MageObject; @@ -37,7 +36,6 @@ import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; import mage.game.Game; -import mage.game.permanent.Permanent; /** * @@ -76,17 +74,18 @@ public class SetPowerToughnessSourceEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - MageObject mageObject = game.getObject(source.getSourceId()); + MageObject mageObject = game.getPermanentEntering(source.getSourceId()); if (mageObject == null) { - if (duration.equals(Duration.Custom)) { - discard(); - } - return false; - } else if (isTemporary()) { // it's somehow w - if (!(mageObject instanceof Permanent)) { - return false; + if (duration.equals(Duration.Custom) || isTemporary()) { + mageObject = game.getPermanent(source.getSourceId()); + } else { + mageObject = game.getObject(source.getSourceId()); } } + if (mageObject == null) { + discard(); + return true; + } if (amount != null) { int value = amount.calculate(game, source, this); mageObject.getPower().setValue(value); diff --git a/Mage/src/mage/abilities/effects/common/counter/AddCountersSourceEffect.java b/Mage/src/mage/abilities/effects/common/counter/AddCountersSourceEffect.java index 82c14c0cdb9..a39c2b7de8b 100644 --- a/Mage/src/mage/abilities/effects/common/counter/AddCountersSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/counter/AddCountersSourceEffect.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,20 +20,20 @@ * 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.common.counter; -import mage.constants.Outcome; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; +import mage.constants.AbilityType; +import mage.constants.Outcome; import mage.counters.Counter; import mage.game.Game; import mage.game.permanent.Permanent; @@ -63,11 +63,12 @@ public class AddCountersSourceEffect extends OneShotEffect { } /** - * + * * @param counter * @param amount this amount will be added to the counter instances * @param informPlayers - * @param putOnCard - counters have to be put on a card instead of a permanent + * @param putOnCard - counters have to be put on a card instead of a + * permanent */ public AddCountersSourceEffect(Counter counter, DynamicValue amount, boolean informPlayers, boolean putOnCard) { super(Outcome.Benefit); @@ -106,7 +107,7 @@ public class AddCountersSourceEffect extends OneShotEffect { if (informPlayers && !game.isSimulation()) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - game.informPlayers(new StringBuilder(player.getLogName()).append(" puts ").append(newCounter.getCount()).append(" ").append(newCounter.getName().toLowerCase()).append(" counter on ").append(card.getLogName()).toString()); + game.informPlayers(player.getLogName() + " puts " + newCounter.getCount() + " " + newCounter.getName().toLowerCase() + " counter on " + card.getLogName()); } } } @@ -114,6 +115,9 @@ public class AddCountersSourceEffect extends OneShotEffect { } } else { Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent == null && source.getAbilityType().equals(AbilityType.STATIC)) { + permanent = game.getPermanentEntering(source.getSourceId()); + } if (permanent != null) { if (counter != null) { Counter newCounter = counter.copy(); @@ -129,7 +133,7 @@ public class AddCountersSourceEffect extends OneShotEffect { int amountAdded = permanent.getCounters().getCount(newCounter.getName()) - before; Player player = game.getPlayer(source.getControllerId()); if (player != null) { - game.informPlayers(player.getLogName()+" puts "+amountAdded+" "+newCounter.getName().toLowerCase()+" counter on "+permanent.getLogName()); + game.informPlayers(player.getLogName() + " puts " + amountAdded + " " + newCounter.getName().toLowerCase() + " counter on " + permanent.getLogName()); } } } @@ -165,5 +169,4 @@ public class AddCountersSourceEffect extends OneShotEffect { return new AddCountersSourceEffect(this); } - } diff --git a/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java index 6776adcc8ea..de9c442f289 100644 --- a/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java +++ b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayEffect.java @@ -1,38 +1,37 @@ /* -* 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. -*/ - + * 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.common.search; import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.SearchEffect; -import mage.cards.Card; +import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; @@ -90,12 +89,8 @@ public class SearchLibraryPutInPlayEffect extends SearchEffect { } if (player.searchLibrary(target, game)) { if (target.getTargets().size() > 0) { - for (UUID cardId: target.getTargets()) { - Card card = player.getLibrary().getCard(cardId, game); - if (card != null) { - player.putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, source.getSourceId(), tapped); - } - } + player.moveCards(new CardsImpl(target.getTargets()).getCards(game), + Zone.BATTLEFIELD, source, game, tapped, false, false, null); } player.shuffleLibrary(game); return true; @@ -110,15 +105,13 @@ public class SearchLibraryPutInPlayEffect extends SearchEffect { StringBuilder sb = new StringBuilder(); sb.append("search your library for "); if (target.getNumberOfTargets() == 0 && target.getMaxNumberOfTargets() > 0) { - if ( target.getMaxNumberOfTargets() == Integer.MAX_VALUE ) { + if (target.getMaxNumberOfTargets() == Integer.MAX_VALUE) { sb.append("any number of ").append(" "); - } - else { + } else { sb.append("up to ").append(target.getMaxNumberOfTargets()).append(" "); } sb.append(target.getTargetName()).append(" and put them onto the battlefield"); - } - else { + } else { sb.append("a ").append(target.getTargetName()).append(" and put it onto the battlefield"); } if (tapped) { @@ -126,8 +119,7 @@ public class SearchLibraryPutInPlayEffect extends SearchEffect { } if (forceShuffle) { sb.append(". Then shuffle your library"); - } - else { + } else { sb.append(". If you do, shuffle your library"); } staticText = sb.toString(); diff --git a/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java index 74cf05fbe9f..8c68601cb38 100644 --- a/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java +++ b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutInPlayTargetPlayerEffect.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.SearchEffect; -import mage.cards.Card; +import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; @@ -20,11 +20,11 @@ import mage.target.common.TargetCardInLibrary; * * @author LevelX2 */ - public class SearchLibraryPutInPlayTargetPlayerEffect extends SearchEffect { protected boolean tapped; protected boolean forceShuffle; + protected boolean ownerIsController; public SearchLibraryPutInPlayTargetPlayerEffect(TargetCardInLibrary target) { this(target, false, true, Outcome.PutCardInPlay); @@ -43,9 +43,14 @@ public class SearchLibraryPutInPlayTargetPlayerEffect extends SearchEffect { } public SearchLibraryPutInPlayTargetPlayerEffect(TargetCardInLibrary target, boolean tapped, boolean forceShuffle, Outcome outcome) { + this(target, tapped, forceShuffle, outcome, false); + } + + public SearchLibraryPutInPlayTargetPlayerEffect(TargetCardInLibrary target, boolean tapped, boolean forceShuffle, Outcome outcome, boolean ownerIsController) { super(target, outcome); this.tapped = tapped; this.forceShuffle = forceShuffle; + this.ownerIsController = ownerIsController; setText(); } @@ -53,6 +58,7 @@ public class SearchLibraryPutInPlayTargetPlayerEffect extends SearchEffect { super(effect); this.tapped = effect.tapped; this.forceShuffle = effect.forceShuffle; + this.ownerIsController = effect.ownerIsController; } @Override @@ -62,26 +68,22 @@ public class SearchLibraryPutInPlayTargetPlayerEffect extends SearchEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); if (player != null) { if (player.searchLibrary(target, game)) { if (target.getTargets().size() > 0) { - for (UUID cardId: target.getTargets()) { - Card card = player.getLibrary().getCard(cardId, game); - if (card != null) { - player.putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, source.getSourceId(), tapped); - } - } + player.moveCards(new CardsImpl(target.getTargets()).getCards(game), + Zone.BATTLEFIELD, source, game, tapped, false, ownerIsController, null); } player.shuffleLibrary(game); return true; } - + if (forceShuffle) { player.shuffleLibrary(game); } } - + return false; } @@ -89,15 +91,13 @@ public class SearchLibraryPutInPlayTargetPlayerEffect extends SearchEffect { StringBuilder sb = new StringBuilder(); sb.append("target player searches his or her library for "); if (target.getNumberOfTargets() == 0 && target.getMaxNumberOfTargets() > 0) { - if ( target.getMaxNumberOfTargets() == Integer.MAX_VALUE ) { + if (target.getMaxNumberOfTargets() == Integer.MAX_VALUE) { sb.append("any number of ").append(" "); - } - else { + } else { sb.append("up to ").append(target.getMaxNumberOfTargets()).append(" "); } sb.append(target.getTargetName()).append(" and put them onto the battlefield"); - } - else { + } else { sb.append("a ").append(target.getTargetName()).append(" and put it onto the battlefield"); } if (tapped) { @@ -105,8 +105,7 @@ public class SearchLibraryPutInPlayTargetPlayerEffect extends SearchEffect { } if (forceShuffle) { sb.append(". Then that player shuffles his or her library"); - } - else { + } else { sb.append(". If that player does, he or she shuffles his or her library"); } staticText = sb.toString(); diff --git a/Mage/src/mage/abilities/effects/common/search/SearchTargetGraveyardHandLibraryForCardNameAndExileEffect.java b/Mage/src/mage/abilities/effects/common/search/SearchTargetGraveyardHandLibraryForCardNameAndExileEffect.java index dd9c3c94e60..26a88824f6c 100644 --- a/Mage/src/mage/abilities/effects/common/search/SearchTargetGraveyardHandLibraryForCardNameAndExileEffect.java +++ b/Mage/src/mage/abilities/effects/common/search/SearchTargetGraveyardHandLibraryForCardNameAndExileEffect.java @@ -27,12 +27,10 @@ */ package mage.abilities.effects.common.search; -import java.util.List; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.Outcome; @@ -98,14 +96,7 @@ public abstract class SearchTargetGraveyardHandLibraryForCardNameAndExileEffect filter.setMessage("card named " + cardName + " in the graveyard of " + targetPlayer.getLogName()); TargetCard target = new TargetCard((graveyardExileOptional ? 0 : cardsCount), cardsCount, Zone.GRAVEYARD, filter); if (controller.choose(Outcome.Exile, targetPlayer.getGraveyard(), target, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card targetCard = targetPlayer.getGraveyard().get(targetId, game); - if (targetCard != null) { - targetPlayer.getGraveyard().remove(targetCard); - controller.moveCardToExileWithInfo(targetCard, null, null, source.getSourceId(), game, Zone.GRAVEYARD, true); - } - } + controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); } } @@ -114,14 +105,7 @@ public abstract class SearchTargetGraveyardHandLibraryForCardNameAndExileEffect filter.setMessage("card named " + cardName + " in the hand of " + targetPlayer.getLogName()); TargetCard target = new TargetCard(0, cardsCount, Zone.HAND, filter); if (controller.choose(Outcome.Exile, targetPlayer.getHand(), target, game)) { - List targets = target.getTargets(); - for (UUID targetId : targets) { - Card targetCard = targetPlayer.getHand().get(targetId, game); - if (targetCard != null) { - targetPlayer.getHand().remove(targetCard); - controller.moveCardToExileWithInfo(targetCard, null, null, source.getSourceId(), game, Zone.HAND, true); - } - } + controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game); } // cards in Library @@ -131,15 +115,8 @@ public abstract class SearchTargetGraveyardHandLibraryForCardNameAndExileEffect filter.setMessage("card named " + cardName + " in the library of " + targetPlayer.getLogName()); TargetCardInLibrary targetLib = new TargetCardInLibrary(0, cardsCount, filter); if (controller.choose(Outcome.Exile, cardsInLibrary, targetLib, game)) { - List targets = targetLib.getTargets(); - for (UUID targetId : targets) { - Card targetCard = targetPlayer.getLibrary().remove(targetId, game); - if (targetCard != null) { - controller.moveCardToExileWithInfo(targetCard, null, null, source.getSourceId(), game, Zone.LIBRARY, true); - } - } + controller.moveCards(new CardsImpl(targetLib.getTargets()), Zone.EXILED, source, game); } - targetPlayer.shuffleLibrary(game); } diff --git a/Mage/src/mage/abilities/effects/keyword/ManifestEffect.java b/Mage/src/mage/abilities/effects/keyword/ManifestEffect.java index 71e6293628d..0640fc8ed85 100644 --- a/Mage/src/mage/abilities/effects/keyword/ManifestEffect.java +++ b/Mage/src/mage/abilities/effects/keyword/ManifestEffect.java @@ -86,13 +86,15 @@ public class ManifestEffect extends OneShotEffect { } MageObjectReference objectReference = new MageObjectReference(card.getId(), card.getZoneChangeCounter(game) + 1, game); game.addEffect(new BecomesFaceDownCreatureEffect(manaCosts, objectReference, Duration.Custom, FaceDownType.MANIFESTED), newSource); - controller.putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, newSource.getSourceId(), false, true); + + } + controller.moveCards(cards, Zone.BATTLEFIELD, source, game, false, true, false, null); + for (Card card : cards) { Permanent permanent = game.getPermanent(card.getId()); if (permanent != null) { permanent.setManifested(true); } } - game.applyEffects(); // to apply before ETB triggered or replace Effects are executed return true; } return false; diff --git a/Mage/src/mage/abilities/effects/keyword/ManifestTargetPlayerEffect.java b/Mage/src/mage/abilities/effects/keyword/ManifestTargetPlayerEffect.java index 897ff83f235..2e904878769 100644 --- a/Mage/src/mage/abilities/effects/keyword/ManifestTargetPlayerEffect.java +++ b/Mage/src/mage/abilities/effects/keyword/ManifestTargetPlayerEffect.java @@ -89,7 +89,9 @@ public class ManifestTargetPlayerEffect extends OneShotEffect { } MageObjectReference objectReference = new MageObjectReference(card.getId(), card.getZoneChangeCounter(game) + 1, game); game.addEffect(new BecomesFaceDownCreatureEffect(manaCosts, objectReference, Duration.Custom, FaceDownType.MANIFESTED), newSource); - targetPlayer.putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, newSource.getSourceId(), false, true); + } + targetPlayer.moveCards(cards, Zone.BATTLEFIELD, source, game, false, true, false, null); + for (Card card : cards) { Permanent permanent = game.getPermanent(card.getId()); if (permanent != null) { permanent.setManifested(true); diff --git a/Mage/src/mage/abilities/keyword/AmplifyAbility.java b/Mage/src/mage/abilities/keyword/AmplifyAbility.java index 22a427c8402..a9cc43d60fc 100644 --- a/Mage/src/mage/abilities/keyword/AmplifyAbility.java +++ b/Mage/src/mage/abilities/keyword/AmplifyAbility.java @@ -39,13 +39,13 @@ import mage.constants.Zone; public class AmplifyAbility extends SimpleStaticAbility { public AmplifyAbility(AmplifyFactor amplifyFactor) { - super(Zone.BATTLEFIELD, new AmplifyEffect(amplifyFactor)); + super(Zone.ALL, new AmplifyEffect(amplifyFactor)); } - + public AmplifyAbility(final AmplifyAbility ability) { super(ability); } - + @Override public AmplifyAbility copy() { return new AmplifyAbility(this); diff --git a/Mage/src/mage/abilities/keyword/AuraSwapAbility.java b/Mage/src/mage/abilities/keyword/AuraSwapAbility.java index ea4ddd1a6aa..385703b6691 100644 --- a/Mage/src/mage/abilities/keyword/AuraSwapAbility.java +++ b/Mage/src/mage/abilities/keyword/AuraSwapAbility.java @@ -104,9 +104,9 @@ class AuraSwapEffect extends OneShotEffect { if (controller.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game)) { Card auraInHand = game.getCard(target.getFirstTarget()); if (auraInHand != null) { - controller.putOntoBattlefieldWithInfo(auraInHand, game, Zone.HAND, source.getSourceId()); + controller.moveCards(auraInHand, Zone.BATTLEFIELD, source, game); enchantedPermanent.addAttachment(auraInHand.getId(), game); - controller.moveCards(auraPermanent, null, Zone.HAND, source, game); + controller.moveCards(auraPermanent, Zone.HAND, source, game); return true; } } diff --git a/Mage/src/mage/abilities/keyword/BloodthirstAbility.java b/Mage/src/mage/abilities/keyword/BloodthirstAbility.java index f93ca9824d6..0b8f4d29c61 100644 --- a/Mage/src/mage/abilities/keyword/BloodthirstAbility.java +++ b/Mage/src/mage/abilities/keyword/BloodthirstAbility.java @@ -12,10 +12,11 @@ import mage.util.CardUtil; import mage.watchers.common.BloodthirstWatcher; /** - * + * * @author Loki */ public class BloodthirstAbility extends EntersBattlefieldAbility { + private int amount; public BloodthirstAbility(int amount) { @@ -48,12 +49,13 @@ public class BloodthirstAbility extends EntersBattlefieldAbility { } class BloodthirstEffect extends OneShotEffect { + private final int amount; BloodthirstEffect(int amount) { super(Outcome.BoostCreature); this.amount = amount; - staticText = new StringBuilder("this permanent comes into play with ").append(this.amount).append(" +1/+1 counters on it").toString(); + staticText = "this permanent comes into play with " + this.amount + " +1/+1 counters on it"; } BloodthirstEffect(final BloodthirstEffect effect) { @@ -67,10 +69,9 @@ class BloodthirstEffect extends OneShotEffect { if (player != null) { BloodthirstWatcher watcher = (BloodthirstWatcher) game.getState().getWatchers().get("DamagedOpponents", source.getControllerId()); if (watcher != null && watcher.conditionMet()) { - Permanent p = game.getPermanent(source.getSourceId()); - if (p != null) { - p.addCounters(CounterType.P1P1.createInstance(amount), game); - + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(amount), game); } } return true; @@ -83,4 +84,3 @@ class BloodthirstEffect extends OneShotEffect { return new BloodthirstEffect(this); } } - diff --git a/Mage/src/mage/abilities/keyword/BuybackAbility.java b/Mage/src/mage/abilities/keyword/BuybackAbility.java index 05059e4e53c..f35c32c874f 100644 --- a/Mage/src/mage/abilities/keyword/BuybackAbility.java +++ b/Mage/src/mage/abilities/keyword/BuybackAbility.java @@ -63,8 +63,8 @@ import mage.players.Player; public class BuybackAbility extends StaticAbility implements OptionalAdditionalSourceCosts { private static final String keywordText = "Buyback"; - private static final String reminderTextCost = "(You may {cost} in addition to any other costs as you cast this spell. If you do, put this card into your hand as it resolves.)"; - private static final String reminderTextMana = "(You may pay an additional {cost} as you cast this spell. If you do, put this card into your hand as it resolves.)"; + private static final String reminderTextCost = "You may {cost} in addition to any other costs as you cast this spell. If you do, put this card into your hand as it resolves."; + private static final String reminderTextMana = "You may pay an additional {cost} as you cast this spell. If you do, put this card into your hand as it resolves."; protected OptionalAdditionalCost buybackCost; public BuybackAbility(String manaString) { diff --git a/Mage/src/mage/abilities/keyword/ConspireAbility.java b/Mage/src/mage/abilities/keyword/ConspireAbility.java index fe470c425fd..a655b31721d 100644 --- a/Mage/src/mage/abilities/keyword/ConspireAbility.java +++ b/Mage/src/mage/abilities/keyword/ConspireAbility.java @@ -27,14 +27,16 @@ */ package mage.abilities.keyword; +import java.util.HashSet; import java.util.Iterator; +import java.util.Set; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.Cost; import mage.abilities.costs.Costs; -import mage.abilities.costs.OptionalAdditionalCost; import mage.abilities.costs.OptionalAdditionalCostImpl; import mage.abilities.costs.OptionalAdditionalSourceCosts; import mage.abilities.costs.common.TapTargetCost; @@ -50,48 +52,78 @@ import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.common.TargetControlledPermanent; -/** - * 702.77. Conspire 702.77a Conspire is a keyword that represents two abilities. - * The first is a static ability that functions while the spell with conspire is - * on the stack. The second is a triggered ability that functions while the - * spell with conspire is on the stack. "Conspire" means "As an additional cost - * to cast this spell, you may tap two untapped creatures you control that each - * share a color with it" and "When you cast this spell, if its conspire cost - * was paid, copy it. If the spell has any targets, you may choose new targets - * for the copy." Paying a spell’s conspire cost follows the rules for paying - * additional costs in rules 601.2b and 601.2e–g. 702.77b If a spell has - * multiple instances of conspire, each is paid separately and triggers based on - * its own payment, not any other instance of conspire. * +/* + * 702.77. Conspire + * 702.77a Conspire is a keyword that represents two abilities. + * The first is a static ability that functions while the spell with conspire is on the stack. + * The second is a triggered ability that functions while the spell with conspire is on the stack. + * "Conspire" means "As an additional cost to cast this spell, + * you may tap two untapped creatures you control that each share a color with it" + * and "When you cast this spell, if its conspire cost was paid, copy it. + * If the spell has any targets, you may choose new targets for the copy." + * Paying a spell’s conspire cost follows the rules for paying additional costs in rules 601.2b and 601.2e–g. + * 702.77b If a spell has multiple instances of conspire, each is paid separately and triggers + * based on its own payment, not any other instance of conspire. * * - * @author jeffwadsworth heavily based off the replicate keyword by LevelX + * @author LevelX */ public class ConspireAbility extends StaticAbility implements OptionalAdditionalSourceCosts { private static final String keywordText = "Conspire"; - private static final String reminderTextCost = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)"; - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("two untapped creatures you control that share a color with it"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatures you control that share a color with it"); + protected static final String CONSPIRE_ACTIVATION_KEY = "ConspireActivation"; static { filter.add(Predicates.not(new TappedPredicate())); filter.add(new SharesColorWithSourcePredicate()); } - Cost costConspire = new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true)); - OptionalAdditionalCost conspireCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderTextCost, costConspire); + public enum ConspireTargets { - public ConspireAbility(Card card) { + NONE, + ONE, + MORE + } + + private UUID conspireId; + private String reminderText; + private OptionalAdditionalCostImpl conspireCost; + + /** + * Unique Id for a ConspireAbility but may not change while a continuous + * effect gives Conspire + * + * @param conspireId + * @param conspireTargets controls the content of the reminder text + */ + public ConspireAbility(UUID conspireId, ConspireTargets conspireTargets) { super(Zone.STACK, null); - setRuleAtTheTop(false); - addSubAbility(new ConspireTriggeredAbility()); + this.conspireId = conspireId; + switch (conspireTargets) { + case NONE: + reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it.)"; + break; + case ONE: + reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)"; + break; + case MORE: + reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new targets for the copy.)"; + break; + } + Cost cost = new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true)); + cost.setText(""); + conspireCost = new OptionalAdditionalCostImpl(keywordText, " ", reminderText, cost); + addSubAbility(new ConspireTriggeredAbility(conspireId)); } public ConspireAbility(final ConspireAbility ability) { super(ability); - conspireCost = ability.conspireCost; + this.conspireId = ability.conspireId; + this.conspireCost = ability.conspireCost.copy(); + this.reminderText = ability.reminderText; } @Override @@ -106,28 +138,32 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional } } - @Override - public boolean isActivated() { - if (conspireCost != null) { - return conspireCost.isActivated(); - } - return false; + public UUID getConspireId() { + return conspireId; } - public void resetConspire() { - if (conspireCost != null) { - conspireCost.reset(); + @Override + public boolean isActivated() { + throw new UnsupportedOperationException("Use ConspireAbility.isActivated(Ability ability, Game game) method instead!"); + } + + public boolean isActivated(Ability ability, Game game) { + Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); + if (activations != null) { + return activations.contains(getConspireId()); } + return false; } @Override public void addOptionalAdditionalCosts(Ability ability, Game game) { if (ability instanceof SpellAbility) { - Player player = game.getPlayer(controllerId); + Player player = game.getPlayer(getControllerId()); if (player != null) { - this.resetConspire(); - if (player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(conspireCost.getText(false)).append(" ?").toString(), ability, game)) { - conspireCost.activate(); + resetConspire(ability, game); + if (conspireCost.canPay(ability, getSourceId(), getControllerId(), game) + && player.chooseUse(Outcome.Benefit, "Pay " + conspireCost.getText(false) + " ?", ability, game)) { + activateConspire(ability, game); for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext();) { Cost cost = (Cost) it.next(); ability.getCosts().add(cost.copy()); @@ -137,6 +173,22 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional } } + private void activateConspire(Ability ability, Game game) { + Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); + if (activations == null) { + activations = new HashSet<>(); + game.getState().setValue(CONSPIRE_ACTIVATION_KEY + ability.getId(), activations); + } + activations.add(getConspireId()); + } + + private void resetConspire(Ability ability, Game game) { + Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); + if (activations != null) { + activations.remove(getConspireId()); + } + } + @Override public String getRule() { StringBuilder sb = new StringBuilder(); @@ -167,13 +219,17 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional class ConspireTriggeredAbility extends TriggeredAbilityImpl { - public ConspireTriggeredAbility() { + private UUID conspireId; + + public ConspireTriggeredAbility(UUID conspireId) { super(Zone.STACK, new ConspireEffect()); + this.conspireId = conspireId; this.setRuleVisible(false); } private ConspireTriggeredAbility(final ConspireTriggeredAbility ability) { super(ability); + this.conspireId = ability.conspireId; } @Override @@ -188,20 +244,18 @@ class ConspireTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.sourceId)) { - StackObject spell = game.getStack().getStackObject(this.sourceId); - if (spell instanceof Spell) { - Card card = game.getCard(spell.getSourceId()); - if (card != null) { - for (Ability ability : card.getAbilities()) { - if (ability instanceof ConspireAbility) { - if (((ConspireAbility) ability).isActivated()) { - for (Effect effect : this.getEffects()) { - effect.setValue("ConspireSpell", spell); - } - return true; + if (event.getSourceId().equals(getSourceId())) { + Spell spell = game.getStack().getSpell(event.getSourceId()); + for (Ability ability : spell.getAbilities(game)) { + if (ability instanceof ConspireAbility + && ((ConspireAbility) ability).getConspireId().equals(getConspireId())) { + if (((ConspireAbility) ability).isActivated(spell.getSpellAbility(), game)) { + for (Effect effect : this.getEffects()) { + if (effect instanceof ConspireEffect) { + ((ConspireEffect) effect).setConspiredSpell(spell); } } + return true; } } } @@ -209,52 +263,53 @@ class ConspireTriggeredAbility extends TriggeredAbilityImpl { return false; } + public UUID getConspireId() { + return conspireId; + } + @Override public String getRule() { - return "Conspire: As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)"; + return "When you pay the conspire costs, copy it and you may choose a new target for the copy."; } } class ConspireEffect extends OneShotEffect { + private Spell conspiredSpell; + public ConspireEffect() { super(Outcome.Copy); } public ConspireEffect(final ConspireEffect effect) { super(effect); + this.conspiredSpell = effect.conspiredSpell; } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Spell spell = (Spell) this.getValue("ConspireSpell"); - if (spell != null) { - Card card = game.getCard(spell.getSourceId()); - if (card != null) { - for (Ability ability : card.getAbilities()) { - if (ability instanceof ConspireAbility) { - if (((ConspireAbility) ability).isActivated()) { - ((ConspireAbility) ability).resetConspire(); - } - } - } - Spell copy = spell.copySpell(); - copy.setControllerId(source.getControllerId()); - copy.setCopiedSpell(true); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - if (!game.isSimulation()) { - game.informPlayers(new StringBuilder(controller.getLogName()).append(copy.getActivatedMessage(game)).toString()); - } - return true; + if (controller != null && conspiredSpell != null) { + Card card = game.getCard(conspiredSpell.getSourceId()); + if (card != null) { + Spell copy = conspiredSpell.copySpell(); + copy.setControllerId(source.getControllerId()); + copy.setCopiedSpell(true); + game.getStack().push(copy); + copy.chooseNewTargets(game, source.getControllerId()); + if (!game.isSimulation()) { + game.informPlayers(controller.getLogName() + copy.getActivatedMessage(game)); } + return true; } } return false; } + public void setConspiredSpell(Spell conspiredSpell) { + this.conspiredSpell = conspiredSpell; + } + @Override public ConspireEffect copy() { return new ConspireEffect(this); diff --git a/Mage/src/mage/abilities/keyword/CumulativeUpkeepAbility.java b/Mage/src/mage/abilities/keyword/CumulativeUpkeepAbility.java index 199c8536108..2e6c6d83fca 100644 --- a/Mage/src/mage/abilities/keyword/CumulativeUpkeepAbility.java +++ b/Mage/src/mage/abilities/keyword/CumulativeUpkeepAbility.java @@ -25,8 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - - package mage.abilities.keyword; import mage.abilities.Ability; @@ -41,6 +39,8 @@ import mage.constants.Outcome; import mage.constants.TargetController; import mage.counters.CounterType; import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; @@ -48,12 +48,10 @@ import mage.players.Player; * * @author Plopman */ - - public class CumulativeUpkeepAbility extends BeginningOfUpkeepTriggeredAbility { - + private Cost cumulativeCost; - + public CumulativeUpkeepAbility(Cost cumulativeCost) { super(new AddCountersSourceEffect(CounterType.AGE.createInstance()), TargetController.YOU, false); this.addEffect(new CumulativeUpkeepEffect(cumulativeCost)); @@ -82,9 +80,9 @@ public class CumulativeUpkeepAbility extends BeginningOfUpkeepTriggeredAbility { } class CumulativeUpkeepEffect extends OneShotEffect { - - private Cost cumulativeCost; - + + private final Cost cumulativeCost; + CumulativeUpkeepEffect(Cost cumulativeCost) { super(Outcome.Sacrifice); this.cumulativeCost = cumulativeCost; @@ -95,46 +93,46 @@ class CumulativeUpkeepEffect extends OneShotEffect { this.cumulativeCost = effect.cumulativeCost.copy(); } - @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getSourceId()); - if (player != null && permanent != null) { + if (player != null && permanent != null) { int ageCounter = permanent.getCounters().getCount(CounterType.AGE); - if(cumulativeCost instanceof ManaCost){ - ManaCostsImpl totalCost = new ManaCostsImpl(); - for(int i = 0 ; i < ageCounter; i++){ + if (cumulativeCost instanceof ManaCost) { + ManaCostsImpl totalCost = new ManaCostsImpl<>(); + for (int i = 0; i < ageCounter; i++) { totalCost.add((ManaCost) cumulativeCost.copy()); } if (player.chooseUse(Outcome.Benefit, "Pay " + totalCost.getText() + "?", source, game)) { totalCost.clearPaid(); - if (totalCost.payOrRollback(source, game, source.getSourceId(), source.getControllerId())){ + if (totalCost.payOrRollback(source, game, source.getSourceId(), source.getControllerId())) { + game.fireEvent(new GameEvent(EventType.PAID_CUMULATIVE_UPKEEP, permanent.getId(), permanent.getId(), player.getId(), ageCounter, false)); return true; } } + game.fireEvent(new GameEvent(EventType.DIDNT_PAY_CUMULATIVE_UPKEEP, permanent.getId(), permanent.getId(), player.getId(), ageCounter, false)); permanent.sacrifice(source.getSourceId(), game); - return true; - } - else{ - CostsImpl totalCost = new CostsImpl(); - for(int i = 0 ; i < ageCounter; i++){ + return true; + } else { + CostsImpl totalCost = new CostsImpl<>(); + for (int i = 0; i < ageCounter; i++) { totalCost.add(cumulativeCost.copy()); } if (player.chooseUse(Outcome.Benefit, totalCost.getText() + "?", source, game)) { totalCost.clearPaid(); int bookmark = game.bookmarkState(); - if (totalCost.pay(source, game, source.getSourceId(), source.getControllerId(), false)){ + if (totalCost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { + game.fireEvent(new GameEvent(EventType.PAID_CUMULATIVE_UPKEEP, permanent.getId(), permanent.getId(), player.getId(), ageCounter, false)); return true; - } - else{ + } else { game.restoreState(bookmark, source.getRule()); } } + game.fireEvent(new GameEvent(EventType.DIDNT_PAY_CUMULATIVE_UPKEEP, permanent.getId(), permanent.getId(), player.getId(), ageCounter, false)); permanent.sacrifice(source.getSourceId(), game); - return true; + return true; } - } return false; } diff --git a/Mage/src/mage/abilities/keyword/DashAbility.java b/Mage/src/mage/abilities/keyword/DashAbility.java index 4a2d96170f0..b6a876053b0 100644 --- a/Mage/src/mage/abilities/keyword/DashAbility.java +++ b/Mage/src/mage/abilities/keyword/DashAbility.java @@ -76,10 +76,10 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts this.addDashCost(manaString); Ability ability = new EntersBattlefieldAbility( new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.Custom, false), - DashedCondition.getInstance(), false, "", ""); + DashedCondition.getInstance(), "", ""); ability.addEffect(new DashAddDelayedTriggeredAbilityEffect()); addSubAbility(ability); - + } public DashAbility(final DashAbility ability) { @@ -226,16 +226,18 @@ class DashAddDelayedTriggeredAbilityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Effect effect = new ReturnToHandTargetEffect(); - effect.setText("return the dashed creature from the battlefield to its owner's hand"); - effect.setTargetPointer(new FixedTarget(source.getSourceId())); - // init target pointer now because the dashed creature will only be returned from current zone - effect.getTargetPointer().init(game, source); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); + if (game.getPermanentEntering(source.getSourceId()) != null) { + Effect effect = new ReturnToHandTargetEffect(); + effect.setText("return the dashed creature from the battlefield to its owner's hand"); + // init target pointer now because the dashed creature will only be returned from battlefield zone (now in entering state so zone change counter is not raised yet) + effect.setTargetPointer(new FixedTarget(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()) + 1)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + return true; + } return false; } } diff --git a/Mage/src/mage/abilities/keyword/DevourAbility.java b/Mage/src/mage/abilities/keyword/DevourAbility.java index 1d4cdcfa822..ec3148a990a 100644 --- a/Mage/src/mage/abilities/keyword/DevourAbility.java +++ b/Mage/src/mage/abilities/keyword/DevourAbility.java @@ -27,51 +27,51 @@ */ package mage.abilities.keyword; -import mage.constants.Zone; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DevourEffect; import mage.abilities.effects.common.DevourEffect.DevourFactor; +import mage.constants.Zone; /** * 502.82. Devour * - * 502.82a Devour is a static ability. "Devour N" means "As this object comes into play, - * you may sacrifice any number of creatures. This permanent comes into play with N +1/+1 - * counters on it for each creature sacrificed this way." + * 502.82a Devour is a static ability. "Devour N" means "As this object comes + * into play, you may sacrifice any number of creatures. This permanent comes + * into play with N +1/+1 counters on it for each creature sacrificed this way." * - * 502.82b Some objects have abilities that refer to the number of creatures the permanent - * devoured. "It devoured" means "sacrificed as a result of its devour ability as it came - * into play." + * 502.82b Some objects have abilities that refer to the number of creatures the + * permanent devoured. "It devoured" means "sacrificed as a result of its devour + * ability as it came into play." * * Devour appears only on creature cards. * - * A creature with devour can devour other creatures no matter how it comes into play. + * A creature with devour can devour other creatures no matter how it comes into + * play. * * You may choose to not sacrifice any creatures. * - * If you play a creature with devour as a spell, you choose how many and which creatures - * to devour as part of the resolution of that spell. (It can't be countered at this point.) - * The same is true of a spell or ability that lets you put a creature with devour into play. + * If you play a creature with devour as a spell, you choose how many and which + * creatures to devour as part of the resolution of that spell. (It can't be + * countered at this point.) The same is true of a spell or ability that lets + * you put a creature with devour into play. * - * You may sacrifice only creatures that are already in play. If a creature with devour and - * another creature are coming into play under your control at the same time, the creature - * with devour can't devour that other creature. The creature with devour also can't devour - * itself. + * You may sacrifice only creatures that are already in play. If a creature with + * devour and another creature are coming into play under your control at the + * same time, the creature with devour can't devour that other creature. The + * creature with devour also can't devour itself. * - * If multiple creatures with devour are coming into play under your control at the same time, - * you may use each one's devour ability. A creature you already control can be devoured by - * only one of them, however. (In other words, you can't sacrifice the same creature to satisfy - * multiple devour abilities.) All creatures devoured this way are sacrificed at the same time. + * If multiple creatures with devour are coming into play under your control at + * the same time, you may use each one's devour ability. A creature you already + * control can be devoured by only one of them, however. (In other words, you + * can't sacrifice the same creature to satisfy multiple devour abilities.) All + * creatures devoured this way are sacrificed at the same time. * * @author LevelX2 */ +public class DevourAbility extends SimpleStaticAbility { - public class DevourAbility extends SimpleStaticAbility { - - - - public DevourAbility(DevourFactor devourFactor) { - super(Zone.BATTLEFIELD, new DevourEffect(devourFactor)); + public DevourAbility(DevourFactor devourFactor) { + super(Zone.ALL, new DevourEffect(devourFactor)); } public DevourAbility(final DevourAbility ability) { @@ -82,4 +82,4 @@ import mage.abilities.effects.common.DevourEffect.DevourFactor; public DevourAbility copy() { return new DevourAbility(this); } -} \ No newline at end of file +} diff --git a/Mage/src/mage/abilities/keyword/EntwineAbility.java b/Mage/src/mage/abilities/keyword/EntwineAbility.java index 8448463486c..0604445c2a4 100644 --- a/Mage/src/mage/abilities/keyword/EntwineAbility.java +++ b/Mage/src/mage/abilities/keyword/EntwineAbility.java @@ -59,7 +59,7 @@ import mage.players.Player; public class EntwineAbility extends StaticAbility implements OptionalAdditionalModeSourceCosts { private static final String keywordText = "Entwine"; - private static final String reminderText = " (Choose both if you pay the entwine cost.)"; + private static final String reminderText = "Choose both if you pay the entwine cost."; protected OptionalAdditionalCost additionalCost; public EntwineAbility(String manaString) { @@ -111,7 +111,7 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM if (player != null) { this.resetCosts(); if (additionalCost != null) { - if (player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) { + if (player.chooseUse(Outcome.Benefit, "Pay " + additionalCost.getText(false) + " ?", ability, game)) { additionalCost.activate(); for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) { Cost cost = (Cost) it.next(); diff --git a/Mage/src/mage/abilities/keyword/EvolveAbility.java b/Mage/src/mage/abilities/keyword/EvolveAbility.java index 16c1b316e01..00712d973c8 100644 --- a/Mage/src/mage/abilities/keyword/EvolveAbility.java +++ b/Mage/src/mage/abilities/keyword/EvolveAbility.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.keyword; import mage.abilities.Ability; @@ -44,46 +43,53 @@ import mage.target.targetpointer.FixedTarget; * FAQ 2013/01/11 * * 702.98. Evolve - * - * 702.98a Evolve is a triggered ability. "Evolve" means "Whenever a creature enters - * the battlefield under your control, if that creature's power is greater than this - * creature's power and/or that creature's toughness is greater than this creature's - * toughness, put a +1/+1 counter on this creature." * - * 702.98b If a creature has multiple instances of evolve, each triggers separately - * + * 702.98a Evolve is a triggered ability. "Evolve" means "Whenever a creature + * enters the battlefield under your control, if that creature's power is + * greater than this creature's power and/or that creature's toughness is + * greater than this creature's toughness, put a +1/+1 counter on this + * creature." + * + * 702.98b If a creature has multiple instances of evolve, each triggers + * separately + * * Rulings - * - * When comparing the stats of the two creatures, you always compare power to power and toughness to toughness. - * Whenever a creature enters the battlefield under your control, check its power and toughness against - * the power and toughness of the creature with evolve. If neither stat of the new creature is greater, - * evolve won't trigger at all. For example, if you control a 2/3 creature with evolve and a 2/2 creature - * enters the battlefield under your control, you won't have the opportunity to cast a spell like Giant Growth - * to make the 2/2 creature large enough to cause evolve to trigger. - * If evolve triggers, the stat comparison will happen again when the ability tries to resolve. If - * neither stat of the new creature is greater, the ability will do nothing. If the creature that - * entered the battlefield leaves the battlefield before evolve tries to resolve, use its last known - * power and toughness to compare the stats. - * If a creature enters the battlefield with +1/+1 counters on it, consider those counters when determining - * if evolve will trigger. For example, a 1/1 creature that enters the battlefield with two +1/+1 counters - * on it will cause the evolve ability of a 2/2 creature to trigger. - * If multiple creatures enter the battlefield at the same time, evolve may trigger multiple times, although the stat - * comparison will take place each time one of those abilities tries to resolve. For example, if you control a 2/2 - * creature with evolve and two 3/3 creatures enter the battlefield, evolve will trigger twice. The first ability - * will resolve and put a +1/+1 counter on the creature with evolve. When the second ability tries to resolve, - * neither the power nor the toughness of the new creature is greater than that of the creature with evolve, - * so that ability does nothing. - * When comparing the stats as the evolve ability resolves, it's possible that the stat that's greater changes - * from power to toughness or vice versa. If this happens, the ability will still resolve and you'll put a +1/+1 - * counter on the creature with evolve. For example, if you control a 2/2 creature with evolve and a 1/3 creature - * enters the battlefield under your control, it toughness is greater so evolve will trigger. In response, the 1/3 - * creature gets +2/-2. When the evolve trigger tries to resolve, its power is greater. You'll put a +1/+1 - * counter on the creature with evolve. - * + * + * When comparing the stats of the two creatures, you always compare power to + * power and toughness to toughness. Whenever a creature enters the battlefield + * under your control, check its power and toughness against the power and + * toughness of the creature with evolve. If neither stat of the new creature is + * greater, evolve won't trigger at all. For example, if you control a 2/3 + * creature with evolve and a 2/2 creature enters the battlefield under your + * control, you won't have the opportunity to cast a spell like Giant Growth to + * make the 2/2 creature large enough to cause evolve to trigger. If evolve + * triggers, the stat comparison will happen again when the ability tries to + * resolve. If neither stat of the new creature is greater, the ability will do + * nothing. If the creature that entered the battlefield leaves the battlefield + * before evolve tries to resolve, use its last known power and toughness to + * compare the stats. If a creature enters the battlefield with +1/+1 counters + * on it, consider those counters when determining if evolve will trigger. For + * example, a 1/1 creature that enters the battlefield with two +1/+1 counters + * on it will cause the evolve ability of a 2/2 creature to trigger. If multiple + * creatures enter the battlefield at the same time, evolve may trigger multiple + * times, although the stat comparison will take place each time one of those + * abilities tries to resolve. For example, if you control a 2/2 creature with + * evolve and two 3/3 creatures enter the battlefield, evolve will trigger + * twice. The first ability will resolve and put a +1/+1 counter on the creature + * with evolve. When the second ability tries to resolve, neither the power nor + * the toughness of the new creature is greater than that of the creature with + * evolve, so that ability does nothing. When comparing the stats as the evolve + * ability resolves, it's possible that the stat that's greater changes from + * power to toughness or vice versa. If this happens, the ability will still + * resolve and you'll put a +1/+1 counter on the creature with evolve. For + * example, if you control a 2/2 creature with evolve and a 1/3 creature enters + * the battlefield under your control, it toughness is greater so evolve will + * trigger. In response, the 1/3 creature gets +2/-2. When the evolve trigger + * tries to resolve, its power is greater. You'll put a +1/+1 counter on the + * creature with evolve. + * * @author LevelX2 */ - - public class EvolveAbility extends TriggeredAbilityImpl { public EvolveAbility() { @@ -169,4 +175,3 @@ class EvolveEffect extends OneShotEffect { return false; } } - diff --git a/Mage/src/mage/abilities/keyword/FadingAbility.java b/Mage/src/mage/abilities/keyword/FadingAbility.java index 4a66d7dcda7..e8e45295810 100644 --- a/Mage/src/mage/abilities/keyword/FadingAbility.java +++ b/Mage/src/mage/abilities/keyword/FadingAbility.java @@ -18,25 +18,17 @@ import mage.game.permanent.Permanent; * 702.31a Fading is a keyword that represents two abilities. “Fading N” means “This permanent enters the battlefield with N fade counters on it” and “At the beginning of your upkeep, remove a fade counter from this permanent. If you can’t, sacrifice the permanent.” * */ - public class FadingAbility extends EntersBattlefieldAbility { - - + private String ruleText; - + public FadingAbility(int fadeCounter, Card card) { super(new AddCountersSourceEffect(CounterType.FADE.createInstance(fadeCounter)), "with"); Ability ability = new BeginningOfUpkeepTriggeredAbility(new FadingEffect(), TargetController.YOU, false); ability.setRuleVisible(false); addSubAbility(ability); - StringBuilder sb = new StringBuilder("Fading "); - sb.append(fadeCounter); - sb.append(" (This permanent enters the battlefield with ") - .append(fadeCounter) - .append(" fade counters on it. ") - .append(" At the beginning of your upkeep, remove a fade counter from this permanent. If you can’t, sacrifice the permanent.") - .append(")"); - ruleText = sb.toString(); + ruleText = "Fading " + fadeCounter + " (This permanent enters the battlefield with " + fadeCounter + " fade counters on it." + + " At the beginning of your upkeep, remove a fade counter from this permanent. If you can’t, sacrifice the permanent."; } public FadingAbility(final FadingAbility ability) { @@ -54,7 +46,9 @@ public class FadingAbility extends EntersBattlefieldAbility { return ruleText; } } + class FadingEffect extends OneShotEffect { + FadingEffect() { super(Outcome.Sacrifice); staticText = "remove a fade counter from this permanent. If you can’t, sacrifice the permanent"; @@ -64,18 +58,15 @@ class FadingEffect extends OneShotEffect { super(effect); } - @Override public boolean apply(Game game, Ability source) { - Permanent p = game.getPermanent(source.getSourceId()); - if (p != null) { - int amount = p.getCounters().getCount(CounterType.FADE); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + int amount = permanent.getCounters().getCount(CounterType.FADE); if (amount > 0) { - p.removeCounters(CounterType.FADE.createInstance(), game); - } - else - { - p.sacrifice(source.getSourceId(), game); + permanent.removeCounters(CounterType.FADE.createInstance(), game); + } else { + permanent.sacrifice(source.getSourceId(), game); } return true; } @@ -86,4 +77,4 @@ class FadingEffect extends OneShotEffect { public FadingEffect copy() { return new FadingEffect(this); } -} \ No newline at end of file +} diff --git a/Mage/src/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/mage/abilities/keyword/FlashbackAbility.java index d1f23c4dbd1..b8631af4523 100644 --- a/Mage/src/mage/abilities/keyword/FlashbackAbility.java +++ b/Mage/src/mage/abilities/keyword/FlashbackAbility.java @@ -29,18 +29,12 @@ package mage.abilities.keyword; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.SpellAbility; import mage.abilities.costs.Cost; -import mage.abilities.costs.VariableCost; -import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; -import mage.abilities.effects.common.ExileSourceEffect; import mage.cards.Card; import mage.cards.SplitCard; -import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SpellAbilityType; @@ -54,13 +48,14 @@ import mage.players.Player; /** * 702.32. Flashback * - * 702.32a. Flashback appears on some instants and sorceries. It represents two static abilities: - * one that functions while the card is in a player‘s graveyard and the other that functions - * while the card is on the stack. Flashback [cost] means, "You may cast this card from your - * graveyard by paying [cost] rather than paying its mana cost" and, "If the flashback cost - * was paid, exile this card instead of putting it anywhere else any time it would leave the - * stack." Casting a spell using its flashback ability follows the rules for paying alternative - * costs in rules 601.2b and 601.2e–g. + * 702.32a. Flashback appears on some instants and sorceries. It represents two + * static abilities: one that functions while the card is in a player‘s + * graveyard and the other that functions while the card is on the stack. + * Flashback [cost] means, "You may cast this card from your graveyard by paying + * [cost] rather than paying its mana cost" and, "If the flashback cost was + * paid, exile this card instead of putting it anywhere else any time it would + * leave the stack." Casting a spell using its flashback ability follows the + * rules for paying alternative costs in rules 601.2b and 601.2e–g. * * @author nantuko */ @@ -92,10 +87,10 @@ public class FlashbackAbility extends SpellAbility { if (card != null) { // Flashback can never cast a split card by Fuse, because Fuse only works from hand if (card.isSplitCard()) { - if (((SplitCard)card).getLeftHalfCard().getName().equals(abilityName)) { - return ((SplitCard)card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game); - } else if (((SplitCard)card).getRightHalfCard().getName().equals(abilityName)) { - return ((SplitCard)card).getRightHalfCard().getSpellAbility().canActivate(playerId, game); + if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) { + return ((SplitCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game); + } else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) { + return ((SplitCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game); } } return card.getSpellAbility().canActivate(playerId, game); @@ -108,7 +103,7 @@ public class FlashbackAbility extends SpellAbility { public FlashbackAbility copy() { return new FlashbackAbility(this); } - + @Override public String getRule(boolean all) { return this.getRule(); @@ -176,45 +171,26 @@ class FlashbackEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { SpellAbility spellAbility; - switch(((FlashbackAbility) source).getSpellAbilityType()) { + switch (((FlashbackAbility) source).getSpellAbilityType()) { case SPLIT_LEFT: - spellAbility = ((SplitCard)card).getLeftHalfCard().getSpellAbility(); + spellAbility = ((SplitCard) card).getLeftHalfCard().getSpellAbility(); break; case SPLIT_RIGHT: - spellAbility = ((SplitCard)card).getRightHalfCard().getSpellAbility(); + spellAbility = ((SplitCard) card).getRightHalfCard().getSpellAbility(); break; default: spellAbility = card.getSpellAbility(); } spellAbility.clear(); - // used if flashbacked spell has a {X} cost - int amount = source.getManaCostsToPay().getX(); - if (amount == 0) { - // add variable cost like Discard X cards to get the X value to the spell - // because there is currently no way to set the x value in anotehr way, it's set for the - // x mana value to be known by the spell - for (Cost cost:source.getCosts()) { - if (cost instanceof VariableCost && cost.isPaid()) { - amount = ((VariableCost) cost).getAmount(); - break; - } - } + // set the payed flashback costs to the spell ability so abilities like Converge or calculation of {X} values work + spellAbility.getManaCostsToPay().clear(); + spellAbility.getManaCostsToPay().addAll(source.getManaCostsToPay()); + if (!game.isSimulation()) { + game.informPlayers(controller.getLogName() + " flashbacks " + card.getLogName()); } - if (amount > 0) { - // multiplier must be taken into account because if the base spell has {X}{X} the x value would be wrongly halfed - for (VariableCost variableCost: spellAbility.getManaCostsToPay().getVariableCosts()) { - if (variableCost instanceof VariableManaCost) { - amount = amount * ((VariableManaCost)variableCost).getMultiplier(); - break; - } - } - spellAbility.getManaCostsToPay().setX(amount); - } - if (!game.isSimulation()) - game.informPlayers(new StringBuilder(controller.getLogName()).append(" flashbacks ").append(card.getName()).toString()); spellAbility.setCostModificationActive(false); // prevents to apply cost modification twice for flashbacked spells - if (controller.cast(spellAbility, game, true)) { + if (controller.cast(spellAbility, game, false)) { game.addEffect(new FlashbackReplacementEffect(), source); return true; } @@ -252,7 +228,7 @@ class FlashbackReplacementEffect extends ReplacementEffectImpl { if (controller != null) { Card card = game.getCard(event.getTargetId()); if (card != null) { - return controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, game.getState().getZone(card.getId()), true); + return controller.moveCards(card, Zone.EXILED, source, game); } } return false; @@ -265,8 +241,8 @@ class FlashbackReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - return event.getTargetId().equals(source.getSourceId()) - && ((ZoneChangeEvent)event).getFromZone() == Zone.STACK - && ((ZoneChangeEvent)event).getToZone() != Zone.EXILED; + return event.getTargetId().equals(source.getSourceId()) + && ((ZoneChangeEvent) event).getFromZone() == Zone.STACK + && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED; } } diff --git a/Mage/src/mage/abilities/keyword/GraftAbility.java b/Mage/src/mage/abilities/keyword/GraftAbility.java index 45eed19b928..14132f4f128 100644 --- a/Mage/src/mage/abilities/keyword/GraftAbility.java +++ b/Mage/src/mage/abilities/keyword/GraftAbility.java @@ -1,30 +1,30 @@ /* -* 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. -*/ + * 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.keyword; import java.util.Locale; @@ -48,18 +48,19 @@ import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; /** - * 702.56. Graft - * 702.56a. Graft represents both a static ability and a triggered ability. Graft N means, - * "This permanent enters the battlefield with N +1/+1 counters on it" and, "Whenever - * another creature enters the battlefield, if this permanent has a +1/+1 counter on it, - * you may move a +1/+1 counter from this permanent onto that creature." + * 702.56. Graft 702.56a. Graft represents both a static ability and a triggered + * ability. Graft N means, "This permanent enters the battlefield with N +1/+1 + * counters on it" and, "Whenever another creature enters the battlefield, if + * this permanent has a +1/+1 counter on it, you may move a +1/+1 counter from + * this permanent onto that creature." * - * 702.56b. If a creature has multiple instances of graft, each one works separately. + * 702.56b. If a creature has multiple instances of graft, each one works + * separately. * * @author LevelX2 */ - public class GraftAbility extends TriggeredAbilityImpl { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); private int amount; @@ -112,10 +113,10 @@ public class GraftAbility extends TriggeredAbilityImpl { @Override public String getRule() { StringBuilder sb = new StringBuilder("Graft"); - sb.append(" ").append(amount).append(" (This ").append(cardtype).append(" enters the battlefield with ") - .append(amount == 1 ? "a": CardUtil.numberToText(amount)) - .append(" +1/+1 counter on it. Whenever a creature enters the battlefield, you may move a +1/+1 counter from this ") - .append(cardtype).append(" onto it.)"); + sb.append(" ").append(amount).append(" (This ").append(cardtype).append(" enters the battlefield with ") + .append(amount == 1 ? "a" : CardUtil.numberToText(amount)) + .append(" +1/+1 counter on it. Whenever a creature enters the battlefield, you may move a +1/+1 counter from this ") + .append(cardtype).append(" onto it.)"); return sb.toString(); } @@ -126,7 +127,7 @@ class GraftStaticAbility extends StaticAbility { private String ruleText; public GraftStaticAbility(int amount) { - super(Zone.BATTLEFIELD, new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(amount)))); + super(Zone.ALL, new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(amount)))); ruleText = new StringBuilder("This enters the battlefield with ").append(amount).append(" +1/+1 counter on it.").toString(); this.setRuleVisible(false); } @@ -147,7 +148,6 @@ class GraftStaticAbility extends StaticAbility { } } - class GraftDistributeCounterEffect extends OneShotEffect { public GraftDistributeCounterEffect() { diff --git a/Mage/src/mage/abilities/keyword/HideawayAbility.java b/Mage/src/mage/abilities/keyword/HideawayAbility.java index 57ff119d397..b312fc9f3fb 100644 --- a/Mage/src/mage/abilities/keyword/HideawayAbility.java +++ b/Mage/src/mage/abilities/keyword/HideawayAbility.java @@ -65,7 +65,7 @@ import mage.util.CardUtil; public class HideawayAbility extends StaticAbility { public HideawayAbility() { - super(Zone.BATTLEFIELD, new EntersBattlefieldEffect(new TapSourceEffect(true))); + super(Zone.ALL, new EntersBattlefieldEffect(new TapSourceEffect(true))); Ability ability = new EntersBattlefieldTriggeredAbility(new HideawayExileEffect(), false); ability.setRuleVisible(false); addSubAbility(ability); @@ -115,17 +115,17 @@ class HideawayExileEffect extends OneShotEffect { if (hideawaySource == null || controller == null) { return false; } - + Cards cards = new CardsImpl(Zone.LIBRARY); - cards.addAll(controller.getLibrary().getTopCards(game, 4)); + cards.addAll(controller.getLibrary().getTopCards(game, 4)); if (cards.size() > 0) { TargetCard target1 = new TargetCard(Zone.LIBRARY, filter1); if (controller.choose(Outcome.Detriment, cards, target1, game)) { Card card = cards.get(target1.getFirstTarget(), game); if (card != null) { cards.remove(card); - controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source), - "Hideaway (" + hideawaySource.getIdName() +")", source.getSourceId(), game, Zone.LIBRARY, false); + controller.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source), + "Hideaway (" + hideawaySource.getIdName() + ")", source.getSourceId(), game, Zone.LIBRARY, false); card.setFaceDown(true, game); } } @@ -159,7 +159,7 @@ class HideawayLookAtFaceDownCardEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (game.getState().getZone(objectId) != Zone.EXILED + if (game.getState().getZone(objectId) != Zone.EXILED || !game.getState().getCardState(objectId).isFaceDown()) { return false; } @@ -180,4 +180,3 @@ class HideawayLookAtFaceDownCardEffect extends AsThoughEffectImpl { return false; } } - diff --git a/Mage/src/mage/abilities/keyword/KickerAbility.java b/Mage/src/mage/abilities/keyword/KickerAbility.java index d28edb5307c..138189fc086 100644 --- a/Mage/src/mage/abilities/keyword/KickerAbility.java +++ b/Mage/src/mage/abilities/keyword/KickerAbility.java @@ -85,8 +85,8 @@ import mage.players.Player; public class KickerAbility extends StaticAbility implements OptionalAdditionalSourceCosts { protected static final String KICKER_KEYWORD = "Kicker"; - protected static final String KICKER_REMINDER_MANA = "(You may pay an additional {cost} as you cast this spell.)"; - protected static final String KICKER_REMINDER_COST = "(You may {cost} in addition to any other costs as you cast this spell.)"; + protected static final String KICKER_REMINDER_MANA = "You may pay an additional {cost} as you cast this spell."; + protected static final String KICKER_REMINDER_COST = "You may {cost} in addition to any other costs as you cast this spell."; protected Map activations = new HashMap<>(); // zoneChangeCounter, activations @@ -203,7 +203,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo if (zcc == 0) { zcc = game.getState().getZoneChangeCounter(source.getSourceId()); } - if (zcc > 0 && (source.getAbilityType().equals(AbilityType.TRIGGERED) || source.getAbilityType().equals(AbilityType.STATIC))) { + if (zcc > 0 && (source.getAbilityType().equals(AbilityType.TRIGGERED))) { --zcc; } return String.valueOf(zcc) + ((kickerCosts.size() > 1) ? costText : ""); diff --git a/Mage/src/mage/abilities/keyword/ModularAbility.java b/Mage/src/mage/abilities/keyword/ModularAbility.java index 19219e21b2d..5efdb7a01a7 100644 --- a/Mage/src/mage/abilities/keyword/ModularAbility.java +++ b/Mage/src/mage/abilities/keyword/ModularAbility.java @@ -22,24 +22,24 @@ import mage.target.Target; import mage.target.common.TargetArtifactPermanent; import mage.util.CardUtil; - /** * * 702.41. Modular * - * 702.41a Modular represents both a static ability and a triggered ability. - * "Modular N" means "This permanent enters the battlefield with N +1/+1 - * counters on it" and "When this permanent is put into a graveyard - * from the battlefield, you may put a +1/+1 counter on target artifact - * creature for each +1/+1 counter on this permanent." - * 702.41b If a creature has multiple instances of modular, each one works separately. + * 702.41a Modular represents both a static ability and a triggered ability. + * "Modular N" means "This permanent enters the battlefield with N +1/+1 + * counters on it" and "When this permanent is put into a graveyard from the + * battlefield, you may put a +1/+1 counter on target artifact creature for each + * +1/+1 counter on this permanent." 702.41b If a creature has multiple + * instances of modular, each one works separately. + * * - * * @author Loki, LevelX2 */ - public class ModularAbility extends DiesTriggeredAbility { + private static final FilterArtifactPermanent filter = new FilterArtifactPermanent("artifact creature"); + static { filter.add(new CardTypePredicate(CardType.CREATURE)); } @@ -74,7 +74,7 @@ public class ModularAbility extends DiesTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { if (super.checkTrigger(event, game)) { - ZoneChangeEvent zEvent = (ZoneChangeEvent)event; + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; if (zEvent.getTarget().getCounters().getCount(CounterType.P1P1) > 0) { return true; } @@ -94,9 +94,9 @@ public class ModularAbility extends DiesTriggeredAbility { sb.append("-Sunburst (This enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. When it dies, you may put its +1/+1 counters on target artifact creature.)"); } else { sb.append(" ").append(amount).append(" (This enters the battlefield with ") - .append(CardUtil.numberToText(amount, "a")) - .append(" +1/+1 counter").append(amount != 1 ? "s":"") - .append(" on it. When it dies, you may put its +1/+1 counters on target artifact creature.)"); + .append(CardUtil.numberToText(amount, "a")) + .append(" +1/+1 counter").append(amount != 1 ? "s" : "") + .append(" on it. When it dies, you may put its +1/+1 counters on target artifact creature.)"); } return sb.toString(); } @@ -108,10 +108,8 @@ class ModularStaticAbility extends StaticAbility { private String ruleText; public ModularStaticAbility(int amount) { - super(Zone.BATTLEFIELD, new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(amount)))); - ruleText = new StringBuilder("This enters the battlefield with ").append(CardUtil.numberToText(amount, "a")) - .append(" +1/+1 counter").append(amount != 1 ? "s":"") - .append(" on it.").toString(); + super(Zone.ALL, new EntersBattlefieldEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(amount)))); + ruleText = "This enters the battlefield with " + CardUtil.numberToText(amount, "a") + " +1/+1 counter" + (amount != 1 ? "s" : "") + " on it."; this.setRuleVisible(false); } @@ -131,9 +129,10 @@ class ModularStaticAbility extends StaticAbility { } } - class ModularDistributeCounterEffect extends OneShotEffect { + private static final FilterArtifactPermanent filter = new FilterArtifactPermanent("artifact creature"); + static { filter.add(new CardTypePredicate(CardType.CREATURE)); } diff --git a/Mage/src/mage/abilities/keyword/ReboundAbility.java b/Mage/src/mage/abilities/keyword/ReboundAbility.java index 6bd8c0d6527..7ab46d546e0 100644 --- a/Mage/src/mage/abilities/keyword/ReboundAbility.java +++ b/Mage/src/mage/abilities/keyword/ReboundAbility.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * 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.keyword; import java.util.UUID; @@ -48,28 +47,29 @@ import mage.game.stack.Spell; import mage.players.Player; /** - * This ability has no effect by default and will always return false on the call - * to apply. This is because of how the {@link ReboundEffect} works. It will - * install the effect if and only if the spell was cast from the {@link Zone#HAND Hand}. + * This ability has no effect by default and will always return false on the + * call to apply. This is because of how the {@link ReboundEffect} works. It + * will install the effect if and only if the spell was cast from the + * {@link Zone#HAND Hand}. *

* 702.85. Rebound *

- * 702.85a Rebound appears on some instants and sorceries. It represents a static - * ability that functions while the spell is on the stack and may create a delayed - * triggered ability. "Rebound" means "If this spell was cast from your hand, - * instead of putting it into your graveyard as it resolves, exile it and, at - * the beginning of your next upkeep, you may cast this card from exile without - * paying its mana cost." + * 702.85a Rebound appears on some instants and sorceries. It represents a + * static ability that functions while the spell is on the stack and may create + * a delayed triggered ability. "Rebound" means "If this spell was cast from + * your hand, instead of putting it into your graveyard as it resolves, exile it + * and, at the beginning of your next upkeep, you may cast this card from exile + * without paying its mana cost." *

- * 702.85b Casting a card without paying its mana cost as the result of a rebound - * ability follows the rules for paying alternative costs in rules 601.2b and 601.2e-g. + * 702.85b Casting a card without paying its mana cost as the result of a + * rebound ability follows the rules for paying alternative costs in rules + * 601.2b and 601.2e-g. *

* 702.85c Multiple instances of rebound on the same spell are redundant. * * @author maurer.it_at_gmail.com, noxx */ - -public class ReboundAbility extends SimpleStaticAbility { +public class ReboundAbility extends SimpleStaticAbility { public ReboundAbility() { super(Zone.STACK, new ReboundCastFromHandReplacementEffect()); @@ -81,8 +81,8 @@ public class ReboundAbility extends SimpleStaticAbility { @Override public ReboundAbility copy() { - return new ReboundAbility(this); - } + return new ReboundAbility(this); + } } class ReboundCastFromHandReplacementEffect extends ReplacementEffectImpl { @@ -95,7 +95,7 @@ class ReboundCastFromHandReplacementEffect extends ReplacementEffectImpl { ReboundCastFromHandReplacementEffect(ReboundCastFromHandReplacementEffect effect) { super(effect); } - + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ZONE_CHANGE; @@ -103,16 +103,18 @@ class ReboundCastFromHandReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (((ZoneChangeEvent) event).getFromZone() == Zone.STACK && - ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD && - event.getSourceId() == source.getSourceId()) { // if countered the source.sourceId is different or null if it fizzles + if (((ZoneChangeEvent) event).getFromZone() == Zone.STACK + && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD + && event.getSourceId() != null + && event.getSourceId().equals(source.getSourceId())) { // if countered the source.sourceId is different or null if it fizzles Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null && spell.getFromZone().equals(Zone.HAND)) { return true; - } + } } return false; } + @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Spell sourceSpell = game.getStack().getSpell(source.getSourceId()); @@ -126,9 +128,9 @@ class ReboundCastFromHandReplacementEffect extends ReplacementEffectImpl { // Add the delayed triggered effect ReboundEffectCastFromExileDelayedTrigger trigger = new ReboundEffectCastFromExileDelayedTrigger(source.getSourceId(), source.getSourceId()); trigger.setControllerId(source.getControllerId()); - trigger.setSourceObject(source.getSourceObject(game), game); + trigger.setSourceObject(source.getSourceObject(game), game); game.addDelayedTriggeredAbility(trigger); - + player.moveCardToExileWithInfo(sourceCard, sourceCard.getId(), player.getName() + " Rebound", source.getSourceId(), game, Zone.STACK, true); return true; } @@ -144,7 +146,6 @@ class ReboundCastFromHandReplacementEffect extends ReplacementEffectImpl { } - class ReboundEffectCastFromExileDelayedTrigger extends DelayedTriggeredAbility { ReboundEffectCastFromExileDelayedTrigger(UUID cardId, UUID sourceId) { @@ -171,6 +172,7 @@ class ReboundEffectCastFromExileDelayedTrigger extends DelayedTriggeredAbility { public boolean checkTrigger(GameEvent event, Game game) { return MyTurnCondition.getInstance().apply(game, this); } + @Override public String getRule() { return "Rebound - You may cast {this} from exile without paying its mana cost."; @@ -178,8 +180,8 @@ class ReboundEffectCastFromExileDelayedTrigger extends DelayedTriggeredAbility { } /** - * Will be triggered by {@link ReboundEffectCastFromExileDelayedTrigger} and will - * simply cast the spell then remove it from its former home in exile. + * Will be triggered by {@link ReboundEffectCastFromExileDelayedTrigger} and + * will simply cast the spell then remove it from its former home in exile. * * @author maurer.it_at_gmail.com */ diff --git a/Mage/src/mage/abilities/keyword/ReplicateAbility.java b/Mage/src/mage/abilities/keyword/ReplicateAbility.java index 8be57a3997e..ed5c34e1a2f 100644 --- a/Mage/src/mage/abilities/keyword/ReplicateAbility.java +++ b/Mage/src/mage/abilities/keyword/ReplicateAbility.java @@ -56,7 +56,7 @@ import mage.players.Player; public class ReplicateAbility extends StaticAbility implements OptionalAdditionalSourceCosts { private static final String keywordText = "Replicate"; - private static final String reminderTextMana = "(When you cast this spell, copy it for each time you paid its replicate cost. You may choose new targets for the copies.)"; + private static final String reminderTextMana = "When you cast this spell, copy it for each time you paid its replicate cost. You may choose new targets for the copies."; protected OptionalAdditionalCost additionalCost; public ReplicateAbility(Card card, String manaString) { diff --git a/Mage/src/mage/abilities/keyword/RetraceAbility.java b/Mage/src/mage/abilities/keyword/RetraceAbility.java index 2ce121fc255..21e6b84d9ed 100644 --- a/Mage/src/mage/abilities/keyword/RetraceAbility.java +++ b/Mage/src/mage/abilities/keyword/RetraceAbility.java @@ -28,6 +28,7 @@ package mage.abilities.keyword; import mage.abilities.SpellAbility; +import mage.abilities.costs.Cost; import mage.abilities.costs.common.DiscardTargetCost; import mage.cards.Card; import mage.constants.SpellAbilityType; @@ -44,7 +45,9 @@ public class RetraceAbility extends SpellAbility { public RetraceAbility(Card card) { super(card.getManaCost(), card.getName() + " with retrace", Zone.GRAVEYARD, SpellAbilityType.BASE_ALTERNATE); this.getCosts().addAll(card.getSpellAbility().getCosts().copy()); - this.addCost(new DiscardTargetCost(new TargetCardInHand(new FilterLandCard()))); + Cost cost = new DiscardTargetCost(new TargetCardInHand(new FilterLandCard())); + cost.setText(""); + this.addCost(cost); this.getEffects().addAll(card.getSpellAbility().getEffects().copy()); this.getTargets().addAll(card.getSpellAbility().getTargets().copy()); this.getChoices().addAll(card.getSpellAbility().getChoices().copy()); diff --git a/Mage/src/mage/abilities/keyword/SunburstAbility.java b/Mage/src/mage/abilities/keyword/SunburstAbility.java index 12b9d7834ae..ba1b59adcee 100644 --- a/Mage/src/mage/abilities/keyword/SunburstAbility.java +++ b/Mage/src/mage/abilities/keyword/SunburstAbility.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.keyword; import mage.abilities.Ability; @@ -46,25 +45,22 @@ import mage.players.Player; * * @author Plopman */ +public class SunburstAbility extends EntersBattlefieldAbility { - -public class SunburstAbility extends EntersBattlefieldAbility{ - - private final static String ruleCreature ="Sunburst (This enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.)"; - private final static String ruleNonCreature ="Sunburst (This enters the battlefield with a charge counter on it for each color of mana spent to cast it.)"; + private final static String ruleCreature = "Sunburst (This enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.)"; + private final static String ruleNonCreature = "Sunburst (This enters the battlefield with a charge counter on it for each color of mana spent to cast it.)"; private boolean isCreature; - public SunburstAbility(Card card){ - super(new SunburstEffect(),""); + public SunburstAbility(Card card) { + super(new SunburstEffect(), ""); isCreature = card.getCardType().contains(CardType.CREATURE); } - - public SunburstAbility(final SunburstAbility ability){ + + public SunburstAbility(final SunburstAbility ability) { super(ability); this.isCreature = ability.isCreature; } - - + @Override public EntersBattlefieldAbility copy() { return new SunburstAbility(this); @@ -74,15 +70,13 @@ public class SunburstAbility extends EntersBattlefieldAbility{ public String getRule() { return isCreature ? ruleCreature : ruleNonCreature; } - - + } class SunburstEffect extends OneShotEffect { private static final DynamicValue amount = new SunburstCount(); - public SunburstEffect() { super(Outcome.Benefit); staticText = "Sunburst"; @@ -94,22 +88,21 @@ class SunburstEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); + Permanent permanent = game.getPermanentEntering(source.getSourceId()); if (permanent != null) { Counter counter; - if(permanent.getCardType().contains(CardType.CREATURE)){ - counter = CounterType.P1P1.createInstance(amount.calculate(game, source, this)); - } - else{ - counter = CounterType.CHARGE.createInstance(amount.calculate(game, source, this)); + if (permanent.getCardType().contains(CardType.CREATURE)) { + counter = CounterType.P1P1.createInstance(amount.calculate(game, source, this)); + } else { + counter = CounterType.CHARGE.createInstance(amount.calculate(game, source, this)); } if (counter != null) { - + permanent.addCounters(counter, game); if (!game.isSimulation()) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - game.informPlayers(player.getLogName()+ " puts " + counter.getCount() + " " + counter.getName() + " counter on " + permanent.getName()); + game.informPlayers(player.getLogName() + " puts " + counter.getCount() + " " + counter.getName() + " counter on " + permanent.getName()); } } } diff --git a/Mage/src/mage/abilities/keyword/TributeAbility.java b/Mage/src/mage/abilities/keyword/TributeAbility.java index ad5ad5b6b7b..5cf0b1cf8e5 100644 --- a/Mage/src/mage/abilities/keyword/TributeAbility.java +++ b/Mage/src/mage/abilities/keyword/TributeAbility.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.keyword; import java.util.UUID; @@ -46,23 +45,20 @@ import mage.util.CardUtil; * * @author LevelX2 */ - - -public class TributeAbility extends EntersBattlefieldAbility{ +public class TributeAbility extends EntersBattlefieldAbility { private int tributeValue; - public TributeAbility(int tributeValue){ - super(new TributeEffect(tributeValue), false); + public TributeAbility(int tributeValue) { + super(new TributeEffect(tributeValue)); this.tributeValue = tributeValue; } - public TributeAbility(final TributeAbility ability){ + public TributeAbility(final TributeAbility ability) { super(ability); this.tributeValue = ability.tributeValue; } - @Override public EntersBattlefieldAbility copy() { return new TributeAbility(this); @@ -81,7 +77,7 @@ public class TributeAbility extends EntersBattlefieldAbility{ class TributeEffect extends OneShotEffect { - private int tributeValue; + private final int tributeValue; public TributeEffect(int tributeValue) { super(Outcome.Detriment); @@ -101,7 +97,7 @@ class TributeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + Permanent sourcePermanent = game.getPermanentEntering(source.getSourceId()); if (controller != null && sourcePermanent != null) { UUID opponentId; if (game.getOpponents(controller.getId()).size() == 1) { @@ -117,16 +113,18 @@ class TributeEffect extends OneShotEffect { StringBuilder sb = new StringBuilder("Pay tribute to "); sb.append(sourcePermanent.getName()); sb.append(" (add ").append(CardUtil.numberToText(tributeValue)).append(" +1/+1 counter"); - sb.append(tributeValue > 1 ? "s":"").append(" to it)?"); + sb.append(tributeValue > 1 ? "s" : "").append(" to it)?"); if (opponent.chooseUse(outcome, sb.toString(), source, game)) { - if (!game.isSimulation()) + if (!game.isSimulation()) { game.informPlayers(opponent.getLogName() + " pays tribute to " + sourcePermanent.getLogName()); + } game.getState().setValue("tributeValue" + source.getSourceId(), "yes"); return new AddCountersSourceEffect(CounterType.P1P1.createInstance(tributeValue), true).apply(game, source); } else { - if (!game.isSimulation()) + if (!game.isSimulation()) { game.informPlayers(opponent.getLogName() + " does not pay tribute to " + sourcePermanent.getLogName()); - game.getState().setValue("tributeValue"+ source.getSourceId(), "no"); + } + game.getState().setValue("tributeValue" + source.getSourceId(), "no"); } return true; } diff --git a/Mage/src/mage/abilities/keyword/UnleashAbility.java b/Mage/src/mage/abilities/keyword/UnleashAbility.java index 45112f075b2..953d482278f 100644 --- a/Mage/src/mage/abilities/keyword/UnleashAbility.java +++ b/Mage/src/mage/abilities/keyword/UnleashAbility.java @@ -26,64 +26,57 @@ * or implied, of BetaSteward_at_googlemail.com. */ package mage.abilities.keyword; - -import mage.constants.Duration; -import mage.constants.Outcome; + import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.RestrictionEffect; +import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; - -/** - * - * @author LevelX2 - */ - // // 702.96. Unleash // -// 702.96a Unleash is a keyword that represents two static abilities. +// 702.96a Unleash is a keyword that represents two static abilities. // "Unleash" means "You may have this permanent enter the battlefield with an additional +1/+1 counter on it" // and "This permanent can’t block as long as it has a +1/+1 counter on it." +public class UnleashAbility extends SimpleStaticAbility { - - public class UnleashAbility extends SimpleStaticAbility { - public UnleashAbility() { super(Zone.ALL, new UnleashReplacementEffect()); this.addEffect(new UnleashRestrictionEffect()); } - + public UnleashAbility(final UnleashAbility ability) { super(ability); } - + @Override public UnleashAbility copy() { return new UnleashAbility(this); } - + @Override public String getRule() { return "Unleash (You may have this creature enter the battlefield with a +1/+1 counter on it. It can't block as long as it has a +1/+1 counter on it.)"; } } - + class UnleashReplacementEffect extends ReplacementEffectImpl { - + public UnleashReplacementEffect() { super(Duration.EndOfGame, Outcome.Detriment); } - + public UnleashReplacementEffect(UnleashReplacementEffect effect) { super(effect); } @@ -95,53 +88,51 @@ class UnleashReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getSourceId())) { - return true; - } - return false; + return event.getTargetId().equals(source.getSourceId()); } - + @Override public boolean apply(Game game, Ability source) { return false; } - + @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent creature = game.getPermanent(event.getTargetId()); + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); Player controller = game.getPlayer(source.getControllerId()); if (creature != null && controller != null) { - if (controller.chooseUse(outcome, "Unleash "+ creature.getName() +"?", source, game)) { - if (!game.isSimulation()) + if (controller.chooseUse(outcome, "Unleash " + creature.getLogName() + "?", source, game)) { + if (!game.isSimulation()) { game.informPlayers(controller.getLogName() + " unleashes " + creature.getName()); + } creature.addCounters(CounterType.P1P1.createInstance(), game); } } return false; } - + @Override public String getText(Mode mode) { return staticText; } - + @Override public UnleashReplacementEffect copy() { return new UnleashReplacementEffect(this); } - + } - + class UnleashRestrictionEffect extends RestrictionEffect { - + public UnleashRestrictionEffect() { super(Duration.WhileOnBattlefield); } - + public UnleashRestrictionEffect(final UnleashRestrictionEffect effect) { super(effect); } - + @Override public boolean applies(Permanent permanent, Ability source, Game game) { if (permanent != null && permanent.getId().equals(source.getSourceId())) { @@ -151,14 +142,14 @@ class UnleashRestrictionEffect extends RestrictionEffect { } return false; } - + @Override public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) { return false; } - + @Override public UnleashRestrictionEffect copy() { return new UnleashRestrictionEffect(this); } -} \ No newline at end of file +} diff --git a/Mage/src/mage/cards/Card.java b/Mage/src/mage/cards/Card.java index 6e5ec8c1d32..5befb3d3a5f 100644 --- a/Mage/src/mage/cards/Card.java +++ b/Mage/src/mage/cards/Card.java @@ -40,6 +40,7 @@ import mage.constants.Zone; import mage.counters.Counter; import mage.counters.Counters; import mage.game.Game; +import mage.game.permanent.Permanent; public interface Card extends MageObject { @@ -65,6 +66,8 @@ public interface Card extends MageObject { String getTokenSetCode(); + void checkForCountersToAdd(Permanent permanent, Game game); + void setFaceDown(boolean value, Game game); boolean isFaceDown(Game game); @@ -125,6 +128,8 @@ public interface Card extends MageObject { boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId); + boolean removeFromZone(Game game, Zone fromZone, UUID sourceId); + boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId); boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped); diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index ee05eb447bd..a24fc9323df 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -54,8 +54,6 @@ import static mage.constants.Zone.EXILED; import static mage.constants.Zone.GRAVEYARD; import static mage.constants.Zone.HAND; import static mage.constants.Zone.LIBRARY; -import static mage.constants.Zone.OUTSIDE; -import static mage.constants.Zone.PICK; import static mage.constants.Zone.STACK; import mage.counters.Counter; import mage.counters.Counters; @@ -65,6 +63,7 @@ import mage.game.Game; import mage.game.command.Commander; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.game.stack.Spell; import mage.game.stack.StackObject; @@ -342,55 +341,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { Zone fromZone = game.getState().getZone(objectId); ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, ownerId, fromZone, toZone, appliedEffects); if (!game.replaceEvent(event)) { - if (event.getFromZone() != null) { - switch (event.getFromZone()) { - case GRAVEYARD: - game.getPlayer(ownerId).removeFromGraveyard(this, game); - break; - case HAND: - game.getPlayer(ownerId).removeFromHand(this, game); - break; - case LIBRARY: - game.getPlayer(ownerId).removeFromLibrary(this, game); - break; - case EXILED: - game.getExile().removeCard(this, game); - break; - case OUTSIDE: - game.getPlayer(ownerId).getSideboard().remove(this); - break; - case COMMAND: - game.getState().getCommand().remove((Commander) game.getObject(objectId)); - break; - case STACK: - StackObject stackObject = game.getStack().getSpell(getSpellAbility().getId()); - if (stackObject == null && (this instanceof SplitCard)) { // handle if half of Split cast is on the stack - stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId()); - if (stackObject == null) { - stackObject = game.getStack().getSpell(((SplitCard) this).getRightHalfCard().getId()); - } - } - if (stackObject == null) { - stackObject = game.getStack().getSpell(getId()); - } - if (stackObject != null) { - game.getStack().remove(stackObject); - } - break; - case PICK: - case BATTLEFIELD: // for sacrificing permanents or putting to library - break; - default: - Card sourceCard = game.getCard(sourceId); - logger.fatal(new StringBuilder("Invalid from zone [").append(fromZone) - .append("] for card [").append(this.getName()) - .append("] to zone [").append(toZone) - .append("] source [").append(sourceCard != null ? sourceCard.getName() : "null").append("]").toString()); - break; - } - game.rememberLKI(objectId, event.getFromZone(), this); - } - + removeFromZone(game, fromZone, sourceId); setFaceDown(false, game); updateZoneChangeCounter(game); switch (event.getToZone()) { @@ -454,32 +405,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { Card mainCard = getMainCard(); ZoneChangeEvent event = new ZoneChangeEvent(mainCard.getId(), ability.getId(), controllerId, fromZone, Zone.STACK); if (!game.replaceEvent(event)) { - if (event.getFromZone() != null) { - switch (event.getFromZone()) { - case GRAVEYARD: - game.getPlayer(ownerId).removeFromGraveyard(mainCard, game); - break; - case HAND: - game.getPlayer(ownerId).removeFromHand(mainCard, game); - break; - case LIBRARY: - game.getPlayer(ownerId).removeFromLibrary(mainCard, game); - break; - case EXILED: - game.getExile().removeCard(mainCard, game); - break; - case OUTSIDE: - game.getPlayer(ownerId).getSideboard().remove(mainCard); - break; - - case COMMAND: - game.getState().getCommand().remove((Commander) game.getObject(mainCard.getId())); - break; - default: - //logger.warning("moveToZone, not fully implemented: from="+event.getFromZone() + ", to="+event.getToZone()); - } - game.rememberLKI(mainCard.getId(), event.getFromZone(), this); - } + mainCard.removeFromZone(game, fromZone, ability.getSourceId()); game.getStack().push(new Spell(this, ability.copy(), controllerId, event.getFromZone())); updateZoneChangeCounter(game); setZone(event.getToZone(), game); @@ -499,36 +425,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { Zone fromZone = game.getState().getZone(objectId); ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, ownerId, fromZone, Zone.EXILED, appliedEffects); if (!game.replaceEvent(event)) { - if (fromZone != null) { - switch (fromZone) { - case GRAVEYARD: - game.getPlayer(ownerId).removeFromGraveyard(this, game); - break; - case HAND: - game.getPlayer(ownerId).removeFromHand(this, game); - break; - case LIBRARY: - game.getPlayer(ownerId).removeFromLibrary(this, game); - break; - case EXILED: - game.getExile().removeCard(this, game); - break; - case STACK: - StackObject stackObject = game.getStack().getSpell(getId()); - if (stackObject != null) { - game.getStack().remove(stackObject); - } - break; - case PICK: - // nothing to do - break; - default: - MageObject object = game.getObject(sourceId); - logger.warn(new StringBuilder("moveToExile, not fully implemented: from = ").append(fromZone).append(" - ").append(object != null ? object.getName() : "null")); - } - game.rememberLKI(objectId, event.getFromZone(), this); - } - + removeFromZone(game, fromZone, sourceId); if (exileId == null) { game.getExile().getPermanentExile().add(this); } else { @@ -568,37 +465,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { if (facedown) { this.setFaceDown(false, game); } - if (fromZone != null) { - boolean removed = false; - switch (fromZone) { - case GRAVEYARD: - removed = game.getPlayer(ownerId).removeFromGraveyard(this, game); - break; - case HAND: - removed = game.getPlayer(ownerId).removeFromHand(this, game); - break; - case LIBRARY: - removed = game.getPlayer(ownerId).removeFromLibrary(this, game); - break; - case EXILED: - game.getExile().removeCard(this, game); - removed = true; - break; - case COMMAND: - // command object (commander) is only on the stack, so no removing neccessary here - removed = true; - break; - case PICK: - removed = true; - break; - default: - logger.warn("putOntoBattlefield, not fully implemented: fromZone=" + fromZone); - } - game.rememberLKI(objectId, event.getFromZone(), this); - if (!removed) { - logger.warn("Couldn't find card in fromZone, card=" + getName() + ", fromZone=" + fromZone); - } - } + removeFromZone(game, fromZone, sourceId); updateZoneChangeCounter(game); PermanentCard permanent = new PermanentCard(this, event.getPlayerId(), game); // make sure the controller of all continuous effects of this card are switched to the current controller @@ -624,7 +491,82 @@ public abstract class CardImpl extends MageObjectImpl implements Card { return false; } - private void checkForCountersToAdd(PermanentCard permanent, Game game) { + @Override + public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) { + boolean removed = false; + MageObject lkiObject = null; + switch (fromZone) { + case GRAVEYARD: + removed = game.getPlayer(ownerId).removeFromGraveyard(this, game); + break; + case HAND: + removed = game.getPlayer(ownerId).removeFromHand(this, game); + break; + case LIBRARY: + removed = game.getPlayer(ownerId).removeFromLibrary(this, game); + break; + case EXILED: + if (game.getExile().getCard(getId(), game) != null) { + removed = game.getExile().removeCard(this, game); + + } + break; + case STACK: + StackObject stackObject; + if (getSpellAbility() != null) { + stackObject = game.getStack().getSpell(getSpellAbility().getId()); + } else { + stackObject = game.getStack().getSpell(this.getId()); + } + if (stackObject == null && (this instanceof SplitCard)) { // handle if half of Split cast is on the stack + stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId()); + if (stackObject == null) { + stackObject = game.getStack().getSpell(((SplitCard) this).getRightHalfCard().getId()); + } + } + if (stackObject == null) { + stackObject = game.getStack().getSpell(getId()); + } + if (stackObject != null) { + removed = game.getStack().remove(stackObject); + lkiObject = stackObject; + } + break; + case COMMAND: + lkiObject = (Commander) game.getObject(objectId); + if (lkiObject != null) { + removed = game.getState().getCommand().remove((Commander) game.getObject(objectId)); + } + break; + case OUTSIDE: + if (isCopy()) { // copied cards have no need to be removed from a previous zone + removed = true; + } else if (game.getPlayer(ownerId).getSideboard().contains(this.getId())) { + game.getPlayer(ownerId).getSideboard().remove(this.getId()); + removed = true; + } + break; + + case PICK: // Pick should no longer be used + case BATTLEFIELD: // for sacrificing permanents or putting to library + removed = true; + break; + default: + MageObject sourceObject = game.getObject(sourceId); + logger.fatal("Invalid from zone [" + fromZone + "] for card [" + this.getIdName() + + "] source [" + (sourceObject != null ? sourceObject.getName() : "null") + "]"); + break; + } + if (removed) { + game.rememberLKI(objectId, fromZone, lkiObject != null ? lkiObject : this); + } else { + logger.warn("Couldn't find card in fromZone, card=" + getIdName() + ", fromZone=" + fromZone); + } + return removed; + } + + @Override + public void checkForCountersToAdd(Permanent permanent, Game game) { Counters countersToAdd = game.getEnterWithCounters(permanent.getId()); if (countersToAdd != null) { for (Counter counter : countersToAdd.values()) { diff --git a/Mage/src/mage/cards/decks/importer/MWSDeckImporter.java b/Mage/src/mage/cards/decks/importer/MWSDeckImporter.java index f8bb97629ae..7ee643ff02a 100644 --- a/Mage/src/mage/cards/decks/importer/MWSDeckImporter.java +++ b/Mage/src/mage/cards/decks/importer/MWSDeckImporter.java @@ -73,7 +73,8 @@ public class MWSDeckImporter extends DeckImporter { if (!cards.isEmpty()) { cardInfo = cards.get(new Random().nextInt(cards.size())); } - } else { + } + if (cardInfo == null) { cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(lineName, true); } diff --git a/Mage/src/mage/cards/repository/CardRepository.java b/Mage/src/mage/cards/repository/CardRepository.java index 8a709d93107..6e96be70a8d 100644 --- a/Mage/src/mage/cards/repository/CardRepository.java +++ b/Mage/src/mage/cards/repository/CardRepository.java @@ -61,9 +61,9 @@ public enum CardRepository { private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE"; private static final String VERSION_ENTITY_NAME = "card"; // raise this if db structure was changed - private static final long CARD_DB_VERSION = 41; + private static final long CARD_DB_VERSION = 42; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 39; + private static final long CARD_CONTENT_VERSION = 42; private final Random random = new Random(); private Dao cardDao; diff --git a/Mage/src/mage/constants/AbilityWord.java b/Mage/src/mage/constants/AbilityWord.java index 29088e89d1f..f9c33524530 100644 --- a/Mage/src/mage/constants/AbilityWord.java +++ b/Mage/src/mage/constants/AbilityWord.java @@ -33,19 +33,37 @@ package mage.constants; */ public enum AbilityWord { - RALLY("Rally"), + BATTALION("Battalion"), BLOODRUSH("Bloodrush"), - CONVERGE("Converge"), + CHANNEL("Channel"), + CHROMA("Chroma"), CONSTELLATION("Constellation"), + CONVERGE("Converge"), + DOMAIN("Domain"), + FATEFUL_HOUR("Fateful hour"), FEROCIOUS("Ferocious"), FORMIDABLE("Formidable"), GRANDEUR("Grandeur"), HELLBENT("Hellbent"), HEROIC("Heroic"), + IMPRINT("Imprint"), + INSPIRED("Inspired"), + JOIN_FORCES("Join forces"), + KINSHIP("Kinship"), LANDFALL("Landfall"), + LIEUTENANT("Lieutenant"), METALCRAFT("Metalcraft"), + MORBID("Morbid"), PARLEY("Parley"), - RAID("Raid"); + RADIANCE("Radiance"), + RAID("Raid"), + RALLY("Rally"), + SPELL_MASTERY("Spell mastery"), + STRIVE("Strive"), + SWEEP("Sweep"), + TEMPTING_OFFER("Tempting offer"), + THRESHOLD("Threshold"), + WILL_OF_THE_COUNCIL("Will of the council"); private final String text; diff --git a/Mage/src/mage/constants/AsThoughEffectType.java b/Mage/src/mage/constants/AsThoughEffectType.java index 61c98018f24..546483c9d72 100644 --- a/Mage/src/mage/constants/AsThoughEffectType.java +++ b/Mage/src/mage/constants/AsThoughEffectType.java @@ -5,12 +5,13 @@ package mage.constants; * @author North */ public enum AsThoughEffectType { + ATTACK, ACTIVATE_HASTE, BLOCK_TAPPED, BLOCK_SHADOW, BLOCK_DRAGON, - BE_BLOCKED, + BE_BLOCKED, PLAY_FROM_NOT_OWN_HAND_ZONE, CAST_AS_INSTANT, ACTIVATE_AS_INSTANT, @@ -18,6 +19,7 @@ public enum AsThoughEffectType { HEXPROOF, PAY, LOOK_AT_FACE_DOWN, - SPEND_ANY_MANA, + SPEND_OTHER_MANA, + SPEND_ONLY_MANA, TARGET } diff --git a/Mage/src/mage/constants/SetTargetPointer.java b/Mage/src/mage/constants/SetTargetPointer.java index 6aeca5e026d..9f4acc8876d 100644 --- a/Mage/src/mage/constants/SetTargetPointer.java +++ b/Mage/src/mage/constants/SetTargetPointer.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.constants; /** @@ -33,5 +32,6 @@ package mage.constants; * @author LevelX2 */ public enum SetTargetPointer { - NONE, PLAYER, SPELL, CARD, PERMANENT; + + NONE, PLAYER, SPELL, CARD, PERMANENT, ATTACHED_TO_CONTROLLER; } diff --git a/Mage/src/mage/counters/CounterType.java b/Mage/src/mage/counters/CounterType.java index 3f952c8ef96..907e0cc8526 100644 --- a/Mage/src/mage/counters/CounterType.java +++ b/Mage/src/mage/counters/CounterType.java @@ -39,8 +39,11 @@ public enum CounterType { ARROWHEAD("arrowhead"), AWAKENING("awakening"), BLAZE("blaze"), + BOUNTY("bounty"), BRIBERY("bribery"), + CARRION("carrion"), CHARGE("charge"), + CORPSE("corpse"), CRYSTAL("crystal"), DELAY("delay"), DEPLETION("depletion"), @@ -50,6 +53,7 @@ public enum CounterType { DOOM("doom"), ELIXIR("elixir"), EON("eon"), + EXPERIENCE("experience"), EYEBALL("eyeball"), FADE("fade"), FATE("fate"), @@ -72,8 +76,10 @@ public enum CounterType { M2M2(new BoostCounter(-2, -2).name), MINING("mining"), MUSTER("muster"), + P0P1(new BoostCounter(0, 1).name), P1P0(new BoostCounter(1, 0).name), P1P1(new BoostCounter(1, 1).name), + P1P2(new BoostCounter(1, 2).name), P2P2(new BoostCounter(2, 2).name), PAGE("page"), PAIN("pain"), @@ -91,6 +97,7 @@ public enum CounterType { STRIFE("strife"), STUDY("study"), THEFT("theft"), + TIDE("tide"), TIME("time"), TOWER("tower"), VELOCITY("velocity"), @@ -131,10 +138,14 @@ public enum CounterType { */ public Counter createInstance(int amount) { switch (this) { + case P0P1: + return new BoostCounter(0, 1, amount); case P1P0: return new BoostCounter(1, 0, amount); case P1P1: return new BoostCounter(1, 1, amount); + case P1P2: + return new BoostCounter(1, 2, amount); case P2P2: return new BoostCounter(2, 2, amount); case M1M1: diff --git a/Mage/src/mage/filter/FilterPermanent.java b/Mage/src/mage/filter/FilterPermanent.java index 24b5418451a..5e14f969cc1 100644 --- a/Mage/src/mage/filter/FilterPermanent.java +++ b/Mage/src/mage/filter/FilterPermanent.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * 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.filter; import java.util.ArrayList; @@ -44,6 +43,7 @@ import mage.game.permanent.Permanent; * @author North */ public class FilterPermanent extends FilterObject implements FilterInPlay { + protected List>> extraPredicates = new ArrayList<>(); public FilterPermanent() { @@ -58,12 +58,13 @@ public class FilterPermanent extends FilterObject implements FilterIn public FilterPermanent(String name) { super(name); } - + public FilterPermanent(String subtype, String name) { super(name); this.add(new SubtypePredicate(subtype)); } + @Override public boolean match(Permanent permanent, UUID sourceId, UUID playerId, Game game) { if (!this.match(permanent, game)) { return false; diff --git a/Mage/src/mage/filter/predicate/mageobject/AnotherTargetPredicate.java b/Mage/src/mage/filter/predicate/mageobject/AnotherTargetPredicate.java new file mode 100644 index 00000000000..cd906f4a2ac --- /dev/null +++ b/Mage/src/mage/filter/predicate/mageobject/AnotherTargetPredicate.java @@ -0,0 +1,72 @@ +/* + * 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.filter.predicate.mageobject; + +import mage.MageObject; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.stack.StackObject; +import mage.target.Target; + +/** + * All targets that are already selected in other target definitions of the + * source are omitted To use this predicate you have to set the targetTag of all + * targets involved in the card constructor to a unique value (e.g. using 1,2,3 + * for three targets) + * + * @author LevelX2 + */ +public class AnotherTargetPredicate implements ObjectSourcePlayerPredicate> { + + private final int targetTag; + + public AnotherTargetPredicate(int targetTag) { + this.targetTag = targetTag; + } + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + StackObject source = game.getStack().getStackObject(input.getSourceId()); + if (source != null) { + for (Target target : source.getStackAbility().getTargets()) { + if (target.getTargetTag() > 0 // target is included in the target group to check + && target.getTargetTag() != targetTag // it's not the target of this predicate + && target.getTargets().contains(input.getObject().getId())) { // if the uuid already is used for another target in the group it's no allowed here + return false; + } + } + } + return true; + } + + @Override + public String toString() { + return "Another target"; + } +} diff --git a/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java b/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java index 74e5164049d..d6f3ad0a97d 100644 --- a/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java @@ -46,7 +46,7 @@ public class ColorPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - return input.getColor(game).contains(color); + return color != null && input.getColor(game).contains(color); } @Override diff --git a/Mage/src/mage/filter/predicate/other/TargetsPermanentPredicate.java b/Mage/src/mage/filter/predicate/other/TargetsPermanentPredicate.java new file mode 100644 index 00000000000..5471eae280c --- /dev/null +++ b/Mage/src/mage/filter/predicate/other/TargetsPermanentPredicate.java @@ -0,0 +1,76 @@ +/* + * 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.filter.predicate.other; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Mode; +import mage.filter.FilterPermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.target.Target; + +/** + * + * @author LoneFox + */ +public class TargetsPermanentPredicate implements ObjectSourcePlayerPredicate> { + + private final FilterPermanent targetFilter; + + public TargetsPermanentPredicate(FilterPermanent targetFilter) { + this.targetFilter = targetFilter; + } + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + StackObject object = game.getStack().getStackObject(input.getObject().getId()); + if(object != null) { + for(UUID modeId : object.getStackAbility().getModes().getSelectedModes()) { + Mode mode = object.getStackAbility().getModes().get(modeId); + for(Target target : mode.getTargets()) { + for(UUID targetId : target.getTargets()) { + Permanent permanent = game.getPermanentOrLKIBattlefield(targetId); + if(permanent != null && targetFilter.match(permanent, input.getSourceId(), input.getPlayerId(), game)) { + return true; + } + } + } + } + } + return false; + } + + @Override + public String toString() { + return "that targets " + targetFilter.getMessage(); + } +} diff --git a/Mage/src/mage/game/CardState.java b/Mage/src/mage/game/CardState.java index 1f471370f0b..cf63e960bb4 100644 --- a/Mage/src/mage/game/CardState.java +++ b/Mage/src/mage/game/CardState.java @@ -7,24 +7,25 @@ import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; import mage.counters.Counters; + /** * * @author BetaSteward */ public class CardState implements Serializable { - + protected boolean faceDown; protected Map info; protected Counters counters; protected Abilities abilities; - + private static final Map emptyInfo = new HashMap<>(); private static final Abilities emptyAbilities = new AbilitiesImpl<>(); - + public CardState() { counters = new Counters(); } - + public CardState(final CardState state) { this.faceDown = state.faceDown; if (state.info != null) { @@ -34,7 +35,7 @@ public class CardState implements Serializable { counters = state.counters.copy(); if (state.abilities != null) { abilities = new AbilitiesImpl<>(); - for (Ability ability: state.abilities) { + for (Ability ability : state.abilities) { abilities.add(ability.copy()); } } @@ -43,7 +44,7 @@ public class CardState implements Serializable { public CardState copy() { return new CardState(this); } - + public void setFaceDown(boolean value) { faceDown = value; } @@ -66,45 +67,45 @@ public class CardState implements Serializable { info.put(key, value); } } - + public Map getInfo() { if (info == null) { return emptyInfo; } return info; } - + public Abilities getAbilities() { if (abilities == null) { return emptyAbilities; } return abilities; } - + public void addAbility(Ability ability) { if (abilities == null) { abilities = new AbilitiesImpl<>(); } abilities.add(ability); - for (Ability sub: ability.getSubAbilities()) { + for (Ability sub : ability.getSubAbilities()) { abilities.add(sub); } } - + public void clearAbilities() { if (abilities != null) { - for (Ability ability: abilities) { - ability.setSourceId(null); - ability.setControllerId(null); - } +// for (Ability ability: abilities) { // Causes problems if temporary (gained) continuous effects are removed +// ability.setSourceId(null); +// ability.setControllerId(null); +// } abilities = null; } } - + public void clear() { counters.clear(); info = null; clearAbilities(); } - + } diff --git a/Mage/src/mage/game/Exile.java b/Mage/src/mage/game/Exile.java index 42b513092f3..8abb6d4cb59 100644 --- a/Mage/src/mage/game/Exile.java +++ b/Mage/src/mage/game/Exile.java @@ -107,12 +107,13 @@ public class Exile implements Serializable, Copyable { return cards; } - public void removeCard(Card card, Game game) { + public boolean removeCard(Card card, Game game) { for (ExileZone exile : exileZones.values()) { if (exile.contains(card.getId())) { - exile.remove(card); + return exile.remove(card.getId()); } } + return false; } @Override diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 1b19e9d30c5..432ade1bf80 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -115,6 +115,10 @@ public interface Game extends MageItem, Serializable { Permanent getPermanentOrLKIBattlefield(UUID permanentId); + Permanent getPermanentEntering(UUID permanentId); + + Map getPermanentsEntering(); + Map> getLKI(); Card getCard(UUID cardId); @@ -364,14 +368,14 @@ public interface Game extends MageItem, Serializable { * This version supports copying of copies of any depth. * * @param copyFromPermanent - * @param copyToPermanent + * @param copyToPermanentId * @param source * @param applier * @return */ - Permanent copyPermanent(Permanent copyFromPermanent, Permanent copyToPermanent, Ability source, ApplyToPermanent applier); + Permanent copyPermanent(Permanent copyFromPermanent, UUID copyToPermanentId, Ability source, ApplyToPermanent applier); - Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, Permanent copyToPermanent, Ability source, ApplyToPermanent applier); + Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, UUID copyToPermanentId, Ability source, ApplyToPermanent applier); Card copyCard(Card cardToCopy, Ability source, UUID newController); diff --git a/Mage/src/mage/game/GameCommanderImpl.java b/Mage/src/mage/game/GameCommanderImpl.java index 4181efc3e4c..2d9602d02ac 100644 --- a/Mage/src/mage/game/GameCommanderImpl.java +++ b/Mage/src/mage/game/GameCommanderImpl.java @@ -129,7 +129,9 @@ public abstract class GameCommanderImpl extends GameImpl { if (!mulliganedCards.containsKey(playerId)) { mulliganedCards.put(playerId, new CardsImpl()); } - card.moveToExile(null, "", null, this); + player.getHand().remove(card); + getExile().add(card); + getState().setZone(card.getId(), Zone.EXILED); card.setFaceDown(true, this); mulliganedCards.get(playerId).add(card); } @@ -168,7 +170,9 @@ public abstract class GameCommanderImpl extends GameImpl { if (player != null && mulliganedCards.containsKey(playerId)) { for (Card card : mulliganedCards.get(playerId).getCards(this)) { if (card != null) { - card.moveToZone(Zone.LIBRARY, null, this, false); + getExile().removeCard(card, this); + player.getLibrary().putOnTop(card, this); + getState().setZone(card.getId(), Zone.LIBRARY); card.setFaceDown(false, this); } } diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index a0b1c225791..6edfd87c44b 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -49,6 +49,7 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbility; import mage.abilities.common.ChancellorAbility; import mage.abilities.common.GemstoneCavernsAbility; @@ -77,6 +78,7 @@ import mage.constants.Outcome; import mage.constants.PhaseStep; import mage.constants.PlayerAction; import mage.constants.RangeOfInfluence; +import mage.constants.SpellAbilityType; import mage.constants.Zone; import mage.counters.CounterType; import mage.counters.Counters; @@ -174,6 +176,9 @@ public abstract class GameImpl implements Game, Serializable { // Used to check if an object was moved by the current effect in resolution (so Wrath like effect can be handled correctly) protected Map> shortLivingLKI = new EnumMap<>(Zone.class); + // Permanents entering the Battlefield while handling replacement effects before they are added to the battlefield + protected Map permanentsEntering = new HashMap<>(); + protected GameState state; private transient Stack savedStates = new Stack<>(); protected transient GameStates gameStates = new GameStates(); @@ -242,6 +247,7 @@ public abstract class GameImpl implements Game, Serializable { this.lki.putAll(game.lki); this.lkiExtended.putAll(game.lkiExtended); this.shortLivingLKI.putAll(game.shortLivingLKI); + this.permanentsEntering.putAll(game.permanentsEntering); if (logger.isDebugEnabled()) { copyCount++; copyTime += (System.currentTimeMillis() - t1); @@ -499,6 +505,16 @@ public abstract class GameImpl implements Game, Serializable { return permanent; } + @Override + public Permanent getPermanentEntering(UUID permanentId) { + return permanentsEntering.get(permanentId); + } + + @Override + public Map getPermanentsEntering() { + return permanentsEntering; + } + @Override public Card getCard(UUID cardId) { if (cardId == null) { @@ -655,7 +671,8 @@ public abstract class GameImpl implements Game, Serializable { } @Override - public void removeBookmark(int bookmark) { + public void removeBookmark(int bookmark + ) { if (!simulation) { if (bookmark != 0) { while (savedStates.size() > bookmark) { @@ -889,7 +906,7 @@ public abstract class GameImpl implements Game, Serializable { return; } getState().setChoosingPlayerId(choosingPlayerId); // needed to start/stop the timer if active - if (choosingPlayer != null && choosingPlayer.choose(Outcome.Benefit, targetPlayer, null, this)) { + if (choosingPlayer.choose(Outcome.Benefit, targetPlayer, null, this)) { startingPlayerId = targetPlayer.getTargets().get(0); } else if (getState().getPlayers().size() < 3) { // not possible to choose starting player, choosing player has probably conceded, so stop here @@ -1402,12 +1419,12 @@ public abstract class GameImpl implements Game, Serializable { } @Override - public Permanent copyPermanent(Permanent copyFromPermanent, Permanent copyToPermanent, Ability source, ApplyToPermanent applier) { - return copyPermanent(Duration.Custom, copyFromPermanent, copyToPermanent, source, applier); + public Permanent copyPermanent(Permanent copyFromPermanent, UUID copyToPermanentId, Ability source, ApplyToPermanent applier) { + return copyPermanent(Duration.Custom, copyFromPermanent, copyToPermanentId, source, applier); } @Override - public Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, Permanent copyToPermanent, Ability source, ApplyToPermanent applier) { + public Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, UUID copyToPermanentId, Ability source, ApplyToPermanent applier) { Permanent newBluePrint = null; // handle copies of copies for (Effect effect : getState().getContinuousEffects().getLayeredEffects(this)) { @@ -1440,7 +1457,7 @@ public abstract class GameImpl implements Game, Serializable { applier.apply(this, newBluePrint); } - CopyEffect newEffect = new CopyEffect(duration, newBluePrint, copyToPermanent.getId()); + CopyEffect newEffect = new CopyEffect(duration, newBluePrint, copyToPermanentId); newEffect.newId(); newEffect.setApplier(applier); Ability newAbility = source.copy(); @@ -1465,7 +1482,7 @@ public abstract class GameImpl implements Game, Serializable { } else { TriggeredAbility newAbility = ability.copy(); newAbility.newId(); - // Too early, becuase no targets set yet !!!!!!!!!!! + // Too early, because no targets set yet !!!!!!!!!!! for (Effect effect : newAbility.getEffects()) { effect.getTargetPointer().init(this, newAbility); } @@ -1686,11 +1703,22 @@ public abstract class GameImpl implements Game, Serializable { } } } else { + SpellAbility spellAbility = perm.getSpellAbility(); if (perm.getSpellAbility().getTargets().isEmpty()) { + for (Ability ability : perm.getAbilities(this)) { + if ((ability instanceof SpellAbility) + && SpellAbilityType.BASE_ALTERNATE.equals(((SpellAbility) ability).getSpellAbilityType()) + && !ability.getTargets().isEmpty()) { + spellAbility = (SpellAbility) ability; + break; + } + } + } + if (spellAbility.getTargets().isEmpty()) { Permanent enchanted = this.getPermanent(perm.getAttachedTo()); logger.error("Aura without target: " + perm.getName() + " attached to " + (enchanted == null ? " null" : enchanted.getName())); } else { - Target target = perm.getSpellAbility().getTargets().get(0); + Target target = spellAbility.getTargets().get(0); if (target instanceof TargetPermanent) { Permanent attachedTo = getPermanent(perm.getAttachedTo()); if (attachedTo == null || !attachedTo.getAttachments().contains(perm.getId())) { @@ -1706,7 +1734,7 @@ public abstract class GameImpl implements Game, Serializable { } } } else { - Filter auraFilter = perm.getSpellAbility().getTargets().get(0).getFilter(); + Filter auraFilter = spellAbility.getTargets().get(0).getFilter(); if (auraFilter instanceof FilterControlledCreaturePermanent) { if (!((FilterControlledCreaturePermanent) auraFilter).match(attachedTo, perm.getId(), perm.getControllerId(), this) || attachedTo.cantBeEnchantedBy(perm, this)) { @@ -1737,7 +1765,7 @@ public abstract class GameImpl implements Game, Serializable { somethingHappened = true; } } else { - Filter auraFilter = perm.getSpellAbility().getTargets().get(0).getFilter(); + Filter auraFilter = spellAbility.getTargets().get(0).getFilter(); if (!auraFilter.match(attachedToPlayer, this) || attachedToPlayer.hasProtectionFrom(perm, this)) { if (movePermanentToGraveyardWithInfo(perm)) { somethingHappened = true; @@ -1849,7 +1877,7 @@ public abstract class GameImpl implements Game, Serializable { Player controller = this.getPlayer(legend.getControllerId()); if (controller != null) { Target targetLegendaryToKeep = new TargetPermanent(filterLegendName); - targetLegendaryToKeep.setTargetName(new StringBuilder(legend.getName()).append(" to keep (Legendary Rule)?").toString()); + targetLegendaryToKeep.setTargetName(legend.getName() + " to keep (Legendary Rule)?"); controller.chooseTarget(Outcome.Benefit, targetLegendaryToKeep, null, this); for (Permanent dupLegend : getBattlefield().getActivePermanents(filterLegendName, legend.getControllerId(), this)) { if (!targetLegendaryToKeep.getTargets().contains(dupLegend.getId())) { @@ -2526,8 +2554,10 @@ public abstract class GameImpl implements Game, Serializable { card.setZone(Zone.BATTLEFIELD, this); card.setOwnerId(ownerId); PermanentCard permanent = new PermanentCard(card.getCard(), ownerId, this); - getBattlefield().addPermanent(permanent); + getPermanentsEntering().put(permanent.getId(), permanent); permanent.entersBattlefield(permanent.getId(), this, Zone.OUTSIDE, false); + getBattlefield().addPermanent(permanent); + getPermanentsEntering().remove(permanent.getId()); ((PermanentImpl) permanent).removeSummoningSickness(); if (card.isTapped()) { permanent.setTapped(true); diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index 3b1bdd805ad..68f312ad7aa 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -624,7 +624,7 @@ public class GameState implements Serializable, Copyable { public Permanent getPermanent(UUID permanentId) { if (permanentId != null && battlefield.containsPermanent(permanentId)) { Permanent permanent = battlefield.getPermanent(permanentId); - setZone(permanent.getId(), Zone.BATTLEFIELD); // shouldn't this be set anyway? (LevelX2) + // setZone(permanent.getId(), Zone.BATTLEFIELD); // shouldn't this be set anyway? (LevelX2) return permanent; } return null; diff --git a/Mage/src/mage/game/combat/Combat.java b/Mage/src/mage/game/combat/Combat.java index b07f052ff3c..b03625617c6 100644 --- a/Mage/src/mage/game/combat/Combat.java +++ b/Mage/src/mage/game/combat/Combat.java @@ -294,9 +294,9 @@ public class Combat implements Serializable, Copyable { } // force attack only if a defender can be attacked without paying a cost if (!defendersCostlessAttackable.isEmpty()) { + creaturesForcedToAttack.put(creature.getId(), defendersForcedToAttack); // No need to attack a special defender if (defendersForcedToAttack.isEmpty()) { - creaturesForcedToAttack.put(creature.getId(), defendersForcedToAttack); if (defendersForcedToAttack.isEmpty()) { if (defendersCostlessAttackable.size() == 1) { player.declareAttacker(creature.getId(), defenders.iterator().next(), game, false); @@ -663,7 +663,7 @@ public class Combat implements Serializable, Copyable { if (mayBlock) { if (controller.isHuman()) { if (!game.isSimulation()) { - game.informPlayer(controller, "Creature should block this turn: " + creature.getLogName()); + game.informPlayer(controller, "Creature should block this turn: " + creature.getIdName()); } } else { Player defender = game.getPlayer(creature.getControllerId()); diff --git a/Mage/src/mage/game/events/GameEvent.java b/Mage/src/mage/game/events/GameEvent.java index 93d4cef0249..38e9ebeefe8 100644 --- a/Mage/src/mage/game/events/GameEvent.java +++ b/Mage/src/mage/game/events/GameEvent.java @@ -185,6 +185,8 @@ public class GameEvent implements Serializable { ENCHANT_PLAYER, ENCHANTED_PLAYER, CAN_TAKE_MULLIGAN, FLIP_COIN, COIN_FLIPPED, SCRY, FATESEAL, + PAID_CUMULATIVE_UPKEEP, + DIDNT_PAY_CUMULATIVE_UPKEEP, //permanent events ENTERS_THE_BATTLEFIELD, TAP, TAPPED, TAPPED_FOR_MANA, @@ -241,7 +243,6 @@ public class GameEvent implements Serializable { //combat events COMBAT_DAMAGE_APPLIED, SELECTED_ATTACKER, SELECTED_BLOCKER; - } public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId) { diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index 84ae5f3986f..b7e8178a815 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -109,6 +109,8 @@ public interface Permanent extends Card, Controllable { UUID getAttachedTo(); + int getAttachedToZoneChangeCounter(); + void attachTo(UUID permanentId, Game game); boolean addAttachment(UUID permanentId, Game game); diff --git a/Mage/src/mage/game/permanent/PermanentCard.java b/Mage/src/mage/game/permanent/PermanentCard.java index af54c92b1dc..2e87290d9e2 100644 --- a/Mage/src/mage/game/permanent/PermanentCard.java +++ b/Mage/src/mage/game/permanent/PermanentCard.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * 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.game.permanent; import java.util.ArrayList; @@ -49,20 +48,21 @@ public class PermanentCard extends PermanentImpl { protected int maxLevelCounters; protected Card card; - protected int zoneChangeCounter; + // protected int zoneChangeCounter; public PermanentCard(Card card, UUID controllerId, Game game) { super(card.getId(), card.getOwnerId(), controllerId, card.getName()); - this.card = card.copy(); + // this.card = card.copy(); + this.card = card; init(card, game); } private void init(Card card, Game game) { copyFromCard(card); - this.zoneChangeCounter = card.getZoneChangeCounter(game); + // this.zoneChangeCounter = card.getZoneChangeCounter(game); /*if (card.getCardType().contains(CardType.PLANESWALKER)) { - this.loyalty = new MageInt(card.getLoyalty().getValue()); - }*/ + this.loyalty = new MageInt(card.getLoyalty().getValue()); + }*/ if (card instanceof LevelerCard) { maxLevelCounters = ((LevelerCard) card).getMaxLevelCounters(); } @@ -79,7 +79,6 @@ public class PermanentCard extends PermanentImpl { super(permanent); this.card = permanent.card.copy(); this.maxLevelCounters = permanent.maxLevelCounters; - this.zoneChangeCounter = permanent.zoneChangeCounter; } @Override @@ -94,13 +93,12 @@ public class PermanentCard extends PermanentImpl { this.name = card.getName(); this.abilities.clear(); if (this.faceDown) { - for (Ability ability: card.getAbilities()) { + for (Ability ability : card.getAbilities()) { if (ability.getWorksFaceDown()) { this.abilities.add(ability.copy()); } } - } - else { + } else { this.abilities = card.getAbilities().copy(); } this.abilities.setControllerId(this.controllerId); @@ -135,6 +133,7 @@ public class PermanentCard extends PermanentImpl { public Card getCard() { return card; } + @Override public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag) { return moveToZone(toZone, sourceId, game, flag, null); @@ -255,20 +254,20 @@ public class PermanentCard extends PermanentImpl { } return super.getManaCost(); } - + @Override public int getZoneChangeCounter(Game game) { - return this.zoneChangeCounter; + return card.getZoneChangeCounter(game); } @Override public void updateZoneChangeCounter(Game game) { - this.zoneChangeCounter++; + card.updateZoneChangeCounter(game); } @Override public void setZoneChangeCounter(int value, Game game) { - this.zoneChangeCounter = value; + card.setZoneChangeCounter(value, game); } } diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 93b2f355be7..3b767772abc 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -115,6 +115,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { protected Map> connectedCards = new HashMap<>(); protected HashSet dealtDamageByThisTurn; protected UUID attachedTo; + protected int attachedToZoneChangeCounter; protected UUID pairedCard; protected Counters counters; protected List markedDamage; @@ -172,6 +173,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } this.counters = permanent.counters.copy(); this.attachedTo = permanent.attachedTo; + this.attachedToZoneChangeCounter = permanent.attachedToZoneChangeCounter; this.minBlockedBy = permanent.minBlockedBy; this.maxBlockedBy = permanent.maxBlockedBy; this.transformed = permanent.transformed; @@ -645,6 +647,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { if (!game.replaceEvent(new GameEvent(GameEvent.EventType.ATTACH, objectId, permanentId, controllerId))) { this.attachments.add(permanentId); Permanent attachment = game.getPermanent(permanentId); + if (attachment == null) { + attachment = game.getPermanentEntering(permanentId); + } if (attachment != null) { attachment.attachTo(objectId, game); game.fireEvent(new GameEvent(GameEvent.EventType.ATTACHED, objectId, permanentId, controllerId)); @@ -676,6 +681,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return attachedTo; } + @Override + public int getAttachedToZoneChangeCounter() { + return attachedToZoneChangeCounter; + } + @Override public void addConnectedCard(String key, UUID connectedCard) { if (this.connectedCards.containsKey(key)) { @@ -712,6 +722,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { } } this.attachedTo = permanentId; + this.attachedToZoneChangeCounter = game.getState().getZoneChangeCounter(permanentId); for (Ability ability : this.getAbilities()) { for (Iterator ite = ability.getEffects(game, EffectType.CONTINUOUS).iterator(); ite.hasNext();) { ContinuousEffect effect = (ContinuousEffect) ite.next(); @@ -908,11 +919,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { EntersTheBattlefieldEvent event = new EntersTheBattlefieldEvent(this, sourceId, getControllerId(), fromZone); if (!game.replaceEvent(event)) { if (fireEvent) { - if (sourceId == null) { // play lands - game.fireEvent(event); - } else { // from effects - game.addSimultaneousEvent(event); - } + game.addSimultaneousEvent(event); } return true; } diff --git a/Mage/src/mage/game/permanent/token/Token.java b/Mage/src/mage/game/permanent/token/Token.java index e60605b0892..5f9686cf8a9 100644 --- a/Mage/src/mage/game/permanent/token/Token.java +++ b/Mage/src/mage/game/permanent/token/Token.java @@ -42,6 +42,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; import mage.players.Player; @@ -148,31 +149,43 @@ public class Token extends MageObjectImpl { GameEvent event = new GameEvent(EventType.CREATE_TOKEN, null, sourceId, controllerId, amount, this.getCardType().contains(CardType.CREATURE)); if (!game.replaceEvent(event)) { amount = event.getAmount(); + + List permanents = new ArrayList<>(); + List permanentsEntered = new ArrayList<>(); + for (int i = 0; i < amount; i++) { PermanentToken newToken = new PermanentToken(this, event.getPlayerId(), setCode, game); // use event.getPlayerId() because it can be replaced by replacement effect game.getState().addCard(newToken); - game.addPermanent(newToken); - if (tapped) { - newToken.setTapped(true); - } - this.lastAddedTokenIds.add(newToken.getId()); - this.lastAddedTokenId = newToken.getId(); - game.setScopeRelevant(true); - game.applyEffects(); - boolean entered = newToken.entersBattlefield(sourceId, game, Zone.OUTSIDE, true); - game.setScopeRelevant(false); - game.applyEffects(); - if (entered) { - game.fireEvent(new ZoneChangeEvent(newToken, event.getPlayerId(), Zone.OUTSIDE, Zone.BATTLEFIELD)); - if (attacking && game.getCombat() != null) { - game.getCombat().addAttackingCreature(newToken.getId(), game); - } - if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + " puts a " + newToken.getLogName() + " token onto the battlefield"); - } + permanents.add(newToken); + game.getPermanentsEntering().put(newToken.getId(), newToken); + newToken.setTapped(tapped); + } + game.setScopeRelevant(true); + for (Permanent permanent : permanents) { + if (permanent.entersBattlefield(sourceId, game, Zone.OUTSIDE, true)) { + permanentsEntered.add(permanent); + } else { + game.getPermanentsEntering().remove(permanent.getId()); } } + game.setScopeRelevant(false); + for (Permanent permanent : permanentsEntered) { + game.addPermanent(permanent); + permanent.setZone(Zone.BATTLEFIELD, game); + game.getPermanentsEntering().remove(permanent.getId()); + this.lastAddedTokenIds.add(permanent.getId()); + this.lastAddedTokenId = permanent.getId(); + game.addSimultaneousEvent(new ZoneChangeEvent(permanent, permanent.getControllerId(), Zone.OUTSIDE, Zone.BATTLEFIELD)); + if (attacking && game.getCombat() != null) { + game.getCombat().addAttackingCreature(permanent.getId(), game); + } + if (!game.isSimulation()) { + game.informPlayers(controller.getLogName() + " puts a " + permanent.getLogName() + " token onto the battlefield"); + } + + } + game.applyEffects(); return true; } return false; diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index b1cf6950138..58d81e3fe6e 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -167,6 +167,10 @@ public class Spell extends StackObjImpl implements Card { @Override public boolean resolve(Game game) { boolean result; + Player controller = game.getPlayer(getControllerId()); + if (controller == null) { + return false; + } if (this.getCardType().contains(CardType.INSTANT) || this.getCardType().contains(CardType.SORCERY)) { int index = 0; result = false; @@ -203,11 +207,8 @@ public class Spell extends StackObjImpl implements Card { } } if (game.getState().getZone(card.getMainCard().getId()) == Zone.STACK) { - if (isCopy() == card.isCopy()) { - Player player = game.getPlayer(getControllerId()); - if (player != null) { - player.moveCards(card, Zone.STACK, Zone.GRAVEYARD, ability, game); - } + if (!isCopy()) { + controller.moveCards(card, Zone.GRAVEYARD, ability, game); } } return result; @@ -221,14 +222,14 @@ public class Spell extends StackObjImpl implements Card { } else if (this.getCardType().contains(CardType.ENCHANTMENT) && this.getSubtype().contains("Aura")) { if (ability.getTargets().stillLegal(ability, game)) { updateOptionalCosts(0); - boolean bestow = this.getSpellAbility() instanceof BestowAbility; + boolean bestow = ability instanceof BestowAbility; if (bestow) { // Must be removed first time, after that will be removed by continous effect // Otherwise effects like evolve trigger from creature comes into play event card.getCardType().remove(CardType.CREATURE); card.getSubtype().add("Aura"); } - if (card.putOntoBattlefield(game, fromZone, ability.getSourceId(), controllerId)) { + if (controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null)) { if (bestow) { // card will be copied during putOntoBattlefield, so the card of CardPermanent has to be changed // TODO: Find a better way to prevent bestow creatures from being effected by creature affecting abilities @@ -238,8 +239,6 @@ public class Spell extends StackObjImpl implements Card { ((PermanentCard) permanent).getCard().getCardType().add(CardType.CREATURE); ((PermanentCard) permanent).getCard().getSubtype().remove("Aura"); } - card.getCardType().add(CardType.CREATURE); - card.getSubtype().remove("Aura"); } return ability.resolve(game); } @@ -251,8 +250,7 @@ public class Spell extends StackObjImpl implements Card { // Aura has no legal target and its a bestow enchantment -> Add it to battlefield as creature if (this.getSpellAbility() instanceof BestowAbility) { updateOptionalCosts(0); - result = card.putOntoBattlefield(game, fromZone, ability.getSourceId(), controllerId); - return result; + return controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null); } else { //20091005 - 608.2b if (!game.isSimulation()) { @@ -263,8 +261,7 @@ public class Spell extends StackObjImpl implements Card { } } else { updateOptionalCosts(0); - result = card.putOntoBattlefield(game, fromZone, ability.getSourceId(), controllerId, false, faceDown); - return result; + return controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null); } } @@ -622,7 +619,8 @@ public class Spell extends StackObjImpl implements Card { } public Spell copySpell() { - return new Spell(this.card.copy(), this.ability.copySpell(), this.controllerId, this.fromZone); + // replaced card.copy by copy (card content should no longer be changed) + return new Spell(this.card, this.ability.copySpell(), this.controllerId, this.fromZone); } @Override @@ -646,6 +644,11 @@ public class Spell extends StackObjImpl implements Card { } } + @Override + public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) { + return card.removeFromZone(game, fromZone, sourceId); + } + @Override public boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag) { return moveToZone(zone, sourceId, game, flag, null); @@ -847,4 +850,9 @@ public class Spell extends StackObjImpl implements Card { return countered; } + @Override + public void checkForCountersToAdd(Permanent permanent, Game game) { + throw new UnsupportedOperationException("Not supported for Spell"); + } + } diff --git a/Mage/src/mage/game/stack/SpellStack.java b/Mage/src/mage/game/stack/SpellStack.java index 62ea3bb72ca..c22c449d2b6 100644 --- a/Mage/src/mage/game/stack/SpellStack.java +++ b/Mage/src/mage/game/stack/SpellStack.java @@ -34,6 +34,7 @@ import mage.MageObject; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; +import org.apache.log4j.Logger; /** * @@ -41,6 +42,8 @@ import mage.game.events.GameEvent; */ public class SpellStack extends ArrayDeque { + private static final Logger logger = Logger.getLogger(SpellStack.class); + protected Date dateLastAdded; public SpellStack() { @@ -61,18 +64,21 @@ public class SpellStack extends ArrayDeque { top.resolve(game); } finally { if (top != null) { - this.remove(top); + if (contains(top)) { + logger.warn("StackObject was still on the stack after resoving" + top.getName()); + this.remove(top); + } } } } - public void remove(StackObject object) { + public boolean remove(StackObject object) { for (StackObject spell : this) { if (spell.getId().equals(object.getId())) { - super.remove(spell); - return; + return super.remove(spell); } } + return false; } public boolean counter(UUID objectId, UUID sourceId, Game game) { diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index aa8d58de407..b23de503454 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -104,12 +104,15 @@ public class StackAbility extends StackObjImpl implements Ability { @Override public boolean resolve(Game game) { if (ability.getTargets().stillLegal(ability, game)) { - return ability.resolve(game); + boolean result = ability.resolve(game); + game.getStack().remove(this); + return result; } if (!game.isSimulation()) { game.informPlayers("Ability has been fizzled: " + getRule()); } counter(null, game); + game.getStack().remove(this); return false; } @@ -237,6 +240,11 @@ public class StackAbility extends StackObjImpl implements Ability { return ability.getEffects(); } + @Override + public Effects getAllEffects() { + return ability.getAllEffects(); + } + @Override public Effects getEffects(Game game, EffectType effectType) { return ability.getEffects(game, effectType); diff --git a/Mage/src/mage/game/stack/StackObjImpl.java b/Mage/src/mage/game/stack/StackObjImpl.java index 8ca3c7a6be5..6cf33e65ea5 100644 --- a/Mage/src/mage/game/stack/StackObjImpl.java +++ b/Mage/src/mage/game/stack/StackObjImpl.java @@ -106,6 +106,7 @@ public abstract class StackObjImpl implements StackObject { public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { Player targetController = game.getPlayer(targetControllerId); if (targetController != null) { + StringBuilder oldTargetDescription = new StringBuilder(); StringBuilder newTargetDescription = new StringBuilder(); // Fused split spells or spells where "Splice on Arcane" was used can have more than one ability Abilities objectAbilities = new AbilitiesImpl<>(); @@ -118,6 +119,7 @@ public abstract class StackObjImpl implements StackObject { // Some spells can have more than one mode for (UUID modeId : ability.getModes().getSelectedModes()) { Mode mode = ability.getModes().get(modeId); + oldTargetDescription.append(ability.getTargetDescription(mode.getTargets(), game)); for (Target target : mode.getTargets()) { Target newTarget = chooseNewTarget(targetController, ability, mode, target, forceChange, filterNewTarget, game); // clear the old target and copy all targets from new target @@ -131,7 +133,7 @@ public abstract class StackObjImpl implements StackObject { } } - if (newTargetDescription.length() > 0 && !game.isSimulation()) { + if (!newTargetDescription.toString().equals(oldTargetDescription.toString()) && !game.isSimulation()) { game.informPlayers(this.getLogName() + " is now " + newTargetDescription.toString()); } return true; diff --git a/Mage/src/mage/game/tournament/TournamentImpl.java b/Mage/src/mage/game/tournament/TournamentImpl.java index 2719c4a277d..a578a681107 100644 --- a/Mage/src/mage/game/tournament/TournamentImpl.java +++ b/Mage/src/mage/game/tournament/TournamentImpl.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.game.tournament; import java.util.ArrayList; @@ -33,6 +32,7 @@ import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; @@ -114,7 +114,7 @@ public abstract class TournamentImpl implements Tournament { } synchronized (this) { this.notifyAll(); - } + } } @Override @@ -188,15 +188,16 @@ public abstract class TournamentImpl implements Tournament { Round round = new Round(rounds.size() + 1, this); rounds.add(round); List roundPlayers = getActivePlayers(); + + // search the player with a bye last round + List playerWithByes = getTournamentPlayersWithBye(roundPlayers); + while (roundPlayers.size() > 1) { - int i = rnd.nextInt(roundPlayers.size()); - TournamentPlayer player1 = roundPlayers.get(i); - roundPlayers.remove(i); - i = rnd.nextInt(roundPlayers.size()); - TournamentPlayer player2 = roundPlayers.get(i); - roundPlayers.remove(i); + TournamentPlayer player1 = getNextAvailablePlayer(roundPlayers, playerWithByes); + TournamentPlayer player2 = getNextAvailablePlayer(roundPlayers, playerWithByes); round.addPairing(new TournamentPairing(player1, player2)); } + if (roundPlayers.size() > 0) { // player free round - add to bye players of this round TournamentPlayer player1 = roundPlayers.get(0); @@ -208,8 +209,39 @@ public abstract class TournamentImpl implements Tournament { return round; } + private TournamentPlayer getNextAvailablePlayer(List roundPlayers, List playerWithByes) { + TournamentPlayer nextPlayer; + if (playerWithByes.isEmpty()) { + int i = rnd.nextInt(roundPlayers.size()); + nextPlayer = roundPlayers.get(i); + roundPlayers.remove(i); + } else { // prefer players with byes to pair + Iterator iterator = playerWithByes.iterator(); + nextPlayer = iterator.next(); + iterator.remove(); + roundPlayers.remove(nextPlayer); + } + return nextPlayer; + } + + private List getTournamentPlayersWithBye(List roundPlayers) { + List playersWithBye = new ArrayList<>(); + if (rounds.size() > 1) { + for (int i = rounds.size() - 2; i >= 0; i--) { + Round oldRound = rounds.get(i); + if (oldRound != null && !oldRound.getPlayerByes().isEmpty()) { + TournamentPlayer tournamentPlayerWithBye = oldRound.getPlayerByes().iterator().next(); + if (roundPlayers.contains(tournamentPlayerWithBye)) { + playersWithBye.add(tournamentPlayerWithBye); + } + } + } + } + return playersWithBye; + } + protected void playRound(Round round) { - for (TournamentPairing pair: round.getPairs()) { + for (TournamentPairing pair : round.getPairs()) { playMatch(pair); } updateResults(); // show points from byes @@ -227,7 +259,7 @@ public abstract class TournamentImpl implements Tournament { protected List getActivePlayers() { List activePlayers = new ArrayList<>(); - for (TournamentPlayer player: players.values()) { + for (TournamentPlayer player : players.values()) { if (!player.isEliminated()) { activePlayers.add(player); } @@ -240,13 +272,13 @@ public abstract class TournamentImpl implements Tournament { */ @Override public void updateResults() { - for (TournamentPlayer player: players.values()) { + for (TournamentPlayer player : players.values()) { player.setResults(""); player.setPoints(0); player.setStateInfo(""); } - for (Round round: rounds) { - for (TournamentPairing pair: round.getPairs()) { + for (Round round : rounds) { + for (TournamentPairing pair : round.getPairs()) { Match match = pair.getMatch(); if (match != null && match.hasEnded()) { TournamentPlayer tp1 = pair.getPlayer1(); @@ -276,9 +308,9 @@ public abstract class TournamentImpl implements Tournament { tp2.setResults(addRoundResult(round.getRoundNumber(), pair, tp2, tp1)); // Add points - if (mp2.hasQuit() || mp1.getWins() > mp2.getWins()) { + if ((!mp1.hasQuit() && mp1.getWins() > mp2.getWins()) || mp2.hasQuit()) { tp1.setPoints(tp1.getPoints() + 3); - } else if (mp1.hasQuit() || mp1.getWins() < mp2.getWins()) { + } else if ((!mp2.hasQuit() && mp1.getWins() < mp2.getWins()) || mp1.hasQuit()) { tp2.setPoints(tp2.getPoints() + 3); } else { tp1.setPoints(tp1.getPoints() + 1); @@ -299,7 +331,7 @@ public abstract class TournamentImpl implements Tournament { playerResult.append(getMatchResultString(tournamentPlayer, opponentPlayer, pair.getMatch())); return playerResult.toString(); } - + private static String getMatchResultString(TournamentPlayer p1, TournamentPlayer p2, Match match) { MatchPlayer mp1 = match.getPlayer(p1.getPlayer().getId()); MatchPlayer mp2 = match.getPlayer(p2.getPlayer().getId()); @@ -307,22 +339,22 @@ public abstract class TournamentImpl implements Tournament { matchResult.append(p2.getPlayer().getName()); matchResult.append(" [").append(mp1.getWins()); if (mp1.hasQuit()) { - matchResult.append(mp1.getPlayer().hasIdleTimeout()? "I" :(mp1.getPlayer().hasTimerTimeout()?"T":"Q")); + matchResult.append(mp1.getPlayer().hasIdleTimeout() ? "I" : (mp1.getPlayer().hasTimerTimeout() ? "T" : "Q")); } if (match.getDraws() > 0) { matchResult.append("-").append(match.getDraws()); } matchResult.append("-").append(mp2.getWins()); if (mp2.hasQuit()) { - matchResult.append(mp2.getPlayer().hasIdleTimeout()? "I" :(mp2.getPlayer().hasTimerTimeout()?"T":"Q")); - } + matchResult.append(mp2.getPlayer().hasIdleTimeout() ? "I" : (mp2.getPlayer().hasTimerTimeout() ? "T" : "Q")); + } matchResult.append("] "); return matchResult.toString(); } - + @Override public boolean isDoneConstructing() { - for (TournamentPlayer player: this.players.values()) { + for (TournamentPlayer player : this.players.values()) { if (!player.isDoneConstructing()) { return false; } @@ -332,7 +364,7 @@ public abstract class TournamentImpl implements Tournament { @Override public boolean allJoined() { - for (TournamentPlayer player: this.players.values()) { + for (TournamentPlayer player : this.players.values()) { if (!player.isJoined()) { return false; } @@ -358,27 +390,26 @@ public abstract class TournamentImpl implements Tournament { public void construct() { tableEventSource.fireTableEvent(EventType.CONSTRUCT); if (!isAbort()) { - for (final TournamentPlayer player: players.values()) { + for (final TournamentPlayer player : players.values()) { player.setConstructing(); new Thread( - new Runnable() { - @Override - public void run() { - player.getPlayer().construct(TournamentImpl.this, player.getDeck()); + new Runnable() { + @Override + public void run() { + player.getPlayer().construct(TournamentImpl.this, player.getDeck()); + } } - } ).start(); } // add autosubmit trigger - - - synchronized(this) { + + synchronized (this) { while (!isDoneConstructing()) { try { this.wait(); } catch (InterruptedException ex) { - + } } } @@ -387,7 +418,7 @@ public abstract class TournamentImpl implements Tournament { } protected void openBoosters() { - for (TournamentPlayer player: this.players.values()) { + for (TournamentPlayer player : this.players.values()) { player.setDeck(new Deck()); if (options.getLimitedOptions().getDraftCube() != null) { DraftCube cube = options.getLimitedOptions().getDraftCube(); @@ -395,7 +426,7 @@ public abstract class TournamentImpl implements Tournament { player.getDeck().getSideboard().addAll(cube.createBooster()); } } else { - for (ExpansionSet set: sets) { + for (ExpansionSet set : sets) { player.getDeck().getSideboard().addAll(set.createBooster()); } } @@ -406,7 +437,7 @@ public abstract class TournamentImpl implements Tournament { public void resetBufferedCards() { HashSet setsDone = new HashSet<>(); - for(ExpansionSet set: sets) { + for (ExpansionSet set : sets) { if (!setsDone.contains(set)) { set.removeSavedCards(); setsDone.add(set); @@ -454,7 +485,7 @@ public abstract class TournamentImpl implements Tournament { protected void winners() { List winners = new ArrayList<>(); int pointsWinner = 1; // with less than 1 point you can't win - for(TournamentPlayer tournamentPlayer: this.getPlayers()) { + for (TournamentPlayer tournamentPlayer : this.getPlayers()) { if (pointsWinner < tournamentPlayer.getPoints()) { winners.clear(); winners.add(tournamentPlayer); @@ -464,14 +495,14 @@ public abstract class TournamentImpl implements Tournament { } } // set winner state for the players with the most points > 0 - for (TournamentPlayer tournamentPlayer: winners) { + for (TournamentPlayer tournamentPlayer : winners) { tournamentPlayer.setStateInfo("Winner"); } } @Override public void cleanUpOnTournamentEnd() { - for(TournamentPlayer tournamentPlayer: players.values()) { + for (TournamentPlayer tournamentPlayer : players.values()) { tournamentPlayer.CleanUpOnTournamentEnd(); } } @@ -509,7 +540,6 @@ public abstract class TournamentImpl implements Tournament { this.startTime = new Date(); } - @Override public void setStepStartTime(Date stepStartTime) { this.stepStartTime = stepStartTime; diff --git a/Mage/src/mage/game/tournament/TournamentSwiss.java b/Mage/src/mage/game/tournament/TournamentSwiss.java index 9f6c970bd4a..6855e57eb01 100644 --- a/Mage/src/mage/game/tournament/TournamentSwiss.java +++ b/Mage/src/mage/game/tournament/TournamentSwiss.java @@ -46,8 +46,8 @@ public abstract class TournamentSwiss extends TournamentImpl { } @Override - protected void runTournament() { - for (Map.Entry entry: players.entrySet()) { + protected void runTournament() { + for (Map.Entry entry : players.entrySet()) { if (entry.getValue().getPlayer().autoLoseGame()) { entry.getValue().setEliminated(); entry.getValue().setResults("Auto Eliminated"); @@ -57,7 +57,7 @@ public abstract class TournamentSwiss extends TournamentImpl { while (this.getActivePlayers().size() > 1 && this.getNumberRounds() > this.getRounds().size()) { // check if some player got killed / disconnected meanwhile and update their state tableEventSource.fireTableEvent(TableEvent.EventType.CHECK_STATE_PLAYERS); - // Swiss pairing + // Swiss pairing Round round = createRoundSwiss(); playRound(round); } @@ -70,8 +70,9 @@ public abstract class TournamentSwiss extends TournamentImpl { List roundPlayers = getActivePlayers(); // sort players by tournament points Collections.sort(roundPlayers, new Comparator() { - @Override public int compare(TournamentPlayer p1, TournamentPlayer p2) { - return p2.getPoints() - p1.getPoints(); + @Override + public int compare(TournamentPlayer p1, TournamentPlayer p2) { + return p2.getPoints() - p1.getPoints(); } }); @@ -80,9 +81,9 @@ public abstract class TournamentSwiss extends TournamentImpl { TournamentPlayer player1 = roundPlayers.get(0); roundPlayers.remove(0); TournamentPlayer playerForPossibleSecondPairing = null; - for (TournamentPlayer player2: roundPlayers) { + for (TournamentPlayer player2 : roundPlayers) { if (alreadyPaired(player1, player2)) { - // if laready paired but equal ponts -> remember if second pairing is needed + // if already paired but equal points -> remember if second pairing is needed if (playerForPossibleSecondPairing == null) { playerForPossibleSecondPairing = player2; } @@ -126,7 +127,7 @@ public abstract class TournamentSwiss extends TournamentImpl { protected boolean alreadyPaired(TournamentPlayer player1, TournamentPlayer player2) { for (Round round : rounds) { - for (TournamentPairing pairing: round.getPairs()) { + for (TournamentPairing pairing : round.getPairs()) { if (pairing.getPlayer1().equals(player1) || pairing.getPlayer2().equals(player1)) { if (pairing.getPlayer1().equals(player2) || pairing.getPlayer2().equals(player2)) { return true; diff --git a/Mage/src/mage/players/Library.java b/Mage/src/mage/players/Library.java index ec7cfc1942a..1b72dd544a3 100644 --- a/Mage/src/mage/players/Library.java +++ b/Mage/src/mage/players/Library.java @@ -128,6 +128,17 @@ public class Library implements Serializable { return game.getCard(library.peekFirst()); } + /** + * Returns the bottommost card of the Library without removing it + * + * @param game + * @return Card + * @see Card + */ + public Card getFromBottom(Game game) { + return game.getCard(library.pollLast()); + } + public void putOnTop(Card card, Game game) { if (card.getOwnerId().equals(playerId)) { card.setZone(Zone.LIBRARY, game); diff --git a/Mage/src/mage/players/ManaPool.java b/Mage/src/mage/players/ManaPool.java index f4745ac20ca..04aec4fa846 100644 --- a/Mage/src/mage/players/ManaPool.java +++ b/Mage/src/mage/players/ManaPool.java @@ -38,7 +38,6 @@ import mage.ConditionalMana; import mage.MageObject; import mage.Mana; import mage.abilities.Ability; -import mage.constants.AsThoughEffectType; import mage.constants.Duration; import mage.constants.ManaType; import mage.constants.TurnPhase; @@ -138,16 +137,15 @@ public class ManaPool implements Serializable { // no mana added beyond the stock so don't auto pay this continue; } - boolean spendAnyMana = spendAnyMana(ability, game); - if (mana.get(manaType) > 0 || (spendAnyMana && mana.count() > 0)) { + ManaType usableManaType = game.getContinuousEffects().asThoughMana(manaType, mana, ability.getSourceId(), ability, ability.getControllerId(), game); + if (usableManaType == null) { + continue; + } + if (mana.get(usableManaType) > 0) { GameEvent event = new GameEvent(GameEvent.EventType.MANA_PAYED, ability.getId(), mana.getSourceId(), ability.getControllerId(), 0, mana.getFlag()); event.setData(mana.getOriginalId().toString()); game.fireEvent(event); - if (spendAnyMana) { - mana.removeAny(); - } else { - mana.remove(manaType); - } + mana.remove(usableManaType); if (mana.count() == 0) { // so no items with count 0 stay in list manaItems.remove(mana); } @@ -158,11 +156,6 @@ public class ManaPool implements Serializable { return false; } - // check if any mana can be spend to cast the mana cost of an ability - private boolean spendAnyMana(Ability ability, Game game) { - return game.getContinuousEffects().asThough(ability.getSourceId(), AsThoughEffectType.SPEND_ANY_MANA, ability, ability.getControllerId(), game); - } - public int get(ManaType manaType) { return getMana().get(manaType); } diff --git a/Mage/src/mage/players/ManaPoolItem.java b/Mage/src/mage/players/ManaPoolItem.java index b6a8f796b9a..9c380389a3d 100644 --- a/Mage/src/mage/players/ManaPoolItem.java +++ b/Mage/src/mage/players/ManaPoolItem.java @@ -1,30 +1,30 @@ /* -* Copyright 2011 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. -*/ + * Copyright 2011 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.players; import java.io.Serializable; @@ -53,7 +53,8 @@ public class ManaPoolItem implements Serializable { private Duration duration; private int stock; // amount the item had at the start of casting something - public ManaPoolItem() {} + public ManaPoolItem() { + } public ManaPoolItem(int red, int green, int blue, int white, int black, int colorless, UUID sourceId, UUID originalId, boolean flag) { this.red = red; @@ -75,7 +76,7 @@ public class ManaPoolItem implements Serializable { this.conditionalMana.setManaProducerId(sourceId); this.conditionalMana.setManaProducerOriginalId(originalId); this.flag = conditionalMana.getFlag(); - this.duration = Duration.EndOfStep; + this.duration = Duration.EndOfStep; } public ManaPoolItem(final ManaPoolItem item) { @@ -191,7 +192,7 @@ public class ManaPoolItem implements Serializable { } public int get(ManaType manaType) { - switch(manaType) { + switch (manaType) { case BLACK: return black; case BLUE: @@ -208,29 +209,26 @@ public class ManaPoolItem implements Serializable { return 0; } - public void removeAny() { - int oldCount = count(); + public ManaType getFirstAvailable() { if (black > 0) { - black--; + return ManaType.BLACK; } else if (blue > 0) { - blue--; + return ManaType.BLUE; } else if (green > 0) { - green--; + return ManaType.GREEN; } else if (red > 0) { - red--; + return ManaType.RED; } else if (white > 0) { - white--; + return ManaType.WHITE; } else if (colorless > 0) { - colorless--; - } - if (stock == oldCount && oldCount > count()) { - stock--; - } + return ManaType.COLORLESS; + } + return null; } - + public void remove(ManaType manaType) { int oldCount = count(); - switch(manaType) { + switch (manaType) { case BLACK: if (black > 0) { black--; @@ -264,11 +262,11 @@ public class ManaPoolItem implements Serializable { } if (stock == oldCount && oldCount > count()) { stock--; - } + } } - + public void clear(ManaType manaType) { - switch(manaType) { + switch (manaType) { case BLACK: black = 0; break; @@ -287,31 +285,36 @@ public class ManaPoolItem implements Serializable { case COLORLESS: colorless = 0; break; - } + } } - + public void add(ManaType manaType, int amount) { - switch(manaType) { + switch (manaType) { case BLACK: black += amount; break; case BLUE: - blue += amount;; + blue += amount; + ; break; case GREEN: - green += amount;; + green += amount; + ; break; case RED: - red += amount;; + red += amount; + ; break; case WHITE: - white += amount;; + white += amount; + ; break; case COLORLESS: - colorless += amount;; + colorless += amount; + ; break; - } - } + } + } public Duration getDuration() { return duration; @@ -328,5 +331,5 @@ public class ManaPoolItem implements Serializable { public void setStock(int stock) { this.stock = stock; } - + } diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index eb3d7d70b2c..7cfa4444370 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -629,12 +629,41 @@ public interface Player extends MageItem, Copyable { * @param game * @return */ + @Deprecated boolean moveCards(Cards cards, Zone fromZone, Zone toZone, Ability source, Game game); + @Deprecated boolean moveCards(Card card, Zone fromZone, Zone toZone, Ability source, Game game); + @Deprecated boolean moveCards(Set cards, Zone fromZone, Zone toZone, Ability source, Game game); + boolean moveCards(Card card, Zone toZone, Ability source, Game game); + + boolean moveCards(Card card, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, ArrayList appliedEffects); + + boolean moveCards(Cards cards, Zone toZone, Ability source, Game game); + + boolean moveCards(Set cards, Zone toZone, Ability source, Game game); + + /** + * Iniversal method to move cards from one zone to another. Do not mix + * objects from different from zones to move. + * + * @param cards + * @param toZone + * @param source + * @param game + * @param tapped tha cards are tapped on the battlefield + * @param faceDown the cards are face down in the to zone + * @param byOwner the card is moved (or put onto battlefield) by the owner + * of the card and if target zone is battlefield controlls the permanent + * (instead of the controller of the source) + * @param appliedEffects + * @return + */ + boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, ArrayList appliedEffects); + boolean moveCardsToExile(Card card, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName); boolean moveCardsToExile(Set cards, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName); @@ -712,45 +741,6 @@ public interface Player extends MageItem, Copyable { */ boolean moveCardToLibraryWithInfo(Card card, UUID sourceId, Game game, Zone fromZone, boolean toTop, boolean withName); - /** - * Uses putOntoBattlefield and posts also a info message about in the game - * log - * - * @param card - * @param game - * @param fromZone - * @param sourceId - * @return - */ - boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId); - - /** - * Uses putOntoBattlefield and posts also a info message about in the game - * log - * - * @param card - * @param game - * @param fromZone - * @param sourceId - * @param tapped the card enters the battlefield tapped - * @return - */ - boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped); - - /** - * Uses putOntoBattlefield and posts also a info message about in the game - * log - * - * @param card - * @param game - * @param fromZone - * @param sourceId - * @param tapped the card enters the battlefield tapped - * @param facedown the card enters the battlefield facedown - * @return - */ - boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped, boolean facedown); - /** * Checks if the playerToCheckId is from an opponent in range * diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 3ce3070c96e..80d529136d5 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -113,6 +113,7 @@ import mage.game.events.DamagePlayerEvent; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; +import mage.game.events.ZoneChangeEvent; import mage.game.events.ZoneChangeGroupEvent; import mage.game.match.MatchPlayer; import mage.game.permanent.Permanent; @@ -671,8 +672,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean removeFromHand(Card card, Game game) { - hand.remove(card); - return true; + return hand.remove(card.getId()); } @Override @@ -773,6 +773,9 @@ public abstract class PlayerImpl implements Player, Serializable { public boolean addAttachment(UUID permanentId, Game game) { if (!this.attachments.contains(permanentId)) { Permanent aura = game.getPermanent(permanentId); + if (aura == null) { + aura = game.getPermanentEntering(permanentId); + } if (aura != null) { if (!game.replaceEvent(new GameEvent(GameEvent.EventType.ENCHANT_PLAYER, playerId, permanentId, aura.getControllerId()))) { this.attachments.add(permanentId); @@ -875,16 +878,15 @@ public abstract class PlayerImpl implements Player, Serializable { */ @Override public boolean putCardsOnTopOfLibrary(Cards cardsToLibrary, Game game, Ability source, boolean anyOrder) { - Cards cards = new CardsImpl(cardsToLibrary); // prevent possible ConcurrentModificationException - cards.addAll(cardsToLibrary); - if (!cards.isEmpty()) { + if (!cardsToLibrary.isEmpty()) { + Cards cards = new CardsImpl(cardsToLibrary); // prevent possible ConcurrentModificationException UUID sourceId = (source == null ? null : source.getSourceId()); if (!anyOrder) { for (UUID cardId : cards) { moveObjectToLibrary(cardId, sourceId, game, true, false); } } else { - TargetCard target = new TargetCard(Zone.PICK, new FilterCard("card to put on the top of your library (last one chosen will be topmost)")); + TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to put on the top of your library (last one chosen will be topmost)")); target.setRequired(true); while (isInGame() && cards.size() > 1) { this.choose(Outcome.Neutral, cards, target, game); @@ -1017,13 +1019,12 @@ public abstract class PlayerImpl implements Player, Serializable { //20091005 - 305.1 if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, card.getId(), card.getId(), playerId))) { // int bookmark = game.bookmarkState(); - Zone zone = game.getState().getZone(card.getId()); - if (card.putOntoBattlefield(game, zone, null, playerId)) { + if (moveCards(card, Zone.BATTLEFIELD, playLandAbility, game, false, false, false, null)) { landsPlayed++; game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LAND_PLAYED, card.getId(), card.getId(), playerId)); game.fireInformEvent(getLogName() + " plays " + card.getLogName()); // game.removeBookmark(bookmark); - resetStoredBookmark(game); + resetStoredBookmark(game); // prevent undo after playing a land return true; } // putOntoBattlefield retured false if putOntoBattlefield was replaced by replacement effect (e.g. Kjeldorian Outpost). @@ -1105,7 +1106,7 @@ public abstract class PlayerImpl implements Player, Serializable { return false; } - private void restoreState(int bookmark, String text, Game game) { + protected void restoreState(int bookmark, String text, Game game) { game.restoreState(bookmark, text); if (storedBookmark >= bookmark) { resetStoredBookmark(game); @@ -1216,11 +1217,18 @@ public abstract class PlayerImpl implements Player, Serializable { public LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game) { LinkedHashMap useable = new LinkedHashMap<>(); boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game); + ManaOptions availableMana = null; +// ManaOptions availableMana = getManaAvailable(game); // can only be activated if mana calculation works flawless otherwise player can't play spells they could play if calculation would work correctly +// availableMana.addMana(manaPool.getMana()); for (Ability ability : object.getAbilities()) { if (canUse || ability.getAbilityType().equals(AbilityType.SPECIAL_ACTION)) { if (ability.getZone().match(zone)) { if (ability instanceof ActivatedAbility) { - if (((ActivatedAbility) ability).canActivate(playerId, game)) { + if (ability instanceof ManaAbility) { + if (((ActivatedAbility) ability).canActivate(playerId, game)) { + useable.put(ability.getId(), (ActivatedAbility) ability); + } + } else if (canPlay(((ActivatedAbility) ability), availableMana, object, game)) { useable.put(ability.getId(), (ActivatedAbility) ability); } } else if (ability instanceof AlternativeSourceCosts) { @@ -2296,6 +2304,14 @@ public abstract class PlayerImpl implements Player, Serializable { return result; } + /** + * + * @param ability + * @param available if null, it won't be checked if enough mana is available + * @param sourceObject + * @param game + * @return + */ protected boolean canPlay(ActivatedAbility ability, ManaOptions available, MageObject sourceObject, Game game) { if (!(ability instanceof ManaAbility)) { ActivatedAbility copy = ability.copy(); @@ -2316,15 +2332,26 @@ public abstract class PlayerImpl implements Player, Serializable { } } } - - ManaOptions abilityOptions = copy.getManaCostsToPay().getOptions(); - if (abilityOptions.size() == 0) { - return true; - } else { - for (Mana mana : abilityOptions) { - for (Mana avail : available) { - if (mana.enough(avail)) { - return true; + boolean canBeCastRegularly = true; + if (copy instanceof SpellAbility && copy.getManaCosts().isEmpty() && copy.getCosts().isEmpty()) { + // 117.6. Some mana costs contain no mana symbols. This represents an unpayable cost... + // 117.6a (...) If an alternative cost is applied to an unpayable cost, + // including an effect that allows a player to cast a spell without paying its mana cost, the alternative cost may be paid. + canBeCastRegularly = false; + } + if (canBeCastRegularly) { + ManaOptions abilityOptions = copy.getManaCostsToPay().getOptions(); + if (abilityOptions.size() == 0) { + return true; + } else { + if (available == null) { + return true; + } + for (Mana mana : abilityOptions) { + for (Mana avail : available) { + if (mana.enough(avail)) { + return true; + } } } } @@ -2368,6 +2395,9 @@ public abstract class PlayerImpl implements Player, Serializable { if (manaCosts.size() == 0) { return true; } else { + if (available == null) { + return true; + } for (Mana mana : manaCosts.getOptions()) { for (Mana avail : available) { if (mana.enough(avail)) { @@ -2981,11 +3011,112 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCards(Set cards, Zone fromZone, Zone toZone, Ability source, Game game) { + return moveCards(cards, toZone, source, game, false, false, false, null); + } + + @Override + public boolean moveCards(Card card, Zone toZone, Ability source, Game game) { + return moveCards(card, toZone, source, game, false, false, false, null); + } + + @Override + public boolean moveCards(Card card, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, ArrayList appliedEffects) { + Set cardList = new HashSet<>(); + if (card != null) { + cardList.add(card); + } + return moveCards(cardList, toZone, source, game, tapped, faceDown, byOwner, appliedEffects); + } + + @Override + public boolean moveCards(Cards cards, Zone toZone, Ability source, Game game) { + return moveCards(cards.getCards(game), toZone, source, game); + } + + @Override + public boolean moveCards(Set cards, Zone toZone, Ability source, Game game) { + return moveCards(cards, toZone, source, game, false, false, false, null); + } + + @Override + public boolean moveCards(Set cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, ArrayList appliedEffects) { if (cards.isEmpty()) { return true; } Set successfulMovedCards = new LinkedHashSet<>(); + Zone fromZone = null; switch (toZone) { + case GRAVEYARD: + fromZone = game.getState().getZone(cards.iterator().next().getId()); + successfulMovedCards = moveCardsToGraveyardWithInfo(cards, source, game, fromZone); + break; + case BATTLEFIELD: // new logic that does not yet add the permanents to battlefield while replacement effects are handled + List permanents = new ArrayList<>(); + List permanentsEntered = new ArrayList<>(); + for (Card card : cards) { + UUID controllingPlayerId = byOwner ? card.getOwnerId() : getId(); + fromZone = game.getState().getZone(card.getId()); + if (faceDown) { + card.setFaceDown(true, game); + } + ZoneChangeEvent event = new ZoneChangeEvent(card.getId(), source.getSourceId(), controllingPlayerId, fromZone, Zone.BATTLEFIELD, appliedEffects, tapped); + if (!game.replaceEvent(event)) { + // get permanent + Permanent permanent = new PermanentCard(card, event.getPlayerId(), game);// controlling player can be replaced so use event player now + permanents.add(permanent); + game.getPermanentsEntering().put(permanent.getId(), permanent); + card.checkForCountersToAdd(permanent, game); + permanent.setTapped(tapped); + permanent.setFaceDown(faceDown, game); + } + if (faceDown) { + card.setFaceDown(false, game); + } + } + game.setScopeRelevant(true); + for (Permanent permanent : permanents) { + fromZone = game.getState().getZone(permanent.getId()); + // make sure the controller of all continuous effects of this card are switched to the current controller + game.getContinuousEffects().setController(permanent.getId(), permanent.getControllerId()); + if (permanent.entersBattlefield(source.getSourceId(), game, fromZone, true)) { + permanentsEntered.add(permanent); + } else { + // revert controller to owner if permanent does not enter + game.getContinuousEffects().setController(permanent.getId(), permanent.getOwnerId()); + game.getPermanentsEntering().remove(permanent.getId()); + } + } + game.setScopeRelevant(false); + for (Permanent permanent : permanentsEntered) { + fromZone = game.getState().getZone(permanent.getId()); + if (((Card) permanent).removeFromZone(game, fromZone, source.getSourceId())) { + permanent.updateZoneChangeCounter(game); + game.addPermanent(permanent); + permanent.setZone(Zone.BATTLEFIELD, game); + game.getPermanentsEntering().remove(permanent.getId()); + game.setScopeRelevant(true); + successfulMovedCards.add(permanent); + game.addSimultaneousEvent(new ZoneChangeEvent(permanent, permanent.getControllerId(), fromZone, Zone.BATTLEFIELD)); + if (!game.isSimulation()) { + game.informPlayers(this.getLogName() + " puts " + (faceDown ? "a card face down " : permanent.getLogName()) + + " from " + fromZone.toString().toLowerCase(Locale.ENGLISH) + " onto the Battlefield"); + } + } else { + game.getPermanentsEntering().remove(permanent.getId()); + } + } + game.applyEffects(); + break; + case HAND: + for (Card card : cards) { + fromZone = game.getState().getZone(card.getId()); + boolean hideCard = fromZone.equals(Zone.LIBRARY) + || (card.isFaceDown(game) && !fromZone.equals(Zone.STACK) && !fromZone.equals(Zone.BATTLEFIELD)); + if (moveCardToHandWithInfo(card, source == null ? null : source.getSourceId(), game, !hideCard)) { + successfulMovedCards.add(card); + } + } + break; case EXILED: for (Card card : cards) { fromZone = game.getState().getZone(card.getId()); @@ -2995,37 +3126,13 @@ public abstract class PlayerImpl implements Player, Serializable { } } break; - case GRAVEYARD: - successfulMovedCards = moveCardsToGraveyardWithInfo(cards, source, game, fromZone); - break; - case HAND: - for (Card card : cards) { - fromZone = game.getState().getZone(card.getId()); - if (fromZone == Zone.STACK) { - // If a spell is returned to its owner's hand, it's removed from the stack and thus will not resolve - Spell spell = game.getStack().getSpell(card.getId()); - if (spell != null) { - game.getStack().remove(spell); - } - } - boolean hideCard = fromZone.equals(Zone.LIBRARY) - || (card.isFaceDown(game) && !fromZone.equals(Zone.STACK) && !fromZone.equals(Zone.BATTLEFIELD)); - if (moveCardToHandWithInfo(card, source == null ? null : source.getSourceId(), game, !hideCard)) { - successfulMovedCards.add(card); - } - } - break; - case BATTLEFIELD: - for (Card card : cards) { - fromZone = game.getState().getZone(card.getId()); - if (putOntoBattlefieldWithInfo(card, game, fromZone, source == null ? null : source.getSourceId(), false, card.isFaceDown(game))) { - successfulMovedCards.add(card); - } - } - break; case LIBRARY: for (Card card : cards) { - fromZone = game.getState().getZone(card.getId()); + if (card instanceof Spell) { + fromZone = game.getState().getZone(((Spell) card).getSourceId()); + } else { + fromZone = game.getState().getZone(card.getId()); + } boolean hideCard = fromZone.equals(Zone.HAND) || fromZone.equals(Zone.LIBRARY); if (moveCardToLibraryWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone, true, !hideCard)) { successfulMovedCards.add(card); @@ -3033,8 +3140,9 @@ public abstract class PlayerImpl implements Player, Serializable { } break; default: - throw new UnsupportedOperationException("to Zone not supported yet"); + throw new UnsupportedOperationException("to Zone" + toZone.toString() + " not supported yet"); } + game.fireEvent(new ZoneChangeGroupEvent(successfulMovedCards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone)); return successfulMovedCards.size() > 0; } @@ -3234,31 +3342,6 @@ public abstract class PlayerImpl implements Player, Serializable { return result; } - @Override - public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId) { - return this.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId, false, false); - } - - @Override - public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped) { - return this.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId, tapped, false); - } - - @Override - public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped, boolean facedown) { - boolean result = false; - if (card.putOntoBattlefield(game, fromZone, sourceId, this.getId(), tapped, facedown)) { - if (!game.isSimulation()) { - game.informPlayers(new StringBuilder(this.getLogName()) - .append(" puts ").append(facedown ? "a card face down " : card.getLogName()) - .append(" from ").append(fromZone.toString().toLowerCase(Locale.ENGLISH)).append(" ") - .append("onto the Battlefield").toString()); - } - result = true; - } - return result; - } - @Override public boolean hasOpponent(UUID playerToCheckId, Game game) { return !this.getId().equals(playerToCheckId) && game.isOpponent(this, playerToCheckId); diff --git a/Mage/src/mage/players/net/UserData.java b/Mage/src/mage/players/net/UserData.java index 0e6e4017334..c2c3b2a3bb4 100644 --- a/Mage/src/mage/players/net/UserData.java +++ b/Mage/src/mage/players/net/UserData.java @@ -59,7 +59,7 @@ public class UserData implements Serializable { } public static UserData getDefaultUserDataView() { - return new UserData(UserGroup.DEFAULT, 0, false, false, true, null, "world.png", false, true, true, false, false, false); + return new UserData(UserGroup.DEFAULT, 0, false, false, true, null, getDefaultFlagName(), false, true, true, false, false, false); } public void setGroupId(int groupId) { @@ -166,4 +166,7 @@ public class UserData implements Serializable { this.autoOrderTrigger = autoOrderTrigger; } + public static String getDefaultFlagName() { + return "world.png"; + } } diff --git a/Mage/src/mage/target/Target.java b/Mage/src/mage/target/Target.java index b6bd637b1e4..80b977f4eed 100644 --- a/Mage/src/mage/target/Target.java +++ b/Mage/src/mage/target/Target.java @@ -151,4 +151,8 @@ public interface Target extends Serializable { UUID getAbilityController(); Player getTargetController(Game game, UUID playerId); + + int getTargetTag(); + + void setTargetTag(int tag); } diff --git a/Mage/src/mage/target/TargetImpl.java b/Mage/src/mage/target/TargetImpl.java index ce0a8790f48..df09789ca13 100644 --- a/Mage/src/mage/target/TargetImpl.java +++ b/Mage/src/mage/target/TargetImpl.java @@ -70,6 +70,8 @@ public abstract class TargetImpl implements Target { protected UUID targetController = null; // if null the ability controller is the targetController protected UUID abilityController = null; // only used if target controller != ability controller + protected int targetTag; // can be set if other target check is needed (AnotherTargetPredicate) + @Override public abstract TargetImpl copy(); @@ -95,6 +97,7 @@ public abstract class TargetImpl implements Target { this.notTarget = target.notTarget; this.targetController = target.targetController; this.abilityController = target.abilityController; + this.targetTag = target.targetTag; } @Override @@ -545,4 +548,20 @@ public abstract class TargetImpl implements Target { return requiredExplicitlySet; } + @Override + public int getTargetTag() { + return targetTag; + } + + /** + * Is used to be able to check, that another target is slected within the + * group of targets of the ability with a target tag > 0. + * + * @param targetTag + */ + @Override + public void setTargetTag(int targetTag) { + this.targetTag = targetTag; + } + } diff --git a/Mage/src/mage/target/TargetPermanent.java b/Mage/src/mage/target/TargetPermanent.java index 222e08d0586..068bac9f0cb 100644 --- a/Mage/src/mage/target/TargetPermanent.java +++ b/Mage/src/mage/target/TargetPermanent.java @@ -1,44 +1,42 @@ /* -* 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. -*/ - + * 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.target; import java.util.HashSet; import java.util.Set; import java.util.UUID; -import mage.constants.Zone; import mage.MageObject; import mage.abilities.Ability; +import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; - /** * * @author BetaSteward_at_googlemail.com @@ -50,7 +48,7 @@ public class TargetPermanent extends TargetObject { public TargetPermanent() { this(1, 1, new FilterPermanent(), false); } - + public TargetPermanent(FilterPermanent filter) { this(1, 1, filter, false); } @@ -88,8 +86,8 @@ public class TargetPermanent extends TargetObject { // first for protection from spells or abilities (e.g. protection from colored spells, r1753) // second for protection from sources (e.g. protection from artifacts + equip ability) if (!isNotTarget()) { - if (!permanent.canBeTargetedBy(game.getObject(source.getId()), controllerId, game) || - !permanent.canBeTargetedBy(game.getObject(source.getSourceId()), controllerId, game)) { + if (!permanent.canBeTargetedBy(game.getObject(source.getId()), controllerId, game) + || !permanent.canBeTargetedBy(game.getObject(source.getSourceId()), controllerId, game)) { return false; } } @@ -117,7 +115,8 @@ public class TargetPermanent extends TargetObject { /** * Checks if there are enough {@link Permanent} that can be chosen. * - * Takes into account notTarget parameter, in case it's true doesn't check for protection, shroud etc. + * Takes into account notTarget parameter, in case it's true doesn't check + * for protection, shroud etc. * * @param sourceId the target event source * @param sourceControllerId controller of the target event source @@ -132,7 +131,7 @@ public class TargetPermanent extends TargetObject { } int count = 0; MageObject targetSource = game.getObject(sourceId); - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { if (!targets.containsKey(permanent.getId())) { if (notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) { count++; @@ -146,9 +145,10 @@ public class TargetPermanent extends TargetObject { } /** - * Checks if there are enough {@link Permanent} that can be selected. Should not be used - * for Ability targets since this does not check for protection, shroud etc. - * + * Checks if there are enough {@link Permanent} that can be selected. Should + * not be used for Ability targets since this does not check for protection, + * shroud etc. + * * @param sourceControllerId - controller of the select event * @param game * @return - true if enough valid {@link Permanent} exist @@ -162,7 +162,7 @@ public class TargetPermanent extends TargetObject { return true; } int count = 0; - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { if (!targets.containsKey(permanent.getId())) { count++; if (count >= remainingTargets) { @@ -177,7 +177,7 @@ public class TargetPermanent extends TargetObject { public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { Set possibleTargets = new HashSet<>(); MageObject targetSource = game.getObject(sourceId); - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, sourceId, game)) { if (!targets.containsKey(permanent.getId())) { if (notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) { possibleTargets.add(permanent.getId()); @@ -190,7 +190,7 @@ public class TargetPermanent extends TargetObject { @Override public Set possibleTargets(UUID sourceControllerId, Game game) { Set possibleTargets = new HashSet<>(); - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { if (!targets.containsKey(permanent.getId())) { possibleTargets.add(permanent.getId()); } diff --git a/Mage/src/mage/target/TargetStackObject.java b/Mage/src/mage/target/TargetStackObject.java index 0a8e91a4e9a..467565ee72d 100644 --- a/Mage/src/mage/target/TargetStackObject.java +++ b/Mage/src/mage/target/TargetStackObject.java @@ -1,16 +1,16 @@ /* * 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 @@ -20,7 +20,7 @@ * 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. @@ -80,21 +80,16 @@ public class TargetStackObject extends TargetObject { public boolean canTarget(UUID id, Ability source, Game game) { StackObject stackObject = game.getStack().getStackObject(id); if (stackObject != null) { - return filter.match(stackObject, game); + return filter.match(stackObject, source.getSourceId(), source.getControllerId(), game); } return false; } @Override public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { - return canChoose(sourceControllerId, game); - } - - @Override - public boolean canChoose(UUID sourceControllerId, Game game) { int count = 0; for (StackObject stackObject: game.getStack()) { - if (game.getPlayer(sourceControllerId).getInRange().contains(stackObject.getControllerId()) && filter.match(stackObject, game)) { + if (game.getPlayer(sourceControllerId).getInRange().contains(stackObject.getControllerId()) && filter.match(stackObject, sourceId, sourceControllerId, game)) { count++; if (count >= this.minNumberOfTargets) { return true; @@ -105,21 +100,26 @@ public class TargetStackObject extends TargetObject { } @Override - public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - return possibleTargets(sourceControllerId, game); + public boolean canChoose(UUID sourceControllerId, Game game) { + return canChoose(null, sourceControllerId, game); } @Override - public Set possibleTargets(UUID sourceControllerId, Game game) { + public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { Set possibleTargets = new HashSet<>(); for (StackObject stackObject: game.getStack()) { - if (game.getPlayer(sourceControllerId).getInRange().contains(stackObject.getControllerId()) && filter.match(stackObject, game)) { + if (game.getPlayer(sourceControllerId).getInRange().contains(stackObject.getControllerId()) && filter.match(stackObject, sourceId, sourceControllerId, game)) { possibleTargets.add(stackObject.getId()); } } return possibleTargets; } + @Override + public Set possibleTargets(UUID sourceControllerId, Game game) { + return this.possibleTargets(null, sourceControllerId, game); + } + @Override public TargetStackObject copy() { return new TargetStackObject(this); diff --git a/Mage/src/mage/util/CardUtil.java b/Mage/src/mage/util/CardUtil.java index 3af5676fa48..9c6f45db5b8 100644 --- a/Mage/src/mage/util/CardUtil.java +++ b/Mage/src/mage/util/CardUtil.java @@ -483,23 +483,6 @@ public class CardUtil { return getExileZoneId(getCardZoneString(SOURCE_EXILE_ZONE_TEXT, sourceId, game, previous), game); } - public static UUID getObjectExileZoneId(Game game, MageObject mageObject) { - return getObjectExileZoneId(game, mageObject, false); - } - - public static UUID getObjectExileZoneId(Game game, MageObject mageObject, boolean previous) { - int zoneChangeCounter = 0; - if (mageObject instanceof Permanent) { - zoneChangeCounter = ((Permanent) mageObject).getZoneChangeCounter(game); - } else if (mageObject instanceof Card) { - zoneChangeCounter = ((Card) mageObject).getZoneChangeCounter(game); - } - if (zoneChangeCounter > 0 && previous) { - zoneChangeCounter--; - } - return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT, mageObject.getId(), game, zoneChangeCounter, false), game); - } - public static UUID getExileZoneId(Game game, UUID objectId, int zoneChangeCounter) { return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT, objectId, game, zoneChangeCounter, false), game); } diff --git a/Mage/src/mage/util/functions/AddSubtypeApplier.java b/Mage/src/mage/util/functions/AddSubtypeApplier.java new file mode 100644 index 00000000000..a007b6a9f31 --- /dev/null +++ b/Mage/src/mage/util/functions/AddSubtypeApplier.java @@ -0,0 +1,40 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.util.functions; + +import mage.MageObject; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author LevelX2 + */ +public class AddSubtypeApplier extends ApplyToPermanent { + + private final String subtype; + + public AddSubtypeApplier(String subtype) { + this.subtype = subtype; + } + + @Override + public Boolean apply(Game game, Permanent permanent) { + if (!permanent.getSubtype().contains(subtype)) { + permanent.getSubtype().add(subtype); + } + return true; + } + + @Override + public Boolean apply(Game game, MageObject mageObject) { + if (!mageObject.getSubtype().contains(subtype)) { + mageObject.getSubtype().add(subtype); + } + return true; + } + +} diff --git a/Mage/src/mage/util/functions/CardTypeApplier.java b/Mage/src/mage/util/functions/CardTypeApplier.java index dcb8eeb8c33..48a3eb54130 100644 --- a/Mage/src/mage/util/functions/CardTypeApplier.java +++ b/Mage/src/mage/util/functions/CardTypeApplier.java @@ -27,10 +27,36 @@ */ package mage.util.functions; +import mage.MageObject; +import mage.constants.CardType; +import mage.game.Game; +import mage.game.permanent.Permanent; + /** * * @author LevelX2 */ -public class CardTypeApplier { +public class CardTypeApplier extends ApplyToPermanent { + private final CardType cardType; + + public CardTypeApplier(CardType cardType) { + this.cardType = cardType; + } + + @Override + public Boolean apply(Game game, Permanent permanent) { + if (!permanent.getCardType().contains(cardType)) { + permanent.getCardType().add(cardType); + } + return true; + } + + @Override + public Boolean apply(Game game, MageObject mageObject) { + if (!mageObject.getCardType().contains(cardType)) { + mageObject.getCardType().add(cardType); + } + return true; + } } diff --git a/Mage/src/mage/watchers/common/FirstTimeStepWatcher.java b/Mage/src/mage/watchers/common/FirstTimeStepWatcher.java new file mode 100644 index 00000000000..028e77e1209 --- /dev/null +++ b/Mage/src/mage/watchers/common/FirstTimeStepWatcher.java @@ -0,0 +1,72 @@ +/* + * 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.watchers.common; + +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.watchers.Watcher; + +/** + * The watcher checks if a specific phase event has already happened during the + * current turn. If not it returns false, otherwise true. + * + * @author LevelX2 + */ +public class FirstTimeStepWatcher extends Watcher { + + private final EventType eventType; + + public FirstTimeStepWatcher(EventType eventType) { + super(eventType.toString() + FirstTimeStepWatcher.class.getName(), WatcherScope.GAME); + this.eventType = eventType; + } + + public FirstTimeStepWatcher(final FirstTimeStepWatcher watcher) { + super(watcher); + this.eventType = watcher.eventType; + } + + @Override + public FirstTimeStepWatcher copy() { + return new FirstTimeStepWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == eventType) { + condition = true; + } + } + + @Override + public void reset() { + super.reset(); + } +}