mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 10:40:06 -08:00
[WHO] implemented Disguise ability (new face down type, ability and token image, #10653)
This commit is contained in:
parent
ab787a2b8b
commit
9ea3356b77
16 changed files with 313 additions and 41 deletions
|
|
@ -31,6 +31,7 @@ public class PermanentView extends CardView {
|
||||||
private final boolean controlled;
|
private final boolean controlled;
|
||||||
private final UUID attachedTo;
|
private final UUID attachedTo;
|
||||||
private final boolean morphed;
|
private final boolean morphed;
|
||||||
|
private final boolean disguised;
|
||||||
private final boolean manifested;
|
private final boolean manifested;
|
||||||
private final boolean attachedToPermanent;
|
private final boolean attachedToPermanent;
|
||||||
// If this card is attached to a permanent which is controlled by a player other than the one which controls this permanent
|
// If this card is attached to a permanent which is controlled by a player other than the one which controls this permanent
|
||||||
|
|
@ -44,6 +45,7 @@ public class PermanentView extends CardView {
|
||||||
this.phasedIn = permanent.isPhasedIn();
|
this.phasedIn = permanent.isPhasedIn();
|
||||||
this.summoningSickness = permanent.hasSummoningSickness();
|
this.summoningSickness = permanent.hasSummoningSickness();
|
||||||
this.morphed = permanent.isMorphed();
|
this.morphed = permanent.isMorphed();
|
||||||
|
this.disguised = permanent.isDisguised();
|
||||||
this.manifested = permanent.isManifested();
|
this.manifested = permanent.isManifested();
|
||||||
this.damage = permanent.getDamage();
|
this.damage = permanent.getDamage();
|
||||||
this.attachments = new ArrayList<>(permanent.getAttachments());
|
this.attachments = new ArrayList<>(permanent.getAttachments());
|
||||||
|
|
@ -139,6 +141,7 @@ public class PermanentView extends CardView {
|
||||||
this.nameController = permanentView.nameController;
|
this.nameController = permanentView.nameController;
|
||||||
this.attachedTo = permanentView.attachedTo;
|
this.attachedTo = permanentView.attachedTo;
|
||||||
this.morphed = permanentView.morphed;
|
this.morphed = permanentView.morphed;
|
||||||
|
this.disguised = permanentView.disguised;
|
||||||
this.manifested = permanentView.manifested;
|
this.manifested = permanentView.manifested;
|
||||||
this.attachedToPermanent = permanentView.attachedToPermanent;
|
this.attachedToPermanent = permanentView.attachedToPermanent;
|
||||||
this.attachedControllerDiffers = permanentView.attachedControllerDiffers;
|
this.attachedControllerDiffers = permanentView.attachedControllerDiffers;
|
||||||
|
|
@ -212,6 +215,10 @@ public class PermanentView extends CardView {
|
||||||
return morphed;
|
return morphed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDisguised() {
|
||||||
|
return disguised;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isManifested() {
|
public boolean isManifested() {
|
||||||
return manifested;
|
return manifested;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,8 @@ class VesuvanShapeshifterFaceDownEffect extends OneShotEffect {
|
||||||
|
|
||||||
permanent.turnFaceDown(source, game, source.getControllerId());
|
permanent.turnFaceDown(source, game, source.getControllerId());
|
||||||
permanent.setManifested(false);
|
permanent.setManifested(false);
|
||||||
permanent.setMorphed(true);
|
permanent.setDisguised(false);
|
||||||
|
permanent.setMorphed(true); // cause it morph card TODO: smells bad
|
||||||
return permanent.isFaceDown(game);
|
return permanent.isFaceDown(game);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public final class MurdersAtKarlovManor extends ExpansionSet {
|
public final class MurdersAtKarlovManor extends ExpansionSet {
|
||||||
|
|
||||||
private static final List<String> unfinished = Arrays.asList("Alley Assailant", "Aurelia's Vindicator", "Basilica Stalker", "Bolrac-Clan Basher", "Branch of Vitu-Ghazi", "Bubble Smuggler", "Concealed Weapon", "Coveted Falcon", "Crowd-Control Warden", "Culvert Ambusher", "Defenestrated Phantom", "Dog Walker", "Essence of Antiquity", "Exit Specialist", "Expose the Culprit", "Faerie Snoop", "Flourishing Bloom-Kin", "Forum Familiar", "Fugitive Codebreaker", "Gadget Technician", "Granite Witness", "Greenbelt Radical", "Hunted Bonebrute", "Lumbering Laundry", "Mistway Spy", "Museum Nightwatch", "Nervous Gardener", "Nightdrinker Moroii", "Offender at Large", "Pyrotechnic Performer", "Rakish Scoundrel", "Riftburst Hellion", "Sanguine Savior", "Shady Informant", "Undercover Crocodelf", "Unyielding Gatekeeper", "Vengeful Creeper");
|
private static final List<String> unfinished = Arrays.asList("Alley Assailant", "Aurelia's Vindicator", "Basilica Stalker", "Bolrac-Clan Basher", "Branch of Vitu-Ghazi", "Bubble Smuggler", "Concealed Weapon", "Coveted Falcon", "Crowd-Control Warden", "Culvert Ambusher", "Defenestrated Phantom", "Essence of Antiquity", "Exit Specialist", "Expose the Culprit", "Faerie Snoop", "Flourishing Bloom-Kin", "Forum Familiar", "Fugitive Codebreaker", "Gadget Technician", "Granite Witness", "Greenbelt Radical", "Hunted Bonebrute", "Lumbering Laundry", "Mistway Spy", "Museum Nightwatch", "Nervous Gardener", "Nightdrinker Moroii", "Offender at Large", "Pyrotechnic Performer", "Rakish Scoundrel", "Riftburst Hellion", "Sanguine Savior", "Shady Informant", "Undercover Crocodelf", "Unyielding Gatekeeper", "Vengeful Creeper");
|
||||||
|
|
||||||
private static final MurdersAtKarlovManor instance = new MurdersAtKarlovManor();
|
private static final MurdersAtKarlovManor instance = new MurdersAtKarlovManor();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
package org.mage.test.cards.abilities.keywords;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
|
||||||
|
import mage.constants.EmptyNames;
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.view.GameView;
|
||||||
|
import mage.view.PermanentView;
|
||||||
|
import mage.view.PlayerView;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Most of the face down logic was tested in MorphTest, here are tests for disguise related only
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class DisguiseTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_NormalPlayAndClientData() {
|
||||||
|
// {R}{W}
|
||||||
|
// 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.)
|
||||||
|
// When Dog Walker is turned face up, create two tapped 1/1 white Dog creature tokens.
|
||||||
|
// Ward {2}. <i>(Whenever it becomes the target of a spell or ability an opponent controls, counter it unless that player pays {2}.)
|
||||||
|
addCard(Zone.HAND, playerA, "Dog Walker@dog");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3 + 2);
|
||||||
|
//
|
||||||
|
addCard(Zone.HAND, playerB, "Lightning Bolt");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
|
||||||
|
|
||||||
|
|
||||||
|
checkPermanentCount("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dog Walker", 0);
|
||||||
|
|
||||||
|
// prepare face down
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dog Walker using Disguise");
|
||||||
|
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, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||||
|
runCode("after face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||||
|
// server side
|
||||||
|
Permanent permanent = currentGame.getBattlefield().getAllPermanents()
|
||||||
|
.stream()
|
||||||
|
.filter(Permanent::isDisguised)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
Assert.assertNotNull("server side: can't find disguised permanent", permanent);
|
||||||
|
Assert.assertEquals("server side: wrong name", EmptyNames.FACE_DOWN_CREATURE.toString(), permanent.getName());
|
||||||
|
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 toughness", "2", permanent.getToughness().toString());
|
||||||
|
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.assertFalse("server side: face up triggered ability must be hidden", ability.getRuleVisible());
|
||||||
|
|
||||||
|
// client side - controller
|
||||||
|
GameView gameView = getGameView(playerA);
|
||||||
|
PermanentView permanentView = gameView.getMyPlayer().getBattlefield().values()
|
||||||
|
.stream()
|
||||||
|
.filter(PermanentView::isDisguised)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
Assert.assertNotNull("client side - controller: can't find disguised permanent", permanentView);
|
||||||
|
Assert.assertEquals("client side - controller: wrong name", "Disguise: Dog Walker", permanentView.getName());
|
||||||
|
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 toughness", "2", permanentView.getToughness());
|
||||||
|
// make sure rules hiding works fine
|
||||||
|
Assert.assertTrue("client side - controller: face up triggered ability must be hidden",
|
||||||
|
permanentView.getRules().stream().noneMatch(r -> r.contains("When ")));
|
||||||
|
|
||||||
|
// client side - opponent
|
||||||
|
gameView = getGameView(playerB);
|
||||||
|
PlayerView playerView = gameView.getPlayers()
|
||||||
|
.stream()
|
||||||
|
.filter(p -> p.getName().equals(playerA.getName()))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
Assert.assertNotNull(playerView);
|
||||||
|
permanentView = playerView.getBattlefield().values()
|
||||||
|
.stream()
|
||||||
|
.filter(PermanentView::isDisguised)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
Assert.assertNotNull("client side - opponent: can't find disguised permanent", permanentView);
|
||||||
|
Assert.assertEquals("client side - opponent: wrong name", "Disguise", permanentView.getName());
|
||||||
|
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 toughness", "2", permanentView.getToughness());
|
||||||
|
// make sure rules hiding works fine
|
||||||
|
Assert.assertTrue("client side - opponent: face up triggered ability must be hidden",
|
||||||
|
permanentView.getRules().stream().noneMatch(r -> r.contains("When ")));
|
||||||
|
});
|
||||||
|
|
||||||
|
// make sure ward works too
|
||||||
|
castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Lightning Bolt");
|
||||||
|
addTarget(playerB, "@dog"); // target face down card by alias
|
||||||
|
setChoice(playerB, true); // try ward pay but no mana, so bolt will be fizzled
|
||||||
|
|
||||||
|
// do face up and generate tokens
|
||||||
|
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{R/W}{R/W}: Turn");
|
||||||
|
waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN, playerA);
|
||||||
|
checkPermanentCount("after face up", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Dog Walker", 1);
|
||||||
|
checkPermanentCount("after face up", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||||
|
checkPermanentCount("after face up", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Dog Token", 2);
|
||||||
|
runCode("after face up", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||||
|
Permanent permanent = currentGame.getBattlefield().getAllPermanents()
|
||||||
|
.stream()
|
||||||
|
.filter(p -> p.getName().equals("Dog Walker"))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
Assert.assertNotNull("server side: can't find normal permanent", permanent);
|
||||||
|
Assert.assertEquals("server side: wrong name", "Dog Walker", permanent.getName());
|
||||||
|
Assert.assertEquals("server side: wrong color", "WR", permanent.getColor(currentGame).toString());
|
||||||
|
Assert.assertEquals("server side: wrong power", "3", permanent.getPower().toString());
|
||||||
|
Assert.assertEquals("server side: wrong toughness", "1", permanent.getToughness().toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -429,6 +429,7 @@ public abstract class AbilityImpl implements Ability {
|
||||||
case BESTOW:
|
case BESTOW:
|
||||||
case MORPH:
|
case MORPH:
|
||||||
case MEGAMORPH:
|
case MEGAMORPH:
|
||||||
|
case DISGUISE:
|
||||||
// from Snapcaster Mage:
|
// from Snapcaster Mage:
|
||||||
// If you cast a spell from a graveyard using its flashback ability, you can't pay other alternative costs
|
// 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)
|
// (such as that of Foil). (2018-12-07)
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl {
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
for (Ability ability : card.getAbilities(game)) {
|
for (Ability ability : card.getAbilities(game)) {
|
||||||
if (ability instanceof MorphAbility) {
|
if (ability instanceof MorphAbility) {
|
||||||
this.turnFaceUpAbilityMap.put(card.getId(), new TurnFaceUpAbility(((MorphAbility) ability).getMorphCosts()));
|
this.turnFaceUpAbilityMap.put(card.getId(), new TurnFaceUpAbility(((MorphAbility) ability).getFaceUpCosts()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ 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.effects.ContinuousEffectImpl;
|
import mage.abilities.effects.ContinuousEffectImpl;
|
||||||
|
import mage.abilities.keyword.WardAbility;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.repository.TokenInfo;
|
import mage.cards.repository.TokenInfo;
|
||||||
|
|
@ -26,9 +28,11 @@ import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Support different face down types: morph/manifest and disguise/cloak
|
||||||
|
* <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
|
||||||
* adds the a TurnFaceUpAbility ability.
|
* adds the TurnFaceUpAbility and other additional abilities
|
||||||
* <p>
|
* <p>
|
||||||
* Warning, if a card has multiple face down abilities then keep only one face up cost
|
* Warning, if a card has multiple face down abilities then keep only one face up cost
|
||||||
* Example: Mischievous Quanar
|
* Example: Mischievous Quanar
|
||||||
|
|
@ -51,7 +55,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int zoneChangeCounter;
|
protected int zoneChangeCounter;
|
||||||
protected Ability turnFaceUpAbility = null;
|
protected List<Ability> additionalAbilities = new ArrayList<>();
|
||||||
protected MageObjectReference objectReference = null;
|
protected MageObjectReference objectReference = null;
|
||||||
protected boolean foundPermanent;
|
protected boolean foundPermanent;
|
||||||
protected FaceDownType faceDownType;
|
protected FaceDownType faceDownType;
|
||||||
|
|
@ -76,9 +80,18 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
super(duration, Layer.CopyEffects_1, SubLayer.FaceDownEffects_1b, Outcome.BecomeCreature);
|
super(duration, Layer.CopyEffects_1, SubLayer.FaceDownEffects_1b, Outcome.BecomeCreature);
|
||||||
this.objectReference = objectReference;
|
this.objectReference = objectReference;
|
||||||
this.zoneChangeCounter = Integer.MIN_VALUE;
|
this.zoneChangeCounter = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
// additional abilities
|
||||||
|
// face up
|
||||||
if (turnFaceUpCosts != null) {
|
if (turnFaceUpCosts != null) {
|
||||||
this.turnFaceUpAbility = new TurnFaceUpAbility(turnFaceUpCosts, faceDownType == FaceDownType.MEGAMORPHED);
|
this.additionalAbilities.add(new TurnFaceUpAbility(turnFaceUpCosts, faceDownType == FaceDownType.MEGAMORPHED));
|
||||||
}
|
}
|
||||||
|
// ward
|
||||||
|
if (faceDownType == FaceDownType.DISGUISED
|
||||||
|
|| faceDownType == faceDownType.CLOAKED) {
|
||||||
|
this.additionalAbilities.add(new WardAbility(new ManaCostsImpl<>("{2}")));
|
||||||
|
}
|
||||||
|
|
||||||
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";
|
||||||
foundPermanent = false;
|
foundPermanent = false;
|
||||||
this.faceDownType = faceDownType;
|
this.faceDownType = faceDownType;
|
||||||
|
|
@ -87,9 +100,9 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
protected BecomesFaceDownCreatureEffect(final BecomesFaceDownCreatureEffect effect) {
|
protected BecomesFaceDownCreatureEffect(final BecomesFaceDownCreatureEffect effect) {
|
||||||
super(effect);
|
super(effect);
|
||||||
this.zoneChangeCounter = effect.zoneChangeCounter;
|
this.zoneChangeCounter = effect.zoneChangeCounter;
|
||||||
if (effect.turnFaceUpAbility != null) {
|
effect.additionalAbilities.forEach(ability -> {
|
||||||
this.turnFaceUpAbility = effect.turnFaceUpAbility.copy();
|
this.additionalAbilities.add(ability.copy());
|
||||||
}
|
});
|
||||||
this.objectReference = effect.objectReference;
|
this.objectReference = effect.objectReference;
|
||||||
this.foundPermanent = effect.foundPermanent;
|
this.foundPermanent = effect.foundPermanent;
|
||||||
this.faceDownType = effect.faceDownType;
|
this.faceDownType = effect.faceDownType;
|
||||||
|
|
@ -139,27 +152,33 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
foundPermanent = true;
|
foundPermanent = true;
|
||||||
switch (faceDownType) {
|
switch (faceDownType) {
|
||||||
case MANIFESTED:
|
case MANIFESTED:
|
||||||
case MANUAL: // sets manifested image
|
case MANUAL: // sets manifested image // TODO: wtf
|
||||||
permanent.setManifested(true);
|
permanent.setManifested(true);
|
||||||
break;
|
break;
|
||||||
case MORPHED:
|
case MORPHED:
|
||||||
case MEGAMORPHED:
|
case MEGAMORPHED:
|
||||||
permanent.setMorphed(true);
|
permanent.setMorphed(true);
|
||||||
break;
|
break;
|
||||||
|
case DISGUISED:
|
||||||
|
permanent.setDisguised(true);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException("FaceDownType not yet supported: " + faceDownType);
|
throw new UnsupportedOperationException("FaceDownType not yet supported: " + faceDownType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
makeFaceDownObject(game, source.getSourceId(), permanent, faceDownType, this.turnFaceUpAbility);
|
makeFaceDownObject(game, source.getSourceId(), permanent, faceDownType, this.additionalAbilities);
|
||||||
} else if (duration == Duration.Custom && foundPermanent) {
|
} else if (duration == Duration.Custom && foundPermanent) {
|
||||||
discard();
|
discard();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: implement multiple face down types?!
|
||||||
public static FaceDownType findFaceDownType(Game game, Permanent permanent) {
|
public static FaceDownType findFaceDownType(Game game, Permanent permanent) {
|
||||||
if (permanent.isMorphed()) {
|
if (permanent.isMorphed()) {
|
||||||
return BecomesFaceDownCreatureEffect.FaceDownType.MORPHED;
|
return BecomesFaceDownCreatureEffect.FaceDownType.MORPHED;
|
||||||
|
} else if (permanent.isDisguised()) {
|
||||||
|
return BecomesFaceDownCreatureEffect.FaceDownType.DISGUISED;
|
||||||
} else if (permanent.isManifested()) {
|
} else if (permanent.isManifested()) {
|
||||||
return BecomesFaceDownCreatureEffect.FaceDownType.MANIFESTED;
|
return BecomesFaceDownCreatureEffect.FaceDownType.MANIFESTED;
|
||||||
} else if (permanent.isFaceDown(game)) {
|
} else if (permanent.isFaceDown(game)) {
|
||||||
|
|
@ -172,7 +191,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
/**
|
/**
|
||||||
* Convert any object (card, token) to face down (remove/hide all face up information and make it a 2/2 creature)
|
* Convert any object (card, token) to face down (remove/hide all face up information and make it a 2/2 creature)
|
||||||
*/
|
*/
|
||||||
public static void makeFaceDownObject(Game game, UUID sourceId, MageObject object, FaceDownType faceDownType, Ability turnFaceUpAbility) {
|
public static void makeFaceDownObject(Game game, UUID sourceId, MageObject object, FaceDownType faceDownType, List<Ability> additionalAbilities) {
|
||||||
String originalObjectInfo = object.toString();
|
String originalObjectInfo = object.toString();
|
||||||
|
|
||||||
// warning, it's a direct changes to the object (without game state, so no game param here)
|
// warning, it's a direct changes to the object (without game state, so no game param here)
|
||||||
|
|
@ -200,38 +219,45 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
//
|
//
|
||||||
// so keep all tune face up abilities and other face down compatible
|
// so keep all tune face up abilities and other face down compatible
|
||||||
if (ability.getWorksFaceDown()) {
|
if (ability.getWorksFaceDown()) {
|
||||||
// only face up abilities hidden by default (see below), so no needs in setRuleVisible
|
// keep face down abilities active, but hide it from rules description
|
||||||
//ability.setRuleVisible(true);
|
// example: When Dog Walker is turned face up, create two tapped 1/1 white Dog creature tokens
|
||||||
|
ability.setRuleVisible(false);
|
||||||
|
|
||||||
|
// but do not hide default ability (becomes a 2/2 face-down creature)
|
||||||
|
if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) {
|
||||||
|
if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) {
|
||||||
|
ability.setRuleVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) {
|
// all other can be removed
|
||||||
if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
abilitiesToRemove.add(ability);
|
abilitiesToRemove.add(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add face up abilities
|
// add additional abilities like face up
|
||||||
// TODO: add here all possible face up like morph/disguis, manifest/cloak?
|
|
||||||
if (object instanceof Permanent) {
|
if (object instanceof Permanent) {
|
||||||
// as permanent
|
// as permanent
|
||||||
Permanent permanentObject = (Permanent) object;
|
Permanent permanentObject = (Permanent) object;
|
||||||
permanentObject.removeAbilities(abilitiesToRemove, sourceId, game);
|
permanentObject.removeAbilities(abilitiesToRemove, sourceId, game);
|
||||||
if (turnFaceUpAbility != null) {
|
if (additionalAbilities != null) {
|
||||||
Ability faceUp = turnFaceUpAbility.copy();
|
additionalAbilities.forEach(blueprintAbility -> {
|
||||||
faceUp.setRuleVisible(true);
|
Ability newAbility = blueprintAbility.copy();
|
||||||
permanentObject.addAbility(faceUp, sourceId, game);
|
newAbility.setRuleVisible(true);
|
||||||
|
permanentObject.addAbility(newAbility, sourceId, game);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (object instanceof CardImpl) {
|
} else if (object instanceof CardImpl) {
|
||||||
// as card
|
// as card
|
||||||
CardImpl cardObject = (CardImpl) object;
|
CardImpl cardObject = (CardImpl) object;
|
||||||
cardObject.getAbilities().removeAll(abilitiesToRemove);
|
cardObject.getAbilities().removeAll(abilitiesToRemove);
|
||||||
if (turnFaceUpAbility != null) {
|
if (additionalAbilities != null) {
|
||||||
Ability faceUp = turnFaceUpAbility.copy();
|
additionalAbilities.forEach(blueprintAbility -> {
|
||||||
faceUp.setRuleVisible(true);
|
Ability newAbility = blueprintAbility.copy();
|
||||||
cardObject.addAbility(faceUp);
|
newAbility.setRuleVisible(true);
|
||||||
|
cardObject.addAbility(newAbility);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,15 +273,15 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
case MEGAMORPHED:
|
case MEGAMORPHED:
|
||||||
tokenName = TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_MEGAMORPH;
|
tokenName = TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_MEGAMORPH;
|
||||||
break;
|
break;
|
||||||
|
case DISGUISED:
|
||||||
|
tokenName = TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_DISGUISE;
|
||||||
|
break;
|
||||||
case MANIFESTED:
|
case MANIFESTED:
|
||||||
tokenName = TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_MANIFEST;
|
tokenName = TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_MANIFEST;
|
||||||
break;
|
break;
|
||||||
case CLOAKED:
|
case CLOAKED:
|
||||||
tokenName = "TODO-CLOAKED";
|
tokenName = "TODO-CLOAKED";
|
||||||
break;
|
break;
|
||||||
case DISGUISED:
|
|
||||||
tokenName = "TODO-DISGUISED";
|
|
||||||
break;
|
|
||||||
case MANUAL:
|
case MANUAL:
|
||||||
tokenName = TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_MANUAL;
|
tokenName = TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_MANUAL;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,91 @@
|
||||||
package mage.abilities.keyword;
|
package mage.abilities.keyword;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.costs.Cost;
|
import mage.abilities.costs.Cost;
|
||||||
|
import mage.abilities.costs.Costs;
|
||||||
|
import mage.abilities.costs.CostsImpl;
|
||||||
import mage.abilities.costs.mana.GenericManaCost;
|
import mage.abilities.costs.mana.GenericManaCost;
|
||||||
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
|
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
|
import mage.constants.SpellAbilityCastMode;
|
||||||
|
import mage.constants.SpellAbilityType;
|
||||||
|
import mage.constants.TimingRule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author TheElk801
|
* 702.168. Disguise
|
||||||
* TODO: Implement this
|
* <p>
|
||||||
|
* 702.168a
|
||||||
|
* Disguise is a static ability that functions in any zone from which you could play the card it’s on,
|
||||||
|
* and the disguise effect works any time the card is face down. “Disguise [cost]” means “You may cast this
|
||||||
|
* card as a 2/2 face-down creature with ward {2}, no name, no subtypes, and no mana cost by paying {3} rather
|
||||||
|
* than paying its mana cost.” (See rule 708, “Face-Down Spells and Permanents.”)
|
||||||
|
* <p>
|
||||||
|
* 702.168b
|
||||||
|
* To cast a card using its disguise ability, turn the card face down and announce that you are using a disguise ability.
|
||||||
|
* It becomes a 2/2 face-down creature card with ward {2}, no name, no subtypes, and no mana cost. Any effects or
|
||||||
|
* prohibitions that would apply to casting a card with these characteristics (and not the face-up card’s characteristics)
|
||||||
|
* are applied to casting this card. These values are the copiable values of that object’s characteristics.
|
||||||
|
* (See rule 613, “Interaction of Continuous Effects,” and rule 707, “Copying Objects.”) Put it onto the stack
|
||||||
|
* (as a face-down spell with the same characteristics), and pay {3} rather than pay its mana cost. This follows the
|
||||||
|
* rules for paying alternative costs. You can use a disguise ability to cast a card from any zone from which you
|
||||||
|
* could normally cast it. When the spell resolves, it enters the battlefield with the same characteristics the spell
|
||||||
|
* had. The disguise effect applies to the face-down object wherever it is, and it ends when the permanent is turned
|
||||||
|
* face up.
|
||||||
|
* <p>
|
||||||
|
* 702.168c
|
||||||
|
* You can’t normally cast a card face down. A disguise ability allows you to do so.
|
||||||
|
* <p>
|
||||||
|
* 702.168d
|
||||||
|
* Any time you have priority, you may turn a face-down permanent you control with a disguise ability face up.
|
||||||
|
* This is a special action; it doesn’t use the stack (see rule 116). To do this, show all players what the
|
||||||
|
* permanent’s disguise cost would be if it were face up, pay that cost, then turn the permanent face up.
|
||||||
|
* (If the permanent wouldn’t have a disguise cost if it were face up, it can’t be turned face up this way.)
|
||||||
|
* The disguise effect on it ends, and it regains its normal characteristics. Any abilities relating to the
|
||||||
|
* permanent entering the battlefield don’t trigger when it’s turned face up and don’t have any effect,
|
||||||
|
* because the permanent has already entered the battlefield.
|
||||||
|
* <p>
|
||||||
|
* 702.168e
|
||||||
|
* If a permanent’s disguise cost includes 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 chosen as the disguise special action was taken.
|
||||||
|
* <p>
|
||||||
|
* 702.168f
|
||||||
|
* See rule 708, “Face-Down Spells and Permanents,” for more information about how to cast cards with a disguise ability.
|
||||||
|
* <p>
|
||||||
|
* <p>
|
||||||
|
* MorphAbility as a reference implementation
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
*/
|
*/
|
||||||
public class DisguiseAbility extends SpellAbility {
|
public class DisguiseAbility extends SpellAbility {
|
||||||
|
|
||||||
|
protected static final String ABILITY_KEYWORD = "Disguise";
|
||||||
|
protected static final String REMINDER_TEXT = "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.";
|
||||||
|
|
||||||
|
protected Costs<Cost> disguiseCosts;
|
||||||
|
|
||||||
public DisguiseAbility(Card card, Cost disguiseCost) {
|
public DisguiseAbility(Card card, Cost disguiseCost) {
|
||||||
super(new GenericManaCost(3), card.getName());
|
super(new GenericManaCost(3), card.getName());
|
||||||
|
this.timing = TimingRule.SORCERY;
|
||||||
|
this.disguiseCosts = new CostsImpl<>();
|
||||||
|
this.disguiseCosts.add(disguiseCost);
|
||||||
|
this.setSpellAbilityCastMode(SpellAbilityCastMode.DISGUISE);
|
||||||
|
this.setSpellAbilityType(SpellAbilityType.BASE_ALTERNATE);
|
||||||
|
|
||||||
|
// face down effect (hidden by default, visible in face down objects)
|
||||||
|
Ability ability = new SimpleStaticAbility(new BecomesFaceDownCreatureEffect(
|
||||||
|
this.disguiseCosts, BecomesFaceDownCreatureEffect.FaceDownType.DISGUISED));
|
||||||
|
ability.setWorksFaceDown(true);
|
||||||
|
ability.setRuleVisible(false);
|
||||||
|
addSubAbility(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DisguiseAbility(final DisguiseAbility ability) {
|
private DisguiseAbility(final DisguiseAbility ability) {
|
||||||
super(ability);
|
super(ability);
|
||||||
|
this.disguiseCosts = ability.disguiseCosts; // can't be changed TODO: looks buggy, need research
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -24,8 +93,16 @@ public class DisguiseAbility extends SpellAbility {
|
||||||
return new DisguiseAbility(this);
|
return new DisguiseAbility(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Costs<Cost> getFaceUpCosts() {
|
||||||
|
return this.disguiseCosts;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRule() {
|
public String getRule() {
|
||||||
return "Disguise";
|
boolean isMana = disguiseCosts.get(0) instanceof ManaCost;
|
||||||
|
return ABILITY_KEYWORD + (isMana ? " " : "—")
|
||||||
|
+ this.disguiseCosts.getText()
|
||||||
|
+ (isMana ? ' ' : ". ")
|
||||||
|
+ " <i>(" + REMINDER_TEXT + ")</i>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ public class MorphAbility extends SpellAbility {
|
||||||
|
|
||||||
// face down effect (hidden by default, visible in face down objects)
|
// face down effect (hidden by default, visible in face down objects)
|
||||||
Ability ability = new SimpleStaticAbility(new BecomesFaceDownCreatureEffect(
|
Ability ability = new SimpleStaticAbility(new BecomesFaceDownCreatureEffect(
|
||||||
morphCosts, (useMegamorph ? FaceDownType.MEGAMORPHED : FaceDownType.MORPHED)));
|
this.morphCosts, (useMegamorph ? FaceDownType.MEGAMORPHED : FaceDownType.MORPHED)));
|
||||||
ability.setWorksFaceDown(true);
|
ability.setWorksFaceDown(true);
|
||||||
ability.setRuleVisible(false);
|
ability.setRuleVisible(false);
|
||||||
addSubAbility(ability);
|
addSubAbility(ability);
|
||||||
|
|
@ -92,7 +92,7 @@ public class MorphAbility extends SpellAbility {
|
||||||
|
|
||||||
protected MorphAbility(final MorphAbility ability) {
|
protected MorphAbility(final MorphAbility ability) {
|
||||||
super(ability);
|
super(ability);
|
||||||
this.morphCosts = ability.morphCosts; // can't be changed
|
this.morphCosts = ability.morphCosts; // can't be changed TODO: looks buggy, need research
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -100,8 +100,8 @@ public class MorphAbility extends SpellAbility {
|
||||||
return new MorphAbility(this);
|
return new MorphAbility(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Costs<Cost> getMorphCosts() {
|
public Costs<Cost> getFaceUpCosts() {
|
||||||
return morphCosts;
|
return this.morphCosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ public interface Card extends MageObject, Ownerable {
|
||||||
|
|
||||||
boolean turnFaceUp(Ability source, Game game, UUID playerId);
|
boolean turnFaceUp(Ability source, Game game, UUID playerId);
|
||||||
|
|
||||||
|
// TODO: need research, is it lost morph and other face down statuses?
|
||||||
boolean turnFaceDown(Ability source, Game game, UUID playerId);
|
boolean turnFaceDown(Ability source, Game game, UUID playerId);
|
||||||
|
|
||||||
boolean isFlipCard();
|
boolean isFlipCard();
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ public enum TokenRepository {
|
||||||
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_MANIFEST = "Manifest";
|
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_MANIFEST = "Manifest";
|
||||||
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_MORPH = "Morph";
|
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_MORPH = "Morph";
|
||||||
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_MEGAMORPH = "Megamorph";
|
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_MEGAMORPH = "Megamorph";
|
||||||
|
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_DISGUISE = "Disguise";
|
||||||
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_FORETELL = "Foretell";
|
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_FORETELL = "Foretell";
|
||||||
public static final String XMAGE_IMAGE_NAME_COPY = "Copy";
|
public static final String XMAGE_IMAGE_NAME_COPY = "Copy";
|
||||||
public static final String XMAGE_IMAGE_NAME_CITY_BLESSING = "City's Blessing";
|
public static final String XMAGE_IMAGE_NAME_CITY_BLESSING = "City's Blessing";
|
||||||
|
|
@ -288,6 +289,10 @@ public enum TokenRepository {
|
||||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_FACE_DOWN_MEGAMORPH, 2, "https://api.scryfall.com/cards/ta25/15/en?format=image"));
|
res.add(createXmageToken(XMAGE_IMAGE_NAME_FACE_DOWN_MEGAMORPH, 2, "https://api.scryfall.com/cards/ta25/15/en?format=image"));
|
||||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_FACE_DOWN_MEGAMORPH, 3, "https://api.scryfall.com/cards/tc19/27/en?format=image"));
|
res.add(createXmageToken(XMAGE_IMAGE_NAME_FACE_DOWN_MEGAMORPH, 3, "https://api.scryfall.com/cards/tc19/27/en?format=image"));
|
||||||
|
|
||||||
|
// Disguise
|
||||||
|
// support only 1 image: https://scryfall.com/card/tmkm/21/a-mysterious-creature
|
||||||
|
res.add(createXmageToken(XMAGE_IMAGE_NAME_FACE_DOWN_DISGUISE, 1, "https://api.scryfall.com/cards/tmkm/21/en?format=image"));
|
||||||
|
|
||||||
// Foretell
|
// Foretell
|
||||||
// https://scryfall.com/search?q=Foretell+unique%3Aprints+otag%3Aassistant-cards&unique=cards&as=grid&order=name
|
// https://scryfall.com/search?q=Foretell+unique%3Aprints+otag%3Aassistant-cards&unique=cards&as=grid&order=name
|
||||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_FACE_DOWN_FORETELL, 1, "https://api.scryfall.com/cards/tkhm/23/en?format=image"));
|
res.add(createXmageToken(XMAGE_IMAGE_NAME_FACE_DOWN_FORETELL, 1, "https://api.scryfall.com/cards/tkhm/23/en?format=image"));
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,15 @@ public enum SpellAbilityCastMode {
|
||||||
PROTOTYPE("Prototype"),
|
PROTOTYPE("Prototype"),
|
||||||
MORPH("Morph", false, true, SpellAbilityCastMode.MORPH_ADDITIONAL_RULE),
|
MORPH("Morph", false, true, SpellAbilityCastMode.MORPH_ADDITIONAL_RULE),
|
||||||
MEGAMORPH("Megamorph", false, true, SpellAbilityCastMode.MORPH_ADDITIONAL_RULE),
|
MEGAMORPH("Megamorph", false, true, SpellAbilityCastMode.MORPH_ADDITIONAL_RULE),
|
||||||
|
DISGUISE("Disguise", false, true, SpellAbilityCastMode.DISGUISE_ADDITIONAL_RULE),
|
||||||
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,"
|
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.";
|
+ " 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;
|
||||||
|
|
||||||
|
|
@ -90,12 +93,18 @@ public enum SpellAbilityCastMode {
|
||||||
break;
|
break;
|
||||||
case MORPH:
|
case MORPH:
|
||||||
case MEGAMORPH:
|
case MEGAMORPH:
|
||||||
|
case DISGUISE:
|
||||||
if (cardCopy instanceof Spell) {
|
if (cardCopy instanceof Spell) {
|
||||||
//Spell doesn't support setName, so make a copy of the card (we're blowing it away anyway)
|
//Spell doesn't support setName, so make a copy of the card (we're blowing it away anyway)
|
||||||
// TODO: research - is it possible to apply face down code to spell instead workaround with card
|
// TODO: research - is it possible to apply face down code to spell instead workaround with card
|
||||||
cardCopy = ((Spell) cardCopy).getCard().copy();
|
cardCopy = ((Spell) cardCopy).getCard().copy();
|
||||||
}
|
}
|
||||||
BecomesFaceDownCreatureEffect.makeFaceDownObject(game, null, cardCopy, BecomesFaceDownCreatureEffect.FaceDownType.MORPHED, null);
|
BecomesFaceDownCreatureEffect.FaceDownType faceDownType = BecomesFaceDownCreatureEffect.FaceDownType.MORPHED;
|
||||||
|
if (this == DISGUISE) {
|
||||||
|
faceDownType = BecomesFaceDownCreatureEffect.FaceDownType.DISGUISED;
|
||||||
|
}
|
||||||
|
// no needs in additional abilities for spell
|
||||||
|
BecomesFaceDownCreatureEffect.makeFaceDownObject(game, null, cardCopy, faceDownType, null);
|
||||||
break;
|
break;
|
||||||
case NORMAL:
|
case NORMAL:
|
||||||
case MADNESS:
|
case MADNESS:
|
||||||
|
|
|
||||||
|
|
@ -442,6 +442,10 @@ public interface Permanent extends Card, Controllable {
|
||||||
|
|
||||||
boolean isMorphed();
|
boolean isMorphed();
|
||||||
|
|
||||||
|
void setDisguised(boolean value);
|
||||||
|
|
||||||
|
boolean isDisguised();
|
||||||
|
|
||||||
void setManifested(boolean value);
|
void setManifested(boolean value);
|
||||||
|
|
||||||
boolean isManifested();
|
boolean isManifested();
|
||||||
|
|
|
||||||
|
|
@ -176,10 +176,12 @@ public class PermanentCard extends PermanentImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean turnFaceUp(Ability source, Game game, UUID playerId) {
|
public boolean turnFaceUp(Ability source, Game game, UUID playerId) {
|
||||||
if (super.turnFaceUp(source, game, playerId)) {
|
if (super.turnFaceUp(source, game, playerId)) {
|
||||||
|
// TODO: miss types, abilities, color and other things for restore?!
|
||||||
power.setModifiedBaseValue(power.getBaseValue());
|
power.setModifiedBaseValue(power.getBaseValue());
|
||||||
toughness.setModifiedBaseValue(toughness.getBaseValue());
|
toughness.setModifiedBaseValue(toughness.getBaseValue());
|
||||||
setManifested(false);
|
setManifested(false);
|
||||||
setMorphed(false);
|
setMorphed(false);
|
||||||
|
setDisguised(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
||||||
protected boolean suspected;
|
protected boolean suspected;
|
||||||
protected boolean manifested = false;
|
protected boolean manifested = false;
|
||||||
protected boolean morphed = false;
|
protected boolean morphed = false;
|
||||||
|
protected boolean disguised = false;
|
||||||
protected boolean ringBearerFlag = false;
|
protected boolean ringBearerFlag = false;
|
||||||
protected boolean canBeSacrificed = true;
|
protected boolean canBeSacrificed = true;
|
||||||
protected int classLevel = 1;
|
protected int classLevel = 1;
|
||||||
|
|
@ -186,6 +187,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
||||||
this.protectorId = permanent.protectorId;
|
this.protectorId = permanent.protectorId;
|
||||||
|
|
||||||
this.morphed = permanent.morphed;
|
this.morphed = permanent.morphed;
|
||||||
|
this.disguised = permanent.disguised;
|
||||||
this.manifested = permanent.manifested;
|
this.manifested = permanent.manifested;
|
||||||
this.createOrder = permanent.createOrder;
|
this.createOrder = permanent.createOrder;
|
||||||
this.prototyped = permanent.prototyped;
|
this.prototyped = permanent.prototyped;
|
||||||
|
|
@ -1908,6 +1910,16 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
||||||
morphed = value;
|
morphed = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDisguised() {
|
||||||
|
return disguised;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDisguised(boolean value) {
|
||||||
|
disguised = value;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setRarity(Rarity rarity) {
|
public void setRarity(Rarity rarity) {
|
||||||
this.rarity = rarity;
|
this.rarity = rarity;
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ public class CopyTokenFunction {
|
||||||
if (source instanceof PermanentCard) {
|
if (source instanceof PermanentCard) {
|
||||||
// create token from non-token permanent
|
// create token from non-token permanent
|
||||||
|
|
||||||
// morph/manifest must hide all info
|
// face down must hide all info
|
||||||
PermanentCard sourcePermanent = (PermanentCard) source;
|
PermanentCard sourcePermanent = (PermanentCard) source;
|
||||||
BecomesFaceDownCreatureEffect.FaceDownType faceDownType = BecomesFaceDownCreatureEffect.findFaceDownType(game, sourcePermanent);
|
BecomesFaceDownCreatureEffect.FaceDownType faceDownType = BecomesFaceDownCreatureEffect.findFaceDownType(game, sourcePermanent);
|
||||||
if (faceDownType != null) {
|
if (faceDownType != null) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue