mirror of
https://github.com/magefree/mage.git
synced 2025-12-24 20:41:58 -08:00
implement [MKM] Cryptic Coat (#12164) and Cloak ability
This commit is contained in:
parent
8172616449
commit
ab280ad2ba
15 changed files with 235 additions and 29 deletions
|
|
@ -111,32 +111,36 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
|||
new TurnFaceUpAbility(turnFaceUpCosts, faceDownType == FaceDownType.MEGAMORPHED)
|
||||
.setCostAdjuster(costAdjuster)
|
||||
);
|
||||
}
|
||||
|
||||
switch (faceDownType) {
|
||||
case MORPHED:
|
||||
case MEGAMORPHED:
|
||||
switch (faceDownType) {
|
||||
case MORPHED:
|
||||
case MEGAMORPHED:
|
||||
if (turnFaceUpCosts != null) {
|
||||
// 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}")));
|
||||
}
|
||||
break;
|
||||
case DISGUISED:
|
||||
case CLOAKED:
|
||||
// Ward {2} -- should not be dependent on turnFaceUpCosts.
|
||||
this.additionalAbilities.add(new WardAbility(new ManaCostsImpl<>("{2}")));
|
||||
|
||||
if (turnFaceUpCosts != null) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
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";
|
||||
|
|
@ -209,6 +213,9 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
|||
case DISGUISED:
|
||||
permanent.setDisguised(true);
|
||||
break;
|
||||
case CLOAKED:
|
||||
permanent.setCloaked(true);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("FaceDownType not yet supported: " + faceDownType);
|
||||
}
|
||||
|
|
@ -228,6 +235,8 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
|||
return BecomesFaceDownCreatureEffect.FaceDownType.DISGUISED;
|
||||
} else if (permanent.isManifested()) {
|
||||
return BecomesFaceDownCreatureEffect.FaceDownType.MANIFESTED;
|
||||
} else if (permanent.isCloaked()) {
|
||||
return BecomesFaceDownCreatureEffect.FaceDownType.CLOAKED;
|
||||
} else if (permanent.isFaceDown(game)) {
|
||||
return BecomesFaceDownCreatureEffect.FaceDownType.MANUAL;
|
||||
} else {
|
||||
|
|
@ -326,7 +335,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
|||
tokenName = TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_MANIFEST;
|
||||
break;
|
||||
case CLOAKED:
|
||||
tokenName = "TODO-CLOAKED";
|
||||
tokenName = TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_CLOAK;
|
||||
break;
|
||||
case MANUAL:
|
||||
tokenName = TokenRepository.XMAGE_IMAGE_NAME_FACE_DOWN_MANUAL;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Manifest
|
||||
|
|
@ -65,6 +65,7 @@ public class ManifestEffect extends OneShotEffect {
|
|||
|
||||
private final DynamicValue amount;
|
||||
private final boolean isPlural;
|
||||
private final boolean cloakNotManifest;
|
||||
|
||||
public ManifestEffect(int amount) {
|
||||
this(StaticValue.get(amount), amount > 1);
|
||||
|
|
@ -75,9 +76,14 @@ public class ManifestEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
private ManifestEffect(DynamicValue amount, boolean isPlural) {
|
||||
this(amount, isPlural, false);
|
||||
}
|
||||
|
||||
private ManifestEffect(DynamicValue amount, boolean isPlural, boolean cloakNotManifest) {
|
||||
super(Outcome.PutCreatureInPlay);
|
||||
this.amount = amount;
|
||||
this.isPlural = isPlural;
|
||||
this.cloakNotManifest = cloakNotManifest;
|
||||
this.staticText = setText();
|
||||
}
|
||||
|
||||
|
|
@ -85,6 +91,7 @@ public class ManifestEffect extends OneShotEffect {
|
|||
super(effect);
|
||||
this.amount = effect.amount;
|
||||
this.isPlural = effect.isPlural;
|
||||
this.cloakNotManifest = effect.cloakNotManifest;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -100,12 +107,16 @@ public class ManifestEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
int manifestAmount = amount.calculate(game, source, this);
|
||||
return doManifestCards(game, source, controller, controller.getLibrary().getTopCards(game, manifestAmount));
|
||||
return !doManifestCards(game, source, controller, controller.getLibrary().getTopCards(game, manifestAmount), cloakNotManifest).isEmpty();
|
||||
}
|
||||
|
||||
public static boolean doManifestCards(Game game, Ability source, Player manifestPlayer, Set<Card> cardsToManifest) {
|
||||
public static List<Permanent> doManifestCards(Game game, Ability source, Player manifestPlayer, Set<Card> cardsToManifest) {
|
||||
return doManifestCards(game, source, manifestPlayer, cardsToManifest, false);
|
||||
}
|
||||
|
||||
public static List<Permanent> doManifestCards(Game game, Ability source, Player manifestPlayer, Set<Card> cardsToManifest, boolean cloakNotManifest) {
|
||||
if (cardsToManifest.isEmpty()) {
|
||||
return false;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// prepare source ability
|
||||
|
|
@ -132,9 +143,10 @@ public class ManifestEffect extends OneShotEffect {
|
|||
|
||||
// zcc + 1 for use case with Rally the Ancestors (see related test)
|
||||
MageObjectReference objectReference = new MageObjectReference(battlefieldCard.getId(), battlefieldCard.getZoneChangeCounter(game) + 1, game);
|
||||
game.addEffect(new BecomesFaceDownCreatureEffect(manaCosts, objectReference, Duration.Custom, FaceDownType.MANIFESTED), newSource);
|
||||
game.addEffect(new BecomesFaceDownCreatureEffect(manaCosts, objectReference, Duration.Custom, cloakNotManifest ? FaceDownType.CLOAKED : FaceDownType.MANIFESTED), newSource);
|
||||
}
|
||||
|
||||
List<Permanent> manifested = new ArrayList<>();
|
||||
// move cards to battlefield as face down
|
||||
// TODO: possible buggy for multiple cards, see rule 701.34e - it require manifest one by one (card to check: Omarthis, Ghostfire Initiate)
|
||||
manifestPlayer.moveCards(cardsToManifest, Zone.BATTLEFIELD, source, game, false, true, false, null);
|
||||
|
|
@ -145,18 +157,25 @@ public class ManifestEffect extends OneShotEffect {
|
|||
if (permanent != null) {
|
||||
// TODO: permanent already has manifested status, so code can be deleted later
|
||||
// TODO: add test with battlefield trigger/watcher (must not see normal card, must not see face down status without manifest)
|
||||
permanent.setManifested(true);
|
||||
if (cloakNotManifest) {
|
||||
permanent.setCloaked(true);
|
||||
} else {
|
||||
permanent.setManifested(true);
|
||||
}
|
||||
manifested.add(permanent);
|
||||
} else {
|
||||
// TODO: looks buggy, card can't be moved to battlefield, but face down effect already active
|
||||
// or it can be face down on another move to battlefield
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return manifested;
|
||||
}
|
||||
|
||||
private String setText() {
|
||||
StringBuilder sb = new StringBuilder("manifest the top ");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(cloakNotManifest ? "cloak" : "manifest");
|
||||
sb.append(" the top ");
|
||||
if (isPlural) {
|
||||
sb.append(CardUtil.numberToText(amount.toString())).append(" cards ");
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class ManifestTargetPlayerEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
|
||||
return ManifestEffect.doManifestCards(game, source, targetPlayer, targetPlayer.getLibrary().getTopCards(game, amount));
|
||||
return !ManifestEffect.doManifestCards(game, source, targetPlayer, targetPlayer.getLibrary().getTopCards(game, amount)).isEmpty();
|
||||
}
|
||||
|
||||
private String setText() {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public enum TokenRepository {
|
|||
// - additional card name for controller like "Morph: face up name"
|
||||
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_MANUAL = "Face Down";
|
||||
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_MANIFEST = "Manifest";
|
||||
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_CLOAK = "Cloak";
|
||||
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_MORPH = "Morph";
|
||||
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_DISGUISE = "Disguise";
|
||||
public static final String XMAGE_IMAGE_NAME_FACE_DOWN_FORETELL = "Foretell";
|
||||
|
|
@ -287,6 +288,10 @@ public enum TokenRepository {
|
|||
// 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"));
|
||||
|
||||
// Cloak
|
||||
// support only 1 image: https://scryfall.com/card/tmkm/21/a-mysterious-creature
|
||||
res.add(createXmageToken(XMAGE_IMAGE_NAME_FACE_DOWN_CLOAK, 1, "https://api.scryfall.com/cards/tmkm/21/en?format=image"));
|
||||
|
||||
// Foretell
|
||||
// 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"));
|
||||
|
|
|
|||
|
|
@ -450,6 +450,10 @@ public interface Permanent extends Card, Controllable {
|
|||
|
||||
boolean isManifested();
|
||||
|
||||
void setCloaked(boolean value);
|
||||
|
||||
boolean isCloaked();
|
||||
|
||||
boolean isRingBearer();
|
||||
|
||||
void setRingBearer(Game game, boolean value);
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ public class PermanentCard extends PermanentImpl {
|
|||
setManifested(false);
|
||||
setMorphed(false);
|
||||
setDisguised(false);
|
||||
setCloaked(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
protected boolean renowned;
|
||||
protected boolean suspected;
|
||||
protected boolean manifested = false;
|
||||
protected boolean cloaked = false;
|
||||
protected boolean morphed = false;
|
||||
protected boolean disguised = false;
|
||||
protected boolean ringBearerFlag = false;
|
||||
|
|
@ -189,6 +190,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
this.morphed = permanent.morphed;
|
||||
this.disguised = permanent.disguised;
|
||||
this.manifested = permanent.manifested;
|
||||
this.cloaked = permanent.cloaked;
|
||||
this.createOrder = permanent.createOrder;
|
||||
this.prototyped = permanent.prototyped;
|
||||
this.canBeSacrificed = permanent.canBeSacrificed;
|
||||
|
|
@ -1905,6 +1907,16 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
manifested = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCloaked() {
|
||||
return cloaked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCloaked(boolean value) {
|
||||
cloaked = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMorphed() {
|
||||
return morphed;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue