mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 04:52:07 -08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
d5ad543583
278 changed files with 2275 additions and 706 deletions
|
|
@ -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 it’s 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()) {
|
||||
|
|
|
|||
|
|
@ -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 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.
|
||||
// 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();
|
||||
|
|
|
|||
|
|
@ -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 opponent’s 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
|
||||
|
|
|
|||
|
|
@ -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 "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.costs.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
|
|||
|
|
@ -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 isn’t 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.
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 can’t 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 can’t 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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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 can’t attack
|
||||
// or block and its activated abilities can’t 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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 can’t 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 can’t 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. *
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 that’s 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
|
||||
// won’t put a token onto the battlefield.
|
||||
// won't put a token onto the battlefield.
|
||||
//
|
||||
public class PopulateEffect extends OneShotEffect {
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import mage.players.Player;
|
|||
*/
|
||||
//20130711
|
||||
/*
|
||||
* 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.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 owner’s 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 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.";
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ import mage.players.Player;
|
|||
import mage.target.Target;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
|
|
|
|||
|
|
@ -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 don’t 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.
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 spell’s conspire cost follows the rules for paying additional costs in rules 601.2b and 601.2e–g.
|
||||
* Paying a spell's conspire cost follows the rules for paying additional costs in rules 601.2b and 601.2e–g.
|
||||
* 702.77b If a spell has multiple instances of conspire, each is paid separately and triggers
|
||||
* based on its own payment, not any other instance of conspire. *
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 spell’s 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 spell’s
|
||||
* 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 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
|
||||
* 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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 can’t, 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 can’t, 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 can’t, sacrifice the permanent";
|
||||
staticText = "remove a fade counter from this permanent. If you can't, sacrifice the permanent";
|
||||
}
|
||||
|
||||
FadingEffect(final FadingEffect effect) {
|
||||
|
|
|
|||
|
|
@ -21,23 +21,23 @@ import mage.util.CardUtil;
|
|||
*
|
||||
* 701.28. Monstrosity
|
||||
*
|
||||
* 701.28a “Monstrosity N” means “If this permanent isn’t 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 permanent’s 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 can’t 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 isn’t an ability that a creature has. It’s 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 won’t trigger if that creature
|
||||
* isn’t 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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 player’s 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.”
|
||||
|
|
|
|||
|
|
@ -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 card’s
|
||||
// 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."
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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 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.
|
||||
* 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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
* you’re 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
|
||||
|
|
|
|||
|
|
@ -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 player’s 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
|
||||
* player’s 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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 can’t 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() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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("&", "//")
|
||||
.replace("Æ", "Ae")
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ public enum CounterType {
|
|||
CRYSTAL("crystal"),
|
||||
CUBE("cube"),
|
||||
CURRENCY("currency"),
|
||||
DEATH("death"),
|
||||
DELAY("delay"),
|
||||
DEPLETION("depletion"),
|
||||
DESPAIR("despair"),
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public class Monarch extends Designation {
|
|||
}
|
||||
}
|
||||
|
||||
// At the beginning of the monarch’s 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 monarch’s end step, that player draws a card.";
|
||||
return "At the beginning of the monarch's end step, that player draws a card.";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 it’s not a spell on the stack. While it’s a spell on the stack, it’s 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();
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ public abstract class GameCommanderImpl extends GameImpl {
|
|||
}
|
||||
|
||||
/* 20130711
|
||||
*903.14a A player that’s 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.)
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 isn’t the source of a chapter ability that has triggered but not yet left the stack, that Saga’s 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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 won’t be able to deal combat damage and you won’t 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)) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ public class PermanentCard extends PermanentImpl {
|
|||
@Override
|
||||
public int getConvertedManaCost() {
|
||||
if (isTransformed()) {
|
||||
// 711.4b While a double-faced permanent’s 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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 isn’t required to find some or all of those cards
|
||||
// even if they’re 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;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import mage.players.Player;
|
|||
import mage.watchers.Watcher;
|
||||
|
||||
/* 20130711
|
||||
*903.14a A player that’s 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.)
|
||||
*
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue