mirror of
https://github.com/magefree/mage.git
synced 2025-12-23 03:51:58 -08:00
[MKM] Implement Coveted Falcon (#12057)
* [MKM] Implement Coveted Falcon * Rework to use OneShotEffect with new approach * Use static ZCC to be safe * Add tests * Remove check in GainControlTargetEffect
This commit is contained in:
parent
2522f712e9
commit
b1b83dc5b8
4 changed files with 323 additions and 2 deletions
119
Mage.Sets/src/mage/cards/c/CovetedFalcon.java
Normal file
119
Mage.Sets/src/mage/cards/c/CovetedFalcon.java
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
package mage.cards.c;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.AttacksTriggeredAbility;
|
||||||
|
import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.abilities.keyword.FlyingAbility;
|
||||||
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
|
import mage.abilities.keyword.DisguiseAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.target.TargetPermanent;
|
||||||
|
import mage.target.common.TargetControlledPermanent;
|
||||||
|
import mage.target.common.TargetOpponent;
|
||||||
|
import mage.target.targetpointer.FixedTarget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Cguy7777
|
||||||
|
*/
|
||||||
|
public final class CovetedFalcon extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter
|
||||||
|
= new FilterPermanent("permanent you own but don't control");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(TargetController.YOU.getOwnerPredicate());
|
||||||
|
filter.add(TargetController.NOT_YOU.getControllerPredicate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public CovetedFalcon(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{U}{U}");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.BIRD);
|
||||||
|
this.power = new MageInt(1);
|
||||||
|
this.toughness = new MageInt(4);
|
||||||
|
|
||||||
|
// Flying
|
||||||
|
this.addAbility(FlyingAbility.getInstance());
|
||||||
|
|
||||||
|
// Whenever Coveted Falcon attacks, gain control of target permanent you own but don't control.
|
||||||
|
Ability attacksTriggeredAbility = new AttacksTriggeredAbility(new GainControlTargetEffect(Duration.Custom));
|
||||||
|
attacksTriggeredAbility.addTarget(new TargetPermanent(filter));
|
||||||
|
this.addAbility(attacksTriggeredAbility);
|
||||||
|
|
||||||
|
// Disguise {1}{U}
|
||||||
|
this.addAbility(new DisguiseAbility(this, new ManaCostsImpl<>("{1}{U}")));
|
||||||
|
|
||||||
|
// When Coveted Falcon is turned face up, target opponent gains control of any number of target permanents you control.
|
||||||
|
// Draw a card for each one they gained control of this way.
|
||||||
|
Ability turnedFaceUpTriggeredAbility = new TurnedFaceUpSourceTriggeredAbility(new CovetedFalconEffect());
|
||||||
|
turnedFaceUpTriggeredAbility.addTarget(new TargetOpponent());
|
||||||
|
turnedFaceUpTriggeredAbility.addTarget(
|
||||||
|
new TargetControlledPermanent(0, Integer.MAX_VALUE, StaticFilters.FILTER_CONTROLLED_PERMANENT, false));
|
||||||
|
this.addAbility(turnedFaceUpTriggeredAbility);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CovetedFalcon(final CovetedFalcon card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CovetedFalcon copy() {
|
||||||
|
return new CovetedFalcon(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CovetedFalconEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
CovetedFalconEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "target opponent gains control of any number of target permanents you control. " +
|
||||||
|
"Draw a card for each one they gained control of this way";
|
||||||
|
}
|
||||||
|
|
||||||
|
private CovetedFalconEffect(final CovetedFalconEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
UUID targetOpponentId = source.getFirstTarget();
|
||||||
|
List<UUID> targetPermanentIds = source.getTargets().get(1).getTargets();
|
||||||
|
for (UUID permanentId : targetPermanentIds) {
|
||||||
|
game.addEffect(
|
||||||
|
new GainControlTargetEffect(Duration.Custom, true, targetOpponentId)
|
||||||
|
.setTargetPointer(new FixedTarget(permanentId, game)),
|
||||||
|
source);
|
||||||
|
}
|
||||||
|
|
||||||
|
game.getState().processAction(game);
|
||||||
|
|
||||||
|
int cardsToDraw = 0;
|
||||||
|
for (UUID permanentId : targetPermanentIds) {
|
||||||
|
Permanent permanent = game.getPermanent(permanentId);
|
||||||
|
if (permanent != null && permanent.isControlledBy(targetOpponentId)) {
|
||||||
|
cardsToDraw++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new DrawCardSourceControllerEffect(cardsToDraw).apply(game, source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CovetedFalconEffect copy() {
|
||||||
|
return new CovetedFalconEffect(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -86,6 +86,8 @@ public final class MurdersAtKarlovManor extends ExpansionSet {
|
||||||
cards.add(new SetCardInfo("Conspiracy Unraveler", 47, Rarity.MYTHIC, mage.cards.c.ConspiracyUnraveler.class));
|
cards.add(new SetCardInfo("Conspiracy Unraveler", 47, Rarity.MYTHIC, mage.cards.c.ConspiracyUnraveler.class));
|
||||||
cards.add(new SetCardInfo("Convenient Target", 119, Rarity.UNCOMMON, mage.cards.c.ConvenientTarget.class));
|
cards.add(new SetCardInfo("Convenient Target", 119, Rarity.UNCOMMON, mage.cards.c.ConvenientTarget.class));
|
||||||
cards.add(new SetCardInfo("Cornered Crook", 120, Rarity.UNCOMMON, mage.cards.c.CorneredCrook.class));
|
cards.add(new SetCardInfo("Cornered Crook", 120, Rarity.UNCOMMON, mage.cards.c.CorneredCrook.class));
|
||||||
|
cards.add(new SetCardInfo("Coveted Falcon", 48, Rarity.RARE, mage.cards.c.CovetedFalcon.class, NON_FULL_USE_VARIOUS));
|
||||||
|
cards.add(new SetCardInfo("Coveted Falcon", 393, Rarity.RARE, mage.cards.c.CovetedFalcon.class, NON_FULL_USE_VARIOUS));
|
||||||
cards.add(new SetCardInfo("Crime Novelist", 121, Rarity.UNCOMMON, mage.cards.c.CrimeNovelist.class));
|
cards.add(new SetCardInfo("Crime Novelist", 121, Rarity.UNCOMMON, mage.cards.c.CrimeNovelist.class));
|
||||||
cards.add(new SetCardInfo("Crimestopper Sprite", 49, Rarity.COMMON, mage.cards.c.CrimestopperSprite.class));
|
cards.add(new SetCardInfo("Crimestopper Sprite", 49, Rarity.COMMON, mage.cards.c.CrimestopperSprite.class));
|
||||||
cards.add(new SetCardInfo("Crowd-Control Warden", 193, Rarity.COMMON, mage.cards.c.CrowdControlWarden.class));
|
cards.add(new SetCardInfo("Crowd-Control Warden", 193, Rarity.COMMON, mage.cards.c.CrowdControlWarden.class));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
package org.mage.test.cards.single.mkm;
|
||||||
|
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
public class CovetedFalconTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Coveted Falcon {1}{U}{U}
|
||||||
|
* Artifact Creature - Bird (1/4)
|
||||||
|
* Flying
|
||||||
|
* Whenever Coveted Falcon attacks, gain control of target permanent you own but don't control.
|
||||||
|
* Disguise {1}{U}
|
||||||
|
* When Coveted Falcon is turned face up, target opponent gains control of any number of target permanents you control.
|
||||||
|
* Draw a card for each one they gained control of this way.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_GiveControl() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 3 + 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
|
||||||
|
addCard(Zone.HAND, playerA, "Coveted Falcon", 1);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Coveted Falcon using Disguise", true);
|
||||||
|
|
||||||
|
// When Coveted Falcon is turned face up, target opponent gains control of any number of target permanents you control.
|
||||||
|
// Draw a card for each one they gained control of this way.
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
addTarget(playerA, "Grizzly Bears");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerB, "Grizzly Bears", 1);
|
||||||
|
assertHandCount(playerA, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_TargetChangesControllerInResponse() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 3 + 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
|
||||||
|
addCard(Zone.HAND, playerA, "Coveted Falcon", 1);
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5);
|
||||||
|
// Turn Against {4}{R}
|
||||||
|
// Instant
|
||||||
|
// Devoid
|
||||||
|
// Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn.
|
||||||
|
addCard(Zone.HAND, playerB, "Turn Against", 1);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Coveted Falcon using Disguise", true);
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
addTarget(playerA, "Grizzly Bears");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Turn Against", "Grizzly Bears");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerB, "Grizzly Bears", 1);
|
||||||
|
assertHandCount(playerA, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_TargetLeavesAndReturnsUnderYourControlInResponse() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 3 + 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Putrid Goblin", 1); // 2/2 Zombie Goblin w/ Persist
|
||||||
|
addCard(Zone.HAND, playerA, "Coveted Falcon", 1);
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3);
|
||||||
|
addCard(Zone.HAND, playerB, "Murder", 1);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Coveted Falcon using Disguise", true);
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
addTarget(playerA, "Putrid Goblin");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Murder", "Putrid Goblin");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerB, "Putrid Goblin", 0);
|
||||||
|
assertHandCount(playerA, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_TargetLeavesAndReturnsUnderOpponentsControlInResponse() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 3 + 2);
|
||||||
|
// Treacherous Pit-Dweller {B}{B}
|
||||||
|
// Creature - Demon (4/3)
|
||||||
|
// When Treacherous Pit-Dweller enters the battlefield from a graveyard, target opponent gains control of it.
|
||||||
|
// Undying
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Treacherous Pit-Dweller", 1);
|
||||||
|
addCard(Zone.HAND, playerA, "Coveted Falcon", 1);
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3);
|
||||||
|
addCard(Zone.HAND, playerB, "Murder", 1);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Coveted Falcon using Disguise", true);
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
addTarget(playerA, "Treacherous Pit-Dweller");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Murder", "Treacherous Pit-Dweller");
|
||||||
|
// When Treacherous Pit-Dweller enters the battlefield from a graveyard, target opponent gains control of it.
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerB, "Treacherous Pit-Dweller", 1);
|
||||||
|
assertHandCount(playerA, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Guardian Beast {3}{B}
|
||||||
|
* Creature - Beast (2/4)
|
||||||
|
* As long as Guardian Beast is untapped, noncreature artifacts you control can't be enchanted,
|
||||||
|
* they have indestructible, and other players can't gain control of them.
|
||||||
|
* This effect doesn't remove Auras already attached to those artifacts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// When you turn Coveted Falcon face up while controlling Guardian Beast, you can target noncreature artifacts,
|
||||||
|
// but your opponent shouldn't gain control of them, and you shouldn't draw cards for them.
|
||||||
|
// If Guardian Beast dies after Falcon's turn-up trigger resolves, you should still keep those artifacts.
|
||||||
|
@Test
|
||||||
|
public void test_GiveArtifactAndNonartifactWhileControllingGuardianBeast() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 3 + 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Guardian Beast", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Darksteel Relic", 1); // Artifact w/ Indestructible
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // Should be given away normally
|
||||||
|
addCard(Zone.HAND, playerA, "Coveted Falcon", 1);
|
||||||
|
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 3);
|
||||||
|
addCard(Zone.HAND, playerB, "Murder", 1);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Coveted Falcon using Disguise", true);
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
addTarget(playerA, "Darksteel Relic^Grizzly Bears");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Murder", "Guardian Beast");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerB, "Darksteel Relic", 0);
|
||||||
|
assertPermanentCount(playerB, "Grizzly Bears", 1);
|
||||||
|
assertHandCount(playerA, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If you target Guardian Beast AND one or more noncreature artifacts to give away, you should only give away the Beast.
|
||||||
|
// Test is duplicated to catch glitches that only occur when the targets are ordered a certain way.
|
||||||
|
// TODO Doesn't work properly
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void test_GiveGuardianBeastAndArtifactsA() {
|
||||||
|
setupGiveGuardianBeastAndArtifactsTest(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void test_GiveGuardianBeastAndArtifactsB() {
|
||||||
|
setupGiveGuardianBeastAndArtifactsTest(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupGiveGuardianBeastAndArtifactsTest(final boolean guardianBeastFirst) {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Island", 3 + 2);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Guardian Beast", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Darksteel Relic", 1);
|
||||||
|
addCard(Zone.HAND, playerA, "Coveted Falcon", 1);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Coveted Falcon using Disguise", true);
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||||
|
addTarget(playerA, playerB);
|
||||||
|
addTarget(playerA, guardianBeastFirst ? "Guardian Beast^Darksteel Relic" : "Darksteel Relic^Guardian Beast");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerB, "Guardian Beast", 1);
|
||||||
|
assertPermanentCount(playerB, "Darksteel Relic", 0);
|
||||||
|
assertHandCount(playerA, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -105,8 +105,7 @@ public class GainControlTargetEffect extends ContinuousEffectImpl {
|
||||||
controlChanged = true;
|
controlChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (source.isActivatedAbility()
|
if (firstControlChange && !controlChanged) {
|
||||||
&& firstControlChange && !controlChanged) {
|
|
||||||
// If it was not possible to get control of target permanent by the activated ability the first time it took place
|
// If it was not possible to get control of target permanent by the activated ability the first time it took place
|
||||||
// the effect failed (e.g. because of Guardian Beast) and must be discarded
|
// the effect failed (e.g. because of Guardian Beast) and must be discarded
|
||||||
// This does not handle correctly multiple targets at once
|
// This does not handle correctly multiple targets at once
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue