forked from External/mage
game: improved visible rules of face down cards, removed visible face up cost (part of #10653, #11884)
This commit is contained in:
parent
9ea3356b77
commit
55f1d36695
10 changed files with 128 additions and 58 deletions
|
|
@ -373,14 +373,6 @@ public class CardView extends SimpleCardView {
|
||||||
this.superTypes = new ArrayList<>(card.getSuperType());
|
this.superTypes = new ArrayList<>(card.getSuperType());
|
||||||
this.subTypes = card.getSubtype().copy();
|
this.subTypes = card.getSubtype().copy();
|
||||||
this.rules = new ArrayList<>(card.getRules());
|
this.rules = new ArrayList<>(card.getRules());
|
||||||
|
|
||||||
// additional rules for stack (example: morph ability text)
|
|
||||||
if (sourceCard instanceof Spell) {
|
|
||||||
List<String> extraRules = sourceCard.getSpellAbility().getSpellAbilityCastMode().getAdditionalRulesOnStack();
|
|
||||||
if (extraRules != null) {
|
|
||||||
this.rules.addAll(extraRules);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GUI: enable day/night button to view original face up card
|
// GUI: enable day/night button to view original face up card
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
package org.mage.test.cards.abilities.keywords;
|
package org.mage.test.cards.abilities.keywords;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.SpellAbility;
|
||||||
|
import mage.abilities.common.TurnFaceUpAbility;
|
||||||
import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
|
import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
|
||||||
import mage.constants.EmptyNames;
|
import mage.constants.EmptyNames;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.view.CardView;
|
||||||
import mage.view.GameView;
|
import mage.view.GameView;
|
||||||
import mage.view.PermanentView;
|
import mage.view.PermanentView;
|
||||||
import mage.view.PlayerView;
|
import mage.view.PlayerView;
|
||||||
|
|
@ -13,6 +16,8 @@ import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Most of the face down logic was tested in MorphTest, here are tests for disguise related only
|
* Most of the face down logic was tested in MorphTest, here are tests for disguise related only
|
||||||
*
|
*
|
||||||
|
|
@ -21,7 +26,17 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
public class DisguiseTest extends CardTestPlayerBase {
|
public class DisguiseTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_NormalPlayAndClientData() {
|
public void test_NormalPlay_ClientData_CostRulesVisible() {
|
||||||
|
// it checks rules visible for face down cards, main logic:
|
||||||
|
// - real face up abilities uses special cost;
|
||||||
|
// - it must be hidden from opponent
|
||||||
|
// - so it must be replaced in rules by non-cost versions (e.g. text only)
|
||||||
|
|
||||||
|
String FACE_DOWN_SPELL = "with no text, no name, no subtypes";
|
||||||
|
String FACE_DOWN_TRIGGER = "When ";
|
||||||
|
String FACE_DOWN_FACE_UP = "down permanent face up";
|
||||||
|
|
||||||
|
|
||||||
// {R}{W}
|
// {R}{W}
|
||||||
// Disguise {R/W}{R/W} (You may cast this card face down for {3} as a 2/2 creature with ward {2}.
|
// Disguise {R/W}{R/W} (You may cast this card face down for {3} as a 2/2 creature with ward {2}.
|
||||||
// Turn it face up any time for its disguise cost.)
|
// Turn it face up any time for its disguise cost.)
|
||||||
|
|
@ -38,6 +53,18 @@ public class DisguiseTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
// prepare face down
|
// prepare face down
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dog Walker using Disguise");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dog Walker using Disguise");
|
||||||
|
runCode("face up on stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||||
|
Assert.assertEquals("stack, server - can't find spell", 1, currentGame.getStack().size());
|
||||||
|
SpellAbility spellAbility = (SpellAbility) currentGame.getStack().getFirst().getStackAbility();
|
||||||
|
Assert.assertEquals("stack, server - can't find spell", "Cast Dog Walker using Disguise", spellAbility.getName());
|
||||||
|
CardView spellView = getGameView(playerA).getStack().values().stream().findFirst().orElse(null);
|
||||||
|
Assert.assertNotNull("stack, client: can't find spell", spellView);
|
||||||
|
|
||||||
|
// make sure rules visible
|
||||||
|
assertRuleExist("client side, stack: face down spell - show", spellView.getRules(), FACE_DOWN_SPELL, true);
|
||||||
|
assertRuleExist("client side, stack: face up - hide", spellView.getRules(), FACE_DOWN_FACE_UP, false);
|
||||||
|
assertRuleExist("client side, stack: triggered ability - hide", spellView.getRules(), FACE_DOWN_TRIGGER, false);
|
||||||
|
});
|
||||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
checkPermanentCount("after face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dog Walker", 0);
|
checkPermanentCount("after face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dog Walker", 0);
|
||||||
checkPermanentCount("after face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
checkPermanentCount("after face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||||
|
|
@ -53,9 +80,18 @@ public class DisguiseTest extends CardTestPlayerBase {
|
||||||
Assert.assertEquals("server side: wrong color", "", permanent.getColor(currentGame).toString());
|
Assert.assertEquals("server side: wrong color", "", permanent.getColor(currentGame).toString());
|
||||||
Assert.assertEquals("server side: wrong power", "2", permanent.getPower().toString());
|
Assert.assertEquals("server side: wrong power", "2", permanent.getPower().toString());
|
||||||
Assert.assertEquals("server side: wrong toughness", "2", permanent.getToughness().toString());
|
Assert.assertEquals("server side: wrong toughness", "2", permanent.getToughness().toString());
|
||||||
|
|
||||||
|
// make sure real abilities exists
|
||||||
|
// trigger
|
||||||
Ability ability = permanent.getAbilities(currentGame).stream().filter(a -> a instanceof TurnedFaceUpSourceTriggeredAbility).findFirst().orElse(null);
|
Ability ability = permanent.getAbilities(currentGame).stream().filter(a -> a instanceof TurnedFaceUpSourceTriggeredAbility).findFirst().orElse(null);
|
||||||
Assert.assertNotNull("server side: must have face up triggered ability", ability);
|
Assert.assertNotNull("server side: must have face up triggered ability", ability);
|
||||||
Assert.assertFalse("server side: face up triggered ability must be hidden", ability.getRuleVisible());
|
Assert.assertFalse("server side: face up triggered ability must be hidden", ability.getRuleVisible());
|
||||||
|
// face up
|
||||||
|
ability = permanent.getAbilities(currentGame).stream().filter(a -> a instanceof TurnFaceUpAbility).findFirst().orElse(null);
|
||||||
|
Assert.assertNotNull("server side: must have turn face up ability", ability);
|
||||||
|
String foundRule = permanent.getRules(currentGame).stream().filter(r -> r.contains("{R/W}")).findFirst().orElse(null);
|
||||||
|
// failed here? search BecomesFaceDownCreatureEffect and additionalAbilities
|
||||||
|
Assert.assertNull("server side: turn face up ability with {R/W} cost must be replaced by text only without cost", foundRule);
|
||||||
|
|
||||||
// client side - controller
|
// client side - controller
|
||||||
GameView gameView = getGameView(playerA);
|
GameView gameView = getGameView(playerA);
|
||||||
|
|
@ -69,9 +105,11 @@ public class DisguiseTest extends CardTestPlayerBase {
|
||||||
Assert.assertEquals("client side - controller: wrong color", "", permanentView.getColor().toString());
|
Assert.assertEquals("client side - controller: wrong color", "", permanentView.getColor().toString());
|
||||||
Assert.assertEquals("client side - controller: wrong power", "2", permanentView.getPower());
|
Assert.assertEquals("client side - controller: wrong power", "2", permanentView.getPower());
|
||||||
Assert.assertEquals("client side - controller: wrong toughness", "2", permanentView.getToughness());
|
Assert.assertEquals("client side - controller: wrong toughness", "2", permanentView.getToughness());
|
||||||
// make sure rules hiding works fine
|
// make sure rules visible
|
||||||
Assert.assertTrue("client side - controller: face up triggered ability must be hidden",
|
assertRuleExist("client side, controller: face down spell - show", permanentView.getRules(), FACE_DOWN_SPELL, true);
|
||||||
permanentView.getRules().stream().noneMatch(r -> r.contains("When ")));
|
assertRuleExist("client side, controller: face up - hide", permanentView.getRules(), FACE_DOWN_FACE_UP, false);
|
||||||
|
assertRuleExist("client side, controller: triggered ability - hide", permanentView.getRules(), FACE_DOWN_TRIGGER, false);
|
||||||
|
assertRuleExist("client side, controller: {R/W} cost hide", permanentView.getRules(), "{R/W}", false);
|
||||||
|
|
||||||
// client side - opponent
|
// client side - opponent
|
||||||
gameView = getGameView(playerB);
|
gameView = getGameView(playerB);
|
||||||
|
|
@ -91,9 +129,11 @@ public class DisguiseTest extends CardTestPlayerBase {
|
||||||
Assert.assertEquals("client side - opponent: wrong color", "", permanentView.getColor().toString());
|
Assert.assertEquals("client side - opponent: wrong color", "", permanentView.getColor().toString());
|
||||||
Assert.assertEquals("client side - opponent: wrong power", "2", permanentView.getPower());
|
Assert.assertEquals("client side - opponent: wrong power", "2", permanentView.getPower());
|
||||||
Assert.assertEquals("client side - opponent: wrong toughness", "2", permanentView.getToughness());
|
Assert.assertEquals("client side - opponent: wrong toughness", "2", permanentView.getToughness());
|
||||||
// make sure rules hiding works fine
|
// make sure rules visible
|
||||||
Assert.assertTrue("client side - opponent: face up triggered ability must be hidden",
|
assertRuleExist("client side, opponent: face down spell - show", permanentView.getRules(), FACE_DOWN_SPELL, true);
|
||||||
permanentView.getRules().stream().noneMatch(r -> r.contains("When ")));
|
assertRuleExist("client side, opponent: face up - hide", permanentView.getRules(), FACE_DOWN_FACE_UP, false);
|
||||||
|
assertRuleExist("client side, opponent: triggered ability - hide", permanentView.getRules(), FACE_DOWN_TRIGGER, false);
|
||||||
|
assertRuleExist("client side, opponent: {R/W} cost hide", permanentView.getRules(), "{R/W}", false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// make sure ward works too
|
// make sure ward works too
|
||||||
|
|
@ -124,4 +164,13 @@ public class DisguiseTest extends CardTestPlayerBase {
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
execute();
|
execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertRuleExist(String info, List<String> rules, String searchPart, boolean mustExists) {
|
||||||
|
String foundAbility = rules.stream().filter(r -> r.contains(searchPart)).findFirst().orElse(null);
|
||||||
|
if (mustExists) {
|
||||||
|
Assert.assertTrue(info, foundAbility != null);
|
||||||
|
} else {
|
||||||
|
Assert.assertFalse(info, foundAbility != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,12 @@ import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author levelX2
|
* @author levelX2, JayDi85
|
||||||
*/
|
*/
|
||||||
public class MorphTest extends CardTestPlayerBase {
|
public class MorphTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
// DisguiseTest contains additional rules generation tests for face down
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests if a creature with Morph is cast normal, it behaves as normal
|
* Tests if a creature with Morph is cast normal, it behaves as normal
|
||||||
* creature
|
* creature
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ public class TurnFaceUpAbility extends SpecialAction {
|
||||||
this.addCost(costs);
|
this.addCost(costs);
|
||||||
this.usesStack = false;
|
this.usesStack = false;
|
||||||
this.abilityType = AbilityType.SPECIAL_ACTION;
|
this.abilityType = AbilityType.SPECIAL_ACTION;
|
||||||
this.setRuleVisible(false); // will be made visible only to controller in CardView
|
|
||||||
this.setWorksFaceDown(true);
|
this.setWorksFaceDown(true);
|
||||||
|
this.setRuleVisible(false); // hide in face up, but show in face down view (it will be enabled as default ability)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TurnFaceUpAbility(final TurnFaceUpAbility ability) {
|
protected TurnFaceUpAbility(final TurnFaceUpAbility ability) {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import mage.game.permanent.Permanent;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TODO: must be reworked to use same face down logic as BecomesFaceDownCreatureEffect
|
||||||
|
*
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,14 @@ import mage.MageObject;
|
||||||
import mage.MageObjectReference;
|
import mage.MageObjectReference;
|
||||||
import mage.ObjectColor;
|
import mage.ObjectColor;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.common.TurnFaceUpAbility;
|
import mage.abilities.common.TurnFaceUpAbility;
|
||||||
import mage.abilities.costs.Cost;
|
import mage.abilities.costs.Cost;
|
||||||
import mage.abilities.costs.Costs;
|
import mage.abilities.costs.Costs;
|
||||||
import mage.abilities.costs.CostsImpl;
|
import mage.abilities.costs.CostsImpl;
|
||||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
import mage.abilities.effects.ContinuousEffectImpl;
|
import mage.abilities.effects.ContinuousEffectImpl;
|
||||||
|
import mage.abilities.effects.common.InfoEffect;
|
||||||
import mage.abilities.keyword.WardAbility;
|
import mage.abilities.keyword.WardAbility;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
|
|
@ -29,6 +31,7 @@ import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support different face down types: morph/manifest and disguise/cloak
|
* Support different face down types: morph/manifest and disguise/cloak
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This effect lets the card be a 2/2 face-down creature, with no text, no name,
|
* This effect lets the card be a 2/2 face-down creature, with no text, no name,
|
||||||
* no subtypes, and no mana cost, if it's face down on the battlefield. And it
|
* no subtypes, and no mana cost, if it's face down on the battlefield. And it
|
||||||
|
|
@ -81,15 +84,36 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
this.objectReference = objectReference;
|
this.objectReference = objectReference;
|
||||||
this.zoneChangeCounter = Integer.MIN_VALUE;
|
this.zoneChangeCounter = Integer.MIN_VALUE;
|
||||||
|
|
||||||
// additional abilities
|
// add additional face up and information abilities
|
||||||
// face up
|
|
||||||
if (turnFaceUpCosts != null) {
|
if (turnFaceUpCosts != null) {
|
||||||
|
// face up for all
|
||||||
this.additionalAbilities.add(new TurnFaceUpAbility(turnFaceUpCosts, faceDownType == FaceDownType.MEGAMORPHED));
|
this.additionalAbilities.add(new TurnFaceUpAbility(turnFaceUpCosts, faceDownType == FaceDownType.MEGAMORPHED));
|
||||||
}
|
|
||||||
// ward
|
switch (faceDownType) {
|
||||||
if (faceDownType == FaceDownType.DISGUISED
|
case MORPHED:
|
||||||
|| faceDownType == faceDownType.CLOAKED) {
|
case MEGAMORPHED:
|
||||||
this.additionalAbilities.add(new WardAbility(new ManaCostsImpl<>("{2}")));
|
// face up rules replace for cost hide
|
||||||
|
this.additionalAbilities.add(new SimpleStaticAbility(Zone.ALL, new InfoEffect(
|
||||||
|
"Turn it face up any time for its morph cost."
|
||||||
|
)));
|
||||||
|
break;
|
||||||
|
case DISGUISED:
|
||||||
|
case CLOAKED:
|
||||||
|
// ward
|
||||||
|
this.additionalAbilities.add(new WardAbility(new ManaCostsImpl<>("{2}")));
|
||||||
|
|
||||||
|
// face up rules replace for cost hide
|
||||||
|
this.additionalAbilities.add(new SimpleStaticAbility(Zone.ALL, new InfoEffect(
|
||||||
|
"Turn it face up any time for its disguise/cloaked cost."
|
||||||
|
)));
|
||||||
|
break;
|
||||||
|
case MANUAL:
|
||||||
|
case MANIFESTED:
|
||||||
|
// no face up abilities
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Un-supported face down type: " + faceDownType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
staticText = "{this} becomes a 2/2 face-down creature, with no text, no name, no subtypes, and no mana cost";
|
staticText = "{this} becomes a 2/2 face-down creature, with no text, no name, no subtypes, and no mana cost";
|
||||||
|
|
@ -216,16 +240,17 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
// If a card with morph is manifested, its controller may turn that card face up using
|
// If a card with morph is manifested, its controller may turn that card face up using
|
||||||
// either the procedure described in rule 702.36e to turn a face-down permanent with morph face up
|
// either the procedure described in rule 702.36e to turn a face-down permanent with morph face up
|
||||||
// or the procedure described above to turn a manifested permanent face up.
|
// or the procedure described above to turn a manifested permanent face up.
|
||||||
//
|
|
||||||
// so keep all tune face up abilities and other face down compatible
|
// keep face down abilities active, but hide it from rules description
|
||||||
if (ability.getWorksFaceDown()) {
|
if (ability.getWorksFaceDown()) {
|
||||||
// keep face down abilities active, but hide it from rules description
|
|
||||||
// example: When Dog Walker is turned face up, create two tapped 1/1 white Dog creature tokens
|
// example: When Dog Walker is turned face up, create two tapped 1/1 white Dog creature tokens
|
||||||
ability.setRuleVisible(false);
|
ability.setRuleVisible(false);
|
||||||
|
|
||||||
// but do not hide default ability (becomes a 2/2 face-down creature)
|
// becomes a 2/2 face-down creature - it hides a real ability too, but adds fake rule, see
|
||||||
if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) {
|
if (!ability.getEffects().isEmpty()) {
|
||||||
if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) {
|
if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) {
|
||||||
|
// enable for stack
|
||||||
ability.setRuleVisible(true);
|
ability.setRuleVisible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +261,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
abilitiesToRemove.add(ability);
|
abilitiesToRemove.add(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add additional abilities like face up
|
// add additional abilities like face up (real ability hidden and duplicated with information without cost data)
|
||||||
if (object instanceof Permanent) {
|
if (object instanceof Permanent) {
|
||||||
// as permanent
|
// as permanent
|
||||||
Permanent permanentObject = (Permanent) object;
|
Permanent permanentObject = (Permanent) object;
|
||||||
|
|
@ -244,7 +269,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
if (additionalAbilities != null) {
|
if (additionalAbilities != null) {
|
||||||
additionalAbilities.forEach(blueprintAbility -> {
|
additionalAbilities.forEach(blueprintAbility -> {
|
||||||
Ability newAbility = blueprintAbility.copy();
|
Ability newAbility = blueprintAbility.copy();
|
||||||
newAbility.setRuleVisible(true);
|
newAbility.setRuleVisible(CardUtil.isInformationAbility(newAbility));
|
||||||
permanentObject.addAbility(newAbility, sourceId, game);
|
permanentObject.addAbility(newAbility, sourceId, game);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -255,7 +280,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
if (additionalAbilities != null) {
|
if (additionalAbilities != null) {
|
||||||
additionalAbilities.forEach(blueprintAbility -> {
|
additionalAbilities.forEach(blueprintAbility -> {
|
||||||
Ability newAbility = blueprintAbility.copy();
|
Ability newAbility = blueprintAbility.copy();
|
||||||
newAbility.setRuleVisible(true);
|
newAbility.setRuleVisible(CardUtil.isInformationAbility(newAbility));
|
||||||
cardObject.addAbility(newAbility);
|
cardObject.addAbility(newAbility);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,9 +100,9 @@ public class DisguiseAbility extends SpellAbility {
|
||||||
@Override
|
@Override
|
||||||
public String getRule() {
|
public String getRule() {
|
||||||
boolean isMana = disguiseCosts.get(0) instanceof ManaCost;
|
boolean isMana = disguiseCosts.get(0) instanceof ManaCost;
|
||||||
|
String costInfo = this.disguiseCosts.getText() + (isMana ? " " : ". ");
|
||||||
return ABILITY_KEYWORD + (isMana ? " " : "—")
|
return ABILITY_KEYWORD + (isMana ? " " : "—")
|
||||||
+ this.disguiseCosts.getText()
|
+ costInfo
|
||||||
+ (isMana ? ' ' : ". ")
|
|
||||||
+ " <i>(" + REMINDER_TEXT + ")</i>";
|
+ " <i>(" + REMINDER_TEXT + ")</i>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,6 @@ import mage.cards.Card;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.stack.Spell;
|
import mage.game.stack.Spell;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
|
|
@ -20,18 +17,13 @@ public enum SpellAbilityCastMode {
|
||||||
FLASHBACK("Flashback"),
|
FLASHBACK("Flashback"),
|
||||||
BESTOW("Bestow"),
|
BESTOW("Bestow"),
|
||||||
PROTOTYPE("Prototype"),
|
PROTOTYPE("Prototype"),
|
||||||
MORPH("Morph", false, true, SpellAbilityCastMode.MORPH_ADDITIONAL_RULE),
|
MORPH("Morph", false, true),
|
||||||
MEGAMORPH("Megamorph", false, true, SpellAbilityCastMode.MORPH_ADDITIONAL_RULE),
|
MEGAMORPH("Megamorph", false, true),
|
||||||
DISGUISE("Disguise", false, true, SpellAbilityCastMode.DISGUISE_ADDITIONAL_RULE),
|
DISGUISE("Disguise", false, true),
|
||||||
TRANSFORMED("Transformed", true),
|
TRANSFORMED("Transformed", true),
|
||||||
DISTURB("Disturb", true),
|
DISTURB("Disturb", true),
|
||||||
MORE_THAN_MEETS_THE_EYE("More than Meets the Eye", true);
|
MORE_THAN_MEETS_THE_EYE("More than Meets the Eye", true);
|
||||||
|
|
||||||
private static final String MORPH_ADDITIONAL_RULE = "You may cast this card as a 2/2 face-down creature, with no text,"
|
|
||||||
+ " no name, no subtypes, and no mana cost by paying {3} rather than paying its mana cost.";
|
|
||||||
private static final String DISGUISE_ADDITIONAL_RULE = "You may cast this card face down for {3} as a 2/2 creature with "
|
|
||||||
+ "ward {2}. Turn it face up any time for its disguise cost.";
|
|
||||||
|
|
||||||
private final String text;
|
private final String text;
|
||||||
|
|
||||||
// should the cast mode use the second face?
|
// should the cast mode use the second face?
|
||||||
|
|
@ -39,10 +31,6 @@ public enum SpellAbilityCastMode {
|
||||||
|
|
||||||
private final boolean isFaceDown;
|
private final boolean isFaceDown;
|
||||||
|
|
||||||
// use it to add additional info in stack object cause face down has nothing
|
|
||||||
// TODO: is it possible to use InfoEffect or CardHint instead that?
|
|
||||||
private final List<String> additionalRulesOnStack;
|
|
||||||
|
|
||||||
public boolean isTransformed() {
|
public boolean isTransformed() {
|
||||||
return this.isTransformed;
|
return this.isTransformed;
|
||||||
}
|
}
|
||||||
|
|
@ -52,24 +40,19 @@ public enum SpellAbilityCastMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
SpellAbilityCastMode(String text, boolean isTransformed) {
|
SpellAbilityCastMode(String text, boolean isTransformed) {
|
||||||
this(text, isTransformed, false, null);
|
this(text, isTransformed, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpellAbilityCastMode(String text, boolean isTransformed, boolean isFaceDown, String additionalRulesOnStack) {
|
SpellAbilityCastMode(String text, boolean isTransformed, boolean isFaceDown) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.isTransformed = isTransformed;
|
this.isTransformed = isTransformed;
|
||||||
this.isFaceDown = isFaceDown;
|
this.isFaceDown = isFaceDown;
|
||||||
this.additionalRulesOnStack = additionalRulesOnStack == null ? null : Collections.singletonList(additionalRulesOnStack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFaceDown() {
|
public boolean isFaceDown() {
|
||||||
return this.isFaceDown;
|
return this.isFaceDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getAdditionalRulesOnStack() {
|
|
||||||
return additionalRulesOnStack;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return text;
|
return text;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import mage.abilities.keyword.special.JohanVigilanceAbility;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.filter.predicate.Predicate;
|
import mage.filter.predicate.Predicate;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
@ -43,11 +44,18 @@ public enum NoAbilityPredicate implements Predicate<MageObject> {
|
||||||
// (2007-05-01)
|
// (2007-05-01)
|
||||||
|
|
||||||
for (Ability ability : abilities) {
|
for (Ability ability : abilities) {
|
||||||
|
// ignore inner face down abilities like turn up and becomes creature
|
||||||
if (ability.getWorksFaceDown()) {
|
if (ability.getWorksFaceDown()) {
|
||||||
// inner face down abilities like turn up and becomes creature
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!Objects.equals(ability.getClass(), SpellAbility.class) && !ability.getClass().equals(JohanVigilanceAbility.class)) {
|
|
||||||
|
// ignore information abilities
|
||||||
|
if (CardUtil.isInformationAbility(ability)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(ability.getClass(), SpellAbility.class)
|
||||||
|
&& !ability.getClass().equals(JohanVigilanceAbility.class)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import mage.abilities.dynamicvalue.common.SavedDamageValue;
|
||||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||||
import mage.abilities.effects.ContinuousEffect;
|
import mage.abilities.effects.ContinuousEffect;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
|
import mage.abilities.effects.common.InfoEffect;
|
||||||
import mage.abilities.effects.common.asthought.CanPlayCardControllerEffect;
|
import mage.abilities.effects.common.asthought.CanPlayCardControllerEffect;
|
||||||
import mage.abilities.effects.common.asthought.YouMaySpendManaAsAnyColorToCastTargetEffect;
|
import mage.abilities.effects.common.asthought.YouMaySpendManaAsAnyColorToCastTargetEffect;
|
||||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||||
|
|
@ -2237,4 +2238,12 @@ public final class CardUtil {
|
||||||
public static boolean canShowAsControlled(Card card, UUID createdForPlayer) {
|
public static boolean canShowAsControlled(Card card, UUID createdForPlayer) {
|
||||||
return card.getControllerOrOwnerId().equals(createdForPlayer);
|
return card.getControllerOrOwnerId().equals(createdForPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ability used for information only, e.g. adds additional rule texts
|
||||||
|
*/
|
||||||
|
public static boolean isInformationAbility(Ability ability) {
|
||||||
|
return !ability.getEffects().isEmpty()
|
||||||
|
&& ability.getEffects().stream().allMatch(e -> e instanceof InfoEffect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue