Merge remote-tracking branch 'upstream/master' into CMH-GuiltyConscienceAndBackfire

This commit is contained in:
Clint Herron 2017-04-18 11:00:24 -04:00
commit d1c8796a49
7431 changed files with 56515 additions and 21681 deletions

View file

@ -1,19 +1,22 @@
package mage;
import java.io.Serializable;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Abilities;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.keyword.ChangelingAbility;
import mage.cards.Card;
import mage.cards.FrameStyle;
import mage.constants.CardType;
import mage.constants.SuperType;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import java.io.Serializable;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
public interface MageObject extends MageItem, Serializable {
String getName();
@ -32,16 +35,16 @@ public interface MageObject extends MageItem, Serializable {
boolean hasSubtype(String subtype, Game game);
List<String> getSupertype();
EnumSet<SuperType> getSuperType();
Abilities<Ability> getAbilities();
boolean hasAbility(UUID abilityId, Game game);
ObjectColor getColor(Game game);
ObjectColor getFrameColor(Game game);
FrameStyle getFrameStyle();
ManaCosts<ManaCost> getManaCost();
@ -51,10 +54,9 @@ public interface MageObject extends MageItem, Serializable {
MageInt getPower();
MageInt getToughness();
int getStartingLoyalty();
void adjustCosts(Ability ability, Game game);
@ -83,33 +85,108 @@ public interface MageObject extends MageItem, Serializable {
void setZoneChangeCounter(int value, Game game);
default boolean isCreature(){
default boolean isCreature() {
return getCardType().contains(CardType.CREATURE);
}
default boolean isArtifact(){
default boolean isArtifact() {
return getCardType().contains(CardType.ARTIFACT);
}
default boolean isLand(){
default boolean isLand() {
return getCardType().contains(CardType.LAND);
}
default boolean isEnchantment(){
default boolean isEnchantment() {
return getCardType().contains(CardType.ENCHANTMENT);
}
default boolean isInstant(){
default boolean isInstant() {
return getCardType().contains(CardType.INSTANT);
}
default boolean isSorcery(){
default boolean isSorcery() {
return getCardType().contains(CardType.SORCERY);
}
default boolean isPlaneswalker(){
default boolean isPlaneswalker() {
return getCardType().contains(CardType.PLANESWALKER);
}
default boolean isPermanent() {
return isCreature() || isArtifact() || isPlaneswalker() || isEnchantment() || isLand();
}
default boolean isLegendary() {
return getSuperType().contains(SuperType.LEGENDARY);
}
default boolean isSnow() {
return getSuperType().contains(SuperType.SNOW);
}
default void addSuperType(SuperType superType) {
getSuperType().add(superType);
}
default boolean isBasic() {
return getSuperType().contains(SuperType.BASIC);
}
default boolean isWorld() {
return getSuperType().contains(SuperType.WORLD);
}
default void addCardType(CardType cardType) {
getCardType().add(cardType);
}
/**
* Checks whether two cards share card types.
*
* @param otherCard
* @return
*/
default boolean shareTypes(Card otherCard) {
if (otherCard == null) {
throw new IllegalArgumentException("Params can't be null");
}
for (CardType type : getCardType()) {
if (otherCard.getCardType().contains(type)) {
return true;
}
}
return false;
}
default boolean shareSubtypes(Card otherCard, Game game) {
if (otherCard == null) {
throw new IllegalArgumentException("Params can't be null");
}
if (this.isCreature() && otherCard.isCreature()) {
if (this.getAbilities().contains(ChangelingAbility.getInstance())
|| this.getSubtype(game).contains(ChangelingAbility.ALL_CREATURE_TYPE)
|| otherCard.getAbilities().contains(ChangelingAbility.getInstance())
|| otherCard.getSubtype(game).contains(ChangelingAbility.ALL_CREATURE_TYPE)) {
return true;
}
}
for (String subtype : this.getSubtype(game)) {
if (otherCard.getSubtype(game).contains(subtype)) {
return true;
}
}
return false;
}
default void addCardTypes(EnumSet<CardType> cardType){
getCardType().addAll(cardType);
}
}

View file

@ -40,6 +40,7 @@ import mage.abilities.keyword.ChangelingAbility;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.cards.FrameStyle;
import mage.constants.CardType;
import mage.constants.SuperType;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import mage.util.CardUtil;
@ -56,7 +57,7 @@ public abstract class MageObjectImpl implements MageObject {
protected FrameStyle frameStyle;
protected EnumSet<CardType> cardType = EnumSet.noneOf(CardType.class);
protected List<String> subtype = new ArrayList<>();
protected List<String> supertype = new ArrayList<>();
protected EnumSet<SuperType> supertype = EnumSet.noneOf(SuperType.class);
protected Abilities<Ability> abilities;
protected String text;
protected MageInt power;
@ -91,7 +92,7 @@ public abstract class MageObjectImpl implements MageObject {
abilities = object.abilities.copy();
this.cardType.addAll(object.cardType);
this.subtype.addAll(object.subtype);
this.supertype.addAll(object.supertype);
supertype.addAll(object.supertype);
this.copy = object.copy;
}
@ -136,7 +137,7 @@ public abstract class MageObjectImpl implements MageObject {
}
@Override
public List<String> getSupertype() {
public EnumSet<SuperType> getSuperType() {
return supertype;
}

View file

@ -29,6 +29,7 @@ package mage.abilities;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import mage.abilities.keyword.ProtectionAbility;
import mage.abilities.mana.ActivatedManaAbilityImpl;
@ -245,7 +246,7 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
* @param abilityId
* @return
*/
T get(UUID abilityId);
Optional<T> get(UUID abilityId);
/**
* TODO The usage of this method seems redundant to that of

View file

@ -27,13 +27,6 @@
*/
package mage.abilities;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import mage.abilities.common.ZoneChangeTriggeredAbility;
import mage.abilities.costs.Cost;
import mage.abilities.keyword.ProtectionAbility;
@ -44,6 +37,8 @@ import mage.game.Game;
import mage.util.ThreadLocalStringBuilder;
import org.apache.log4j.Logger;
import java.util.*;
/**
* @param <T>
* @author BetaSteward_at_googlemail.com
@ -277,12 +272,7 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
@Override
public boolean containsRule(T ability) {
for (T test : this) {
if (ability.getRule().equals(test.getRule())) {
return true;
}
}
return false;
return stream().anyMatch(rule -> rule.getRule().equals(ability.getRule()));
}
@Override
@ -301,32 +291,16 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
@Override
public boolean containsKey(UUID abilityId) {
for (T ability : this) {
if (ability.getId().equals(abilityId)) {
return true;
}
}
return false;
return stream().anyMatch(ability -> ability.getId().equals(abilityId));
}
@Override
public boolean containsClass(Class classObject) {
for (T ability : this) {
if (ability.getClass().equals(classObject)) {
return true;
}
}
return false;
return stream().anyMatch(ability -> ability.getClass().equals(classObject));
}
@Override
public T get(UUID abilityId) {
for (T ability : this) {
if (ability.getId().equals(abilityId)) {
return ability;
}
}
return null;
public Optional<T> get(UUID abilityId) {
return stream().filter(ability -> ability.getId().equals(abilityId)).findFirst();
}
@Override

View file

@ -27,6 +27,10 @@
*/
package mage.abilities;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.Mana;
@ -59,11 +63,6 @@ import mage.util.ThreadLocalStringBuilder;
import mage.watchers.Watcher;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -287,6 +286,19 @@ public abstract class AbilityImpl implements Ability {
}
}
// 117.6. Some mana costs contain no mana symbols. This represents an unpayable cost. An ability can
// also have an unpayable cost if its cost is based on the mana cost of an object with no mana cost.
// Attempting to cast a spell or activate an ability that has an unpayable cost is a legal action.
// However, attempting to pay an unpayable cost is an illegal action.
//
// We apply this now, *AFTER* the user has made the choice to pay an alternative cost for the
// spell. You can also still cast a spell with an unplayable cost by... not paying it's mana cost.
//if (getAbilityType() == AbilityType.SPELL && getManaCostsToPay().isEmpty() && !noMana) {
// return false;
//}
if (getAbilityType() == AbilityType.SPELL && (getManaCostsToPay().isEmpty() && getCosts().isEmpty()) && !noMana) {
return false;
}
// 20121001 - 601.2b
// If the spell has a variable cost that will be paid as it's being cast (such as an {X} in
// its mana cost; see rule 107.3), the player announces the value of that variable.
@ -328,7 +340,7 @@ public abstract class AbilityImpl implements Ability {
Outcome outcome = getEffects().isEmpty() ? Outcome.Detriment : getEffects().get(0).getOutcome();
if (getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game) == false) {
if ((variableManaCost != null || announceString != null) && !game.isSimulation()) {
game.informPlayer(controller, (sourceObject != null ? sourceObject.getIdName() : "") + ": no valid targets with this value of X");
game.informPlayer(controller, (sourceObject != null ? sourceObject.getIdName() : "") + ": no valid targets");
}
return false; // when activation of ability is canceled during target selection
}
@ -489,7 +501,7 @@ public abstract class AbilityImpl implements Ability {
protected String handleOtherXCosts(Game game, Player controller) {
StringBuilder announceString = new StringBuilder();
for (VariableCost variableCost : this.costs.getVariableCosts()) {
if (!(variableCost instanceof VariableManaCost)) {
if (!(variableCost instanceof VariableManaCost) && !((Cost) variableCost).isPaid()) {
int xValue = variableCost.announceXValue(this, game);
Cost fixedCost = variableCost.getFixedCostsFromAnnouncedValue(xValue);
if (fixedCost != null) {
@ -506,19 +518,20 @@ public abstract class AbilityImpl implements Ability {
}
/**
* 601.2b
* If a cost that will be paid as the spell is being cast includes Phyrexian mana symbols,
* the player announces whether he or she intends to pay 2 life or the corresponding colored mana cost for each of those symbols.
* 601.2b If a cost that will be paid as the spell is being cast includes
* Phyrexian mana symbols, the player announces whether he or she intends to
* pay 2 life or the corresponding colored mana cost for each of those
* symbols.
*/
private void handlePhyrexianManaCosts(Game game, UUID sourceId, Player controller) {
Iterator<ManaCost> costIterator = manaCostsToPay.iterator();
while(costIterator.hasNext()) {
while (costIterator.hasNext()) {
ManaCost cost = costIterator.next();
if(cost instanceof PhyrexianManaCost) {
PhyrexianManaCost phyrexianManaCost = (PhyrexianManaCost)cost;
if (cost instanceof PhyrexianManaCost) {
PhyrexianManaCost phyrexianManaCost = (PhyrexianManaCost) cost;
PayLifeCost payLifeCost = new PayLifeCost(2);
if(payLifeCost.canPay(this, sourceId, controller.getId(), game) &&
controller.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + phyrexianManaCost.getBaseText() + '?', this, game)) {
if (payLifeCost.canPay(this, sourceId, controller.getId(), game)
&& controller.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + phyrexianManaCost.getBaseText() + '?', this, game)) {
costIterator.remove();
costs.add(payLifeCost);
}
@ -558,7 +571,11 @@ public abstract class AbilityImpl implements Ability {
} else {
String manaSymbol = null;
if (variableManaCost.getFilter().isBlack()) {
manaSymbol = "B";
if (variableManaCost.getFilter().isRed()) {
manaSymbol = "B/R";
} else {
manaSymbol = "B";
}
} else if (variableManaCost.getFilter().isRed()) {
manaSymbol = "R";
} else if (variableManaCost.getFilter().isBlue()) {

View file

@ -1,22 +0,0 @@
package mage.abilities;
/**
* Created by IGOUDT on 5-3-2017.
*/
public enum CountType {
MORE_THAN, FEWER_THAN, EQUAL_TO;
public static boolean compare(int source, CountType comparison, int target){
switch (comparison){
case MORE_THAN:
return source > target;
case FEWER_THAN:
return source < target;
case EQUAL_TO:
return source == target;
default:
throw new IllegalArgumentException("comparison rules for "+comparison + " missing");
}
}
}

View file

@ -28,20 +28,21 @@
package mage.abilities;
import java.util.Iterator;
import mage.constants.Duration;
import mage.game.Game;
import mage.game.events.GameEvent;
import java.util.Iterator;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class DelayedTriggeredAbilities extends AbilitiesImpl<DelayedTriggeredAbility> {
public class DelayedTriggeredAbilities extends AbilitiesImpl<DelayedTriggeredAbility> {
public DelayedTriggeredAbilities() {}
public DelayedTriggeredAbilities() {
}
public DelayedTriggeredAbilities(final DelayedTriggeredAbilities abilities) {
public DelayedTriggeredAbilities(final DelayedTriggeredAbilities abilities) {
super(abilities);
}
@ -52,9 +53,9 @@ import mage.game.events.GameEvent;
public void checkTriggers(GameEvent event, Game game) {
if (this.size() > 0) {
for (Iterator<DelayedTriggeredAbility> it = this.iterator();it.hasNext();) {
for (Iterator<DelayedTriggeredAbility> it = this.iterator(); it.hasNext(); ) {
DelayedTriggeredAbility ability = it.next();
if (ability.getDuration()== Duration.Custom){
if (ability.getDuration() == Duration.Custom) {
if (ability.isInactive(game)) {
it.remove();
continue;

View file

@ -27,6 +27,7 @@
*/
package mage.abilities;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.costs.Cost;
import mage.abilities.costs.VariableCost;
@ -44,8 +45,6 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import java.util.UUID;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -94,7 +93,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
public boolean canActivate(UUID playerId, Game game) {
if (game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) // check this first to allow Offering in main phase
|| this.spellCanBeActivatedRegularlyNow(playerId, game)) {
if (spellAbilityType == SpellAbilityType.SPLIT) {
if (spellAbilityType == SpellAbilityType.SPLIT || spellAbilityType == SpellAbilityType.SPLIT_AFTERMATH) {
return false;
}
// fix for Gitaxian Probe and casting opponent's spells
@ -110,7 +109,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
}
}
// Alternate spell abilities (Flashback, Overload) can't be cast with no mana to pay option
if (getSpellAbilityType()== SpellAbilityType.BASE_ALTERNATE) {
if (getSpellAbilityType() == SpellAbilityType.BASE_ALTERNATE) {
Player player = game.getPlayer(playerId);
if (player != null && getSourceId().equals(player.getCastSourceIdWithAlternateMana())) {
return false;
@ -183,27 +182,27 @@ public class SpellAbility extends ActivatedAbilityImpl {
public int getConvertedXManaCost(Card card) {
int xMultiplier = 0;
int amount = 0;
if(card == null) {
if (card == null) {
return 0;
}
for(ManaCost manaCost : card.getManaCost()) {
if(manaCost instanceof VariableManaCost) {
xMultiplier = ((VariableManaCost)manaCost).getMultiplier();
for (ManaCost manaCost : card.getManaCost()) {
if (manaCost instanceof VariableManaCost) {
xMultiplier = ((VariableManaCost) manaCost).getMultiplier();
break;
}
}
boolean hasNonManaXCost = false;
for(Cost cost : getCosts()) {
if(cost instanceof VariableCost) {
for (Cost cost : getCosts()) {
if (cost instanceof VariableCost) {
hasNonManaXCost = true;
amount = ((VariableCost) cost).getAmount();
break;
}
}
if(!hasNonManaXCost) {
if (!hasNonManaXCost) {
amount = getManaCostsToPay().getX();
}
return amount * xMultiplier;

View file

@ -45,7 +45,6 @@ import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
*
@ -126,7 +125,7 @@ class KinshipBaseEffect extends OneShotEffect {
if (card != null) {
Cards cards = new CardsImpl(card);
controller.lookAtCards(sourcePermanent.getName(), cards, game);
if (CardUtil.shareSubtypes(sourcePermanent, card, game)) {
if (sourcePermanent.shareSubtypes(card, game)) {
if (controller.chooseUse(outcome,new StringBuilder("Kinship - Reveal ").append(card.getLogName()).append('?').toString(), source, game)) {
controller.revealCards(sourcePermanent.getName(), cards, game);
for (Effect effect: kinshipEffects) {

View file

@ -46,14 +46,14 @@ import mage.constants.Zone;
public class LieutenantAbility extends SimpleStaticAbility {
public LieutenantAbility(ContinuousEffect effect) {
super(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), CommanderInPlayCondition.getInstance(), "<i>Lieutenant</i> - As long as you control your commander, {this} gets +2/+2"));
this.addEffect(new ConditionalContinuousEffect(effect, CommanderInPlayCondition.getInstance(), effect.getText(null)));
super(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), CommanderInPlayCondition.instance, "<i>Lieutenant</i> - As long as you control your commander, {this} gets +2/+2"));
this.addEffect(new ConditionalContinuousEffect(effect, CommanderInPlayCondition.instance, effect.getText(null)));
}
public LieutenantAbility(Effects effects) {
super(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), CommanderInPlayCondition.getInstance(), "<i>Lieutenant</i> - As long as you control your commander, {this} gets +2/+2"));
super(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), CommanderInPlayCondition.instance, "<i>Lieutenant</i> - As long as you control your commander, {this} gets +2/+2"));
for (Effect effect : effects) {
this.addEffect(new ConditionalContinuousEffect((ContinuousEffect) effect, CommanderInPlayCondition.getInstance(), effect.getText(null)));
this.addEffect(new ConditionalContinuousEffect((ContinuousEffect) effect, CommanderInPlayCondition.instance, effect.getText(null)));
}
}

View file

@ -94,7 +94,7 @@ public class ActivateIfConditionActivatedAbility extends ActivatedAbilityImpl {
}
sb.append(condition.toString()).append('.');
} else {
sb.append(" [Condition toSting() == null] ");
sb.append(" [Condition toString() == null] ");
}
return sb.toString() ;
}

View file

@ -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.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
*
* @author LevelX2
*/
public class BecomesExertSourceTriggeredAbility extends TriggeredAbilityImpl {
public BecomesExertSourceTriggeredAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect, false);
}
public BecomesExertSourceTriggeredAbility(final BecomesExertSourceTriggeredAbility ability) {
super(ability);
}
@Override
public BecomesExertSourceTriggeredAbility copy() {
return new BecomesExertSourceTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.BECOMES_EXERTED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getSourceId().equals(this.getSourceId())) {
return true;
}
return false;
}
@Override
public String getRule() {
return "When {this} becomes exerted, " + super.getRule();
}
}

View file

@ -27,10 +27,10 @@
*/
package mage.abilities.common;
import mage.constants.ComparisonType;
import mage.abilities.StateTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.filter.Filter.ComparisonType;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
@ -69,24 +69,7 @@ public class ControlsPermanentsControllerTriggeredAbility extends StateTriggered
@Override
public boolean checkTrigger(GameEvent event, Game game) {
int inputValue = game.getBattlefield().countAll(filter, getControllerId(), game);
switch (type) {
case Equal:
if (inputValue != value) {
return false;
}
break;
case GreaterThan:
if (inputValue <= value) {
return false;
}
break;
case LessThan:
if (inputValue >= value) {
return false;
}
break;
}
return true;
return ComparisonType.compare(inputValue, type, value);
}
@Override

View file

@ -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.common;
import mage.constants.Zone;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
*
* @author fireshoes
*/
public class CycleOrDiscardControllerTriggeredAbility extends TriggeredAbilityImpl {
public CycleOrDiscardControllerTriggeredAbility(Effect effect) {
this(effect, false);
}
public CycleOrDiscardControllerTriggeredAbility(Effect effect, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
}
public CycleOrDiscardControllerTriggeredAbility(final CycleOrDiscardControllerTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DISCARDED_CARD;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return event.getPlayerId().equals(controllerId);
}
@Override
public String getRule() {
return "Whenever you cycle or discard a card, " + super.getRule();
}
@Override
public CycleOrDiscardControllerTriggeredAbility copy() {
return new CycleOrDiscardControllerTriggeredAbility(this);
}
}

View file

@ -0,0 +1,45 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
/**
* @author stravant
*/
public class ExertCreatureControllerTriggeredAbility extends TriggeredAbilityImpl {
public ExertCreatureControllerTriggeredAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect);
}
public ExertCreatureControllerTriggeredAbility(final ExertCreatureControllerTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.BECOMES_EXERTED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
boolean weAreExerting = getControllerId().equals(event.getPlayerId());
Permanent exerted = game.getPermanent(event.getTargetId());
boolean exertedIsCreature = (exerted != null) && exerted.isCreature();
return weAreExerting && exertedIsCreature;
}
@Override
public ExertCreatureControllerTriggeredAbility copy() {
return new ExertCreatureControllerTriggeredAbility(this);
}
@Override
public String getRule() {
return "Whenever you exert a creature, " + super.getRule();
}
}

View file

@ -139,7 +139,7 @@ class LicidContinuousEffect extends ContinuousEffectImpl {
switch (layer) {
case TypeChangingEffects_4:
licid.getCardType().clear();
licid.getCardType().add(CardType.ENCHANTMENT);
licid.addCardType(CardType.ENCHANTMENT);
licid.getSubtype(game).clear();
licid.getSubtype(game).add("Aura");
break;

View file

@ -1,9 +1,10 @@
package mage.abilities.condition;
import java.io.Serializable;
import mage.abilities.Ability;
import mage.game.Game;
import java.io.Serializable;
/**
* Interface describing condition occurrence.
@ -13,24 +14,6 @@ import mage.game.Game;
@FunctionalInterface
public interface Condition extends Serializable {
enum ComparisonType {
GreaterThan(">"),
Equal("=="),
LessThan("<");
private final String text;
ComparisonType(String text) {
this.text = text;
}
@Override
public String toString() {
return text;
}
}
/**
* Checks the game to see if this condition applies for the given ability.
*

View file

@ -28,6 +28,7 @@
package mage.abilities.condition;
import mage.abilities.Ability;
import mage.constants.ComparisonType;
import mage.game.Game;
/**
@ -36,10 +37,10 @@ import mage.game.Game;
*/
public abstract class IntCompareCondition implements Condition {
protected final Condition.ComparisonType type;
protected final ComparisonType type;
protected final int value;
public IntCompareCondition(Condition.ComparisonType type, int value) {
public IntCompareCondition(ComparisonType type, int value) {
this.type = type;
this.value = value;
}
@ -49,24 +50,7 @@ public abstract class IntCompareCondition implements Condition {
@Override
public final boolean apply(Game game, Ability source) {
int inputValue = getInputValue(game, source);
switch (type) {
case Equal:
if (inputValue != value) {
return false;
}
break;
case GreaterThan:
if (inputValue <= value) {
return false;
}
break;
case LessThan:
if (inputValue >= value) {
return false;
}
break;
}
return true;
return ComparisonType.compare(inputValue , type, value);
}
@Override

View file

@ -16,13 +16,9 @@ import mage.game.Game;
*
* @author LevelX2
*/
public class BeforeBlockersAreDeclaredCondition implements Condition {
public enum BeforeBlockersAreDeclaredCondition implements Condition {
private static final BeforeBlockersAreDeclaredCondition instance = new BeforeBlockersAreDeclaredCondition();
public static Condition getInstance() {
return instance;
}
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -27,22 +27,23 @@
*/
package mage.abilities.condition.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.constants.ComparisonType;
import mage.abilities.condition.Condition;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public class CardsInAnyLibraryCondition implements Condition {
protected final Condition.ComparisonType type;
protected final ComparisonType type;
protected final int value;
public CardsInAnyLibraryCondition(Condition.ComparisonType type, int value) {
public CardsInAnyLibraryCondition(ComparisonType type, int value) {
this.type = type;
this.value = value;
}
@ -54,23 +55,8 @@ public class CardsInAnyLibraryCondition implements Condition {
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);
if (player != null) {
switch (type) {
case GreaterThan:
if (player.getLibrary().size() > value) {
return true;
}
break;
case Equal:
if (player.getLibrary().size() == value) {
return true;
}
break;
case LessThan:
if (player.getLibrary().size() < value) {
return true;
}
break;
}
return ComparisonType.compare(player.getLibrary().size(), type, value);
}
}
}
@ -81,13 +67,13 @@ public class CardsInAnyLibraryCondition implements Condition {
public String toString() {
StringBuilder sb = new StringBuilder("a library has ");
switch (type) {
case GreaterThan:
case MORE_THAN:
sb.append(value + 1).append(" or more cards in it ");
break;
case Equal:
case EQUAL_TO:
sb.append(value).append(" cards in it ");
break;
case LessThan:
case FEWER_THAN:
sb.append(value - 1).append(" or fewer cards in it ");
break;
}

View file

@ -30,7 +30,7 @@ package mage.abilities.condition.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.CountType;
import mage.constants.ComparisonType;
import mage.abilities.condition.Condition;
import mage.constants.TargetController;
import mage.game.Game;
@ -47,23 +47,23 @@ public class CardsInHandCondition implements Condition {
private Condition condition;
private CountType type;
private ComparisonType type;
private int count;
private TargetController targetController;
public CardsInHandCondition() {
this(CountType.EQUAL_TO, 0);
this(ComparisonType.EQUAL_TO, 0);
}
public CardsInHandCondition(CountType type, int count) {
public CardsInHandCondition(ComparisonType type, int count) {
this(type, count, null);
}
public CardsInHandCondition(CountType type, int count, Condition conditionToDecorate) {
public CardsInHandCondition(ComparisonType type, int count, Condition conditionToDecorate) {
this(type, count, conditionToDecorate, TargetController.YOU);
}
public CardsInHandCondition(CountType type, int count, Condition conditionToDecorate, TargetController targetController) {
public CardsInHandCondition(ComparisonType type, int count, Condition conditionToDecorate, TargetController targetController) {
this.type = type;
this.count = count;
this.condition = conditionToDecorate;
@ -77,12 +77,12 @@ public class CardsInHandCondition implements Condition {
if (controller != null) {
switch (targetController) {
case YOU:
conditionApplies = CountType.compare(game.getPlayer(source.getControllerId()).getHand().size(), type, count);
conditionApplies = ComparisonType.compare(game.getPlayer(source.getControllerId()).getHand().size(), type, count);
break;
case ACTIVE:
Player player = game.getPlayer(game.getActivePlayerId());
if (player != null) {
conditionApplies = CountType.compare(player.getHand().size(), type, count);
conditionApplies = ComparisonType.compare(player.getHand().size(), type, count);
}
break;
case ANY:
@ -90,7 +90,7 @@ public class CardsInHandCondition implements Condition {
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
player = game.getPlayer(playerId);
if (player != null) {
if (!CountType.compare(player.getHand().size(), type, this.count)) {
if (!ComparisonType.compare(player.getHand().size(), type, this.count)) {
conflict = true;
break;
}

View file

@ -13,7 +13,9 @@ import mage.watchers.common.CastFromHandWatcher;
*
* @author Loki
*/
public class CastFromHandSourceCondition implements Condition {
public enum CastFromHandSourceCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -27,31 +27,22 @@
*/
package mage.abilities.condition.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import java.util.UUID;
/**
* Checks if the player has its commander in play and controls it
*
* @author LevelX2
*/
public class CommanderInPlayCondition implements Condition {
public enum CommanderInPlayCondition implements Condition {
private static CommanderInPlayCondition instance = null;
private CommanderInPlayCondition() {
}
public static Condition getInstance() {
if (instance == null) {
instance = new CommanderInPlayCondition();
}
return instance;
}
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -28,7 +28,7 @@
package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.constants.ComparisonType;
import mage.abilities.condition.IntCompareCondition;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DevourEffect;
@ -43,7 +43,7 @@ import mage.game.permanent.Permanent;
*/
public class DevouredCreaturesCondition extends IntCompareCondition {
public DevouredCreaturesCondition(Condition.ComparisonType type, int value) {
public DevouredCreaturesCondition(ComparisonType type, int value) {
super(type, value);
}

View file

@ -29,10 +29,13 @@ package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.EnumSet;
/**
*
* @author LevelX2
@ -40,15 +43,15 @@ import mage.game.permanent.Permanent;
public class EquippedHasSupertypeCondition implements Condition {
private String superType;
private String[] superTypes; // scope = Any
private SuperType superType;
private EnumSet<SuperType> superTypes = EnumSet.noneOf(SuperType.class); // scope = Any
public EquippedHasSupertypeCondition(String subType) {
this.superType = subType;
public EquippedHasSupertypeCondition(SuperType supertype) {
this.superType = supertype;
}
public EquippedHasSupertypeCondition(String... subTypes) {
this.superTypes = subTypes;
public EquippedHasSupertypeCondition(EnumSet<SuperType> superTypes) {
this.superTypes = superTypes;
}
@Override
@ -61,12 +64,12 @@ public class EquippedHasSupertypeCondition implements Condition {
}
if (attachedTo != null) {
if (superType != null) {
if (attachedTo.getSupertype().contains(this.superType)) {
if (attachedTo.getSuperType().contains(this.superType)) {
return true;
}
} else {
for (String s : superTypes) {
if (attachedTo.getSupertype().contains(s)) {
for (SuperType s : superTypes) {
if (attachedTo.getSuperType().contains(s)) {
return true;
}
}

View file

@ -27,24 +27,21 @@
*/
package mage.abilities.condition.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
* Describes condition when creature is equipped.
*
* @author nantuko
*/
public class EquippedSourceCondition implements Condition {
public enum EquippedSourceCondition implements Condition {
private static final EquippedSourceCondition instance = new EquippedSourceCondition();
public static Condition getInstance() {
return instance;
}
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -29,8 +29,8 @@
package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.constants.ComparisonType;
import mage.abilities.condition.Condition;
import mage.filter.Filter;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.PowerPredicate;
import mage.game.Game;
@ -43,7 +43,7 @@ public enum FerociousCondition implements Condition {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
static {
filter.add(new PowerPredicate(Filter.ComparisonType.GreaterThan, 3));
filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3));
}

View file

@ -32,13 +32,9 @@ import mage.abilities.condition.Condition;
import mage.game.Game;
import mage.game.permanent.Permanent;
public class FlippedCondition implements Condition {
public enum FlippedCondition implements Condition {
private static FlippedCondition instance = new FlippedCondition();
public static Condition getInstance() {
return instance;
}
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -0,0 +1,22 @@
package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.game.Game;
/**
* @author stravant
*/
public enum HeckbentCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {
return game.getPlayer(source.getControllerId()).getHand().size() <= 1;
}
@Override
public String toString() {
return "if you have one or fewer cards in hand";
}
}

View file

@ -8,15 +8,8 @@ import mage.watchers.common.LandfallWatcher;
/**
* @author Loki
*/
public class LandfallCondition implements Condition {
private final static LandfallCondition instance = new LandfallCondition();
public static LandfallCondition getInstance() {
return instance;
}
private LandfallCondition() {
}
public enum LandfallCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -35,13 +35,9 @@ import mage.game.Game;
*
* @author LevelX2
*/
public class MonarchIsSourceControllerCondition implements Condition {
public enum MonarchIsSourceControllerCondition implements Condition {
private final static MonarchIsSourceControllerCondition instance = new MonarchIsSourceControllerCondition();
public static Condition getInstance() {
return instance;
}
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -7,7 +7,9 @@ import mage.players.Player;
import java.util.UUID;
public class MoreCardsInHandThanOpponentsCondition implements Condition {
public enum MoreCardsInHandThanOpponentsCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -37,12 +37,8 @@ import mage.game.Game;
*
* @author LevelX2
*/
public class MyTurnBeforeAttackersDeclaredCondition implements Condition {
private static final MyTurnBeforeAttackersDeclaredCondition instance = new MyTurnBeforeAttackersDeclaredCondition();
public static Condition getInstance() {
return instance;
}
public enum MyTurnBeforeAttackersDeclaredCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -31,7 +31,7 @@ package mage.abilities.condition.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.CountType;
import mage.constants.ComparisonType;
import mage.abilities.condition.Condition;
import mage.filter.FilterPermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
@ -48,18 +48,18 @@ public class OpponentControlsPermanentCondition implements Condition {
private FilterPermanent filter;
private CountType type;
private ComparisonType type;
private int count;
/**
* @param filter
*/
public OpponentControlsPermanentCondition(FilterPermanent filter) {
this(filter, CountType.MORE_THAN, 0);
this(filter, ComparisonType.MORE_THAN, 0);
}
/**
* Applies a filter, a {@link CountType}, and count to permanents on the
* Applies a filter, a {@link ComparisonType}, and count to permanents on the
* battlefield when checking the condition during the
* {@link #apply(mage.game.Game, mage.abilities.Ability) apply} method invocation.
*
@ -67,7 +67,7 @@ public class OpponentControlsPermanentCondition implements Condition {
* @param type
* @param count
*/
public OpponentControlsPermanentCondition(FilterPermanent filter, CountType type, int count) {
public OpponentControlsPermanentCondition(FilterPermanent filter, ComparisonType type, int count) {
this.filter = filter;
this.type = type;
this.count = count;
@ -79,7 +79,7 @@ public class OpponentControlsPermanentCondition implements Condition {
for (UUID opponentId : game.getOpponents(source.getControllerId())) {
FilterPermanent localFilter = filter.copy();
localFilter.add(new ControllerIdPredicate(opponentId));
if (CountType.compare(game.getBattlefield().count(localFilter, source.getSourceId(), source.getControllerId(), game), type, this.count)) {
if (ComparisonType.compare(game.getBattlefield().count(localFilter, source.getSourceId(), source.getControllerId(), game), type, this.count)) {
conditionApplies = true;
break;
}

View file

@ -28,21 +28,21 @@
package mage.abilities.condition.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author fireshoes
*/
public class OpponentHasMoreLifeCondition implements Condition {
public enum OpponentHasMoreLifeCondition implements Condition {
public OpponentHasMoreLifeCondition() {
}
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -27,14 +27,14 @@
*/
package mage.abilities.condition.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.constants.ComparisonType;
import mage.abilities.condition.IntCompareCondition;
import mage.game.Game;
import mage.watchers.common.PlayerLostLifeWatcher;
import java.util.UUID;
/**
* Describes condition when an opponent has lost an amount of life
*
@ -42,7 +42,7 @@ import mage.watchers.common.PlayerLostLifeWatcher;
*/
public class OpponentLostLifeCondition extends IntCompareCondition {
public OpponentLostLifeCondition(Condition.ComparisonType type, int value) {
public OpponentLostLifeCondition(ComparisonType type, int value) {
super(type, value);
}
@ -65,13 +65,13 @@ public class OpponentLostLifeCondition extends IntCompareCondition {
public String toString() {
StringBuilder sb = new StringBuilder("if an opponent lost ");
switch (type) {
case GreaterThan:
case MORE_THAN:
sb.append(value + 1).append(" or more life this turn ");
break;
case Equal:
case EQUAL_TO:
sb.append(value).append(" life this turn ");
break;
case LessThan:
case FEWER_THAN:
sb.append(" less than ").append(value).append(" life this turn ");
break;
}

View file

@ -28,7 +28,7 @@
package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.abilities.CountType;
import mage.constants.ComparisonType;
import mage.abilities.condition.Condition;
import mage.counters.CounterType;
import mage.game.Game;
@ -47,15 +47,15 @@ public class PermanentHasCounterCondition implements Condition {
private CounterType counterType;
private int amount;
private FilterPermanent filter;
private CountType counttype;
private ComparisonType counttype;
private boolean anyPlayer;
public PermanentHasCounterCondition(CounterType counterType, int amount, FilterPermanent filter) {
this(counterType, amount, filter, CountType.EQUAL_TO);
this(counterType, amount, filter, ComparisonType.EQUAL_TO);
this.anyPlayer = false;
}
public PermanentHasCounterCondition(CounterType counterType, int amount, FilterPermanent filter, CountType type) {
public PermanentHasCounterCondition(CounterType counterType, int amount, FilterPermanent filter, ComparisonType type) {
this.counterType = counterType;
this.amount = amount;
this.filter = filter;
@ -63,7 +63,7 @@ public class PermanentHasCounterCondition implements Condition {
this.anyPlayer = false;
}
public PermanentHasCounterCondition(CounterType counterType, int amount, FilterPermanent filter, CountType type, boolean any) {
public PermanentHasCounterCondition(CounterType counterType, int amount, FilterPermanent filter, ComparisonType type, boolean any) {
this.counterType = counterType;
this.amount = amount;
this.filter = filter;
@ -77,7 +77,7 @@ public class PermanentHasCounterCondition implements Condition {
permanents = game.getBattlefield().getAllActivePermanents(this.filter, game);
}
for (Permanent permanent : permanents) {
if(CountType.compare(permanent.getCounters(game).getCount(this.counterType), counttype, this.amount))
if(ComparisonType.compare(permanent.getCounters(game).getCount(this.counterType), counttype, this.amount))
{
return true;
}

View file

@ -28,7 +28,7 @@
package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.abilities.CountType;
import mage.constants.ComparisonType;
import mage.abilities.condition.Condition;
import mage.filter.FilterPermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
@ -47,23 +47,23 @@ public class PermanentsOnTheBattlefieldCondition implements Condition {
private FilterPermanent filter;
private Condition condition;
private CountType type;
private ComparisonType type;
private int count;
private boolean onlyControlled;
/**
* Applies a filter and delegates creation to
* {@link #ControlsPermanent(mage.filter.FilterPermanent, mage.abilities.condition.common.ControlsPermanent.CountType, int)}
* with {@link CountType#MORE_THAN}, and 0.
* with {@link ComparisonType#MORE_THAN}, and 0.
*
* @param filter
*/
public PermanentsOnTheBattlefieldCondition(FilterPermanent filter) {
this(filter, CountType.MORE_THAN, 0);
this(filter, ComparisonType.MORE_THAN, 0);
}
/**
* Applies a filter, a {@link CountType}, and count to permanents on the
* Applies a filter, a {@link ComparisonType}, and count to permanents on the
* battlefield when checking the condition during the
* {@link #apply(mage.game.Game, mage.abilities.Ability) apply} method invocation.
*
@ -71,11 +71,11 @@ public class PermanentsOnTheBattlefieldCondition implements Condition {
* @param type
* @param count
*/
public PermanentsOnTheBattlefieldCondition(FilterPermanent filter, CountType type, int count) {
public PermanentsOnTheBattlefieldCondition(FilterPermanent filter, ComparisonType type, int count) {
this(filter, type, count, true);
}
public PermanentsOnTheBattlefieldCondition(FilterPermanent filter, CountType type, int count, boolean onlyControlled) {
public PermanentsOnTheBattlefieldCondition(FilterPermanent filter, ComparisonType type, int count, boolean onlyControlled) {
this.filter = filter;
this.type = type;
this.count = count;
@ -83,7 +83,7 @@ public class PermanentsOnTheBattlefieldCondition implements Condition {
}
/**
* Applies a filter, a {@link CountType}, and count to permanents on the
* Applies a filter, a {@link ComparisonType}, and count to permanents on the
* battlefield and calls the decorated condition to see if it
* {@link #apply(mage.game.Game, mage.abilities.Ability) applies}
* as well. This will force both conditions to apply for this to be true.
@ -93,7 +93,7 @@ public class PermanentsOnTheBattlefieldCondition implements Condition {
* @param count
* @param conditionToDecorate
*/
public PermanentsOnTheBattlefieldCondition(FilterPermanent filter, CountType type, int count, Condition conditionToDecorate) {
public PermanentsOnTheBattlefieldCondition(FilterPermanent filter, ComparisonType type, int count, Condition conditionToDecorate) {
this(filter, type, count);
this.condition = conditionToDecorate;
}
@ -108,7 +108,7 @@ public class PermanentsOnTheBattlefieldCondition implements Condition {
}
int permanentsOnBattlefield = game.getBattlefield().count(localFilter, source.getSourceId(), source.getControllerId(), game);
conditionApplies = CountType.compare(permanentsOnBattlefield, type, count);
conditionApplies = ComparisonType.compare(permanentsOnBattlefield, type, count);
//If a decorated condition exists, check it as well and apply them together.
if (this.condition != null) {

View file

@ -36,19 +36,9 @@ import mage.game.permanent.Permanent;
*
* @author LevelX2
*/
public class RenownedTargetCondition implements Condition {
public enum RenownedTargetCondition implements Condition {
private static RenownedTargetCondition instance = null;
private RenownedTargetCondition() {
}
public static RenownedTargetCondition getInstance() {
if (instance == null) {
instance = new RenownedTargetCondition();
}
return instance;
}
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -37,13 +37,9 @@ import mage.game.permanent.Permanent;
* @author LevelX2
*/
public class SourceAttackingCondition implements Condition {
public enum SourceAttackingCondition implements Condition {
private static final SourceAttackingCondition instance = new SourceAttackingCondition();
public static SourceAttackingCondition getInstance() {
return instance;
}
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -35,7 +35,7 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
*
* @author nantuko
*/
public class SourceHasCounterCondition implements Condition {
@ -82,12 +82,26 @@ public class SourceHasCounterCondition implements Condition {
return count >= from;
}
return count >= from && count <= to;
} else { // single compare (lte)
} else // single compare (lte)
{
if (card != null) {
return card.getCounters(game).getCount(counterType) >= amount;
} else {
} else {
return permanent.getCounters(game).getCount(counterType) >= amount;
}
}
}
@Override
public String toString() {
if (from != -1) { //range compare
if (to == Integer.MAX_VALUE) {
return "{this} has equal to or more than " + from + " " + this.counterType.toString() + " counters";
}
return "{this} has between " + from + " and " + to + " " + this.counterType.toString() + " counters";
} else // single compare (lte)
{
return "{this} has equal or more than " + amount + " " + this.counterType.toString() + " counters";
}
}
}

View file

@ -38,13 +38,9 @@ import mage.game.Game;
*
* @author LevelX2
*/
public class SourceOnBattlefieldCondition implements Condition {
public enum SourceOnBattlefieldCondition implements Condition {
private static final SourceOnBattlefieldCondition instance = new SourceOnBattlefieldCondition();
public static SourceOnBattlefieldCondition getInstance() {
return instance;
}
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -28,7 +28,6 @@
package mage.abilities.condition.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.constants.PhaseStep;
@ -37,6 +36,8 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.turn.Step;
import java.util.UUID;
/**
*
* @author spjspj
@ -44,15 +45,11 @@ import mage.game.turn.Step;
public class SourceTappedBeforeUntapStepCondition implements Condition {
private static final SourceTappedBeforeUntapStepCondition instance = new SourceTappedBeforeUntapStepCondition();
UUID permanentId = null;
boolean permanentWasTappedBeforeUntapStep = false;
int lastTurnNum = -1;
public static SourceTappedBeforeUntapStepCondition getInstance() {
return instance;
}
public void setPermanentId(UUID permanentId) {
this.permanentId = permanentId;

View file

@ -45,18 +45,9 @@ import mage.game.Game;
*
*/
public class SuspendedCondition implements Condition {
public enum SuspendedCondition implements Condition {
private static SuspendedCondition instance = null;
private SuspendedCondition() {}
public static Condition getInstance() {
if (instance == null) {
instance = new SuspendedCondition();
}
return instance;
}
instance;
@Override
public boolean apply(Game game, Ability source) {

View file

@ -27,6 +27,7 @@
*/
package mage.abilities.condition.common;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.game.Game;
@ -42,7 +43,7 @@ public enum TargetAttackedThisTurnCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
Permanent creature = game.getPermanentOrLKIBattlefield(source.getTargets().getFirstTarget());
AttackedThisTurnWatcher watcher = (AttackedThisTurnWatcher) game.getState().getWatchers().get("AttackedThisTurn");
return watcher.getAttackedThisTurnCreatures().contains(creature.getId());
AttackedThisTurnWatcher watcher = (AttackedThisTurnWatcher) game.getState().getWatchers().get(AttackedThisTurnWatcher.class.getName());
return watcher.getAttackedThisTurnCreatures().contains(new MageObjectReference(creature, game));
}
}

View file

@ -27,25 +27,28 @@
*/
package mage.abilities.condition.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.game.Game;
import mage.players.Player;
import mage.players.PlayerList;
import java.util.UUID;
/**
*
* @author maurer.it_at_gmail.com
*/
public class TenOrLessLifeCondition implements Condition {
public class XorLessLifeCondition implements Condition {
public enum CheckType { AN_OPPONENT, CONTROLLER, TARGET_OPPONENT, EACH_PLAYER }
private final CheckType type;
private final int amount;
public TenOrLessLifeCondition ( CheckType type ) {
public XorLessLifeCondition ( CheckType type , int amount) {
this.type = type;
this.amount = amount;
}
@Override
@ -55,11 +58,11 @@ public class TenOrLessLifeCondition implements Condition {
switch ( this.type ) {
case AN_OPPONENT:
for ( UUID opponentUUID : game.getOpponents(source.getControllerId()) ) {
conditionApplies |= game.getPlayer(opponentUUID).getLife() <= 10;
conditionApplies |= game.getPlayer(opponentUUID).getLife() <= amount;
}
break;
case CONTROLLER:
conditionApplies |= game.getPlayer(source.getControllerId()).getLife() <= 10;
conditionApplies |= game.getPlayer(source.getControllerId()).getLife() <= amount;
break;
case TARGET_OPPONENT:
//TODO: Implement this.
@ -71,11 +74,11 @@ public class TenOrLessLifeCondition implements Condition {
Player p = game.getPlayer(pid);
if (p != null) {
if (maxLife < p.getLife()) {
maxLife = p.getLife();
maxLife = p.getLife();
}
}
}
conditionApplies |= maxLife <= 10;
conditionApplies |= maxLife <= amount;
break;
}

View file

@ -0,0 +1,33 @@
package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.constants.ComparisonType;
import mage.abilities.condition.IntCompareCondition;
import mage.game.Game;
import mage.watchers.common.PlayerGainedLifeWatcher;
/**
* Created by IGOUDT on 5-4-2017.
*/
public class YouGainedLifeCondition extends IntCompareCondition {
public YouGainedLifeCondition(ComparisonType type, int value) {
super(type, value);
}
@Override
protected int getInputValue(Game game, Ability source) {
int gainedLife = 0;
PlayerGainedLifeWatcher watcher = (PlayerGainedLifeWatcher) game.getState().getWatchers().get(PlayerGainedLifeWatcher.class.getName());
if (watcher != null) {
gainedLife = watcher.getLiveGained(source.getControllerId());
}
return gainedLife;
}
@Override
public String toString() {
return String.format("if you gained %s or more life this turn ", value);
}
}

View file

@ -29,7 +29,9 @@ package mage.abilities.costs;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.keyword.FlashbackAbility;
import mage.game.Game;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.Target;
import mage.target.Targets;
@ -131,13 +133,15 @@ public abstract class VariableCostImpl implements Cost, VariableCost {
@Override
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
return true; /* not used */
return true;
/* not used */
}
@Override
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
return true; /* not used */
return true;
/* not used */
}
@ -165,7 +169,9 @@ public abstract class VariableCostImpl implements Cost, VariableCost {
public int announceXValue(Ability source, Game game) {
int xValue = 0;
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
StackObject stackObject = game.getStack().getStackObject(source.getId());
if (controller != null
&& (source instanceof FlashbackAbility || stackObject != null)) {
xValue = controller.announceXCost(getMinValue(source, game), getMaxValue(source, game),
"Announce the number of " + actionText, game, source, this);
}

View file

@ -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.costs.common;
import mage.abilities.Ability;
@ -40,7 +39,6 @@ import mage.target.common.TargetCardInHand;
*
* @author LevelX2
*/
public class DiscardXTargetCost extends VariableCostImpl {
protected FilterCard filter;
@ -50,9 +48,8 @@ public class DiscardXTargetCost extends VariableCostImpl {
}
public DiscardXTargetCost(FilterCard filter, boolean additionalCostText) {
super(new StringBuilder(filter.getMessage()).append(" to discard").toString());
this.text = new StringBuilder(additionalCostText ? "As an additional cost to cast {source}, discard ":"Discard ")
.append(xText).append(' ').append(filter.getMessage()).toString();
super(filter.getMessage() + " to discard");
this.text = (additionalCostText ? "As an additional cost to cast {source}, discard " : "Discard ") + xText + ' ' + filter.getMessage();
this.filter = filter;
}

View file

@ -31,6 +31,7 @@ import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.constants.ColoredManaSymbol;
import mage.constants.SuperType;
import mage.filter.FilterObject;
import mage.filter.predicate.mageobject.SupertypePredicate;
import mage.game.Game;
@ -41,7 +42,7 @@ public class SnowManaCost extends ManaCostImpl {
private static final FilterObject filter = new FilterObject("Snow object");
static {
filter.add(new SupertypePredicate("Snow"));
filter.add(new SupertypePredicate(SuperType.SNOW));
}
public SnowManaCost() {

View file

@ -0,0 +1,45 @@
package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.game.Game;
/**
* @author stravant
*/
public class EffectKeyValue implements DynamicValue {
private String key;
private String description;
public EffectKeyValue(String key) {
this.key = key;
this.description = key;
}
public EffectKeyValue(String key, String description) {
this(key);
this.description = description;
}
@Override
public int calculate(Game game, Ability source, Effect effect) {
return (Integer)effect.getValue(key);
}
@Override
public EffectKeyValue copy(){
return new EffectKeyValue(this.key, this.description);
}
@Override
public String toString() {
return "equal to";
}
@Override
public String getMessage() {
return description;
}
}

View file

@ -40,6 +40,15 @@ import mage.players.ManaPoolItem;
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
/**
*
* @param manaType type of mana with which the player wants to pay the cost
* @param mana mana pool item to pay from the cost
* @param affectedControllerId
* @param source
* @param game
* @return
*/
ManaType getAsThoughManaType(ManaType manaType, ManaPoolItem mana, UUID affectedControllerId, Ability source, Game game);
}

View file

@ -27,6 +27,9 @@
*/
package mage.abilities.effects;
import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;
import mage.MageObject;
import mage.abilities.*;
import mage.abilities.keyword.SpliceOntoArcaneAbility;
@ -49,10 +52,6 @@ import mage.players.Player;
import mage.target.common.TargetCardInHand;
import org.apache.log4j.Logger;
import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -691,8 +690,7 @@ public class ContinuousEffects implements Serializable {
spliceAbilities.remove(selectedAbility);
}
}
}
while (!spliceAbilities.isEmpty() && controller.chooseUse(Outcome.Benefit, "Splice another card?", abilityToModify, game));
} while (!spliceAbilities.isEmpty() && controller.chooseUse(Outcome.Benefit, "Splice another card?", abilityToModify, game));
controller.revealCards("Spliced cards", cardsToReveal, game);
}
}
@ -702,10 +700,10 @@ public class ContinuousEffects implements Serializable {
* Checks if an event won't happen because of an rule modifying effect
*
* @param event
* @param targetAbility ability the event is attached to. can be null.
* @param targetAbility ability the event is attached to. can be null.
* @param game
* @param checkPlayableMode true if the event does not really happen but
* it's checked if the event would be replaced
* it's checked if the event would be replaced
* @return
*/
public boolean preventedByRuleModification(GameEvent event, Ability targetAbility, Game game, boolean checkPlayableMode) {
@ -752,7 +750,7 @@ public class ContinuousEffects implements Serializable {
do {
HashMap<ReplacementEffect, HashSet<Ability>> rEffects = getApplicableReplacementEffects(event, game);
// Remove all consumed effects (ability dependant)
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext(); ) {
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext();) {
ReplacementEffect entry = it1.next();
if (consumed.containsKey(entry.getId())) {
HashSet<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
@ -933,7 +931,7 @@ public class ContinuousEffects implements Serializable {
if (!waitingEffects.isEmpty()) {
// check if waiting effects can be applied now
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) {
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<ContinuousEffect, Set<UUID>> entry = iterator.next();
if (appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself
appliedAbilities = appliedEffectAbilities.get(entry.getKey());

View file

@ -46,7 +46,7 @@ public interface Effect extends Serializable {
UUID getId();
void newId();
String getText(Mode mode);
void setText(String staticText);
Effect setText(String staticText);
boolean apply(Game game, Ability source);
Outcome getOutcome();
void setOutcome(Outcome outcome);

View file

@ -61,8 +61,8 @@ public abstract class EffectImpl implements Effect {
public EffectImpl(final EffectImpl effect) {
this.id = effect.id;
this.outcome = effect.outcome;
this.effectType = effect.effectType;
this.staticText = effect.staticText;
this.effectType = effect.effectType;
this.targetPointer = effect.targetPointer.copy();
if (effect.values != null) {
values = new HashMap<>();
@ -85,8 +85,9 @@ public abstract class EffectImpl implements Effect {
}
@Override
public void setText(String staticText) {
public Effect setText(String staticText) {
this.staticText = staticText;
return this;
}
@Override

View file

@ -148,6 +148,7 @@ public class EntersBattlefieldEffect extends ReplacementEffectImpl {
if (spell != null) {
effect.setValue(SOURCE_CAST_SPELL_ABILITY, spell.getSpellAbility());
}
effect.setValue("appliedEffects", event.getAppliedEffects());
effect.apply(game, source);
}
// }

View file

@ -27,25 +27,20 @@
*/
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;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.Zone;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
import mage.game.permanent.PermanentToken;
import mage.util.functions.ApplyToPermanent;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
@ -128,15 +123,15 @@ public class CopyEffect extends ContinuousEffectImpl {
permanent.getManaCost().add(copyFromObject.getManaCost());
permanent.getCardType().clear();
for (CardType type : copyFromObject.getCardType()) {
permanent.getCardType().add(type);
permanent.addCardType(type);
}
permanent.getSubtype(game).clear();
for (String type : copyFromObject.getSubtype(game)) {
permanent.getSubtype(game).add(type);
}
permanent.getSupertype().clear();
for (String type : copyFromObject.getSupertype()) {
permanent.getSupertype().add(type);
permanent.getSuperType().clear();
for (SuperType type : copyFromObject.getSuperType()) {
permanent.addSuperType(type);
}
permanent.removeAllAbilities(source.getSourceId(), game);

View file

@ -2,11 +2,7 @@ package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.Token;
@ -32,15 +28,15 @@ public class CopyTokenEffect extends ContinuousEffectImpl {
permanent.getColor(game).setColor(token.getColor(game));
permanent.getCardType().clear();
for (CardType type: token.getCardType()) {
permanent.getCardType().add(type);
permanent.addCardType(type);
}
permanent.getSubtype(game).clear();
for (String type: token.getSubtype(game)) {
permanent.getSubtype(game).add(type);
}
permanent.getSupertype().clear();
for (String type: token.getSupertype()) {
permanent.getSupertype().add(type);
permanent.getSuperType().clear();
for (SuperType type: token.getSuperType()) {
permanent.addSuperType(type);
}
permanent.getAbilities().clear();
for (Ability ability: token.getAbilities()) {

View file

@ -48,7 +48,7 @@ public class EndTurnEffect extends OneShotEffect {
if (!game.isSimulation()) {
game.informPlayers("The current turn ends");
}
return game.endTurn();
return game.endTurn(source);
}
@Override

View file

@ -27,6 +27,8 @@
*/
package mage.abilities.effects.common;
import java.util.ArrayList;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.effects.EntersBattlefieldEffect;
@ -75,7 +77,8 @@ public class EntersBattlefieldWithXCountersEffect extends OneShotEffect {
if (amount > 0) {
Counter counterToAdd = counter.copy();
counterToAdd.add(amount - counter.getCount());
permanent.addCounters(counterToAdd, source, game);
ArrayList<UUID> appliedEffects = (ArrayList<UUID>) this.getValue("appliedEffects");
permanent.addCounters(counterToAdd, source, game, appliedEffects);
}
}
}

View file

@ -172,6 +172,8 @@ public class ExileTargetEffect extends OneShotEffect {
sb.append("target ");
}
sb.append(targetName);
} else if (target.getNumberOfTargets() == 0 && target.getMaxNumberOfTargets() > 0) {
sb.append("exile up to ").append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(" target ").append(target.getTargetName());
} else {
sb.append("exile ").append(CardUtil.numberToText(target.getNumberOfTargets())).append(" target ").append(target.getTargetName());
}

View file

@ -30,7 +30,6 @@ package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.OneShotEffect;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;

View file

@ -36,7 +36,7 @@ public class FlipSourceEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
if (permanent != null && controller != null) {
if (permanent.flip(game)) {
ContinuousEffect effect = new ConditionalContinuousEffect(new CopyTokenEffect(flipToken), FlippedCondition.getInstance(), "");
ContinuousEffect effect = new ConditionalContinuousEffect(new CopyTokenEffect(flipToken), FlippedCondition.instance, "");
game.addEffect(effect, source);
if (!game.isSimulation())
game.informPlayers(new StringBuilder(controller.getLogName()).append(" flips ").append(permanent.getName()).toString());

View file

@ -50,10 +50,11 @@ public class NameACardEffect extends OneShotEffect {
public enum TypeOfName {
ALL,
NON_ARTFIACT_AND_NON_LAND_NAME,
NON_ARTIFACT_AND_NON_LAND_NAME,
NON_LAND_NAME,
NON_LAND_AND_NON_CREATURE_NAME,
CREATURE_NAME
CREATURE_NAME,
ARTIFACT_NAME
}
private final TypeOfName typeOfName;
@ -83,7 +84,7 @@ public class NameACardEffect extends OneShotEffect {
cardChoice.setChoices(CardRepository.instance.getNames());
cardChoice.setMessage("Name a card");
break;
case NON_ARTFIACT_AND_NON_LAND_NAME:
case NON_ARTIFACT_AND_NON_LAND_NAME:
cardChoice.setChoices(CardRepository.instance.getNonArtifactAndNonLandNames());
cardChoice.setMessage("Name a non artifact and non land card");
break;
@ -99,6 +100,10 @@ public class NameACardEffect extends OneShotEffect {
cardChoice.setChoices(CardRepository.instance.getCreatureNames());
cardChoice.setMessage("Name a creature card");
break;
case ARTIFACT_NAME:
cardChoice.setChoices(CardRepository.instance.getArtifactNames());
cardChoice.setMessage("Name an artifact card");
break;
}
cardChoice.clearChoice();
while (!controller.choose(Outcome.Detriment, cardChoice, game)) {
@ -130,7 +135,7 @@ public class NameACardEffect extends OneShotEffect {
case ALL:
sb.append("card");
break;
case NON_ARTFIACT_AND_NON_LAND_NAME:
case NON_ARTIFACT_AND_NON_LAND_NAME:
sb.append("nonartifact, nonland card");
break;
case NON_LAND_AND_NON_CREATURE_NAME:
@ -139,6 +144,12 @@ public class NameACardEffect extends OneShotEffect {
case NON_LAND_NAME:
sb.append("nonland card");
break;
case CREATURE_NAME:
sb.append("creature card");
break;
case ARTIFACT_NAME:
sb.append("artifact card");
break;
}
return sb.toString();
}

View file

@ -188,12 +188,12 @@ public class PutTokenOntoBattlefieldCopyTargetEffect extends OneShotEffect {
EmptyToken token = new EmptyToken();
CardUtil.copyTo(token).from(copyFrom); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer)
applier.apply(game, token);
applier.apply(game, token, source, targetId);
if (becomesArtifact) {
token.getCardType().add(CardType.ARTIFACT);
token.addCardType(CardType.ARTIFACT);
}
if (additionalCardType != null && !token.getCardType().contains(additionalCardType)) {
token.getCardType().add(additionalCardType);
token.addCardType(additionalCardType);
}
if (gainsHaste) {
token.addAbility(HasteAbility.getInstance());

View file

@ -0,0 +1,12 @@
package mage.abilities.effects.common;
import mage.abilities.costs.common.DiscardCardCost;
/**
* @author stravant
*/
public class RummageEffect extends DoIfCostPaid {
public RummageEffect() {
super(new DrawCardSourceControllerEffect(1), new DiscardCardCost());
}
}

View file

@ -0,0 +1,68 @@
/*
* 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.game.Game;
import mage.game.events.GameEvent;
/**
*
* @author LevelX2
*/
public class SendOptionUsedEventEffect extends OneShotEffect {
private final int value;
public SendOptionUsedEventEffect() {
this(0);
}
public SendOptionUsedEventEffect(int value) {
super(Outcome.Detriment);
this.value = value;
}
public SendOptionUsedEventEffect(final SendOptionUsedEventEffect effect) {
super(effect);
this.value = effect.value;
}
@Override
public SendOptionUsedEventEffect copy() {
return new SendOptionUsedEventEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId(), value));
return true;
}
}

View file

@ -44,32 +44,42 @@ import mage.players.Player;
public class UntapAllControllerEffect extends OneShotEffect {
private final FilterPermanent filter;
private final boolean includeSource;
public UntapAllControllerEffect(FilterPermanent filter) {
this(filter, null);
}
public UntapAllControllerEffect(FilterPermanent filter, String rule) {
this(filter, rule, true);
}
public UntapAllControllerEffect(FilterPermanent filter, String rule, boolean includeSource) {
super(Outcome.Untap);
if (rule == null || rule.isEmpty()) {
staticText = "untap all " + filter.getMessage() + " you control";
staticText = "untap all " + (includeSource ? "" : "other ") + filter.getMessage() + " you control";
} else {
staticText = rule;
}
this.filter = filter;
this.includeSource = includeSource;
}
public UntapAllControllerEffect(final UntapAllControllerEffect effect) {
super(effect);
this.filter = effect.filter;
this.includeSource = effect.includeSource;
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (player != null) {
for (Permanent permanent: game.getBattlefield().getAllActivePermanents(filter, player.getId(), game)) {
permanent.untap(game);
if (includeSource || sourcePermanent == null || !sourcePermanent.getId().equals(permanent.getId())) {
permanent.untap(game);
}
}
return true;
}

View file

@ -28,10 +28,9 @@
package mage.abilities.effects.common;
import java.util.Set;
import java.util.UUID;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -63,11 +62,11 @@ public class UntapAllThatAttackedEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Watcher watcher = game.getState().getWatchers().get("AttackedThisTurn");
Watcher watcher = game.getState().getWatchers().get(AttackedThisTurnWatcher.class.getName());
if (watcher != null && watcher instanceof AttackedThisTurnWatcher) {
Set<UUID> attackedThisTurn = ((AttackedThisTurnWatcher) watcher).getAttackedThisTurnCreatures();
for (UUID uuid : attackedThisTurn) {
Permanent permanent = game.getPermanent(uuid);
Set<MageObjectReference> attackedThisTurn = ((AttackedThisTurnWatcher) watcher).getAttackedThisTurnCreatures();
for (MageObjectReference mor : attackedThisTurn) {
Permanent permanent = mor.getPermanent(game);
if (permanent != null && permanent.isCreature()) {
permanent.untap(game);
}

View file

@ -75,7 +75,7 @@ public class UntapTargetEffect extends OneShotEffect {
Target target = mode.getTargets().get(0);
StringBuilder sb = new StringBuilder();
sb.append("Untap ");
sb.append("untap ");
if (target.getNumberOfTargets() == 0) {
sb.append("up to ");
}

View file

@ -5,6 +5,7 @@
*/
package mage.abilities.effects.common.combat;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.RequirementEffect;
import mage.constants.Duration;
@ -59,8 +60,8 @@ public class AttacksIfAbleAllEffect extends RequirementEffect {
if (eachCombat) {
return true;
}
AttackedThisTurnWatcher watcher = (AttackedThisTurnWatcher) game.getState().getWatchers().get("AttackedThisTurn");
return watcher != null && !watcher.getAttackedThisTurnCreatures().contains(permanent.getId());
AttackedThisTurnWatcher watcher = (AttackedThisTurnWatcher) game.getState().getWatchers().get(AttackedThisTurnWatcher.class.getName());
return watcher != null && !watcher.getAttackedThisTurnCreatures().contains(new MageObjectReference(permanent, game));
}
return false;
}

View file

@ -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.combat;
import mage.constants.Duration;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.RequirementEffect;
import mage.constants.Duration;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.watchers.common.AttackedThisTurnWatcher;
@ -42,18 +42,18 @@ import mage.watchers.common.AttackedThisTurnWatcher;
public class AttacksIfAbleSourceEffect extends RequirementEffect {
boolean eachCombat;
public AttacksIfAbleSourceEffect(Duration duration) {
this(duration, false);
this(duration, false);
}
public AttacksIfAbleSourceEffect(Duration duration, boolean eachCombat) {
super(duration);
this.eachCombat = eachCombat;
if (this.duration == Duration.EndOfTurn) {
staticText = "{this} attacks " + (eachCombat ? "each combat" :"this turn") + " if able";
staticText = "{this} attacks " + (eachCombat ? "each combat" : "this turn") + " if able";
} else {
staticText = "{this} attacks each " + (eachCombat ? "combat" :"turn") + " if able";
staticText = "{this} attacks each " + (eachCombat ? "combat" : "turn") + " if able";
}
}
@ -73,8 +73,8 @@ public class AttacksIfAbleSourceEffect extends RequirementEffect {
if (eachCombat) {
return true;
}
AttackedThisTurnWatcher watcher = (AttackedThisTurnWatcher)game.getState().getWatchers().get("AttackedThisTurn");
return watcher != null && !watcher.getAttackedThisTurnCreatures().contains(permanent.getId());
AttackedThisTurnWatcher watcher = (AttackedThisTurnWatcher) game.getState().getWatchers().get(AttackedThisTurnWatcher.class.getName());
return watcher != null && !watcher.getAttackedThisTurnCreatures().contains(new MageObjectReference(permanent, game));
}
return false;
}

View file

@ -60,7 +60,7 @@ public class AddCardTypeAttachedEffect extends ContinuousEffectImpl {
if (equipment != null && equipment.getAttachedTo() != null) {
Permanent target = game.getPermanent(equipment.getAttachedTo());
if (target != null && !target.getCardType().contains(addedCardType))
target.getCardType().add(addedCardType);
target.addCardType(addedCardType);
}
return true;
}

View file

@ -65,9 +65,7 @@ public class AddCardTypeSourceEffect extends ContinuousEffectImpl {
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null && affectedObjectList.contains(new MageObjectReference(permanent, game))) {
if (!permanent.getCardType().contains(addedCardType)) {
permanent.getCardType().add(addedCardType);
}
permanent.addCardType(addedCardType);
return true;
} else if (this.getDuration() == Duration.Custom) {
this.discard();

View file

@ -69,7 +69,7 @@ public class AddCardTypeTargetEffect extends ContinuousEffectImpl {
Permanent target = game.getPermanent(targetId);
if (target != null) {
if (!target.getCardType().contains(addedCardType)) {
target.getCardType().add(addedCardType);
target.addCardType(addedCardType);
}
result = true;
}

View file

@ -145,7 +145,7 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl {
case TypeChangingEffects_4:
// Attention: Cards like Unstable Frontier that use this class do not give the "Basic" supertype to the target
if (!land.isLand()) {
land.getCardType().add(CardType.LAND);
land.addCardType(CardType.LAND);
}
if (loseOther) {
// 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects

View file

@ -0,0 +1,93 @@
/*
* 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.continuous;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
* @author JRHerlehy
* Created on 4/8/17.
*/
public class BecomesBlackZombieAdditionEffect extends ContinuousEffectImpl {
public BecomesBlackZombieAdditionEffect() {
super(Duration.Custom, Outcome.Neutral);
staticText = "That creature is a black Zombie in addition to its other colors and types";
}
public BecomesBlackZombieAdditionEffect(final BecomesBlackZombieAdditionEffect effect) {
super(effect);
}
@Override
public boolean hasLayer(Layer layer) {
return layer == Layer.ColorChangingEffects_5 || layer == Layer.TypeChangingEffects_4;
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Permanent creature = game.getPermanent(targetPointer.getFirst(game, source));
if (creature != null) {
switch (layer) {
case TypeChangingEffects_4:
if (sublayer == SubLayer.NA) {
if (!creature.getSubtype(game).contains("Zombie")) {
creature.getSubtype(game).add("Zombie");
}
}
break;
case ColorChangingEffects_5:
if (sublayer == SubLayer.NA) {
creature.getColor(game).setBlack(true);
}
break;
}
return true;
} else {
this.used = true;
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public BecomesBlackZombieAdditionEffect copy() {
return new BecomesBlackZombieAdditionEffect(this);
}
}

View file

@ -79,7 +79,7 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl {
if (!token.getCardType().isEmpty()) {
for (CardType t : token.getCardType()) {
if (!permanent.getCardType().contains(t)) {
permanent.getCardType().add(t);
permanent.addCardType(t);
}
}
}

View file

@ -30,11 +30,7 @@ package mage.abilities.effects.common.continuous;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.Token;
@ -85,10 +81,9 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
switch (layer) {
case TypeChangingEffects_4:
if (sublayer == SubLayer.NA) {
for (String t : token.getSupertype()) {
if (!permanent.getSupertype().contains(t)) {
permanent.getSupertype().add(t);
}
for (SuperType t : token.getSuperType()) {
permanent.addSuperType(t);
}
// card type
switch (loseType) {
@ -98,7 +93,7 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
break;
}
for (CardType t : token.getCardType()) {
permanent.getCardType().add(t);
permanent.addCardType(t);
}
// sub type
@ -167,9 +162,9 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
@Override
public boolean hasLayer(Layer layer) {
return layer == Layer.PTChangingEffects_7
|| layer == Layer.AbilityAddingRemovingEffects_6
|| layer == Layer.ColorChangingEffects_5
return layer == Layer.PTChangingEffects_7
|| layer == Layer.AbilityAddingRemovingEffects_6
|| layer == Layer.ColorChangingEffects_5
|| layer == Layer.TypeChangingEffects_4;
}

View file

@ -31,11 +31,7 @@ import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.Token;
@ -101,10 +97,9 @@ public class BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect extends Co
switch (layer) {
case TypeChangingEffects_4:
if (sublayer == SubLayer.NA) {
for (String superType : token.getSupertype()) {
if (!permanentAttachedTo.getSupertype().contains(superType)) {
permanentAttachedTo.getSupertype().add(superType);
}
for (SuperType superType : token.getSuperType()) {
permanentAttachedTo.addSuperType(superType);
}
// card type
switch (loseType) {
@ -114,7 +109,7 @@ public class BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect extends Co
break;
}
for (CardType cardType : token.getCardType()) {
permanentAttachedTo.getCardType().add(cardType);
permanentAttachedTo.addCardType(cardType);
}
// sub type

View file

@ -0,0 +1,42 @@
package mage.abilities.effects.common.continuous;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.*;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
* Created by IGOUDT on 5-4-2017.
*/
public class BecomesCreatureIfVehicleEffect extends ContinuousEffectImpl {
private CardType addedType = CardType.CREATURE;
public BecomesCreatureIfVehicleEffect() {
super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit);
this.staticText = "As long as enchanted permanent is a Vehicle, it's a creature in addition to its other types";
}
public BecomesCreatureIfVehicleEffect(final BecomesCreatureIfVehicleEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent aura = game.getPermanent(source.getSourceId());
if (aura != null && aura.getAttachedTo() != null) {
Permanent enchanted = game.getPermanent(aura.getAttachedTo());
if (enchanted != null && enchanted.getSubtype(game).contains("Vehicle")) {
enchanted.addCardType(addedType);
}
}
return true;
}
@Override
public BecomesCreatureIfVehicleEffect copy() {
return new BecomesCreatureIfVehicleEffect(this);
}
}

View file

@ -42,7 +42,6 @@ import mage.game.permanent.Permanent;
import mage.game.permanent.token.Token;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements SourceEffect {
@ -113,12 +112,8 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements
if (losePreviousTypes) {
permanent.getCardType().clear();
}
if (!token.getCardType().isEmpty()) {
for (CardType t : token.getCardType()) {
if (!permanent.getCardType().contains(t)) {
permanent.getCardType().add(t);
}
}
for (CardType t : token.getCardType()) {
permanent.addCardType(t);
}
if (type != null && type.isEmpty() || type == null && permanent.isLand()) {
permanent.getSubtype(game).retainAll(CardRepository.instance.getLandTypes());
@ -137,11 +132,10 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements
break;
case AbilityAddingRemovingEffects_6:
if (sublayer == SubLayer.NA) {
if (!token.getAbilities().isEmpty()) {
for (Ability ability : token.getAbilities()) {
permanent.addAbility(ability, source.getSourceId(), game);
}
for (Ability ability : token.getAbilities()) {
permanent.addAbility(ability, source.getSourceId(), game);
}
}
break;
case PTChangingEffects_7:

View file

@ -103,7 +103,7 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl {
if (!token.getCardType().isEmpty()) {
for (CardType t : token.getCardType()) {
if (!permanent.getCardType().contains(t)) {
permanent.getCardType().add(t);
permanent.addCardType(t);
}
}
}

View file

@ -107,9 +107,9 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple
switch (layer) {
case TypeChangingEffects_4:
permanent.setName("");
permanent.getSupertype().clear();
permanent.getSuperType().clear();
permanent.getCardType().clear();
permanent.getCardType().add(CardType.CREATURE);
permanent.addCardType(CardType.CREATURE);
permanent.getSubtype(game).clear();
permanent.getManaCost().clear();
break;

View file

@ -163,9 +163,9 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen
switch (layer) {
case TypeChangingEffects_4:
permanent.setName("");
permanent.getSupertype().clear();
permanent.getSuperType().clear();
permanent.getCardType().clear();
permanent.getCardType().add(CardType.CREATURE);
permanent.addCardType(CardType.CREATURE);
permanent.getSubtype(game).clear();
break;
case ColorChangingEffects_5:

View file

@ -1,40 +1,102 @@
/*
* 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.
* 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.continuous;
import java.util.HashMap;
import java.util.Iterator;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.CompoundAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
* @author BetaSteward_at_googlemail.com
*/
public class LoseAbilityAllEffect extends ContinuousEffectImpl {
protected final FilterPermanent filter;
protected final Ability ability;
protected CompoundAbility ability;
protected boolean excludeSource;
protected FilterPermanent filter;
public LoseAbilityAllEffect(FilterPermanent filter, Ability ability, Duration duration) {
public LoseAbilityAllEffect(Ability ability, Duration duration) {
this(ability, duration, new FilterPermanent("permanents"));
}
public LoseAbilityAllEffect(CompoundAbility ability, Duration duration) {
this(ability, duration, new FilterPermanent("permanents"));
}
public LoseAbilityAllEffect(Ability ability, Duration duration, FilterPermanent filter) {
this(ability, duration, filter, false);
}
public LoseAbilityAllEffect(CompoundAbility ability, Duration duration, FilterPermanent filter) {
this(ability, duration, filter, false);
}
public LoseAbilityAllEffect(Ability ability, Duration duration, FilterPermanent filter, boolean excludeSource) {
this(new CompoundAbility(ability), duration, filter, excludeSource);
}
public LoseAbilityAllEffect(CompoundAbility ability, Duration duration, FilterPermanent filter, boolean excludeSource) {
super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
this.filter = filter;
this.ability = ability;
staticText = filter.getMessage() + " lose " + ability.toString() + (duration.toString().isEmpty() ? "" : ' ' + duration.toString());
this.filter = filter;
this.excludeSource = excludeSource;
}
public LoseAbilityAllEffect(final LoseAbilityAllEffect effect) {
super(effect);
this.ability = effect.ability.copy();
this.filter = effect.filter.copy();
this.ability = effect.ability;
this.excludeSource = effect.excludeSource;
}
@Override
public void init(Ability source, Game game) {
super.init(source, game);
if (this.affectedObjectsSet) {
for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
affectedObjectList.add(new MageObjectReference(perm, game));
}
}
}
}
@Override
@ -44,10 +106,40 @@ public class LoseAbilityAllEffect extends ContinuousEffectImpl {
@Override
public boolean apply(Game game, Ability source) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
if (permanent != null) {
while (permanent.getAbilities().contains(ability)) {
permanent.getAbilities().remove(ability);
if (this.affectedObjectsSet) {
for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext();) { // filter may not be used again, because object can have changed filter relevant attributes but still geets boost
Permanent perm = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets (e.g. Showstopper)
if (perm != null) {
for (Ability ability : ability) {
perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId()));
}
} else {
it.remove();
if (affectedObjectList.isEmpty()) {
discard();
}
}
}
} else {
for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
for (Ability ability : ability) {
perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId()));
}
}
}
// still as long as the prev. permanent is known to the LKI (e.g. Mikaeus, the Unhallowed) so gained dies triggered ability will trigger
HashMap<UUID, MageObject> LKIBattlefield = game.getLKI().get(Zone.BATTLEFIELD);
if (LKIBattlefield != null) {
for (MageObject mageObject : LKIBattlefield.values()) {
Permanent perm = (Permanent) mageObject;
if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
if (filter.match(perm, source.getSourceId(), source.getControllerId(), game)) {
for (Ability ability : ability) {
perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId()));
}
}
}
}
}
}

View file

@ -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.continuous;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class SetPowerToughnessEnchantedEffect extends ContinuousEffectImpl {
public SetPowerToughnessEnchantedEffect() {
super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.BoostCreature);
staticText = "Enchanted creature has base power and toughness 0/2";
}
public SetPowerToughnessEnchantedEffect(final SetPowerToughnessEnchantedEffect effect) {
super(effect);
}
@Override
public SetPowerToughnessEnchantedEffect copy() {
return new SetPowerToughnessEnchantedEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent enchantment = game.getPermanent(source.getSourceId());
if (enchantment != null && enchantment.getAttachedTo() != null) {
Permanent enchanted = game.getPermanent(enchantment.getAttachedTo());
if (enchanted != null) {
enchanted.getPower().setValue(0);
enchanted.getToughness().setValue(2);
}
return true;
}
return false;
}
}

View file

@ -29,10 +29,10 @@
package mage.abilities.effects.common.cost;
import mage.abilities.Ability;
import mage.constants.ComparisonType;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.filter.Filter;
import mage.filter.common.FilterNonlandCard;
import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
import mage.game.Game;
@ -58,7 +58,7 @@ public class CastWithoutPayingManaCostEffect extends OneShotEffect {
public CastWithoutPayingManaCostEffect(int maxCost) {
super(Outcome.PlayForFree);
filter = new FilterNonlandCard("card with converted mana cost " + maxCost + " or less from your hand");
filter.add(new ConvertedManaCostPredicate(Filter.ComparisonType.LessThan, maxCost + 1));
filter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, maxCost + 1));
this.manaCost = maxCost;
this.staticText = "you may cast a card with converted mana cost " + maxCost + " or less from your hand without paying its mana cost";
}

View file

@ -0,0 +1,86 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.effects.common.cost;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.constants.CostModificationType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
/**
*
* @author Styxo
*/
public class SourceCostReductionForEachCardInGraveyardEffect extends CostModificationEffectImpl {
private FilterCard filter;
public SourceCostReductionForEachCardInGraveyardEffect() {
this(new FilterCard());
}
public SourceCostReductionForEachCardInGraveyardEffect(FilterCard filter) {
super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST);
this.filter = filter;
staticText = "{this} costs {1} less to cast for each " + filter.getMessage() + " in your graveyard";
}
SourceCostReductionForEachCardInGraveyardEffect(SourceCostReductionForEachCardInGraveyardEffect effect) {
super(effect);
this.filter = effect.filter.copy();
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
int reductionAmount = player.getGraveyard().count(filter, game);
CardUtil.reduceCost(abilityToModify, reductionAmount);
return true;
}
return false;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if ((abilityToModify instanceof SpellAbility) && abilityToModify.getSourceId().equals(source.getSourceId())) {
return game.getCard(abilityToModify.getSourceId()) != null;
}
return false;
}
@Override
public SourceCostReductionForEachCardInGraveyardEffect copy() {
return new SourceCostReductionForEachCardInGraveyardEffect(this);
}
}

View file

@ -27,6 +27,8 @@
*/
package mage.abilities.effects.common.counter;
import java.util.ArrayList;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
@ -103,7 +105,8 @@ public class AddCountersSourceEffect extends OneShotEffect {
countersToAdd--;
}
newCounter.add(countersToAdd);
card.addCounters(newCounter, source, game);
ArrayList<UUID> appliedEffects = (ArrayList<UUID>) this.getValue("appliedEffects");
card.addCounters(newCounter, source, game, appliedEffects);
if (informPlayers && !game.isSimulation()) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
@ -128,7 +131,8 @@ public class AddCountersSourceEffect extends OneShotEffect {
}
newCounter.add(countersToAdd);
int before = permanent.getCounters(game).getCount(newCounter.getName());
permanent.addCounters(newCounter, source, game);
ArrayList<UUID> appliedEffects = (ArrayList<UUID>) this.getValue("appliedEffects");
permanent.addCounters(newCounter, source, game, appliedEffects); // if used from a replacement effect, the basic event determines if an effect was already applied to an event
if (informPlayers && !game.isSimulation()) {
int amountAdded = permanent.getCounters(game).getCount(newCounter.getName()) - before;
Player player = game.getPlayer(source.getControllerId());
@ -150,12 +154,10 @@ public class AddCountersSourceEffect extends OneShotEffect {
sb.append("put ");
if (counter.getCount() > 1) {
sb.append(CardUtil.numberToText(counter.getCount())).append(' ');
} else if (amount.toString().equals("X") && amount.getMessage().isEmpty()) {
sb.append("X ");
} else {
if (amount.toString().equals("X") && amount.getMessage().isEmpty()) {
sb.append("X ");
} else {
sb.append("a ");
}
sb.append("a ");
}
sb.append(counter.getName().toLowerCase()).append(" counter on {this}");
if (!amount.getMessage().isEmpty()) {

View file

@ -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.replacement;
import mage.abilities.Ability;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
*
* @author LevelX2
*/
public class CreateTwiceThatManyTokensEffect extends ReplacementEffectImpl {
public CreateTwiceThatManyTokensEffect() {
super(Duration.WhileOnBattlefield, Outcome.Copy);
staticText = "If an effect would create one or more tokens under your control, it creates twice that many of those tokens instead";
}
public CreateTwiceThatManyTokensEffect(final CreateTwiceThatManyTokensEffect effect) {
super(effect);
}
@Override
public CreateTwiceThatManyTokensEffect copy() {
return new CreateTwiceThatManyTokensEffect(this);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CREATE_TOKEN;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return event.getPlayerId().equals(source.getControllerId());
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
event.setAmount(event.getAmount() * 2);
return false;
}
}

View file

@ -1,113 +1,114 @@
/*
* 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 mage.MageObject;
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.TargetCard;
import mage.target.common.TargetCardInLibrary;
/**
*
* @author Styxo
*/
public class SearchLibraryGraveyardPutInHandEffect extends OneShotEffect {
private FilterCard filter;
private boolean forceToSearchBoth;
public SearchLibraryGraveyardPutInHandEffect(FilterCard filter) {
this(filter, false);
}
public SearchLibraryGraveyardPutInHandEffect(FilterCard filter, boolean forceToSearchBoth) {
super(Outcome.Benefit);
this.filter = filter;
this.forceToSearchBoth = forceToSearchBoth;
staticText = "Search your library and" + (forceToSearchBoth ? "" : "/or ") + " graveyard for a card named " + filter.getMessage() + ", reveal it, and put it into your hand. Then shuffle your library";
}
public SearchLibraryGraveyardPutInHandEffect(final SearchLibraryGraveyardPutInHandEffect effect) {
super(effect);
this.filter = effect.filter;
this.forceToSearchBoth = effect.forceToSearchBoth;
}
@Override
public SearchLibraryGraveyardPutInHandEffect copy() {
return new SearchLibraryGraveyardPutInHandEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
Card cardFound = null;
if (controller != null && sourceObject != null) {
if (forceToSearchBoth || controller.chooseUse(outcome, "Search your library for a card named " + filter.getMessage() + '?', source, game)) {
TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter);
target.clearChosen();
if (controller.searchLibrary(target, game)) {
if (!target.getTargets().isEmpty()) {
cardFound = game.getCard(target.getFirstTarget());
}
}
controller.shuffleLibrary(source, game);
}
if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a card named " + filter.getMessage() + '?', source, game)) {
TargetCard target = new TargetCard(0, 1, Zone.GRAVEYARD, filter);
target.clearChosen();
if (controller.choose(outcome, controller.getGraveyard(), target, game)) {
if (!target.getTargets().isEmpty()) {
cardFound = game.getCard(target.getFirstTarget());
}
}
}
if (cardFound != null) {
controller.revealCards(sourceObject.getIdName(), new CardsImpl(cardFound), game);
controller.moveCards(cardFound, Zone.HAND, source, game);
}
return true;
}
return false;
}
}
/*
* 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 mage.MageObject;
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.TargetCard;
import mage.target.common.TargetCardInLibrary;
/**
*
* @author Styxo
*/
public class SearchLibraryGraveyardPutInHandEffect extends OneShotEffect {
private FilterCard filter;
private boolean forceToSearchBoth;
public SearchLibraryGraveyardPutInHandEffect(FilterCard filter) {
this(filter, false);
}
public SearchLibraryGraveyardPutInHandEffect(FilterCard filter, boolean forceToSearchBoth) {
super(Outcome.Benefit);
this.filter = filter;
this.forceToSearchBoth = forceToSearchBoth;
staticText = "Search your library and" + (forceToSearchBoth ? "" : "/or ") + " graveyard for a card named " + filter.getMessage() +
", reveal it, and put it into your hand. " + (forceToSearchBoth ? "Then shuffle your library" : "If you search your library this way, shuffle it");
}
public SearchLibraryGraveyardPutInHandEffect(final SearchLibraryGraveyardPutInHandEffect effect) {
super(effect);
this.filter = effect.filter;
this.forceToSearchBoth = effect.forceToSearchBoth;
}
@Override
public SearchLibraryGraveyardPutInHandEffect copy() {
return new SearchLibraryGraveyardPutInHandEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
Card cardFound = null;
if (controller != null && sourceObject != null) {
if (forceToSearchBoth || controller.chooseUse(outcome, "Search your library for a card named " + filter.getMessage() + '?', source, game)) {
TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter);
target.clearChosen();
if (controller.searchLibrary(target, game)) {
if (!target.getTargets().isEmpty()) {
cardFound = game.getCard(target.getFirstTarget());
}
}
controller.shuffleLibrary(source, game);
}
if (cardFound == null && controller.chooseUse(outcome, "Search your graveyard for a card named " + filter.getMessage() + '?', source, game)) {
TargetCard target = new TargetCard(0, 1, Zone.GRAVEYARD, filter);
target.clearChosen();
if (controller.choose(outcome, controller.getGraveyard(), target, game)) {
if (!target.getTargets().isEmpty()) {
cardFound = game.getCard(target.getFirstTarget());
}
}
}
if (cardFound != null) {
controller.revealCards(sourceObject.getIdName(), new CardsImpl(cardFound), game);
controller.moveCards(cardFound, Zone.HAND, source, game);
}
return true;
}
return false;
}
}

View file

@ -28,11 +28,11 @@
package mage.abilities.effects.common.search;
import mage.abilities.Ability;
import mage.constants.ComparisonType;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.Filter;
import mage.filter.FilterCard;
import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
import mage.game.Game;
@ -67,7 +67,7 @@ public class SearchLibraryWithLessCMCPutInPlayEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
FilterCard advancedFilter = filter.copy(); // never change static objects so copy the object here before
advancedFilter.add(new ConvertedManaCostPredicate(Filter.ComparisonType.LessThan, source.getManaCostsToPay().getX() + 1));
advancedFilter.add(new ConvertedManaCostPredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1));
TargetCardInLibrary target = new TargetCardInLibrary(advancedFilter);
if (controller.searchLibrary(target, game)) {
if (!target.getTargets().isEmpty()) {

View file

@ -28,6 +28,7 @@
package mage.abilities.effects.keyword;
import mage.abilities.Ability;
import mage.constants.ComparisonType;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.Effect;
@ -35,7 +36,6 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.constants.Outcome;
import mage.counters.CounterType;
import mage.filter.Filter;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.common.FilterCreaturePermanent;
@ -96,7 +96,7 @@ public class BolsterEffect extends OneShotEffect {
if (leastToughness != Integer.MAX_VALUE) {
if (selectedCreature == null) {
FilterPermanent filter = new FilterControlledCreaturePermanent("creature you control with toughness " + leastToughness);
filter.add(new ToughnessPredicate(Filter.ComparisonType.Equal, leastToughness));
filter.add(new ToughnessPredicate(ComparisonType.EQUAL_TO, leastToughness));
Target target = new TargetPermanent(1,1, filter, true);
if (controller.chooseTarget(outcome, target, source, game)) {
selectedCreature = game.getPermanent(target.getFirstTarget());

View file

@ -0,0 +1,227 @@
/*
* 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.abilities.keyword;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.*;
import mage.cards.Card;
import mage.cards.SplitCardHalf;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.stack.Spell;
import mage.players.Player;
/**
* Aftermath
*
* TODO: Implement once we get details on the comprehensive rules meaning of the
* ability
*
* Current text is a shell copied from Flashback
*
* @author stravant
*/
public class AftermathAbility extends SimpleStaticAbility {
public AftermathAbility() {
super(Zone.ALL, new AftermathCastFromGraveyard());
addEffect(new AftermathCantCastFromHand());
addEffect(new AftermathExileAsResolvesFromGraveyard());
}
public AftermathAbility(final AftermathAbility ability) {
super(ability);
}
@Override
public AftermathAbility copy() {
return new AftermathAbility(this);
}
@Override
public String getRule(boolean all) {
if (all) {
return "Aftermath <i>(Cast this card only from your graveyard. Exile it afterwards.)</i>";
} else {
return "Aftermath";
}
}
}
class AftermathCastFromGraveyard extends AsThoughEffectImpl {
public AftermathCastFromGraveyard() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit);
staticText = "Cast {this} from your graveyard";
}
public AftermathCastFromGraveyard(final AftermathCastFromGraveyard effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public AftermathCastFromGraveyard copy() {
return new AftermathCastFromGraveyard(this);
}
private static String msb(UUID id) {
return Integer.toUnsignedString((int) id.getMostSignificantBits(), 16);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (objectId.equals(source.getSourceId())
& affectedControllerId.equals(source.getControllerId())) {
Card card = game.getCard(source.getSourceId());
if (card != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) {
return true;
}
}
return false;
}
}
class AftermathCantCastFromHand extends ContinuousRuleModifyingEffectImpl {
public AftermathCantCastFromHand() {
super(Duration.EndOfGame, Outcome.Detriment);
staticText = ", but not from anywhere else";
}
public AftermathCantCastFromHand(final AftermathCantCastFromHand effect) {
super(effect);
}
@Override
public AftermathCantCastFromHand copy() {
return new AftermathCantCastFromHand(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CAST_SPELL;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Card card = game.getCard(event.getSourceId());
if (card != null && card.getId().equals(source.getSourceId())) {
Zone zone = game.getState().getZone(card.getId());
if (zone != null && (zone != Zone.GRAVEYARD)) {
return true;
}
}
return false;
}
}
class AftermathExileAsResolvesFromGraveyard extends ReplacementEffectImpl {
AftermathExileAsResolvesFromGraveyard() {
super(Duration.WhileOnStack, Outcome.Detriment);
this.staticText = "Exile it afterwards.";
}
AftermathExileAsResolvesFromGraveyard(AftermathExileAsResolvesFromGraveyard effect) {
super(effect);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
}
@Override
public boolean applies(GameEvent evt, Ability source, Game game) {
ZoneChangeEvent event = (ZoneChangeEvent) evt;
if (event.getFromZone() == Zone.STACK && event.getToZone() != Zone.EXILED) {
// Moving something from stack to somewhere else
// Get the source id, getting the whole split card's ID, because
// that's the card that is changing zones in the event, but
// source.getSourceId is only the split card half.
// If branch so that we also support putting Aftermath on
// non-split cards for... whatever reason, in case somebody
// wants to do that in the future.
UUID sourceId = source.getSourceId();
Card sourceCard = game.getCard(source.getSourceId());
if (sourceCard != null && sourceCard instanceof SplitCardHalf) {
sourceCard = ((SplitCardHalf) sourceCard).getParentCard();
sourceId = sourceCard.getId();
}
if (event.getTargetId() == sourceId) {
// Moving this spell from stack to yard
Spell spell = game.getStack().getSpell(source.getSourceId());
if (spell != null && spell.getFromZone() == Zone.GRAVEYARD) {
// And this spell was cast from the graveyard, so we need to exile it
return true;
}
}
}
return false;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
UUID sourceId = source.getSourceId();
Card sourceCard = game.getCard(source.getSourceId());
if (sourceCard != null && sourceCard instanceof SplitCardHalf) {
sourceCard = ((SplitCardHalf) sourceCard).getParentCard();
sourceId = sourceCard.getId();
}
if (sourceCard != null) {
Player player = game.getPlayer(sourceCard.getOwnerId());
if (player != null) {
return player.moveCardToExileWithInfo(sourceCard, null, "", sourceId, game, ((ZoneChangeEvent) event).getFromZone(), true);
}
}
return false;
}
@Override
public AftermathExileAsResolvesFromGraveyard copy() {
return new AftermathExileAsResolvesFromGraveyard(this);
}
}

View file

@ -151,12 +151,12 @@ public class BestowAbility extends SpellAbility {
if (basicObject != null) {
basicObject.getSubtype(null).remove("Aura");
if (!basicObject.isCreature()) {
basicObject.getCardType().add(CardType.CREATURE);
basicObject.addCardType(CardType.CREATURE);
}
}
permanent.getSubtype(null).remove("Aura");
if (!permanent.isCreature()) {
permanent.getCardType().add(CardType.CREATURE);
permanent.addCardType(CardType.CREATURE);
}
}

View file

@ -1,5 +1,7 @@
package mage.abilities.keyword;
import java.util.ArrayList;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.effects.OneShotEffect;
@ -71,7 +73,8 @@ class BloodthirstEffect extends OneShotEffect {
if (watcher != null && watcher.conditionMet()) {
Permanent permanent = game.getPermanentEntering(source.getSourceId());
if (permanent != null) {
permanent.addCounters(CounterType.P1P1.createInstance(amount), source, game);
ArrayList<UUID> appliedEffects = (ArrayList<UUID>) this.getValue("appliedEffects"); // the basic event is the EntersBattlefieldEvent, so use already applied replacement effects from that event
permanent.addCounters(CounterType.P1P1.createInstance(amount), source, game, appliedEffects);
}
}
return true;

View file

@ -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,25 +20,29 @@
* 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 mage.constants.Zone;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.DiscardSourceCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.game.events.CostEvent;
import mage.game.events.GameEvent;
import mage.target.Targets;
import mage.target.common.TargetCardInLibrary;
/**
*
* @author BetaSteward_at_googlemail.com
@ -47,16 +51,16 @@ public class CyclingAbility extends ActivatedAbilityImpl {
private final Cost cost;
private final String text;
public CyclingAbility(Cost cost) {
super(Zone.HAND, new DrawCardSourceControllerEffect(1), cost);
super(Zone.HAND, new DrawCardSourceControllerEffect(1), new CyclingCost(cost));
this.addCost(new DiscardSourceCost());
this.cost = cost;
this.text = "Cycling";
}
public CyclingAbility(Cost cost, FilterCard filter, String text){
super(Zone.HAND, new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true), cost);
public CyclingAbility(Cost cost, FilterCard filter, String text) {
super(Zone.HAND, new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, true), new CyclingCost(cost));
this.addCost(new DiscardSourceCost());
this.cost = cost;
this.text = text;
@ -76,14 +80,87 @@ public class CyclingAbility extends ActivatedAbilityImpl {
@Override
public String getRule() {
StringBuilder rule = new StringBuilder(this.text);
if(cost instanceof ManaCost){
if (cost instanceof ManaCost) {
rule.append(' ');
}
else{
rule.append("&mdash;");
} else {
rule.append("&mdash;");
}
rule.append(cost.getText()).append(" <i>(").append(super.getRule(true)).append(")</i>");
return rule.toString();
}
}
class CyclingCost implements Cost {
protected Cost cost;
public CyclingCost(Cost cost) {
this.cost = cost;
}
public CyclingCost(final CyclingCost cost) {
this.cost = cost.cost.copy();
}
@Override
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) {
return pay(ability, game, sourceId, controllerId, noMana, cost);
}
@Override
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
CostEvent costEvent = new CostEvent(GameEvent.EventType.CAN_PAY_CYCLE_COST, sourceId, sourceId, controllerId, cost);
game.replaceEvent(costEvent);
return cost.canPay(ability, sourceId, controllerId, game) || costEvent.getCost().canPay(ability, sourceId, controllerId, game);
}
@Override
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
CostEvent costEvent = new CostEvent(GameEvent.EventType.PAY_CYCLE_COST, sourceId, sourceId, controllerId, cost);
game.replaceEvent(costEvent);
cost = costEvent.getCost();
return cost.pay(ability, game, sourceId, controllerId, noMana, cost);
}
@Override
public String getText() {
return cost.getText();
}
@Override
public void setText(String text) {
cost.setText(text);
}
@Override
public Targets getTargets() {
return cost.getTargets();
}
@Override
public boolean isPaid() {
return cost.isPaid();
}
@Override
public void clearPaid() {
cost.clearPaid();
}
@Override
public void setPaid() {
cost.setPaid();
}
@Override
public UUID getId() {
return cost.getId();
}
@Override
public Cost copy() {
return new CyclingCost(this);
}
}

View file

@ -0,0 +1,126 @@
/*
* 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 mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.ExileSourceFromGraveCost;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.token.EmptyToken;
import mage.players.Player;
import mage.util.CardUtil;
/**
*
* @author LevelX2
*/
public class EmbalmAbility extends ActivatedAbilityImpl {
private String rule;
public EmbalmAbility(Cost cost, Card card) {
super(Zone.GRAVEYARD, new EmbalmEffect(), cost);
addCost(new ExileSourceFromGraveCost());
this.rule = setRule(cost, card);
this.timing = TimingRule.SORCERY;
setRule(cost, card);
}
public EmbalmAbility(final EmbalmAbility ability) {
super(ability);
this.rule = ability.rule;
}
@Override
public EmbalmAbility copy() {
return new EmbalmAbility(this);
}
@Override
public String getRule() {
return rule;
}
private String setRule(Cost cost, Card card) {
StringBuilder sb = new StringBuilder("Embalm ").append(cost.getText());
sb.append(" <i>(").append(cost.getText());
sb.append(", Exile this card from your graveyard: Create a token that's a copy of it, except it's a white Zombie ");
for (String subtype : card.getSubtype(null)) {
sb.append(subtype).append(" ");
}
sb.append(" with no mana cost. Embalm only as a sorcery.)</i>");
return sb.toString();
}
}
class EmbalmEffect extends OneShotEffect {
public EmbalmEffect() {
super(Outcome.PutCreatureInPlay);
}
public EmbalmEffect(final EmbalmEffect effect) {
super(effect);
}
@Override
public EmbalmEffect copy() {
return new EmbalmEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Card card = game.getCard(source.getSourceId());
Player controller = game.getPlayer(source.getControllerId());
if (controller != null && card != null) {
EmptyToken token = new EmptyToken();
CardUtil.copyTo(token).from(card); // needed so that entersBattlefied triggered abilities see the attributes (e.g. Master Biomancer)
token.getColor(game).setColor(ObjectColor.WHITE);
if (!token.getSubtype(game).contains("Zombie")) {
token.getSubtype(game).add(0, "Zombie");
}
token.getManaCost().clear();
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.EMBALMED_CREATURE, token.getId(), source.getSourceId(), controller.getId()));
token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId(), false, false, null);
// Probably it makes sense to remove also the Embalm ability (it's not shown on the token cards).
// Also it can never get active or? But it's not mentioned in the reminder text.
return true;
}
return false;
}
}

View file

@ -0,0 +1,206 @@
/*
* 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.HashSet;
import java.util.Set;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.BecomesExertSourceTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.watchers.Watcher;
/**
*
* @author LevelX2
*/
public class ExertAbility extends SimpleStaticAbility {
private String ruleText;
public ExertAbility(BecomesExertSourceTriggeredAbility ability) {
this(ability, false);
}
public ExertAbility(BecomesExertSourceTriggeredAbility ability, boolean exertOnlyOncePerTurn) {
super(Zone.BATTLEFIELD, new ExertReplacementEffect(exertOnlyOncePerTurn));
ruleText = (exertOnlyOncePerTurn
? "If {this} hasn't been exerted this turn, you may exert it"
: "You may exert {this}") + " as it attacks. ";
if (ability != null) {
this.addSubAbility(ability);
ruleText += ("When you do,");
ability.getEffects().stream().forEach((effect) -> {
ruleText += " " + effect.getText(ability.getModes().getMode());
});
ruleText += ". ";
ability.setRuleVisible(false);
}
ruleText += "<i>(An exterted creature can't untap during your next untap step)</i>";
if (exertOnlyOncePerTurn) {
getWatchers().add(new ExertedThisTurnWatcher());
}
}
public ExertAbility(final ExertAbility ability) {
super(ability);
this.ruleText = ability.ruleText;
}
@Override
public ExertAbility copy() {
return new ExertAbility(this);
}
@Override
public String getRule() {
return ruleText;
}
}
class ExertReplacementEffect extends ReplacementEffectImpl {
final private boolean exertOnlyOncePerTurn;
public ExertReplacementEffect(boolean exertOnlyOncePerTurn) {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
staticText = "You may exert {this} as it attacks";
this.exertOnlyOncePerTurn = exertOnlyOncePerTurn;
}
public ExertReplacementEffect(ExertReplacementEffect effect) {
super(effect);
this.exertOnlyOncePerTurn = effect.exertOnlyOncePerTurn;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == EventType.ATTACKER_DECLARED;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return event.getSourceId().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.getSourceId());
Player controller = game.getPlayer(source.getControllerId());
if (creature != null && controller != null) {
if (exertOnlyOncePerTurn) {
MageObjectReference creatureReference = new MageObjectReference(creature.getId(), creature.getZoneChangeCounter(game), game);
ExertedThisTurnWatcher watcher = (ExertedThisTurnWatcher) game.getState().getWatchers().get(ExertedThisTurnWatcher.class.getName());
if (watcher != null && watcher.getExertedThisTurnCreatures().contains(creatureReference)) {
return false;
}
}
if (controller.chooseUse(outcome, "Exert " + creature.getLogName() + '?',
"An exterted creature can't untap during your next untap step.", "Yes", "No", source, game)) {
if (!game.isSimulation()) {
game.informPlayers(controller.getLogName() + " exerted " + creature.getName());
}
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BECOMES_EXERTED, creature.getId(), creature.getId(), creature.getControllerId()));
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect();
effect.setTargetPointer(new FixedTarget(creature, game));
game.addEffect(effect, source);
}
}
return false;
}
@Override
public String getText(Mode mode) {
return staticText;
}
@Override
public ExertReplacementEffect copy() {
return new ExertReplacementEffect(this);
}
}
class ExertedThisTurnWatcher extends Watcher {
private final Set<MageObjectReference> exertedThisTurnCreatures;
public ExertedThisTurnWatcher() {
super(ExertedThisTurnWatcher.class.getName(), WatcherScope.GAME);
exertedThisTurnCreatures = new HashSet<>();
}
public ExertedThisTurnWatcher(final ExertedThisTurnWatcher watcher) {
super(watcher);
exertedThisTurnCreatures = new HashSet<>(watcher.exertedThisTurnCreatures);
}
@Override
public Watcher copy() {
return new ExertedThisTurnWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.BECOMES_EXERTED) {
this.exertedThisTurnCreatures.add(new MageObjectReference(event.getSourceId(), game));
}
}
public Set<MageObjectReference> getExertedThisTurnCreatures() {
return this.exertedThisTurnCreatures;
}
@Override
public void reset() {
super.reset();
exertedThisTurnCreatures.clear();
}
}

Some files were not shown because too many files have changed in this diff Show more