Merge remote-tracking branch 'origin/master'

This commit is contained in:
Michael Simons 2018-06-07 22:41:40 -04:00
commit d5ad543583
278 changed files with 2275 additions and 706 deletions

View file

@ -460,7 +460,7 @@ public abstract class AbilityImpl implements Ability {
// controller specific alternate spell costs
if (!noMana && !alternativeCostisUsed) {
if (this.getAbilityType() == AbilityType.SPELL
// 117.9a Only one alternative cost can be applied to any one spell as its being cast.
// 117.9a Only one alternative cost can be applied to any one spell as it's being cast.
// So an alternate spell ability can't be paid with Omniscience
&& ((SpellAbility) this).getSpellAbilityType() != SpellAbilityType.BASE_ALTERNATE) {
for (AlternativeSourceCosts alternativeSourceCosts : controller.getAlternativeSourceCosts()) {

View file

@ -198,8 +198,8 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
// 700.2d
// Some spells and abilities specify that a player other than their controller chooses a mode for it.
// In that case, the other player does so when the spell or abilitys controller normally would do so.
// If there is more than one other player who could make such a choice, the spell or abilitys controller decides which of those players will make the choice.
// In that case, the other player does so when the spell or ability's controller normally would do so.
// If there is more than one other player who could make such a choice, the spell or ability's controller decides which of those players will make the choice.
UUID playerId = null;
if (modeChooser == TargetController.OPPONENT) {
TargetOpponent targetOpponent = new TargetOpponent();

View file

@ -141,7 +141,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
* fail to do anything. The ability could be unable to find the object
* because the object never entered the specified zone, because it left
* the zone before the ability resolved, or because it is in a zone that
* is hidden from a player, such as a library or an opponents hand.
* is hidden from a player, such as a library or an opponent's hand.
* (This rule applies even if the object leaves the zone and returns
* again before the ability resolves.) The most common zone-change
* triggers are enters-the-battlefield triggers and

View file

@ -77,11 +77,11 @@ public class BeginningOfCombatTriggeredAbility extends TriggeredAbilityImpl {
public String getRule() {
switch (targetController) {
case YOU:
return "At the beginning of combat on your turn, " + generateZoneString() + getEffects().getText(modes.getMode());
return "At the beginning of combat on your turn, " + generateZoneString() + super.getRule();
case OPPONENT:
return "At the beginning of each opponent's combat step, " + generateZoneString() + getEffects().getText(modes.getMode());
return "At the beginning of each opponent's combat step, " + generateZoneString() + super.getRule();
case ANY:
return "At the beginning of each combat, " + generateZoneString() + getEffects().getText(modes.getMode());
return "At the beginning of each combat, " + generateZoneString() + super.getRule();
}
return "";
}

View file

@ -1,4 +1,3 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
@ -6,6 +5,7 @@ import mage.abilities.effects.Effect;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterSpell;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
@ -17,13 +17,12 @@ import mage.target.targetpointer.FixedTarget;
*/
public class SpellCastAllTriggeredAbility extends TriggeredAbilityImpl {
private static final FilterSpell spellCard = new FilterSpell("a spell");
protected FilterSpell filter;
protected String rule;
protected SetTargetPointer setTargetPointer;
public SpellCastAllTriggeredAbility(Effect effect, boolean optional) {
this(Zone.BATTLEFIELD, effect, spellCard, optional, SetTargetPointer.NONE);
this(Zone.BATTLEFIELD, effect, StaticFilters.FILTER_SPELL_A, optional, SetTargetPointer.NONE);
}
public SpellCastAllTriggeredAbility(Effect effect, FilterSpell filter, boolean optional) {

View file

@ -1,4 +1,3 @@
package mage.abilities.costs.common;
import java.util.ArrayList;

View file

@ -41,7 +41,7 @@ public class PayLoyaltyCost extends CostImpl {
* Gatherer Ruling: 10/1/2005: Planeswalkers will enter the battlefield with
* double the normal amount of loyalty counters. However, if you activate an
* ability whose cost has you put loyalty counters on a planeswalker, the
* number you put on isnt doubled. This is because those counters are put
* number you put on isn't doubled. This is because those counters are put
* on as a cost, not as an effect.
*
*/

View file

@ -1,6 +1,6 @@
package mage.abilities.decorator;
import java.util.List;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
@ -47,7 +47,11 @@ public class ConditionalManaEffect extends ManaEffect {
if (controller == null) {
return false;
}
controller.getManaPool().addMana(getMana(game, source), game, source);
Mana mana = getMana(game, source);
if (produceMana(true, game, source).getAny() > 0) {
checkToFirePossibleEvents(mana, game, source);
}
controller.getManaPool().addMana(mana, game, source);
return true;
}
@ -56,6 +60,21 @@ public class ConditionalManaEffect extends ManaEffect {
return new ConditionalManaEffect(this);
}
@Override
public Mana getMana(Game game, Ability source) {
return produceMana(false, game, source);
}
@Override
public List<Mana> getNetMana(Game game, Ability source) {
if (condition.apply(game, source)) {
return effect.getNetMana(game, source);
} else if (otherwiseEffect != null) {
return otherwiseEffect.getNetMana(game, source);
}
return null;
}
@Override
public Mana produceMana(boolean netMana, Game game, Ability source) {
Mana mana = new Mana();
@ -75,7 +94,6 @@ public class ConditionalManaEffect extends ManaEffect {
mana.setAny(0);
mana.add(choice.getMana(amount));
}
checkToFirePossibleEvents(mana, game, source);
}
return mana;
}

View file

@ -570,7 +570,7 @@ public class ContinuousEffects implements Serializable {
* the mana cost or alternative cost (as determined in rule 601.2b), plus
* all additional costs and cost increases, and minus all cost reductions.
* If the mana component of the total cost is reduced to nothing by cost
* reduction effects, it is considered to be {0}. It cant be reduced to
* reduction effects, it is considered to be {0}. It can't be reduced to
* less than {0}. Once the total cost is determined, any effects that
* directly affect the total cost are applied. Then the resulting total cost
* becomes locked in. If effects would change the total cost after this

View file

@ -76,6 +76,10 @@ public abstract class RequirementEffect extends ContinuousEffectImpl {
return null;
}
public int getMinNumberOfBlockers() {
return 0;
}
/**
* Player related check The player returned or controlled planeswalker must
* be attacked with at least one attacker

View file

@ -33,7 +33,7 @@ import java.util.List;
* 702.37. Amplify 702.37a Amplify is a static ability. Amplify N means As
* this object enters the battlefield, reveal any number of cards from your hand
* that share a creature type with it. This permanent enters the battlefield
* with N +1/+1 counters on it for each card revealed this way. You cant reveal
* with N +1/+1 counters on it for each card revealed this way. You can't reveal
* this card or any other cards that are entering the battlefield at the same
* time as this card. 702.37b If a creature has multiple instances of amplify,
* each one works separately.

View file

@ -1,4 +1,3 @@
package mage.abilities.effects.common;
import mage.MageObject;
@ -17,13 +16,14 @@ import mage.util.CardUtil;
*
* @author LevelX2
*/
public class NameACardEffect extends OneShotEffect {
public class ChooseACardNameEffect extends OneShotEffect {
public static String INFO_KEY = "NAMED_CARD";
public enum TypeOfName {
ALL,
NOT_BASIC_LAND_NAME,
NON_ARTIFACT_AND_NON_LAND_NAME,
NON_LAND_NAME,
NON_LAND_AND_NON_CREATURE_NAME,
@ -33,13 +33,13 @@ public class NameACardEffect extends OneShotEffect {
private final TypeOfName typeOfName;
public NameACardEffect(TypeOfName typeOfName) {
public ChooseACardNameEffect(TypeOfName typeOfName) {
super(Outcome.Detriment);
this.typeOfName = typeOfName;
staticText = setText();
}
public NameACardEffect(final NameACardEffect effect) {
public ChooseACardNameEffect(final ChooseACardNameEffect effect) {
super(effect);
this.typeOfName = effect.typeOfName;
}
@ -58,6 +58,10 @@ public class NameACardEffect extends OneShotEffect {
cardChoice.setChoices(CardRepository.instance.getNames());
cardChoice.setMessage("Choose a card name");
break;
case NOT_BASIC_LAND_NAME:
cardChoice.setChoices(CardRepository.instance.getNotBasicLandNames());
cardChoice.setMessage("Choose a card name other than a basic land card name");
break;
case NON_ARTIFACT_AND_NON_LAND_NAME:
cardChoice.setChoices(CardRepository.instance.getNonArtifactAndNonLandNames());
cardChoice.setMessage("Choose a nonartifact, nonland card name");
@ -96,8 +100,8 @@ public class NameACardEffect extends OneShotEffect {
}
@Override
public NameACardEffect copy() {
return new NameACardEffect(this);
public ChooseACardNameEffect copy() {
return new ChooseACardNameEffect(this);
}
private String setText() {
@ -106,6 +110,9 @@ public class NameACardEffect extends OneShotEffect {
case ALL:
sb.append("card");
break;
case NOT_BASIC_LAND_NAME:
sb.append("card name other than a basic land card");
break;
case NON_ARTIFACT_AND_NON_LAND_NAME:
sb.append("nonartifact, nonland card");
break;

View file

@ -24,8 +24,8 @@ import mage.util.CardUtil;
// 701.26. Detain
//
// 701.26a Certain spells and abilities can detain a permanent. Until the next
// turn of the controller of that spell or ability, that permanent cant attack
// or block and its activated abilities cant be activated.
// turn of the controller of that spell or ability, that permanent can't attack
// or block and its activated abilities can't be activated.
//
public class DetainTargetEffect extends OneShotEffect {

View file

@ -28,10 +28,10 @@ public class DrawDiscardTargetEffect extends OneShotEffect {
this.cardsToDiscard = cardsToDiscard;
staticText = new StringBuilder("Target player draws ")
.append(cardsToDraw == 1?"a": CardUtil.numberToText(cardsToDraw))
.append(" card").append(cardsToDraw == 1?" ": "s")
.append(" card").append(cardsToDraw == 1?"": "s")
.append(", then discards ")
.append(cardsToDiscard == 1?"a": CardUtil.numberToText(cardsToDiscard))
.append(" card").append(cardsToDiscard == 1?" ": "s").toString();
.append(" card").append(cardsToDiscard == 1?"": "s").toString();
}
public DrawDiscardTargetEffect(final DrawDiscardTargetEffect effect) {

View file

@ -26,10 +26,10 @@ import mage.players.Player;
*
* 702.49. Epic 702.49a Epic represents two spell abilities, one of which
* creates a delayed triggered ability. Epic means For the rest of the game,
* you cant cast spells, and At the beginning of each of your upkeeps for the
* you can't cast spells, and At the beginning of each of your upkeeps for the
* rest of the game, copy this spell except for its epic ability. If the spell
* has any targets, you may choose new targets for the copy. See rule 706.10.
* 702.49b A player cant cast spells once a spell with epic he or she controls
* 702.49b A player can't cast spells once a spell with epic he or she controls
* resolves, but effects (such as the epic ability itself) can still put copies
* of spells onto the stack. *
*/

View file

@ -60,7 +60,7 @@ public class GainLifeTargetEffect extends OneShotEffect {
String message = life.getMessage();
if (!mode.getTargets().isEmpty()) {
sb.append("Target ").append(mode.getTargets().get(0).getTargetName());
sb.append("target ").append(mode.getTargets().get(0).getTargetName());
} else {
sb.append("that player");
}

View file

@ -24,10 +24,10 @@ import mage.target.targetpointer.FixedTarget;
// 701.27. Populate
//
// 701.27a To populate means to choose a creature token you control and put a
// token onto the battlefield thats a copy of that creature token.
// token onto the battlefield that's a copy of that creature token.
//
// 701.27b If you control no creature tokens when instructed to populate, you
// wont put a token onto the battlefield.
// won't put a token onto the battlefield.
//
public class PopulateEffect extends OneShotEffect {

View file

@ -1,5 +1,3 @@
package mage.abilities.effects.common;
import java.util.HashMap;
@ -15,13 +13,12 @@ import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetCreatureOrPlayerAmount;
import mage.target.TargetAmount;
/**
*
* @author LevelX2
*/
public class PreventDamageToTargetMultiAmountEffect extends PreventionEffectImpl {
private final Map<UUID, Integer> targetAmountMap = new HashMap<>();
@ -44,9 +41,9 @@ public class PreventDamageToTargetMultiAmountEffect extends PreventionEffectImpl
super.init(source, game);
Target target = source.getTargets().get(0);
MageObject sourceObject = game.getObject(source.getSourceId());
if (target instanceof TargetCreatureOrPlayerAmount && sourceObject != null) {
TargetCreatureOrPlayerAmount multiTarget = (TargetCreatureOrPlayerAmount) target;
for (UUID targetId: multiTarget.getTargets()) {
if (target instanceof TargetAmount && sourceObject != null) {
TargetAmount multiTarget = (TargetAmount) target;
for (UUID targetId : multiTarget.getTargets()) {
Player player = null;
Permanent permanent = game.getPermanent(targetId);
if (permanent == null) {
@ -107,7 +104,7 @@ public class PreventDamageToTargetMultiAmountEffect extends PreventionEffectImpl
if (duration == Duration.EndOfTurn) {
sb.append("this turn ");
}
sb.append("to any number of target creatures and/or players, divided as you choose");
sb.append("to any number of targets, divided as you choose");
return sb.toString();
}

View file

@ -35,18 +35,26 @@ import mage.game.permanent.Permanent;
* @author LevelX2
*/
public class MustBeBlockedByAtLeastOneSourceEffect extends RequirementEffect {
private int minNumberOfBlockers;
public MustBeBlockedByAtLeastOneSourceEffect() {
this(Duration.EndOfTurn);
}
public MustBeBlockedByAtLeastOneSourceEffect(Duration duration) {
this(duration, 1);
}
public MustBeBlockedByAtLeastOneSourceEffect(Duration duration, int minNumberOfBlockers) {
super(duration);
this.minNumberOfBlockers = minNumberOfBlockers;
staticText = "{this} must be blocked " + (duration == Duration.EndOfTurn ? "this turn " : "") + "if able";
}
public MustBeBlockedByAtLeastOneSourceEffect(final MustBeBlockedByAtLeastOneSourceEffect effect) {
super(effect);
this.minNumberOfBlockers = effect.minNumberOfBlockers;
}
@Override
@ -69,6 +77,11 @@ public class MustBeBlockedByAtLeastOneSourceEffect extends RequirementEffect {
return source.getSourceId();
}
@Override
public int getMinNumberOfBlockers() {
return minNumberOfBlockers;
}
@Override
public MustBeBlockedByAtLeastOneSourceEffect copy() {
return new MustBeBlockedByAtLeastOneSourceEffect(this);

View file

@ -147,7 +147,7 @@ public class BoostAllEffect extends ContinuousEffectImpl {
protected void setText() {
StringBuilder sb = new StringBuilder();
if (excludeSource) {
sb.append("Other ");
sb.append("other ");
}
sb.append(filter.getMessage()).append(" get ");
String p = power.toString();

View file

@ -21,9 +21,9 @@ import mage.players.Player;
*/
//20130711
/*
* 903.11. If a commander would be put into its owners graveyard from anywhere, that player may put it into the command zone instead.
* 903.11. If a commander would be put into its owner's graveyard from anywhere, that player may put it into the command zone instead.
* 903.12. If a commander would be put into the exile zone from anywhere, its owner may put it into the command zone instead.
* 903.9. If a commander would be exiled from anywhere or put into its owners hand, graveyard, or
* 903.9. If a commander would be exiled from anywhere or put into its owner's hand, graveyard, or
library from anywhere, its owner may put it into the command zone instead. This replacement effect
may apply more than once to the same event. This is an exception to rule 614.5.
*/
@ -35,7 +35,7 @@ public class CommanderReplacementEffect extends ReplacementEffectImpl {
public CommanderReplacementEffect(UUID commanderId, boolean alsoHand, boolean alsoLibrary) {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "If a commander would be put into its owners graveyard from anywhere, that player may put it into the command zone instead. If a commander would be put into the exile zone from anywhere, its owner may put it into the command zone instead.";
staticText = "If a commander would be put into its owner's graveyard from anywhere, that player may put it into the command zone instead. If a commander would be put into the exile zone from anywhere, its owner may put it into the command zone instead.";
this.commanderId = commanderId;
this.duration = Duration.EndOfGame;
this.alsoHand = alsoHand;

View file

@ -23,8 +23,8 @@ import mage.players.Player;
import mage.target.Target;
/**
* 6/8/2016 If a spell or abilitys targets are changed, or if a copy of a spell
* or ability is put onto the stack and has new targets chosen, it doesnt have
* 6/8/2016 If a spell or ability's targets are changed, or if a copy of a spell
* or ability is put onto the stack and has new targets chosen, it doesn't have
* to target a Flagbearer.
*
* 3/16/2017 A Flagbearer only requires targeting of itself when choosing targets

View file

@ -26,7 +26,7 @@ public abstract class SearchTargetGraveyardHandLibraryForCardNameAndExileEffect
protected String searchForText;
/* Slaughter Games
* 10/1/2012: You can leave any cards with that name in the zone they are in. You dont have to exile them.
* 10/1/2012: You can leave any cards with that name in the zone they are in. You don't have to exile them.
*
* Sowing Salt
* 2/1/2005: The copies must be found if they are in publicly viewable zones. Finding copies while searching private zones is optional.

View file

@ -1,6 +1,7 @@
package mage.abilities.effects.mana;
import java.util.ArrayList;
import java.util.List;
import mage.Mana;
import mage.abilities.Ability;
import mage.choices.ChoiceColor;
@ -13,25 +14,28 @@ import mage.util.CardUtil;
*/
public class AddManaOfAnyColorEffect extends BasicManaEffect {
protected int amount;
protected final int amount;
protected final ArrayList<Mana> netMana = new ArrayList<>();
public AddManaOfAnyColorEffect() {
this(1);
}
public AddManaOfAnyColorEffect(final int amount) {
public AddManaOfAnyColorEffect(int amount) {
super(new Mana(0, 0, 0, 0, 0, 0, amount, 0));
this.amount = amount;
this.staticText = new StringBuilder("add ")
.append(CardUtil.numberToText(amount))
.append(" mana of any ")
.append(amount > 1 ? "one " : "")
.append("color").toString();
netMana.add(Mana.GreenMana(amount));
netMana.add(Mana.BlueMana(amount));
netMana.add(Mana.BlackMana(amount));
netMana.add(Mana.WhiteMana(amount));
netMana.add(Mana.RedMana(amount));
this.staticText = "add " + CardUtil.numberToText(amount) + " mana of any " + (amount > 1 ? "one " : "") + "color";
}
public AddManaOfAnyColorEffect(final AddManaOfAnyColorEffect effect) {
super(effect);
this.amount = effect.amount;
this.netMana.addAll(effect.netMana);
}
@Override
@ -41,23 +45,33 @@ public class AddManaOfAnyColorEffect extends BasicManaEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
checkToFirePossibleEvents(getMana(game, source), game, source);
controller.getManaPool().addMana(getMana(game, source), game, source);
return true;
}
return false;
}
@Override
public List<Mana> getNetMana(Game game, Ability source) {
return netMana;
}
@Override
public Mana produceMana(boolean netMana, Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
String mes = String.format("Select color of %d mana to add it", this.amount);
ChoiceColor choice = new ChoiceColor(true, mes, game.getObject(source.getSourceId()));
if (controller.choose(outcome, choice, game)) {
if (choice.getColor() == null) {
return false;
if (choice.getColor() != null) {
return choice.getMana(amount);
}
Mana createdMana = choice.getMana(amount);
if (createdMana != null) {
checkToFirePossibleEvents(createdMana, game, source);
controller.getManaPool().addMana(createdMana, game, source);
}
return true;
}
}
return false;
return null;
}
public int getAmount() {

View file

@ -42,7 +42,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate;
* you may tap two untapped creatures you control that each share a color with it"
* and "When you cast this spell, if its conspire cost was paid, copy it.
* If the spell has any targets, you may choose new targets for the copy."
* Paying a spells conspire cost follows the rules for paying additional costs in rules 601.2b and 601.2eg.
* Paying a spell's conspire cost follows the rules for paying additional costs in rules 601.2b and 601.2eg.
* 702.77b If a spell has multiple instances of conspire, each is paid separately and triggers
* based on its own payment, not any other instance of conspire. *
*

View file

@ -27,20 +27,20 @@ import mage.util.CardUtil;
/**
* 702.65. Delve 702.65a Delve is a static ability that functions while the
* spell with delve is on the stack. Delve means For each generic mana in
* this spells total cost, you may exile a card from your graveyard rather than
* this spell's total cost, you may exile a card from your graveyard rather than
* pay that mana. The delve ability isn't an additional or alternative cost and
* applies only after the total cost of the spell with delve is determined.
* 702.65b Multiple instances of delve on the same spell are redundant.
*
* The rules for delve have changed slightly since it was last in an expansion.
* Previously, delve reduced the cost to cast a spell. Under the current rules,
* you exile cards from your graveyard at the same time you pay the spells
* you exile cards from your graveyard at the same time you pay the spell's
* cost. Exiling a card this way is simply another way to pay that cost. * Delve
* doesn't change a spells mana cost or converted mana cost. For example, Dead
* Drops converted mana cost is 10 even if you exiled three cards to cast it. *
* You cant exile cards to pay for the colored mana requirements of a spell
* with delve. * You cant exile more cards than the generic mana requirement of
* a spell with delve. For example, you cant exile more than nine cards from
* doesn't change a spell's mana cost or converted mana cost. For example, Dead
* Drop's converted mana cost is 10 even if you exiled three cards to cast it. *
* You can't exile cards to pay for the colored mana requirements of a spell
* with delve. * You can't exile more cards than the generic mana requirement of
* a spell with delve. For example, you can't exile more than nine cards from
* your graveyard to cast Dead Drop. * Because delve isn't an alternative cost,
* it can be used in conjunction with alternative costs.
*

View file

@ -15,7 +15,7 @@ import mage.game.permanent.Permanent;
/*
* 702.31. Fading
*
* 702.31a Fading is a keyword that represents two abilities. Fading N means This permanent enters the battlefield with N fade counters on it and At the beginning of your upkeep, remove a fade counter from this permanent. If you cant, sacrifice the permanent.
* 702.31a Fading is a keyword that represents two abilities. Fading N means This permanent enters the battlefield with N fade counters on it and At the beginning of your upkeep, remove a fade counter from this permanent. If you can't, sacrifice the permanent.
*
*/
public class FadingAbility extends EntersBattlefieldAbility {
@ -28,7 +28,7 @@ public class FadingAbility extends EntersBattlefieldAbility {
ability.setRuleVisible(false);
addSubAbility(ability);
ruleText = "Fading " + fadeCounter + " <i>(This permanent enters the battlefield with " + fadeCounter + " fade counters on it."
+ " At the beginning of your upkeep, remove a fade counter from this permanent. If you cant, sacrifice the permanent.</i>";
+ " At the beginning of your upkeep, remove a fade counter from this permanent. If you can't, sacrifice the permanent.</i>";
}
public FadingAbility(final FadingAbility ability) {
@ -51,7 +51,7 @@ class FadingEffect extends OneShotEffect {
FadingEffect() {
super(Outcome.Sacrifice);
staticText = "remove a fade counter from this permanent. If you cant, sacrifice the permanent";
staticText = "remove a fade counter from this permanent. If you can't, sacrifice the permanent";
}
FadingEffect(final FadingEffect effect) {

View file

@ -21,23 +21,23 @@ import mage.util.CardUtil;
*
* 701.28. Monstrosity
*
* 701.28a Monstrosity N means If this permanent isnt monstrous, put N +1/+1 counters on it
* 701.28a Monstrosity N means If this permanent isn't monstrous, put N +1/+1 counters on it
* and it becomes monstrous. Monstrous is a condition of that permanent that can be
* referred to by other abilities.
*
* 701.28b If a permanents ability instructs a player to monstrosity X, other abilities of
* 701.28b If a permanent's ability instructs a player to monstrosity X, other abilities of
* that permanent may also refer to X. The value of X in those abilities is equal to
* the value of X as that permanent became monstrous.
*
* * Once a creature becomes monstrous, it cant become monstrous again. If the creature
* * Once a creature becomes monstrous, it can't become monstrous again. If the creature
* is already monstrous when the monstrosity ability resolves, nothing happens.
*
* * Monstrous isnt an ability that a creature has. Its just something true about that
* * Monstrous isn't an ability that a creature has. It's just something true about that
* creature. If the creature stops being a creature or loses its abilities, it will
* continue to be monstrous.
*
* * An ability that triggers when a creature becomes monstrous wont trigger if that creature
* isnt on the battlefield when its monstrosity ability resolves.
* * An ability that triggers when a creature becomes monstrous won't trigger if that creature
* isn't on the battlefield when its monstrosity ability resolves.
*
* @author LevelX2
*/

View file

@ -18,7 +18,7 @@ import mage.players.Player;
/**
* 702.58a Recover is a triggered ability that functions only while the card
* with recover is in a players graveyard. Recover [cost] means When a
* with recover is in a player's graveyard. Recover [cost] means When a
* creature is put into your graveyard from the battlefield, you may pay [cost].
* If you do, return this card from your graveyard to your hand. Otherwise,
* exile this card.

View file

@ -28,7 +28,7 @@ import mage.target.common.TargetCreaturePermanent;
//
// 702.95a Scavenge is an activated ability that functions only while the card
// with scavenge is in a graveyard. "Scavenge [cost]" means "[Cost], Exile this
// card from your graveyard: Put a number of +1/+1 counters equal to this cards
// card from your graveyard: Put a number of +1/+1 counters equal to this card's
// power on target creature. Activate this ability only any time you could cast
// a sorcery."
//

View file

@ -48,9 +48,9 @@ import mage.util.GameLog;
* 702.94d A creature can be paired with only one other creature.
*
* 702.94e A paired creature becomes unpaired if any of the following occur:
* another player gains control of it or the creature its paired with; it or
* the creature its paired with stops being a creature; or it or the creature
* its paired with leaves the battlefield.
* another player gains control of it or the creature it's paired with; it or
* the creature it's paired with stops being a creature; or it or the creature
* it's paired with leaves the battlefield.
*
* @author LevelX2
*/

View file

@ -82,7 +82,7 @@ import mage.target.targetpointer.FixedTarget;
* play the spell if possible, even if that player doesn't want to. Normal
* timing considerations for the spell are ignored (for example, if the
* suspended card is a creature and this ability resolves during your upkeep,
* youre able to play the card), but other play restrictions are not ignored.
* you're able to play the card), but other play restrictions are not ignored.
*
* If the second triggered ability of suspend resolves and the suspended card
* can't be played due to a lack of legal targets or a play restriction, for

View file

@ -23,14 +23,14 @@ import mage.target.common.TargetCardInLibrary;
* 702.52. Transmute
*
* 702.52a Transmute is an activated ability that functions only while the card
* with transmute is in a players hand. Transmute [cost] means [Cost],
* with transmute is in a player's hand. Transmute [cost] means [Cost],
* Discard this card: Search your library for a card with the same converted
* mana cost as the discarded card, reveal that card, and put it into your hand.
* Then shuffle your library. Play this ability only any time you could play a
* sorcery.
*
* 702.52b Although the transmute ability is playable only if the card is in a
* players hand, it continues to exist while the object is in play and in all
* player's hand, it continues to exist while the object is in play and in all
* other zones. Therefore objects with transmute will be affected by effects
* that depend on objects having one or more activated abilities.
*

View file

@ -22,7 +22,7 @@ import mage.players.Player;
//
// 702.96a Unleash is a keyword that represents two static abilities.
// "Unleash" means "You may have this permanent enter the battlefield with an additional +1/+1 counter on it"
// and "This permanent cant block as long as it has a +1/+1 counter on it."
// and "This permanent can't block as long as it has a +1/+1 counter on it."
public class UnleashAbility extends SimpleStaticAbility {
public UnleashAbility() {

View file

@ -1,5 +1,3 @@
package mage.abilities.mana;
import java.util.ArrayList;
@ -14,7 +12,6 @@ import mage.game.Game;
*
* @author LevelX2
*/
public class ConditionalManaAbility extends ActivatedManaAbilityImpl {
ConditionalManaEffect conditionalManaEffect;
@ -37,7 +34,7 @@ public class ConditionalManaAbility extends ActivatedManaAbilityImpl {
@Override
public List<Mana> getNetMana(Game game) {
List<Mana> newNetMana = new ArrayList<>();
newNetMana.add(conditionalManaEffect.getMana(game, this));
newNetMana.addAll(conditionalManaEffect.getNetMana(game, this));
return newNetMana;
}
}

View file

@ -12,7 +12,7 @@ import mage.abilities.*;
import mage.abilities.costs.Cost;
import mage.abilities.costs.VariableCost;
import mage.abilities.costs.common.RemoveVariableCountersTargetCost;
import mage.abilities.effects.common.NameACardEffect;
import mage.abilities.effects.common.ChooseACardNameEffect;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.cards.repository.PluginClassloaderRegistery;
import mage.constants.*;
@ -401,7 +401,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
case CHOSEN_NAME: //Declaration of Naught only
ability.getTargets().clear();
FilterSpell filterSpell = new FilterSpell("spell with the chosen name");
filterSpell.add(new NamePredicate((String) game.getState().getValue(ability.getSourceId().toString() + NameACardEffect.INFO_KEY)));
filterSpell.add(new NamePredicate((String) game.getState().getValue(ability.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY)));
TargetSpell target = new TargetSpell(1, filterSpell);
ability.addTarget(target);
break;

View file

@ -83,7 +83,7 @@ public class TxtDeckImporter extends DeckImporter {
return;
}
String lineNum = line.substring(0, delim).trim();
String lineName = line.substring(delim).replace("", "\'").trim();
String lineName = line.substring(delim).replace("'", "\'").trim();
lineName = lineName
.replace("&amp;", "//")
.replace("Æ", "Ae")

View file

@ -1,4 +1,3 @@
package mage.cards.repository;
import com.j256.ormlite.dao.Dao;
@ -17,6 +16,7 @@ import java.util.*;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SetType;
import mage.constants.SuperType;
import mage.util.RandomUtil;
import org.apache.log4j.Logger;
@ -32,7 +32,7 @@ public enum CardRepository {
// raise this if db structure was changed
private static final long CARD_DB_VERSION = 51;
// raise this if new cards were added to the server
private static final long CARD_CONTENT_VERSION = 112;
private static final long CARD_CONTENT_VERSION = 113;
private Dao<CardInfo, Object> cardDao;
private Set<String> classNames;
@ -153,6 +153,29 @@ public enum CardRepository {
return names;
}
public Set<String> getNotBasicLandNames() {
Set<String> names = new TreeSet<>();
try {
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
qb.distinct().selectColumns("name");
qb.where().not().like("supertypes", new SelectArg('%' + SuperType.BASIC.name() + '%'));
List<CardInfo> results = cardDao.query(qb.prepare());
for (CardInfo card : results) {
int result = card.getName().indexOf(" // ");
if (result > 0) {
names.add(card.getName().substring(0, result));
names.add(card.getName().substring(result + 4));
} else {
names.add(card.getName());
}
}
} catch (SQLException ex) {
Logger.getLogger(CardRepository.class).error("Error getting non-land names from DB : " + ex);
}
return names;
}
public Set<String> getCreatureNames() {
Set<String> names = new TreeSet<>();
try {

View file

@ -25,6 +25,7 @@ public enum CounterType {
CRYSTAL("crystal"),
CUBE("cube"),
CURRENCY("currency"),
DEATH("death"),
DELAY("delay"),
DEPLETION("depletion"),
DESPAIR("despair"),

View file

@ -27,7 +27,7 @@ public class Monarch extends Designation {
}
}
// At the beginning of the monarchs end step, that player draws a card
// At the beginning of the monarch's end step, that player draws a card
class MonarchDrawTriggeredAbility extends BeginningOfEndStepTriggeredAbility {
public MonarchDrawTriggeredAbility() {
@ -60,7 +60,7 @@ class MonarchDrawTriggeredAbility extends BeginningOfEndStepTriggeredAbility {
@Override
public String getRule() {
return "At the beginning of the monarchs end step, that player draws a card.";
return "At the beginning of the monarch's end step, that player draws a card.";
}
}

View file

@ -1,4 +1,3 @@
package mage.filter;
import java.util.ArrayList;
@ -68,6 +67,9 @@ public class FilterCard extends FilterObject<Card> {
}
public void add(ObjectPlayerPredicate predicate) {
if (isLockedFilter()) {
throw new UnsupportedOperationException("You may not modify a locked filter");
}
extraPredicates.add(predicate);
}

View file

@ -1,4 +1,3 @@
package mage.filter;
import java.util.ArrayList;
@ -17,13 +16,14 @@ public abstract class FilterImpl<E> implements Filter<E> {
protected List<Predicate<Object>> predicates = new ArrayList<>();
protected String message;
protected boolean lockedFilter = false; // Helps to prevent to "accidently" modify the StaticFilters objects
protected boolean lockedFilter; // Helps to prevent to "accidently" modify the StaticFilters objects
@Override
public abstract FilterImpl<E> copy();
public FilterImpl(String name) {
this.message = name;
this.lockedFilter = false;
}
public FilterImpl(final FilterImpl<E> filter) {

View file

@ -1,4 +1,3 @@
package mage.filter;
import java.util.ArrayList;
@ -27,11 +26,6 @@ public class FilterPermanent extends FilterObject<Permanent> implements FilterIn
super("permanent");
}
public FilterPermanent(final FilterPermanent filter) {
super(filter);
this.extraPredicates = new ArrayList<>(filter.extraPredicates);
}
public FilterPermanent(String name) {
super(name);
}
@ -43,11 +37,16 @@ public class FilterPermanent extends FilterObject<Permanent> implements FilterIn
public FilterPermanent(Set<SubType> subtypesList, String name) {
super(name);
for (SubType subtype: subtypesList) {
for (SubType subtype : subtypesList) {
this.add(new SubtypePredicate(subtype));
}
}
public FilterPermanent(final FilterPermanent filter) {
super(filter);
this.extraPredicates = new ArrayList<>(filter.extraPredicates);
}
@Override
public boolean checkObjectClass(Object object) {
return object instanceof Permanent;
@ -63,6 +62,9 @@ public class FilterPermanent extends FilterObject<Permanent> implements FilterIn
}
public void add(ObjectPlayerPredicate predicate) {
if (isLockedFilter()) {
throw new UnsupportedOperationException("You may not modify a locked filter");
}
extraPredicates.add(predicate);
}

View file

@ -1,4 +1,3 @@
package mage.filter;
import java.util.ArrayList;
@ -34,6 +33,9 @@ public class FilterPlayer extends FilterImpl<Player> {
}
public void add(ObjectPlayerPredicate predicate) {
if (isLockedFilter()) {
throw new UnsupportedOperationException("You may not modify a locked filter");
}
extraPredicates.add(predicate);
}

View file

@ -1,4 +1,3 @@
package mage.filter;
import java.util.ArrayList;
@ -42,6 +41,9 @@ public class FilterStackObject extends FilterObject<StackObject> {
}
public void add(ObjectPlayerPredicate predicate) {
if (isLockedFilter()) {
throw new UnsupportedOperationException("You may not modify a locked filter");
}
extraPredicates.add(predicate);
}

View file

@ -16,7 +16,7 @@ public class MulticoloredPredicate implements Predicate<MageObject> {
@Override
public boolean apply(MageObject input, Game game) {
// 708.3. Each split card that consists of two halves with different colored mana symbols in their mana costs
// is a multicolored card while its not a spell on the stack. While its a spell on the stack, its only the
// is a multicolored card while it's not a spell on the stack. While it's a spell on the stack, it's only the
// color or colors of the half or halves being cast. #
if (input instanceof SplitCardHalf && game.getState().getZone(input.getId()) != Zone.STACK) {
return 1 < ((SplitCardHalf) input).getMainCard().getColor(game).getColorCount();

View file

@ -147,7 +147,7 @@ public abstract class GameCommanderImpl extends GameImpl {
}
/* 20130711
*903.14a A player thats been dealt 21 or more combat damage by the same commander
*903.14a A player that's been dealt 21 or more combat damage by the same commander
* over the course of the game loses the game. (This is a state-based action. See rule 704.)
*
*/

View file

@ -2007,7 +2007,7 @@ public abstract class GameImpl implements Game, Serializable {
}
}
// 704.5s If the number of lore counters on a Saga permanent is greater than or equal to its final chapter number
// and it isnt the source of a chapter ability that has triggered but not yet left the stack, that Sagas controller sacrifices it.
// and it isn't the source of a chapter ability that has triggered but not yet left the stack, that Saga's controller sacrifices it.
if (perm.hasSubtype(SubType.SAGA, this)) {
for (Ability sagaAbility : perm.getAbilities()) {
if (sagaAbility instanceof SagaAbility) {

View file

@ -695,6 +695,7 @@ public class Combat implements Serializable, Copyable<Combat> {
//20101001 - 509.1c
// map with attackers (UUID) that must be blocked by at least one blocker and a set of all creatures that can block it and don't block yet
Map<UUID, Set<UUID>> mustBeBlockedByAtLeastOne = new HashMap<>();
Map<UUID, Integer> minNumberOfBlockersMap = new HashMap<>();
// check mustBlock requirements of creatures from opponents of attacking player
for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED, player.getId(), game)) {
@ -710,6 +711,7 @@ public class Combat implements Serializable, Copyable<Combat> {
for (Ability ability : entry.getValue()) {
UUID toBeBlockedCreature = effect.mustBlockAttackerIfElseUnblocked(ability, game);
if (toBeBlockedCreature != null) {
minNumberOfBlockersMap.put(toBeBlockedCreature, effect.getMinNumberOfBlockers());
Set<UUID> potentialBlockers;
if (mustBeBlockedByAtLeastOne.containsKey(toBeBlockedCreature)) {
potentialBlockers = mustBeBlockedByAtLeastOne.get(toBeBlockedCreature);
@ -804,6 +806,7 @@ public class Combat implements Serializable, Copyable<Combat> {
for (Ability ability : entry.getValue()) {
UUID toBeBlockedCreature = effect.mustBlockAttackerIfElseUnblocked(ability, game);
if (toBeBlockedCreature != null) {
minNumberOfBlockersMap.put(toBeBlockedCreature, effect.getMinNumberOfBlockers());
Set<UUID> potentialBlockers;
if (mustBeBlockedByAtLeastOne.containsKey(toBeBlockedCreature)) {
potentialBlockers = mustBeBlockedByAtLeastOne.get(toBeBlockedCreature);
@ -898,6 +901,7 @@ public class Combat implements Serializable, Copyable<Combat> {
break;
}
}
requirementFulfilled &= (combatGroup.getBlockers().size() >= Math.min(minNumberOfBlockersMap.get(toBeBlockedCreatureId), mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId).size()));
if (!requirementFulfilled) {
// creature is not blocked but has possible blockers
if (controller.isHuman()) {
@ -906,6 +910,9 @@ public class Combat implements Serializable, Copyable<Combat> {
// check if all possible blocker block other creatures they are forced to block
// read through all possible blockers
for (UUID possibleBlockerId : mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId)) {
if (combatGroup.getBlockers().contains(possibleBlockerId)) {
continue;
}
String blockRequiredMessage = isCreatureDoingARequiredBlock(
possibleBlockerId, toBeBlockedCreatureId, mustBeBlockedByAtLeastOne, game);
if (blockRequiredMessage != null) { // message means not required
@ -931,7 +938,9 @@ public class Combat implements Serializable, Copyable<Combat> {
}
defender.declareBlocker(defender.getId(), possibleBlockerId, toBeBlockedCreatureId, game);
}
break;
if (combatGroup.getBlockers().size() >= minNumberOfBlockersMap.get(toBeBlockedCreatureId)) {
break;
}
}
}
}

View file

@ -869,7 +869,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
// for handling Butcher Orgg
if (creature.getAbilities().containsKey(ControllerDivideCombatDamageAbility.getInstance().getId())) {
Player player = game.getPlayer(defenderAssignsCombatDamage(game) ? defendingPlayerId : (!isAttacking && attackerAssignsCombatDamage(game) ? game.getCombat().getAttackingPlayerId() : playerId));
// 10/4/2004 If it is blocked but then all of its blockers are removed before combat damage is assigned, then it wont be able to deal combat damage and you wont be able to use its ability.
// 10/4/2004 If it is blocked but then all of its blockers are removed before combat damage is assigned, then it won't be able to deal combat damage and you won't be able to use its ability.
// (same principle should apply if it's blocking and its blocked attacker is removed from combat)
if (!((blocked && blockers.isEmpty() && isAttacking) || (attackers.isEmpty() && !isAttacking)) && canDamage(creature, first)) {
if (player.chooseUse(Outcome.Damage, "Do you wish to assign " + creature.getLogName() + "'s combat damage divided among defending player and/or any number of defending creatures?", null, game)) {

View file

@ -1,19 +1,18 @@
package mage.game.command.emblems;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.mana.ManaAbility;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.command.Emblem;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.StackAbility;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/**
*
@ -30,14 +29,19 @@ public class RowanKenrithEmblem extends Emblem {
class RowanKenrithEmblemTriggeredAbility extends TriggeredAbilityImpl {
public RowanKenrithEmblemTriggeredAbility() {
super(Zone.COMMAND, new RowanKenrithEmblemCopyEffect(), false);
RowanKenrithEmblemTriggeredAbility() {
super(Zone.BATTLEFIELD, new RowanKenrithEmblemEffect(), false);
}
public RowanKenrithEmblemTriggeredAbility(final RowanKenrithEmblemTriggeredAbility ability) {
RowanKenrithEmblemTriggeredAbility(final RowanKenrithEmblemTriggeredAbility ability) {
super(ability);
}
@Override
public RowanKenrithEmblemTriggeredAbility copy() {
return new RowanKenrithEmblemTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY;
@ -45,10 +49,11 @@ class RowanKenrithEmblemTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getPlayerId().equals(this.getControllerId())) {
StackObject ability = game.getStack().getStackObject(event.getTargetId());
if (ability != null && !(ability instanceof ManaAbility)) {
this.getEffects().get(0).setTargetPointer(new FixedTarget(ability.getId()));
if (event.getPlayerId().equals(getControllerId())) {
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId());
if (stackAbility != null && !(stackAbility.getStackAbility() instanceof ActivatedManaAbilityImpl)) {
Effect effect = this.getEffects().get(0);
effect.setValue("stackAbility", stackAbility);
return true;
}
}
@ -59,40 +64,38 @@ class RowanKenrithEmblemTriggeredAbility extends TriggeredAbilityImpl {
public String getRule() {
return "Whenever you activate an ability that isn't a mana ability, copy it. You may choose new targets for the copy.";
}
@Override
public RowanKenrithEmblemTriggeredAbility copy() {
return new RowanKenrithEmblemTriggeredAbility(this);
}
}
class RowanKenrithEmblemCopyEffect extends OneShotEffect {
class RowanKenrithEmblemEffect extends OneShotEffect {
public RowanKenrithEmblemCopyEffect() {
super(Outcome.Copy);
this.staticText = "copy it. You may choose new targets for the copy.";
RowanKenrithEmblemEffect() {
super(Outcome.Benefit);
this.staticText = ", copy it. You may choose new targets for the copy.";
}
public RowanKenrithEmblemCopyEffect(final RowanKenrithEmblemCopyEffect effect) {
RowanKenrithEmblemEffect(final RowanKenrithEmblemEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source));
if (stackAbility != null) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
stackAbility.createCopyOnStack(game, source, source.getControllerId(), true);
return true;
}
}
return false;
public RowanKenrithEmblemEffect copy() {
return new RowanKenrithEmblemEffect(this);
}
@Override
public RowanKenrithEmblemCopyEffect copy() {
return new RowanKenrithEmblemCopyEffect(this);
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
StackAbility ability = (StackAbility) getValue("stackAbility");
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (ability != null && controller != null && sourcePermanent != null) {
ability.createCopyOnStack(game, source, source.getControllerId(), true);
game.informPlayers(sourcePermanent.getIdName() + ": " + controller.getLogName() + " copied activated ability");
return true;
}
return false;
}
}

View file

@ -178,7 +178,7 @@ public class PermanentCard extends PermanentImpl {
@Override
public int getConvertedManaCost() {
if (isTransformed()) {
// 711.4b While a double-faced permanents back face is up, it has only the characteristics of its back face.
// 711.4b While a double-faced permanent's back face is up, it has only the characteristics of its back face.
// However, its converted mana cost is calculated using the mana cost of its front face. This is a change from previous rules.
// If a permanent is copying the back face of a double-faced card (even if the card representing that copy
// is itself a double-faced card), the converted mana cost of that permanent is 0.

View file

@ -1,4 +1,3 @@
package mage.players;
import java.io.Serializable;
@ -366,25 +365,27 @@ public class ManaPool implements Serializable {
}
public void addMana(Mana manaToAdd, Game game, Ability source, boolean emptyOnTurnsEnd) {
Mana mana = manaToAdd.copy();
if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), playerId, mana))) {
if (mana instanceof ConditionalMana) {
ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceObject(game),
((ConditionalMana) mana).getManaProducerOriginalId() != null ? ((ConditionalMana) mana).getManaProducerOriginalId() : source.getOriginalId());
if (emptyOnTurnsEnd) {
item.setDuration(Duration.EndOfTurn);
if (manaToAdd != null) {
Mana mana = manaToAdd.copy();
if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), playerId, mana))) {
if (mana instanceof ConditionalMana) {
ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceObject(game),
((ConditionalMana) mana).getManaProducerOriginalId() != null ? ((ConditionalMana) mana).getManaProducerOriginalId() : source.getOriginalId());
if (emptyOnTurnsEnd) {
item.setDuration(Duration.EndOfTurn);
}
this.manaItems.add(item);
} else {
ManaPoolItem item = new ManaPoolItem(mana.getRed(), mana.getGreen(), mana.getBlue(), mana.getWhite(), mana.getBlack(), mana.getGeneric() + mana.getColorless(), source.getSourceObject(game), source.getOriginalId(), mana.getFlag());
if (emptyOnTurnsEnd) {
item.setDuration(Duration.EndOfTurn);
}
this.manaItems.add(item);
}
this.manaItems.add(item);
} else {
ManaPoolItem item = new ManaPoolItem(mana.getRed(), mana.getGreen(), mana.getBlue(), mana.getWhite(), mana.getBlack(), mana.getGeneric() + mana.getColorless(), source.getSourceObject(game), source.getOriginalId(), mana.getFlag());
if (emptyOnTurnsEnd) {
item.setDuration(Duration.EndOfTurn);
}
this.manaItems.add(item);
ManaEvent manaEvent = new ManaEvent(EventType.MANA_ADDED, source.getId(), source.getSourceId(), playerId, mana);
manaEvent.setData(mana.toString());
game.fireEvent(manaEvent);
}
ManaEvent manaEvent = new ManaEvent(EventType.MANA_ADDED, source.getId(), source.getSourceId(), playerId, mana);
manaEvent.setData(mana.toString());
game.fireEvent(manaEvent);
}
}

View file

@ -1,4 +1,3 @@
package mage.players;
import java.io.Serializable;
@ -492,18 +491,22 @@ public abstract class PlayerImpl implements Player, Serializable {
PlayerList players = game.getState().getPlayerList(playerId);
for (int i = 0; i < range.getRange(); i++) {
Player player = players.getNext(game);
while (player.hasLeft()) {
player = players.getNext(game);
if (player != null) {
while (player.hasLeft()) {
player = players.getNext(game);
}
inRange.add(player.getId());
}
inRange.add(player.getId());
}
players = game.getState().getPlayerList(playerId);
for (int i = 0; i < range.getRange(); i++) {
Player player = players.getPrevious(game);
while (player.hasLeft()) {
player = players.getPrevious(game);
if (player != null) {
while (player.hasLeft()) {
player = players.getPrevious(game);
}
inRange.add(player.getId());
}
inRange.add(player.getId());
}
}
}

View file

@ -1,21 +1,19 @@
package mage.target;
import mage.constants.Zone;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.constants.Zone;
import mage.game.Game;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public abstract class TargetObject extends TargetImpl {
protected TargetObject() {}
protected TargetObject() {
}
public TargetObject(Zone zone) {
this(1, 1, zone, false);
@ -40,7 +38,7 @@ public abstract class TargetObject extends TargetImpl {
@Override
public String getTargetedName(Game game) {
StringBuilder sb = new StringBuilder();
for (UUID targetId: getTargets()) {
for (UUID targetId : getTargets()) {
MageObject object = game.getObject(targetId);
if (object != null) {
sb.append(object.getLogName()).append(' ');
@ -52,7 +50,9 @@ public abstract class TargetObject extends TargetImpl {
@Override
public boolean canTarget(UUID id, Game game) {
MageObject object = game.getObject(id);
return object != null && game.getState().getZone(id).match(zone) && getFilter().match(object, game);
return object != null
&& zone != null && zone.match(game.getState().getZone(id))
&& getFilter() != null && getFilter().match(object, game);
}
@Override

View file

@ -41,8 +41,8 @@ public class TargetCardInLibrary extends TargetCard {
public TargetCardInLibrary(int minNumTargets, int maxNumTargets, FilterCard filter) {
super(minNumTargets, maxNumTargets, Zone.LIBRARY, filter);
// 701.15b If a player is searching a hidden zone for cards with a stated quality, such as a card
// with a certain card type or color, that player isnt required to find some or all of those cards
// even if theyre present in that zone.
// with a certain card type or color, that player isn't required to find some or all of those cards
// even if they're present in that zone.
this.setRequired(!filter.hasPredicates());
this.setNotTarget(true);
this.librarySearchLimit = Integer.MAX_VALUE;

View file

@ -16,7 +16,7 @@ import mage.players.Player;
import mage.watchers.Watcher;
/* 20130711
*903.14a A player thats been dealt 21 or more combat damage by the same commander
*903.14a A player that's been dealt 21 or more combat damage by the same commander
* over the course of the game loses the game. (This is a state-based action. See rule 704.)
*
*