Commit b94ec249 authored by Peter Patel-Schneider's avatar Peter Patel-Schneider

Changes to do parts of multi-player scry in correct order

parent e3257e02
......@@ -50,6 +50,7 @@ import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
import forge.util.maps.HashMapOfLists;
import forge.util.maps.MapOfLists;
import org.apache.commons.lang3.tuple.ImmutablePair;
import java.util.*;
......@@ -1711,12 +1712,16 @@ public class GameAction {
mulliganDelta++;
} while (!allKept);
//Vancouver Mulligan
//Vancouver Mulligan as a scry with the decisions inside
ArrayList<Player> scryers = new ArrayList<Player>();
for(Player p : whoCanMulligan) {
if (p.getStartingHandSize() > p.getZone(ZoneType.Hand).size()) {
p.scry(1, null);
scryers.add(p);
}
}
if ( scryers.size() > 0 ) {
scry(scryers,1,true,null);
}
}
private void runPreOpeningHandActions(final Player first) {
......@@ -1812,4 +1817,64 @@ public class GameAction {
runParams.put("Player", p);
game.getTriggerHandler().runTrigger(TriggerType.BecomeMonarch, runParams, false);
}
// Make scry an action function so that it can be used for mulligans (with a null cause)
// Assumes that the list of players is in APNAP order, which should be the case
// Optional here as well to handle the way that mulligans do the choice
// 701.17. Scry
// 701.17a To “scry N” means to look at the top N cards of your library, then put any number of them
// on the bottom of your library in any order and the rest on top of your library in any order.
// 701.17b If a player is instructed to scry 0, no scry event occurs. Abilities that trigger whenever a
// player scries won’t trigger.
// 701.17c If multiple players scry at once, each of those players looks at the top cards of their library
// at the same time. Those players decide in APNAP order (see rule 101.4) where to put those
// cards, then those cards move at the same time.
public void scry(List<Player>players,int numScry, boolean isOptional, SpellAbility cause) {
if ( numScry == 0 ) { return; }
// reveal the top N library cards to the player (only)
if ( players.size() > 1 ) { // no real need to separate out the look if there is only one player scrying
for ( final Player p : players ) {
final CardCollection topN = new CardCollection(p.getCardsIn(ZoneType.Library, numScry));
p.getGame().getAction().revealTo(topN,p);
}
}
// make the decisions
ArrayList<ImmutablePair<CardCollection, CardCollection>> decisions =
new ArrayList<ImmutablePair<CardCollection, CardCollection>>();
for ( final Player p : players ) {
if (isOptional && !p.getController().confirmAction(cause, null, "Do you want to scry?")) {
decisions.add(new ImmutablePair<CardCollection, CardCollection>(new CardCollection(),new CardCollection()));
} else {
final CardCollection topN = new CardCollection(p.getCardsIn(ZoneType.Library, numScry));
ImmutablePair<CardCollection, CardCollection> decision = p.getController().arrangeForScry(topN);
decisions.add(decision);
int numToTop = decision.getLeft()==null ? 0 : decision.getLeft().size();
int numToBottom = decision.getRight()==null ? 0 : decision.getRight().size();
p.getGame().fireEvent(new GameEventScry(p, numToTop, numToBottom)); // publicize the decision
}
}
// do the moves after all the decisions (maybe not necesssary, but let's do it the official way)
for ( int i = 0; i<players.size(); i++ ) { // no good iterate simultaneously in Java
final Player p = players.get(i);
final CardCollection toTop = decisions.get(i).getLeft();
final CardCollection toBottom = decisions.get(i).getRight();
if (toTop != null) {
Collections.reverse(toTop); // reverse to get the correct order
for(Card c : toTop) {
p.getGame().getAction().moveToLibrary(c, cause, null);
}
}
if (toBottom != null) {
for(Card c : toBottom) {
p.getGame().getAction().moveToBottomOfLibrary(c, cause, null);
}
}
// set up triggers (but not actually do them until later)
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Player", p);
p.getGame().getTriggerHandler().runTrigger(TriggerType.Scry, runParams, false);
}
}
}
......@@ -6,6 +6,7 @@ import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import java.util.List;
import java.util.ArrayList;
public class ScryEffect extends SpellAbilityEffect {
@Override
......@@ -38,17 +39,16 @@ public class ScryEffect extends SpellAbilityEffect {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final List<Player> tgtPlayers = getTargetPlayers(sa);
final ArrayList<Player> players = new ArrayList<Player>(); // players really affected
// Optional here for spells that have optional multi-player scrying
for (final Player p : tgtPlayers) {
if ((tgt == null) || p.canBeTargetedBy(sa)) {
if (isOptional && !p.getController().confirmAction(sa, null, "Do you want to scry?")) {
continue;
}
p.scry(num, sa);
}
}
if ( ((tgt == null) || p.canBeTargetedBy(sa)) &&
(!isOptional || p.getController().confirmAction(sa, null, "Do you want to scry?")) ) {
players.add(p);
}
}
sa.getActivatingPlayer().getGame().getAction().scry(players,num,false,sa);
}
}
......@@ -1262,42 +1262,6 @@ public class Player extends GameEntity implements Comparable<Player> {
return drawCards(1);
}
public void scry(final int numScry, SpellAbility cause) {
final CardCollection topN = new CardCollection(this.getCardsIn(ZoneType.Library, numScry));
if (topN.isEmpty()) {
return;
}
final ImmutablePair<CardCollection, CardCollection> lists = getController().arrangeForScry(topN);
final CardCollection toTop = lists.getLeft();
final CardCollection toBottom = lists.getRight();
int numToBottom = 0;
int numToTop = 0;
if (toBottom != null) {
for(Card c : toBottom) {
getGame().getAction().moveToBottomOfLibrary(c, cause, null);
numToBottom++;
}
}
if (toTop != null) {
Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus.
for(Card c : toTop) {
getGame().getAction().moveToLibrary(c, cause, null);
numToTop++;
}
}
getGame().fireEvent(new GameEventScry(this, numToTop, numToBottom));
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Player", this);
getGame().getTriggerHandler().runTrigger(TriggerType.Scry, runParams, false);
}
public void surveil(int num, SpellAbility cause) {
final Map<String, Object> repParams = Maps.newHashMap();
......
......@@ -17,4 +17,4 @@ public enum PlayerActionConfirmMode {
// Ripple;
}
\ No newline at end of file
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment