Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ChangeZoneEffect ordering to library; Aetherspouts prompt #5798

Merged
merged 5 commits into from
Oct 2, 2024
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
6 changes: 6 additions & 0 deletions forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,12 @@ public CardCollectionView orderMoveToZoneList(CardCollectionView cards, ZoneType
}
}

if(source == null || !source.hasParam("LibraryPosition")
|| AbilityUtils.calculateAmount(source.getHostCard(), source.getParam("LibraryPosition"), source) >= 0) {
//Cards going to the top of a deck are returned in reverse order.
Collections.reverse(reordered);
}

assert(reordered.size() == cards.size());

return reordered;
Expand Down
8 changes: 2 additions & 6 deletions forge-game/src/main/java/forge/game/GameAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -423,9 +423,6 @@ private Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer
cards = (CardCollection) c.getOwner().getController().orderMoveToZoneList(cards, zoneTo.getZoneType(), cause);
}
cards.set(cards.indexOf(copied), c);
if (zoneTo.is(ZoneType.Library)) {
Collections.reverse(cards);
}
mergedCards = cards;
if (cause != null) {
// Replace sa targeting cards
Expand Down Expand Up @@ -454,9 +451,8 @@ private Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer
}
game.getCombat().removeFromCombat(c);
}
if ((zoneFrom.is(ZoneType.Library) || zoneFrom.is(ZoneType.PlanarDeck)
|| zoneFrom.is(ZoneType.SchemeDeck) || zoneFrom.is(ZoneType.AttractionDeck))
&& zoneFrom == zoneTo && position.equals(zoneFrom.size()) && position != 0) {
if (zoneFrom.getZoneType().isDeck() && zoneFrom == zoneTo
&& position.equals(zoneFrom.size()) && position != 0) {
position--;
}
if (mergedCards != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,22 +85,20 @@ public void resolve(SpellAbility sa) {
final String imprint = sa.getParam("Imprint");
final boolean random = sa.hasParam("RandomOrder");
final boolean remLKI = sa.hasParam("RememberLKI");
final boolean movingToDeck = destination.isDeck();

final int libraryPos = sa.hasParam("LibraryPosition") ? Integer.parseInt(sa.getParam("LibraryPosition")) : 0;

if (!random && !((destination == ZoneType.Library || destination == ZoneType.PlanarDeck) && sa.hasParam("Shuffle"))) {
if ((destination == ZoneType.Library || destination == ZoneType.PlanarDeck) && cards.size() >= 2) {
if (!random && !sa.hasParam("Shuffle")) {
if (movingToDeck && cards.size() >= 2) {
Player p = AbilityUtils.getDefinedPlayers(source, sa.getParam("DefinedPlayer"), sa).get(0);
cards = (CardCollection) p.getController().orderMoveToZoneList(cards, destination, sa);
//the last card in this list will be the closest to the top, but we want the first card to be closest.
//so reverse it here before moving them to the library.
java.util.Collections.reverse(cards);
} else {
cards = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, cards, destination, sa);
}
}

if (destination.equals(ZoneType.Library) && random) {
if (movingToDeck && random) {
CardLists.shuffle(cards);
}

Expand Down Expand Up @@ -208,6 +206,7 @@ public void resolve(SpellAbility sa) {
// CR 701.20d If an effect would cause a player to shuffle a set of objects into a library,
// that library is shuffled even if there are no objects in that set.
if (sa.hasParam("Shuffle")) {
//TODO: If destination zone is some other kind of deck like a planar deck, shuffle that instead.
for (Player p : tgtPlayers) {
p.shuffle(sa);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -515,15 +515,16 @@ private void changeKnownOriginResolve(final SpellAbility sa) {
}

// CR 401.4
if (destination.equals(ZoneType.Library) && !shuffle && tgtCards.size() > 1) {
if (destination.isDeck() && !shuffle && tgtCards.size() > 1) {
if (sa.hasParam("RandomOrder")) {
final CardCollection random = new CardCollection(tgtCards);
CardLists.shuffle(random);
tgtCards = random;
} else if (sa.hasParam("Chooser")) {
tgtCards = chooser.getController().orderMoveToZoneList(tgtCards, destination, sa);
} else {
tgtCards = GameActionUtil.orderCardsByTheirOwners(game, tgtCards, destination, sa);
if (sa.hasParam("Chooser"))
tgtCards = chooser.getController().orderMoveToZoneList(tgtCards, destination, sa);
else
tgtCards = GameActionUtil.orderCardsByTheirOwners(game, tgtCards, destination, sa);
}
}

Expand Down Expand Up @@ -718,7 +719,7 @@ private void changeKnownOriginResolve(final SpellAbility sa) {
handleExiledWith(gameCard, sa);
}

movedCard = game.getAction().moveTo(destination, gameCard, sa, moveParams);
movedCard = game.getAction().moveTo(destination, gameCard, libraryPosition, sa, moveParams);

if (destination.equals(ZoneType.Exile) && lastStateBattlefield.contains(gameCard) && hostCard.equals(gameCard)) {
// support Parallax Wave returning itself
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ else if (!sa.hasParam("NoLooking")) {
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
AbilityKey.addCardZoneTableParams(moveParams, zoneMovements);

if (destZone1.equals(ZoneType.Library) || destZone1.equals(ZoneType.PlanarDeck) || destZone1.equals(ZoneType.SchemeDeck)) {
if (destZone1.isDeck()) {
c = game.getAction().moveTo(destZone1, c, libraryPosition, sa, AbilityKey.newMap());
} else {
if (destZone1.equals(ZoneType.Exile) && !c.canExiledBy(sa, true)) {
Expand Down Expand Up @@ -445,8 +445,7 @@ else if (!sa.hasParam("NoLooking")) {
if (!rest.isEmpty() && (!sa.hasParam("DestZone2Optional") || p.getController().confirmAction(sa, null,
Localizer.getInstance().getMessage("lblDoYouWantPutCardToZone",
destZone2.getTranslatedName()), null))) {
if (destZone2 == ZoneType.Library || destZone2 == ZoneType.PlanarDeck
|| destZone2 == ZoneType.SchemeDeck || destZone2 == ZoneType.Graveyard) {
if (destZone2.isDeck() || destZone2 == ZoneType.Graveyard) {
CardCollection afterOrder = rest;
if (sa.hasParam("RestRandomOrder")) {
CardLists.shuffle(afterOrder);
Expand All @@ -457,10 +456,6 @@ else if (!sa.hasParam("NoLooking")) {
afterOrder = (CardCollection) chooser.getController().orderMoveToZoneList(rest, destZone2, sa);
}
}
if (libraryPosition2 != -1) {
// Closest to top
Collections.reverse(afterOrder);
}

for (final Card c : afterOrder) {
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public void resolve(SpellAbility sa) {
final PlayerZone zone = c.getOwner().getZone(destZone1);

if (!sa.hasParam("ChangeLater")) {
if (zone.is(ZoneType.Library) || zone.is(ZoneType.PlanarDeck) || zone.is(ZoneType.SchemeDeck)) {
if (zone.getZoneType().isDeck()) {
c = game.getAction().moveTo(destZone1, c, libraryPosition, sa, AbilityKey.newMap());
} else {
if (destZone1.equals(ZoneType.Battlefield)) {
Expand Down Expand Up @@ -153,8 +153,7 @@ public void resolve(SpellAbility sa) {

// now, move the rest to destZone2
if (!sa.hasParam("ChangeLater")) {
if (destZone2 == ZoneType.Library || destZone2 == ZoneType.PlanarDeck
|| destZone2 == ZoneType.SchemeDeck || destZone2 == ZoneType.Graveyard) {
if (destZone2.isDeck() || destZone2 == ZoneType.Graveyard) {
CardCollection afterOrder = rest;
if (sa.hasParam("RestRandomOrder")) {
CardLists.shuffle(afterOrder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,9 @@ private static void rearrangeTopOfLibrary(final Card src,
}

CardCollection topCards = player.getTopXCardsFromLibrary(numCards);
int maxCards = topCards.size();

CardCollectionView orderedCards = activator.getController().orderMoveToZoneList(topCards, ZoneType.Library, sa);
for (int i = maxCards - 1; i >= 0; i--) {
Card next = orderedCards.get(i);
for (Card next : orderedCards) {
player.getGame().getAction().moveToLibrary(next, 0, sa);
}
if (mayshuffle && activator.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoyouWantShuffleTheLibrary"), null)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import forge.game.ability.SpellAbilityEffect;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
Expand Down Expand Up @@ -36,7 +37,8 @@ public void resolve(SpellAbility sa) {
Collections.shuffle(list, MyRandom.getRandom());
p.getZone(zone).setCards(list);
} else {
p.getController().orderMoveToZoneList(list, zone, sa);
CardCollectionView orderedCards = p.getController().orderMoveToZoneList(list, zone, sa);
p.getZone(zone).setCards(orderedCards);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,13 @@ public final void reveal(List<CardView> cards, ZoneType zone, PlayerView owner,
public abstract ImmutablePair<CardCollection, CardCollection> arrangeForSurveil(CardCollection topN);

public abstract boolean willPutCardOnTop(Card c);

/**
* Prompts the player to choose the order for cards being moved into a zone.
* The cards will be returned in the order that they should be moved, one at a time,
* to the given zone and position. Be aware that when moving cards to the top of a
* deck, this will be the reverse of the order they will ultimately end up in.
*/
public abstract CardCollectionView orderMoveToZoneList(CardCollectionView cards, ZoneType destinationZone, SpellAbility source);

/** p = target player, validCards - possible discards, min cards to discard */
Expand Down
15 changes: 12 additions & 3 deletions forge-game/src/main/java/forge/game/zone/ZoneType.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import forge.util.Localizer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;

/**
Expand All @@ -30,8 +30,9 @@ public enum ZoneType {
ExtraHand(true, "lblHandZone"),
None(true, "lblNoneZone");

public static final List<ZoneType> STATIC_ABILITIES_SOURCE_ZONES = Arrays.asList(Battlefield, Graveyard, Exile, Command, Stack/*, Hand*/);
public static final List<ZoneType> PART_OF_COMMAND_ZONE = Arrays.asList(Command, SchemeDeck, PlanarDeck, AttractionDeck, Junkyard);
public static final EnumSet<ZoneType> STATIC_ABILITIES_SOURCE_ZONES = EnumSet.of(Battlefield, Graveyard, Exile, Command, Stack/*, Hand*/);
public static final EnumSet<ZoneType> PART_OF_COMMAND_ZONE = EnumSet.of(Command, SchemeDeck, PlanarDeck, AttractionDeck, Junkyard);
public static final EnumSet<ZoneType> DECK_ZONES = EnumSet.of(Library, SchemeDeck, PlanarDeck, AttractionDeck);

private final boolean holdsHiddenInfo;
private final String zoneName;
Expand Down Expand Up @@ -79,6 +80,14 @@ public boolean isPartOfCommandZone() {
return PART_OF_COMMAND_ZONE.contains(this);
}

/**
* Indicates that this zone behaves as a deck - an ordered pile of face down cards
* such as the Library or Planar Deck.
*/
public boolean isDeck() {
return DECK_ZONES.contains(this);
}

public String getTranslatedName() {
return zoneName;
}
Expand Down
26 changes: 20 additions & 6 deletions forge-gui/src/main/java/forge/player/PlayerControllerHuman.java
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,15 @@ public List<SpellAbility> chooseSpellAbilitiesForEffect(List<SpellAbility> spell
// create a mapping between a spell's view and the spell itself
Map<SpellAbilityView, SpellAbility> spellViewCache = SpellAbilityView.getMap(spells);

if(sa.hasParam("ShowCurrentCard"))
{
Card current = Iterables.getFirst(AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("ShowCurrentCard"), sa), null);
if(current != null) {
String promptCurrent = localizer.getMessage("lblCurrentCard") + ": " + current;
title = title + "\n" + promptCurrent;
}
}

//override generic
List<SpellAbilityView> chosen = getGui().getChoices(title, num, num, Lists.newArrayList(spellViewCache.keySet()));

Expand Down Expand Up @@ -1092,11 +1101,14 @@ public CardCollectionView orderMoveToZoneList(final CardCollectionView cards, fi
GameEntityViewMap<Card, CardView> gameCacheMove = GameEntityView.getMap(cards);
List<CardView> choices = gameCacheMove.getTrackableKeys();

boolean topOfDeck = destinationZone.isDeck()
&& (source == null
|| !source.hasParam("LibraryPosition")
|| AbilityUtils.calculateAmount(source.getHostCard(), source.getParam("LibraryPosition"), source) >= 0);

switch (destinationZone) {
case Library:
boolean bottomOfLibrary = (source != null && source.hasParam("LibraryPosition") ?
AbilityUtils.calculateAmount(source.getHostCard(), source.getParam("LibraryPosition"), source) : 0) < 0;
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoLibrary"), localizer.getMessage(bottomOfLibrary ? "lblClosestToBottom" : "lblClosestToTop"), choices, null);
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoLibrary"), localizer.getMessage(topOfDeck ? "lblClosestToTop" : "lblClosestToBottom"), choices, null);
break;
case Battlefield:
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutOntoBattlefield"), localizer.getMessage("lblPutFirst"), choices, null);
Expand All @@ -1108,13 +1120,13 @@ public CardCollectionView orderMoveToZoneList(final CardCollectionView cards, fi
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoExile"), localizer.getMessage("lblPutFirst"), choices, null);
break;
case PlanarDeck:
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoPlanarDeck"), localizer.getMessage("lblClosestToTop"), choices, null);
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoPlanarDeck"), localizer.getMessage(topOfDeck ? "lblClosestToTop" : "lblClosestToBottom"), choices, null);
break;
case SchemeDeck:
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoSchemeDeck"), localizer.getMessage("lblClosestToTop"), choices, null);
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoSchemeDeck"), localizer.getMessage(topOfDeck ? "lblClosestToTop" : "lblClosestToBottom"), choices, null);
break;
case AttractionDeck:
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoAttractionDeck"), localizer.getMessage("lblClosestToTop"), choices, null);
choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoAttractionDeck"), localizer.getMessage(topOfDeck ? "lblClosestToTop" : "lblClosestToBottom"), choices, null);
case Stack:
choices = getGui().order(localizer.getMessage("lblChooseOrderCopiesCast"), localizer.getMessage("lblPutFirst"), choices, null);
break;
Expand All @@ -1127,6 +1139,8 @@ public CardCollectionView orderMoveToZoneList(final CardCollectionView cards, fi
return cards;
}
endTempShowCards();
if(topOfDeck)
Collections.reverse(choices);
CardCollection result = new CardCollection();
gameCacheMove.addToList(choices, result);
return result;
Expand Down
Loading