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("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("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("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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
if (source.isActivatedAbility()
|
||||
&& firstControlChange && !controlChanged) {
|
||||
if (firstControlChange && !controlChanged) {
|
||||
// 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
|
||||
// This does not handle correctly multiple targets at once
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue