forked from External/mage
Merge pull request #11178 from ssk97/MorphRework_v2
Morph rework and check spell characteristics
This commit is contained in:
commit
9456650693
283 changed files with 905 additions and 649 deletions
|
|
@ -281,11 +281,8 @@ public abstract class AbilityImpl implements Ability {
|
|||
// as buyback, kicker, or convoke costs (see rules 117.8 and 117.9), the player announces his
|
||||
// or her intentions to pay any or all of those costs (see rule 601.2e).
|
||||
// A player can't apply two alternative methods of casting or two alternative costs to a single spell.
|
||||
if (isMainPartAbility && !activateAlternateOrAdditionalCosts(sourceObject, noMana, controller, game)) {
|
||||
if (getAbilityType() == AbilityType.SPELL
|
||||
&& ((SpellAbility) this).getSpellAbilityType() == SpellAbilityType.FACE_DOWN_CREATURE) {
|
||||
return false;
|
||||
}
|
||||
if (isMainPartAbility) {
|
||||
activateAlternateOrAdditionalCosts(sourceObject, noMana, controller, game);
|
||||
}
|
||||
|
||||
// 117.6. Some mana costs contain no mana symbols. This represents an unpayable cost. An ability can
|
||||
|
|
@ -436,6 +433,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
case DISTURB:
|
||||
case MORE_THAN_MEETS_THE_EYE:
|
||||
case BESTOW:
|
||||
case MORPH:
|
||||
// from Snapcaster Mage:
|
||||
// If you cast a spell from a graveyard using its flashback ability, you can't pay other alternative costs
|
||||
// (such as that of Foil). (2018-12-07)
|
||||
|
|
@ -452,6 +450,12 @@ public abstract class AbilityImpl implements Ability {
|
|||
throw new IllegalArgumentException("Unknown ability cast mode: " + ((SpellAbility) this).getSpellAbilityCastMode());
|
||||
}
|
||||
}
|
||||
if (this.getAbilityType() == AbilityType.SPELL && this instanceof SpellAbility
|
||||
// 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) {
|
||||
canUseAlternativeCost = false;
|
||||
}
|
||||
|
||||
boolean alternativeCostUsed = false;
|
||||
if (sourceObject != null && !(sourceObject instanceof Permanent)) {
|
||||
|
|
@ -475,17 +479,12 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
// controller specific alternate spell costs
|
||||
if (canUseAlternativeCost && !noMana && !alternativeCostUsed) {
|
||||
if (this.getAbilityType() == AbilityType.SPELL
|
||||
// 117.9a Only one alternative cost can be applied to any one spell as it's being cast.
|
||||
// So an alternate spell ability can't be paid with Omniscience
|
||||
&& ((SpellAbility) this).getSpellAbilityType() != SpellAbilityType.BASE_ALTERNATE) {
|
||||
for (AlternativeSourceCosts alternativeSourceCosts : controller.getAlternativeSourceCosts()) {
|
||||
if (alternativeSourceCosts.isAvailable(this, game)) {
|
||||
if (alternativeSourceCosts.askToActivateAlternativeCosts(this, game)) {
|
||||
// only one alternative costs may be activated
|
||||
alternativeCostUsed = true;
|
||||
break;
|
||||
}
|
||||
for (AlternativeSourceCosts alternativeSourceCosts : controller.getAlternativeSourceCosts()) {
|
||||
if (alternativeSourceCosts.isAvailable(this, game)) {
|
||||
if (alternativeSourceCosts.askToActivateAlternativeCosts(this, game)) {
|
||||
// only one alternative costs may be activated
|
||||
alternativeCostUsed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -328,8 +328,17 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
return spellCharacteristics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a spell cast event, returns the relevant SpellAbility involved
|
||||
* Currently used to get the characteristics of the spell, specifically
|
||||
* for "can't cast" effects using CAST_SPELL_LATE events
|
||||
*
|
||||
* @param event
|
||||
* @param game
|
||||
* @return SpellAbility of the event
|
||||
*/
|
||||
public static SpellAbility getSpellAbilityFromEvent(GameEvent event, Game game) {
|
||||
if (event.getType() != GameEvent.EventType.CAST_SPELL) {
|
||||
if (event.getType() != GameEvent.EventType.CAST_SPELL && event.getType() != GameEvent.EventType.CAST_SPELL_LATE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -514,14 +514,14 @@ public class ContinuousEffects implements Serializable {
|
|||
|
||||
// usage check: effect must apply for specific ability only, not to full object (example: PLAY_FROM_NOT_OWN_HAND_ZONE)
|
||||
if (type.needAffectedAbility() && affectedAbility == null) {
|
||||
throw new IllegalArgumentException("ERROR, you can't call asThough check to whole object, call it with affected ability instead: " + type);
|
||||
throw new IllegalArgumentException("Wrong code usage: you can't call asThough check to whole object, call it with affected ability instead: " + type);
|
||||
}
|
||||
|
||||
// usage check: effect must apply to full object, not specific ability (example: ATTACK_AS_HASTE)
|
||||
// P.S. In theory a same AsThough effect can be applied to object or to ability, so if you really, really
|
||||
// need it then disable that check or add extra param to AsThoughEffectType like needAffectedAbilityOrFullObject
|
||||
if (!type.needAffectedAbility() && affectedAbility != null) {
|
||||
throw new IllegalArgumentException("ERROR, you can't call AsThough check to affected ability, call it with empty affected ability instead: " + type);
|
||||
throw new IllegalArgumentException("Wrong code usage: you can't call AsThough check to affected ability, call it with empty affected ability instead: " + type);
|
||||
}
|
||||
|
||||
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(type, game);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ package mage.abilities.effects.common;
|
|||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
|
|
@ -45,11 +47,18 @@ public class OpponentsCantCastChosenUntilNextTurnEffect extends ContinuousRuleMo
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
|
||||
if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) {
|
||||
MageObject object = game.getObject(event.getSourceId());
|
||||
return object != null && CardUtil.haveSameNames(object, cardName, game);
|
||||
if (!game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
SpellAbility spellAbility = SpellAbility.getSpellAbilityFromEvent(event, game);
|
||||
if (spellAbility == null) {
|
||||
return false;
|
||||
}
|
||||
Card card = spellAbility.getCharacteristics(game);
|
||||
if (card == null) {
|
||||
return false;
|
||||
}
|
||||
String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
|
||||
return CardUtil.haveSameNames(card, cardName, game);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package mage.abilities.effects.common.asthought;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.*;
|
||||
|
|
@ -47,16 +48,23 @@ public class PlayFromNotOwnHandZoneAllEffect extends AsThoughEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility");
|
||||
}
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
Card card = game.getCard(objectId);
|
||||
if (card != null) {
|
||||
if (affectedAbility instanceof SpellAbility) {
|
||||
card = ((SpellAbility) affectedAbility).getCharacteristics(game);
|
||||
}
|
||||
switch (allowedCaster) {
|
||||
case YOU:
|
||||
if (affectedControllerId != source.getControllerId()) {
|
||||
if (playerId != source.getControllerId()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case OPPONENT:
|
||||
if (!game.getOpponents(source.getControllerId()).contains(affectedControllerId)) {
|
||||
if (!game.getOpponents(source.getControllerId()).contains(playerId)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl {
|
|||
// PLAY_FROM_NOT_OWN_HAND_ZONE must applies to affectedAbility only
|
||||
// If you see it then parent conditional effect must override both applies methods to support different
|
||||
// AsThough effect types in one conditional effect
|
||||
throw new IllegalArgumentException("ERROR, can't call applies method on empty affectedAbility");
|
||||
throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility");
|
||||
}
|
||||
|
||||
// invalid targets
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.keyword.MorphAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.FilterCard;
|
||||
|
|
@ -50,25 +49,19 @@ public class CastAsThoughItHadFlashAllEffect extends AsThoughEffectImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID affectedSpellId, Ability source, UUID affectedControllerId, Game game) {
|
||||
if (anyPlayer || source.isControlledBy(affectedControllerId)) {
|
||||
Card card = game.getCard(affectedSpellId);
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
if (affectedAbility instanceof SpellAbility && (anyPlayer||source.isControlledBy(playerId))) {
|
||||
Card card = ((SpellAbility) affectedAbility).getCharacteristics(game);
|
||||
if (card != null) {
|
||||
//Allow lands with morph to be played at instant speed
|
||||
if (card.isLand(game)) {
|
||||
boolean morphAbility = card.getAbilities().stream().anyMatch(MorphAbility.class::isInstance);
|
||||
if (morphAbility) {
|
||||
Card cardCopy = card.copy();
|
||||
cardCopy.removeAllCardTypes(game);
|
||||
cardCopy.addCardType(game, CardType.CREATURE);
|
||||
return filter.match(cardCopy, affectedControllerId, source, game);
|
||||
}
|
||||
}
|
||||
return filter.match(card, affectedControllerId, source, game);
|
||||
return filter.match(card, playerId, source, game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility");
|
||||
}
|
||||
|
||||
private String setText() {
|
||||
StringBuilder sb = new StringBuilder(anyPlayer ? "any player" : "you");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
|
|
@ -87,6 +88,10 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility");
|
||||
}
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
// main card and all parts are checks in different calls.
|
||||
// two modes:
|
||||
// * can play cards (must check main card and allows any parts)
|
||||
|
|
@ -101,10 +106,15 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
|
|||
if (this.canPlayCardOnly) {
|
||||
// check whole card instead part
|
||||
cardToCheck = cardToCheck.getMainCard();
|
||||
} else if (affectedAbility instanceof SpellAbility) {
|
||||
SpellAbility spell = (SpellAbility) affectedAbility;
|
||||
cardToCheck = spell.getCharacteristics(game);
|
||||
if (spell.getManaCosts().isEmpty()){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// must be you
|
||||
if (!affectedControllerId.equals(source.getControllerId())) {
|
||||
if (!playerId.equals(source.getControllerId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -154,12 +164,7 @@ public class PlayTheTopCardEffect extends AsThoughEffectImpl {
|
|||
}
|
||||
}
|
||||
|
||||
// can't cast without mana cost
|
||||
if (!cardToCheck.isLand(game) && cardToCheck.getManaCost().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// must be correct card
|
||||
return filter.match(cardToCheck, affectedControllerId, source, game);
|
||||
return filter.match(cardToCheck, playerId, source, game);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package mage.abilities.effects.common.cost;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.keyword.MorphAbility;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterCreatureCard;
|
||||
import mage.game.Game;
|
||||
|
||||
public class MorphSpellsCostReductionControllerEffect extends SpellsCostReductionControllerEffect{
|
||||
private static final FilterCreatureCard standardFilter = new FilterCreatureCard("Face-down creature spells");
|
||||
|
||||
public MorphSpellsCostReductionControllerEffect(int amount) {
|
||||
super(standardFilter, amount);
|
||||
}
|
||||
public MorphSpellsCostReductionControllerEffect(FilterCard filter, int amount) {
|
||||
super(filter, amount);
|
||||
}
|
||||
protected MorphSpellsCostReductionControllerEffect(final MorphSpellsCostReductionControllerEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
@Override
|
||||
public MorphSpellsCostReductionControllerEffect copy() {
|
||||
return new MorphSpellsCostReductionControllerEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
if (abilityToModify instanceof MorphAbility) {
|
||||
return super.applies(abilityToModify, source, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package mage.abilities.effects.common.ruleModifying;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
|
|
@ -48,6 +49,10 @@ public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility");
|
||||
}
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
|
||||
// current card's part
|
||||
Card cardToCheck = game.getCard(objectId);
|
||||
if (cardToCheck == null) {
|
||||
|
|
@ -55,13 +60,13 @@ public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
|
|||
}
|
||||
|
||||
// must be you
|
||||
if (!affectedControllerId.equals(source.getControllerId())) {
|
||||
if (!playerId.equals(source.getControllerId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// must be your card
|
||||
Player player = game.getPlayer(cardToCheck.getOwnerId());
|
||||
if (player == null || !player.getId().equals(affectedControllerId)) {
|
||||
if (player == null || !player.getId().equals(playerId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -75,8 +80,10 @@ public class PlayLandsFromGraveyardControllerEffect extends AsThoughEffectImpl {
|
|||
if (!cardToCheck.isLand(game) && cardToCheck.getManaCost().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (affectedAbility instanceof SpellAbility){
|
||||
cardToCheck = ((SpellAbility) affectedAbility).getCharacteristics(game);
|
||||
}
|
||||
// must be correct card
|
||||
return filter.match(cardToCheck, affectedControllerId, source, game);
|
||||
return filter.match(cardToCheck, playerId, source, game);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package mage.abilities.keyword;
|
|||
import mage.MageObject;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.AlternativeSourceCostsImpl;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
|
|
@ -12,13 +12,12 @@ import mage.abilities.costs.mana.GenericManaCost;
|
|||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect;
|
||||
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect.FaceDownType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Rarity;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.EmptyToken;
|
||||
import mage.game.permanent.token.Token;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
|
|
@ -62,8 +61,7 @@ import mage.util.CardUtil;
|
|||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class MorphAbility extends AlternativeSourceCostsImpl {
|
||||
|
||||
public class MorphAbility extends SpellAbility {
|
||||
protected static final String ABILITY_KEYWORD = "Morph";
|
||||
protected static final String ABILITY_KEYWORD_MEGA = "Megamorph";
|
||||
protected static final String REMINDER_TEXT = "You may cast this card face down as a "
|
||||
|
|
@ -75,20 +73,22 @@ public class MorphAbility extends AlternativeSourceCostsImpl {
|
|||
// needed to check activation status, if card changes zone after casting it
|
||||
private final boolean megamorph;
|
||||
|
||||
public MorphAbility(Cost morphCost) {
|
||||
this(morphCost, false);
|
||||
public MorphAbility(Card card, Cost morphCost) {
|
||||
this(card, morphCost, false);
|
||||
}
|
||||
|
||||
public MorphAbility(Cost morphCost, boolean megamorph) {
|
||||
super(megamorph ? ABILITY_KEYWORD_MEGA : ABILITY_KEYWORD, megamorph ? REMINDER_TEXT_MEGA : REMINDER_TEXT, new GenericManaCost(3));
|
||||
public MorphAbility(Card card, Cost morphCost, boolean megamorph) {
|
||||
super(new GenericManaCost(3), card.getName());
|
||||
this.morphCosts = new CostsImpl<>();
|
||||
this.morphCosts.add(morphCost);
|
||||
this.megamorph = megamorph;
|
||||
this.setWorksFaceDown(true);
|
||||
this.setSpellAbilityCastMode(SpellAbilityCastMode.MORPH);
|
||||
this.setSpellAbilityType(SpellAbilityType.BASE_ALTERNATE);
|
||||
Ability ability = new SimpleStaticAbility(new BecomesFaceDownCreatureEffect(
|
||||
morphCosts, (megamorph ? FaceDownType.MEGAMORPHED : FaceDownType.MORPHED)));
|
||||
ability.setWorksFaceDown(true);
|
||||
ability.setRuleVisible(false);
|
||||
this.timing = TimingRule.SORCERY;
|
||||
addSubAbility(ability);
|
||||
}
|
||||
|
||||
|
|
@ -103,28 +103,6 @@ public class MorphAbility extends AlternativeSourceCostsImpl {
|
|||
return new MorphAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean askToActivateAlternativeCosts(Ability ability, Game game) {
|
||||
switch (ability.getAbilityType()) {
|
||||
case SPELL:
|
||||
Spell spell = game.getStack().getSpell(ability.getId());
|
||||
if (spell != null) {
|
||||
spell.setFaceDown(true, game);
|
||||
if (handleActivatingAlternativeCosts(ability, game)) {
|
||||
game.getState().setValue("MorphAbility" + ability.getSourceId(), "activated");
|
||||
spell.getColor(game).setColor(null);
|
||||
game.getState().getCreateMageObjectAttribute(spell.getCard(), game).getSubtype().clear();
|
||||
} else {
|
||||
spell.setFaceDown(false, game);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PLAY_LAND:
|
||||
handleActivatingAlternativeCosts(ability, game);
|
||||
}
|
||||
return isActivated(ability, game);
|
||||
}
|
||||
|
||||
public Costs<Cost> getMorphCosts() {
|
||||
return morphCosts;
|
||||
}
|
||||
|
|
@ -132,8 +110,10 @@ public class MorphAbility extends AlternativeSourceCostsImpl {
|
|||
@Override
|
||||
public String getRule() {
|
||||
boolean isMana = morphCosts.get(0) instanceof ManaCost;
|
||||
return alternativeCost.getName() + (isMana ? " " : "—") +
|
||||
morphCosts.getText() + (isMana ? ' ' : ". ") + alternativeCost.getReminderText();
|
||||
String name = megamorph ? ABILITY_KEYWORD_MEGA : ABILITY_KEYWORD;
|
||||
String reminder = megamorph ? REMINDER_TEXT_MEGA : REMINDER_TEXT;
|
||||
return name + (isMana ? " " : "—") +
|
||||
morphCosts.getText() + (isMana ? ' ' : ". ") + reminder;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -169,4 +149,16 @@ public class MorphAbility extends AlternativeSourceCostsImpl {
|
|||
throw new IllegalArgumentException("Wrong code usage: un-supported targetObject in face down method: " + targetObject.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
public static void setCardToFaceDownCreature(Card targetCard) {
|
||||
targetCard.getPower().setModifiedBaseValue(2);
|
||||
targetCard.getToughness().setModifiedBaseValue(2);
|
||||
targetCard.getAbilities().clear();
|
||||
targetCard.getColor().setColor(new ObjectColor());
|
||||
targetCard.setName("");
|
||||
targetCard.removeAllCardTypes();
|
||||
targetCard.addCardType(CardType.CREATURE);
|
||||
targetCard.getSubtype().clear();
|
||||
targetCard.removeAllSuperTypes();
|
||||
targetCard.getManaCost().clear();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package mage.constants;
|
||||
|
||||
import mage.abilities.keyword.BestowAbility;
|
||||
import mage.abilities.keyword.MorphAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
|
|
@ -12,6 +14,7 @@ public enum SpellAbilityCastMode {
|
|||
MADNESS("Madness"),
|
||||
FLASHBACK("Flashback"),
|
||||
BESTOW("Bestow"),
|
||||
MORPH("Morph"),
|
||||
TRANSFORMED("Transformed", true),
|
||||
DISTURB("Disturb", true),
|
||||
MORE_THAN_MEETS_THE_EYE("More than Meets the Eye", true);
|
||||
|
|
@ -44,12 +47,19 @@ public enum SpellAbilityCastMode {
|
|||
if (this.equals(BESTOW)) {
|
||||
BestowAbility.becomeAura(cardCopy);
|
||||
}
|
||||
if (this.isTransformed){
|
||||
if (this.isTransformed) {
|
||||
Card tmp = card.getSecondCardFace();
|
||||
if (tmp != null) {
|
||||
cardCopy = tmp.copy();
|
||||
}
|
||||
}
|
||||
if (this.equals(MORPH)) {
|
||||
if (cardCopy instanceof Spell) {
|
||||
//Spell doesn't support setName, so make a copy of the card (we're blowing it away anyway)
|
||||
cardCopy = ((Spell) cardCopy).getCard().copy();
|
||||
}
|
||||
MorphAbility.setCardToFaceDownCreature(cardCopy);
|
||||
}
|
||||
return cardCopy;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ package mage.constants;
|
|||
public enum SpellAbilityType {
|
||||
BASE("Basic SpellAbility"),
|
||||
BASE_ALTERNATE("Basic SpellAbility Alternate"), // used for Overload, Flashback to know they must be handled as Alternate casting costs
|
||||
FACE_DOWN_CREATURE("Face down creature"), // used for Lands with Morph to cast as Face Down creature
|
||||
SPLIT("Split SpellAbility"),
|
||||
SPLIT_AFTERMATH("AftermathSplit SpellAbility"),
|
||||
SPLIT_FUSED("Split SpellAbility"),
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
package mage.filter.predicate.card;
|
||||
|
||||
import mage.abilities.keyword.MorphAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum FaceDownCastablePredicate implements Predicate<Card> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Card input, Game game) {
|
||||
// is card able to cast as face down
|
||||
return input.getAbilities(game).containsClass(MorphAbility.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Face-down";
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +58,6 @@ public class Commander extends CommandObjectImpl {
|
|||
abilities.add(spellAbility.copyWithZone(Zone.COMMAND));
|
||||
}
|
||||
break;
|
||||
case FACE_DOWN_CREATURE: // dynamic added spell for alternative cost like cast as face down
|
||||
case SPLICE: // only from hand
|
||||
case SPLIT_AFTERMATH: // only from graveyard
|
||||
// can't use from command zone
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public class PermanentCard extends PermanentImpl {
|
|||
}
|
||||
}
|
||||
if (!goodForBattlefield) {
|
||||
throw new IllegalArgumentException("ERROR, can't create permanent card from split or mdf: " + card.getName());
|
||||
throw new IllegalArgumentException("Wrong code usage: can't create permanent card from split or mdf: " + card.getName());
|
||||
}
|
||||
|
||||
this.card = card;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import mage.MageObject;
|
|||
import mage.Mana;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.*;
|
||||
import mage.abilities.costs.AlternativeSourceCosts;
|
||||
import mage.abilities.costs.mana.ActivationManaAbilityStep;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCosts;
|
||||
|
|
@ -91,6 +90,12 @@ public class Spell extends StackObjectImpl implements Card {
|
|||
this.zoneChangeCounter = affectedCard.getZoneChangeCounter(game); // sync card's ZCC with spell (copy spell settings)
|
||||
this.ability = ability;
|
||||
this.ability.setControllerId(controllerId);
|
||||
|
||||
if (ability.getSpellAbilityCastMode() == SpellAbilityCastMode.MORPH){
|
||||
this.faceDown = true;
|
||||
this.getColor(game).setColor(null);
|
||||
game.getState().getCreateMageObjectAttribute(this.getCard(), game).getSubtype().clear();
|
||||
}
|
||||
if (ability.getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) {
|
||||
// if this spell is going to be a copy, these abilities will be copied in copySpell
|
||||
if (!isCopy) {
|
||||
|
|
@ -182,11 +187,8 @@ public class Spell extends StackObjectImpl implements Card {
|
|||
}
|
||||
|
||||
public String getSpellCastText(Game game) {
|
||||
for (Ability spellAbility : getAbilities()) {
|
||||
if (spellAbility instanceof MorphAbility
|
||||
&& ((AlternativeSourceCosts) spellAbility).isActivated(getSpellAbility(), game)) {
|
||||
return "a card face down";
|
||||
}
|
||||
if (this.getSpellAbility() instanceof MorphAbility) {
|
||||
return "a card face down";
|
||||
}
|
||||
|
||||
if (card instanceof AdventureCardSpell) {
|
||||
|
|
|
|||
|
|
@ -1267,34 +1267,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean playLand(Card card, Game game, boolean ignoreTiming) {
|
||||
// Check for alternate casting possibilities: e.g. land with Morph
|
||||
if (card == null) {
|
||||
return false;
|
||||
}
|
||||
ActivatedAbility playLandAbility = null;
|
||||
boolean foundAlternative = false;
|
||||
for (Ability ability : card.getAbilities(game)) {
|
||||
// if cast for noMana no Alternative costs are allowed
|
||||
if ((ability instanceof AlternativeSourceCosts)
|
||||
|| (ability instanceof OptionalAdditionalSourceCosts)) {
|
||||
foundAlternative = true;
|
||||
}
|
||||
if (ability instanceof PlayLandAbility) {
|
||||
playLandAbility = (ActivatedAbility) ability;
|
||||
}
|
||||
}
|
||||
|
||||
// try alternative cast (face down)
|
||||
if (foundAlternative) {
|
||||
SpellAbility spellAbility = new SpellAbility(null, "",
|
||||
game.getState().getZone(card.getId()), SpellAbilityType.FACE_DOWN_CREATURE);
|
||||
spellAbility.setControllerId(this.getId());
|
||||
spellAbility.setSourceId(card.getId());
|
||||
if (cast(spellAbility, game, false, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (playLandAbility == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue