[BRC] Implement Kayla's Music Box (#11130)

This commit is contained in:
Vivian Greenslade 2023-09-09 17:02:34 -02:30 committed by GitHub
parent 98324b042a
commit dc1e94648b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 221 additions and 0 deletions

View file

@ -0,0 +1,169 @@
package mage.cards.k;
import java.util.UUID;
import mage.constants.SuperType;
import mage.game.ExileZone;
import mage.game.Game;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AsThoughEffectType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
/**
*
* @author Xanderhall
*/
public final class KaylasMusicBox extends CardImpl {
public KaylasMusicBox(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
this.supertype.add(SuperType.LEGENDARY);
// {W}, {T}: Look at the top card of your library, then exile it face down.
Ability ability = new SimpleActivatedAbility(new KaylasMusicBoxExileEffect(), new ManaCostsImpl<>("{W}"));
ability.addCost(new TapSourceCost());
this.addAbility(ability);
// {T}: Until end of turn, you may play cards you own exiled with Kayla's Music Box.
this.addAbility(new SimpleActivatedAbility(new KaylasMusicBoxPlayFromExileEffect(), new TapSourceCost()));
}
private KaylasMusicBox(final KaylasMusicBox card) {
super(card);
}
@Override
public KaylasMusicBox copy() {
return new KaylasMusicBox(this);
}
}
class KaylasMusicBoxExileEffect extends OneShotEffect {
KaylasMusicBoxExileEffect() {
super(Outcome.Benefit);
staticText = "look at the top card of your library, then exile it face down";
}
private KaylasMusicBoxExileEffect(final KaylasMusicBoxExileEffect effect) {
super(effect);
}
@Override
public KaylasMusicBoxExileEffect copy() {
return new KaylasMusicBoxExileEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller == null || sourceObject == null) {
return false;
}
Card card = controller.getLibrary().getFromTop(game);
if (card == null) {
return false;
}
card.setFaceDown(true, game);
controller.lookAtCards(null, card, game);
if (controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source))) {
card.setFaceDown(true, game);
// No other player may look at the face-down cards you own exiled with Kaylas Music Box, even if another player takes control of it.
// (2022-10-14)
ContinuousEffect effect = new KaylasMusicBoxLookEffect(controller.getId());
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
}
return true;
}
}
class KaylasMusicBoxLookEffect extends AsThoughEffectImpl {
private final UUID authorizedPlayerId;
public KaylasMusicBoxLookEffect(UUID authorizedPlayerId) {
super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
this.authorizedPlayerId = authorizedPlayerId;
staticText = "You may look at the cards exiled with {this}";
}
private KaylasMusicBoxLookEffect(final KaylasMusicBoxLookEffect effect) {
super(effect);
this.authorizedPlayerId = effect.authorizedPlayerId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public KaylasMusicBoxLookEffect copy() {
return new KaylasMusicBoxLookEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
UUID cardId = getTargetPointer().getFirst(game, source);
if (cardId == null) {
this.discard(); // card is no longer in the origin zone, effect can be discarded
}
return affectedControllerId.equals(authorizedPlayerId)
&& objectId.equals(cardId);
}
}
class KaylasMusicBoxPlayFromExileEffect extends AsThoughEffectImpl {
KaylasMusicBoxPlayFromExileEffect() {
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit);
staticText = "until end of turn, you may play cards you own exiled with {this}.";
}
private KaylasMusicBoxPlayFromExileEffect(final KaylasMusicBoxPlayFromExileEffect effect) {
super(effect);
}
@Override
public KaylasMusicBoxPlayFromExileEffect copy() {
return new KaylasMusicBoxPlayFromExileEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) {
ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source));
if (exileZone == null || !exileZone.contains(sourceId)) {
return false;
}
CardUtil.makeCardPlayable(game, source, exileZone.get(sourceId, game), Duration.EndOfTurn, false);
return true;
}
}

View file

@ -87,6 +87,7 @@ public final class TheBrothersWarCommander extends ExpansionSet {
cards.add(new SetCardInfo("Island", 31, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Izzet Boilerworks", 188, Rarity.UNCOMMON, mage.cards.i.IzzetBoilerworks.class));
cards.add(new SetCardInfo("Jhoira, Weatherlight Captain", 126, Rarity.MYTHIC, mage.cards.j.JhoiraWeatherlightCaptain.class));
cards.add(new SetCardInfo("Kayla's Music Box", 15, Rarity.RARE, mage.cards.k.KaylasMusicBox.class));
cards.add(new SetCardInfo("Liquimetal Torque", 145, Rarity.UNCOMMON, mage.cards.l.LiquimetalTorque.class));
cards.add(new SetCardInfo("Lithoform Engine", 146, Rarity.MYTHIC, mage.cards.l.LithoformEngine.class));
cards.add(new SetCardInfo("Losheel, Clockwork Scholar", 73, Rarity.RARE, mage.cards.l.LosheelClockworkScholar.class));

View file

@ -0,0 +1,51 @@
package org.mage.test.cards.single.brc;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import mage.constants.PhaseStep;
import mage.constants.Zone;
public class KaylasMusicBoxTest extends CardTestPlayerBase {
private static final String BOX = "Kayla's Music Box";
private static final String LION = "Silvercoat Lion";
/**
* {W}, {T}: Look at the top card of your library, then exile it face down. (You may look at it any time.)
* {T}: Until end of turn, you may play cards you own exiled with Kaylas Music Box.
*/
@Test
public void testEffect() {
removeAllCardsFromLibrary(playerA);
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, LION, 2);
addCard(Zone.LIBRARY, playerA, "Mountain", 1);
addCard(Zone.LIBRARY, playerA, "Plains", 1);
addCard(Zone.LIBRARY, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, BOX);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
// Player A activates the box, exiling lion
activateAbility(1, PhaseStep.END_TURN, playerA, "{W}, {T}");
// Player A draws mountain, activates box again to exile plains
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}, {T}");
// Player A activates the box on next turn, plays lion and plains
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}");
waitStackResolved(5, PhaseStep.PRECOMBAT_MAIN, playerA);
playLand(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Plains");
castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, LION);
setStrictChooseMode(true);
setStopAt(5, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, LION, 1);
assertPermanentCount(playerA, "Plains", 2);
}
}