Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 38 additions & 62 deletions Mage.Sets/src/mage/cards/a/Aetherspouts.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterAttackingCreature;
import mage.filter.predicate.permanent.PermanentReferenceInCollectionPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import mage.players.PlayerList;
import mage.target.TargetCard;
import mage.target.TargetPermanent;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
* @author LevelX2
Expand Down Expand Up @@ -85,6 +88,11 @@ public boolean apply(Game game, Ability source) {
List<Permanent> permanentsToBottom = new ArrayList<>();
for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterAttackingCreature(), player.getId(), source, game)) {
if (permanent.isOwnedBy(player.getId())) {
// it doesn't matter if tokens go to top or bottom of library, so don't bother asking the player
if (permanent instanceof PermanentToken) {
permanentsToTop.add(permanent);
continue;
}
if (player.chooseUse(outcome, "Put " + permanent.getLogName() + " to the top? (else it goes to bottom)", source, game)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are existing PutOnTopOrBottomLibraryTargetEffect with support of any amount and any object type. It also support puts by owner. It's require only one thing - sorted targets list in player order (I recommend to modify PutOnLibraryTargetEffect to support players order due paper rules anyway).

So final logic for current card:

  • use standard PutOnLibraryTargetEffect;
  • collect target as all attacking permanents;
  • it's will process all objects inside effect, no need in custom code

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's require little modification to start cycle by getPlayersInRange, collect cards list and call choose dialog to process it.

shot_260312_075127

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are existing PutOnTopOrBottomLibraryTargetEffect with support of any amount and any object type. It also support puts by owner. It's require only one thing - sorted targets list in player order (I recommend to modify PutOnLibraryTargetEffect to support players order due paper rules anyway).

So final logic for current card:

* use standard PutOnLibraryTargetEffect;

* collect target as all attacking permanents;

* it's will process all objects inside effect, no need in custom code

PutOnTopOrBottomLibraryTargetEffect doesn't seem to support multiple targets in the sense the Aetherspouts needs, because it only prompts the player once. Aetherspouts needs to prompt each player for each card where they want it to go.

We could use PutOnLibraryTargetEffect, but it barely saves us any code, as most of the logic here is looping to split the cards into one list for the top and one list for the bottom, and PutOnLibraryTargetEffect only supports sending all cards to top or all cards to bottom.

permanentsToTop.add(permanent);
game.informPlayers(permanent.getLogName() + " goes to the top of " + player.getLogName() + "'s library");
Expand All @@ -95,82 +103,50 @@ public boolean apply(Game game, Ability source) {
}
}
// cards to top
Cards cards = new CardsImpl();
List<Permanent> toLibrary = new ArrayList<>();
for (Permanent permanent : permanentsToTop) {
if (permanent instanceof PermanentToken) {
toLibrary.add(permanent);
} else {
Card card = game.getCard(permanent.getId());
if (card != null) {
cards.add(card);
}
}
}
TargetCard target = new TargetCard(Zone.BATTLEFIELD, new FilterCard("order to put on the top of library (last chosen will be the top most)"));
while (cards.size() > 1) {
final Map<Boolean, List<Permanent>> orderedToTop = permanentsToTop.stream().collect(
Collectors.partitioningBy(permanent -> permanent instanceof PermanentToken));
while (orderedToTop.get(false).size() > 1) {
if (!player.canRespond()) {
return false;
}
player.choose(Outcome.Neutral, cards, target, source, game);
Card card = cards.get(target.getFirstTarget(), game);
if (card != null) {
cards.remove(card);
Permanent permanent = game.getPermanent(card.getId());
if (permanent != null) {
toLibrary.add(permanent);
}
}
target.clearChosen();
}
if (cards.size() == 1) {
Card card = cards.get(cards.iterator().next(), game);
Permanent permanent = game.getPermanent(card.getId());
final FilterPermanent filter = new FilterPermanent("order to put on the top of library (last chosen will be the top most)");
filter.add(new PermanentReferenceInCollectionPredicate(orderedToTop.get(false), game));
final TargetPermanent target = new TargetPermanent(1, 1, filter, true);
player.chooseTarget(Outcome.Neutral, target, source, game);
final Permanent permanent = game.getPermanent(target.getFirstTarget());
if (permanent != null) {
toLibrary.add(permanent);
orderedToTop.get(false).remove(permanent);
orderedToTop.get(true).add(permanent);
}
}
if (orderedToTop.get(false).size() == 1) {
orderedToTop.get(true).add(orderedToTop.get(false).get(0));
}
// move all permanents to lib at the same time
for (Permanent permanent : toLibrary) {
for (Permanent permanent : orderedToTop.get(true)) {
player.moveCardToLibraryWithInfo(permanent, source, game, Zone.BATTLEFIELD, true, false);
}
// cards to bottom
cards.clear();
toLibrary.clear();
for (Permanent permanent : permanentsToBottom) {
if (permanent instanceof PermanentToken) {
toLibrary.add(permanent);
} else {
Card card = game.getCard(permanent.getId());
if (card != null) {
cards.add(card);
}
}
}
target = new TargetCard(Zone.BATTLEFIELD, new FilterCard("order to put on bottom of library (last chosen will be bottommost card)"));
while (player.canRespond() && cards.size() > 1) {
player.choose(Outcome.Neutral, cards, target, source, game);
Card card = cards.get(target.getFirstTarget(), game);
if (card != null) {
cards.remove(card);
Permanent permanent = game.getPermanent(card.getId());
if (permanent != null) {
toLibrary.add(permanent);
}
target.clearChosen();
final Map<Boolean, List<Permanent>> orderedToBottom = permanentsToBottom.stream().collect(
Collectors.partitioningBy(permanent -> permanent instanceof PermanentToken));
while (player.canRespond() && orderedToBottom.get(false).size() > 1) {
final FilterPermanent filter = new FilterPermanent("order to put on bottom of library (last chosen will be bottommost card)");
filter.add(new PermanentReferenceInCollectionPredicate(orderedToBottom.get(false), game));
final TargetPermanent target = new TargetPermanent(1, 1, filter, true);
player.chooseTarget(Outcome.Neutral, target, source, game);
final Permanent permanent = game.getPermanent(target.getFirstTarget());
if (permanent != null) {
orderedToBottom.get(false).remove(permanent);
orderedToBottom.get(true).add(permanent);
} else {
break;
}
}
if (cards.size() == 1) {
Card card = cards.get(cards.iterator().next(), game);
Permanent permanent = game.getPermanent(card.getId());
if (permanent != null) {
toLibrary.add(permanent);
}
if (orderedToBottom.get(false).size() == 1) {
orderedToBottom.get(true).add(orderedToBottom.get(false).get(0));
}
// move all permanents to lib at the same time
for (Permanent permanent : toLibrary) {
for (Permanent permanent : orderedToBottom.get(true)) {
player.moveCardToLibraryWithInfo(permanent, source, game, Zone.BATTLEFIELD, false, false);
}
player = playerList.getNext(game, false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.mage.test.cards.single.mic;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, original printing m15


import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;

public class AetherspoutsTest extends CardTestPlayerBase {

// https://github.com/magefree/mage/issues/14351
@Test
public void testAllToTop() {
addCard(Zone.HAND, playerB, "Aetherspouts");
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears");
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears");
addCard(Zone.BATTLEFIELD, playerA, "Kalonian Behemoth");
addCard(Zone.BATTLEFIELD, playerB, "Island", 5);
addCard(Zone.HAND, playerA, "Beast Attack");
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);

castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Beast Attack");
attack(3, playerA, "Balduvian Bears", playerB);
attack(3, playerA, "Grizzly Bears", playerB);
attack(3, playerA, "Beast Token", playerB);
attack(3, playerA, "Kalonian Behemoth", playerB);
castSpell(3, PhaseStep.DECLARE_BLOCKERS, playerB, "Aetherspouts");
setChoice(playerA, true); // yes, send Balduvian Bears to top
setChoice(playerA, true); // yes, send Grizzly Bears to top
setChoice(playerA, true); // yes, send Kalonian Behemoth to top
addTarget(playerA, "Balduvian Bears");
addTarget(playerA, "Grizzly Bears");

setStrictChooseMode(true);
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
execute();

assertPermanentCount(playerA, "Balduvian Bears", 0);
assertPermanentCount(playerA, "Grizzly Bears", 0);
assertPermanentCount(playerA, "Kalonian Behemoth", 0);
assertPermanentCount(playerA, "Beast Token", 0);
assertLibraryCount(playerA, "Balduvian Bears", 1);
assertLibraryCount(playerA, "Grizzly Bears", 1);
assertLibraryCount(playerA, "Kalonian Behemoth", 1);
assertLibraryCount(playerA, "Beast Token", 0);
}

@Test
public void testSomeToBottom() {
addCard(Zone.HAND, playerB, "Aetherspouts");
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears");
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears");
addCard(Zone.BATTLEFIELD, playerA, "Kalonian Behemoth");
addCard(Zone.BATTLEFIELD, playerB, "Island", 5);
addCard(Zone.HAND, playerA, "Beast Attack");
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);

castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Beast Attack");
attack(3, playerA, "Balduvian Bears", playerB);
attack(3, playerA, "Grizzly Bears", playerB);
attack(3, playerA, "Beast Token", playerB);
attack(3, playerA, "Kalonian Behemoth", playerB);
castSpell(3, PhaseStep.DECLARE_BLOCKERS, playerB, "Aetherspouts");
setChoice(playerA, true); // yes, send Balduvian Bears to top
setChoice(playerA, false); // no, send Grizzly Bears to bottom
setChoice(playerA, false); // no, send Kalonian Behemoth to bottom
addTarget(playerA, "Grizzly Bears");

setStrictChooseMode(true);
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
execute();

assertPermanentCount(playerA, "Balduvian Bears", 0);
assertPermanentCount(playerA, "Grizzly Bears", 0);
assertPermanentCount(playerA, "Kalonian Behemoth", 0);
assertPermanentCount(playerA, "Beast Token", 0);
assertLibraryCount(playerA, "Balduvian Bears", 1);
assertLibraryCount(playerA, "Grizzly Bears", 1);
assertLibraryCount(playerA, "Kalonian Behemoth", 1);
assertLibraryCount(playerA, "Beast Token", 0);
}
}
Loading