diff --git a/Mage.Sets/src/mage/cards/u/UnfinishedBusiness.java b/Mage.Sets/src/mage/cards/u/UnfinishedBusiness.java
new file mode 100644
index 00000000000..e3b52374ac8
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/u/UnfinishedBusiness.java
@@ -0,0 +1,126 @@
+package mage.cards.u;
+
+import java.util.UUID;
+
+
+import mage.abilities.Ability;
+import mage.abilities.effects.OneShotEffect;
+import mage.cards.*;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.Zone;
+import mage.filter.FilterCard;
+import mage.filter.StaticFilters;
+import mage.filter.predicate.Predicates;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.target.common.TargetCardInYourGraveyard;
+
+/**
+ *
+ * @author Codermann63
+ */
+public final class UnfinishedBusiness extends CardImpl {
+
+ private static final FilterCard auraOrEquipmentFilter = new FilterCard("Aura or Equipment card");
+
+ static {
+ auraOrEquipmentFilter.add(Predicates.or(
+ SubType.EQUIPMENT.getPredicate(),
+ SubType.AURA.getPredicate()
+ ));
+ }
+
+ public UnfinishedBusiness(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}");
+ this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(1,1, StaticFilters.FILTER_CARD_CREATURE));
+ this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 2, auraOrEquipmentFilter));
+ // Return target creature card from your graveyard to the battlefield,
+ // then return up to two target Aura and/or Equipment cards from your graveyard to the battlefield attached to that creature.
+ this.getSpellAbility().addEffect(new UnfinishedBusinessEffect());
+ }
+
+ private UnfinishedBusiness(final UnfinishedBusiness card) {
+ super(card);
+ }
+
+ @Override
+ public UnfinishedBusiness copy() {
+ return new UnfinishedBusiness(this);
+ }
+}
+
+class UnfinishedBusinessEffect extends OneShotEffect{
+
+ UnfinishedBusinessEffect() {
+ super(Outcome.PutCreatureInPlay);
+ staticText = "Return target creature card from your graveyard to the battlefield, then return up to two target Aura and/or Equipment cards from your graveyard to the battlefield attached to that creature. (If the Auras can not enchant that creature, they remain in your graveyard.)";
+ }
+
+ private UnfinishedBusinessEffect(final UnfinishedBusinessEffect effect) {super(effect);}
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ Player controller = game.getPlayer((source.getControllerId()));
+ if (controller == null){
+ return false;
+ }
+
+ // Return target creature from the graveyard to the battlefield
+ Card targetCreature = game.getCard(source.getTargets().getFirstTarget());
+
+ if (targetCreature != null){
+ controller.moveCards(targetCreature, Zone.BATTLEFIELD, source, game);
+ game.getState().processAction(game);
+ }
+ Permanent permanentCreature = targetCreature == null ? null : game.getPermanent(targetCreature.getId());
+
+ // Target auras and/or equipment in your graveyard.
+ Cards cardsInitial = new CardsImpl(source.getTargets().get(1).getTargets());
+ if (cardsInitial.isEmpty()) {
+ return false;
+ }
+
+ // Auras that cannot be attached to the creature stay in the graveyard
+ // Create a list of legal cards to return
+ Cards cards = new CardsImpl();
+ for(UUID c: cardsInitial){
+ if (game.getCard(c).hasSubtype(SubType.EQUIPMENT,game)){
+ // always add equipment cards
+ cards.add(c);
+ }
+ else if (permanentCreature != null &&
+ !permanentCreature.cantBeAttachedBy(game.getCard(c),source, game, false) &&
+ game.getCard(c).hasSubtype(SubType.AURA, game)){
+ // only add auras if the creature has returned
+ // only add auras that can be attached to creature
+ cards.add(c);
+ }
+ }
+ if (cards.isEmpty()){
+ return false;
+ }
+
+ // Handle return of legal auras and equipment
+ if (permanentCreature != null){
+ cards.getCards(game)
+ .forEach(card -> game.getState().setValue("attachTo:" + card.getId(), permanentCreature));
+ }
+ controller.moveCards(cards, Zone.BATTLEFIELD, source, game);
+ if (permanentCreature != null){
+ for(UUID id: cards){
+ if (!permanentCreature.cantBeAttachedBy(game.getCard(id), source, game, true)){
+ permanentCreature.addAttachment(id, source, game);
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public UnfinishedBusinessEffect copy() {
+ return new UnfinishedBusinessEffect(this);
+ }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java b/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java
index c23b03bdb3e..628656e3e6f 100644
--- a/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java
+++ b/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java
@@ -148,6 +148,7 @@ public final class WildsOfEldraineCommander extends ExpansionSet {
cards.add(new SetCardInfo("Tithe Taker", 80, Rarity.RARE, mage.cards.t.TitheTaker.class));
cards.add(new SetCardInfo("Transcendent Envoy", 81, Rarity.COMMON, mage.cards.t.TranscendentEnvoy.class));
cards.add(new SetCardInfo("Umbra Mystic", 82, Rarity.RARE, mage.cards.u.UmbraMystic.class));
+ cards.add(new SetCardInfo("Unfinished Business", 8, Rarity.RARE, mage.cards.u.UnfinishedBusiness.class));
cards.add(new SetCardInfo("Utopia Sprawl", 135, Rarity.COMMON, mage.cards.u.UtopiaSprawl.class));
cards.add(new SetCardInfo("Verdant Embrace", 136, Rarity.RARE, mage.cards.v.VerdantEmbrace.class));
cards.add(new SetCardInfo("Vitu-Ghazi, the City-Tree", 173, Rarity.UNCOMMON, mage.cards.v.VituGhaziTheCityTree.class));
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReconfigureTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReconfigureTest.java
index 0822fd180c8..73ea0307797 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReconfigureTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/activated/ReconfigureTest.java
@@ -33,7 +33,7 @@ public class ReconfigureTest extends CardTestPlayerBase {
assertType(boar, CardType.CREATURE, false);
assertSubtype(boar, SubType.EQUIPMENT);
- assertIsAttachedTo(playerA, boar, lion);
+ assertAttachedTo(playerA, boar, lion, true);
assertPowerToughness(playerA, lion, 2 + 3, 2 + 2);
assertAbility(playerA, lion, TrampleAbility.getInstance(), true);
}
@@ -74,7 +74,7 @@ public class ReconfigureTest extends CardTestPlayerBase {
assertType(boar, CardType.CREATURE, false);
assertSubtype(boar, SubType.EQUIPMENT);
- assertIsAttachedTo(playerA, boar, lion);
+ assertAttachedTo(playerA, boar, lion, true);
assertPowerToughness(playerA, lion, 2 + 3, 2 + 2);
assertAbility(playerA, lion, TrampleAbility.getInstance(), true);
}
@@ -94,7 +94,7 @@ public class ReconfigureTest extends CardTestPlayerBase {
assertType(boar, CardType.CREATURE, false);
assertSubtype(boar, SubType.EQUIPMENT);
- assertIsAttachedTo(playerA, boar, lion);
+ assertAttachedTo(playerA, boar, lion, true);
assertPowerToughness(playerA, lion, 2 + 3, 2 + 2);
assertAbility(playerA, lion, TrampleAbility.getInstance(), true);
}
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BronzehideLionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BronzehideLionTest.java
index 1ce2a16fe26..ad92bd91359 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BronzehideLionTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/enters/BronzehideLionTest.java
@@ -27,7 +27,7 @@ public class BronzehideLionTest extends CardTestPlayerBase {
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, lion, 0);
- assertIsAttachedTo(playerA, lion, "Grizzly Bears");
+ assertAttachedTo(playerA, lion, "Grizzly Bears", true);
}
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/afc/BeltOfGiantStrengthTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/afc/BeltOfGiantStrengthTest.java
index 166afca8762..f79679ec443 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/single/afc/BeltOfGiantStrengthTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/afc/BeltOfGiantStrengthTest.java
@@ -28,7 +28,7 @@ public class BeltOfGiantStrengthTest extends CardTestPlayerBase {
setStopAt(1, PhaseStep.END_TURN);
execute();
- assertIsAttachedTo(playerA, belt, gigantosauras);
+ assertAttachedTo(playerA, belt, gigantosauras, true);
Assert.assertTrue(
"All Forests should be untapped",
currentGame
@@ -51,6 +51,6 @@ public class BeltOfGiantStrengthTest extends CardTestPlayerBase {
setStopAt(1, PhaseStep.END_TURN);
execute();
- assertIsAttachedTo(playerA, belt, gigantosauras);
+ assertAttachedTo(playerA, belt, gigantosauras, true);
}
}
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/ForgeAnewTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/ForgeAnewTest.java
index d19dd55424a..dc5770dfe35 100644
--- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/ForgeAnewTest.java
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/ForgeAnewTest.java
@@ -39,6 +39,6 @@ public class ForgeAnewTest extends CardTestPlayerBase {
execute();
// Make sure it is attached
- assertIsAttachedTo(playerA, EQUIPMENT, CREATURE);
+ assertAttachedTo(playerA, EQUIPMENT, CREATURE, true);
}
}
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/woc/UnfinishedBusinessTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/woc/UnfinishedBusinessTest.java
new file mode 100644
index 00000000000..0a6684a6bd3
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/woc/UnfinishedBusinessTest.java
@@ -0,0 +1,138 @@
+package org.mage.test.cards.single.woc;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+/**
+ * @author Codermann63
+ */
+
+public class UnfinishedBusinessTest extends CardTestPlayerBase {
+ /*
+ * Unfinished Business
+ * {3}{W}{W} - Sorcery
+ * Return target creature card from your graveyard to the battlefield,
+ * then return up to two target Aura and/or Equipment cards from your graveyard to the battlefield attached to that creature.
+ * (If the Auras can’t enchant that creature, they remain in your graveyard.)
+ */
+ private static final String UNFINISHEDBUSINESS = "Unfinished Business";
+ // Deadly insect - 6/1 creature with Shroud
+ private static final String SHROUDCREATURE = "Deadly Insect";
+ // Apostle of Purifying Light - white creature with protection from black & activated ability to exile target card from a graveyard.
+ private static final String APOSTLE = "Apostle of Purifying Light";
+ private static final String BEAR = "Grizzly Bears";
+ // Blanchwood Armor - green aura - enchanted creature gets +1/+1 for each forest you control
+ private static final String AURA = "Blanchwood Armor";
+ // Ghoulflesh - black aura - enchanted creature get -1/-1 and is a black Zombie in addition to its other colors and types.
+ private static final String GHOULFLESH = "Ghoulflesh";
+ // Shuko - colorless Equipment - Equipped creature gets +1/+0.
+ private static final String EQUIPMENT = "Shuko";
+ // Enormous Energy Blade - black equipment - Equipped creature gets +4/+0. Whenever Enormous Energy Blade becomes attached to a creature, tap that creature.
+ private static final String EEB = "Enormous Energy Blade";
+
+
+ // Return a creature with shroud, and return an Aura and an Equipment checking that both attach.
+ @Test
+ public void testShroud() {
+ addCard(Zone.HAND, playerA, UNFINISHEDBUSINESS);
+ addCard(Zone.GRAVEYARD, playerA, SHROUDCREATURE, 1);
+ addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+ addCard(Zone.GRAVEYARD, playerA, AURA);
+ addCard(Zone.GRAVEYARD, playerA, EQUIPMENT);
+
+ setStrictChooseMode(true);
+
+ castSpell(1,PhaseStep.PRECOMBAT_MAIN,playerA,UNFINISHEDBUSINESS,SHROUDCREATURE);
+ addTarget(playerA, AURA+"^"+EQUIPMENT);
+ waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
+
+ execute();
+
+ // Check that all returned to the battlefield
+ assertPermanentCount(playerA, SHROUDCREATURE, 1);
+ assertPermanentCount(playerA, AURA, 1);
+ assertPermanentCount(playerA, EQUIPMENT, 1);
+
+ // Check aura and equipment is attached
+ assertAttachedTo(playerA, AURA, SHROUDCREATURE, true);
+ assertAttachedTo(playerA, EQUIPMENT, SHROUDCREATURE, true);
+ }
+
+
+
+ // Return Apostle of purifying light (creature with protection from black),
+ // and try to return a Ghoulflesh(black aura) and Enormous energy blade(black equipment).
+ // The aura should remain in graveyard and the equipment should return but not attach.
+ @Test
+ public void testProtection(){
+ addCard(Zone.HAND, playerA, UNFINISHEDBUSINESS);
+ // Nexus wardens gain life if an enchantment entered the battlefield
+ // This is to test if ghoulflesh enters the battlefield
+ addCard(Zone.BATTLEFIELD, playerA, "Nexus Wardens");
+ addCard(Zone.GRAVEYARD, playerA, APOSTLE);
+ addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+ addCard(Zone.GRAVEYARD, playerA, GHOULFLESH);
+ addCard(Zone.GRAVEYARD, playerA, EEB);
+
+ setStrictChooseMode(true);
+
+ castSpell(1,PhaseStep.PRECOMBAT_MAIN,playerA,UNFINISHEDBUSINESS,APOSTLE);
+ addTarget(playerA, GHOULFLESH+"^"+EEB);
+ waitStackResolved(1, PhaseStep.END_TURN);
+
+ execute();
+
+ // Check boardstate
+ assertPermanentCount(playerA, APOSTLE, 1);
+ assertPermanentCount(playerA, GHOULFLESH, 0);
+ assertPermanentCount(playerA, EEB, 1);
+
+ // EEB should never have been attached and therefore the White knight should be untapped
+ assertTapped(APOSTLE,false);
+ assertAttachedTo(playerA, EEB, APOSTLE,false);
+
+ // Check that Ghoulflesh never entered the battlefield
+ assertLife(playerA, 20);
+ assertGraveyardCount(playerA,GHOULFLESH, 1);
+ }
+
+
+
+ // Test equipment return if creature is exiled from graveyard before spell resolution
+ @Test
+ public void testExileCreatureBeforeResolution(){
+ addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
+ addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
+ addCard(Zone.HAND,playerA,UNFINISHEDBUSINESS);
+ addCard(Zone.GRAVEYARD, playerA, EQUIPMENT);
+ addCard(Zone.GRAVEYARD, playerA, AURA);
+ addCard(Zone.GRAVEYARD, playerA, BEAR);
+ // Apostle of Purifying Light has an activated ability to exile card from graveyard
+ addCard(Zone.BATTLEFIELD, playerB, APOSTLE);
+ // Nexus wardens gain life if an enchantment entered the battlefield
+ // This is to test if the aura enters the battlefield
+ addCard(Zone.BATTLEFIELD, playerA, "Nexus Wardens");
+
+ setStrictChooseMode(true);
+
+ // Cast Unfinished Business targeting GrizzlyBears, and aura and an equipment
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, UNFINISHEDBUSINESS);
+ addTarget(playerA, BEAR);
+ addTarget(playerA, EQUIPMENT+"^"+AURA);
+ // Exile Grizzly Bears from graveyard before spell resolution
+ activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB,"{2}: ",BEAR,UNFINISHEDBUSINESS);
+ waitStackResolved(1, PhaseStep.END_TURN);
+
+ execute();
+
+ // Grizzly Bears should be exiled
+ assertExileCount(playerA, BEAR, 1);
+ // The aura should still be in the graveyard and should never have entered
+ assertGraveyardCount(playerA, AURA, 1);
+ assertLife(playerA, 20);
+ // The equipment should have returned to the battlefield
+ assertPermanentCount(playerA,EQUIPMENT, 1);
+ }
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
index 0e6a10c74f4..a24ee16c362 100644
--- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
+++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java
@@ -1556,24 +1556,27 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
public void assertTopCardRevealed(TestPlayer player, boolean isRevealed) {
Assert.assertEquals(isRevealed, player.isTopCardRevealed());
}
-
- public void assertIsAttachedTo(TestPlayer thePlayer, String theAttachment, String thePermanent) {
-
+ /**
+ * Asserts if, or if not, theAttachment is attached to thePermanent.
+ *
+ * @param isAttached true => assertIsAttachedTo, false => assertIsNotAttachedTo
+ */
+ public void assertAttachedTo(TestPlayer thePlayer, String theAttachment, String thePermanent, boolean isAttached) {
List permanents = currentGame.getBattlefield().getAllActivePermanents().stream()
.filter(permanent -> permanent.isControlledBy(thePlayer.getId()))
.filter(permanent -> permanent.getName().equals(thePermanent))
.collect(Collectors.toList());
- assertTrue(theAttachment + " was not attached to " + thePermanent,
+ assertTrue(theAttachment + " was "+ (!isAttached ? "":"not") +" attached to " + thePermanent,
+ !isAttached ^
permanents.stream()
.anyMatch(permanent -> permanent.getAttachments()
.stream()
.map(id -> currentGame.getCard(id))
.map(MageObject::getName)
.collect(Collectors.toList()).contains(theAttachment)));
-
-
}
+
public Permanent getPermanent(String cardName, UUID controller) {
assertAliaseSupportInActivateCommand(cardName, false);
Permanent found = null;